Call an LLM from Delphi

Send a prompt from a VCL, FMX or console application and get an answer back, from a hosted model such as OpenAI or Anthropic Claude, or from a model running on your own machine through Ollama. This page takes you from an empty form to a working call, then to the three things every project hits next: streaming, tool calling and the decision between a hosted model and a local one.

OpenAI, Claude, Gemini, Grok, DeepSeek, Mistral, Ollama
Streaming over Server-Sent Events
Delphi 7 to RAD Studio 13

Two ways to make the call

There is a provider-neutral chat component, and there is a dedicated REST client for each vendor. Both ship in the same library, so you can start with one and drop down to the other without changing project.

TsgcAIChat, one API for every provider

Set Provider, an API key and a model, then call Chat. The component builds the vendor JSON, keeps the conversation history and returns the assistant text. Switching from OpenAI to Claude, or to a local Ollama model, is one assignment. Implemented by TsgcAI_Chat in the unit sgcAI_Chat, and registered on the palette as TsgcAIChat.

The per-vendor REST clients

TsgcHTTP_API_OpenAI, TsgcHTTP_API_Anthropic and TsgcHTTP_API_Ollama expose each vendor API in full, including the parts no neutral layer can cover: vision input, document input, extended thinking, batches, files, embeddings, image generation and transcription. Use these when you need a specific endpoint.

Edition and platform

The AI and LLM clients are an Enterprise feature of sgcWebSockets, not Standard and not Professional, or you can buy the standalone sgcAI package.

Platform matters here. The three REST clients compile on Windows, macOS, Linux, iOS and Android. TsgcAIChat does not: it is compiled for Windows only, so on a Linux service, a macOS build or a mobile target you call the REST client directly. The samples on this page are written so that either route works.

Your first call, in about ten lines

Drop the component, set a key and a model, send a prompt. Pick the tab for the provider you are starting with. The last tab needs no API key at all, because the model runs on your machine.

uChat.pas
uses
  Classes, SysUtils,
  // sgc
  sgcAI_Chat;

procedure TfrmMain.btnAskClick(Sender: TObject);
var
  oChat: TsgcAI_Chat;
begin
  oChat := TsgcAI_Chat.Create(nil);
  try
    oChat.Provider := aicpOpenAI;
    oChat.ChatOptions.ApiKey := GetApiKey;
    oChat.ChatOptions.Model := 'gpt-4o-mini';
    oChat.ChatOptions.MaxTokens := 1024;
    oChat.SystemMessage := 'You are a concise assistant inside a Delphi ERP.';

    memoAnswer.Lines.Text := oChat.Chat(memoPrompt.Lines.Text);
  finally
    oChat.Free;
  end;
end;

Changing vendor is a single line. Provider accepts aicpOpenAI, aicpAnthropic, aicpGemini, aicpDeepSeek, aicpOllama, aicpGrok and aicpMistral. Everything else in your code stays where it is.

Windows only. TsgcAI_Chat and its palette component TsgcAIChat are compiled for Windows, Win32 and Win64. On macOS, Linux, iOS and Android the unit is not compiled at all, so if your target is a Linux service or a mobile app, use the vendor REST clients in the other three tabs. Those have no platform restriction.

uOpenAI.pas
uses
  Classes, SysUtils,
  // sgc
  sgcHTTP_API_OpenAI;

var
  oOpenAI: TsgcHTTP_API_OpenAI;
begin
  oOpenAI := TsgcHTTP_API_OpenAI.Create(nil);
  try
    oOpenAI.OpenAIOptions.ApiKey := GetApiKey;

    // Shortcut: model plus one user message, raw JSON back
    memoAnswer.Lines.Text := oOpenAI._CreateChatCompletion(
      'gpt-4o', 'Say hello');
  finally
    oOpenAI.Free;
  end;
end;

The underscore methods are string shortcuts that return the raw response body. When you want a parsed object, build a TsgcOpenAIClass_Request_ChatCompletion and call CreateChatCompletion, shown further down this page.

uClaude.pas
uses
  Classes, SysUtils,
  // sgc
  sgcHTTP_API_Anthropic;

var
  oAnthropic: TsgcHTTP_API_Anthropic;
