Reference for the ChatRequest Display action — the operator-facing chat surface for Local AI. Covers configuration, transcript persistence, hook attachment, and tool dispatch.
AI Integration → Local AI → ChatRequest Action Reference (10.1.5 draft)
10.1.5 draft. This page documents the |
ChatRequest is a Display Action type — the same dynamic kind as Click, HTTPRequest, SetValue. It runs when an operator interacts with a Display control (typically a Button) that has the action attached. The action sends the operator's typed query to the solution's Local AI endpoint, holds the per-Display-panel transcript across follow-up turns, dispatches tool calls the LLM may request, and writes the reply back to a tag the Display reads.
It is portable across Display targets — WPF (rich client) and OpenSilver (HTML5/browser) render and execute it identically.
On a Button (or any clickable control), add an Action dynamic with:
Field | Setting |
|---|---|
Action type |
|
Query | Tag whose value is the operator's typed query (typically a String tag bound to a TextBox). |
Return | Tag that receives the full reply envelope JSON. Recommended type: JSON, so the built-in tag-method surface ( |
Result 1, Result 2, ... (optional) | Tags whose values are computed from the reply via Expressions. Typically Result 1 is bound to a String tag holding the plain-text answer, with Expression |
The Action editor hides fields that don't apply to ChatRequest (the HTTP method picker, the Object selector, the Force-Change checkbox). Only Query, Return, and the Result/Expression rows surface.
Three tags, one Action, two controls. No scripting.
Tag | Type | Wired to |
|---|---|---|
| String | TextBox write binding |
| JSON | Action Return field |
| String | Result 1 with Expression |
Operator types into the TextBox, presses the Button. Within a few hundred milliseconds to a few seconds (model-dependent), the answer appears in the TextBlock. The full reply envelope (status, tool trace, latency, warnings) sits on the JSON tag and can be surfaced for diagnostics or hidden.
Each Display panel keeps its own conversation history with the model. Follow-up questions on the same panel retain context — the model sees the full transcript and can refer back to earlier turns. Default ON for new 10.1.5 solutions.
The transcript is keyed by the panel's ClientInfo.Guid — an internal per-TCP-connection identifier the platform assigns when a client connects. Two operators chatting on two panels get two independent transcripts. The same operator with two browser tabs gets two transcripts. The runtime Client.* namespace does not expose the Guid; it's an internal cache key.
When the calling user changes on the same panel (shift change, RBAC re-login), the transcript clears transparently before the next turn. Operator A's chat history is not visible to Operator B. The reset is lazy — it happens on the next chat turn after the user change, not on the user change itself.
To disable transcript persistence solution-wide, clear bit 0x80 (EnableChatHistory) on SolutionSettings.ModelOptions. Every ChatRequest call then behaves atomically — the LLM sees only the current turn, with no prior context. The atomic TK.AIExecute script API always bypasses the cache regardless of this bit.
When tool-category bits are enabled in SolutionSettings.ModelOptions, the LLM may decide during a chat turn to call platform tools that read tag values, browse the namespace, query alarms, or query historian data. The platform dispatches these in-process against the running solution and feeds the results back to the LLM, which composes a final answer. The full sequence — every tool name, arguments, result, and timing — appears in the reply envelope's toolTrace[] array.
Bit | Tools enabled |
|---|---|
| Required for ANY tool to be exposed. Off → no tools, even if sub-bits are on. |
| Read tag values, browse the UNS, search the namespace, get object context. |
| Read active alarms, query alarm history. |
| Query historian time-series data. |
| Call solution-authored MCP Tool class methods. |
See Local AI Configuration (10.1.5 draft) for the complete bit reference.
To prevent runaway tool loops, the platform caps each chat turn at:
When the cap trips, the platform gives the LLM one final round (no new tools) to compose a terminal reply from the tools it has seen, and returns status="truncated" if the LLM still does not produce a terminal answer.
Server-domain code can attach handlers to two hooks that fire on every ChatRequest turn:
OnBeforeChat — fires before the LLM POST. Receives the query JSON; may return modified query JSON. Use for redaction, audit-logging, rate-limiting, or rewriting the query before it leaves the platform.OnAfterChatReply — fires after the reply envelope is built, before it returns to the caller. Receives the reply JSON; may return modified reply JSON. Use for post-processing, additional audit, or in-place rewriting.Both hooks are multicast — multiple handlers may attach. A throwing handler is caught and logged into the reply's warnings[] with the handler's identity; the chain continues with the un-mutated input. Hook failures do NOT flip status to error — the LLM call still proceeds.
Atomic TK.AIExecute calls do NOT raise these hooks. Hooks are scoped to the operator-chat surface specifically — a TK.AIExecute call from an alarm-describer or a report generator should not fire chat-redaction or chat-audit handlers, because the contexts and policies are different.
In FrameworX 10.1.5, hook attachment uses reflection from a Server-domain script (typically a Server.Class method invoked at solution startup). A first-class binding surface (@Solution.AI.OnBeforeChat += handler) is post-10.1.5 work.
// In a Server.Class method invoked at solution startup:
public void AttachChatHooks()
{
var aiType = T.Library.TAssembly.GetType(null, "T.LocalAI.LocalAIService");
var getMethod = aiType.GetMethod("Get",
BindingFlags.NonPublic | BindingFlags.Static,
null, new[] { typeof(ObjectServer) }, null);
var instance = getMethod.Invoke(null, new object[] { TK.ObjServer });
var eventInfo = aiType.GetEvent("OnBeforeChat",
BindingFlags.NonPublic | BindingFlags.Instance);
Func<string, Task<string>> handler = async json =>
{
// Customer logic: read query JSON, mutate, return new JSON.
// Throwing is safe — the chain continues with the un-mutated input.
return json;
};
eventInfo.AddEventHandler(instance, handler);
} |
The handler must execute in a server-domain context — TK.ObjServer must be the server-side ObjectServer. Display CodeBehind cannot attach handlers (those scripts execute at the client).
ChatRequest applies the gates in this fixed order on every call:
SolutionSettings.ModelEnabled = true — kill-switch must be ON. Off returns status="disabled".SolutionSettings.ModelOptions bit 0x02 (EnableRuntimeMCP) — master tool-surface bit. Off returns status="disabled".See Local AI Configuration (10.1.5 draft) for full gate semantics.
Same shape as TK.AIExecute — see Local AI Reply Envelope Schema (10.1.5 draft). Briefly:
{
"text": "<the LLM answer text or '' on non-ok status>",
"status": "ok | error | disabled | truncated",
"toolTrace": [ /* zero or more dispatched tools */ ],
"latencyMs": 1247,
"warnings": [ /* zero or more diagnostic strings */ ]
} |
Phase | Typical latency |
|---|---|
First call after startup or after the model's keep-alive expires | ~15 seconds (model loads from disk into RAM) |
Subsequent calls (model resident) | ~500 ms on a typical CPU; faster with a GPU |
Tool-equipped chat turn | Add ~1–3 seconds per dispatched tool round-trip; the LLM may dispatch zero, one, or several tools per turn |
For the cold-start cost, the recommended workaround is setting OLLAMA_KEEP_ALIVE=24h in the environment of the Ollama host so the model stays resident.
An operator chat panel that exposes the answer text plus a small "details" line showing latency and status:
Tag | Type | Wired to |
|---|---|---|
| String | TextBox write |
| JSON | Action Return |
| String | Result 1, Expression |
| String | Result 2, Expression |
| Long | Result 3, Expression |
No scripting needed; everything is wired in the Action editor and Display Expressions.
status set to error, disabled, or truncated.