Server-side models
Run the model on your server while the chart stays in the browser.
Most AI integrations call the model from a server while the chart runs in the browser. A remote session keeps the chart, its fences and its native owners in the browser and gives the server the same command facade and model tools.
// Browser
import { serveAutomation } from '@lumencharts/chart-automation/sdk';
const sdk = await chart.openAutomation({ policy: { effects: ['read', 'mutation'] } });
const server = serveAutomation(sdk, {
captureResult: async capture => ({ attachmentId: await upload(capture.blob) }),
});
myChannel.onCall(async (call, signal) => myChannel.reply(await server.handle(call, signal)));
startTurn({ chart: server.snapshot() });// Server
import { createAutomationTools, connectAutomation } from '@lumencharts/chart-automation/sdk';
const chart = connectAutomation(turn.chart, {
send: (call, signal) => myChannel.call(call, signal),
});
const tools = createAutomationTools(chart, { use: [budgets, auditLog], imageResult: toProviderImage });
const system = chart.instructions();Protocol
| Message | Shape |
|---|---|
| Snapshot | { version: 1, target, capabilities, instructions }, sent by the browser at the start of a turn and again when capabilities().revision changes (chart.update(snapshot)) |
| Call | { version: 1, requestId, target, name, commandVersion?, input, timeoutMs? } |
| Result | { version: 1, ok: true, value, target } or { version: 1, ok: false, code, target } |
name is a command name or one of data.series, data.read, data.requestHistory and capture.image. Timeouts are relative, so server and browser clocks need not agree; the remote session also enforces the snapshot's deadline ceiling locally. Results carry the chart's current target, so the server learns about a navigation and later calls are fenced to the new chart. A call built against a different commandVersion rejects with unsupported. Errors carry a chart error code and, for rejected input, optional details ({ path, reason }), never a message. parseAutomationRemoteResult validates and bounds details and drops anything else.
Capture is advertised only when the browser supplies captureResult, which turns the PNG into something your channel can carry; on the server, capture.image() and the capture_image tool resolve to that value.
Transport responsibilities
The SDK is transport-neutral. Your channel owns:
- delivery and reconnection;
- durable replay by
requestId: a repeated call returns the stored result instead of running twice; - rejecting a known
requestIdsent with different input; - forwarding cancellation through the
signalon both sides.
server.handle never rejects; it always resolves to a result. chart.destroy() aborts in-flight calls on the server side, and later calls reject with unavailable.