begin
  oAnthropic := TsgcHTTP_API_Anthropic.Create(nil);
  try
    oAnthropic.AnthropicOptions.ApiKey := GetApiKey;
    oAnthropic.AnthropicOptions.AnthropicVersion := '2023-06-01';

    // Model, prompt, max tokens
    memoAnswer.Lines.Text := oAnthropic._CreateMessage(
      'claude-sonnet-4-20250514',
      'Summarise RFC 6455 in three bullet points.', 1024);
  finally
    oAnthropic.Free;
  end;
end;

Claude requires the API version header, so set AnthropicVersion alongside the key. _CreateMessageWithSystem adds a system prompt, and _CountTokens prices a prompt before you send it.

uOllama.pas
uses
  Classes, SysUtils,
  // sgc
  sgcHTTP_API_Ollama;

var
  oOllama: TsgcHTTP_API_Ollama;
begin
  oOllama := TsgcHTTP_API_Ollama.Create(nil);
  try
    // Local server, no API key required
    oOllama.OllamaOptions.Host := 'http://localhost:11434';

    // Which models are pulled on this machine?
    memoModels.Lines.Text := oOllama._GetTags;

    memoAnswer.Lines.Text := oOllama._CreateMessage(
      'llama3', 'Summarise this invoice in one line.');
  finally
    oOllama.Free;
  end;
end;

Host defaults to http://localhost:11434, so on a default install you can leave it alone. _PullModel downloads a model, _GetTags lists what is already on disk and _ShowModel reads its details.

Show the answer as it is written

A one-shot call blocks until the model has finished, which on a long answer feels broken. Streaming delivers the reply in fragments, so text appears in your memo while the model is still thinking. This is the part most people get stuck on, so here is both levels of it.

With TsgcAIChat, deltas already decoded

Call ChatStream instead of Chat and handle OnChatStream. The component asks the provider for a streamed response, parses each Server-Sent Event and hands you aChunk, which is the new text and nothing else. You append it, and that is the whole implementation.

Every provider streams a different JSON shape. OpenAI, DeepSeek, Ollama, Grok and Mistral put the text in choices[0].delta.content, Claude in delta.text, Gemini deeper still inside candidates. TsgcAIChat already knows which one applies to the provider you selected, so your handler never sees JSON.

Set Cancel to True inside the handler to stop a runaway answer. The request is abandoned at that point, and no further chunks arrive. When the stream ends, the assembled text is added to the history and returned by ChatStream, and OnChatMessage fires once with the complete answer.

uChatStream.pas
procedure TfrmMain.btnStreamClick(Sender: TObject);
begin
  FChat.Provider := aicpAnthropic;
  FChat.ChatOptions.ApiKey := GetApiKey;
  FChat.ChatOptions.Model := 'claude-sonnet-4-20250514';
  FChat.OnChatStream := OnChatStream;
  FChat.OnChatError := OnChatError;

  memoAnswer.Lines.Clear;
  FChat.ChatStream(memoPrompt.Lines.Text);
end;

procedure TfrmMain.OnChatStream(Sender: TObject;
  const aChunk: string; var Cancel: Boolean);
begin
  memoAnswer.Text := memoAnswer.Text + aChunk;
  Cancel := FUserPressedStop;
end;

procedure TfrmMain.OnChatError(Sender: TObject;
  const aError: string);
begin
  memoAnswer.Lines.Add('ERROR: ' + aError);
end;

With a vendor client, raw events

The REST clients stream too. Assign OnHTTPAPISSE and call the streaming shortcut: _CreateMessageStream on Claude and on Ollama, and on OpenAI a TsgcOpenAIClass_Request_ChatCompletion with Stream set to True. The event gives you aEvent, the Server-Sent Event name, and aData, the payload for that event, exactly as the vendor sent it.

At this level you parse the JSON yourself, which is the point: you see tool call deltas, stop reasons, usage records and anything else the vendor puts on the wire. Partial network reads are already reassembled for you, so an event split across two TCP reads still arrives whole, and Ollama newline delimited JSON is delivered through the same event.

Terminators differ by vendor. OpenAI closes with the literal [DONE], Claude with an event named message_stop. Handle both if your code targets both.

uRawStream.pas
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  FAnthropic := TsgcHTTP_API_Anthropic.Create(nil);
  FAnthropic.AnthropicOptions.ApiKey := GetApiKey;
  FAnthropic.AnthropicOptions.AnthropicVersion := '2023-06-01';
  FAnthropic.OnHTTPAPISSE := HandleSSE;
  FAnthropic.OnHTTPAPIException := HandleException;
