@warlock.js/ai-panoptic 4.3.0
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.
- package/CHANGELOG.md +47 -0
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/cjs/index.cjs +1370 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/collector/collector.d.mts +19 -0
- package/esm/collector/collector.d.mts.map +1 -0
- package/esm/collector/collector.mjs +89 -0
- package/esm/collector/collector.mjs.map +1 -0
- package/esm/collector/extract-span-attributes.d.mts +29 -0
- package/esm/collector/extract-span-attributes.d.mts.map +1 -0
- package/esm/collector/extract-span-attributes.mjs +76 -0
- package/esm/collector/extract-span-attributes.mjs.map +1 -0
- package/esm/collector/index.d.mts +5 -0
- package/esm/collector/normalize-error.d.mts +27 -0
- package/esm/collector/normalize-error.d.mts.map +1 -0
- package/esm/collector/normalize-error.mjs +49 -0
- package/esm/collector/normalize-error.mjs.map +1 -0
- package/esm/collector/report-to-span.d.mts +31 -0
- package/esm/collector/report-to-span.d.mts.map +1 -0
- package/esm/collector/report-to-span.mjs +53 -0
- package/esm/collector/report-to-span.mjs.map +1 -0
- package/esm/collector/report-to-trace.d.mts +35 -0
- package/esm/collector/report-to-trace.d.mts.map +1 -0
- package/esm/collector/report-to-trace.mjs +53 -0
- package/esm/collector/report-to-trace.mjs.map +1 -0
- package/esm/contracts/collector.contract.d.mts +63 -0
- package/esm/contracts/collector.contract.d.mts.map +1 -0
- package/esm/contracts/exporter.contract.d.mts +76 -0
- package/esm/contracts/exporter.contract.d.mts.map +1 -0
- package/esm/contracts/index.d.mts +3 -0
- package/esm/contracts/trace.type.d.mts +122 -0
- package/esm/contracts/trace.type.d.mts.map +1 -0
- package/esm/exporters/console/console-exporter.d.mts +25 -0
- package/esm/exporters/console/console-exporter.d.mts.map +1 -0
- package/esm/exporters/console/console-exporter.mjs +79 -0
- package/esm/exporters/console/console-exporter.mjs.map +1 -0
- package/esm/exporters/console/console-exporter.type.d.mts +36 -0
- package/esm/exporters/console/console-exporter.type.d.mts.map +1 -0
- package/esm/exporters/console/format-span-line.d.mts +17 -0
- package/esm/exporters/console/format-span-line.d.mts.map +1 -0
- package/esm/exporters/console/format-span-line.mjs +41 -0
- package/esm/exporters/console/format-span-line.mjs.map +1 -0
- package/esm/exporters/console/index.d.mts +3 -0
- package/esm/exporters/file/file-exporter.d.mts +23 -0
- package/esm/exporters/file/file-exporter.d.mts.map +1 -0
- package/esm/exporters/file/file-exporter.mjs +93 -0
- package/esm/exporters/file/file-exporter.mjs.map +1 -0
- package/esm/exporters/file/file-exporter.type.d.mts +39 -0
- package/esm/exporters/file/file-exporter.type.d.mts.map +1 -0
- package/esm/exporters/file/index.d.mts +2 -0
- package/esm/exporters/index.d.mts +12 -0
- package/esm/exporters/index.mjs +12 -0
- package/esm/exporters/langfuse/index.d.mts +2 -0
- package/esm/exporters/langfuse/index.mjs +3 -0
- package/esm/exporters/langfuse/langfuse-exporter.d.mts +27 -0
- package/esm/exporters/langfuse/langfuse-exporter.d.mts.map +1 -0
- package/esm/exporters/langfuse/langfuse-exporter.mjs +158 -0
- package/esm/exporters/langfuse/langfuse-exporter.mjs.map +1 -0
- package/esm/exporters/langfuse/langfuse-exporter.type.d.mts +90 -0
- package/esm/exporters/langfuse/langfuse-exporter.type.d.mts.map +1 -0
- package/esm/exporters/otel/index.d.mts +2 -0
- package/esm/exporters/otel/index.mjs +3 -0
- package/esm/exporters/otel/otel-exporter.d.mts +29 -0
- package/esm/exporters/otel/otel-exporter.d.mts.map +1 -0
- package/esm/exporters/otel/otel-exporter.mjs +142 -0
- package/esm/exporters/otel/otel-exporter.mjs.map +1 -0
- package/esm/exporters/otel/otel-exporter.type.d.mts +39 -0
- package/esm/exporters/otel/otel-exporter.type.d.mts.map +1 -0
- package/esm/exporters/utils/gen-ai-attributes.d.mts +65 -0
- package/esm/exporters/utils/gen-ai-attributes.d.mts.map +1 -0
- package/esm/exporters/utils/gen-ai-attributes.mjs +88 -0
- package/esm/exporters/utils/gen-ai-attributes.mjs.map +1 -0
- package/esm/exporters/utils/index.d.mts +3 -0
- package/esm/exporters/utils/total-cost.d.mts +20 -0
- package/esm/exporters/utils/total-cost.d.mts.map +1 -0
- package/esm/exporters/utils/total-cost.mjs +23 -0
- package/esm/exporters/utils/total-cost.mjs.map +1 -0
- package/esm/exporters/utils/walk-spans.d.mts +18 -0
- package/esm/exporters/utils/walk-spans.d.mts.map +1 -0
- package/esm/exporters/utils/walk-spans.mjs +21 -0
- package/esm/exporters/utils/walk-spans.mjs.map +1 -0
- package/esm/index.d.mts +31 -0
- package/esm/index.mjs +22 -0
- package/esm/panoptic/index.d.mts +4 -0
- package/esm/panoptic/panoptic-middleware.d.mts +38 -0
- package/esm/panoptic/panoptic-middleware.d.mts.map +1 -0
- package/esm/panoptic/panoptic-middleware.mjs +68 -0
- package/esm/panoptic/panoptic-middleware.mjs.map +1 -0
- package/esm/panoptic/panoptic-target.type.d.mts +53 -0
- package/esm/panoptic/panoptic-target.type.d.mts.map +1 -0
- package/esm/panoptic/panoptic.d.mts +37 -0
- package/esm/panoptic/panoptic.d.mts.map +1 -0
- package/esm/panoptic/panoptic.mjs +134 -0
- package/esm/panoptic/panoptic.mjs.map +1 -0
- package/esm/panoptic/panoptic.type.d.mts +100 -0
- package/esm/panoptic/panoptic.type.d.mts.map +1 -0
- package/esm/store/in-memory-trace-store.d.mts +32 -0
- package/esm/store/in-memory-trace-store.d.mts.map +1 -0
- package/esm/store/in-memory-trace-store.mjs +134 -0
- package/esm/store/in-memory-trace-store.mjs.map +1 -0
- package/esm/store/index.d.mts +6 -0
- package/esm/store/index.mjs +5 -0
- package/esm/store/match-trace.d.mts +21 -0
- package/esm/store/match-trace.d.mts.map +1 -0
- package/esm/store/match-trace.mjs +44 -0
- package/esm/store/match-trace.mjs.map +1 -0
- package/esm/store/sum-usage.d.mts +34 -0
- package/esm/store/sum-usage.d.mts.map +1 -0
- package/esm/store/sum-usage.mjs +65 -0
- package/esm/store/sum-usage.mjs.map +1 -0
- package/esm/store/trace-aggregate.type.d.mts +51 -0
- package/esm/store/trace-aggregate.type.d.mts.map +1 -0
- package/esm/store/trace-query.type.d.mts +45 -0
- package/esm/store/trace-query.type.d.mts.map +1 -0
- package/esm/store/trace-store.contract.d.mts +55 -0
- package/esm/store/trace-store.contract.d.mts.map +1 -0
- package/llms-full.txt +450 -0
- package/llms.txt +11 -0
- package/package.json +40 -0
- package/skills/README.md +17 -0
- package/skills/export-traces/SKILL.md +152 -0
- package/skills/observe-with-panoptic/SKILL.md +128 -0
- package/skills/query-traces/SKILL.md +152 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
//#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic-middleware.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build an {@link AgentMiddleware} that feeds a collector from the
|
|
4
|
+
* `execute`- and `supervisor`-level hooks. An alternative wiring to event
|
|
5
|
+
* subscription for apps that already compose cross-cutting concerns
|
|
6
|
+
* through the agent middleware pipeline (`[cache, budget, guardrail,
|
|
7
|
+
* observability]`). Declaring both hook maps lets a single middleware
|
|
8
|
+
* object work uniformly on agents (which fire the `execute` map) and
|
|
9
|
+
* supervisors (which fire the `supervisor` map) — registering it on a
|
|
10
|
+
* supervisor would otherwise install and collect nothing silently.
|
|
11
|
+
*
|
|
12
|
+
* Both terminal paths are covered on each surface:
|
|
13
|
+
* - `after` — fires on a run that produced a result. A run can complete
|
|
14
|
+
* with `result.error` populated (the engine still calls `after`), so
|
|
15
|
+
* the report AND the envelope error are collected; the error type and
|
|
16
|
+
* message land on the root span.
|
|
17
|
+
* - `onError` — fires when the run threw before assembling a result. The
|
|
18
|
+
* error carries the partial result's report on its envelope; when
|
|
19
|
+
* present it is collected, with the error itself threaded onto the
|
|
20
|
+
* root span so failed runs still produce a trace.
|
|
21
|
+
*
|
|
22
|
+
* The hooks never return a value, so they never mutate the agent's /
|
|
23
|
+
* supervisor's result. The `collect` call is fire-and-forget relative to
|
|
24
|
+
* the run — the collector isolates exporter failures internally, and we
|
|
25
|
+
* additionally swallow any rejection here so an observability fault can
|
|
26
|
+
* never surface on the run's hot path.
|
|
27
|
+
*
|
|
28
|
+
* @param collector - the collector traces are fed into.
|
|
29
|
+
* @param name - stable middleware name (kebab-case). Defaults to
|
|
30
|
+
* `"panoptic"`.
|
|
31
|
+
*/
|
|
32
|
+
function createPanopticMiddleware(collector, name = "panoptic") {
|
|
33
|
+
const collectReport = (report, rootError) => {
|
|
34
|
+
if (!isReport(report)) return;
|
|
35
|
+
collector.collect(report, rootError).catch(() => {});
|
|
36
|
+
};
|
|
37
|
+
const onResult = (result) => {
|
|
38
|
+
collectReport(result.report, result.error);
|
|
39
|
+
};
|
|
40
|
+
const onError = (error) => {
|
|
41
|
+
collectReport(error.report, error);
|
|
42
|
+
};
|
|
43
|
+
const terminalHooks = {
|
|
44
|
+
after(_ctx, result) {
|
|
45
|
+
onResult(result);
|
|
46
|
+
},
|
|
47
|
+
onError(_ctx, error) {
|
|
48
|
+
onError(error);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
name,
|
|
53
|
+
execute: terminalHooks,
|
|
54
|
+
supervisor: terminalHooks
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Narrow an unknown value to a `BaseReport`-shaped object. Structural
|
|
59
|
+
* (checks the lineage fields the collector reads) so it accepts any
|
|
60
|
+
* primitive's report subtype without importing each concrete type.
|
|
61
|
+
*/
|
|
62
|
+
function isReport(value) {
|
|
63
|
+
return typeof value === "object" && value !== null && typeof value.runId === "string" && typeof value.rootRunId === "string";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { createPanopticMiddleware };
|
|
68
|
+
//# sourceMappingURL=panoptic-middleware.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panoptic-middleware.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic-middleware.ts"],"sourcesContent":["import type { AgentMiddleware } from \"@warlock.js/ai\";\nimport type { CollectorContract } from \"../contracts/collector.contract\";\n\n/**\n * Build an {@link AgentMiddleware} that feeds a collector from the\n * `execute`- and `supervisor`-level hooks. An alternative wiring to event\n * subscription for apps that already compose cross-cutting concerns\n * through the agent middleware pipeline (`[cache, budget, guardrail,\n * observability]`). Declaring both hook maps lets a single middleware\n * object work uniformly on agents (which fire the `execute` map) and\n * supervisors (which fire the `supervisor` map) — registering it on a\n * supervisor would otherwise install and collect nothing silently.\n *\n * Both terminal paths are covered on each surface:\n * - `after` — fires on a run that produced a result. A run can complete\n * with `result.error` populated (the engine still calls `after`), so\n * the report AND the envelope error are collected; the error type and\n * message land on the root span.\n * - `onError` — fires when the run threw before assembling a result. The\n * error carries the partial result's report on its envelope; when\n * present it is collected, with the error itself threaded onto the\n * root span so failed runs still produce a trace.\n *\n * The hooks never return a value, so they never mutate the agent's /\n * supervisor's result. The `collect` call is fire-and-forget relative to\n * the run — the collector isolates exporter failures internally, and we\n * additionally swallow any rejection here so an observability fault can\n * never surface on the run's hot path.\n *\n * @param collector - the collector traces are fed into.\n * @param name - stable middleware name (kebab-case). Defaults to\n * `\"panoptic\"`.\n */\nexport function createPanopticMiddleware(\n collector: CollectorContract,\n name = \"panoptic\",\n): AgentMiddleware {\n const collectReport = (report: unknown, rootError?: unknown): void => {\n if (!isReport(report)) {\n return;\n }\n\n void collector.collect(report, rootError).catch(() => {\n // Swallow — the collector already isolates exporter failures; this\n // guard keeps an observability fault off the run's hot path.\n });\n };\n\n const onResult = (result: unknown): void => {\n // A run can complete with `result.error` populated (`after` still\n // fires); thread that envelope error onto the root span.\n collectReport(\n (result as { report?: unknown }).report,\n (result as { error?: unknown }).error,\n );\n };\n\n const onError = (error: unknown): void => {\n // A failed run's report rides on the error envelope when the engine\n // built one before throwing; collect it so failures trace, threading\n // the error itself onto the root span.\n collectReport((error as { report?: unknown }).report, error);\n };\n\n const terminalHooks = {\n after(_ctx: unknown, result: unknown) {\n onResult(result);\n },\n onError(_ctx: unknown, error: unknown) {\n onError(error);\n },\n };\n\n return {\n name,\n execute: terminalHooks as AgentMiddleware[\"execute\"],\n supervisor: terminalHooks as AgentMiddleware[\"supervisor\"],\n };\n}\n\n/**\n * Narrow an unknown value to a `BaseReport`-shaped object. Structural\n * (checks the lineage fields the collector reads) so it accepts any\n * primitive's report subtype without importing each concrete type.\n */\nfunction isReport(value: unknown): value is import(\"@warlock.js/ai\").BaseReport {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { runId?: unknown }).runId === \"string\" &&\n typeof (value as { rootRunId?: unknown }).rootRunId === \"string\"\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,yBACd,WACA,OAAO,YACU;CACjB,MAAM,iBAAiB,QAAiB,cAA8B;EACpE,IAAI,CAAC,SAAS,MAAM,GAClB;EAGF,AAAK,UAAU,QAAQ,QAAQ,SAAS,CAAC,CAAC,YAAY,CAGtD,CAAC;CACH;CAEA,MAAM,YAAY,WAA0B;EAG1C,cACG,OAAgC,QAChC,OAA+B,KAClC;CACF;CAEA,MAAM,WAAW,UAAyB;EAIxC,cAAe,MAA+B,QAAQ,KAAK;CAC7D;CAEA,MAAM,gBAAgB;EACpB,MAAM,MAAe,QAAiB;GACpC,SAAS,MAAM;EACjB;EACA,QAAQ,MAAe,OAAgB;GACrC,QAAQ,KAAK;EACf;CACF;CAEA,OAAO;EACL;EACA,SAAS;EACT,YAAY;CACd;AACF;;;;;;AAOA,SAAS,SAAS,OAA8D;CAC9E,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA8B,UAAU,YAChD,OAAQ,MAAkC,cAAc;AAE5D"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { BaseReport, BaseResult } from "@warlock.js/ai";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic-target.type.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The minimal completed-event payload Panoptic reads off any primitive's
|
|
6
|
+
* terminal `*.completed` event. Every core `@warlock.js/ai` primitive
|
|
7
|
+
* (agent, workflow, supervisor) emits a `*.completed` event carrying the
|
|
8
|
+
* full `result` — and the result always carries a `report` tree — even
|
|
9
|
+
* when the run failed (`agent.error` / `workflow.error` fire first, then
|
|
10
|
+
* `*.completed` still fires). Reading the report off this single event is
|
|
11
|
+
* therefore enough to capture every run regardless of outcome.
|
|
12
|
+
*/
|
|
13
|
+
type CompletedEventPayload = {
|
|
14
|
+
/** The finalized result of the run; carries the full `report` tree. */result: BaseResult & {
|
|
15
|
+
report: BaseReport;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Structural shape of anything Panoptic can subscribe to — the
|
|
20
|
+
* `on(event, handler)` surface shared verbatim by every core
|
|
21
|
+
* `@warlock.js/ai` primitive (`AgentContract`, `WorkflowInstance`,
|
|
22
|
+
* `SupervisorContract`, `OrchestratorContract`).
|
|
23
|
+
*
|
|
24
|
+
* Kept intentionally structural (not an import of the concrete
|
|
25
|
+
* contracts) so {@link panoptic} attaches to any of the four primitives
|
|
26
|
+
* — and to user-defined executables that mirror the same surface —
|
|
27
|
+
* without Panoptic taking a hard dependency on each primitive's exact
|
|
28
|
+
* event-map generics. `on` returns an unsubscribe function in the core,
|
|
29
|
+
* so the subscriber can detach cleanly.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Every primitive satisfies this:
|
|
33
|
+
* const agent = ai.agent({ model });
|
|
34
|
+
* const sub = panoptic({ exporters: [consoleExporter()] });
|
|
35
|
+
* const detach = sub.attach(agent); // agent is a PanopticTarget
|
|
36
|
+
*/
|
|
37
|
+
type PanopticTarget = {
|
|
38
|
+
/**
|
|
39
|
+
* Stable identifier of the primitive. Used only for diagnostics
|
|
40
|
+
* (Panoptic never branches behavior on it). Present on every core
|
|
41
|
+
* primitive instance.
|
|
42
|
+
*/
|
|
43
|
+
readonly name?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Subscribe a handler to a single event name for the lifetime of the
|
|
46
|
+
* instance, returning an unsubscribe function. Mirrors the core 3-tier
|
|
47
|
+
* model's instance tier (`agent.on(...)`, `workflow.on(...)`, …).
|
|
48
|
+
*/
|
|
49
|
+
on(event: string, handler: (payload: unknown) => void): () => void;
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
export { CompletedEventPayload, PanopticTarget };
|
|
53
|
+
//# sourceMappingURL=panoptic-target.type.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panoptic-target.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic-target.type.ts"],"mappings":";;;;;AAWA;;;;;;;KAAY,qBAAA;EAE+B,uEAAzC,MAAA,EAAQ,UAAA;IAAe,MAAA,EAAQ,UAAU;EAAA;AAAA;;;;;;;;AAkCY;;;;;;;;;;;;KAZ3C,cAAA;;;;;;WAMD,IAAA;;;;;;EAMT,EAAA,CAAG,KAAA,UAAe,OAAA,GAAU,OAAA;AAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Panoptic, PanopticOptions } from "./panoptic.type.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Create a Panoptic subscriber — the one-call entry point that wires the
|
|
6
|
+
* observability pipeline. Pass the exporters you want and Panoptic
|
|
7
|
+
* builds a collector, registers them, and hands back a subscriber you can
|
|
8
|
+
* `attach()` to any agent/workflow/supervisor, install as agent
|
|
9
|
+
* `middleware()`, or feed reports to directly with `collect()`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Attach to a primitive's event stream (captures every run):
|
|
13
|
+
* const observe = panoptic({
|
|
14
|
+
* exporters: [consoleExporter(), otelExporter({ tracerName: "app" })],
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* const agent = ai.agent({ model });
|
|
18
|
+
* const detach = observe.attach(agent);
|
|
19
|
+
*
|
|
20
|
+
* await agent.execute("Summarize this");
|
|
21
|
+
* // ...later, on shutdown:
|
|
22
|
+
* await observe.shutdown();
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Or wire it through the agent middleware pipeline:
|
|
26
|
+
* const observe = panoptic({ exporters: [langfuseExporter({ ... })] });
|
|
27
|
+
* const agent = ai.agent({ model, middleware: [observe.middleware()] });
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // Orchestrator turns carry no result-bearing event — collect directly:
|
|
31
|
+
* const result = await orchestrator.execute(input, { sessionId });
|
|
32
|
+
* await observe.collect(result.report);
|
|
33
|
+
*/
|
|
34
|
+
declare function panoptic(options?: PanopticOptions): Panoptic;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { panoptic };
|
|
37
|
+
//# sourceMappingURL=panoptic.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panoptic.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic.ts"],"mappings":";;;;;AAwLA;;;;;;;;AAAiE;;;;;;;;;;;;;;;;;;;;iBAAjD,QAAA,CAAS,OAAA,GAAS,eAAA,GAAuB,QAAQ"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { createCollector } from "../collector/collector.mjs";
|
|
2
|
+
import { createPanopticMiddleware } from "./panoptic-middleware.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic.ts
|
|
5
|
+
/**
|
|
6
|
+
* Terminal `*.completed` events of every core primitive that carries the
|
|
7
|
+
* finalized `result` (and therefore the `report` tree). These fire once
|
|
8
|
+
* per run regardless of outcome — the matching `*.error` event fires
|
|
9
|
+
* first on failure, then `*.completed` still fires — so subscribing here
|
|
10
|
+
* captures completed, failed, and cancelled runs alike.
|
|
11
|
+
*
|
|
12
|
+
* The orchestrator is intentionally absent: its `orchestrator.turn.*`
|
|
13
|
+
* events carry only session identity, not a result. Collect an
|
|
14
|
+
* orchestrator turn via {@link Panoptic.collect} with
|
|
15
|
+
* `result.report` instead.
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_COMPLETED_EVENTS = [
|
|
18
|
+
"agent.completed",
|
|
19
|
+
"workflow.completed",
|
|
20
|
+
"supervisor.completed"
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* The Panoptic subscriber — binds a collector + its exporters to the
|
|
24
|
+
* three feed paths (events, middleware, direct). Instantiated via
|
|
25
|
+
* {@link panoptic}; callers never see `new`.
|
|
26
|
+
*/
|
|
27
|
+
var PanopticSubscriber = class {
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.collector = options.collector ?? createCollector();
|
|
30
|
+
for (const exporter of options.exporters ?? []) this.collector.use(exporter);
|
|
31
|
+
this.completedEvents = options.completedEvents ?? [...DEFAULT_COMPLETED_EVENTS];
|
|
32
|
+
this.middlewareName = options.middlewareName ?? "panoptic";
|
|
33
|
+
}
|
|
34
|
+
use(exporter) {
|
|
35
|
+
this.collector.use(exporter);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
attach(target) {
|
|
39
|
+
const unsubscribes = [];
|
|
40
|
+
for (const event of this.completedEvents) {
|
|
41
|
+
const unsubscribe = target.on(event, (payload) => {
|
|
42
|
+
this.handleCompleted(payload);
|
|
43
|
+
});
|
|
44
|
+
unsubscribes.push(unsubscribe);
|
|
45
|
+
}
|
|
46
|
+
return () => {
|
|
47
|
+
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
middleware() {
|
|
51
|
+
return createPanopticMiddleware(this.collector, this.middlewareName);
|
|
52
|
+
}
|
|
53
|
+
async collect(report) {
|
|
54
|
+
await this.collector.collect(report);
|
|
55
|
+
}
|
|
56
|
+
toTrace(report) {
|
|
57
|
+
return this.collector.toTrace(report);
|
|
58
|
+
}
|
|
59
|
+
async flush() {
|
|
60
|
+
await this.collector.flush();
|
|
61
|
+
}
|
|
62
|
+
async shutdown() {
|
|
63
|
+
await this.collector.shutdown();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Project one terminal `*.completed` payload's report into the
|
|
67
|
+
* collector. The fan-out is fire-and-forget relative to the emitting
|
|
68
|
+
* run: the core swallows handler errors, the collector isolates
|
|
69
|
+
* exporter failures, and we additionally guard the rejection here so an
|
|
70
|
+
* observability fault never escapes the event handler.
|
|
71
|
+
*/
|
|
72
|
+
handleCompleted(payload) {
|
|
73
|
+
const report = readReport(payload);
|
|
74
|
+
if (!report) return;
|
|
75
|
+
const rootError = readResultError(payload);
|
|
76
|
+
this.collector.collect(report, rootError).catch(() => {});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Read the envelope error off a primitive's completed-event payload
|
|
81
|
+
* (`{ result: { error } }`). The error rides on the result envelope, not
|
|
82
|
+
* the report tree, so the collector needs it separately to populate a
|
|
83
|
+
* failed root span. Returns `undefined` when the run succeeded.
|
|
84
|
+
*/
|
|
85
|
+
function readResultError(payload) {
|
|
86
|
+
return (payload?.result)?.error;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Read the `report` tree off a primitive's completed-event payload.
|
|
90
|
+
* Structural (no concrete-type import) so it accepts every primitive's
|
|
91
|
+
* result subtype; returns `undefined` when the payload isn't the
|
|
92
|
+
* expected `{ result: { report } }` shape.
|
|
93
|
+
*/
|
|
94
|
+
function readReport(payload) {
|
|
95
|
+
const report = (payload?.result)?.report;
|
|
96
|
+
if (typeof report === "object" && report !== null && typeof report.runId === "string" && typeof report.rootRunId === "string") return report;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Create a Panoptic subscriber — the one-call entry point that wires the
|
|
100
|
+
* observability pipeline. Pass the exporters you want and Panoptic
|
|
101
|
+
* builds a collector, registers them, and hands back a subscriber you can
|
|
102
|
+
* `attach()` to any agent/workflow/supervisor, install as agent
|
|
103
|
+
* `middleware()`, or feed reports to directly with `collect()`.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // Attach to a primitive's event stream (captures every run):
|
|
107
|
+
* const observe = panoptic({
|
|
108
|
+
* exporters: [consoleExporter(), otelExporter({ tracerName: "app" })],
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* const agent = ai.agent({ model });
|
|
112
|
+
* const detach = observe.attach(agent);
|
|
113
|
+
*
|
|
114
|
+
* await agent.execute("Summarize this");
|
|
115
|
+
* // ...later, on shutdown:
|
|
116
|
+
* await observe.shutdown();
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* // Or wire it through the agent middleware pipeline:
|
|
120
|
+
* const observe = panoptic({ exporters: [langfuseExporter({ ... })] });
|
|
121
|
+
* const agent = ai.agent({ model, middleware: [observe.middleware()] });
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* // Orchestrator turns carry no result-bearing event — collect directly:
|
|
125
|
+
* const result = await orchestrator.execute(input, { sessionId });
|
|
126
|
+
* await observe.collect(result.report);
|
|
127
|
+
*/
|
|
128
|
+
function panoptic(options = {}) {
|
|
129
|
+
return new PanopticSubscriber(options);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
export { panoptic };
|
|
134
|
+
//# sourceMappingURL=panoptic.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panoptic.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic.ts"],"sourcesContent":["import type { AgentMiddleware, BaseReport } from \"@warlock.js/ai\";\nimport { createCollector } from \"../collector/collector\";\nimport type { CollectorContract } from \"../contracts/collector.contract\";\nimport type { ExporterContract } from \"../contracts/exporter.contract\";\nimport type { Trace } from \"../contracts/trace.type\";\nimport { createPanopticMiddleware } from \"./panoptic-middleware\";\nimport type { CompletedEventPayload, PanopticTarget } from \"./panoptic-target.type\";\nimport type { Panoptic, PanopticOptions } from \"./panoptic.type\";\n\n/**\n * Terminal `*.completed` events of every core primitive that carries the\n * finalized `result` (and therefore the `report` tree). These fire once\n * per run regardless of outcome — the matching `*.error` event fires\n * first on failure, then `*.completed` still fires — so subscribing here\n * captures completed, failed, and cancelled runs alike.\n *\n * The orchestrator is intentionally absent: its `orchestrator.turn.*`\n * events carry only session identity, not a result. Collect an\n * orchestrator turn via {@link Panoptic.collect} with\n * `result.report` instead.\n */\nconst DEFAULT_COMPLETED_EVENTS = [\n \"agent.completed\",\n \"workflow.completed\",\n \"supervisor.completed\",\n] as const;\n\n/**\n * The Panoptic subscriber — binds a collector + its exporters to the\n * three feed paths (events, middleware, direct). Instantiated via\n * {@link panoptic}; callers never see `new`.\n */\nclass PanopticSubscriber implements Panoptic {\n public readonly collector: CollectorContract;\n\n private readonly completedEvents: string[];\n\n private readonly middlewareName: string;\n\n public constructor(options: PanopticOptions = {}) {\n this.collector = options.collector ?? createCollector();\n\n for (const exporter of options.exporters ?? []) {\n this.collector.use(exporter);\n }\n\n this.completedEvents =\n options.completedEvents ?? [...DEFAULT_COMPLETED_EVENTS];\n this.middlewareName = options.middlewareName ?? \"panoptic\";\n }\n\n public use(exporter: ExporterContract): Panoptic {\n this.collector.use(exporter);\n\n return this;\n }\n\n public attach(target: PanopticTarget): () => void {\n const unsubscribes: Array<() => void> = [];\n\n for (const event of this.completedEvents) {\n const unsubscribe = target.on(event, (payload) => {\n this.handleCompleted(payload);\n });\n\n unsubscribes.push(unsubscribe);\n }\n\n return () => {\n for (const unsubscribe of unsubscribes) {\n unsubscribe();\n }\n };\n }\n\n public middleware(): AgentMiddleware {\n return createPanopticMiddleware(this.collector, this.middlewareName);\n }\n\n public async collect(report: BaseReport): Promise<void> {\n await this.collector.collect(report);\n }\n\n public toTrace(report: BaseReport): Trace {\n return this.collector.toTrace(report);\n }\n\n public async flush(): Promise<void> {\n await this.collector.flush();\n }\n\n public async shutdown(): Promise<void> {\n await this.collector.shutdown();\n }\n\n /**\n * Project one terminal `*.completed` payload's report into the\n * collector. The fan-out is fire-and-forget relative to the emitting\n * run: the core swallows handler errors, the collector isolates\n * exporter failures, and we additionally guard the rejection here so an\n * observability fault never escapes the event handler.\n */\n private handleCompleted(payload: unknown): void {\n const report = readReport(payload);\n\n if (!report) {\n return;\n }\n\n // The failing run's typed error lives on the result envelope\n // (`BaseResult.error`), never on the report tree — thread it so a\n // failed root span carries its error type/message.\n const rootError = readResultError(payload);\n\n void this.collector.collect(report, rootError).catch(() => {\n // Swallow — see the JSDoc above. Never surface on the run.\n });\n }\n}\n\n/**\n * Read the envelope error off a primitive's completed-event payload\n * (`{ result: { error } }`). The error rides on the result envelope, not\n * the report tree, so the collector needs it separately to populate a\n * failed root span. Returns `undefined` when the run succeeded.\n */\nfunction readResultError(payload: unknown): unknown {\n const result = (payload as Partial<CompletedEventPayload>)?.result;\n\n return (result as { error?: unknown })?.error;\n}\n\n/**\n * Read the `report` tree off a primitive's completed-event payload.\n * Structural (no concrete-type import) so it accepts every primitive's\n * result subtype; returns `undefined` when the payload isn't the\n * expected `{ result: { report } }` shape.\n */\nfunction readReport(payload: unknown): BaseReport | undefined {\n const result = (payload as Partial<CompletedEventPayload>)?.result;\n const report = (result as { report?: unknown })?.report;\n\n if (\n typeof report === \"object\" &&\n report !== null &&\n typeof (report as { runId?: unknown }).runId === \"string\" &&\n typeof (report as { rootRunId?: unknown }).rootRunId === \"string\"\n ) {\n return report as BaseReport;\n }\n\n return undefined;\n}\n\n/**\n * Create a Panoptic subscriber — the one-call entry point that wires the\n * observability pipeline. Pass the exporters you want and Panoptic\n * builds a collector, registers them, and hands back a subscriber you can\n * `attach()` to any agent/workflow/supervisor, install as agent\n * `middleware()`, or feed reports to directly with `collect()`.\n *\n * @example\n * // Attach to a primitive's event stream (captures every run):\n * const observe = panoptic({\n * exporters: [consoleExporter(), otelExporter({ tracerName: \"app\" })],\n * });\n *\n * const agent = ai.agent({ model });\n * const detach = observe.attach(agent);\n *\n * await agent.execute(\"Summarize this\");\n * // ...later, on shutdown:\n * await observe.shutdown();\n *\n * @example\n * // Or wire it through the agent middleware pipeline:\n * const observe = panoptic({ exporters: [langfuseExporter({ ... })] });\n * const agent = ai.agent({ model, middleware: [observe.middleware()] });\n *\n * @example\n * // Orchestrator turns carry no result-bearing event — collect directly:\n * const result = await orchestrator.execute(input, { sessionId });\n * await observe.collect(result.report);\n */\nexport function panoptic(options: PanopticOptions = {}): Panoptic {\n return new PanopticSubscriber(options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAqBA,MAAM,2BAA2B;CAC/B;CACA;CACA;AACF;;;;;;AAOA,IAAM,qBAAN,MAA6C;CAO3C,AAAO,YAAY,UAA2B,CAAC,GAAG;EAChD,KAAK,YAAY,QAAQ,aAAa,gBAAgB;EAEtD,KAAK,MAAM,YAAY,QAAQ,aAAa,CAAC,GAC3C,KAAK,UAAU,IAAI,QAAQ;EAG7B,KAAK,kBACH,QAAQ,mBAAmB,CAAC,GAAG,wBAAwB;EACzD,KAAK,iBAAiB,QAAQ,kBAAkB;CAClD;CAEA,AAAO,IAAI,UAAsC;EAC/C,KAAK,UAAU,IAAI,QAAQ;EAE3B,OAAO;CACT;CAEA,AAAO,OAAO,QAAoC;EAChD,MAAM,eAAkC,CAAC;EAEzC,KAAK,MAAM,SAAS,KAAK,iBAAiB;GACxC,MAAM,cAAc,OAAO,GAAG,QAAQ,YAAY;IAChD,KAAK,gBAAgB,OAAO;GAC9B,CAAC;GAED,aAAa,KAAK,WAAW;EAC/B;EAEA,aAAa;GACX,KAAK,MAAM,eAAe,cACxB,YAAY;EAEhB;CACF;CAEA,AAAO,aAA8B;EACnC,OAAO,yBAAyB,KAAK,WAAW,KAAK,cAAc;CACrE;CAEA,MAAa,QAAQ,QAAmC;EACtD,MAAM,KAAK,UAAU,QAAQ,MAAM;CACrC;CAEA,AAAO,QAAQ,QAA2B;EACxC,OAAO,KAAK,UAAU,QAAQ,MAAM;CACtC;CAEA,MAAa,QAAuB;EAClC,MAAM,KAAK,UAAU,MAAM;CAC7B;CAEA,MAAa,WAA0B;EACrC,MAAM,KAAK,UAAU,SAAS;CAChC;;;;;;;;CASA,AAAQ,gBAAgB,SAAwB;EAC9C,MAAM,SAAS,WAAW,OAAO;EAEjC,IAAI,CAAC,QACH;EAMF,MAAM,YAAY,gBAAgB,OAAO;EAEzC,AAAK,KAAK,UAAU,QAAQ,QAAQ,SAAS,CAAC,CAAC,YAAY,CAE3D,CAAC;CACH;AACF;;;;;;;AAQA,SAAS,gBAAgB,SAA2B;CAGlD,QAFgB,SAA4C,OAE9C,EAA0B;AAC1C;;;;;;;AAQA,SAAS,WAAW,SAA0C;CAE5D,MAAM,UADU,SAA4C,OACtC,EAA2B;CAEjD,IACE,OAAO,WAAW,YAClB,WAAW,QACX,OAAQ,OAA+B,UAAU,YACjD,OAAQ,OAAmC,cAAc,UAEzD,OAAO;AAIX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,SAAS,UAA2B,CAAC,GAAa;CAChE,OAAO,IAAI,mBAAmB,OAAO;AACvC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Trace } from "../contracts/trace.type.mjs";
|
|
2
|
+
import { ExporterContract } from "../contracts/exporter.contract.mjs";
|
|
3
|
+
import { CollectorContract } from "../contracts/collector.contract.mjs";
|
|
4
|
+
import { PanopticTarget } from "./panoptic-target.type.mjs";
|
|
5
|
+
import { AgentMiddleware, BaseReport } from "@warlock.js/ai";
|
|
6
|
+
|
|
7
|
+
//#region ../@warlock.js/ai-panoptic/src/panoptic/panoptic.type.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Options for the {@link panoptic} subscriber factory.
|
|
10
|
+
*
|
|
11
|
+
* The common case is `{ exporters: [...] }` — Panoptic creates its own
|
|
12
|
+
* collector and registers the exporters on it. Pass `collector` to reuse
|
|
13
|
+
* a pre-built collector (e.g. one that already has a store registered);
|
|
14
|
+
* any `exporters` supplied are registered onto it as well.
|
|
15
|
+
*/
|
|
16
|
+
type PanopticOptions = {
|
|
17
|
+
/**
|
|
18
|
+
* Exporters to register on the collector. Each is deduped by
|
|
19
|
+
* `ExporterContract.name` (registering the same exporter twice is a
|
|
20
|
+
* no-op). Optional — you can also `use()` exporters after construction.
|
|
21
|
+
*/
|
|
22
|
+
exporters?: ExporterContract[];
|
|
23
|
+
/**
|
|
24
|
+
* Bring your own collector instead of letting Panoptic create one.
|
|
25
|
+
* When supplied, `exporters` are registered onto it; when omitted,
|
|
26
|
+
* Panoptic calls `createCollector()` internally. Inject this to share
|
|
27
|
+
* one collector across several subscribers or to pre-wire a store.
|
|
28
|
+
*/
|
|
29
|
+
collector?: CollectorContract;
|
|
30
|
+
/**
|
|
31
|
+
* Names of the terminal `*.completed` events to subscribe to when
|
|
32
|
+
* {@link Panoptic.attach} is called. Defaults to the completed event of
|
|
33
|
+
* every core primitive — `agent.completed`, `workflow.completed`,
|
|
34
|
+
* `supervisor.completed`. Override to widen/narrow the surface (e.g.
|
|
35
|
+
* attach only to agents). Panoptic subscribes to each name and silently
|
|
36
|
+
* ignores the ones the target doesn't emit.
|
|
37
|
+
*/
|
|
38
|
+
completedEvents?: string[];
|
|
39
|
+
/**
|
|
40
|
+
* Stable name reported by the middleware returned from
|
|
41
|
+
* {@link Panoptic.middleware}. Defaults to `"panoptic"`. Override when
|
|
42
|
+
* registering more than one Panoptic middleware on the same agent.
|
|
43
|
+
*/
|
|
44
|
+
middlewareName?: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The subscriber returned by {@link panoptic}. Wraps a collector +
|
|
48
|
+
* exporters and offers three ways to feed it:
|
|
49
|
+
*
|
|
50
|
+
* - **{@link Panoptic.attach}** — subscribe to a primitive's terminal
|
|
51
|
+
* `*.completed` events (the 3-tier model's instance tier) so every run
|
|
52
|
+
* is captured automatically. Returns a detach function.
|
|
53
|
+
* - **{@link Panoptic.middleware}** — an `AgentMiddleware` that collects
|
|
54
|
+
* each `agent.execute()` result from the `execute.after` / `onError`
|
|
55
|
+
* hooks, for apps that prefer the middleware pipeline over events.
|
|
56
|
+
* - **{@link Panoptic.collect}** — feed a `BaseReport` directly (e.g. an
|
|
57
|
+
* orchestrator turn's `result.report`, which has no result-bearing
|
|
58
|
+
* completed event).
|
|
59
|
+
*
|
|
60
|
+
* All three converge on the same collector, so a trace flows to every
|
|
61
|
+
* registered exporter exactly once per run.
|
|
62
|
+
*/
|
|
63
|
+
type Panoptic = {
|
|
64
|
+
/** The underlying collector — exposed for `use()`, `toTrace()`, etc. */readonly collector: CollectorContract;
|
|
65
|
+
/**
|
|
66
|
+
* Register another exporter on the collector. Returns `this` for
|
|
67
|
+
* chaining; mirrors `CollectorContract.use`.
|
|
68
|
+
*/
|
|
69
|
+
use(exporter: ExporterContract): Panoptic;
|
|
70
|
+
/**
|
|
71
|
+
* Subscribe to a primitive's terminal completed events so every run it
|
|
72
|
+
* performs is collected automatically. Safe to call on agents,
|
|
73
|
+
* workflows, and supervisors. Returns a detach function that removes
|
|
74
|
+
* every listener this call added.
|
|
75
|
+
*/
|
|
76
|
+
attach(target: PanopticTarget): () => void;
|
|
77
|
+
/**
|
|
78
|
+
* An `AgentMiddleware` that feeds the collector from the
|
|
79
|
+
* `execute`-level `after` / `onError` hooks — an alternative to
|
|
80
|
+
* {@link Panoptic.attach} for apps wiring observability through the
|
|
81
|
+
* middleware pipeline. The hooks never mutate the result.
|
|
82
|
+
*/
|
|
83
|
+
middleware(): AgentMiddleware;
|
|
84
|
+
/**
|
|
85
|
+
* Feed a finalized root report straight into the collector. Use for
|
|
86
|
+
* primitives whose completed event carries no result (the
|
|
87
|
+
* orchestrator) or to collect a report captured out of band.
|
|
88
|
+
*/
|
|
89
|
+
collect(report: BaseReport): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Pure projection of a report into a {@link Trace} without dispatching
|
|
92
|
+
* — passthrough to `CollectorContract.toTrace`.
|
|
93
|
+
*/
|
|
94
|
+
toTrace(report: BaseReport): Trace; /** Flush every registered exporter that supports it. */
|
|
95
|
+
flush(): Promise<void>; /** Flush and shut down every registered exporter. */
|
|
96
|
+
shutdown(): Promise<void>;
|
|
97
|
+
};
|
|
98
|
+
//#endregion
|
|
99
|
+
export { Panoptic, PanopticOptions };
|
|
100
|
+
//# sourceMappingURL=panoptic.type.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panoptic.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/panoptic/panoptic.type.ts"],"mappings":";;;;;;;;;AAcA;;;;;;KAAY,eAAA;EAaE;;;;AAeE;EAtBd,SAAA,GAAY,gBAAA;EA0CM;;;;;;EAnClB,SAAA,GAAY,iBAAiB;EA8Db;;;;;;;;EArDhB,eAAA;EA4BoB;;;;;EAtBpB,cAAA;AAAA;;;;;;;;;;;;;;;;;AAwDmB;KApCT,QAAA;mFAED,SAAA,EAAW,iBAAA;;;;;EAKpB,GAAA,CAAI,QAAA,EAAU,gBAAA,GAAmB,QAAA;;;;;;;EAOjC,MAAA,CAAO,MAAA,EAAQ,cAAA;;;;;;;EAOf,UAAA,IAAc,eAAA;;;;;;EAMd,OAAA,CAAQ,MAAA,EAAQ,UAAA,GAAa,OAAA;;;;;EAK7B,OAAA,CAAQ,MAAA,EAAQ,UAAA,GAAa,KAAA;EAE7B,KAAA,IAAS,OAAA;EAET,QAAA,IAAY,OAAA;AAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ExporterContract } from "../contracts/exporter.contract.mjs";
|
|
2
|
+
import { TraceStoreContract } from "./trace-store.contract.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai-panoptic/src/store/in-memory-trace-store.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Options for {@link createInMemoryTraceStore}.
|
|
7
|
+
*/
|
|
8
|
+
type InMemoryTraceStoreOptions = {
|
|
9
|
+
/**
|
|
10
|
+
* Maximum number of traces to retain. When set and exceeded, the
|
|
11
|
+
* oldest-ingested trace is evicted (insertion-order FIFO) so the
|
|
12
|
+
* store stays bounded for long-lived processes. Absent / `0` =
|
|
13
|
+
* unbounded (keep everything until `clear`).
|
|
14
|
+
*/
|
|
15
|
+
capacity?: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Create an in-memory trace store. Optionally bound it with `capacity`
|
|
19
|
+
* for long-lived processes; leave it unset for dev/test where you want
|
|
20
|
+
* every trace retained.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const store = createInMemoryTraceStore({ capacity: 1000 });
|
|
24
|
+
* collector.use(store);
|
|
25
|
+
* // later:
|
|
26
|
+
* const recentFailures = store.query({ status: "failed" });
|
|
27
|
+
* const sessionSpend = store.aggregate({ sessionId });
|
|
28
|
+
*/
|
|
29
|
+
declare function createInMemoryTraceStore(options?: InMemoryTraceStoreOptions): TraceStoreContract & ExporterContract;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { InMemoryTraceStoreOptions, createInMemoryTraceStore };
|
|
32
|
+
//# sourceMappingURL=in-memory-trace-store.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-memory-trace-store.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/store/in-memory-trace-store.ts"],"mappings":";;;;;;AAWA;KAAY,yBAAA;;;AAOF;AAoLV;;;EApLE,QAAQ;AAAA;;;;;;;;;AAoL0G;;;;iBAApG,wBAAA,CAAyB,OAAA,GAAU,yBAAA,GAA4B,kBAAA,GAAqB,gBAAA"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { matchTrace } from "./match-trace.mjs";
|
|
2
|
+
import { emptyUsage, sumUsage } from "./sum-usage.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai-panoptic/src/store/in-memory-trace-store.ts
|
|
5
|
+
/**
|
|
6
|
+
* In-memory {@link TraceStoreContract} that doubles as an
|
|
7
|
+
* {@link ExporterContract} — register it on a collector
|
|
8
|
+
* (`collector.use(store)`) and it fills as traces complete, then query
|
|
9
|
+
* or aggregate it after the fact.
|
|
10
|
+
*
|
|
11
|
+
* Backed by an insertion-ordered `Map` keyed by `traceId`, giving O(1)
|
|
12
|
+
* `get` / `add` / overwrite and O(n) scans for `query` / `aggregate`
|
|
13
|
+
* (the price of an in-memory store with no secondary indexes — fine for
|
|
14
|
+
* the dev/test and modest-volume runtime use this targets). When a
|
|
15
|
+
* `capacity` is configured, ingesting past the cap evicts the oldest
|
|
16
|
+
* trace.
|
|
17
|
+
*
|
|
18
|
+
* Instantiated fresh per store via {@link createInMemoryTraceStore};
|
|
19
|
+
* callers never see `new`.
|
|
20
|
+
*/
|
|
21
|
+
var InMemoryTraceStore = class {
|
|
22
|
+
constructor(options) {
|
|
23
|
+
this.name = "in-memory-trace-store";
|
|
24
|
+
this.traces = /* @__PURE__ */ new Map();
|
|
25
|
+
this.capacity = options?.capacity ?? 0;
|
|
26
|
+
}
|
|
27
|
+
get size() {
|
|
28
|
+
return this.traces.size;
|
|
29
|
+
}
|
|
30
|
+
add(trace) {
|
|
31
|
+
this.traces.delete(trace.traceId);
|
|
32
|
+
this.traces.set(trace.traceId, trace);
|
|
33
|
+
this.evictOverflow();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* `ExporterContract.export` — a collector dispatches a completed
|
|
37
|
+
* trace here, which is exactly an `add`. Lets the store be wired into
|
|
38
|
+
* a collector as a sink without an adapter.
|
|
39
|
+
*/
|
|
40
|
+
export(trace) {
|
|
41
|
+
this.add(trace);
|
|
42
|
+
}
|
|
43
|
+
get(traceId) {
|
|
44
|
+
return this.traces.get(traceId);
|
|
45
|
+
}
|
|
46
|
+
query(filter) {
|
|
47
|
+
const matched = [];
|
|
48
|
+
for (const trace of this.traces.values()) if (matchTrace(trace, filter)) matched.push(trace);
|
|
49
|
+
return this.sortNewestFirst(matched);
|
|
50
|
+
}
|
|
51
|
+
aggregate(filter) {
|
|
52
|
+
const aggregate = {
|
|
53
|
+
traces: 0,
|
|
54
|
+
completed: 0,
|
|
55
|
+
failed: 0,
|
|
56
|
+
cancelled: 0,
|
|
57
|
+
usage: emptyUsage(),
|
|
58
|
+
totalDuration: 0
|
|
59
|
+
};
|
|
60
|
+
for (const trace of this.traces.values()) {
|
|
61
|
+
if (!matchTrace(trace, filter)) continue;
|
|
62
|
+
aggregate.traces += 1;
|
|
63
|
+
aggregate.totalDuration += trace.duration;
|
|
64
|
+
aggregate.usage = sumUsage(aggregate.usage, trace.usage);
|
|
65
|
+
this.countStatus(aggregate, trace);
|
|
66
|
+
}
|
|
67
|
+
if (aggregate.usage.cost !== void 0) aggregate.cost = aggregate.usage.cost;
|
|
68
|
+
return aggregate;
|
|
69
|
+
}
|
|
70
|
+
clear() {
|
|
71
|
+
this.traces.clear();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Increment the matching terminal-status counter for one trace.
|
|
75
|
+
* Non-terminal statuses (`awaiting-input`, `max-iterations`) are
|
|
76
|
+
* counted in `traces` but tracked by none of the three headline
|
|
77
|
+
* counters — intentional, those three answer the common
|
|
78
|
+
* "succeeded / errored / aborted" question.
|
|
79
|
+
*/
|
|
80
|
+
countStatus(aggregate, trace) {
|
|
81
|
+
switch (trace.root.status) {
|
|
82
|
+
case "completed":
|
|
83
|
+
aggregate.completed += 1;
|
|
84
|
+
break;
|
|
85
|
+
case "failed":
|
|
86
|
+
aggregate.failed += 1;
|
|
87
|
+
break;
|
|
88
|
+
case "cancelled":
|
|
89
|
+
aggregate.cancelled += 1;
|
|
90
|
+
break;
|
|
91
|
+
default: break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Sort matched traces newest-started first. A copy is sorted so the
|
|
96
|
+
* underlying insertion order (which eviction depends on) is never
|
|
97
|
+
* disturbed.
|
|
98
|
+
*/
|
|
99
|
+
sortNewestFirst(traces) {
|
|
100
|
+
return traces.sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Evict oldest-inserted traces until the store is within `capacity`.
|
|
104
|
+
* No-op when unbounded. The `Map` iterator yields keys in insertion
|
|
105
|
+
* order, so the first key is always the oldest.
|
|
106
|
+
*/
|
|
107
|
+
evictOverflow() {
|
|
108
|
+
if (this.capacity <= 0) return;
|
|
109
|
+
while (this.traces.size > this.capacity) {
|
|
110
|
+
const oldest = this.traces.keys().next().value;
|
|
111
|
+
if (oldest === void 0) return;
|
|
112
|
+
this.traces.delete(oldest);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Create an in-memory trace store. Optionally bound it with `capacity`
|
|
118
|
+
* for long-lived processes; leave it unset for dev/test where you want
|
|
119
|
+
* every trace retained.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* const store = createInMemoryTraceStore({ capacity: 1000 });
|
|
123
|
+
* collector.use(store);
|
|
124
|
+
* // later:
|
|
125
|
+
* const recentFailures = store.query({ status: "failed" });
|
|
126
|
+
* const sessionSpend = store.aggregate({ sessionId });
|
|
127
|
+
*/
|
|
128
|
+
function createInMemoryTraceStore(options) {
|
|
129
|
+
return new InMemoryTraceStore(options);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
export { createInMemoryTraceStore };
|
|
134
|
+
//# sourceMappingURL=in-memory-trace-store.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-memory-trace-store.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/store/in-memory-trace-store.ts"],"sourcesContent":["import type { ExporterContract } from \"../contracts/exporter.contract\";\nimport type { Trace } from \"../contracts/trace.type\";\nimport { matchTrace } from \"./match-trace\";\nimport { emptyUsage, sumUsage } from \"./sum-usage\";\nimport type { TraceAggregate } from \"./trace-aggregate.type\";\nimport type { TraceQuery } from \"./trace-query.type\";\nimport type { TraceStoreContract } from \"./trace-store.contract\";\n\n/**\n * Options for {@link createInMemoryTraceStore}.\n */\nexport type InMemoryTraceStoreOptions = {\n /**\n * Maximum number of traces to retain. When set and exceeded, the\n * oldest-ingested trace is evicted (insertion-order FIFO) so the\n * store stays bounded for long-lived processes. Absent / `0` =\n * unbounded (keep everything until `clear`).\n */\n capacity?: number;\n};\n\n/**\n * In-memory {@link TraceStoreContract} that doubles as an\n * {@link ExporterContract} — register it on a collector\n * (`collector.use(store)`) and it fills as traces complete, then query\n * or aggregate it after the fact.\n *\n * Backed by an insertion-ordered `Map` keyed by `traceId`, giving O(1)\n * `get` / `add` / overwrite and O(n) scans for `query` / `aggregate`\n * (the price of an in-memory store with no secondary indexes — fine for\n * the dev/test and modest-volume runtime use this targets). When a\n * `capacity` is configured, ingesting past the cap evicts the oldest\n * trace.\n *\n * Instantiated fresh per store via {@link createInMemoryTraceStore};\n * callers never see `new`.\n */\nclass InMemoryTraceStore implements TraceStoreContract, ExporterContract {\n /** Stable exporter id so a collector can dedupe / log this sink. */\n public readonly name = \"in-memory-trace-store\";\n\n /**\n * Retained traces keyed by `traceId`. A `Map` preserves insertion\n * order, which is what FIFO eviction and newest-first `query` ordering\n * both rely on.\n */\n private readonly traces = new Map<string, Trace>();\n\n private readonly capacity: number;\n\n public constructor(options?: InMemoryTraceStoreOptions) {\n this.capacity = options?.capacity ?? 0;\n }\n\n public get size(): number {\n return this.traces.size;\n }\n\n public add(trace: Trace): void {\n // Re-insert so an overwrite also refreshes insertion position —\n // keeps \"oldest\" honest for FIFO eviction.\n this.traces.delete(trace.traceId);\n this.traces.set(trace.traceId, trace);\n\n this.evictOverflow();\n }\n\n /**\n * `ExporterContract.export` — a collector dispatches a completed\n * trace here, which is exactly an `add`. Lets the store be wired into\n * a collector as a sink without an adapter.\n */\n public export(trace: Trace): void {\n this.add(trace);\n }\n\n public get(traceId: string): Trace | undefined {\n return this.traces.get(traceId);\n }\n\n public query(filter?: TraceQuery): Trace[] {\n const matched: Trace[] = [];\n\n for (const trace of this.traces.values()) {\n if (matchTrace(trace, filter)) {\n matched.push(trace);\n }\n }\n\n return this.sortNewestFirst(matched);\n }\n\n public aggregate(filter?: TraceQuery): TraceAggregate {\n const aggregate: TraceAggregate = {\n traces: 0,\n completed: 0,\n failed: 0,\n cancelled: 0,\n usage: emptyUsage(),\n totalDuration: 0,\n };\n\n for (const trace of this.traces.values()) {\n if (!matchTrace(trace, filter)) {\n continue;\n }\n\n aggregate.traces += 1;\n aggregate.totalDuration += trace.duration;\n aggregate.usage = sumUsage(aggregate.usage, trace.usage);\n\n this.countStatus(aggregate, trace);\n }\n\n if (aggregate.usage.cost !== undefined) {\n aggregate.cost = aggregate.usage.cost;\n }\n\n return aggregate;\n }\n\n public clear(): void {\n this.traces.clear();\n }\n\n /**\n * Increment the matching terminal-status counter for one trace.\n * Non-terminal statuses (`awaiting-input`, `max-iterations`) are\n * counted in `traces` but tracked by none of the three headline\n * counters — intentional, those three answer the common\n * \"succeeded / errored / aborted\" question.\n */\n private countStatus(aggregate: TraceAggregate, trace: Trace): void {\n switch (trace.root.status) {\n case \"completed\": {\n aggregate.completed += 1;\n break;\n }\n\n case \"failed\": {\n aggregate.failed += 1;\n break;\n }\n\n case \"cancelled\": {\n aggregate.cancelled += 1;\n break;\n }\n\n default: {\n break;\n }\n }\n }\n\n /**\n * Sort matched traces newest-started first. A copy is sorted so the\n * underlying insertion order (which eviction depends on) is never\n * disturbed.\n */\n private sortNewestFirst(traces: Trace[]): Trace[] {\n return traces.sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt));\n }\n\n /**\n * Evict oldest-inserted traces until the store is within `capacity`.\n * No-op when unbounded. The `Map` iterator yields keys in insertion\n * order, so the first key is always the oldest.\n */\n private evictOverflow(): void {\n if (this.capacity <= 0) {\n return;\n }\n\n while (this.traces.size > this.capacity) {\n const oldest = this.traces.keys().next().value;\n\n if (oldest === undefined) {\n return;\n }\n\n this.traces.delete(oldest);\n }\n }\n}\n\n/**\n * Create an in-memory trace store. Optionally bound it with `capacity`\n * for long-lived processes; leave it unset for dev/test where you want\n * every trace retained.\n *\n * @example\n * const store = createInMemoryTraceStore({ capacity: 1000 });\n * collector.use(store);\n * // later:\n * const recentFailures = store.query({ status: \"failed\" });\n * const sessionSpend = store.aggregate({ sessionId });\n */\nexport function createInMemoryTraceStore(options?: InMemoryTraceStoreOptions): TraceStoreContract & ExporterContract {\n return new InMemoryTraceStore(options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqCA,IAAM,qBAAN,MAAyE;CAavE,AAAO,YAAY,SAAqC;cAXjC;gCAOG,IAAI,IAAmB;EAK/C,KAAK,WAAW,SAAS,YAAY;CACvC;CAEA,IAAW,OAAe;EACxB,OAAO,KAAK,OAAO;CACrB;CAEA,AAAO,IAAI,OAAoB;EAG7B,KAAK,OAAO,OAAO,MAAM,OAAO;EAChC,KAAK,OAAO,IAAI,MAAM,SAAS,KAAK;EAEpC,KAAK,cAAc;CACrB;;;;;;CAOA,AAAO,OAAO,OAAoB;EAChC,KAAK,IAAI,KAAK;CAChB;CAEA,AAAO,IAAI,SAAoC;EAC7C,OAAO,KAAK,OAAO,IAAI,OAAO;CAChC;CAEA,AAAO,MAAM,QAA8B;EACzC,MAAM,UAAmB,CAAC;EAE1B,KAAK,MAAM,SAAS,KAAK,OAAO,OAAO,GACrC,IAAI,WAAW,OAAO,MAAM,GAC1B,QAAQ,KAAK,KAAK;EAItB,OAAO,KAAK,gBAAgB,OAAO;CACrC;CAEA,AAAO,UAAU,QAAqC;EACpD,MAAM,YAA4B;GAChC,QAAQ;GACR,WAAW;GACX,QAAQ;GACR,WAAW;GACX,OAAO,WAAW;GAClB,eAAe;EACjB;EAEA,KAAK,MAAM,SAAS,KAAK,OAAO,OAAO,GAAG;GACxC,IAAI,CAAC,WAAW,OAAO,MAAM,GAC3B;GAGF,UAAU,UAAU;GACpB,UAAU,iBAAiB,MAAM;GACjC,UAAU,QAAQ,SAAS,UAAU,OAAO,MAAM,KAAK;GAEvD,KAAK,YAAY,WAAW,KAAK;EACnC;EAEA,IAAI,UAAU,MAAM,SAAS,QAC3B,UAAU,OAAO,UAAU,MAAM;EAGnC,OAAO;CACT;CAEA,AAAO,QAAc;EACnB,KAAK,OAAO,MAAM;CACpB;;;;;;;;CASA,AAAQ,YAAY,WAA2B,OAAoB;EACjE,QAAQ,MAAM,KAAK,QAAnB;GACE,KAAK;IACH,UAAU,aAAa;IACvB;GAGF,KAAK;IACH,UAAU,UAAU;IACpB;GAGF,KAAK;IACH,UAAU,aAAa;IACvB;GAGF,SACE;EAEJ;CACF;;;;;;CAOA,AAAQ,gBAAgB,QAA0B;EAChD,OAAO,OAAO,MAAM,MAAM,UAAU,KAAK,MAAM,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC;CAC9F;;;;;;CAOA,AAAQ,gBAAsB;EAC5B,IAAI,KAAK,YAAY,GACnB;EAGF,OAAO,KAAK,OAAO,OAAO,KAAK,UAAU;GACvC,MAAM,SAAS,KAAK,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;GAEzC,IAAI,WAAW,QACb;GAGF,KAAK,OAAO,OAAO,MAAM;EAC3B;CACF;AACF;;;;;;;;;;;;;AAcA,SAAgB,yBAAyB,SAA4E;CACnH,OAAO,IAAI,mBAAmB,OAAO;AACvC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { TraceAggregate } from "./trace-aggregate.type.mjs";
|
|
2
|
+
import { TraceQuery } from "./trace-query.type.mjs";
|
|
3
|
+
import { TraceStoreContract } from "./trace-store.contract.mjs";
|
|
4
|
+
import { InMemoryTraceStoreOptions, createInMemoryTraceStore } from "./in-memory-trace-store.mjs";
|
|
5
|
+
import { matchTrace } from "./match-trace.mjs";
|
|
6
|
+
import { emptyUsage, sumUsage } from "./sum-usage.mjs";
|