Table of Contents

TMS FNC Cloud AI Guides

TTMSFNCCloudAI is a single non-visual component that talks to many AI language model services — OpenAI, Gemini, Claude, Grok, Perplexity, Mistral, DeepSeek and local Ollama or llama.cpp servers — through one consistent API. You set a Service, supply the matching API key, push a prompt into Context, and call Execute; the reply arrives asynchronously in OnExecuted. The same component also transcribes and translates audio, submits documents (PDF, Word, Excel) as context, exposes tool/function calling, and tracks token usage across requests. Reach for it whenever an application needs chat completions, document Q&A, or speech-to-text without binding to a single vendor's SDK. This guide covers the core request flow and the capabilities added in recent releases.

Quick start: send a prompt

Configure the service and key, assign an OnExecuted handler, set the prompt text on Context, and call Execute. Requests are asynchronous — Execute returns immediately and the response is delivered to the event handler, so never block the UI thread waiting on it.

procedure TForm1.FormCreate(Sender: TObject);
begin
  TMSFNCCloudAI1.Service := aiOpenAI;
  TMSFNCCloudAI1.APIKeys.OpenAI := 'your-openai-api-key';
  TMSFNCCloudAI1.Settings.OpenAIModel := 'gpt-4o';

  TMSFNCCloudAI1.OnExecuted := AIExecuted;

  TMSFNCCloudAI1.Context.Text := 'Summarize the benefits of unit testing in two sentences.';
  TMSFNCCloudAI1.Execute;   // asynchronous; the result arrives in OnExecuted
end;

procedure TForm1.AIExecuted(Sender: TObject; AResponse: TTMSFNCCloudAIResponse;
  AHttpStatusCode: Integer; AHttpResult: string);
begin
  if (AHttpStatusCode = 200) and Assigned(AResponse) then
    Memo1.Lines.Text := AResponse.Content.Text
  else
    Memo1.Lines.Add('Request failed: ' + AHttpResult);
end;

AResponse.Content holds the reply text; AResponse.TotalTokens and the component's Usage object report token consumption. Always check AHttpStatusCode before reading the response — on a transport or service error AResponse may be nil and AHttpResult carries the raw error body.

Choosing a service at runtime

When the same app may be configured with different providers, let the user pick from only the services that are actually usable. GetActiveServices returns the services that have an API key set (or a reachable local endpoint), and the capability flags narrow the list further — for example to services that accept file input or support function calling.

procedure TForm1.PopulateServiceList;
var
  Services: TStringList;
begin
  // GetActiveServices returns only services with an available API key or local endpoint.
  // Pass UseFiles := True to keep only services that accept file input.
  Services := TMSFNCCloudAI1.GetActiveServices(False, False);

  // The returned list is owned by the component - copy it, do not free it.
  ComboBox1.Items.Assign(Services);
end;

The returned TStringList is owned by the component and reused on the next call, so copy it (as above) rather than freeing it. Use GetServices instead of GetActiveServices when you want the full catalogue regardless of configured keys.

Requesting reasoning effort

Reasoning-capable models can spend extra internal "thinking" on harder problems. Set Settings.Reasoning to ask for more or less effort; the component maps the value to each provider's own reasoning option (OpenAI, Claude, Gemini, Ollama and llama.cpp are supported). Use it for multi-step logic, code analysis, or math where a quick answer is often wrong.

procedure TForm1.AskWithReasoning;
begin
  TMSFNCCloudAI1.Service := aiOpenAI;
  TMSFNCCloudAI1.APIKeys.OpenAI := 'your-openai-api-key';

  // Ask reasoning-capable services (OpenAI, Claude, Gemini, Ollama, llama.cpp) to think harder.
  // aiIgnore (default) sends no option; aiOff explicitly disables reasoning.
  TMSFNCCloudAI1.Settings.Reasoning := aiHigh;

  TMSFNCCloudAI1.OnExecuted := AIExecuted;
  TMSFNCCloudAI1.Context.Text :=
    'A bat and a ball cost 1.10 in total. The bat costs 1.00 more than the ball. How much is the ball?';
  TMSFNCCloudAI1.Execute;