end;

procedure TfrmMain.btnStreamClick(Sender: TObject);
begin
  memoRaw.Lines.Clear;
  FAnthropic._CreateMessageStream(
    'claude-sonnet-4-20250514',
    'Write a haiku about Object Pascal.', 1024);
end;

procedure TfrmMain.HandleSSE(Sender: TObject;
  const aEvent, aData: string; var Cancel: Boolean);
begin
  if (aEvent = 'message_stop') or (aData = '[DONE]') then
    Exit;
  memoRaw.Lines.Add(aEvent + ': ' + aData);
end;

procedure TfrmMain.HandleException(Sender: TObject;
  E: Exception);
begin
  memoRaw.Lines.Add('ERROR: ' + E.Message);
end;

Threads. Chat and ChatStream are synchronous, so calling them straight from a button click freezes the form for the duration of the request. On Delphi 2010 and later, ChatAsync runs the call on a worker thread and returns an IsgcFuture<string>. Chain ThenProc for the result and OnError for failures, and call Cancel to abandon a request in flight. The ThenProc callback is dispatched on the main thread, so you can touch the UI from it directly.

Let the model call your Pascal code

Tool calling, also called function calling, is how a model asks your application to look something up or perform an action. You describe the function with a JSON Schema, the model replies with the arguments it wants, you run the Pascal code and send the result back. This is the mechanism behind every useful assistant inside a line of business application.

Claude, with typed tool objects

Build a TsgcAnthropicClass_Request_Messages, attach one or more TsgcAnthropicClass_Request_Tool entries and call CreateMessage. Each tool carries a Name, a Description and an InputSchema, which is the JSON Schema for its arguments.

The reply is a TsgcAnthropicClass_Response_Messages whose Content is an array of blocks. A block with ContentType equal to 'tool_use' carries the tool Name, the arguments in Input and an Id. Run your function, then send a follow up message containing a TsgcAnthropicClass_Request_Content_Block with ContentType set to 'tool_result', the same ToolUseId and your answer in Content. Set IsError when the call failed, so the model can recover instead of guessing.

Note the ownership: the Anthropic request does not own the messages and tools you attach, so free them yourself, as in the sample.

uToolUse.pas
var
  oRequest: TsgcAnthropicClass_Request_Messages;
  oMessage: TsgcAnthropicClass_Request_Message;
  oTool: TsgcAnthropicClass_Request_Tool;
  oMessages: TsgcAnthropicArray_Request_Messages;
  oTools: TsgcAnthropicArray_Request_Tools;
  oResponse: TsgcAnthropicClass_Response_Messages;
  i: Integer;
begin
  oRequest := TsgcAnthropicClass_Request_Messages.Create;
  try
    oRequest.Model := 'claude-sonnet-4-20250514';
    oRequest.MaxTokens := 4096;

    oMessage := TsgcAnthropicClass_Request_Message.Create;
    oMessage.Role := 'user';
    oMessage.Content := 'What is the stock of SKU 8841?';
    SetLength(oMessages, 1);
    oMessages[0] := oMessage;
    oRequest.Messages := oMessages;

    oTool := TsgcAnthropicClass_Request_Tool.Create;
    oTool.Name := 'get_stock';
    oTool.Description := 'Read the on-hand stock for a SKU';
    oTool.InputSchema :=
      '{"type":"object","properties":{"sku":{"type":"string",' +
      '"description":"The product code"}},"required":["sku"]}';
    SetLength(oTools, 1);
    oTools[0] := oTool;
    oRequest.Tools := oTools;

    oResponse := FAnthropic.CreateMessage(oRequest);
    try
      for i := 0 to Length(oResponse.Content) - 1 do
        if oResponse.Content[i].ContentType = 'tool_use' then
          // .Name is the tool, .Input the JSON arguments,
          // .Id the value to echo back as ToolUseId
          RunTool(oResponse.Content[i].Name,
            oResponse.Content[i].Input, oResponse.Content[i].Id)
        else if oResponse.Content[i].ContentType = 'text' then
          memoAnswer.Lines.Add(oResponse.Content[i].Text);
    finally
      oResponse.Free;
    end;
  finally
    sgcFree(oMessage);
    sgcFree(oTool);
    sgcFree(oRequest);
  end;
