Skip to content

Running the engine on a server ​

By default everything runs inside your application: the engine, the documentation index, the local HTTP server and — with a model provider — the API key, on the user's machine. That is the right answer for an internal application on managed PCs. It is the wrong one when:

  • the application is installed at customers: whoever owns the machine can recover the key;
  • it is mobile: a local server in-process is awkward and a key inside the package is extractable;
  • you need quotas, logs, or one index kept up to date in a single place.

The HelpChatAI server is the other half: the same engine, the same page, the same endpoints, serving many clients.

Starting a server ​

HelpChatAIServerApp --key demo-key --provider claude --index help-index.json --port 8080
HelpChatAIServerApp --write-config helpchatai-server.json     # a commented starting point

helpchatai-server.json holds everything:

json
{
  "Port": 8080,
  "BindIP": "0.0.0.0",
  "Keys": [ "the-key-of-my-application" ],
  "Sessions": { "Storage": "memory", "Folder": "sessions", "IdleMinutes": 120, "Max": 500 },
  "Provider": "claude",
  "Language": "it",
  "DocIndexFile": "help-index.json",
  "DocBaseUrl": "https://help.acme.com/manual/",
  "Claude": { "ApiKey": "sk-ant-...", "Model": "claude-haiku-4-5" },
  "OpenAI": { "ApiKey": "", "Model": "gpt-6-luna" },
  "Gemini": { "ApiKey": "", "Model": "gemini-3.8-flash" }
}

The server refuses to start without at least one application key: an open server spends your model budget for whoever finds it. /health answers with the version, the number of sessions, the provider and the size of the index.

Everything it does lives in THelpChatRemoteServer (unit HelpChatAI.Server.Remote), so you can host the same server inside an application you already have instead of running the console one.

Pointing a client at it ​

A configuration change, no code:

json
{
  "Remote": {
    "BaseUrl": "https://help.acme.com",
    "AppKey": "the-key-of-my-application",
    "UserId": "",
    "UseHostChannel": true
  }
}

TVclHelpChatAI and TFmxHelpChatAI read Settings.Remote and build a remote transport instead of the local server. The component, the page, F1, the screen context and the events stay exactly the same; what changes is that this process no longer loads an index, starts a worker pool or holds a model key.

The session opens the first time the user asks for help, not at startup: an application whose help server is down still starts, and is told about it when someone presses F1.

Sessions and conversations ​

A client opens a session with POST /session presenting its application key and, when the application knows it, a UserId. A session is a conversation of the engine:

  • u:<user> when there is a user — so the same person continues where they left off, from any device;
  • s:<session> when there is not.

One engine serves everybody: the store keys the conversations, the worker pool is shared and bounded, the index is loaded once.

Sessions.Storage decides what happens when the last session on a conversation ends:

StorageBehaviour
memorythe conversation is forgotten
diskit is written to Sessions.Folder and read back when that user returns, server restart included

Idle sessions are swept after IdleMinutes.

Who may connect ​

The application key identifies the application, not the user, and grants nothing but the right to ask questions. To decide per user — against your own user store, your own tenants — handle OnAuthenticate on the server object:

pascal
FServer.OnAuthenticate :=
  procedure(Sender: TObject; const AAppKey, AUserId, AUserToken: string;
    var AAllowed: Boolean; var AConversationKey: string)
  begin
    AAllowed := MyUsers.CheckToken(AUserId, AUserToken);
    AConversationKey := 'tenant:' + MyUsers.TenantOf(AUserId) + ':' + AUserId;
  end;

How the page still reaches the application ​

Served from another origin, the panel could no longer ask the application to close it. The transport opens a small listener on 127.0.0.1 — the host channel — and tells the server where it is; the ✕ and Esc go there. CORS is restricted to the panel origin, the channel has its own secret, and the page is told when there is none (its ✕ disappears and your application closes the panel its own way).

That channel is also where file exchange between page and application will live.

Behind a reverse proxy ​

Mount the server at the root of a host or virtual host: the panel page is served from /. Remember that the clients and the page speak HTTP to it, so terminate TLS at the proxy.

What is not there yet ​

Actions do not work with a remote engine: the model runs on the server and the actions live in the client. The queue in the broker and the host channel are shaped for it, but the round trip is not built yet.

Released under the Apache License 2.0