end;

The default aiIgnore sends no reasoning option at all (the model decides), while aiOff explicitly disables reasoning on services that support switching it off. Higher effort increases latency and token cost, so reserve aiHigh for prompts that genuinely need it.

Submitting documents as context

OpenAI accepts PDF, Word (.docx) and Excel (.xlsx) files as request context, and Mistral accepts PDF; the model can then answer questions grounded in the document. Attach files with AddFile, passing the matching TTMSFNCCloudAIFileType, then send your question through Context as usual.

procedure TForm1.AskAboutDocument;
begin
  TMSFNCCloudAI1.Service := aiOpenAI;   // OpenAI accepts PDF, DOCX and XLSX context
  TMSFNCCloudAI1.APIKeys.OpenAI := 'your-openai-api-key';

  TMSFNCCloudAI1.ClearFiles;
  TMSFNCCloudAI1.AddFile('C:\docs\contract.pdf', aiftPDF);
  // Also supported: aiftWord (.docx), aiftExcel (.xlsx), aiftImage, aiftCSV, aiftText.
  // Mistral accepts PDF context; set Service := aiMistral and APIKeys.Mistral to use it.

  TMSFNCCloudAI1.OnExecuted := AIExecuted;
  TMSFNCCloudAI1.Context.Text := 'List the payment terms described in the attached contract.';
  TMSFNCCloudAI1.Execute;
end;

Call ClearFiles before building a new request so files from a previous prompt do not leak into the next one. Use AddText to inject inline text content and AddURL to reference a remote resource; AddFile also accepts aiftImage, aiftCSV and aiftText for other content kinds.

Transcribing and translating audio

TTMSFNCCloudAI performs speech-to-text in addition to chat. Transcribe returns text in the spoken language, while Translate transcribes non-English audio directly into English. Both are asynchronous and deliver their result to OnTranscribeAudio. Set the provider's transcription model first — Settings.OpenAITranscribeModel for OpenAI or Settings.MistralTranscribeModel for Mistral.

procedure TForm1.TranscribeRecording;
begin
  TMSFNCCloudAI1.Service := aiOpenAI;
  TMSFNCCloudAI1.APIKeys.OpenAI := 'your-openai-api-key';
  TMSFNCCloudAI1.Settings.OpenAITranscribeModel := 'whisper-1';

  // For Mistral or Gemini, set Service accordingly and use Settings.MistralTranscribeModel.
  TMSFNCCloudAI1.OnTranscribeAudio := AITranscribed;

  // The language hint is optional (ISO code); pass '' to let the service auto-detect.
  TMSFNCCloudAI1.Transcribe('C:\audio\meeting.mp3', 'en');
end;

procedure TForm1.AITranscribed(Sender: TObject; HttpStatusCode: integer;
  HttpResult: string; Text: string);
begin
  if HttpStatusCode = 200 then
    Memo1.Lines.Text := Text
  else
    Memo1.Lines.Add('Transcription failed: ' + HttpResult);
end;

To translate foreign-language audio into English instead of transcribing it verbatim, call Translate:

procedure TForm1.TranslateRecording;
begin
  TMSFNCCloudAI1.Service := aiOpenAI;
  TMSFNCCloudAI1.APIKeys.OpenAI := 'your-openai-api-key';
  TMSFNCCloudAI1.Settings.OpenAITranscribeModel := 'whisper-1';

  TMSFNCCloudAI1.OnTranscribeAudio := AITranscribed;

  // Translate transcribes non-English audio and returns English text in OnTranscribeAudio.
  TMSFNCCloudAI1.Translate('C:\audio\interview-es.mp3');
end;

Both methods also have an overload that accepts a TMemoryStream, which is handy when the audio is captured in memory rather than saved to disk.