end;

OpenAI, with a typed request

The same idea, different shape. Fill a TsgcOpenAIClass_Request_ChatCompletion, assign the Messages array, put your tool definitions in Tools as a JSON array and optionally steer the model with ToolChoice. ParallelToolCalls controls whether the model may ask for several tools at once, and ResponseFormat pins the answer to JSON when you need to parse it.

CreateChatCompletion returns a parsed TsgcOpenAIClass_Response_ChatCompletion. Read the answer from Choices[0]._Message.Content, the requested calls from Choices[0]._Message.ToolCalls, why the model stopped from Choices[0].FinishReason, and what it cost from Usage.PromptTokens, Usage.CompletionTokens and Usage.TotalTokens. Unlike the Anthropic request, this one owns the message objects you attach and frees them with itself.

The older Functions and FunctionCall properties are still there for code written against the original OpenAI function calling shape.

uTypedRequest.pas
var
  oRequest: TsgcOpenAIClass_Request_ChatCompletion;
  oResponse: TsgcOpenAIClass_Response_ChatCompletion;
  oSystem, oUser: TsgcOpenAIClass_Request_Completion_Message;
  oMessages: TsgcOpenAIArray_Request_Completion_Messages;
begin
  oRequest := TsgcOpenAIClass_Request_ChatCompletion.Create;
  try
    oRequest.Model := 'gpt-4o';
    oRequest.MaxTokens := 1024;
    oRequest.Temperature := 0.2;

    oSystem := TsgcOpenAIClass_Request_Completion_Message.Create;
    oSystem.Role := 'system';
    oSystem.Content := 'You are a warehouse assistant.';

    oUser := TsgcOpenAIClass_Request_Completion_Message.Create;
    oUser.Role := 'user';
    oUser.Content := 'What is the stock of SKU 8841?';

    SetLength(oMessages, 2);
    oMessages[0] := oSystem;
    oMessages[1] := oUser;
    oRequest.Messages := oMessages;

    // Tool definitions as a JSON array
    oRequest.Tools :=
      '[{"type":"function","function":{"name":"get_stock",' +
      '"description":"Read the on-hand stock for a SKU",' +
      '"parameters":{"type":"object","properties":' +
      '{"sku":{"type":"string"}},"required":["sku"]}}}]';
    oRequest.ToolChoice := 'auto';

    oResponse := FOpenAI.CreateChatCompletion(oRequest);
    try
      if Length(oResponse.Choices) > 0 then
      begin
        memoAnswer.Lines.Text := oResponse.Choices[0]._Message.Content;
        memoTools.Lines.Text := oResponse.Choices[0]._Message.ToolCalls;
        lblStop.Caption := oResponse.Choices[0].FinishReason;
      end;
      lblTokens.Caption := IntToStr(oResponse.Usage.TotalTokens);
    finally
      oResponse.Free;
    end;
  finally
    // frees the attached Messages too
    oRequest.Free;
  end;
end;

Hosted model or local model

If your prompts contain client records, medical data, contracts or anything covered by a data processing agreement, this is not a performance question, it is a compliance question. Here is the comparison.

Hosted, OpenAI or Claude Local, Ollama
Where the prompt goes To the vendor, over HTTPS, under their terms Nowhere. The request goes to http://localhost:11434
Credentials An API key you must keep out of source control and out of the binary None by default. OllamaOptions.ApiKey exists for a proxied or remote instance
Answer quality The strongest models available today Good and improving, clearly behind the frontier on hard reasoning
Cost Per token, forever. Watch Usage.TotalTokens Your hardware, once. A useful model wants a lot of RAM or a GPU
Latency A network round trip, plus vendor queueing at busy times No network. Speed is whatever your machine does
Offline and air-gapped No Yes
Rate limits and outages Vendor side. Use RetryOptions and honour Retry-After Only your own capacity
Component TsgcHTTP_API_OpenAI, TsgcHTTP_API_Anthropic TsgcHTTP_API_Ollama

A common answer is both. Because TsgcAIChat puts every provider behind one API, you can route by data classification at runtime: local for anything that touches a customer record, hosted for the rest. The switch is Provider, plus ChatOptions.BaseUrl when the provider is Ollama.

