Architectural reference for the Local AI capability in FrameworX 10.1.5: the two-path execution model, code organization, master-gate semantics, transcript cache mechanics, tool dispatch loop, and integration points with the rest of the platform.
Technical Reference → Platform Architecture Reference → Local AI Architecture Reference (10.1.5 draft)
10.1.5 draft. This page documents Local AI internals as they ship in FrameworX 10.1.5. It is the architecture map — for task-oriented quick-starts and end-user reference, see Local AI. Content may change before GA.
This page is for system integrators, custom-driver authors, OEM partners, and engineers who need to reason about how Local AI is composed inside the FrameworX runtime: which assembly hosts what, which thread runs which work, where gates apply, and which contracts must be honoured when extending or wrapping Local AI.
End users configuring Local AI from the Designer or wiring a chat panel from a Display do not need this page. See Local AI and the user-facing reference children there.
Local AI exposes one platform capability — calling an OpenAI-compatible LLM from inside a FrameworX solution — through two architecturally independent consumer paths:
Path | Surface | Caller context | Stateful? | Tools? | Hooks? |
|---|---|---|---|---|---|
Cached |
| Operator click → ServiceClient TCP → Kernel TCPServer dispatch → Local AI | Per-Display-panel transcript cache | Yes (UNS / Alarm / Historian / custom) | Yes ( |
Atomic |
| Server-domain script (Server.Class, Script.Task, alarm callback, report generator) → Toolkit static | Stateless — every call independent | No (single POST, no tools) | No |
Both paths POST to the same OpenAI-compatible endpoint, configured once per solution on three columns of SolutionSettings. Both paths return the same reply envelope (see Local AI Reply Envelope Schema (10.1.5 draft)).
The two paths are deliberately independent code paths sharing a small set of explicit anchors. Code duplication is welcomed where the alternative would be a shared helper that couples them.
SolutionSettings.ModelSettings JSON via the same defensive accessors on the Local AI service singleton.SolutionSettings.ModelEnabled at entry and short-circuit identically when off.Security.CheckAndResolveSecretCommConfiguration on resolved auth strings; this is a Kernel-side platform service, not a path-specific helper.text, status, toolTrace, latencyMs, warnings).ReportWebData.DecodeAuthorizationHeader static, which is the single source of truth for the None / BearerToken / BasicAuth / CustomAuth wire format.Concern | Cached path | Atomic path |
|---|---|---|
Implementation class | Sealed orchestrator owning the per-Display-panel transcript | Separate sealed class — no shared methods, no shared state |
HTTP-POST helper | Inline in the cached orchestrator | Inline in the atomic class — separate copy, even if today the bodies look similar, so divergence as features grow stays isolated |
Master tool-surface bit ( | Required — off → | Not consulted — atomic has no tools to gate |
Per-category tool bits ( | AND-ed against master to assemble the tool catalog | Not consulted |
Hook chain | Raises | Does NOT raise hooks — chat-attached behaviour does not apply |
Transcript cache | Per- | None |
Tool dispatch loop | Bounded by per-turn cap and 60-second wall-clock budget | Single POST, no loop |
Caller wire signature | Receives | Receives |
Local AI is implemented in its own peer assembly, T.LocalAI.dll, sibling to T.Modules.dll and T.Toolkit.dll. It is NOT inside T.Modules.dll.
The placement is governed by a platform rule: code lives in T.Modules.dll if and only if it contributes to the runtime Object Model — Tag namespaces, eObjType registration, ListObj / RunObj entries, user-visible runtime addressability. Local AI has zero Object Model contribution: TK.AIExecute (a Toolkit static) and the Display ChatRequest action both consume the Object Model — they read tags, dispatch runtime_* tools — but neither contributes a Tag namespace or a runtime root. Therefore Local AI is a peer infrastructure assembly, not a runtime Module.
This explains why there is no AI namespace in the runtime Object Model in 10.1.5 — Local AI is reachable only as TK.AIExecute(...) from script and as the ChatRequest Display action. A future @Solution.AI.* binding surface is post-10.1.5 work.
Assembly | Depends on | Consumed by |
|---|---|---|
|
|
|
The build position is between Kernel/ProjectServer and Toolkit. The Toolkit consumer (TK_AI.cs) takes a compile-time project reference to T.LocalAI.dll — there is no reflection-by-name on this binding.
Two callers reach the Local AI service entry methods. Both are compile-time-checked under the prescribed code organization, so signature changes surface as build errors rather than runtime nulls.
Caller | Binding | Calls |
|---|---|---|
Kernel-side TCP service handler (cached path) | Compile-time reference inside |
|
Toolkit static | Compile-time reference via project reference on |
|
The Local AI service singleton, the cached-path orchestrator, and the atomic-path class are all internal sealed. The single public static surface is the defaults class, which exposes the recommended endpoint constants (URL, model name, etc.) for the Designer-side configuration dialog to seed its 5-field editor.
Local AI HTTP traffic is server-side only. Both paths execute their LLM POST inside the TServer process; clients never directly contact the LLM endpoint.
Caller surface | Process where LLM POST happens | Thread |
|---|---|---|
| TServer (TCP service handler dispatch) | TCPServer dispatch thread for the inbound ServiceClient call |
| TServer | Caller's thread (script execution thread); sync wrapper unwraps async via |
| ScriptTaskServer.exe (a server-domain process) | Task execution thread |
| ReportServer.exe | Report-rendering thread |
The cached path is reachable only through the Kernel-side TCP service handler — by construction, that handler runs in the TServer process. A misrouted client-side caller cannot land in ExecuteChatAsync.
The atomic path is reachable from a Toolkit static, so technically a Display CodeBehind script (which runs at the client) could call TK.AIExecute. The platform does not block this with a runtime gate, because three platform-level safety nets already defend against degraded execution at the client:
Security.CheckAndResolveSecretCommConfiguration returns the unresolved literal /secret:<Name> token when not running server-side. The LLM endpoint then receives the literal token as the credential and rejects with 401.SolutionSettings reads route through ObjServer.DB. Some client-side configurations have a stripped-config DB; reads fall back to defaults targeting localhost:11434 — unreachable from a remote browser.ModelEnabled is off, the call short-circuits with status="disabled" before any HTTP work.Together these mean a misrouted call from a thin client never silently succeeds with leaked credentials — it returns a normal HTTP-error envelope or the master-gate disabled envelope. No runtime view-client predicate is required.
Both paths apply gates in a fixed sequence at entry to the service method. The order is non-negotiable.
Order | Gate | Cached path | Atomic path | Outcome when gate is closed |
|---|---|---|---|---|
1 |
| Yes | Yes |
|
2 |
| Yes | Skipped | Cached: |
3 | Per-category sub-bits ( | AND-ed with master to build tool catalog | Skipped | Tool category absent from the catalog the LLM sees |
The asymmetry on gate 2 is intentional. ModelOptions exists to gate the LLM's tool surface; the atomic path exposes no tools, so the bit is meaningless to it. Gating the atomic path on 0x02 would prevent customers from using TK.AIExecute for pure-language tasks (translation, summary, rephrase) while reserving tool-equipped chat for selected operator panels — a legitimate posture.
The cached orchestrator maintains a per-solution ConcurrentDictionary<string, ChatSession> keyed by ClientInfo.Guid — the platform's per-TCP-connection identifier (the same key shape ModuleHistorian and ServiceSyncObjects use for their own per-connection bookkeeping).
Property | Behaviour |
|---|---|
Cache key |
|
Cache participation | Gated by |
Login reset | Lazy. The orchestrator records the last-seen |
Transcript truncation | Per-cache-entry, truncated to a fixed maximum number of messages calibrated for ~8B-class context windows. Older messages drop oldest-first. |
Cache lifetime | Per-process. Restarting TServer clears all transcripts. There is no persistence to disk in 10.1.5. |
When the master tool bit is on and at least one category sub-bit is on, each chat turn assembles a tool catalog and may enter a tool-dispatch loop:
messages + tools to the LLM endpoint.tool_calls, dispatch each tool in-process against the live ObjectServer (no HTTP loopback to RuntimeMCP — sub-millisecond direct dispatch).tool_calls).The loop is bounded by two limits:
status="truncated" if it does not.OnBeforeChat and OnAfterChatReply are event Func<string, Task<string>> on the per-ObjectServer Local AI service singleton. Multicast; throwing handlers caught and logged into the reply's warnings[] with handler identity; chain proceeds with the un-mutated input. Hook failures do NOT flip the envelope's status away from ok — that's reserved for chain-aborting failures (LLM HTTP error, malformed query, etc.).
See Local AI Developer Reference (10.1.5 draft) for the reflection-attach pattern and worked hook examples.
The atomic path is deliberately simple. After the master ModelEnabled gate:
system / user / context / metadata fields).ModelSettings via the same defensive accessors as the cached path./secret:<Name> tokens via the platform's secret resolver.{Tag.X} expressions in URL via the platform's expression resolver.ReportWebData.DecodeAuthorizationHeader.messages[] array — no transcript prefix, no prior turns.tools[], 60-second wall-clock CancellationTokenSource, no loop.choices[0].message.content. Build the SPEC envelope (toolTrace=[] always for atomic).No hooks. No transcript. No tool dispatch. No retry. Every step lives in the atomic class; nothing calls into the cached orchestrator.
SolutionSettingsLocal AI uses three pre-existing scaffolding columns. No schema migration; no per-solution row creation; no AI-specific table.
Column | Type | Role |
|---|---|---|
| Boolean | Master kill-switch. Off short-circuits both paths. |
| String (JSON blob) | Five-key endpoint configuration: |
| Int (bitmask) | Master tool bit and per-category sub-bits. Shared bitmask with the AI Runtime Connector and the AI Designer connector. |
Every Local AI call re-parses ModelSettings JSON on every read. There is no caching layer with invalidation. The parse cost is negligible compared to the LLM round-trip; the alternative (cached parsed values + invalidation on column change) introduces a stale-cache bug surface that buys nothing measurable. Reads of NULL / empty / malformed JSON / missing keys / empty values transparently fall back to the defaults class.
The Designer's 5-field editor preserves unknown JSON keys via a capture/replay mechanism, so future SPEC extensions to ModelSettings (e.g., a Provider dialect enum) survive an open/OK round-trip in the editor without being stripped.
Local AI uses the same SecuritySecrets pattern that protocol drivers, the WebData connector, the UNS tag-provider service, and the Devices module use for credentials. There is no AI-specific secret mechanism.
Aspect | How Local AI uses it |
|---|---|
Reference syntax |
|
Resolver |
|
Off-server behaviour | Resolver short-circuits when not |
Encryption | SecretValue is encrypted at rest. Field is write-only via the Designer Security UI; redacted on read-back. |
For the customer-facing walk-through and worked examples, see SecuritySecrets Authentication for Local AI (10.1.5 draft).
The reply envelope is a strict five-field shape, identical for both paths and identical across success / error / disabled / truncated outcomes:
Error rendering macro 'code': Invalid value specified for parameter 'com.atlassian.confluence.ext.code.render.InvalidValueException'{
"text": "<answer text or '' on non-ok status>",
"status": "ok | error | disabled | truncated",
"toolTrace": [ ... ],
"latencyMs": 1247,
"warnings": [ ... ]
}Architectural guarantees:
JObject.Parse(reply) never throws on a Local AI reply.latencyMs always present — including on errors. Pre-condition errors (gates) report 0; post-condition errors report actual elapsed time.status="error" or status="disabled" and a populated warnings[].Full schema and worked examples: Local AI Reply Envelope Schema (10.1.5 draft).
Service | How Local AI uses it |
|---|---|
ObjectServer (per-host runtime) | Local AI service is per- |
Security (SecuritySecrets resolver) | Token resolution at call time, server-side only. |
SolutionSettings (configuration) | Three columns carry all configuration; no AI-specific table. |
Kernel TCPServer (cached path entry) | Hosts the |
Toolkit (atomic path entry) |
|
WebData connector | Reuses |
RuntimeMCP tool catalog | Cached path's tool catalog mirrors the same |
AsyncHelpers | Both paths use |
Not added in 10.1.5 | Why |
|---|---|
An | The 12-root taxonomy ( |
A | Local AI does not contribute Object Model namespace surface; it is a peer infrastructure assembly, not a runtime Module. |
New | The three existing columns ( |
A provider-dialect enum (OpenAI / Ollama / Anthropic) | The dialect is implicit (OpenAI-compatible JSON). Adding a discriminator waits for a real customer needing a non-conforming endpoint. |
Streaming output | Sync request/response only. Each call returns one complete envelope when the model finishes. |
A bundled LLM runtime or model weights | Customers install Ollama themselves and pull the model from the official registry. Keeps the FX installer lean and avoids redistribution licensing friction. |
HTTP loopback to RuntimeMCP for tool dispatch | Tool dispatch is in-process direct against the live |
Atomic-path hooks or atomic-path tools | Atomic is a deliberately stateless one-shot. A customer wanting hooks or tools should use the |
ModelSettings JSON or as new bits in ModelOptions.T.LocalAI.dll assembly home is stable for 10.1.5; later releases may split or rename internal types. The compile-time entry surface (ExecuteChatAsync, ExecuteAtomicAsync) is signature-stable.