@warlock.js/ai-panoptic 5.31.0 → 5.33.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 +18 -0
- package/cjs/index.cjs +72 -67
- package/cjs/index.cjs.map +1 -1
- package/esm/collector/collector.d.mts +3 -5
- package/esm/collector/collector.d.mts.map +1 -1
- package/esm/collector/collector.mjs +2 -0
- package/esm/collector/collector.mjs.map +1 -1
- package/esm/collector/content-capture.type.d.mts +4 -5
- package/esm/collector/content-capture.type.d.mts.map +1 -1
- package/esm/collector/extract-span-attributes.d.mts +1 -3
- package/esm/collector/extract-span-attributes.d.mts.map +1 -1
- package/esm/collector/extract-span-attributes.mjs +30 -6
- package/esm/collector/extract-span-attributes.mjs.map +1 -1
- package/esm/collector/index.d.mts +3 -1
- package/esm/collector/index.mjs +2 -1
- package/esm/collector/normalize-error.d.mts +1 -3
- package/esm/collector/normalize-error.d.mts.map +1 -1
- package/esm/collector/report-to-span.d.mts +1 -3
- package/esm/collector/report-to-span.d.mts.map +1 -1
- package/esm/collector/report-to-trace.d.mts +1 -3
- package/esm/collector/report-to-trace.d.mts.map +1 -1
- package/esm/collector/trace-subscriptions.d.mts +14 -0
- package/esm/collector/trace-subscriptions.d.mts.map +1 -0
- package/esm/collector/trace-subscriptions.mjs +26 -0
- package/esm/collector/trace-subscriptions.mjs.map +1 -0
- package/esm/config/apply-panoptic-config.d.mts +2 -3
- package/esm/config/apply-panoptic-config.d.mts.map +1 -1
- package/esm/config/index.d.mts +2 -1
- package/esm/config/index.mjs +1 -1
- package/esm/config/panoptic-config.type.d.mts +2 -4
- package/esm/config/panoptic-config.type.d.mts.map +1 -1
- package/esm/contracts/collector.contract.d.mts +1 -3
- package/esm/contracts/collector.contract.d.mts.map +1 -1
- package/esm/contracts/exporter.contract.d.mts +1 -3
- package/esm/contracts/exporter.contract.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +2 -1
- package/esm/contracts/trace.type.d.mts +37 -20
- package/esm/contracts/trace.type.d.mts.map +1 -1
- package/esm/dashboard/dashboard.d.mts +1 -3
- package/esm/dashboard/dashboard.d.mts.map +1 -1
- package/esm/dashboard/dashboard.mjs +3 -2
- package/esm/dashboard/dashboard.mjs.map +1 -1
- package/esm/dashboard/dashboard.type.d.mts +9 -7
- package/esm/dashboard/dashboard.type.d.mts.map +1 -1
- package/esm/dashboard/index.d.mts +4 -1
- package/esm/dashboard/index.mjs +1 -1
- package/esm/dashboard/parse-query.d.mts +1 -1
- package/esm/dashboard/serve.d.mts +2 -1
- package/esm/dashboard/serve.mjs +1 -1
- package/esm/dashboard/serve.mjs.map +1 -1
- package/esm/dashboard/trace-filter.d.mts +44 -32
- package/esm/dashboard/trace-filter.d.mts.map +1 -1
- package/esm/evaluate/evaluate-system-prompt.d.mts +1 -3
- package/esm/evaluate/evaluate-system-prompt.d.mts.map +1 -1
- package/esm/evaluate/evaluate-system-prompt.mjs +3 -1
- package/esm/evaluate/evaluate-system-prompt.mjs.map +1 -1
- package/esm/evaluate/evaluate.type.d.mts +2 -4
- package/esm/evaluate/evaluate.type.d.mts.map +1 -1
- package/esm/evaluate/extract-last-system-prompt.d.mts +1 -3
- package/esm/evaluate/extract-last-system-prompt.d.mts.map +1 -1
- package/esm/evaluate/find-span-by-id.d.mts +1 -3
- package/esm/evaluate/find-span-by-id.d.mts.map +1 -1
- package/esm/evaluate/index.d.mts +2 -1
- package/esm/evaluate/index.mjs +1 -1
- package/esm/exporters/console/console-exporter.d.mts +2 -3
- package/esm/exporters/console/console-exporter.d.mts.map +1 -1
- package/esm/exporters/console/console-exporter.type.d.mts +2 -3
- package/esm/exporters/console/console-exporter.type.d.mts.map +1 -1
- package/esm/exporters/console/format-span-io.d.mts +2 -2
- package/esm/exporters/console/format-span-io.d.mts.map +1 -1
- package/esm/exporters/console/format-span-line.d.mts +2 -2
- package/esm/exporters/console/format-span-line.d.mts.map +1 -1
- package/esm/exporters/console/index.d.mts +2 -1
- package/esm/exporters/console/index.mjs +1 -1
- package/esm/exporters/file/file-exporter.d.mts +2 -3
- package/esm/exporters/file/file-exporter.d.mts.map +1 -1
- package/esm/exporters/file/file-exporter.type.d.mts +8 -5
- package/esm/exporters/file/file-exporter.type.d.mts.map +1 -1
- package/esm/exporters/file/index.d.mts +2 -1
- package/esm/exporters/index.d.mts +7 -1
- package/esm/exporters/index.mjs +1 -1
- package/esm/exporters/langfuse/index.d.mts +2 -1
- package/esm/exporters/langfuse/index.mjs +1 -1
- package/esm/exporters/langfuse/langfuse-exporter.d.mts +2 -3
- package/esm/exporters/langfuse/langfuse-exporter.d.mts.map +1 -1
- package/esm/exporters/langfuse/langfuse-exporter.mjs +1 -1
- package/esm/exporters/langfuse/langfuse-exporter.mjs.map +1 -1
- package/esm/exporters/langfuse/langfuse-exporter.type.d.mts +23 -17
- package/esm/exporters/langfuse/langfuse-exporter.type.d.mts.map +1 -1
- package/esm/exporters/otel/index.d.mts +2 -1
- package/esm/exporters/otel/index.mjs +1 -1
- package/esm/exporters/otel/otel-api.shim.type.d.mts +6 -7
- package/esm/exporters/otel/otel-api.shim.type.d.mts.map +1 -1
- package/esm/exporters/otel/otel-exporter.d.mts +2 -3
- package/esm/exporters/otel/otel-exporter.d.mts.map +1 -1
- package/esm/exporters/otel/otel-exporter.type.d.mts +1 -3
- package/esm/exporters/otel/otel-exporter.type.d.mts.map +1 -1
- package/esm/exporters/utils/gen-ai-attributes.d.mts +9 -7
- package/esm/exporters/utils/gen-ai-attributes.d.mts.map +1 -1
- package/esm/exporters/utils/index.d.mts +2 -1
- package/esm/exporters/utils/index.mjs +1 -1
- package/esm/exporters/utils/total-cost.d.mts +1 -3
- package/esm/exporters/utils/total-cost.d.mts.map +1 -1
- package/esm/exporters/utils/walk-spans.d.mts +2 -2
- package/esm/exporters/utils/walk-spans.d.mts.map +1 -1
- package/esm/index.d.mts +10 -1
- package/esm/index.mjs +2 -1
- package/esm/panoptic/index.d.mts +2 -1
- package/esm/panoptic/index.mjs +1 -1
- package/esm/panoptic/panoptic-middleware.d.mts +1 -3
- package/esm/panoptic/panoptic-middleware.d.mts.map +1 -1
- package/esm/panoptic/panoptic-target.type.d.mts +4 -5
- package/esm/panoptic/panoptic-target.type.d.mts.map +1 -1
- package/esm/panoptic/panoptic.d.mts +1 -3
- package/esm/panoptic/panoptic.d.mts.map +1 -1
- package/esm/panoptic/panoptic.type.d.mts +8 -7
- package/esm/panoptic/panoptic.type.d.mts.map +1 -1
- package/esm/store/cache-trace-store.d.mts +4 -6
- package/esm/store/cache-trace-store.d.mts.map +1 -1
- package/esm/store/cache-trace-store.mjs +1 -4
- package/esm/store/cache-trace-store.mjs.map +1 -1
- package/esm/store/in-memory-trace-store.d.mts +2 -4
- package/esm/store/in-memory-trace-store.d.mts.map +1 -1
- package/esm/store/in-memory-trace-store.mjs +1 -4
- package/esm/store/in-memory-trace-store.mjs.map +1 -1
- package/esm/store/index.d.mts +2 -1
- package/esm/store/index.mjs +1 -1
- package/esm/store/match-trace.d.mts +1 -3
- package/esm/store/match-trace.d.mts.map +1 -1
- package/esm/store/match-trace.mjs +1 -0
- package/esm/store/match-trace.mjs.map +1 -1
- package/esm/store/sum-usage.d.mts +4 -30
- package/esm/store/sum-usage.d.mts.map +1 -1
- package/esm/store/sum-usage.mjs +6 -49
- package/esm/store/sum-usage.mjs.map +1 -1
- package/esm/store/trace-aggregate.type.d.mts +12 -9
- package/esm/store/trace-aggregate.type.d.mts.map +1 -1
- package/esm/store/trace-query.type.d.mts +7 -5
- package/esm/store/trace-query.type.d.mts.map +1 -1
- package/esm/store/trace-store.contract.d.mts +1 -3
- package/esm/store/trace-store.contract.d.mts.map +1 -1
- package/llms-full.txt +105 -0
- package/package.json +4 -4
- package/skills/observe-with-panoptic/SKILL.md +35 -0
- package/skills/query-traces/SKILL.md +49 -0
- package/skills/use-local-dashboard/SKILL.md +21 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Trace } from "../contracts/trace.type.mjs";
|
|
2
2
|
import { TraceQuery } from "./trace-query.type.mjs";
|
|
3
|
-
|
|
4
3
|
//#region ../../packages/ai-panoptic/src/store/match-trace.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Test whether one trace satisfies a {@link TraceQuery}. Every declared
|
|
@@ -15,7 +14,6 @@ import { TraceQuery } from "./trace-query.type.mjs";
|
|
|
15
14
|
* Pure — used by the store's `query` and `aggregate` so both share one
|
|
16
15
|
* matching definition.
|
|
17
16
|
*/
|
|
18
|
-
declare function matchTrace(trace: Trace, filter?: TraceQuery): boolean;
|
|
17
|
+
export declare function matchTrace(trace: Trace, filter?: TraceQuery): boolean;
|
|
19
18
|
//#endregion
|
|
20
|
-
export { matchTrace };
|
|
21
19
|
//# sourceMappingURL=match-trace.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-trace.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/match-trace.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"match-trace.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/match-trace.ts"],"mappings":";;;;;;;;;;;;;;;;wBAiBgB,WAAW,OAAO,OAAO,SAAS"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
function matchTrace(trace, filter) {
|
|
16
16
|
if (!filter) return true;
|
|
17
|
+
if (filter.requestId !== void 0 && trace.requestId !== filter.requestId) return false;
|
|
17
18
|
if (filter.traceId !== void 0 && trace.traceId !== filter.traceId) return false;
|
|
18
19
|
if (filter.sessionId !== void 0 && trace.sessionId !== filter.sessionId) return false;
|
|
19
20
|
if (filter.status !== void 0 && !statusMatches(trace.root.status, filter.status)) return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-trace.mjs","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/match-trace.ts"],"sourcesContent":["import type { ReportStatus } from \"@warlock.js/ai\";\nimport type { Trace } from \"../contracts/trace.type\";\nimport type { TraceQuery } from \"./trace-query.type\";\n\n/**\n * Test whether one trace satisfies a {@link TraceQuery}. Every declared\n * filter field must match (logical AND); absent fields are ignored, so\n * an empty / undefined filter matches every trace.\n *\n * Time bounds compare against the trace's root `startedAt`, parsed to\n * an epoch once per call, inclusive on both ends. Status accepts a\n * single value or an array (membership test). Identity fields are exact\n * string equality.\n *\n * Pure — used by the store's `query` and `aggregate` so both share one\n * matching definition.\n */\nexport function matchTrace(trace: Trace, filter?: TraceQuery): boolean {\n if (!filter) {\n return true;\n }\n\n if (filter.traceId !== undefined && trace.traceId !== filter.traceId) {\n return false;\n }\n\n if (filter.sessionId !== undefined && trace.sessionId !== filter.sessionId) {\n return false;\n }\n\n if (filter.status !== undefined && !statusMatches(trace.root.status, filter.status)) {\n return false;\n }\n\n const startedAt = Date.parse(trace.startedAt);\n\n if (filter.startedAfter !== undefined && startedAt < toEpoch(filter.startedAfter)) {\n return false;\n }\n\n if (filter.startedBefore !== undefined && startedAt > toEpoch(filter.startedBefore)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Membership test for the status filter — true when `status` equals the\n * single wanted value, or is one of the wanted array.\n */\nfunction statusMatches(status: ReportStatus, wanted: ReportStatus | ReportStatus[]): boolean {\n if (Array.isArray(wanted)) {\n return wanted.includes(status);\n }\n\n return status === wanted;\n}\n\n/**\n * Normalize a time bound (ISO string or `Date`) to epoch milliseconds\n * for comparison against a parsed `startedAt`.\n */\nfunction toEpoch(bound: string | Date): number {\n if (bound instanceof Date) {\n return bound.getTime();\n }\n\n return Date.parse(bound);\n}\n"],"mappings":";;;;;;;;;;;;;;AAiBA,SAAgB,WAAW,OAAc,QAA8B;CACrE,IAAI,CAAC,QACH,OAAO;CAGT,IAAI,OAAO,YAAY,UAAa,MAAM,YAAY,OAAO,SAC3D,OAAO;CAGT,IAAI,OAAO,cAAc,UAAa,MAAM,cAAc,OAAO,WAC/D,OAAO;CAGT,IAAI,OAAO,WAAW,UAAa,CAAC,cAAc,MAAM,KAAK,QAAQ,OAAO,MAAM,GAChF,OAAO;CAGT,MAAM,YAAY,KAAK,MAAM,MAAM,SAAS;CAE5C,IAAI,OAAO,iBAAiB,UAAa,YAAY,QAAQ,OAAO,YAAY,GAC9E,OAAO;CAGT,IAAI,OAAO,kBAAkB,UAAa,YAAY,QAAQ,OAAO,aAAa,GAChF,OAAO;CAGT,OAAO;AACT;;;;;AAMA,SAAS,cAAc,QAAsB,QAAgD;CAC3F,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,OAAO,SAAS,MAAM;CAG/B,OAAO,WAAW;AACpB;;;;;AAMA,SAAS,QAAQ,OAA8B;CAC7C,IAAI,iBAAiB,MACnB,OAAO,MAAM,QAAQ;CAGvB,OAAO,KAAK,MAAM,KAAK;AACzB"}
|
|
1
|
+
{"version":3,"file":"match-trace.mjs","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/match-trace.ts"],"sourcesContent":["import type { ReportStatus } from \"@warlock.js/ai\";\nimport type { Trace } from \"../contracts/trace.type\";\nimport type { TraceQuery } from \"./trace-query.type\";\n\n/**\n * Test whether one trace satisfies a {@link TraceQuery}. Every declared\n * filter field must match (logical AND); absent fields are ignored, so\n * an empty / undefined filter matches every trace.\n *\n * Time bounds compare against the trace's root `startedAt`, parsed to\n * an epoch once per call, inclusive on both ends. Status accepts a\n * single value or an array (membership test). Identity fields are exact\n * string equality.\n *\n * Pure — used by the store's `query` and `aggregate` so both share one\n * matching definition.\n */\nexport function matchTrace(trace: Trace, filter?: TraceQuery): boolean {\n if (!filter) {\n return true;\n }\n\n if (filter.requestId !== undefined && trace.requestId !== filter.requestId) {\n return false;\n }\n\n if (filter.traceId !== undefined && trace.traceId !== filter.traceId) {\n return false;\n }\n\n if (filter.sessionId !== undefined && trace.sessionId !== filter.sessionId) {\n return false;\n }\n\n if (filter.status !== undefined && !statusMatches(trace.root.status, filter.status)) {\n return false;\n }\n\n const startedAt = Date.parse(trace.startedAt);\n\n if (filter.startedAfter !== undefined && startedAt < toEpoch(filter.startedAfter)) {\n return false;\n }\n\n if (filter.startedBefore !== undefined && startedAt > toEpoch(filter.startedBefore)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Membership test for the status filter — true when `status` equals the\n * single wanted value, or is one of the wanted array.\n */\nfunction statusMatches(status: ReportStatus, wanted: ReportStatus | ReportStatus[]): boolean {\n if (Array.isArray(wanted)) {\n return wanted.includes(status);\n }\n\n return status === wanted;\n}\n\n/**\n * Normalize a time bound (ISO string or `Date`) to epoch milliseconds\n * for comparison against a parsed `startedAt`.\n */\nfunction toEpoch(bound: string | Date): number {\n if (bound instanceof Date) {\n return bound.getTime();\n }\n\n return Date.parse(bound);\n}\n"],"mappings":";;;;;;;;;;;;;;AAiBA,SAAgB,WAAW,OAAc,QAA8B;CACrE,IAAI,CAAC,QACH,OAAO;CAGT,IAAI,OAAO,cAAc,UAAa,MAAM,cAAc,OAAO,WAC/D,OAAO;CAGT,IAAI,OAAO,YAAY,UAAa,MAAM,YAAY,OAAO,SAC3D,OAAO;CAGT,IAAI,OAAO,cAAc,UAAa,MAAM,cAAc,OAAO,WAC/D,OAAO;CAGT,IAAI,OAAO,WAAW,UAAa,CAAC,cAAc,MAAM,KAAK,QAAQ,OAAO,MAAM,GAChF,OAAO;CAGT,MAAM,YAAY,KAAK,MAAM,MAAM,SAAS;CAE5C,IAAI,OAAO,iBAAiB,UAAa,YAAY,QAAQ,OAAO,YAAY,GAC9E,OAAO;CAGT,IAAI,OAAO,kBAAkB,UAAa,YAAY,QAAQ,OAAO,aAAa,GAChF,OAAO;CAGT,OAAO;AACT;;;;;AAMA,SAAS,cAAc,QAAsB,QAAgD;CAC3F,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,OAAO,SAAS,MAAM;CAG/B,OAAO,WAAW;AACpB;;;;;AAMA,SAAS,QAAQ,OAA8B;CAC7C,IAAI,iBAAiB,MACnB,OAAO,MAAM,QAAQ;CAGvB,OAAO,KAAK,MAAM,KAAK;AACzB"}
|
|
@@ -1,34 +1,8 @@
|
|
|
1
1
|
import { Usage } from "@warlock.js/ai";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/ai-panoptic/src/store/sum-usage.d.ts
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* `reasoningTokens`) sum only when at least one side reported them, so
|
|
9
|
-
* a provider that never meters a channel doesn't fabricate a `0` for
|
|
10
|
-
* it. The `cost` breakdown is merged with the core framework's
|
|
11
|
-
* {@link accumulateCost}, keeping cost-rollup semantics identical to a
|
|
12
|
-
* native report tree — an unpriced contributor never erases a priced
|
|
13
|
-
* one.
|
|
14
|
-
*
|
|
15
|
-
* Pure: returns a fresh `Usage`, never mutates either argument. Seed an
|
|
16
|
-
* aggregation with {@link emptyUsage}.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* let total = emptyUsage();
|
|
20
|
-
* for (const trace of traces) {
|
|
21
|
-
* total = sumUsage(total, trace.usage);
|
|
22
|
-
* }
|
|
23
|
-
*/
|
|
24
|
-
declare function sumUsage(accumulator: Usage, next: Usage): Usage;
|
|
25
|
-
/**
|
|
26
|
-
* A zero-valued {@link Usage} to seed an aggregation. Only the required
|
|
27
|
-
* token channels are set; optional channels stay absent until a
|
|
28
|
-
* contributor reports them, preserving the "never reported" vs
|
|
29
|
-
* "reported as 0" distinction.
|
|
30
|
-
*/
|
|
31
|
-
declare function emptyUsage(): Usage;
|
|
3
|
+
/** Return an independent all-channel usage rollup, preserving hosted-tool counters. */
|
|
4
|
+
export declare function sumUsage(accumulator: Usage, next: Usage): Usage;
|
|
5
|
+
/** Seed an aggregation without claiming any optional counter was reported. */
|
|
6
|
+
export declare function emptyUsage(): Usage;
|
|
32
7
|
//#endregion
|
|
33
|
-
export { emptyUsage, sumUsage };
|
|
34
8
|
//# sourceMappingURL=sum-usage.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sum-usage.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/sum-usage.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"sum-usage.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/sum-usage.ts"],"mappings":";;;wBAGgB,SAAS,aAAa,OAAO,MAAM,QAAQ;;wBAQ3C,cAAc"}
|
package/esm/store/sum-usage.mjs
CHANGED
|
@@ -1,48 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mergeUsage } from "@warlock.js/ai";
|
|
2
2
|
|
|
3
3
|
//#region ../../packages/ai-panoptic/src/store/sum-usage.ts
|
|
4
|
-
/**
|
|
5
|
-
* Fold a child {@link Usage} into a running accumulator. Token channels
|
|
6
|
-
* (`input` / `output` / `total`) always sum; the optional cache /
|
|
7
|
-
* reasoning channels (`cachedTokens` / `cacheWriteTokens` /
|
|
8
|
-
* `reasoningTokens`) sum only when at least one side reported them, so
|
|
9
|
-
* a provider that never meters a channel doesn't fabricate a `0` for
|
|
10
|
-
* it. The `cost` breakdown is merged with the core framework's
|
|
11
|
-
* {@link accumulateCost}, keeping cost-rollup semantics identical to a
|
|
12
|
-
* native report tree — an unpriced contributor never erases a priced
|
|
13
|
-
* one.
|
|
14
|
-
*
|
|
15
|
-
* Pure: returns a fresh `Usage`, never mutates either argument. Seed an
|
|
16
|
-
* aggregation with {@link emptyUsage}.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* let total = emptyUsage();
|
|
20
|
-
* for (const trace of traces) {
|
|
21
|
-
* total = sumUsage(total, trace.usage);
|
|
22
|
-
* }
|
|
23
|
-
*/
|
|
4
|
+
/** Return an independent all-channel usage rollup, preserving hosted-tool counters. */
|
|
24
5
|
function sumUsage(accumulator, next) {
|
|
25
|
-
const merged =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
total: accumulator.total + next.total
|
|
29
|
-
};
|
|
30
|
-
const cachedTokens = sumOptional(accumulator.cachedTokens, next.cachedTokens);
|
|
31
|
-
if (cachedTokens !== void 0) merged.cachedTokens = cachedTokens;
|
|
32
|
-
const cacheWriteTokens = sumOptional(accumulator.cacheWriteTokens, next.cacheWriteTokens);
|
|
33
|
-
if (cacheWriteTokens !== void 0) merged.cacheWriteTokens = cacheWriteTokens;
|
|
34
|
-
const reasoningTokens = sumOptional(accumulator.reasoningTokens, next.reasoningTokens);
|
|
35
|
-
if (reasoningTokens !== void 0) merged.reasoningTokens = reasoningTokens;
|
|
36
|
-
const cost = accumulateCost(accumulator.cost, next.cost);
|
|
37
|
-
if (cost !== void 0) merged.cost = cost;
|
|
6
|
+
const merged = emptyUsage();
|
|
7
|
+
mergeUsage(merged, accumulator);
|
|
8
|
+
mergeUsage(merged, next);
|
|
38
9
|
return merged;
|
|
39
10
|
}
|
|
40
|
-
/**
|
|
41
|
-
* A zero-valued {@link Usage} to seed an aggregation. Only the required
|
|
42
|
-
* token channels are set; optional channels stay absent until a
|
|
43
|
-
* contributor reports them, preserving the "never reported" vs
|
|
44
|
-
* "reported as 0" distinction.
|
|
45
|
-
*/
|
|
11
|
+
/** Seed an aggregation without claiming any optional counter was reported. */
|
|
46
12
|
function emptyUsage() {
|
|
47
13
|
return {
|
|
48
14
|
input: 0,
|
|
@@ -50,15 +16,6 @@ function emptyUsage() {
|
|
|
50
16
|
total: 0
|
|
51
17
|
};
|
|
52
18
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Add two optional token counts, treating either side's `undefined` as
|
|
55
|
-
* zero — but return `undefined` when both are absent, so an unreported
|
|
56
|
-
* channel stays unreported rather than collapsing to `0`.
|
|
57
|
-
*/
|
|
58
|
-
function sumOptional(accumulator, next) {
|
|
59
|
-
if (accumulator === void 0 && next === void 0) return;
|
|
60
|
-
return (accumulator ?? 0) + (next ?? 0);
|
|
61
|
-
}
|
|
62
19
|
|
|
63
20
|
//#endregion
|
|
64
21
|
export { emptyUsage, sumUsage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sum-usage.mjs","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/sum-usage.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"sum-usage.mjs","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/sum-usage.ts"],"sourcesContent":["import { mergeUsage, type Usage } from \"@warlock.js/ai\";\n\n/** Return an independent all-channel usage rollup, preserving hosted-tool counters. */\nexport function sumUsage(accumulator: Usage, next: Usage): Usage {\n const merged = emptyUsage();\n mergeUsage(merged, accumulator);\n mergeUsage(merged, next);\n return merged;\n}\n\n/** Seed an aggregation without claiming any optional counter was reported. */\nexport function emptyUsage(): Usage {\n return { input: 0, output: 0, total: 0 };\n}\n"],"mappings":";;;;AAGA,SAAgB,SAAS,aAAoB,MAAoB;CAC/D,MAAM,SAAS,WAAW;CAC1B,WAAW,QAAQ,WAAW;CAC9B,WAAW,QAAQ,IAAI;CACvB,OAAO;AACT;;AAGA,SAAgB,aAAoB;CAClC,OAAO;EAAE,OAAO;EAAG,QAAQ;EAAG,OAAO;CAAE;AACzC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { ModelCost, Usage } from "@warlock.js/ai";
|
|
3
2
|
//#region ../../packages/ai-panoptic/src/store/trace-aggregate.type.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Rolled-up usage + cost across a set of stored traces, returned by the
|
|
@@ -21,10 +20,14 @@ import { ModelPricing, Usage } from "@warlock.js/ai";
|
|
|
21
20
|
* console.log(`${stats.traces} runs, ${stats.usage.total} tokens`);
|
|
22
21
|
* const totalUsd = totalCost(stats.cost) ?? 0; // `totalCost` from @warlock.js/ai
|
|
23
22
|
*/
|
|
24
|
-
type TraceAggregate = {
|
|
25
|
-
/** Number of traces matched by the query. */
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
export type TraceAggregate = {
|
|
24
|
+
/** Number of traces matched by the query. */
|
|
25
|
+
traces: number;
|
|
26
|
+
/** Count of matched traces whose root status was `"completed"`. */
|
|
27
|
+
completed: number;
|
|
28
|
+
/** Count of matched traces whose root status was `"failed"` or `"incomplete"`. */
|
|
29
|
+
failed: number;
|
|
30
|
+
/** Count of matched traces whose root status was `"cancelled"`. */
|
|
28
31
|
cancelled: number;
|
|
29
32
|
/**
|
|
30
33
|
* Summed token usage across every matched trace. `input` / `output` /
|
|
@@ -36,12 +39,12 @@ type TraceAggregate = {
|
|
|
36
39
|
/**
|
|
37
40
|
* Merged per-channel USD cost across every matched trace, or
|
|
38
41
|
* `undefined` when no matched trace carried pricing. Same shape as
|
|
39
|
-
* `
|
|
42
|
+
* `ModelCost` so callers can read the input/output/cache split,
|
|
40
43
|
* not just one opaque number.
|
|
41
44
|
*/
|
|
42
|
-
cost?:
|
|
45
|
+
cost?: ModelCost;
|
|
46
|
+
/** Summed wall-clock duration in milliseconds across matched traces. */
|
|
43
47
|
totalDuration: number;
|
|
44
48
|
};
|
|
45
49
|
//#endregion
|
|
46
|
-
export { TraceAggregate };
|
|
47
50
|
//# sourceMappingURL=trace-aggregate.type.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-aggregate.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/trace-aggregate.type.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"trace-aggregate.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/trace-aggregate.type.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;YAsBY;;EAEV;;EAEA;;EAEA;;EAEA;;;;;;;EAOA,OAAO;;;;;;;EAOP,OAAO;;EAEP"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ReportStatus } from "@warlock.js/ai";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/ai-panoptic/src/store/trace-query.type.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Filter passed to the trace store's `query` / `aggregate`. Every field
|
|
@@ -21,7 +20,9 @@ import { ReportStatus } from "@warlock.js/ai";
|
|
|
21
20
|
* @example
|
|
22
21
|
* const sessionSpend = store.aggregate({ sessionId: "session-42" });
|
|
23
22
|
*/
|
|
24
|
-
type TraceQuery = {
|
|
23
|
+
export type TraceQuery = {
|
|
24
|
+
/** Exact HTTP request correlation id; absent on background traces. */
|
|
25
|
+
requestId?: string;
|
|
25
26
|
/**
|
|
26
27
|
* Match a single trace by its `traceId` (equals the root run's
|
|
27
28
|
* `runId`). The most specific filter — narrows to at most one trace.
|
|
@@ -36,10 +37,11 @@ type TraceQuery = {
|
|
|
36
37
|
* Match traces whose root terminal `status` equals this value, or any
|
|
37
38
|
* of these values when an array is given.
|
|
38
39
|
*/
|
|
39
|
-
status?: ReportStatus | ReportStatus[];
|
|
40
|
-
|
|
40
|
+
status?: ReportStatus | ReportStatus[];
|
|
41
|
+
/** Inclusive lower bound on the trace's root `startedAt`. */
|
|
42
|
+
startedAfter?: string | Date;
|
|
43
|
+
/** Inclusive upper bound on the trace's root `startedAt`. */
|
|
41
44
|
startedBefore?: string | Date;
|
|
42
45
|
};
|
|
43
46
|
//#endregion
|
|
44
|
-
export { TraceQuery };
|
|
45
47
|
//# sourceMappingURL=trace-query.type.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-query.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/trace-query.type.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"trace-query.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/trace-query.type.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;YAsBY;;EAEV;;;;;EAKA;;;;;EAKA;;;;;EAKA,SAAS,eAAe;;EAExB,wBAAwB;;EAExB,yBAAyB"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Trace } from "../contracts/trace.type.mjs";
|
|
2
2
|
import { TraceAggregate } from "./trace-aggregate.type.mjs";
|
|
3
3
|
import { TraceQuery } from "./trace-query.type.mjs";
|
|
4
|
-
|
|
5
4
|
//#region ../../packages/ai-panoptic/src/store/trace-store.contract.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Contract for a Panoptic trace store — a queryable sink that retains
|
|
@@ -26,7 +25,7 @@ import { TraceQuery } from "./trace-query.type.mjs";
|
|
|
26
25
|
* const failed = store.query({ status: "failed" });
|
|
27
26
|
* const spend = store.aggregate({ sessionId });
|
|
28
27
|
*/
|
|
29
|
-
interface TraceStoreContract {
|
|
28
|
+
export interface TraceStoreContract {
|
|
30
29
|
/**
|
|
31
30
|
* Ingest one finalized trace. Replaces any existing trace with the
|
|
32
31
|
* same `traceId` (re-collecting a run overwrites rather than
|
|
@@ -51,5 +50,4 @@ interface TraceStoreContract {
|
|
|
51
50
|
clear(): void;
|
|
52
51
|
}
|
|
53
52
|
//#endregion
|
|
54
|
-
export { TraceStoreContract };
|
|
55
53
|
//# sourceMappingURL=trace-store.contract.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-store.contract.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/trace-store.contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"trace-store.contract.d.mts","names":[],"sources":["../../../../../../../../packages/ai-panoptic/src/store/trace-store.contract.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2BiB;;;;;;EAMf,IAAI,OAAO;;EAGX,IAAI,kBAAkB;;;;;EAMtB,MAAM,SAAS,aAAa;;;;;EAM5B,UAAU,SAAS,aAAa;;WAGvB;;EAGT"}
|
package/llms-full.txt
CHANGED
|
@@ -472,6 +472,41 @@ Add exporters after construction with `observe.use(exporter)` (chainable, dedupe
|
|
|
472
472
|
- The `query-traces` topic — retaining traces in the queryable in-memory store and slicing them (`query` / `aggregate` / `capacity`).
|
|
473
473
|
- The `run-ai-agent` topic of the `warlock-js-ai` skill — running the agent and the `BaseReport` shape Panoptic consumes.
|
|
474
474
|
|
|
475
|
+
## Subscribe across collectors and inspect requests in devtools
|
|
476
|
+
|
|
477
|
+
`subscribeTraces(listener)` is a process-local adapter hook: it receives every
|
|
478
|
+
finalized trace from built-in collectors (including ones created earlier), in
|
|
479
|
+
the collecting async context, before exporter dispatch. It has no replay and
|
|
480
|
+
adds no retention. It does not enable observation or subscribe to pure
|
|
481
|
+
`toTrace()` calls. Treat incoming traces as read-only. Listener throws/rejections
|
|
482
|
+
are isolated; promises are not awaited.
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
import { subscribeTraces } from "@warlock.js/ai-panoptic";
|
|
486
|
+
import type { TraceListener } from "@warlock.js/ai-panoptic";
|
|
487
|
+
|
|
488
|
+
const onTrace: TraceListener = (trace) => {
|
|
489
|
+
console.log(trace.traceId, trace.root.status, trace.usage);
|
|
490
|
+
};
|
|
491
|
+
const unsubscribe = subscribeTraces(onTrace);
|
|
492
|
+
// ...later, on adapter teardown:
|
|
493
|
+
unsubscribe();
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
With `@warlock.js/devtools` installed, its **AI** tab at `/__warlock` subscribes
|
|
497
|
+
for you. Enable `ai.config({ panoptic: { observeAll: true } })` as above, or use
|
|
498
|
+
an explicit subscriber. No `dashboard: true` is needed. Request-local context
|
|
499
|
+
links runs to their HTTP request when available, independently of `sessionId`.
|
|
500
|
+
The panel never mounts in production.
|
|
501
|
+
|
|
502
|
+
New diagnostic attributes are `retry.attempts` (timings/outcomes),
|
|
503
|
+
`agent.cacheHits` (trips reporting positive provider cache reads),
|
|
504
|
+
`agent.truncatedTrips`, `agent.providerCompactions` and `agent.compactions`
|
|
505
|
+
(applied/skipped decisions, trip indices, token counts and replaced ranges).
|
|
506
|
+
They exclude compaction summaries and callback error text. Unknown cache hits
|
|
507
|
+
remain absent; do not infer semantic-cache hits from zero tokens. An incomplete
|
|
508
|
+
run is identified by its status or normalized `AGENT_INCOMPLETE` error code.
|
|
509
|
+
|
|
475
510
|
|
|
476
511
|
## query-traces `@warlock.js/ai-panoptic/query-traces/SKILL.md`
|
|
477
512
|
|
|
@@ -532,6 +567,7 @@ Every field is optional and **ANDed** together — an absent field is "don't car
|
|
|
532
567
|
```ts
|
|
533
568
|
type TraceQuery = {
|
|
534
569
|
traceId?: string; // exact one run
|
|
570
|
+
requestId?: string; // exact HTTP request id, when an integration supplied it
|
|
535
571
|
sessionId?: string; // every run for a session
|
|
536
572
|
status?: ReportStatus | ReportStatus[]; // single status or membership set
|
|
537
573
|
startedAfter?: string | Date; // inclusive lower bound on root startedAt
|
|
@@ -677,6 +713,54 @@ for (const trace of store.query({ sessionId })) {
|
|
|
677
713
|
- The `export-traces` topic — sending traces to an external backend instead of (or alongside) retaining them in memory; writing a custom sink.
|
|
678
714
|
- The `run-ai-agent` topic of the `warlock-js-ai` skill — the `BaseReport` the collector projects into the `Trace` this store retains.
|
|
679
715
|
|
|
716
|
+
## Hosted-tool rollups
|
|
717
|
+
|
|
718
|
+
`sumUsage` uses the framework's all-channel `mergeUsage`, so traces retain
|
|
719
|
+
`usage.serverToolUse` and `usage.cost.serverTools` alongside cache lifetime
|
|
720
|
+
counts and mixed-provider cache semantics. Request counts remain absent when
|
|
721
|
+
unreported; unpriced requests do not erase priced siblings.
|
|
722
|
+
|
|
723
|
+
```ts
|
|
724
|
+
const stats = store.aggregate({ sessionId });
|
|
725
|
+
console.log(stats.usage.serverToolUse?.webSearchRequests);
|
|
726
|
+
console.log(stats.cost?.serverTools?.webSearch); // historical USD
|
|
727
|
+
console.log(totalCost(stats.cost)); // includes hosted fees
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
## Correlate traces with HTTP requests
|
|
731
|
+
|
|
732
|
+
`Trace.requestId` is an optional integration-supplied identity. It is independent
|
|
733
|
+
of `sessionId`; legacy and background traces omit it. In-memory and cache stores
|
|
734
|
+
use the same exact-match `requestId` filter in `query()` and `aggregate()`, and
|
|
735
|
+
cache hydration preserves it.
|
|
736
|
+
|
|
737
|
+
```ts
|
|
738
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
739
|
+
import { createInMemoryTraceStore, subscribeTraces } from "@warlock.js/ai-panoptic";
|
|
740
|
+
|
|
741
|
+
// Your HTTP adapter runs the flow inside this context.
|
|
742
|
+
const httpContext = new AsyncLocalStorage<{ requestId: string }>();
|
|
743
|
+
const requests = createInMemoryTraceStore({ capacity: 200 });
|
|
744
|
+
const unsubscribe = subscribeTraces((trace) => {
|
|
745
|
+
requests.add({ ...trace, requestId: httpContext.getStore()?.requestId });
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
const runs = requests.query({ requestId: "request-42" });
|
|
749
|
+
const usage = requests.aggregate({ requestId: "request-42" });
|
|
750
|
+
unsubscribe();
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
The callback reads request context synchronously, before exporters run. It sees
|
|
754
|
+
future collection from all built-in collectors, with no replay. Treat incoming
|
|
755
|
+
traces as read-only and add an enriched copy. Errors are isolated, async
|
|
756
|
+
callbacks are not awaited, and `toTrace()` does not notify subscribers.
|
|
757
|
+
|
|
758
|
+
For app development, `@warlock.js/devtools` already does this: open the **AI** tab
|
|
759
|
+
at `/__warlock`, or an HTTP request's **AI runs** section. Its bounded,
|
|
760
|
+
content-free store is separate from your exporter/cache stores. Clearing the
|
|
761
|
+
panel does not clear application trace history. Enable flow observation first;
|
|
762
|
+
installing the viewer alone does not collect runs.
|
|
763
|
+
|
|
680
764
|
|
|
681
765
|
## use-local-dashboard `@warlock.js/ai-panoptic/use-local-dashboard/SKILL.md`
|
|
682
766
|
|
|
@@ -909,4 +993,25 @@ These three findings (link sanitization + the two token-handling items above) ca
|
|
|
909
993
|
- The `export-traces` topic — shipping traces to OTel / Langfuse / a file instead of (or alongside) the local dashboard.
|
|
910
994
|
- The `evaluate-system-prompt` topic — the drawer's "Evaluate system prompt" action, the dashboard's one write route.
|
|
911
995
|
|
|
996
|
+
## Use the app's devtools AI panel
|
|
997
|
+
|
|
998
|
+
When developing a Warlock HTTP app with `@warlock.js/devtools` installed, open
|
|
999
|
+
`/__warlock` → **AI** to inspect runs alongside their HTTP requests. This uses
|
|
1000
|
+
the existing app server and needs no `dashboard: true`:
|
|
1001
|
+
|
|
1002
|
+
```ts
|
|
1003
|
+
import { ai } from "@warlock.js/ai";
|
|
1004
|
+
import "@warlock.js/ai-panoptic";
|
|
1005
|
+
ai.config({ panoptic: { observeAll: true } });
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
Restart `warlock dev` after installing the optional Panoptic package. Runs
|
|
1009
|
+
appear after collection, with model, tokens, historical cost, provider cache
|
|
1010
|
+
hits, tool timings, retries, incomplete/truncated status and compaction metadata.
|
|
1011
|
+
The panel uses a bounded content-free in-memory view (200 traces by default),
|
|
1012
|
+
not the standalone dashboard's persisted cache store. Clear/restart discards
|
|
1013
|
+
only that view. HTTP request identity is captured from the collecting async
|
|
1014
|
+
context; missing identities remain background runs. Devtools never mounts in
|
|
1015
|
+
production and retains its socket-loopback guard.
|
|
1016
|
+
|
|
912
1017
|
|
package/package.json
CHANGED
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@opentelemetry/api": "*",
|
|
21
21
|
"@opentelemetry/sdk-trace-base": "*",
|
|
22
|
-
"@warlock.js/ai": "5.
|
|
23
|
-
"@warlock.js/cache": "5.
|
|
24
|
-
"@warlock.js/logger": "5.
|
|
22
|
+
"@warlock.js/ai": "5.33.0",
|
|
23
|
+
"@warlock.js/cache": "5.33.0",
|
|
24
|
+
"@warlock.js/logger": "5.33.0",
|
|
25
25
|
"langfuse": "*"
|
|
26
26
|
},
|
|
27
27
|
"peerDependenciesMeta": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"langfuse": "^3.38.20"
|
|
43
43
|
},
|
|
44
|
-
"version": "5.
|
|
44
|
+
"version": "5.33.0",
|
|
45
45
|
"main": "./cjs/index.cjs",
|
|
46
46
|
"module": "./esm/index.mjs",
|
|
47
47
|
"types": "./esm/index.d.mts",
|
|
@@ -173,3 +173,38 @@ Add exporters after construction with `observe.use(exporter)` (chainable, dedupe
|
|
|
173
173
|
- The `export-traces` topic — the sink end: choosing/writing an exporter, the `gen_ai.*` / `warlock.*` attribute mapping.
|
|
174
174
|
- The `query-traces` topic — retaining traces in the queryable in-memory store and slicing them (`query` / `aggregate` / `capacity`).
|
|
175
175
|
- The `run-ai-agent` topic of the `warlock-js-ai` skill — running the agent and the `BaseReport` shape Panoptic consumes.
|
|
176
|
+
|
|
177
|
+
## Subscribe across collectors and inspect requests in devtools
|
|
178
|
+
|
|
179
|
+
`subscribeTraces(listener)` is a process-local adapter hook: it receives every
|
|
180
|
+
finalized trace from built-in collectors (including ones created earlier), in
|
|
181
|
+
the collecting async context, before exporter dispatch. It has no replay and
|
|
182
|
+
adds no retention. It does not enable observation or subscribe to pure
|
|
183
|
+
`toTrace()` calls. Treat incoming traces as read-only. Listener throws/rejections
|
|
184
|
+
are isolated; promises are not awaited.
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
import { subscribeTraces } from "@warlock.js/ai-panoptic";
|
|
188
|
+
import type { TraceListener } from "@warlock.js/ai-panoptic";
|
|
189
|
+
|
|
190
|
+
const onTrace: TraceListener = (trace) => {
|
|
191
|
+
console.log(trace.traceId, trace.root.status, trace.usage);
|
|
192
|
+
};
|
|
193
|
+
const unsubscribe = subscribeTraces(onTrace);
|
|
194
|
+
// ...later, on adapter teardown:
|
|
195
|
+
unsubscribe();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
With `@warlock.js/devtools` installed, its **AI** tab at `/__warlock` subscribes
|
|
199
|
+
for you. Enable `ai.config({ panoptic: { observeAll: true } })` as above, or use
|
|
200
|
+
an explicit subscriber. No `dashboard: true` is needed. Request-local context
|
|
201
|
+
links runs to their HTTP request when available, independently of `sessionId`.
|
|
202
|
+
The panel never mounts in production.
|
|
203
|
+
|
|
204
|
+
New diagnostic attributes are `retry.attempts` (timings/outcomes),
|
|
205
|
+
`agent.cacheHits` (trips reporting positive provider cache reads),
|
|
206
|
+
`agent.truncatedTrips`, `agent.providerCompactions` and `agent.compactions`
|
|
207
|
+
(applied/skipped decisions, trip indices, token counts and replaced ranges).
|
|
208
|
+
They exclude compaction summaries and callback error text. Unknown cache hits
|
|
209
|
+
remain absent; do not infer semantic-cache hits from zero tokens. An incomplete
|
|
210
|
+
run is identified by its status or normalized `AGENT_INCOMPLETE` error code.
|
|
@@ -55,6 +55,7 @@ Every field is optional and **ANDed** together — an absent field is "don't car
|
|
|
55
55
|
```ts
|
|
56
56
|
type TraceQuery = {
|
|
57
57
|
traceId?: string; // exact one run
|
|
58
|
+
requestId?: string; // exact HTTP request id, when an integration supplied it
|
|
58
59
|
sessionId?: string; // every run for a session
|
|
59
60
|
status?: ReportStatus | ReportStatus[]; // single status or membership set
|
|
60
61
|
startedAfter?: string | Date; // inclusive lower bound on root startedAt
|
|
@@ -199,3 +200,51 @@ for (const trace of store.query({ sessionId })) {
|
|
|
199
200
|
- The `observe-with-panoptic` topic — wiring the `panoptic(...)` subscriber and bring-your-own-collector so the store fills as runs complete.
|
|
200
201
|
- The `export-traces` topic — sending traces to an external backend instead of (or alongside) retaining them in memory; writing a custom sink.
|
|
201
202
|
- The `run-ai-agent` topic of the `warlock-js-ai` skill — the `BaseReport` the collector projects into the `Trace` this store retains.
|
|
203
|
+
|
|
204
|
+
## Hosted-tool rollups
|
|
205
|
+
|
|
206
|
+
`sumUsage` uses the framework's all-channel `mergeUsage`, so traces retain
|
|
207
|
+
`usage.serverToolUse` and `usage.cost.serverTools` alongside cache lifetime
|
|
208
|
+
counts and mixed-provider cache semantics. Request counts remain absent when
|
|
209
|
+
unreported; unpriced requests do not erase priced siblings.
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
const stats = store.aggregate({ sessionId });
|
|
213
|
+
console.log(stats.usage.serverToolUse?.webSearchRequests);
|
|
214
|
+
console.log(stats.cost?.serverTools?.webSearch); // historical USD
|
|
215
|
+
console.log(totalCost(stats.cost)); // includes hosted fees
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Correlate traces with HTTP requests
|
|
219
|
+
|
|
220
|
+
`Trace.requestId` is an optional integration-supplied identity. It is independent
|
|
221
|
+
of `sessionId`; legacy and background traces omit it. In-memory and cache stores
|
|
222
|
+
use the same exact-match `requestId` filter in `query()` and `aggregate()`, and
|
|
223
|
+
cache hydration preserves it.
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
227
|
+
import { createInMemoryTraceStore, subscribeTraces } from "@warlock.js/ai-panoptic";
|
|
228
|
+
|
|
229
|
+
// Your HTTP adapter runs the flow inside this context.
|
|
230
|
+
const httpContext = new AsyncLocalStorage<{ requestId: string }>();
|
|
231
|
+
const requests = createInMemoryTraceStore({ capacity: 200 });
|
|
232
|
+
const unsubscribe = subscribeTraces((trace) => {
|
|
233
|
+
requests.add({ ...trace, requestId: httpContext.getStore()?.requestId });
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const runs = requests.query({ requestId: "request-42" });
|
|
237
|
+
const usage = requests.aggregate({ requestId: "request-42" });
|
|
238
|
+
unsubscribe();
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The callback reads request context synchronously, before exporters run. It sees
|
|
242
|
+
future collection from all built-in collectors, with no replay. Treat incoming
|
|
243
|
+
traces as read-only and add an enriched copy. Errors are isolated, async
|
|
244
|
+
callbacks are not awaited, and `toTrace()` does not notify subscribers.
|
|
245
|
+
|
|
246
|
+
For app development, `@warlock.js/devtools` already does this: open the **AI** tab
|
|
247
|
+
at `/__warlock`, or an HTTP request's **AI runs** section. Its bounded,
|
|
248
|
+
content-free store is separate from your exporter/cache stores. Clearing the
|
|
249
|
+
panel does not clear application trace history. Enable flow observation first;
|
|
250
|
+
installing the viewer alone does not collect runs.
|
|
@@ -226,3 +226,24 @@ These three findings (link sanitization + the two token-handling items above) ca
|
|
|
226
226
|
- The `query-traces` topic — the `TraceStoreContract` (`query` / `get` / `aggregate`) the dashboard reads, the `TraceQuery` filter, and capacity eviction.
|
|
227
227
|
- The `export-traces` topic — shipping traces to OTel / Langfuse / a file instead of (or alongside) the local dashboard.
|
|
228
228
|
- The `evaluate-system-prompt` topic — the drawer's "Evaluate system prompt" action, the dashboard's one write route.
|
|
229
|
+
|
|
230
|
+
## Use the app's devtools AI panel
|
|
231
|
+
|
|
232
|
+
When developing a Warlock HTTP app with `@warlock.js/devtools` installed, open
|
|
233
|
+
`/__warlock` → **AI** to inspect runs alongside their HTTP requests. This uses
|
|
234
|
+
the existing app server and needs no `dashboard: true`:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import { ai } from "@warlock.js/ai";
|
|
238
|
+
import "@warlock.js/ai-panoptic";
|
|
239
|
+
ai.config({ panoptic: { observeAll: true } });
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Restart `warlock dev` after installing the optional Panoptic package. Runs
|
|
243
|
+
appear after collection, with model, tokens, historical cost, provider cache
|
|
244
|
+
hits, tool timings, retries, incomplete/truncated status and compaction metadata.
|
|
245
|
+
The panel uses a bounded content-free in-memory view (200 traces by default),
|
|
246
|
+
not the standalone dashboard's persisted cache store. Clear/restart discards
|
|
247
|
+
only that view. HTTP request identity is captured from the collecting async
|
|
248
|
+
context; missing identities remain background runs. Devtools never mounts in
|
|
249
|
+
production and retains its socket-loopback guard.
|