if aContainsPersonalData then
begin
  FChat.Provider := aicpOllama;
  FChat.ChatOptions.BaseUrl := 'http://localhost:11434';
  FChat.ChatOptions.Model := 'llama3';
end
else
begin
  FChat.Provider := aicpOpenAI;
  FChat.ChatOptions.ApiKey := GetApiKey;
  FChat.ChatOptions.Model := 'gpt-4o-mini';
end;

memoAnswer.Lines.Text := FChat.Chat(memoPrompt.Lines.Text);

The settings that matter in production

A demo that works on your desk is not the same as a client that survives a rate limit, a slow model and a support ticket that says "it just stopped".

Retries and backoff

Each client carries a RetryOptions block: Enabled, Retries, Wait, Multiplier, MaxInterval, Jitter and HonorRetryAfter. Turn it on and a transient failure is retried with exponential backoff instead of surfacing as an exception. HonorRetryAfter makes the client obey the vendor Retry-After header rather than guessing.

Timeouts

HttpOptions.ReadTimeout is the one to raise. A long generation, especially on a local model, can outlast a default HTTP read timeout and fail halfway through an answer that was going fine.

Logging

LogOptions.Enabled plus LogOptions.FileName writes the traffic to a file. When a prompt behaves differently in production, this is the fastest way to see the JSON your code actually sent.

Conversation history

TsgcAIChat keeps the exchange and replays it on the next call, which is what makes a follow up question work. Cap it with MaxHistoryMessages so a long session does not grow the prompt, and therefore the bill, without limit. ClearHistory starts over, and GetHistory exposes Count and the individual messages.

Azure OpenAI

Set OpenAIOptions.Provider to oapvAzure and fill AzureOptions.ResourceName, AzureOptions.DeploymentId and AzureOptions.APIVersion. The rest of your code is unchanged, which matters when procurement insists the traffic stays inside your Azure tenant.

Errors and circuit breaking

OnHTTPAPIException surfaces failures from the REST clients, and OnChatError does the same for TsgcAIChat. An HTTP error arrives as an EsgcHTTPAPIProtocolException, which still descends from EIdHTTPProtocolException and additionally carries the response headers. CircuitBreaker and RateLimit are available on the client for the calls you make yourself.

What people build after the first call

A chat box is the beginning. These are the four directions this work usually takes, and every one of them is already in the library.

Answer from your own data

Turn your documents into vectors with TsgcAIOpenAIEmbeddings, store them in TsgcAIDatabaseVectorFile or TsgcAIDatabaseVectorPinecone, and retrieve the closest passages to put in the prompt. This is retrieval augmented generation, and it is how you stop the model inventing answers about your business.

Embeddings and vector databases

Talk to it, and let it talk back

TsgcAIOpenAIChatBot wires a recorder, transcription, the chat call and text to speech into one component, so a user can hold a spoken conversation with your application. TsgcAIOpenAITranslator does the same for live translation.

AI ChatBot and AI Translator

Expose your app to an AI agent

The Model Context Protocol is how assistants discover and call tools. TsgcWSServer_API_MCP turns your Delphi application into an MCP server that Claude and other clients can drive, and TsgcWSAPI_Client_MCP consumes other servers from your code. On the palette they appear as TsgcWSAPIServer_MCP and TsgcWSAPIClient_MCP.

Worth knowing: unlike the chat component, the MCP units are not restricted to Windows, so an MCP server written in Delphi can run on Linux.

MCP overview, MCP server and MCP client

Use the rest of each vendor API

Image generation, transcription, moderation, batches and fine tuning on OpenAI. Vision, documents, extended thinking, web search and token counting on Claude. Embeddings and model management on Ollama. Each vendor page lists what its client exposes.

OpenAI, Claude and Ollama

Reference, demos and tutorials

The component reference documents every property and event. Ready-to-run demo projects ship inside the library, under Demos\AI.

Reference, OpenAI client Every method, option and event on TsgcHTTP_API_OpenAI.
Reference, Anthropic client Messages, tools, vision, batches and token counting on TsgcHTTP_API_Anthropic.
Tutorial, Claude from Delphi The long form walkthrough of the Anthropic client, end to end.
Tutorial, local models with Ollama Pull a model, point the client at localhost and run it offline.
Tutorial, function calling Wiring a model to your own Pascal functions, step by step.
User Manual (PDF) Comprehensive manual covering every component in the library.

