@dbx-tools/appkit-mastra 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,104 +1,156 @@
1
1
  /**
2
- * Chart-rendering primitives.
2
+ * Chart planner + chart cache.
3
3
  *
4
- * Two surfaces, one shared brain:
4
+ * Self-contained chart subsystem with two layers:
5
5
  *
6
- * - {@link runChartPlanner}: the chart-planner Agent + ECOption
7
- * expansion as a plain async function. Takes a dataset and
8
- * returns a promise that resolves to a full `EChartsOption`
9
- * JSON plus the chosen `chartType`. No background work, no
10
- * writer side-effects, no id allocation - callers stitch the
11
- * result into whatever shape their producer needs.
6
+ * 1. Inner planner agent (private). Pure dataset-in /
7
+ * `EChartsOption`-out brain. Driven by {@link prepareChart};
8
+ * callers never instantiate it directly.
9
+ * 2. {@link prepareChart}: orchestration on top of the planner.
10
+ * Mints a `chartId`, caches an empty `{ chartId }` record
11
+ * synchronously, then resolves the dataset and runs the
12
+ * planner in the background. The terminal entry settles with
13
+ * either `result` (success) or `error` (failure). Both
14
+ * undefined means the entry is still processing.
12
15
  *
13
- * - {@link buildRenderDataTool}: a Mastra tool the model calls
14
- * ("here is a dataset, render it as a chart"). Mints a short
15
- * `chartId`, `await`s {@link runChartPlanner} so the planner
16
- * latency is attributed to this tool's trace span, emits one
17
- * `type: "chart"` writer event carrying the dataset + resolved
18
- * `option`, and returns `{ chartId }` to the model. The
19
- * LLM-bound output stays flat regardless of dataset size.
16
+ * The cache surface ({@link fetchChart}) is the only state the
17
+ * HTTP route and the chart-producing tools share. `prepareChart`
18
+ * is dataset-agnostic - callers supply a `resolveData` callback
19
+ * that fetches the rows however they like (Genie statement, inline
20
+ * dataset, custom API). The module has no knowledge of Genie or
21
+ * statement ids; those concerns live in the tools that wrap it.
20
22
  *
21
- * The model wires the chart into its reply by emitting the marker
22
- * `[[chart:<chartId>]]` on its own line in markdown. The chat
23
- * client splits the assistant text on these markers and drops a
24
- * `<ChartSlot>` in at the position the model placed it. The slot
25
- * resolves directly to the rendered Echarts visualisation - no
26
- * skeleton state, because the option is in the same event as the
27
- * dataset.
23
+ * Wire-format schemas (`ChartSchema`, `ChartResultSchema`,
24
+ * {@link ChartTypeSchema}) live in
25
+ * `@dbx-tools/appkit-mastra-shared` so the demo client and any
26
+ * other UI consumer share the exact same shape this module reads
27
+ * and writes.
28
+ *
29
+ * Public surface (everything else is module-private):
30
+ * - {@link chartPlannerRequestSchema} / {@link ChartPlannerRequest}
31
+ * - {@link prepareChart} / {@link PrepareChartOptions}
32
+ * - {@link fetchChart} / {@link FetchChartOptions}
33
+ * - {@link buildRenderDataTool} (the `render_data` Mastra tool
34
+ * auto-wired on every agent in `agents.ts`)
28
35
  */
36
+ import { type Chart } from "@dbx-tools/appkit-mastra-shared";
29
37
  import type { RequestContext } from "@mastra/core/request-context";
30
38
  import { z } from "zod";
31
39
  import type { MastraPluginConfig } from "./config.js";
32
40
  /**
33
- * Compact, model-friendly representation of an Echarts spec. The
34
- * planner agent emits this; {@link planToEchartsOption} expands it
35
- * into a real `EChartsOption` JSON. Two layers because letting the
36
- * model fill in a fully-typed `EChartsOption` is brittle (hundreds
37
- * of optional fields, deep unions, version-dependent shapes). A
38
- * small "chart plan" schema is much more reliable for a fast model
39
- * and keeps animation / tooltip / styling defaults consistent
40
- * across charts.
41
+ * Canonical planner input shape. Tools that source rows from an
42
+ * inline dataset (`render_data`) use it as their `inputSchema`
43
+ * verbatim; tools that resolve rows from a remote (`prepare_chart`
44
+ * over a Genie statement) `omit({ data })` and `extend` with their
45
+ * own identifier field, so the field-level `.describe()` text
46
+ * stays a single source of truth. Server-only - the UI never
47
+ * sees a planner request, only the resolved {@link Chart}.
41
48
  */
