@warlock.js/ai-panoptic 4.8.1 → 4.9.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 +12 -0
- package/cjs/index.cjs +340 -42
- package/cjs/index.cjs.map +1 -1
- package/esm/collector/collector.d.mts +1 -1
- package/esm/collector/collector.d.mts.map +1 -1
- package/esm/collector/collector.mjs +1 -1
- package/esm/collector/collector.mjs.map +1 -1
- package/esm/collector/content-capture.type.d.mts +1 -1
- package/esm/collector/content-capture.type.d.mts.map +1 -1
- package/esm/collector/extract-span-attributes.d.mts +1 -1
- package/esm/collector/extract-span-attributes.d.mts.map +1 -1
- package/esm/collector/extract-span-attributes.mjs +1 -1
- package/esm/collector/extract-span-attributes.mjs.map +1 -1
- package/esm/collector/normalize-error.d.mts +1 -1
- package/esm/collector/normalize-error.d.mts.map +1 -1
- package/esm/collector/normalize-error.mjs +1 -1
- package/esm/collector/normalize-error.mjs.map +1 -1
- package/esm/collector/report-to-span.d.mts +1 -1
- package/esm/collector/report-to-span.d.mts.map +1 -1
- package/esm/collector/report-to-span.mjs +1 -1
- package/esm/collector/report-to-span.mjs.map +1 -1
- package/esm/collector/report-to-trace.d.mts +1 -1
- package/esm/collector/report-to-trace.d.mts.map +1 -1
- package/esm/collector/report-to-trace.mjs +1 -1
- package/esm/collector/report-to-trace.mjs.map +1 -1
- package/esm/config/apply-panoptic-config.d.mts +1 -1
- package/esm/config/apply-panoptic-config.d.mts.map +1 -1
- package/esm/config/apply-panoptic-config.mjs +4 -2
- package/esm/config/apply-panoptic-config.mjs.map +1 -1
- package/esm/config/panoptic-config.type.d.mts +12 -1
- package/esm/config/panoptic-config.type.d.mts.map +1 -1
- package/esm/contracts/collector.contract.d.mts +1 -1
- package/esm/contracts/collector.contract.d.mts.map +1 -1
- package/esm/contracts/exporter.contract.d.mts +1 -1
- package/esm/contracts/exporter.contract.d.mts.map +1 -1
- package/esm/contracts/trace.type.d.mts +1 -1
- package/esm/contracts/trace.type.d.mts.map +1 -1
- package/esm/dashboard/dashboard.d.mts +1 -1
- 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 +16 -3
- package/esm/dashboard/dashboard.type.d.mts.map +1 -1
- package/esm/dashboard/index.mjs +7 -0
- package/esm/dashboard/parse-query.mjs +1 -1
- package/esm/dashboard/parse-query.mjs.map +1 -1
- package/esm/dashboard/serve.mjs +124 -9
- package/esm/dashboard/serve.mjs.map +1 -1
- package/esm/dashboard/trace-filter.d.mts +1 -1
- package/esm/dashboard/trace-filter.d.mts.map +1 -1
- package/esm/dashboard/trace-filter.mjs +1 -1
- package/esm/dashboard/trace-filter.mjs.map +1 -1
- package/esm/dashboard/ui.html.mjs +134 -5
- package/esm/dashboard/ui.html.mjs.map +1 -1
- package/esm/dashboard/warlock-logo.mjs +1 -1
- package/esm/dashboard/warlock-logo.mjs.map +1 -1
- package/esm/evaluate/evaluate-system-prompt.d.mts +19 -0
- package/esm/evaluate/evaluate-system-prompt.d.mts.map +1 -0
- package/esm/evaluate/evaluate-system-prompt.mjs +22 -0
- package/esm/evaluate/evaluate-system-prompt.mjs.map +1 -0
- package/esm/evaluate/evaluate.type.d.mts +47 -0
- package/esm/evaluate/evaluate.type.d.mts.map +1 -0
- package/esm/evaluate/extract-last-system-prompt.d.mts +19 -0
- package/esm/evaluate/extract-last-system-prompt.d.mts.map +1 -0
- package/esm/evaluate/extract-last-system-prompt.mjs +24 -0
- package/esm/evaluate/extract-last-system-prompt.mjs.map +1 -0
- package/esm/evaluate/find-span-by-id.d.mts +8 -0
- package/esm/evaluate/find-span-by-id.d.mts.map +1 -0
- package/esm/evaluate/find-span-by-id.mjs +13 -0
- package/esm/evaluate/find-span-by-id.mjs.map +1 -0
- package/esm/evaluate/index.d.mts +4 -0
- package/esm/evaluate/index.mjs +5 -0
- package/esm/exporters/console/console-exporter.d.mts +1 -1
- package/esm/exporters/console/console-exporter.d.mts.map +1 -1
- package/esm/exporters/console/console-exporter.mjs +1 -1
- package/esm/exporters/console/console-exporter.mjs.map +1 -1
- package/esm/exporters/console/console-exporter.type.d.mts +1 -1
- package/esm/exporters/console/console-exporter.type.d.mts.map +1 -1
- package/esm/exporters/console/format-span-io.d.mts +1 -1
- package/esm/exporters/console/format-span-io.d.mts.map +1 -1
- package/esm/exporters/console/format-span-io.mjs +1 -1
- package/esm/exporters/console/format-span-io.mjs.map +1 -1
- package/esm/exporters/console/format-span-line.d.mts +1 -1
- package/esm/exporters/console/format-span-line.d.mts.map +1 -1
- package/esm/exporters/console/format-span-line.mjs +1 -1
- package/esm/exporters/console/format-span-line.mjs.map +1 -1
- package/esm/exporters/file/file-exporter.d.mts +1 -1
- package/esm/exporters/file/file-exporter.d.mts.map +1 -1
- package/esm/exporters/file/file-exporter.mjs +1 -1
- package/esm/exporters/file/file-exporter.mjs.map +1 -1
- package/esm/exporters/file/file-exporter.type.d.mts +1 -1
- package/esm/exporters/file/file-exporter.type.d.mts.map +1 -1
- package/esm/exporters/langfuse/langfuse-exporter.d.mts +1 -1
- 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 +1 -1
- package/esm/exporters/langfuse/langfuse-exporter.type.d.mts.map +1 -1
- package/esm/exporters/otel/otel-api.shim.type.d.mts +1 -1
- package/esm/exporters/otel/otel-api.shim.type.d.mts.map +1 -1
- package/esm/exporters/otel/otel-exporter.d.mts +1 -1
- package/esm/exporters/otel/otel-exporter.d.mts.map +1 -1
- package/esm/exporters/otel/otel-exporter.mjs +1 -1
- package/esm/exporters/otel/otel-exporter.mjs.map +1 -1
- package/esm/exporters/otel/otel-exporter.type.d.mts +1 -1
- package/esm/exporters/otel/otel-exporter.type.d.mts.map +1 -1
- package/esm/exporters/utils/gen-ai-attributes.d.mts +1 -1
- package/esm/exporters/utils/gen-ai-attributes.d.mts.map +1 -1
- package/esm/exporters/utils/gen-ai-attributes.mjs +1 -1
- package/esm/exporters/utils/gen-ai-attributes.mjs.map +1 -1
- package/esm/exporters/utils/total-cost.d.mts +1 -1
- package/esm/exporters/utils/total-cost.d.mts.map +1 -1
- package/esm/exporters/utils/total-cost.mjs +1 -1
- package/esm/exporters/utils/total-cost.mjs.map +1 -1
- package/esm/exporters/utils/walk-spans.d.mts +1 -1
- package/esm/exporters/utils/walk-spans.d.mts.map +1 -1
- package/esm/exporters/utils/walk-spans.mjs +1 -1
- package/esm/exporters/utils/walk-spans.mjs.map +1 -1
- package/esm/index.d.mts +5 -1
- package/esm/index.mjs +6 -1
- package/esm/panoptic/panoptic-middleware.d.mts +1 -1
- package/esm/panoptic/panoptic-middleware.d.mts.map +1 -1
- package/esm/panoptic/panoptic-middleware.mjs +1 -1
- package/esm/panoptic/panoptic-middleware.mjs.map +1 -1
- package/esm/panoptic/panoptic-target.type.d.mts +1 -1
- package/esm/panoptic/panoptic-target.type.d.mts.map +1 -1
- package/esm/panoptic/panoptic.d.mts +1 -1
- package/esm/panoptic/panoptic.d.mts.map +1 -1
- package/esm/panoptic/panoptic.mjs +1 -1
- package/esm/panoptic/panoptic.mjs.map +1 -1
- package/esm/panoptic/panoptic.type.d.mts +1 -1
- package/esm/panoptic/panoptic.type.d.mts.map +1 -1
- package/esm/register.mjs +1 -1
- package/esm/register.mjs.map +1 -1
- package/esm/store/cache-trace-store.d.mts +1 -1
- package/esm/store/cache-trace-store.d.mts.map +1 -1
- package/esm/store/cache-trace-store.mjs +1 -1
- package/esm/store/cache-trace-store.mjs.map +1 -1
- package/esm/store/in-memory-trace-store.d.mts +1 -1
- package/esm/store/in-memory-trace-store.d.mts.map +1 -1
- package/esm/store/in-memory-trace-store.mjs +1 -1
- package/esm/store/in-memory-trace-store.mjs.map +1 -1
- package/esm/store/match-trace.d.mts +1 -1
- package/esm/store/match-trace.d.mts.map +1 -1
- package/esm/store/match-trace.mjs +1 -1
- package/esm/store/match-trace.mjs.map +1 -1
- package/esm/store/sum-usage.d.mts +1 -1
- package/esm/store/sum-usage.d.mts.map +1 -1
- package/esm/store/sum-usage.mjs +1 -1
- package/esm/store/sum-usage.mjs.map +1 -1
- package/esm/store/trace-aggregate.type.d.mts +1 -1
- package/esm/store/trace-aggregate.type.d.mts.map +1 -1
- package/esm/store/trace-query.type.d.mts +1 -1
- package/esm/store/trace-query.type.d.mts.map +1 -1
- package/esm/store/trace-store.contract.d.mts +1 -1
- package/esm/store/trace-store.contract.d.mts.map +1 -1
- package/llms-full.txt +95 -3
- package/llms.txt +1 -0
- package/package.json +4 -3
- package/skills/README.md +4 -0
- package/skills/evaluate-system-prompt/SKILL.md +86 -0
- package/skills/use-local-dashboard/SKILL.md +5 -3
package/cjs/index.cjs
CHANGED
|
@@ -3,8 +3,9 @@ let _warlock_js_ai = require("@warlock.js/ai");
|
|
|
3
3
|
let node_fs_promises = require("node:fs/promises");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
let node_http = require("node:http");
|
|
6
|
+
let _warlock_js_logger = require("@warlock.js/logger");
|
|
6
7
|
|
|
7
|
-
//#region
|
|
8
|
+
//#region ../ai-panoptic/src/exporters/utils/walk-spans.ts
|
|
8
9
|
/**
|
|
9
10
|
* Depth-first pre-order traversal of a {@link TraceSpan} tree, yielding
|
|
10
11
|
* the root first and then each descendant in `children` (invocation)
|
|
@@ -23,7 +24,7 @@ function* walkSpans(root) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
//#endregion
|
|
26
|
-
//#region
|
|
27
|
+
//#region ../ai-panoptic/src/collector/normalize-error.ts
|
|
27
28
|
/**
|
|
28
29
|
* Project a captured execution error onto the structural
|
|
29
30
|
* {@link TraceSpanError} shape used on a failed / cancelled span.
|
|
@@ -71,7 +72,7 @@ function pickString(value) {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
//#endregion
|
|
74
|
-
//#region
|
|
75
|
+
//#region ../ai-panoptic/src/collector/extract-span-attributes.ts
|
|
75
76
|
/**
|
|
76
77
|
* Build the free-form `TraceSpan.attributes` bag for one report node.
|
|
77
78
|
*
|
|
@@ -148,7 +149,7 @@ function addToolAttributes(attributes, extensions) {
|
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
//#endregion
|
|
151
|
-
//#region
|
|
152
|
+
//#region ../ai-panoptic/src/collector/report-to-span.ts
|
|
152
153
|
/**
|
|
153
154
|
* Project one {@link BaseReport} node — and its entire subtree — into a
|
|
154
155
|
* {@link TraceSpan}. Pure and recursive: identity, timing, status, and
|
|
@@ -281,7 +282,7 @@ function lastNonEmptyOutput(trips) {
|
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
//#endregion
|
|
284
|
-
//#region
|
|
285
|
+
//#region ../ai-panoptic/src/collector/report-to-trace.ts
|
|
285
286
|
/**
|
|
286
287
|
* Project an outermost {@link BaseReport} (one whole `.execute()` /
|
|
287
288
|
* `.invoke()` run) into a {@link Trace} — the root {@link
|
|
@@ -332,7 +333,7 @@ function reportToTrace(report, rootError, options) {
|
|
|
332
333
|
}
|
|
333
334
|
|
|
334
335
|
//#endregion
|
|
335
|
-
//#region
|
|
336
|
+
//#region ../ai-panoptic/src/collector/collector.ts
|
|
336
337
|
/**
|
|
337
338
|
* Drive the source end of the Panoptic pipeline: ingest core
|
|
338
339
|
* `@warlock.js/ai` {@link BaseReport} trees, project them into {@link
|
|
@@ -456,7 +457,7 @@ function createCollector(options = {}) {
|
|
|
456
457
|
}
|
|
457
458
|
|
|
458
459
|
//#endregion
|
|
459
|
-
//#region
|
|
460
|
+
//#region ../ai-panoptic/src/store/match-trace.ts
|
|
460
461
|
/**
|
|
461
462
|
* Test whether one trace satisfies a {@link TraceQuery}. Every declared
|
|
462
463
|
* filter field must match (logical AND); absent fields are ignored, so
|
|
@@ -498,7 +499,7 @@ function toEpoch(bound) {
|
|
|
498
499
|
}
|
|
499
500
|
|
|
500
501
|
//#endregion
|
|
501
|
-
//#region
|
|
502
|
+
//#region ../ai-panoptic/src/store/sum-usage.ts
|
|
502
503
|
/**
|
|
503
504
|
* Fold a child {@link Usage} into a running accumulator. Token channels
|
|
504
505
|
* (`input` / `output` / `total`) always sum; the optional cache /
|
|
@@ -559,7 +560,7 @@ function sumOptional(accumulator, next) {
|
|
|
559
560
|
}
|
|
560
561
|
|
|
561
562
|
//#endregion
|
|
562
|
-
//#region
|
|
563
|
+
//#region ../ai-panoptic/src/store/cache-trace-store.ts
|
|
563
564
|
const DEFAULT_PREFIX = "panoptic";
|
|
564
565
|
/**
|
|
565
566
|
* Cache-backed {@link TraceStoreContract} with a **write-through** design
|
|
@@ -844,7 +845,7 @@ function createCacheTraceStore(cache, options = {}) {
|
|
|
844
845
|
}
|
|
845
846
|
|
|
846
847
|
//#endregion
|
|
847
|
-
//#region
|
|
848
|
+
//#region ../ai-panoptic/src/store/in-memory-trace-store.ts
|
|
848
849
|
/**
|
|
849
850
|
* In-memory {@link TraceStoreContract} that doubles as an
|
|
850
851
|
* {@link ExporterContract} — register it on a collector
|
|
@@ -980,7 +981,7 @@ function createInMemoryTraceStore(options) {
|
|
|
980
981
|
}
|
|
981
982
|
|
|
982
983
|
//#endregion
|
|
983
|
-
//#region
|
|
984
|
+
//#region ../ai-panoptic/src/exporters/utils/total-cost.ts
|
|
984
985
|
/**
|
|
985
986
|
* Collapse a {@link Usage.cost} breakdown into a single USD scalar by
|
|
986
987
|
* summing every populated field. Mirrors the formula documented on
|
|
@@ -1001,7 +1002,7 @@ function totalCostUsd(usage) {
|
|
|
1001
1002
|
}
|
|
1002
1003
|
|
|
1003
1004
|
//#endregion
|
|
1004
|
-
//#region
|
|
1005
|
+
//#region ../ai-panoptic/src/exporters/utils/gen-ai-attributes.ts
|
|
1005
1006
|
/**
|
|
1006
1007
|
* Subset of the OpenTelemetry GenAI semantic-convention attribute keys
|
|
1007
1008
|
* Panoptic emits. Kept as a named constant map (not inline string
|
|
@@ -1089,7 +1090,7 @@ function mergeScalarAttributes(target, source) {
|
|
|
1089
1090
|
}
|
|
1090
1091
|
|
|
1091
1092
|
//#endregion
|
|
1092
|
-
//#region
|
|
1093
|
+
//#region ../ai-panoptic/src/exporters/console/format-span-io.ts
|
|
1093
1094
|
/** Default cap on how many characters of captured content a console line shows. */
|
|
1094
1095
|
const DEFAULT_IO_MAX_CHARS = 500;
|
|
1095
1096
|
/**
|
|
@@ -1139,7 +1140,7 @@ function stringify(value) {
|
|
|
1139
1140
|
}
|
|
1140
1141
|
|
|
1141
1142
|
//#endregion
|
|
1142
|
-
//#region
|
|
1143
|
+
//#region ../ai-panoptic/src/exporters/console/format-span-line.ts
|
|
1143
1144
|
/**
|
|
1144
1145
|
* Render a single {@link TraceSpan} as one scannable console line:
|
|
1145
1146
|
* `<status> <type> "<name>" — <duration>ms, <tokens> tok[, $<cost>]`.
|
|
@@ -1176,7 +1177,7 @@ function statusMarker(status) {
|
|
|
1176
1177
|
}
|
|
1177
1178
|
|
|
1178
1179
|
//#endregion
|
|
1179
|
-
//#region
|
|
1180
|
+
//#region ../ai-panoptic/src/exporters/console/console-exporter.ts
|
|
1180
1181
|
const EXPORTER_NAME$3 = "console";
|
|
1181
1182
|
/**
|
|
1182
1183
|
* Zero-dependency {@link ExporterContract} that prints traces to a
|
|
@@ -1267,7 +1268,7 @@ function spanDepth(root, targetSpanId, depth = 0) {
|
|
|
1267
1268
|
}
|
|
1268
1269
|
|
|
1269
1270
|
//#endregion
|
|
1270
|
-
//#region
|
|
1271
|
+
//#region ../ai-panoptic/src/exporters/file/file-exporter.ts
|
|
1271
1272
|
const EXPORTER_NAME$2 = "file";
|
|
1272
1273
|
/**
|
|
1273
1274
|
* Zero-dependency {@link ExporterContract} that appends completed traces
|
|
@@ -1358,7 +1359,7 @@ var FileTraceWriter = class {
|
|
|
1358
1359
|
};
|
|
1359
1360
|
|
|
1360
1361
|
//#endregion
|
|
1361
|
-
//#region
|
|
1362
|
+
//#region ../ai-panoptic/src/exporters/langfuse/langfuse-exporter.ts
|
|
1362
1363
|
const EXPORTER_NAME$1 = "langfuse";
|
|
1363
1364
|
let LangfuseSdk;
|
|
1364
1365
|
let isModuleExists$1 = null;
|
|
@@ -1562,7 +1563,7 @@ function toLevel(span) {
|
|
|
1562
1563
|
}
|
|
1563
1564
|
|
|
1564
1565
|
//#endregion
|
|
1565
|
-
//#region
|
|
1566
|
+
//#region ../ai-panoptic/src/exporters/otel/otel-exporter.ts
|
|
1566
1567
|
const EXPORTER_NAME = "otel";
|
|
1567
1568
|
const DEFAULT_TRACER_NAME = "@warlock.js/ai-panoptic";
|
|
1568
1569
|
let OtelApi;
|
|
@@ -1715,7 +1716,7 @@ function toEpochMillis(isoTimestamp) {
|
|
|
1715
1716
|
}
|
|
1716
1717
|
|
|
1717
1718
|
//#endregion
|
|
1718
|
-
//#region
|
|
1719
|
+
//#region ../ai-panoptic/src/panoptic/panoptic-middleware.ts
|
|
1719
1720
|
/**
|
|
1720
1721
|
* Build an {@link AgentMiddleware} that feeds a collector from the
|
|
1721
1722
|
* `execute`- and `supervisor`-level hooks. An alternative wiring to event
|
|
@@ -1781,7 +1782,7 @@ function isReport(value) {
|
|
|
1781
1782
|
}
|
|
1782
1783
|
|
|
1783
1784
|
//#endregion
|
|
1784
|
-
//#region
|
|
1785
|
+
//#region ../ai-panoptic/src/panoptic/panoptic.ts
|
|
1785
1786
|
/**
|
|
1786
1787
|
* Terminal `*.completed` events of every core primitive that carries the
|
|
1787
1788
|
* finalized `result` (and therefore the `report` tree). These fire once
|
|
@@ -1918,7 +1919,58 @@ function panoptic(options = {}) {
|
|
|
1918
1919
|
}
|
|
1919
1920
|
|
|
1920
1921
|
//#endregion
|
|
1921
|
-
//#region
|
|
1922
|
+
//#region ../ai-panoptic/src/evaluate/evaluate-system-prompt.ts
|
|
1923
|
+
/**
|
|
1924
|
+
* Grade `systemPrompt` with the configured judge model, reusing
|
|
1925
|
+
* `ai.prompts().validate()`'s own `judgePromptBody` — never a second
|
|
1926
|
+
* judging implementation. `instructionsOverride` (the dashboard's
|
|
1927
|
+
* per-run textarea) wins over `config.instructions`; with neither, the
|
|
1928
|
+
* judge falls back to `judgePromptBody`'s built-in prompt-quality rubric.
|
|
1929
|
+
*
|
|
1930
|
+
* The judge itself never throws (`judgePromptBody` degrades to an
|
|
1931
|
+
* issues-only outcome on failure) — the only thing that CAN throw here is
|
|
1932
|
+
* resolving `config.model` (a factory constructing an SDK client), which
|
|
1933
|
+
* the caller (the dashboard route) is expected to catch.
|
|
1934
|
+
*/
|
|
1935
|
+
async function evaluateSystemPrompt(systemPrompt, config, instructionsOverride) {
|
|
1936
|
+
return (0, _warlock_js_ai.judgePromptBody)(systemPrompt, typeof config.model === "function" ? await config.model() : config.model, instructionsOverride?.trim() || config.instructions);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
//#endregion
|
|
1940
|
+
//#region ../ai-panoptic/src/evaluate/extract-last-system-prompt.ts
|
|
1941
|
+
/**
|
|
1942
|
+
* Pull the LAST `{role: "system"}` message off a span's captured `input`.
|
|
1943
|
+
*
|
|
1944
|
+
* Only present under `captureContent` (off by default), and only shaped
|
|
1945
|
+
* this way for non-tool spans — either the `[system, user]` first-trip pair
|
|
1946
|
+
* or, under `fullHistory`, the full `CapturedMessage[]` conversation (see
|
|
1947
|
+
* `collector/report-to-span.ts`). "Last" (not "only") matters for
|
|
1948
|
+
* `fullHistory`: a long-running agent can carry more than one system-role
|
|
1949
|
+
* turn, and the most recent one is the one actually in effect. Returns
|
|
1950
|
+
* `undefined` for a tool span, an agent with no system prompt, or when
|
|
1951
|
+
* content capture is off — the caller treats that as "nothing to evaluate."
|
|
1952
|
+
*/
|
|
1953
|
+
function extractLastSystemPrompt(span) {
|
|
1954
|
+
if (!Array.isArray(span.input)) return;
|
|
1955
|
+
for (let index = span.input.length - 1; index >= 0; index -= 1) {
|
|
1956
|
+
const entry = span.input[index];
|
|
1957
|
+
if (entry && typeof entry === "object" && entry.role === "system") return typeof entry.content === "string" ? entry.content : void 0;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
//#endregion
|
|
1962
|
+
//#region ../ai-panoptic/src/evaluate/find-span-by-id.ts
|
|
1963
|
+
/** Depth-first search for the span with `spanId` inside a trace's span tree. */
|
|
1964
|
+
function findSpanById(root, spanId) {
|
|
1965
|
+
if (root.spanId === spanId) return root;
|
|
1966
|
+
for (const child of root.children) {
|
|
1967
|
+
const found = findSpanById(child, spanId);
|
|
1968
|
+
if (found !== void 0) return found;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
//#endregion
|
|
1973
|
+
//#region ../ai-panoptic/src/dashboard/parse-query.ts
|
|
1922
1974
|
/**
|
|
1923
1975
|
* The set of terminal statuses a stored trace can carry. Used to keep
|
|
1924
1976
|
* `parseQuery` from forwarding arbitrary `?status=` junk into the store.
|
|
@@ -1970,7 +2022,7 @@ function parseQuery(params) {
|
|
|
1970
2022
|
}
|
|
1971
2023
|
|
|
1972
2024
|
//#endregion
|
|
1973
|
-
//#region
|
|
2025
|
+
//#region ../ai-panoptic/src/dashboard/warlock-logo.ts
|
|
1974
2026
|
/**
|
|
1975
2027
|
* The Warlock logo as an inlined base64 PNG data URI (64x64, ~9KB).
|
|
1976
2028
|
* Inlined so the dashboard stays a single self-contained, offline-capable
|
|
@@ -1981,7 +2033,7 @@ function parseQuery(params) {
|
|
|
1981
2033
|
const WARLOCK_LOGO_DATA_URI = "data:image/png;base64,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";
|
|
1982
2034
|
|
|
1983
2035
|
//#endregion
|
|
1984
|
-
//#region
|
|
2036
|
+
//#region ../ai-panoptic/src/dashboard/ui.html.ts
|
|
1985
2037
|
/**
|
|
1986
2038
|
* Build the single self-contained dashboard HTML page. No external
|
|
1987
2039
|
* assets, no bundler, no framework — one inlined string that polls the
|
|
@@ -2021,7 +2073,7 @@ const WARLOCK_LOGO_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEA
|
|
|
2021
2073
|
* @param basePath Normalized mount path ending in `/` (e.g. `"/"`).
|
|
2022
2074
|
* @param title Header title shown in the page.
|
|
2023
2075
|
*/
|
|
2024
|
-
function dashboardHtml(basePath, title) {
|
|
2076
|
+
function dashboardHtml(basePath, title, evaluateEnabled = false, evaluateDefaultInstructions = "") {
|
|
2025
2077
|
const apiBase = `${basePath}api`;
|
|
2026
2078
|
const safeTitle = escapeHtml(title);
|
|
2027
2079
|
return `<!doctype html>
|
|
@@ -2202,6 +2254,18 @@ function dashboardHtml(basePath, title) {
|
|
|
2202
2254
|
.legend { margin-left: auto; display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--dim); }
|
|
2203
2255
|
.legend .grad { width: 64px; height: 8px; border-radius: 4px; background: linear-gradient(90deg, var(--surface2), var(--cost)); border: 1px solid var(--border); }
|
|
2204
2256
|
.gantt { padding: 8px 10px; }
|
|
2257
|
+
|
|
2258
|
+
/* Evaluate — the drawer's one write action (config-gated). */
|
|
2259
|
+
.eval-btn { background: transparent; border: 1px solid var(--border); color: var(--text2); cursor: pointer; font-size: 12px; padding: 4px 10px; border-radius: 6px; margin: 10px 0 0; }
|
|
2260
|
+
.eval-btn:hover:not(:disabled) { color: var(--text); border-color: var(--border2); }
|
|
2261
|
+
.eval-btn:disabled { opacity: .6; cursor: default; }
|
|
2262
|
+
.eval-panel { margin-top: 8px; padding: 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface2); }
|
|
2263
|
+
.eval-panel textarea { width: 100%; min-height: 64px; background: var(--panel); color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 8px; font: 12px/1.5 ui-sans-serif, system-ui, sans-serif; resize: vertical; }
|
|
2264
|
+
.eval-actions { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
|
|
2265
|
+
.eval-error { color: var(--fail); font-size: 12px; }
|
|
2266
|
+
.eval-result { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); font-size: 12px; }
|
|
2267
|
+
.eval-score { font-weight: 600; }
|
|
2268
|
+
.eval-issues { margin: 6px 0 0; padding-left: 18px; }
|
|
2205
2269
|
.grow { display: flex; align-items: center; gap: 8px; padding: 2px 0; font-size: 12px; cursor: pointer; border-radius: 4px; }
|
|
2206
2270
|
.grow:hover { background: var(--surface); }
|
|
2207
2271
|
.grow.selected { background: var(--sel-bg); }
|
|
@@ -2266,7 +2330,21 @@ function dashboardHtml(basePath, title) {
|
|
|
2266
2330
|
<script>
|
|
2267
2331
|
(function () {
|
|
2268
2332
|
var API = ${JSON.stringify(apiBase)};
|
|
2333
|
+
var EVALUATE_ENABLED = ${JSON.stringify(evaluateEnabled)};
|
|
2269
2334
|
var BT = String.fromCharCode(96);
|
|
2335
|
+
var EVALUATE_DEFAULT_INSTRUCTIONS = ${encodeForInlineScript(evaluateDefaultInstructions)};
|
|
2336
|
+
|
|
2337
|
+
// Carry the ?token= the page itself was loaded with onto every
|
|
2338
|
+
// subsequent poll — otherwise the API calls below inherit no auth and
|
|
2339
|
+
// 401 forever once authToken is configured (the initial page load is
|
|
2340
|
+
// the only request the URL's query string naturally reaches).
|
|
2341
|
+
var TOKEN = new URLSearchParams(window.location.search).get("token");
|
|
2342
|
+
function fetchAuthed(url, options) {
|
|
2343
|
+
var opts = options || {};
|
|
2344
|
+
var headers = opts.headers || {};
|
|
2345
|
+
if (TOKEN) headers = Object.assign({}, headers, { Authorization: "Bearer " + TOKEN });
|
|
2346
|
+
return fetch(url, Object.assign({}, opts, { headers: headers }));
|
|
2347
|
+
}
|
|
2270
2348
|
|
|
2271
2349
|
var state = {
|
|
2272
2350
|
traces: [], selectedId: null, selectedSpanId: null, collapsed: {}, sig: null,
|
|
@@ -2278,7 +2356,8 @@ function dashboardHtml(basePath, title) {
|
|
|
2278
2356
|
showStats: false, // per-type aggregate-stats panel toggle (independent of grouping)
|
|
2279
2357
|
collapsedGroups: {}, // collapsed group headers (session, prompt, or type)
|
|
2280
2358
|
view: "tree", // drawer left pane: "tree" | "timeline"
|
|
2281
|
-
hashApplied: false
|
|
2359
|
+
hashApplied: false, // guards one-time deep-link open on load
|
|
2360
|
+
evaluate: {} // per-span evaluate UI state, keyed by spanId
|
|
2282
2361
|
};
|
|
2283
2362
|
|
|
2284
2363
|
var STATUS_FILTERS = ["completed", "failed", "cancelled"];
|
|
@@ -2987,6 +3066,80 @@ function dashboardHtml(basePath, title) {
|
|
|
2987
3066
|
return '<div class="meta-sec"><div class="meta-title">metadata</div><div class="kv">' + kv + "</div></div>";
|
|
2988
3067
|
}
|
|
2989
3068
|
|
|
3069
|
+
// --- Evaluate: grade a span's last captured system prompt -------------
|
|
3070
|
+
// Config-gated (EVALUATE_ENABLED) — the drawer's only write action, POSTing
|
|
3071
|
+
// to a route that itself only exists when the server was configured with
|
|
3072
|
+
// evaluate. UI state lives in state.evaluate, keyed by spanId, so it
|
|
3073
|
+
// survives a re-render (e.g. switching to a sibling span and back).
|
|
3074
|
+
function extractLastSystemPrompt(span) {
|
|
3075
|
+
if (!Array.isArray(span.input)) return null;
|
|
3076
|
+
for (var i = span.input.length - 1; i >= 0; i--) {
|
|
3077
|
+
var m = span.input[i];
|
|
3078
|
+
if (m && typeof m === "object" && m.role === "system" && typeof m.content === "string") return m.content;
|
|
3079
|
+
}
|
|
3080
|
+
return null;
|
|
3081
|
+
}
|
|
3082
|
+
function evalState(spanId) {
|
|
3083
|
+
return state.evaluate[spanId] || (state.evaluate[spanId] = {
|
|
3084
|
+
open: false, instructions: EVALUATE_DEFAULT_INSTRUCTIONS, status: "idle", result: null, error: null
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
function evalResultHtml(result) {
|
|
3088
|
+
var score = typeof result.score === "number" ? Math.round(result.score * 100) + "%" : "n/a";
|
|
3089
|
+
var issues = (result.issues || []).map(function (i) { return "<li>" + esc(i) + "</li>"; }).join("");
|
|
3090
|
+
return '<div class="eval-result"><span class="eval-score">Score: ' + score + "</span>"
|
|
3091
|
+
+ (issues ? '<ul class="eval-issues">' + issues + "</ul>" : "") + "</div>";
|
|
3092
|
+
}
|
|
3093
|
+
function evalSectionHtml(span) {
|
|
3094
|
+
if (!EVALUATE_ENABLED) return "";
|
|
3095
|
+
var sysPrompt = extractLastSystemPrompt(span);
|
|
3096
|
+
if (!sysPrompt) return "";
|
|
3097
|
+
var st = evalState(span.spanId);
|
|
3098
|
+
var btn = '<button type="button" class="eval-btn" data-evaluate-toggle="' + esc(span.spanId) + '">'
|
|
3099
|
+
+ (st.open ? "Hide evaluate" : "Evaluate system prompt") + "</button>";
|
|
3100
|
+
if (!st.open) return btn;
|
|
3101
|
+
var running = st.status === "running";
|
|
3102
|
+
return btn + '<div class="eval-panel">'
|
|
3103
|
+
+ '<textarea id="eval-instructions" placeholder="Grading instructions (optional — falls back to the configured default)"' + (running ? " disabled" : "") + ">" + esc(st.instructions || "") + "</textarea>"
|
|
3104
|
+
+ '<div class="eval-actions">'
|
|
3105
|
+
+ '<button type="button" class="eval-btn" data-evaluate-run="' + esc(span.spanId) + '"' + (running ? " disabled" : "") + ">" + (running ? "Evaluating…" : "Run") + "</button>"
|
|
3106
|
+
+ (st.error ? '<span class="eval-error">' + esc(st.error) + "</span>" : "")
|
|
3107
|
+
+ "</div>"
|
|
3108
|
+
+ (st.result ? evalResultHtml(st.result) : "")
|
|
3109
|
+
+ "</div>";
|
|
3110
|
+
}
|
|
3111
|
+
function rerenderDetail() {
|
|
3112
|
+
var t = findTrace(state.selectedId);
|
|
3113
|
+
if (!t) return;
|
|
3114
|
+
var sel = findSpan(t.root, state.selectedSpanId) || t.root;
|
|
3115
|
+
document.getElementById("drawer-detail").innerHTML = renderDetail(sel, t);
|
|
3116
|
+
}
|
|
3117
|
+
function toggleEvalPanel(spanId) {
|
|
3118
|
+
evalState(spanId).open = !evalState(spanId).open;
|
|
3119
|
+
rerenderDetail();
|
|
3120
|
+
}
|
|
3121
|
+
function runEvaluate(traceId, spanId) {
|
|
3122
|
+
var st = evalState(spanId);
|
|
3123
|
+
var textarea = document.getElementById("eval-instructions");
|
|
3124
|
+
if (textarea) st.instructions = textarea.value;
|
|
3125
|
+
st.status = "running"; st.error = null;
|
|
3126
|
+
rerenderDetail();
|
|
3127
|
+
fetchAuthed(API + "/traces/" + encodeURIComponent(traceId) + "/spans/" + encodeURIComponent(spanId) + "/evaluate", {
|
|
3128
|
+
method: "POST",
|
|
3129
|
+
headers: { "content-type": "application/json" },
|
|
3130
|
+
body: JSON.stringify({ instructions: st.instructions })
|
|
3131
|
+
}).then(function (r) {
|
|
3132
|
+
return r.json().then(function (data) { return { ok: r.ok, data: data }; });
|
|
3133
|
+
}).then(function (res) {
|
|
3134
|
+
if (res.ok) { st.status = "done"; st.result = res.data; st.error = null; }
|
|
3135
|
+
else { st.status = "error"; st.error = (res.data && (res.data.message || res.data.error)) || "evaluate failed"; st.result = null; }
|
|
3136
|
+
rerenderDetail();
|
|
3137
|
+
}).catch(function () {
|
|
3138
|
+
st.status = "error"; st.error = "network error"; st.result = null;
|
|
3139
|
+
rerenderDetail();
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
|
|
2990
3143
|
function renderDetail(span, trace) {
|
|
2991
3144
|
var path = findPath(trace.root, span.spanId) || [span];
|
|
2992
3145
|
var crumb = path.map(function (p, i) { return (i ? '<span class="sep"> › </span>' : "") + "<span>" + esc(p.name) + "</span>"; }).join("");
|
|
@@ -3009,6 +3162,7 @@ function dashboardHtml(basePath, title) {
|
|
|
3009
3162
|
+ '<span class="dim">· ' + dur(span.duration) + "</span></div>"
|
|
3010
3163
|
+ metaLine
|
|
3011
3164
|
+ io
|
|
3165
|
+
+ evalSectionHtml(span, trace)
|
|
3012
3166
|
+ renderMeta(span, trace);
|
|
3013
3167
|
}
|
|
3014
3168
|
|
|
@@ -3171,9 +3325,20 @@ function dashboardHtml(basePath, title) {
|
|
|
3171
3325
|
if (vb) { setView(vb.getAttribute("data-view")); return; }
|
|
3172
3326
|
var tog = e.target.closest ? e.target.closest("[data-toggle]") : null;
|
|
3173
3327
|
if (tog) { toggleSpan(tog.getAttribute("data-toggle")); return; }
|
|
3328
|
+
var evalToggle = e.target.closest ? e.target.closest("[data-evaluate-toggle]") : null;
|
|
3329
|
+
if (evalToggle) { toggleEvalPanel(evalToggle.getAttribute("data-evaluate-toggle")); return; }
|
|
3330
|
+
var evalRun = e.target.closest ? e.target.closest("[data-evaluate-run]") : null;
|
|
3331
|
+
if (evalRun && state.selectedId) { runEvaluate(state.selectedId, evalRun.getAttribute("data-evaluate-run")); return; }
|
|
3174
3332
|
var node = e.target.closest ? e.target.closest(".tnode[data-span], .grow[data-span]") : null;
|
|
3175
3333
|
if (node) selectSpan(node.getAttribute("data-span"));
|
|
3176
3334
|
});
|
|
3335
|
+
// Track the instructions textarea live (no re-render on keystroke, so
|
|
3336
|
+
// typing never loses focus/cursor position).
|
|
3337
|
+
document.getElementById("drawer-body").addEventListener("input", function (e) {
|
|
3338
|
+
if (e.target && e.target.id === "eval-instructions" && state.selectedSpanId) {
|
|
3339
|
+
evalState(state.selectedSpanId).instructions = e.target.value;
|
|
3340
|
+
}
|
|
3341
|
+
});
|
|
3177
3342
|
document.getElementById("backdrop").addEventListener("click", closeDrawer);
|
|
3178
3343
|
document.addEventListener("keydown", function (e) { if (e.key === "Escape" || e.keyCode === 27) closeDrawer(); });
|
|
3179
3344
|
|
|
@@ -3269,8 +3434,8 @@ function dashboardHtml(basePath, title) {
|
|
|
3269
3434
|
|
|
3270
3435
|
function poll() {
|
|
3271
3436
|
Promise.all([
|
|
3272
|
-
|
|
3273
|
-
|
|
3437
|
+
fetchAuthed(API + "/aggregate").then(function (r) { return r.json(); }),
|
|
3438
|
+
fetchAuthed(API + "/traces").then(function (r) { return r.json(); })
|
|
3274
3439
|
]).then(function (res) {
|
|
3275
3440
|
renderStats(res[0]);
|
|
3276
3441
|
state.traces = res[1] || [];
|
|
@@ -3305,9 +3470,27 @@ function dashboardHtml(basePath, title) {
|
|
|
3305
3470
|
function escapeHtml(value) {
|
|
3306
3471
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3307
3472
|
}
|
|
3473
|
+
/**
|
|
3474
|
+
* Encode free-form text (e.g. `evaluate.instructions`, which an operator
|
|
3475
|
+
* could type anything into) as a JS EXPRESSION that reconstructs it at
|
|
3476
|
+
* runtime, WITHOUT ever emitting a literal backtick into the served page —
|
|
3477
|
+
* the client script is itself built from a TS template literal, so a raw
|
|
3478
|
+
* backtick in the output would be a real syntax hazard (see the `BT =
|
|
3479
|
+
* String.fromCharCode(96)` construction already in the client script for
|
|
3480
|
+
* the same reason). `JSON.stringify` alone doesn't escape backticks (they
|
|
3481
|
+
* aren't JSON-significant), so a value containing one is split around it
|
|
3482
|
+
* and rejoined with the client's own `BT` constant. No backticks in the
|
|
3483
|
+
* input ⇒ a single plain `JSON.stringify(value)` — no unnecessary
|
|
3484
|
+
* concatenation in the common case.
|
|
3485
|
+
*/
|
|
3486
|
+
function encodeForInlineScript(value) {
|
|
3487
|
+
return value.split("`").map((part) => JSON.stringify(part)).join(" + BT + ");
|
|
3488
|
+
}
|
|
3308
3489
|
|
|
3309
3490
|
//#endregion
|
|
3310
|
-
//#region
|
|
3491
|
+
//#region ../ai-panoptic/src/dashboard/serve.ts
|
|
3492
|
+
/** Hard cap on the evaluate route's request body — well beyond a real instructions string. */
|
|
3493
|
+
const MAX_EVALUATE_BODY_BYTES = 64 * 1024;
|
|
3311
3494
|
/**
|
|
3312
3495
|
* Security response headers added to every dashboard response (S4):
|
|
3313
3496
|
* block MIME-sniffing, framing, referrer leakage, and lock the page's
|
|
@@ -3337,15 +3520,22 @@ function isAuthorized(req, url, token) {
|
|
|
3337
3520
|
* trace store. Kept separate from the server lifecycle so it can be unit
|
|
3338
3521
|
* tested by feeding it a fake `req`/`res` without binding a port.
|
|
3339
3522
|
*
|
|
3340
|
-
* Routes (all under `config.basePath
|
|
3523
|
+
* Routes (all under `config.basePath`):
|
|
3341
3524
|
*
|
|
3342
3525
|
* - `GET api/traces` → `store.query(parseQuery(searchParams))`
|
|
3343
3526
|
* - `GET api/traces/:id` → `store.get(id)` or `404`
|
|
3344
3527
|
* - `GET api/aggregate` → `store.aggregate(parseQuery(searchParams))`
|
|
3345
3528
|
* - `GET {basePath}` → the self-contained HTML page
|
|
3529
|
+
* - `POST api/traces/:traceId/spans/:spanId/evaluate` → ONLY when
|
|
3530
|
+
* `config.evaluate` is set; grades the span's last captured system
|
|
3531
|
+
* prompt and returns a {@link EvaluateVerdict}. The dashboard's one
|
|
3532
|
+
* write route — absent config, POST 405s like it would against any
|
|
3533
|
+
* other route (the path is never even pattern-matched).
|
|
3346
3534
|
*
|
|
3347
|
-
* Anything else → `404`.
|
|
3348
|
-
*
|
|
3535
|
+
* Anything else → `404`. A method the matched route doesn't accept → `405`.
|
|
3536
|
+
* Host allowlist + bearer-token auth (S4) are checked for EVERY request,
|
|
3537
|
+
* regardless of method, before any routing. The store shapes
|
|
3538
|
+
* ({@link Trace}/{@link TraceSpan}) are already JSON-safe, so GET responses
|
|
3349
3539
|
* are a plain `JSON.stringify` with no serializer.
|
|
3350
3540
|
*
|
|
3351
3541
|
* @example
|
|
@@ -3356,10 +3546,6 @@ function createRequestHandler(store, config) {
|
|
|
3356
3546
|
const base = config.basePath;
|
|
3357
3547
|
const apiPrefix = `${base}api`;
|
|
3358
3548
|
return function handle(req, res) {
|
|
3359
|
-
if (req.method !== "GET") {
|
|
3360
|
-
sendJson(res, 405, { error: "method_not_allowed" });
|
|
3361
|
-
return;
|
|
3362
|
-
}
|
|
3363
3549
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
3364
3550
|
const pathname = url.pathname;
|
|
3365
3551
|
const host = hostHeaderName(req.headers.host);
|
|
@@ -3371,6 +3557,17 @@ function createRequestHandler(store, config) {
|
|
|
3371
3557
|
sendJson(res, 401, { error: "unauthorized" });
|
|
3372
3558
|
return;
|
|
3373
3559
|
}
|
|
3560
|
+
if (req.method === "POST" && config.evaluate) {
|
|
3561
|
+
const match = matchEvaluatePath(pathname, apiPrefix);
|
|
3562
|
+
if (match) {
|
|
3563
|
+
handleEvaluate(store, config.evaluate, match.traceId, match.spanId, req, res);
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
if (req.method !== "GET") {
|
|
3568
|
+
sendJson(res, 405, { error: "method_not_allowed" });
|
|
3569
|
+
return;
|
|
3570
|
+
}
|
|
3374
3571
|
if (pathname === `${apiPrefix}/traces`) {
|
|
3375
3572
|
sendJson(res, 200, store.query(parseQuery(url.searchParams)));
|
|
3376
3573
|
return;
|
|
@@ -3397,7 +3594,7 @@ function createRequestHandler(store, config) {
|
|
|
3397
3594
|
"content-type": "text/html; charset=utf-8",
|
|
3398
3595
|
...SECURITY_HEADERS
|
|
3399
3596
|
});
|
|
3400
|
-
res.end(dashboardHtml(base, config.title));
|
|
3597
|
+
res.end(dashboardHtml(base, config.title, Boolean(config.evaluate), config.evaluate?.instructions ?? ""));
|
|
3401
3598
|
return;
|
|
3402
3599
|
}
|
|
3403
3600
|
sendJson(res, 404, { error: "not_found" });
|
|
@@ -3411,9 +3608,105 @@ function sendJson(res, status, body) {
|
|
|
3411
3608
|
});
|
|
3412
3609
|
res.end(JSON.stringify(body));
|
|
3413
3610
|
}
|
|
3611
|
+
/**
|
|
3612
|
+
* Match `{apiPrefix}/traces/:traceId/spans/:spanId/evaluate` — the one
|
|
3613
|
+
* write route. Returns `undefined` for anything else, including the plain
|
|
3614
|
+
* `{apiPrefix}/traces/:id` read route (no `/spans/.../evaluate` suffix),
|
|
3615
|
+
* so the two never collide.
|
|
3616
|
+
*/
|
|
3617
|
+
function matchEvaluatePath(pathname, apiPrefix) {
|
|
3618
|
+
const prefix = `${apiPrefix}/traces/`;
|
|
3619
|
+
if (!pathname.startsWith(prefix)) return;
|
|
3620
|
+
const match = /^([^/]+)\/spans\/([^/]+)\/evaluate$/.exec(pathname.slice(prefix.length));
|
|
3621
|
+
if (!match) return;
|
|
3622
|
+
return {
|
|
3623
|
+
traceId: decodeURIComponent(match[1]),
|
|
3624
|
+
spanId: decodeURIComponent(match[2])
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
/**
|
|
3628
|
+
* Collect and JSON-parse a request body, capped at
|
|
3629
|
+
* {@link MAX_EVALUATE_BODY_BYTES} so an oversized body can't hold the
|
|
3630
|
+
* connection open indefinitely. An empty body resolves to `undefined` —
|
|
3631
|
+
* the evaluate route treats that as "no per-run override".
|
|
3632
|
+
*/
|
|
3633
|
+
function readJsonBody(req) {
|
|
3634
|
+
return new Promise((resolve, reject) => {
|
|
3635
|
+
const chunks = [];
|
|
3636
|
+
let size = 0;
|
|
3637
|
+
req.on("data", (chunk) => {
|
|
3638
|
+
size += chunk.length;
|
|
3639
|
+
if (size > MAX_EVALUATE_BODY_BYTES) {
|
|
3640
|
+
req.destroy();
|
|
3641
|
+
reject(/* @__PURE__ */ new Error("payload_too_large"));
|
|
3642
|
+
return;
|
|
3643
|
+
}
|
|
3644
|
+
chunks.push(chunk);
|
|
3645
|
+
});
|
|
3646
|
+
req.on("end", () => {
|
|
3647
|
+
if (chunks.length === 0) {
|
|
3648
|
+
resolve(void 0);
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
try {
|
|
3652
|
+
resolve(JSON.parse(Buffer.concat(chunks).toString("utf-8")));
|
|
3653
|
+
} catch {
|
|
3654
|
+
reject(/* @__PURE__ */ new Error("invalid_json"));
|
|
3655
|
+
}
|
|
3656
|
+
});
|
|
3657
|
+
req.on("error", reject);
|
|
3658
|
+
});
|
|
3659
|
+
}
|
|
3660
|
+
/**
|
|
3661
|
+
* Handle `POST {apiPrefix}/traces/:traceId/spans/:spanId/evaluate`. Looks
|
|
3662
|
+
* up the trace + span, extracts its last captured system prompt, and
|
|
3663
|
+
* grades it via {@link evaluateSystemPrompt}. `judgePromptBody` itself
|
|
3664
|
+
* never throws (a broken judge degrades to an issues-only outcome) — the
|
|
3665
|
+
* try/catch here only guards `config.evaluate.model` resolution, the one
|
|
3666
|
+
* step that CAN throw (e.g. a factory constructing an SDK client).
|
|
3667
|
+
*/
|
|
3668
|
+
async function handleEvaluate(store, evaluate, traceId, spanId, req, res) {
|
|
3669
|
+
const trace = store.get(traceId);
|
|
3670
|
+
if (trace === void 0) {
|
|
3671
|
+
sendJson(res, 404, {
|
|
3672
|
+
error: "trace_not_found",
|
|
3673
|
+
traceId
|
|
3674
|
+
});
|
|
3675
|
+
return;
|
|
3676
|
+
}
|
|
3677
|
+
const span = findSpanById(trace.root, spanId);
|
|
3678
|
+
if (span === void 0) {
|
|
3679
|
+
sendJson(res, 404, {
|
|
3680
|
+
error: "span_not_found",
|
|
3681
|
+
spanId
|
|
3682
|
+
});
|
|
3683
|
+
return;
|
|
3684
|
+
}
|
|
3685
|
+
const systemPrompt = extractLastSystemPrompt(span);
|
|
3686
|
+
if (systemPrompt === void 0) {
|
|
3687
|
+
sendJson(res, 422, { error: "no_system_prompt" });
|
|
3688
|
+
return;
|
|
3689
|
+
}
|
|
3690
|
+
let body;
|
|
3691
|
+
try {
|
|
3692
|
+
body = await readJsonBody(req);
|
|
3693
|
+
} catch (error) {
|
|
3694
|
+
const message = error instanceof Error ? error.message : "invalid_json";
|
|
3695
|
+
sendJson(res, message === "payload_too_large" ? 413 : 400, { error: message });
|
|
3696
|
+
return;
|
|
3697
|
+
}
|
|
3698
|
+
try {
|
|
3699
|
+
sendJson(res, 200, await evaluateSystemPrompt(systemPrompt, evaluate, body?.instructions));
|
|
3700
|
+
} catch (error) {
|
|
3701
|
+
sendJson(res, 502, {
|
|
3702
|
+
error: "evaluate_failed",
|
|
3703
|
+
message: error instanceof Error ? error.message : String(error)
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3414
3707
|
|
|
3415
3708
|
//#endregion
|
|
3416
|
-
//#region
|
|
3709
|
+
//#region ../ai-panoptic/src/dashboard/dashboard.ts
|
|
3417
3710
|
const DEFAULT_PORT = 4319;
|
|
3418
3711
|
const DEFAULT_HOST = "127.0.0.1";
|
|
3419
3712
|
const DEFAULT_TITLE = "Panoptic";
|
|
@@ -3452,7 +3745,8 @@ function dashboard(store, options = {}) {
|
|
|
3452
3745
|
basePath,
|
|
3453
3746
|
title,
|
|
3454
3747
|
authToken: options.authToken,
|
|
3455
|
-
allowedHosts
|
|
3748
|
+
allowedHosts,
|
|
3749
|
+
evaluate: options.evaluate
|
|
3456
3750
|
}));
|
|
3457
3751
|
return new Promise((resolve, reject) => {
|
|
3458
3752
|
const onError = (error) => {
|
|
@@ -3540,7 +3834,7 @@ function openBrowser(url) {
|
|
|
3540
3834
|
}
|
|
3541
3835
|
|
|
3542
3836
|
//#endregion
|
|
3543
|
-
//#region
|
|
3837
|
+
//#region ../ai-panoptic/src/dashboard/trace-filter.ts
|
|
3544
3838
|
/** Statuses the errors-only toggle keeps. */
|
|
3545
3839
|
const ERROR_STATUSES = ["failed", "cancelled"];
|
|
3546
3840
|
/**
|
|
@@ -3808,7 +4102,7 @@ function maxNodeCost(root) {
|
|
|
3808
4102
|
}
|
|
3809
4103
|
|
|
3810
4104
|
//#endregion
|
|
3811
|
-
//#region
|
|
4105
|
+
//#region ../ai-panoptic/src/config/apply-panoptic-config.ts
|
|
3812
4106
|
let applied;
|
|
3813
4107
|
/**
|
|
3814
4108
|
* Read a {@link PanopticConfig} and wire panoptic onto the core observe
|
|
@@ -3839,7 +4133,8 @@ function applyPanopticConfig(config) {
|
|
|
3839
4133
|
let cacheStore;
|
|
3840
4134
|
if (store === void 0 && config.dashboard) {
|
|
3841
4135
|
if (config.cache !== void 0) {
|
|
3842
|
-
|
|
4136
|
+
const onError = config.onError ?? ((error) => _warlock_js_logger.log.error("ai-panoptic", "cacheStore", error));
|
|
4137
|
+
cacheStore = createCacheTraceStore(config.cache, { onError });
|
|
3843
4138
|
store = cacheStore;
|
|
3844
4139
|
} else store = createInMemoryTraceStore();
|
|
3845
4140
|
exporters.push(store);
|
|
@@ -3889,7 +4184,7 @@ function startDashboard(store, dashboardConfig) {
|
|
|
3889
4184
|
}
|
|
3890
4185
|
|
|
3891
4186
|
//#endregion
|
|
3892
|
-
//#region
|
|
4187
|
+
//#region ../ai-panoptic/src/register.ts
|
|
3893
4188
|
(0, _warlock_js_ai.onConfigApplied)((config) => {
|
|
3894
4189
|
applyPanopticConfig(config.panoptic);
|
|
3895
4190
|
});
|
|
@@ -3910,9 +4205,12 @@ exports.createInMemoryTraceStore = createInMemoryTraceStore;
|
|
|
3910
4205
|
exports.createPanopticMiddleware = createPanopticMiddleware;
|
|
3911
4206
|
exports.dashboard = dashboard;
|
|
3912
4207
|
exports.emptyUsage = emptyUsage;
|
|
4208
|
+
exports.evaluateSystemPrompt = evaluateSystemPrompt;
|
|
4209
|
+
exports.extractLastSystemPrompt = extractLastSystemPrompt;
|
|
3913
4210
|
exports.extractSpanAttributes = extractSpanAttributes;
|
|
3914
4211
|
exports.fileExporter = fileExporter;
|
|
3915
4212
|
exports.filterTraces = filterTraces;
|
|
4213
|
+
exports.findSpanById = findSpanById;
|
|
3916
4214
|
exports.formatSpanIO = formatSpanIO;
|
|
3917
4215
|
exports.formatSpanLine = formatSpanLine;
|
|
3918
4216
|
exports.groupByPrompt = groupByPrompt;
|