More reading: the OpenAI client from Delphi, building an AI chatbot, building an AI agent and the MCP client. The component landing pages are Delphi OpenAI client and Anthropic API.

This page is one of the Delphi use cases, each of which takes a single job end to end. The others so far are signing a user in with OAuth2 and PKCE and connecting two applications peer to peer with WebRTC.

Frequently Asked Questions

Create a TsgcAI_Chat from the unit sgcAI_Chat, set Provider to the vendor you want, set ChatOptions.ApiKey and ChatOptions.Model, then call Chat('your prompt'), which returns the answer as a string. If you would rather talk to one vendor API directly, use TsgcHTTP_API_OpenAI, TsgcHTTP_API_Anthropic or TsgcHTTP_API_Ollama and call _CreateChatCompletion or _CreateMessage. Both approaches ship in sgcWebSockets and in the standalone sgcAI package, and they work from Delphi 7 through RAD Studio 13.
Call ChatStream rather than Chat, and handle OnChatStream, which fires with aChunk, the decoded text of each delta, and a Cancel flag you can set to stop early. At the REST client level, assign OnHTTPAPISSE and call _CreateMessageStream on the Claude and Ollama clients, or set Stream to True on a TsgcOpenAIClass_Request_ChatCompletion. That event gives you the raw Server-Sent Event name and data, with partial network reads already reassembled.
Yes. Install Ollama, pull a model, then point TsgcHTTP_API_Ollama at it. OllamaOptions.Host already defaults to http://localhost:11434, so on a default install you only choose a model and call _CreateMessage. Nothing leaves the machine, no API key is needed and the application works offline. Through TsgcAIChat the same server is reached by setting Provider to aicpOllama and, when the server is not on localhost, ChatOptions.BaseUrl.
Describe the function with a JSON Schema and attach it to the request. On Claude, add a TsgcAnthropicClass_Request_Tool with Name, Description and InputSchema to the Tools array of a TsgcAnthropicClass_Request_Messages. The reply then contains a content block whose ContentType is tool_use, carrying Name, Input and Id, and you answer with a tool_result block quoting the same ToolUseId. On OpenAI, put the definitions in the Tools property of a TsgcOpenAIClass_Request_ChatCompletion and read the requested calls from Choices[0]._Message.ToolCalls.
The AI and LLM clients are an Enterprise feature of sgcWebSockets. They are not included in the Standard or Professional editions. If you do not need the rest of sgcWebSockets, the standalone sgcAI package contains the same components together with the runtime they need. The library supports Delphi 7 through RAD Studio 13 and the matching C++ Builder versions. On platforms, the REST clients TsgcHTTP_API_OpenAI, TsgcHTTP_API_Anthropic and TsgcHTTP_API_Ollama compile for Windows, macOS, Linux, iOS and Android, while TsgcAIChat is compiled for Windows only, Win32 and Win64. The MCP client and server are not restricted to Windows either.
For hosted providers, yes. The components are REST clients, so you bring a key from your own OpenAI, Anthropic, Google, xAI, DeepSeek or Mistral account and assign it to ChatOptions.ApiKey, or to the vendor options such as OpenAIOptions.ApiKey. Usage is billed by that vendor against your key, and you can track it per response through Usage.PromptTokens, Usage.CompletionTokens and Usage.TotalTokens. Ollama needs no key, because the model runs locally.
Chat and ChatStream are synchronous. On Delphi 2010 and later, call ChatAsync instead: it runs the request on a worker thread and returns an IsgcFuture of string, so you chain ThenProc to receive the answer, OnError to receive an exception and Cancel to abandon a request in flight. The ThenProc callback is dispatched on the main thread, so it is safe to update the UI from it. Also raise HttpOptions.ReadTimeout, because a long generation can outlive a default read timeout.
Yes. Set OpenAIOptions.Provider to oapvAzure and fill AzureOptions.ResourceName, AzureOptions.DeploymentId and AzureOptions.APIVersion. The client then targets your Azure deployment, and the calls you already wrote stay exactly the same.
Best value: All-AccessEvery eSeGeCe product, Premium Support included, from €1,059/year.
See All-Access pricing

Ready to call an LLM from your Delphi app?

Download the free trial and make your first call today.