@hue-run/sdk 0.11.4 → 0.12.1
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 +11 -6
- package/EVALUATIONS.md +27 -9
- package/dist/cli/eval.js +48 -9
- package/dist/client.d.ts +3 -0
- package/dist/client.js +36 -24
- package/dist/evals/client.d.ts +25 -5
- package/dist/evals/client.js +93 -15
- package/dist/evals/environment-target.d.ts +15 -8
- package/dist/evals/environment-target.js +61 -21
- package/dist/evals/failure.d.ts +18 -0
- package/dist/evals/failure.js +150 -0
- package/dist/evals/local-worker.d.ts +39 -4
- package/dist/evals/local-worker.js +158 -34
- package/dist/evals/runner.d.ts +45 -2
- package/dist/evals/runner.js +207 -14
- package/dist/evals/simulation.d.ts +1 -1
- package/dist/evals/types.d.ts +12 -4
- package/dist/evals.d.ts +4 -4
- package/dist/evals.js +2 -2
- package/dist/transport.d.ts +7 -0
- package/dist/transport.js +76 -15
- package/dist/types.d.ts +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CLI.md
CHANGED
|
@@ -42,7 +42,7 @@ project manifest are refused because managers can update ancestor locks; Python
|
|
|
42
42
|
The generated `hue.setup.mjs` or `hue_setup.py` always selects `captureContent: false` /
|
|
43
43
|
`capture_content=False`. For a supported application, setup installs the dependency and adds the
|
|
44
44
|
managed import and middleware registration to the existing entrypoint; an unreferenced helper is
|
|
45
|
-
not a completed integration. TypeScript uses `@hue-run/sdk@0.
|
|
45
|
+
not a completed integration. TypeScript uses `@hue-run/sdk@0.12.1`, `@opentelemetry/api@1.9.1` and
|
|
46
46
|
`@opentelemetry/context-async-hooks@2.11.0`; Python setup uses its separately tested package pin.
|
|
47
47
|
Content capture requires an ordinary account-managed key and a later explicit application decision.
|
|
48
48
|
|
|
@@ -469,7 +469,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
|
|
|
469
469
|
# Set project to an existing supported fixture; use the same directory on resume.
|
|
470
470
|
project=/absolute/path/to/supported-fixture
|
|
471
471
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
472
|
-
--archive .artifacts/typescript/hue-run-sdk-0.
|
|
472
|
+
--archive .artifacts/typescript/hue-run-sdk-0.12.1.tgz \
|
|
473
473
|
--origin https://STAGING_ORIGIN \
|
|
474
474
|
--project "$project" --command setup \
|
|
475
475
|
--evidence .context/setup-staging-before-claim.json
|
|
@@ -480,7 +480,7 @@ the private local handoff and finish the real browser claim, then reconcile the
|
|
|
480
480
|
|
|
481
481
|
```sh
|
|
482
482
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
483
|
-
--archive .artifacts/typescript/hue-run-sdk-0.
|
|
483
|
+
--archive .artifacts/typescript/hue-run-sdk-0.12.1.tgz \
|
|
484
484
|
--origin https://STAGING_ORIGIN \
|
|
485
485
|
--project "$project" --command claim \
|
|
486
486
|
--evidence .context/setup-staging-after-claim.json
|
|
@@ -618,6 +618,11 @@ for example, needs a reviewed `attemptBaselineV2` (see
|
|
|
618
618
|
registers none, so put `"attemptBaselineV2"` in the run's configuration JSON when you launch the
|
|
619
619
|
run from Hue.
|
|
620
620
|
|
|
621
|
+
`--trace-not-accepted pending` completes a case whose telemetry Hue did not accept in time in its
|
|
622
|
+
true state with the trace still to arrive, carrying the count of spans the case ended; Hue scores
|
|
623
|
+
the sealed world at once and attaches the trace once its export is complete. The default,
|
|
624
|
+
`fail_case`, completes such a case as failed with its evidence omitted.
|
|
625
|
+
|
|
621
626
|
A Scenario or eval set whose dataset version is not saved cannot back an experiment: the command
|
|
622
627
|
exits 1 and asks for **Save eval-set version** in Hue or `--save-version`, which freezes that
|
|
623
628
|
version at its current revision. Connection settings are `HUE_API_KEY` and `HUE_BASE_URL`
|
|
@@ -663,9 +668,9 @@ an interrupted run without invoking the agent again; a different selection is re
|
|
|
663
668
|
unfinished one is resumed or its directory is removed.
|
|
664
669
|
|
|
665
670
|
Exit codes: `0` every case passed the evaluators that apply to it (advisory ones never count),
|
|
666
|
-
`1` a case failed, errored (including one no pinned evaluator applies to or decides), was skipped
|
|
667
|
-
or
|
|
668
|
-
still pending at `--wait`, `2` usage or configuration error (including a missing key), `130`
|
|
671
|
+
`1` a case failed, errored (including one no pinned evaluator applies to or decides), was skipped,
|
|
672
|
+
was inconclusive (a required judge could not run or answer; the table says `INCONCLUSIVE` and the
|
|
673
|
+
summary counts it apart from skipped cases) or Hue's checks were still pending at `--wait`, `2` usage or configuration error (including a missing key), `130`
|
|
669
674
|
interrupted. On Node.js 22, load TypeScript adapters with `NODE_OPTIONS=--experimental-strip-types`;
|
|
670
675
|
non-erasable syntax (enums, parameter properties, namespaces) needs a loader such as `--import tsx`
|
|
671
676
|
on any Node.js version.
|
package/EVALUATIONS.md
CHANGED
|
@@ -116,7 +116,7 @@ Both choices are required and independent:
|
|
|
116
116
|
|
|
117
117
|
Frozen dataset inputs/references already exist on Hue. Their presence is independent of these switches. No output is inferred from telemetry. JavaScript `undefined` means unavailable output; JSON `null`, false, zero and empty string remain present values. Historical subjects with unavailable output produce skipped results without executing a scorer callback. A failed quality metric remains `state:"scored"` with `passed:false`. Target errors and scorer errors remain separate. Target error types are generalized to `TargetError`; when content is enabled, bounded messages may be stored. Never put credentials in error messages, output, custom metric values, names or metadata.
|
|
118
118
|
|
|
119
|
-
`traceEvidence:{mode:"required"}` waits for trace and log export acknowledgement after the root span ends. The explicit alternative `{mode:"omit",reason:"..."}` stores the omission reason and declared trace ID without a fake snapshot. It can complete despite an export failure; export diagnostics remain available on the Hue client. There is no automatic fallback to omission. When required telemetry is not accepted, the runner by default keeps the saved outcome, leaves the execution started and rejects (`traceNotAccepted: "stop"`). With `traceNotAccepted: "fail_case"` it instead completes that case as failed (a `TelemetryNotAccepted` error, the evidence omitted with a reason starting `telemetry_not_accepted`, and no output or generated files attached, so no scorer can pass it), calls `onTelemetryNotAccepted(entry)` once, as soon as it completes the case (a resumed call only lists it), lists it in `report.telemetryNotAccepted` with sanitized issue counts, and goes on; `hue eval` uses this. A scorer that grades only the world can still score such a case, so treat a listed case as failed whatever its results say, as `hue eval` does.
|
|
119
|
+
`traceEvidence:{mode:"required"}` waits for trace and log export acknowledgement after the root span ends. The transport is shared by every case in flight, so each export issue names the traces of the records it concerned (`ExportIssue.traceIds`): a refused or rejected batch, a dropped record, a capture failure inside a case. A failure that names the case's trace is that case's own and final (its checkpoint reads `failed`, and resume refuses it); one that names only other traces leaves the case accepted; one that names no trace (a processor flush that failed) or a rejection in a batch of several traces is decided by the case's trace receipt, at once or on resume for a checkpoint still `pending`: the receipt must hold the case's root span and at least as many spans as the case ended (the checkpoint records the count at the flush; without it the receipt does not accept). The explicit alternative `{mode:"omit",reason:"..."}` stores the omission reason and declared trace ID without a fake snapshot. It can complete despite an export failure; export diagnostics remain available on the Hue client. There is no automatic fallback to omission. When required telemetry is not accepted, the runner by default keeps the saved outcome, leaves the execution started and rejects (`traceNotAccepted: "stop"`). With `traceNotAccepted: "fail_case"` it instead completes that case as failed (a `TelemetryNotAccepted` error, the evidence omitted with a reason starting `telemetry_not_accepted`, and no output or generated files attached, so no scorer can pass it), calls `onTelemetryNotAccepted(entry)` once, as soon as it completes the case (a resumed call only lists it), lists it in `report.telemetryNotAccepted` with sanitized issue counts, and goes on; `hue eval` uses this. A scorer that grades only the world can still score such a case, so treat a listed case as failed whatever its results say, as `hue eval` does. `traceNotAccepted:"pending"` completes a case whose telemetry Hue did not accept in time in its true state with `traceEvidence:"pending"` and the count of spans the case ended (`traceSpanCount`): Hue scores the sealed world at once, attaches the trace once it holds every span (a root span alone is not the trace), or records the evidence omitted a day later; such cases are listed in `RunnerReport.tracePending`. A saved outcome whose export the receipt does not accept on resume completes the same way. `hue eval --trace-not-accepted pending` chooses it for the CLI.
|
|
120
120
|
|
|
121
121
|
## Local scorers
|
|
122
122
|
|
|
@@ -252,14 +252,30 @@ without invoking the target or scorers. A lost preparation acknowledgement remai
|
|
|
252
252
|
and is never recovered through binding reads, credential refresh or target replay. Synthetic
|
|
253
253
|
acceptance does not contact official Gmail or claim universal provider parity.
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
255
|
+
The worker does not die with one failure. A read, or a mutation the server deduplicates by its
|
|
256
|
+
idempotency key (the worker's own register, claim, heartbeat and completion included), is sent
|
|
257
|
+
again after a connection failure, a timeout or a 408 or 5xx that carried no `Retry-After`, up to
|
|
258
|
+
`maxAttempts` (default 4) times; a mutation without a key is sent once. A registration or claim
|
|
259
|
+
that still fails transiently is reported through `onPollError` and polled again with a growing
|
|
260
|
+
wait; a refusal Hue decided on (a revoked key, a disabled agent) is thrown. A transient failure
|
|
261
|
+
of a completion or result upload keeps the run claimed by the durable worker identity: the next
|
|
262
|
+
claim returns it to the same process, which resumes saved uploads without invoking the candidate
|
|
263
|
+
again (`onRunFailed` says `retry`), and after `maxRunAttempts` (default 5) such failures the run
|
|
264
|
+
is given up as `attention` for a project member to requeue or cancel from its run page; a failure
|
|
265
|
+
that would recur (a refusal Hue decided on, a case file whose bytes differ from its manifest) is
|
|
266
|
+
given up at once. A lost
|
|
267
|
+
world-seal acknowledgement is recovered by reading authoritative world state. If the seal or
|
|
268
|
+
candidate outcome cannot be confirmed, or an outcome cannot be serialized, the run is given up
|
|
269
|
+
as `attention` at once and the worker goes on to other runs; when Hue cannot record that either,
|
|
270
|
+
the worker stops with the error. Presenting the same uncertain checkpoint again cannot replay the
|
|
271
|
+
candidate. `maxRuns` counts runs that settled either way. Inside a run, one case's own failure
|
|
272
|
+
does not stop the cases beside it; a failure every case would share (Hue not answering or
|
|
273
|
+
refusing, telemetry not accepted, a world that could not be sealed) stops new cases from
|
|
274
|
+
starting until the run resumes. Each world's lifetime is `worldTtlSeconds` (the server's default
|
|
275
|
+
is an hour): a target still running past it is told through `context.signal`, its case ends as
|
|
276
|
+
an `error` of type `TargetTimeout` and the world is sealed abandoned. Public package acceptance
|
|
277
|
+
proves this lifecycle against local fixtures; exact installed-registry-package to hosted-facade
|
|
278
|
+
acceptance remains a post-publication Fern gate.
|
|
263
279
|
|
|
264
280
|
## Direct cases and files
|
|
265
281
|
|
|
@@ -355,6 +371,8 @@ files directory must be owned by the current user and closed to everyone else (m
|
|
|
355
371
|
target again; if result content is not persisted, the JSON output cannot be reconstructed and the
|
|
356
372
|
case is reported as uncertain.
|
|
357
373
|
|
|
374
|
+
A target that throws completes its execution as `error` with a type that says what stopped the case. `TargetError` is the agent's own failure. A service the agent called that refused with a retryable status (408, 429 or 5xx, such as a model provider's rate limit or outage, when the error carries the exchange: headers, a response or a URL) is `ServiceRefused`; a connection that failed (`ECONNRESET`, `ECONNREFUSED`, `fetch failed`, an `APIConnectionError`) is `ConnectionFailed`; a call that timed out (`TimeoutError`, `APITimeoutError`) is `TimedOut`. The runner reads the error's `cause`, `lastError` and `errors` chains for these signals. Hue counts the three as infrastructure and leaves them out of the agent's pass rate; a refusal the agent caused (a 400 from a model provider or another service) stays `TargetError`. Any other status Hue's own clients answered with (a 4xx: a key, a project, an environment version or a world Hue refused) is `ConfigurationRejected`, which Hue counts as configuration and also leaves out of the agent's pass rate: an error from Hue's own clients is never the agent's. The clients fetch with `redirect: "error"`, so a redirect from the configured endpoint surfaces without a status, as `ConnectionFailed`. A runner of your own that completes an execution with a `type` of its own can say which class Hue files it under with the error's `class`.
|
|
375
|
+
|
|
358
376
|
`rescore` downloads a subject's frozen `files` — the pinned inputs and the generated outputs, with
|
|
359
377
|
`role: "output"` for the documents a run produced — so a code evaluator can grade saved documents
|
|
360
378
|
without invoking an agent. Local scorers receive them as `context.files` exactly as they do during
|
package/dist/cli/eval.js
CHANGED
|
@@ -83,16 +83,28 @@ Output and limits:
|
|
|
83
83
|
--save-version Freeze an unsaved eval-set version before running
|
|
84
84
|
--checkpoint-dir <path> Private checkpoint directory (default: .hue/eval/<agent-key>)
|
|
85
85
|
--concurrency <n> Cases in flight, 1-64 (default: 1)
|
|
86
|
+
--trace-not-accepted <policy> A case whose telemetry Hue did not accept in time: fail_case
|
|
87
|
+
(default) completes it as failed with the evidence omitted;
|
|
88
|
+
pending completes it in its true state with the trace still to
|
|
89
|
+
arrive, which Hue attaches once its export is complete
|
|
86
90
|
--timeout <seconds> Per-case --command timeout (default: 600)
|
|
87
91
|
--wait <seconds> Verdict wait after the run finishes (default: 300)
|
|
88
92
|
-h, --help Show this help
|
|
89
93
|
|
|
90
94
|
HUE_API_KEY must be a "Read and write" project key; it is never printed.
|
|
91
95
|
Code evaluators pinned to a direct run are graded by Hue's executor after the upload; the wait
|
|
92
|
-
covers them. Exit codes: 0 every case passed, 1 a case failed, errored or is
|
|
93
|
-
2 usage error, 130 interrupted.
|
|
96
|
+
covers them. Exit codes: 0 every case passed, 1 a case failed, errored, is inconclusive or is
|
|
97
|
+
incomplete, 2 usage error, 130 interrupted.
|
|
94
98
|
`;
|
|
95
99
|
/** Thrown for invalid arguments or configuration; exits with status 2. */
|
|
100
|
+
/** The `--trace-not-accepted` policy: the failed-case default, or pending evidence. */
|
|
101
|
+
function traceNotAcceptedPolicy(value) {
|
|
102
|
+
if (value === undefined || value === "fail_case")
|
|
103
|
+
return "fail_case";
|
|
104
|
+
if (value === "pending")
|
|
105
|
+
return "pending";
|
|
106
|
+
throw new UsageError("--trace-not-accepted must be fail_case or pending");
|
|
107
|
+
}
|
|
96
108
|
class UsageError extends Error {
|
|
97
109
|
constructor(message) {
|
|
98
110
|
super(message);
|
|
@@ -129,6 +141,7 @@ function parse(argv) {
|
|
|
129
141
|
json: { type: "boolean", default: false },
|
|
130
142
|
content: { type: "boolean", default: false },
|
|
131
143
|
"no-output": { type: "boolean", default: false },
|
|
144
|
+
"trace-not-accepted": { type: "string" },
|
|
132
145
|
"save-version": { type: "boolean", default: false },
|
|
133
146
|
"checkpoint-dir": { type: "string" },
|
|
134
147
|
concurrency: { type: "string" },
|
|
@@ -702,6 +715,11 @@ const STATE_LABEL = {
|
|
|
702
715
|
skipped: "SKIPPED",
|
|
703
716
|
pending: "PENDING",
|
|
704
717
|
};
|
|
718
|
+
/** A skipped case whose deciding evaluator could not decide it, as an answer-graded case whose
|
|
719
|
+
* required judge could not run or answer: Hue's explanation begins "Inconclusive:". It is named
|
|
720
|
+
* apart from a case its evaluators did not grade, since it needs a judge, not another pin. */
|
|
721
|
+
const inconclusive = (item) => item.state === "skipped" && item.explanations.some((text) => /^inconclusive:/i.test(text));
|
|
722
|
+
const stateLabel = (item) => inconclusive(item) ? "INCONCLUSIVE" : STATE_LABEL[item.state];
|
|
705
723
|
export function renderTable(verdicts, output) {
|
|
706
724
|
const cases = verdicts.summary.cases;
|
|
707
725
|
// A column per metric name; a name several evaluators report, such as two judges' `verdict`,
|
|
@@ -736,7 +754,7 @@ export function renderTable(verdicts, output) {
|
|
|
736
754
|
// reporting the metric shows "-".
|
|
737
755
|
return item.notApplicable?.includes(column.version) ? "n/a" : "-";
|
|
738
756
|
}),
|
|
739
|
-
|
|
757
|
+
stateLabel(item),
|
|
740
758
|
]);
|
|
741
759
|
const widths = header.map((cell, index) => Math.max(cell.length, ...rows.map((row) => row[index].length)));
|
|
742
760
|
const line = (cells) => cells
|
|
@@ -751,15 +769,18 @@ export function renderTable(verdicts, output) {
|
|
|
751
769
|
if (item.state === "passed")
|
|
752
770
|
continue;
|
|
753
771
|
const details = [...item.errors.map((type) => `scorer error ${type}`), ...item.explanations];
|
|
754
|
-
for (const detail of details.length ? details : [
|
|
772
|
+
for (const detail of details.length ? details : [stateLabel(item).toLowerCase()])
|
|
755
773
|
output.log(` ${item.externalKey}: ${detail}`);
|
|
756
774
|
}
|
|
757
775
|
const totals = verdicts.summary.totals;
|
|
776
|
+
const inconclusiveCount = cases.filter(inconclusive).length;
|
|
777
|
+
const skipped = totals.skipped - inconclusiveCount;
|
|
758
778
|
const advisoryFailures = cases.reduce((count, item) => count +
|
|
759
779
|
item.metrics.filter((metric) => item.advisory?.includes(metric.scorerVersionId) && !metricPassed(metric)).length, 0);
|
|
760
780
|
const extra = [
|
|
761
781
|
totals.error ? `${totals.error} error` : "",
|
|
762
|
-
|
|
782
|
+
inconclusiveCount ? `${inconclusiveCount} inconclusive` : "",
|
|
783
|
+
skipped > 0 ? `${skipped} skipped` : "",
|
|
763
784
|
totals.pending ? `${totals.pending} pending` : "",
|
|
764
785
|
totals.notApplicable
|
|
765
786
|
? `${totals.notApplicable} evaluator result${totals.notApplicable === 1 ? "" : "s"} not applicable`
|
|
@@ -1116,7 +1137,7 @@ async function runOnce(values, connection, agents, agent, hue, output, signal) {
|
|
|
1116
1137
|
persistResultContent: !values["no-output"],
|
|
1117
1138
|
traceEvidence: { mode: "required" },
|
|
1118
1139
|
// A case whose telemetry Hue did not accept fails instead of staying started.
|
|
1119
|
-
traceNotAccepted: "
|
|
1140
|
+
traceNotAccepted: traceNotAcceptedPolicy(values["trace-not-accepted"]),
|
|
1120
1141
|
onTelemetryNotAccepted: telemetry.report,
|
|
1121
1142
|
concurrency,
|
|
1122
1143
|
agentRevision: agent.revision,
|
|
@@ -1221,7 +1242,7 @@ async function runDirect(run, connection) {
|
|
|
1221
1242
|
// only the telemetry.
|
|
1222
1243
|
persistResultContent: !values["no-output"],
|
|
1223
1244
|
traceEvidence: { mode: "required" },
|
|
1224
|
-
traceNotAccepted: "
|
|
1245
|
+
traceNotAccepted: traceNotAcceptedPolicy(values["trace-not-accepted"]),
|
|
1225
1246
|
onTelemetryNotAccepted: telemetry.report,
|
|
1226
1247
|
concurrency: run.concurrency,
|
|
1227
1248
|
scorers: [],
|
|
@@ -1297,11 +1318,25 @@ async function runWorker(values, connection, adapter, agent, hue, output, signal
|
|
|
1297
1318
|
},
|
|
1298
1319
|
scorers: [],
|
|
1299
1320
|
concurrency,
|
|
1300
|
-
traceNotAccepted: "
|
|
1321
|
+
traceNotAccepted: traceNotAcceptedPolicy(values["trace-not-accepted"]),
|
|
1301
1322
|
onTelemetryNotAccepted: telemetry.report,
|
|
1302
1323
|
deprecationWarnings: false,
|
|
1303
1324
|
signal,
|
|
1304
1325
|
...(maxRuns === undefined ? {} : { maxRuns }),
|
|
1326
|
+
onPollError(error) {
|
|
1327
|
+
output.error(`Hue did not answer the poll (${explain(error)}); polling again`);
|
|
1328
|
+
},
|
|
1329
|
+
onRunFailed(failure) {
|
|
1330
|
+
const run = `Run ${failure.runId}`;
|
|
1331
|
+
if (failure.outcome === "retry") {
|
|
1332
|
+
const waitMillis = failure.waitMillis ?? 0;
|
|
1333
|
+
output.error(`${run} attempt ${failure.attempt} failed (${explain(failure.error)}); resuming in ${Math.round(waitMillis / 1000)} s`);
|
|
1334
|
+
}
|
|
1335
|
+
else if (failure.outcome === "attention")
|
|
1336
|
+
output.error(`${run} needs attention (${explain(failure.error)}); requeue or cancel it from its run page`);
|
|
1337
|
+
else
|
|
1338
|
+
output.error(`${run} was released or cancelled in Hue; its outcome here is not recorded`);
|
|
1339
|
+
},
|
|
1305
1340
|
target(inputs, tools, context) {
|
|
1306
1341
|
output.log(`[${context.item.externalKey}] agent started`);
|
|
1307
1342
|
return adapter(inputs, {
|
|
@@ -1315,7 +1350,9 @@ async function runWorker(values, connection, adapter, agent, hue, output, signal
|
|
|
1315
1350
|
...(context.connectionBundle ? { connectionBundle: context.connectionBundle } : {}),
|
|
1316
1351
|
files: context.files,
|
|
1317
1352
|
outputDirectory: context.outputDirectory,
|
|
1318
|
-
signal,
|
|
1353
|
+
// The case's own signal: the worker stopping, or the world's deadline passing, both
|
|
1354
|
+
// end a command that would otherwise keep running past its timed-out case.
|
|
1355
|
+
signal: context.signal ?? signal,
|
|
1319
1356
|
});
|
|
1320
1357
|
},
|
|
1321
1358
|
async onCompleted(report) {
|
|
@@ -1386,6 +1423,8 @@ export async function runEvalCommand(argv) {
|
|
|
1386
1423
|
let json = false;
|
|
1387
1424
|
try {
|
|
1388
1425
|
const { values, positionals } = parse(argv);
|
|
1426
|
+
// A usage error before anything is prepared or created: no run is left to recover from it.
|
|
1427
|
+
traceNotAcceptedPolicy(values["trace-not-accepted"]);
|
|
1389
1428
|
json = values.json;
|
|
1390
1429
|
const output = {
|
|
1391
1430
|
log: (line) => (json ? process.stderr : process.stdout).write(`${line}\n`),
|
package/dist/client.d.ts
CHANGED
|
@@ -43,6 +43,9 @@ export declare class HueClient {
|
|
|
43
43
|
readonly enabled: boolean;
|
|
44
44
|
private logger;
|
|
45
45
|
private storage;
|
|
46
|
+
/** Reports a contained capture or instrumentation failure, named to the trace active where
|
|
47
|
+
* it happened when there is one. */
|
|
48
|
+
private instrumentationFailed;
|
|
46
49
|
private tracerProvider;
|
|
47
50
|
private loggerProvider;
|
|
48
51
|
private ownedProviders?;
|
package/dist/client.js
CHANGED
|
@@ -169,6 +169,20 @@ export class HueClient {
|
|
|
169
169
|
enabled;
|
|
170
170
|
logger;
|
|
171
171
|
storage = new AsyncLocalStorage();
|
|
172
|
+
/** Reports a contained capture or instrumentation failure, named to the trace active where
|
|
173
|
+
* it happened when there is one. */
|
|
174
|
+
instrumentationFailed(signal = "traces", count = 1) {
|
|
175
|
+
let traceId;
|
|
176
|
+
try {
|
|
177
|
+
traceId = trace
|
|
178
|
+
.getSpan(this.storage.getStore()?.context ?? context.active())
|
|
179
|
+
?.spanContext().traceId;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// The failure is recorded without a trace.
|
|
183
|
+
}
|
|
184
|
+
this.transport.instrumentationFailure(signal, undefined, count, traceId);
|
|
185
|
+
}
|
|
172
186
|
tracerProvider;
|
|
173
187
|
loggerProvider;
|
|
174
188
|
ownedProviders;
|
|
@@ -210,7 +224,7 @@ export class HueClient {
|
|
|
210
224
|
}
|
|
211
225
|
this.captureContent = this.transport.options.captureContent;
|
|
212
226
|
this.enabled = this.transport.options.enabled !== false;
|
|
213
|
-
this.tracer = new ContextualTracer(this.tracerProvider.getTracer(HUE_SCOPE, sdkVersion), this.storage, () => this.enabled && !this.closed, () => this.
|
|
227
|
+
this.tracer = new ContextualTracer(this.tracerProvider.getTracer(HUE_SCOPE, sdkVersion), this.storage, () => this.enabled && !this.closed, () => this.instrumentationFailed());
|
|
214
228
|
this.logger = this.loggerProvider.getLogger(HUE_SCOPE, sdkVersion);
|
|
215
229
|
}
|
|
216
230
|
/**
|
|
@@ -232,7 +246,7 @@ export class HueClient {
|
|
|
232
246
|
return this.storage.getStore()?.context ?? context.active();
|
|
233
247
|
}
|
|
234
248
|
catch {
|
|
235
|
-
this.
|
|
249
|
+
this.instrumentationFailed();
|
|
236
250
|
return ROOT_CONTEXT;
|
|
237
251
|
}
|
|
238
252
|
}
|
|
@@ -256,7 +270,7 @@ export class HueClient {
|
|
|
256
270
|
span = this.storage.run(active, () => this.tracer.startSpan(name, { kind: options.kind ?? SpanKind.INTERNAL, attributes: options.attributes }, active.context));
|
|
257
271
|
}
|
|
258
272
|
catch {
|
|
259
|
-
this.
|
|
273
|
+
this.instrumentationFailed();
|
|
260
274
|
}
|
|
261
275
|
}
|
|
262
276
|
// A disabled or failed client creates no span; the application's own active span then stays
|
|
@@ -268,7 +282,7 @@ export class HueClient {
|
|
|
268
282
|
spanContext = trace.setSpan(active.context, span);
|
|
269
283
|
}
|
|
270
284
|
catch {
|
|
271
|
-
this.
|
|
285
|
+
this.instrumentationFailed();
|
|
272
286
|
spanContext = trace.setSpan(ROOT_CONTEXT, span);
|
|
273
287
|
}
|
|
274
288
|
const handle = {
|
|
@@ -287,7 +301,7 @@ export class HueClient {
|
|
|
287
301
|
handle.setInput(options.input);
|
|
288
302
|
}
|
|
289
303
|
catch {
|
|
290
|
-
this.
|
|
304
|
+
this.instrumentationFailed();
|
|
291
305
|
}
|
|
292
306
|
try {
|
|
293
307
|
return await callback(handle);
|
|
@@ -303,9 +317,7 @@ export class HueClient {
|
|
|
303
317
|
// The span is also OpenTelemetry's active span while the callback runs, so spans from other
|
|
304
318
|
// instrumentations (HTTP clients, provider SDKs) join this trace when the application has
|
|
305
319
|
// registered a context manager. Hue still registers none itself.
|
|
306
|
-
return this.storage.run({ ...active, context: spanContext }, () => created
|
|
307
|
-
? runInContext(spanContext, execute, () => this.transport.instrumentationFailure())
|
|
308
|
-
: execute());
|
|
320
|
+
return this.storage.run({ ...active, context: spanContext }, () => created ? runInContext(spanContext, execute, () => this.instrumentationFailed()) : execute());
|
|
309
321
|
}
|
|
310
322
|
/**
|
|
311
323
|
* Runs `execute` inside an `execute_tool` span named after the tool. `input` and a defined result
|
|
@@ -329,7 +341,7 @@ export class HueClient {
|
|
|
329
341
|
if (isLabel(value))
|
|
330
342
|
attributes[key] = value;
|
|
331
343
|
else if (this.enabled && !this.closed)
|
|
332
|
-
this.
|
|
344
|
+
this.instrumentationFailed();
|
|
333
345
|
};
|
|
334
346
|
stamp("gen_ai.tool.call.id", options.callId);
|
|
335
347
|
stamp("mcp.server.name", options.mcp?.name);
|
|
@@ -364,7 +376,7 @@ export class HueClient {
|
|
|
364
376
|
if (isLabel(value))
|
|
365
377
|
return value;
|
|
366
378
|
if (active)
|
|
367
|
-
this.
|
|
379
|
+
this.instrumentationFailed();
|
|
368
380
|
return fallback;
|
|
369
381
|
};
|
|
370
382
|
const requestModel = label(model, "unknown");
|
|
@@ -413,14 +425,14 @@ export class HueClient {
|
|
|
413
425
|
if (value === undefined)
|
|
414
426
|
continue;
|
|
415
427
|
if (!Number.isInteger(value) || value < 0) {
|
|
416
|
-
this.
|
|
428
|
+
this.instrumentationFailed();
|
|
417
429
|
continue;
|
|
418
430
|
}
|
|
419
431
|
try {
|
|
420
432
|
span.setAttribute(key, value);
|
|
421
433
|
}
|
|
422
434
|
catch {
|
|
423
|
-
this.
|
|
435
|
+
this.instrumentationFailed();
|
|
424
436
|
}
|
|
425
437
|
}
|
|
426
438
|
}
|
|
@@ -433,7 +445,7 @@ export class HueClient {
|
|
|
433
445
|
propagator.inject(activeContext, carrier, defaultTextMapSetter);
|
|
434
446
|
}
|
|
435
447
|
catch {
|
|
436
|
-
this.
|
|
448
|
+
this.instrumentationFailed();
|
|
437
449
|
}
|
|
438
450
|
}
|
|
439
451
|
/** Reads W3C trace context from a carrier for use as `parentContext`. */
|
|
@@ -442,7 +454,7 @@ export class HueClient {
|
|
|
442
454
|
return propagator.extract(ROOT_CONTEXT, carrier, defaultTextMapGetter);
|
|
443
455
|
}
|
|
444
456
|
catch {
|
|
445
|
-
this.
|
|
457
|
+
this.instrumentationFailed();
|
|
446
458
|
return ROOT_CONTEXT;
|
|
447
459
|
}
|
|
448
460
|
}
|
|
@@ -461,7 +473,7 @@ export class HueClient {
|
|
|
461
473
|
span.addEvent("exception", { "exception.type": type });
|
|
462
474
|
}
|
|
463
475
|
catch {
|
|
464
|
-
this.
|
|
476
|
+
this.instrumentationFailed();
|
|
465
477
|
}
|
|
466
478
|
}
|
|
467
479
|
/**
|
|
@@ -498,7 +510,7 @@ export class HueClient {
|
|
|
498
510
|
else if (typeof explicit === "string" && explicit.trim() && explicit.length <= 256)
|
|
499
511
|
attributes[key] = explicit;
|
|
500
512
|
else
|
|
501
|
-
this.
|
|
513
|
+
this.instrumentationFailed("logs");
|
|
502
514
|
};
|
|
503
515
|
stamp("gen_ai.operation.name", messages.operation, enclosing?.model?.operation);
|
|
504
516
|
stamp("gen_ai.provider.name", messages.provider, enclosing?.model?.provider);
|
|
@@ -513,7 +525,7 @@ export class HueClient {
|
|
|
513
525
|
});
|
|
514
526
|
}
|
|
515
527
|
catch {
|
|
516
|
-
this.
|
|
528
|
+
this.instrumentationFailed("logs");
|
|
517
529
|
}
|
|
518
530
|
}
|
|
519
531
|
/**
|
|
@@ -545,7 +557,7 @@ export class HueClient {
|
|
|
545
557
|
const addresses = hostedServerAddresses(provider, options.request);
|
|
546
558
|
const activity = hostedToolActivity(provider, response);
|
|
547
559
|
if (activity.skipped > 0)
|
|
548
|
-
this.
|
|
560
|
+
this.instrumentationFailed("traces", activity.skipped);
|
|
549
561
|
const server = (label) => {
|
|
550
562
|
const attributes = {};
|
|
551
563
|
if (label === undefined)
|
|
@@ -564,7 +576,7 @@ export class HueClient {
|
|
|
564
576
|
if (isLabel(value))
|
|
565
577
|
attributes[key] = value;
|
|
566
578
|
else
|
|
567
|
-
this.
|
|
579
|
+
this.instrumentationFailed();
|
|
568
580
|
}
|
|
569
581
|
const address = addresses.get(label);
|
|
570
582
|
if (address !== undefined)
|
|
@@ -617,7 +629,7 @@ export class HueClient {
|
|
|
617
629
|
span.setAttributes(toolCatalogSummary(encodeContent(listing.definitions, catalogLimits)));
|
|
618
630
|
}
|
|
619
631
|
catch {
|
|
620
|
-
this.
|
|
632
|
+
this.instrumentationFailed();
|
|
621
633
|
}
|
|
622
634
|
if (listing.errorType !== undefined)
|
|
623
635
|
fail(span, listing.errorType);
|
|
@@ -625,7 +637,7 @@ export class HueClient {
|
|
|
625
637
|
}
|
|
626
638
|
}
|
|
627
639
|
catch {
|
|
628
|
-
this.
|
|
640
|
+
this.instrumentationFailed();
|
|
629
641
|
}
|
|
630
642
|
}
|
|
631
643
|
/**
|
|
@@ -692,12 +704,12 @@ export class HueClient {
|
|
|
692
704
|
if (isLabel(name))
|
|
693
705
|
attributes["hue.file.name"] = name;
|
|
694
706
|
else
|
|
695
|
-
this.
|
|
707
|
+
this.instrumentationFailed();
|
|
696
708
|
}
|
|
697
709
|
span.addEvent("hue.file", attributes);
|
|
698
710
|
}
|
|
699
711
|
catch {
|
|
700
|
-
this.
|
|
712
|
+
this.instrumentationFailed();
|
|
701
713
|
}
|
|
702
714
|
}
|
|
703
715
|
setContent(span, key, value) {
|
|
@@ -707,7 +719,7 @@ export class HueClient {
|
|
|
707
719
|
span.setAttribute(key, encodeContent(value));
|
|
708
720
|
}
|
|
709
721
|
catch {
|
|
710
|
-
this.
|
|
722
|
+
this.instrumentationFailed();
|
|
711
723
|
}
|
|
712
724
|
}
|
|
713
725
|
/**
|
package/dist/evals/client.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ export interface EvaluationClientOptions {
|
|
|
9
9
|
baseUrl?: string;
|
|
10
10
|
/** Per-request budget in milliseconds, 100–60000. Default 10000. */
|
|
11
11
|
timeoutMillis?: number;
|
|
12
|
+
/** Bounded attempts for a read or a mutation the server deduplicates by its key, after a
|
|
13
|
+
* connection failure, a timeout or a 408 or 5xx Hue did not time with `Retry-After`. Default 4,
|
|
14
|
+
* maximum 10; 1 sends every such request once. */
|
|
15
|
+
maxAttempts?: number;
|
|
12
16
|
}
|
|
13
17
|
/** Thrown for a failed evaluation API request; the message is fixed and never includes response text. */
|
|
14
18
|
export declare class HueApiError extends Error {
|
|
@@ -16,12 +20,22 @@ export declare class HueApiError extends Error {
|
|
|
16
20
|
readonly status?: number | undefined;
|
|
17
21
|
/** Seconds Hue asked the caller to wait (`Retry-After` on a 429 or 503), when it said. */
|
|
18
22
|
readonly retryAfterSeconds?: number | undefined;
|
|
23
|
+
/** Hue's `X-Hue-Diagnostic` code, which tells refusals of one status apart, when it sent one. */
|
|
24
|
+
readonly diagnostic?: string | undefined;
|
|
19
25
|
constructor(
|
|
20
26
|
/** HTTP status when Hue answered; absent for network, timeout and parsing failures. */
|
|
21
27
|
status?: number | undefined,
|
|
22
28
|
/** Seconds Hue asked the caller to wait (`Retry-After` on a 429 or 503), when it said. */
|
|
23
|
-
retryAfterSeconds?: number | undefined
|
|
29
|
+
retryAfterSeconds?: number | undefined,
|
|
30
|
+
/** Hue's `X-Hue-Diagnostic` code, which tells refusals of one status apart, when it sent one. */
|
|
31
|
+
diagnostic?: string | undefined);
|
|
24
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether the same request, sent again, can succeed: a connection failure, a timeout, or a status
|
|
35
|
+
* Hue answers while it cannot act yet (408, 429, 500, 502, 503, 504). A refusal Hue decided on,
|
|
36
|
+
* any other 4xx, is not, and no number of attempts changes it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isTransientApiError(error: unknown): boolean;
|
|
25
39
|
/** An artifact download that ran past the size its caller expected; the rest is not read. */
|
|
26
40
|
export declare class ArtifactSizeError extends Error {
|
|
27
41
|
/** The size the caller expected, in bytes. */
|
|
@@ -32,22 +46,28 @@ export declare class ArtifactSizeError extends Error {
|
|
|
32
46
|
}
|
|
33
47
|
/**
|
|
34
48
|
* Typed client for Hue's evaluation REST API: datasets, scorers, experiments, executions, runs,
|
|
35
|
-
* results and hosted judge jobs.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
49
|
+
* results and hosted judge jobs. A request Hue refused before acting on it with a short
|
|
50
|
+
* `Retry-After` (HTTP 429 or 503, at most 5 seconds) is sent again after that wait whatever its
|
|
51
|
+
* method, up to four times; a longer wait is the caller's error, carried as `retryAfterSeconds`.
|
|
52
|
+
* A read, or a mutation the server deduplicates by its idempotency key, is also sent again after
|
|
53
|
+
* a failure Hue did not time, a connection failure, a timeout or a 408 or 5xx without the header,
|
|
54
|
+
* up to `maxAttempts` times with a jittered backoff. A mutation without a key is never retried
|
|
55
|
+
* implicitly: callers retain stable idempotency keys for experiments and results. Responses are
|
|
56
|
+
* bounded to 4 MiB.
|
|
39
57
|
*/
|
|
40
58
|
export declare class EvaluationClient {
|
|
41
59
|
/** Validated Hue origin. */
|
|
42
60
|
readonly baseUrl: string;
|
|
43
61
|
private readonly apiKey;
|
|
44
62
|
private readonly timeoutMillis;
|
|
63
|
+
private readonly maxAttempts;
|
|
45
64
|
constructor(options: EvaluationClientOptions);
|
|
46
65
|
/** Sends one request to Hue, again after the wait while Hue refuses it with a short
|
|
47
66
|
* `Retry-After`. `init` runs for every attempt, so each has its own timeout; `signal` also ends
|
|
48
67
|
* each attempt, and a wait to send one again, whose refusal is then the caller's error. */
|
|
49
68
|
private send;
|
|
50
69
|
private request;
|
|
70
|
+
private requestOnce;
|
|
51
71
|
/** Verified bytes of one ready artifact in this project, bounded to the 25 MiB pilot file size.
|
|
52
72
|
* With `maxBytes`, the download stops one byte past it and throws `ArtifactSizeError`. */
|
|
53
73
|
downloadArtifact(id: string, options?: {
|