@hue-run/sdk 0.6.0 → 0.8.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/CLI.md +22 -13
- package/ENVIRONMENTS.md +61 -0
- package/README.md +47 -1
- package/dist/cli/eval.js +79 -24
- package/dist/client.js +3 -2
- package/dist/config.d.ts +3 -1
- package/dist/config.js +8 -1
- package/dist/environment/client.d.ts +16 -2
- package/dist/environment/client.js +46 -3
- package/dist/environment/types.d.ts +131 -2
- package/dist/environment/world.d.ts +50 -0
- package/dist/environment/world.js +105 -0
- package/dist/environment.d.ts +2 -0
- package/dist/environment.js +1 -0
- package/dist/evals/environment-target.d.ts +53 -2
- package/dist/evals/environment-target.js +114 -10
- package/dist/evals/local-worker.d.ts +12 -5
- package/dist/evals/local-worker.js +13 -5
- package/dist/evals/runner.d.ts +1 -1
- package/dist/evals/runner.js +2 -2
- package/dist/evals/simulation.d.ts +14 -5
- package/dist/evals/simulation.js +26 -8
- package/dist/live-spans.d.ts +25 -0
- package/dist/live-spans.js +115 -0
- package/dist/transport.d.ts +17 -0
- package/dist/transport.js +390 -61
- package/dist/types.d.ts +7 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/evals/runner.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ interface RunnerOptions {
|
|
|
50
50
|
* (for example a Hue-operated grading worker that owns the evaluator source) instead of
|
|
51
51
|
* refusing the run. Their IDs are reported in `deferredScorerVersionIds`. */
|
|
52
52
|
deferUnboundLocalScorers?: boolean;
|
|
53
|
-
/** Cases in flight at once, 1–
|
|
53
|
+
/** Cases in flight at once, 1–64. Default 1. */
|
|
54
54
|
concurrency?: number;
|
|
55
55
|
/** Deadline for JSON Schema scoring in its worker, 100–60000 ms. Default 2000. */
|
|
56
56
|
schemaTimeoutMillis?: number;
|
package/dist/evals/runner.js
CHANGED
|
@@ -80,8 +80,8 @@ function settings(options) {
|
|
|
80
80
|
if (typeof options.persistResultContent !== "boolean")
|
|
81
81
|
throw new TypeError("Choose persistResultContent explicitly: true or false");
|
|
82
82
|
const concurrency = options.concurrency ?? 1;
|
|
83
|
-
if (!Number.isInteger(concurrency) || concurrency < 1 || concurrency >
|
|
84
|
-
throw new RangeError("concurrency must be 1–
|
|
83
|
+
if (!Number.isInteger(concurrency) || concurrency < 1 || concurrency > 64)
|
|
84
|
+
throw new RangeError("concurrency must be 1–64");
|
|
85
85
|
const timeout = options.schemaTimeoutMillis ?? 2000;
|
|
86
86
|
if (!Number.isInteger(timeout) || timeout < 100 || timeout > 60_000)
|
|
87
87
|
throw new RangeError("schemaTimeoutMillis must be 100–60000");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { HueClient } from "../client.js";
|
|
2
2
|
import { type EnvironmentClient } from "../environment/client.js";
|
|
3
3
|
import type { EnvironmentTool } from "../environment/tools.js";
|
|
4
|
-
import type { EnvironmentIdentity, PublishableEnvironmentDefinition } from "../environment/types.js";
|
|
4
|
+
import type { EnvironmentIdentity, PublishableEnvironmentDefinition, WorldHandoff } from "../environment/types.js";
|
|
5
5
|
import { type EvaluationClient } from "./client.js";
|
|
6
6
|
import type { ActualAgentManifestInputV2, AttemptConnectionBundleV2, RequestedAttemptProviderV2 } from "./attempt.js";
|
|
7
7
|
import { type RunnerReport } from "./runner.js";
|
|
@@ -109,10 +109,15 @@ export interface SimulationTargetContext {
|
|
|
109
109
|
executionId: string;
|
|
110
110
|
/** Stable world identity for adapter control operations such as coverage reporting. */
|
|
111
111
|
environmentRunId: string;
|
|
112
|
-
/** Framework-neutral local callables backed by this attempt's isolated world
|
|
112
|
+
/** Framework-neutral local callables backed by this attempt's isolated world; empty for a
|
|
113
|
+
* gateway world, whose calls go to the provider mirrors in `world`. */
|
|
113
114
|
tools: Record<string, EnvironmentTool>;
|
|
114
|
-
/**
|
|
115
|
-
|
|
115
|
+
/** The mirror URLs, world token, environment carriers and MCP configuration of a gateway
|
|
116
|
+
* world; absent for a world created while the gateway was off. */
|
|
117
|
+
world?: WorldHandoff;
|
|
118
|
+
/** One MCP endpoint and bearer: the gateway world's first MCP mirror with the world token, or
|
|
119
|
+
* the deprecated execution-scoped `hue_sim_` capability of a legacy world. */
|
|
120
|
+
mcp?: SimulationMcpCapability;
|
|
116
121
|
/** Credential-bearing provider connections for this callback only. Hue never
|
|
117
122
|
* checkpoints, logs or adds this response to parity digests. */
|
|
118
123
|
connectionBundle?: AttemptConnectionBundleV2;
|
|
@@ -149,7 +154,7 @@ export interface RunSimulationOptions {
|
|
|
149
154
|
};
|
|
150
155
|
/** Local scorer callbacks bound by their declared source digests. */
|
|
151
156
|
localScorers?: LocalScorer[];
|
|
152
|
-
/** Cases in flight, 1–
|
|
157
|
+
/** Cases in flight, 1–64; defaults to 1. */
|
|
153
158
|
concurrency?: number;
|
|
154
159
|
/** JSON Schema worker deadline in milliseconds. */
|
|
155
160
|
schemaTimeoutMillis?: number;
|
|
@@ -157,6 +162,10 @@ export interface RunSimulationOptions {
|
|
|
157
162
|
maxSteps?: number;
|
|
158
163
|
/** Per-world lease in seconds, 1–86400. */
|
|
159
164
|
ttlSeconds?: number;
|
|
165
|
+
/** The agent revision under test, sent on world create for the world's fingerprint. */
|
|
166
|
+
agentRevision?: string;
|
|
167
|
+
/** Emit a one-time `DeprecationWarning` when the deployment serves a legacy world; on by default. */
|
|
168
|
+
deprecationWarnings?: boolean;
|
|
160
169
|
/** Cooperative caller cancellation signal. */
|
|
161
170
|
signal?: AbortSignal;
|
|
162
171
|
/** Optional display name for the fresh experiment. */
|
package/dist/evals/simulation.js
CHANGED
|
@@ -378,10 +378,21 @@ export async function runSimulation(options) {
|
|
|
378
378
|
baseUrl: options.client.baseUrl,
|
|
379
379
|
});
|
|
380
380
|
try {
|
|
381
|
-
|
|
381
|
+
// The agent revision is part of the attempt: a resume after it changed would otherwise
|
|
382
|
+
// finish the remaining cases under a different revision than the completed ones. A
|
|
383
|
+
// checkpoint written before revisions were tracked carries the definition digest alone and
|
|
384
|
+
// still matches its definition.
|
|
385
|
+
const scenarioDigest = digest({
|
|
386
|
+
definition: definitionIdentity(definition),
|
|
387
|
+
agentRevision: options.agentRevision ?? null,
|
|
388
|
+
});
|
|
382
389
|
let attempt = await store.read("active-attempt");
|
|
383
|
-
|
|
384
|
-
|
|
390
|
+
const previousDigest = digest(definitionIdentity(definition));
|
|
391
|
+
if (attempt &&
|
|
392
|
+
attempt.stage !== "completed" &&
|
|
393
|
+
attempt.scenarioDigest !== scenarioDigest &&
|
|
394
|
+
attempt.scenarioDigest !== previousDigest)
|
|
395
|
+
throw new Error("Recover the unfinished simulation before running a changed scenario or agent revision");
|
|
385
396
|
if (!attempt || attempt.stage === "completed") {
|
|
386
397
|
attempt = {
|
|
387
398
|
scenarioDigest,
|
|
@@ -432,6 +443,8 @@ export async function runSimulation(options) {
|
|
|
432
443
|
requested,
|
|
433
444
|
maxSteps: options.maxSteps,
|
|
434
445
|
ttlSeconds: options.ttlSeconds,
|
|
446
|
+
agentRevision: options.agentRevision,
|
|
447
|
+
deprecationWarnings: options.deprecationWarnings,
|
|
435
448
|
signal: options.signal,
|
|
436
449
|
onProgress: (event) => options.onProgress?.({
|
|
437
450
|
...event,
|
|
@@ -448,11 +461,16 @@ export async function runSimulation(options) {
|
|
|
448
461
|
executionId: targetContext.executionId,
|
|
449
462
|
environmentRunId: targetContext.environmentRunId,
|
|
450
463
|
tools: targetContext.tools,
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
464
|
+
...(targetContext.world ? { world: structuredClone(targetContext.world) } : {}),
|
|
465
|
+
...(targetContext.mcp
|
|
466
|
+
? {
|
|
467
|
+
mcp: {
|
|
468
|
+
url: targetContext.mcp.url,
|
|
469
|
+
token: targetContext.mcp.token,
|
|
470
|
+
expiresAt: targetContext.mcp.expiresAt,
|
|
471
|
+
},
|
|
472
|
+
}
|
|
473
|
+
: {}),
|
|
456
474
|
...(targetContext.connectionBundle
|
|
457
475
|
? { connectionBundle: structuredClone(targetContext.connectionBundle) }
|
|
458
476
|
: {}),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Attributes } from "@opentelemetry/api";
|
|
2
|
+
import { type ReadableSpan } from "@opentelemetry/sdk-trace";
|
|
3
|
+
/**
|
|
4
|
+
* A finished span never carries the placeholder markers, whatever an application set: Hue would
|
|
5
|
+
* read such a span as a malformed placeholder and reject it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withoutPlaceholderMarkers(attributes: Attributes): Attributes;
|
|
8
|
+
/** Open spans tracked for announcement at once; later starts are not announced. */
|
|
9
|
+
export declare const MAX_LIVE_SPANS = 1024;
|
|
10
|
+
export declare const LIVE_SPAN_INTERVAL_MILLIS = 500;
|
|
11
|
+
/**
|
|
12
|
+
* Response header on every trace acknowledgement from a Hue that accepts placeholders. Without
|
|
13
|
+
* it the receiver predates them and rejects each one by its zero end time.
|
|
14
|
+
*/
|
|
15
|
+
export declare const PLACEHOLDERS_HEADER = "hue-pending-spans";
|
|
16
|
+
/** Hue's own spans and recognizable AI spans, judged from what is known when the span starts. */
|
|
17
|
+
export declare function announcesLiveSpan(span: ReadableSpan): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* A normal OTLP span announcing `span` while it runs: a new identity whose parent is the real
|
|
20
|
+
* span, the real start time and an end time of 0. The markers are added after redaction.
|
|
21
|
+
*/
|
|
22
|
+
export declare function pendingPlaceholder(span: ReadableSpan): {
|
|
23
|
+
record: ReadableSpan;
|
|
24
|
+
markers: Attributes;
|
|
25
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { SpanStatusCode } from "@opentelemetry/api";
|
|
2
|
+
import { RandomIdGenerator } from "@opentelemetry/sdk-trace";
|
|
3
|
+
import { HUE_SCOPE } from "./config.js";
|
|
4
|
+
/** Marks a placeholder for a span that has started but not ended. The value versions the shape. */
|
|
5
|
+
const PENDING_SPAN_TYPE_KEY = "hue.span_type";
|
|
6
|
+
const PENDING_SPAN_TYPE = "pending_span";
|
|
7
|
+
/** The real span's own parent. Absent when the real span is a root. */
|
|
8
|
+
const PENDING_PARENT_KEY = "hue.pending_parent_id";
|
|
9
|
+
const markerKeys = [PENDING_SPAN_TYPE_KEY, PENDING_PARENT_KEY];
|
|
10
|
+
/**
|
|
11
|
+
* A finished span never carries the placeholder markers, whatever an application set: Hue would
|
|
12
|
+
* read such a span as a malformed placeholder and reject it.
|
|
13
|
+
*/
|
|
14
|
+
export function withoutPlaceholderMarkers(attributes) {
|
|
15
|
+
if (!markerKeys.some((key) => Object.hasOwn(attributes, key)))
|
|
16
|
+
return attributes;
|
|
17
|
+
const kept = { ...attributes };
|
|
18
|
+
for (const key of markerKeys)
|
|
19
|
+
delete kept[key];
|
|
20
|
+
return kept;
|
|
21
|
+
}
|
|
22
|
+
/** Open spans tracked for announcement at once; later starts are not announced. */
|
|
23
|
+
export const MAX_LIVE_SPANS = 1024;
|
|
24
|
+
export const LIVE_SPAN_INTERVAL_MILLIS = 500;
|
|
25
|
+
/**
|
|
26
|
+
* Response header on every trace acknowledgement from a Hue that accepts placeholders. Without
|
|
27
|
+
* it the receiver predates them and rejects each one by its zero end time.
|
|
28
|
+
*/
|
|
29
|
+
export const PLACEHOLDERS_HEADER = "hue-pending-spans";
|
|
30
|
+
const MAX_PLACEHOLDER_VALUE_BYTES = 64 * 1024;
|
|
31
|
+
// Kept narrow on purpose: common application processors export only these spans, and a
|
|
32
|
+
// placeholder whose real span is filtered later would read as running until the trace stalls.
|
|
33
|
+
const livePrefixes = ["gen_ai.", "ai.", "llm.", "traceloop."];
|
|
34
|
+
// Definitions and instructions are large and rarely useful while a span runs; the real span
|
|
35
|
+
// still carries them. Copied markers would misplace the placeholder.
|
|
36
|
+
const omittedKeys = [
|
|
37
|
+
"gen_ai.tool.definitions",
|
|
38
|
+
"gen_ai.system_instructions",
|
|
39
|
+
PENDING_SPAN_TYPE_KEY,
|
|
40
|
+
PENDING_PARENT_KEY,
|
|
41
|
+
];
|
|
42
|
+
const ids = new RandomIdGenerator();
|
|
43
|
+
/** Hue's own spans and recognizable AI spans, judged from what is known when the span starts. */
|
|
44
|
+
export function announcesLiveSpan(span) {
|
|
45
|
+
if (span.instrumentationScope.name === HUE_SCOPE || span.name.startsWith("ai."))
|
|
46
|
+
return true;
|
|
47
|
+
for (const key in span.attributes)
|
|
48
|
+
if (livePrefixes.some((prefix) => key.startsWith(prefix)))
|
|
49
|
+
return true;
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
function valueBytes(value) {
|
|
53
|
+
if (typeof value === "string")
|
|
54
|
+
return Buffer.byteLength(value);
|
|
55
|
+
if (!Array.isArray(value))
|
|
56
|
+
return 8;
|
|
57
|
+
let bytes = 0;
|
|
58
|
+
for (const item of value)
|
|
59
|
+
bytes += typeof item === "string" ? Buffer.byteLength(item) : 8;
|
|
60
|
+
return bytes;
|
|
61
|
+
}
|
|
62
|
+
function placeholderAttributes(source) {
|
|
63
|
+
const attributes = {};
|
|
64
|
+
for (const key in source) {
|
|
65
|
+
if (!Object.hasOwn(source, key))
|
|
66
|
+
continue;
|
|
67
|
+
if (omittedKeys.some((omitted) => key === omitted || key.startsWith(`${omitted}.`)))
|
|
68
|
+
continue;
|
|
69
|
+
const value = source[key];
|
|
70
|
+
if (valueBytes(value) <= MAX_PLACEHOLDER_VALUE_BYTES)
|
|
71
|
+
attributes[key] = value;
|
|
72
|
+
}
|
|
73
|
+
return attributes;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A normal OTLP span announcing `span` while it runs: a new identity whose parent is the real
|
|
77
|
+
* span, the real start time and an end time of 0. The markers are added after redaction.
|
|
78
|
+
*/
|
|
79
|
+
export function pendingPlaceholder(span) {
|
|
80
|
+
const real = span.spanContext();
|
|
81
|
+
const own = {
|
|
82
|
+
traceId: real.traceId,
|
|
83
|
+
spanId: ids.generateSpanId(),
|
|
84
|
+
traceFlags: real.traceFlags,
|
|
85
|
+
...(real.traceState ? { traceState: real.traceState } : {}),
|
|
86
|
+
};
|
|
87
|
+
const parent = span.parentSpanContext?.spanId;
|
|
88
|
+
const record = {
|
|
89
|
+
name: span.name,
|
|
90
|
+
kind: span.kind,
|
|
91
|
+
spanContext: () => own,
|
|
92
|
+
// Hue re-keys a placeholder as its real span, so the flags describe the real span's parent.
|
|
93
|
+
parentSpanContext: { ...real, isRemote: span.parentSpanContext?.isRemote === true },
|
|
94
|
+
startTime: span.startTime,
|
|
95
|
+
endTime: [0, 0],
|
|
96
|
+
duration: [0, 0],
|
|
97
|
+
ended: true,
|
|
98
|
+
status: { code: SpanStatusCode.UNSET },
|
|
99
|
+
attributes: placeholderAttributes(span.attributes),
|
|
100
|
+
links: [],
|
|
101
|
+
events: [],
|
|
102
|
+
resource: span.resource,
|
|
103
|
+
instrumentationScope: span.instrumentationScope,
|
|
104
|
+
droppedAttributesCount: 0,
|
|
105
|
+
droppedEventsCount: 0,
|
|
106
|
+
droppedLinksCount: 0,
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
record,
|
|
110
|
+
markers: {
|
|
111
|
+
[PENDING_SPAN_TYPE_KEY]: PENDING_SPAN_TYPE,
|
|
112
|
+
...(parent ? { [PENDING_PARENT_KEY]: parent } : {}),
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
package/dist/transport.d.ts
CHANGED
|
@@ -50,8 +50,25 @@ export declare class HueTransport {
|
|
|
50
50
|
private closed;
|
|
51
51
|
private shutdownPromise?;
|
|
52
52
|
private flushPromise?;
|
|
53
|
+
private live;
|
|
54
|
+
private liveTimer?;
|
|
55
|
+
private liveSpans;
|
|
56
|
+
private placeholdersRejected;
|
|
57
|
+
private placeholders;
|
|
58
|
+
private placeholderSources;
|
|
59
|
+
private batchSpans?;
|
|
53
60
|
constructor(options: HueOptions);
|
|
61
|
+
/**
|
|
62
|
+
* Advisory records (placeholders) are admitted only while the queue is under a quarter of its
|
|
63
|
+
* record and byte budgets, so they never take more than a quarter from real records. They are
|
|
64
|
+
* skipped silently.
|
|
65
|
+
*/
|
|
54
66
|
private enqueue;
|
|
67
|
+
private track;
|
|
68
|
+
/** Placeholders are built lazily, so input set right after a span starts is included. */
|
|
69
|
+
private announceLiveSpans;
|
|
70
|
+
private stopLiveTimer;
|
|
71
|
+
private stopLiveSpans;
|
|
55
72
|
/** Cumulative counters and current queue gauges. */
|
|
56
73
|
getReport(): ExportReport;
|
|
57
74
|
/** Copies of the latest 128 sanitized issues, oldest first. */
|