Tracking token usage

The Usage object accumulates token counters across every request the component makes, so you can surface running cost or enforce a budget. Read PromptTokens, CompletionTokens and TotalTokens at any time, and call Usage.Reset to start a fresh accounting window. Audio requests additionally report AudioDuration and AudioCharacters.

procedure TForm1.ShowUsage;
begin
  // Usage accumulates across every request until Reset is called.
  Label1.Caption := Format('Prompt: %d   Completion: %d   Total: %d',
    [TMSFNCCloudAI1.Usage.PromptTokens,
     TMSFNCCloudAI1.Usage.CompletionTokens,
     TMSFNCCloudAI1.Usage.TotalTokens]);
end;

procedure TForm1.StartNewSession;
begin
  TMSFNCCloudAI1.Usage.Reset;   // clear the counters before a new billing window
end;

Usage keeps counting until you call Reset, so reset it at the start of each logical session (per user, per document, per billing window) to keep the figures meaningful.

Combining features: analysed document Q&A with usage

Real workflows combine several of the capabilities above. The example below submits a PDF as context, asks a reasoning-capable model to analyse it, resets the usage window beforehand, and reports the exact token cost of the analysis when the response arrives — document context, reasoning effort, and usage tracking working together in one request.

procedure TForm1.AnalyzeContract;
begin
  TMSFNCCloudAI1.Service := aiOpenAI;
  TMSFNCCloudAI1.APIKeys.OpenAI := 'your-openai-api-key';
  TMSFNCCloudAI1.Settings.OpenAIModel := 'gpt-4o';

  // Ask a reasoning-capable model to analyse the document carefully.
  TMSFNCCloudAI1.Settings.Reasoning := aiHigh;

  // Start a clean usage window so the counters reflect only this analysis.
  TMSFNCCloudAI1.Usage.Reset;

  TMSFNCCloudAI1.ClearFiles;
  TMSFNCCloudAI1.AddFile('C:\docs\contract.pdf', aiftPDF);

  TMSFNCCloudAI1.OnExecuted := ContractAnalyzed;
  TMSFNCCloudAI1.Context.Text :=
    'Identify any clauses that expose us to financial penalties and explain why.';
  TMSFNCCloudAI1.Execute;
end;

procedure TForm1.ContractAnalyzed(Sender: TObject; AResponse: TTMSFNCCloudAIResponse;
  AHttpStatusCode: Integer; AHttpResult: string);
begin
  if (AHttpStatusCode = 200) and Assigned(AResponse) then
  begin
    Memo1.Lines.Text := AResponse.Content.Text;
    StatusBar1.SimpleText := Format('Tokens - prompt: %d, completion: %d, total: %d',
      [TMSFNCCloudAI1.Usage.PromptTokens,
       TMSFNCCloudAI1.Usage.CompletionTokens,
       TMSFNCCloudAI1.Usage.TotalTokens]);
  end
  else
    Memo1.Lines.Add('Analysis failed: ' + AHttpResult);
end;

Common pitfalls

  • Treating Execute as synchronous. It returns immediately; read the result only inside OnExecuted (or OnTranscribeAudio for audio). Do not inspect Context or any "result" property right after the call.
  • Not checking AHttpStatusCode. On failure AResponse can be nil. Guard every handler with a status check and fall back to AHttpResult for the error detail.
  • Freeing the GetServices / GetActiveServices list. The list is the component's own internal object — copy its contents; never free it.
  • Leftover files between requests. AddFile, AddText and AddURL accumulate. Call ClearFiles before composing a new prompt or the model receives stale context.
  • Usage counters never resetting. Usage is cumulative until Reset; a long-running app will keep adding to the totals across unrelated requests.
  • Setting the wrong model for the task. Text uses Settings.OpenAIModel (and the per-service equivalents), transcription uses Settings.OpenAITranscribeModel / Settings.MistralTranscribeModel, and speech generation uses Settings.OpenAISoundModel.

See also