42
- declare const chartPlanSchema: z.ZodObject<{
43
- chartType: z.ZodEnum<{
44
- bar: "bar";
45
- line: "line";
46
- area: "area";
47
- scatter: "scatter";
48
- pie: "pie";
49
- }>;
50
- title: z.ZodOptional<z.ZodString>;
51
- xAxisLabel: z.ZodOptional<z.ZodString>;
52
- yAxisLabel: z.ZodOptional<z.ZodString>;
53
- categories: z.ZodOptional<z.ZodArray<z.ZodString>>;
54
- series: z.ZodArray<z.ZodObject<{
55
- name: z.ZodString;
56
- data: z.ZodArray<z.ZodCatch<z.ZodPreprocess<z.ZodUnion<readonly [z.ZodNumber, z.ZodNull, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, z.ZodObject<{
57
- name: z.ZodString;
58
- value: z.ZodNumber;
59
- }, z.core.$strip>]>>>>;
60
- }, z.core.$strip>>;
49
+ export declare const chartPlannerRequestSchema: z.ZodObject<{
50
+ title: z.ZodString;
51
+ description: z.ZodOptional<z.ZodString>;
52
+ data: z.ZodReadonly<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
61
53
  }, z.core.$strip>;
62
- type ChartPlan = z.infer<typeof chartPlanSchema>;
63
- /** Inputs to {@link runChartPlanner}. */
64
- export interface RunChartPlannerOptions {
54
+ export type ChartPlannerRequest = z.infer<typeof chartPlannerRequestSchema>;
55
+ /** Inputs to {@link prepareChart}. */
56
+ export interface PrepareChartOptions {
57
+ /** Plugin config; resolves the planner agent's model. */
65
58
  config: MastraPluginConfig;
66
- requestContext?: RequestContext;
67
- title: string;
59
+ /** Display title forwarded to the planner agent. */
60
+ title?: string;
61
+ /** Optional intent hint forwarded to the planner agent. */
68
62
  description?: string;
69
- data: ReadonlyArray<Record<string, unknown>>;
70
63
  /**
71
- * Cooperative cancellation. Forwarded to the planner agent's
72
- * `generate({ abortSignal })` call so concurrent renders can be
73
- * aborted as a group when the parent Genie agent's signal fires.
64
+ * Resolves the rows to chart. Called once, in the background.
65
+ * Any thrown error lands in the cache as the entry's `error`
66
+ * field (never propagated to the caller of {@link prepareChart}).
67
+ * An empty `rows` array is rejected as `"dataset has no rows;
68
+ * nothing to chart"`.
69
+ */
70
+ resolveData: (signal?: AbortSignal) => Promise<{
71
+ rows: ReadonlyArray<Record<string, unknown>>;
72
+ }>;
73
+ /**
74
+ * Per-request `RequestContext`. Forwarded to the planner agent so
75
+ * user-scoped model resolution (OBO) stays in effect.
76
+ */
77
+ requestContext?: RequestContext;
78
+ /**
79
+ * Cooperative cancellation. Forwarded to `resolveData` and the
80
+ * planner agent. Note: the chart task continues running in the
81
+ * background after the parent request ends, so external abort
82
+ * signals are best-effort; typical use is to leave this unset
83
+ * and let the 1h TTL cap stale entries.
74
84
  */
75
85
  signal?: AbortSignal;
76
86
  }
77
- /** Output of {@link runChartPlanner}: a fully-formed Echarts spec. */
78
- export interface RunChartPlannerResult {
79
- option: Record<string, unknown>;
80
- chartType: ChartPlan["chartType"];
87
+ /**
88
+ * Mint a `chartId`, cache an empty `{ chartId }` placeholder
89
+ * synchronously, and kick off a background task that resolves the
90
+ * dataset and runs the planner. Returns the `chartId` once the
91
+ * placeholder lands so the first {@link fetchChart} call always
92
+ * sees an entry (no spurious 404 race).
93
+ *
94
+ * The background task swallows its own failures and writes them
95
+ * as `error` entries, so callers never see a rejected promise.
96
+ * Cache state machine:
97
+ *
98
+ * - just after this call returns: `{ chartId }` (processing)
99
+ * - on planner success: `{ chartId, result }`
100
+ * - on data / planner failure: `{ chartId, error }`
101
+ */
102
+ export declare function prepareChart(opts: PrepareChartOptions): Promise<{
103
+ chartId: string;
104
+ }>;
105
+ /** Inputs to {@link fetchChart}. */
106
+ export interface FetchChartOptions {
107
+ /**
108
+ * Server-side polling budget in ms. When the entry stays in
109
+ * the processing state past this window, the helper returns the
110
+ * last seen value (still processing) so the client can re-poll.
111
+ * Defaults to {@link DEFAULT_FETCH_TIMEOUT_MS} (60s).
112
+ */
113
+ timeoutMs?: number;
114
+ /**
115
+ * Poll interval in ms. Defaults to
116
+ * {@link DEFAULT_FETCH_INTERVAL_MS} (250ms).
117
+ */
118
+ intervalMs?: number;
119
+ /** External cancellation handle (e.g. request `req.signal`). */
120
+ signal?: AbortSignal;
81
121
  }
82
122
  /**
83
- * Run the chart planner against the given dataset and return a
84
- * full Echarts `EChartsOption` JSON. Pure async function: no
85
- * writer side-effects, no id minting, no background work.
86
- * Producers (the `render_data` tool, the Genie agent,
87
- * anything else that needs a chart) await this and stitch the
88
- * result into whatever shape their wire contract needs.
123
+ * Long-poll the chart cache until the entry settles (`result` or
124
+ * `error` set), the entry is missing, or the server-side timeout
125
+ * elapses.
126
+ *
127
+ * Returns:
128
+ * - the resolved {@link Chart} when it settled, errored, or
129
+ * stayed in processing past `timeoutMs` (so the client can
130
+ * re-poll);
131
+ * - `undefined` when the entry is missing or expired (the
132
+ * consumer should treat as 404).
133
+ *
134
+ * `signal` lets the caller cancel ahead of timeout (e.g. the HTTP
135
+ * request closed). Cancellation propagates to the inter-poll sleep
136
+ * so the helper returns immediately.
89
137
  */
90
- export declare function runChartPlanner(opts: RunChartPlannerOptions): Promise<RunChartPlannerResult>;
138
+ export declare function fetchChart(chartId: string, options?: FetchChartOptions): Promise<Chart | undefined>;
91
139
  /**
92
- * Build the `render_data` tool bound to the given plugin config.
140
+ * Build the `render_data` Mastra tool bound to the given plugin
141
+ * config. Auto-wired as a system tool on every agent (see
142
+ * `agents.ts`); per-agent tools can shadow it by registering a
143
+ * same-named entry.
144
+ *
145
+ * Thin wrapper over {@link prepareChart} for callers that already
146
+ * have a dataset in hand. Mints a `chartId` synchronously, caches
147
+ * an empty placeholder, and kicks off the chart-planner in the
148
+ * background. Returns just the `chartId`; the host UI resolves
149
+ * `[chart:<chartId>]` markers by hitting the plugin's
150
+ * `/embed/chart/:id` route.
93
151
  *
94
- * The tool awaits {@link runChartPlanner} so the planner's
95
- * latency is attributed to this tool's trace span, then emits
96
- * one `type: "chart"` writer event carrying the dataset and the
97
- * resolved `EChartsOption`. The LLM-bound output is just
98
- * `{ chartId }` so the model's context stays flat regardless of
99
- * dataset size. Planner failures are caught and surfaced as a
100
- * `type: "error"` writer event so the slot can fall back to
101
- * "couldn't render chart" without taking the parent agent down.
152
+ * For Genie statement results, prefer the Genie agent's
153
+ * `prepare_chart` tool, which accepts a `statement_id` and
154
+ * resolves the rows lazily.
102
155
  */
103
156
  export declare function buildRenderDataTool(config: MastraPluginConfig): import("@mastra/core/tools").Tool<any, any, any, any, import("@mastra/core/tools").ToolExecutionContext<any, any, unknown>, "render_data", unknown>;
104
- export {};