@hue-run/sdk 0.13.0 → 0.13.2
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 +62 -24
- package/ENVIRONMENTS.md +5 -2
- package/EVALUATIONS.md +27 -2
- package/README.md +5 -3
- package/dist/cli/eval.js +31 -5
- package/dist/environment/world.d.ts +6 -1
- package/dist/environment/world.js +44 -4
- package/dist/environment.d.ts +1 -1
- package/dist/environment.js +1 -1
- package/dist/evals/client.d.ts +37 -6
- package/dist/evals/client.js +44 -5
- package/dist/evals/scorer-publication.js +16 -4
- package/dist/evals/scorers.js +3 -1
- package/dist/evals/types.d.ts +71 -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.13.
|
|
45
|
+
not a completed integration. TypeScript uses `@hue-run/sdk@0.13.2`, `@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.13.
|
|
472
|
+
--archive .artifacts/typescript/hue-run-sdk-0.13.2.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.13.
|
|
483
|
+
--archive .artifacts/typescript/hue-run-sdk-0.13.2.tgz \
|
|
484
484
|
--origin https://STAGING_ORIGIN \
|
|
485
485
|
--project "$project" --command claim \
|
|
486
486
|
--evidence .context/setup-staging-after-claim.json
|
|
@@ -512,12 +512,38 @@ the sealed world. Hue never executes the agent. `HUE_API_KEY` must be a **Read a
|
|
|
512
512
|
write** project key (a **Read** or **Tracing only** key cannot read cases or create runs);
|
|
513
513
|
the CLI never prints it. The optional `zod` peer of `@hue-run/sdk/evals` must be installed.
|
|
514
514
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
515
|
+
Run the agent's own start command with `--command`. `hue eval` spawns it once per case with the
|
|
516
|
+
world's environment (below) and `{"inputs", "config"}` on stdin, and stores its stdout as the
|
|
517
|
+
answer. The agent reaches each app through Hue's mirror with its own production client: the one
|
|
518
|
+
committed helper described in [Make your agent eval-ready](https://docs.hue.run/evaluations/eval-ready-agent)
|
|
519
|
+
reads `HUE_SIM_<SURFACE ID>_URL` and `HUE_WORLD_TOKEN` when `HUE_WORLD_TOKEN` is set, fails
|
|
520
|
+
closed when a needed variable is missing, and returns the production URL and credential
|
|
521
|
+
otherwise. Never hand the agent Hue-native tools (`context.tools`, `context.mcp`) or a tool its
|
|
522
|
+
production agent lacks. Pin the SDK and bring its `zod` peer along, so the agent's repository
|
|
523
|
+
gains no dependency:
|
|
524
|
+
|
|
525
|
+
```sh
|
|
526
|
+
npx --yes --package @hue-run/sdk@0.13.2 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" --command "node agent.js" --env-file .env.hue
|
|
527
|
+
npx --yes --package @hue-run/sdk@0.13.2 --package "zod@^4.6.5" hue eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <experiment id>
|
|
528
|
+
npx --yes --package @hue-run/sdk@0.13.2 --package "zod@^4.6.5" hue eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
|
|
529
|
+
npx --yes --package @hue-run/sdk@0.13.2 --package "zod@^4.6.5" hue eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision> --env-file .env.hue
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
Without the second `--package`, `hue eval` exits with `hue eval needs zod`, even for `--help`;
|
|
533
|
+
installing zod in the project does not help when the SDK itself is not installed there. An
|
|
534
|
+
unpinned `--package @hue-run/sdk` runs a local, possibly older, copy in a project that already
|
|
535
|
+
depends on it.
|
|
536
|
+
|
|
537
|
+
An adapter module is the in-process alternative for a TypeScript agent. It exports `default` or
|
|
538
|
+
`runMyAgent`; `context` is the SDK's `SimulationTargetContext` (`world`, `mcp`, `tools`, `config`,
|
|
539
|
+
`item`, `executionId`, `environmentRunId`, `files`, `outputDirectory`, `signal`; `world` carries
|
|
540
|
+
the provider mirror URLs, the world token, `env` and `mcpConfig` where Hue's simulation gateway
|
|
541
|
+
serves the world). An adapter runs inside `hue eval`'s own process, where `HUE_WORLD_TOKEN` and
|
|
542
|
+
`HUE_EXECUTION_ID` are not set, so a helper that reads the process environment would return
|
|
543
|
+
production values: throw when `context.world` is missing, then pass `context.world.env` as the
|
|
544
|
+
helper's environment argument or spawn the agent with `agentEnvironment(context.world)`. Never
|
|
545
|
+
set `process.env` yourself; with `--concurrency` above 1 that leaks one case's world into another.
|
|
546
|
+
When the case also pins input files, `files` holds verified copies of its agent-visible ones (see
|
|
521
547
|
[Evaluate a document eval set](#evaluate-a-document-eval-set)); return
|
|
522
548
|
`withFiles(output, files)` from `@hue-run/sdk/evals` to upload documents the agent produced:
|
|
523
549
|
|
|
@@ -527,19 +553,16 @@ import type { JsonValue, SimulationTargetContext } from "@hue-run/sdk/evals";
|
|
|
527
553
|
import { runAgent } from "./src/agent.js"; // The application's existing entry point.
|
|
528
554
|
|
|
529
555
|
export default function runMyAgent(inputs: JsonValue, context: SimulationTargetContext) {
|
|
530
|
-
|
|
531
|
-
//
|
|
532
|
-
|
|
533
|
-
|
|
556
|
+
if (!context.world) throw new Error("This case has no world handoff; refusing to run against the real app");
|
|
557
|
+
// The agent's own app clients read the mirrors through the committed helper, given the
|
|
558
|
+
// world's environment; never hand it context.tools or context.mcp. Pass context.signal for
|
|
559
|
+
// cooperative cancellation.
|
|
560
|
+
return runAgent({ inputs, config: context.config, env: context.world.env, signal: context.signal });
|
|
534
561
|
}
|
|
535
562
|
```
|
|
536
563
|
|
|
537
564
|
```sh
|
|
538
|
-
hue eval --case "Refund an eligible charge" ./hue-agent.ts --content --env-file .env.hue
|
|
539
|
-
hue eval --case https://app.hue.run/projects/demo/scenarios/<id> ./hue-agent.ts --content --baseline <run id>
|
|
540
|
-
hue eval --set "Billing regressions" --scorer-version <id> ./hue-agent.ts --content --save-version
|
|
541
|
-
hue eval --case "Refund an eligible charge" --command "python agent.py" --timeout 120 --content
|
|
542
|
-
hue eval --worker ./hue-agent.ts --agent-key support-agent --content --env-file .env.hue
|
|
565
|
+
npx --yes --package @hue-run/sdk@0.13.2 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" ./hue-agent.ts --content --env-file .env.hue
|
|
543
566
|
```
|
|
544
567
|
|
|
545
568
|
`--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
|
|
@@ -558,9 +581,11 @@ Failing cases print the scorer explanation. An evaluator Hue records as not appl
|
|
|
558
581
|
that case's columns and neither passes nor fails it, so in a mixed eval set a case is decided by
|
|
559
582
|
the evaluators that apply to it; the pass line counts the not-applicable results. A case that no
|
|
560
583
|
pinned evaluator applies to is an error that names what they need. A result of a pinned Hue judge
|
|
561
|
-
(an evaluator of kind `world_judge`) that Hue marks advisory
|
|
584
|
+
(an evaluator of kind `world_judge`) that Hue marks advisory is
|
|
562
585
|
shown in its column, marked `(advisory)`, but never decides a case or the exit code; the pass line
|
|
563
|
-
counts advisory failures as not counted, and a case only advisory results ran for is an error.
|
|
586
|
+
counts advisory failures as not counted, and a case only advisory results ran for is an error.
|
|
587
|
+
The Answer outcome evaluator uses its required judge to decide answer-only cases; if that judge
|
|
588
|
+
cannot run, the case is inconclusive. Any
|
|
564
589
|
other evaluator's result, an error, or a metric that carries `passed` is never advisory, whatever
|
|
565
590
|
its evidence says. A metric name that
|
|
566
591
|
several evaluators report, such as two judges' `verdict`, gets a column per evaluator, labelled
|
|
@@ -576,8 +601,8 @@ Hue-owned `world_outcome` checks are graded after the world seals. An experiment
|
|
|
576
601
|
every case of the saved version; there is no case subset.
|
|
577
602
|
|
|
578
603
|
`--command "<shell command>"` spawns the command once per case with the world's environment
|
|
579
|
-
(`HUE_WORLD_ID`, `HUE_WORLD_TOKEN`, `HUE_WORLD_NOW` (the world's clock at creation, RFC 3339
|
|
580
|
-
|
|
604
|
+
(`HUE_WORLD_ID`, `HUE_WORLD_TOKEN`, `HUE_WORLD_NOW` (the world's clock at creation, RFC 3339; `worldNow()` reads it and returns
|
|
605
|
+
`null` without it, so fall back explicitly with `worldNow() ?? new Date()`), one `HUE_SIM_<SURFACE ID>_URL` per provider
|
|
581
606
|
mirror,
|
|
582
607
|
`HUE_MCP_CONFIG` naming an owner-only `mcpServers` file that is removed after the case, and
|
|
583
608
|
`HUE_MCP_URL`, `HUE_MCP_TOKEN`, `HUE_MCP_EXPIRES_AT` for the first MCP mirror; these name the
|
|
@@ -606,8 +631,21 @@ script name; `--revision` is sent to Hue as the agent revision of every world.
|
|
|
606
631
|
|
|
607
632
|
`--worker` registers the adapter through `runLocalAgent()` with key `--agent-key` (default: the
|
|
608
633
|
adapter filename slug), name `--agent-name` (default: the key), revision `--revision` (default:
|
|
609
|
-
`AGENT_REVISION`, then the Git `HEAD` short hash, then `dev`)
|
|
610
|
-
plus any repeated `--capability <value
|
|
634
|
+
`AGENT_REVISION`, then the Git `HEAD` short hash, then `dev`). Command workers register both
|
|
635
|
+
`environment:v1` and `direct:v1`, plus any repeated `--capability <value>`: the same worker runs
|
|
636
|
+
world cases, answer-only cases and mixed eval sets launched from Hue. Commands keep the launch
|
|
637
|
+
directory for both case kinds, so relative application entry points and configuration work.
|
|
638
|
+
Direct cases receive the task/config on stdin and private files/output through `HUE_CASE_*`,
|
|
639
|
+
without a world token, MCP connection or `HUE_ENVIRONMENT_RUN_ID`. App calls that require a world
|
|
640
|
+
must stay disabled for this handoff; never fall back to live app credentials.
|
|
641
|
+
|
|
642
|
+
Adapter-file workers register `environment:v1` by default. Add `--capability direct:v1` only when
|
|
643
|
+
the adapter also handles `context.mode === "direct"`; its direct context carries `config`, case
|
|
644
|
+
identity, `executionId`, `files`, `outputDirectory` and `signal`, with no world or tools. These
|
|
645
|
+
worker behaviors are added in TypeScript 0.13.2. Upgrading an existing command worker requires a
|
|
646
|
+
new `--revision`, because adding a capability to a previously registered revision is refused.
|
|
647
|
+
|
|
648
|
+
The worker prints the registration and each claimed run with its
|
|
611
649
|
URL, executes runs launched from Hue until Ctrl+C or `--max-runs <n>`, and prints the verdict table
|
|
612
650
|
after each run. Selection flags do not apply; Hue chooses the pinned experiment. The worker exits 0
|
|
613
651
|
when it stops normally. Hue offers a case whose world comes with agent-visible files only to a
|
package/ENVIRONMENTS.md
CHANGED
|
@@ -328,8 +328,11 @@ type; see [EVALUATIONS.md](EVALUATIONS.md#direct-cases-and-files).
|
|
|
328
328
|
A refusal such as 409 `simulation_gateway_required` exposes its validated server code as `HueEnvironmentError.diagnostic`.
|
|
329
329
|
|
|
330
330
|
`agentEnvironment` removes Hue control-plane credentials from the child by default: `HUE_API_KEY`,
|
|
331
|
-
`HUE_MCP_KEY
|
|
332
|
-
|
|
331
|
+
`HUE_MCP_KEY`, and any value carrying a Hue control-plane credential (`hue_sk_`, `hue_setup_`,
|
|
332
|
+
`hue_attempt_`, `hue_mcp_`, `hue_inv_`, `hue_install_`, `hue_at_`, `hue_rt_`, `hue_oauth_`)
|
|
333
|
+
anywhere in it, including wrapped, percent-encoded or JSON-Unicode-escaped values. World tokens and
|
|
334
|
+
`hue_sim_` capabilities pass. Pass `includeHueCredentials: true` only for an agent that must call
|
|
335
|
+
Hue's own API.
|
|
333
336
|
Nothing in these helpers logs the token; keep it out of your own logs and checkpoints.
|
|
334
337
|
|
|
335
338
|
Finish answers `lifecycle: "completing"` with `sealedAt: null` for a gateway world: the seal
|
package/EVALUATIONS.md
CHANGED
|
@@ -107,6 +107,24 @@ evidence and scoring while retaining this runner's checkpoint guarantees. See
|
|
|
107
107
|
Cases whose inputs are a task plus pinned documents, and rescoring over documents a run already
|
|
108
108
|
saved, use the same runner without a world. See [Direct cases and files](#direct-cases-and-files).
|
|
109
109
|
|
|
110
|
+
## Tags
|
|
111
|
+
|
|
112
|
+
Tags group datasets, scorers and experiments, for example by the area of the agent they test.
|
|
113
|
+
Methods take tag names: a name the project has no tag for creates one, and names match without
|
|
114
|
+
case. `createDataset`, `createScorer` and `createExperiment` accept `tags`; `updateDataset`,
|
|
115
|
+
`updateScorer` and `updateExperiment` replace an item's tags; `listDatasets` and `listScorers`
|
|
116
|
+
filter by `tags`, matching any of them; and `listTags()` returns the project's tags. An experiment
|
|
117
|
+
also shows its dataset's tags, which experiment filters match as well. The product-named methods
|
|
118
|
+
(`createEvalSet`, `updateEvalSet`, `listEvalSets`, `createEvaluator`, `updateEvaluator`,
|
|
119
|
+
`listEvaluators`, `createRun`, `updateRun`) take the same `tags`. Tags require a Hue server that
|
|
120
|
+
supports them.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
await client.createEvalSet({ name: "Refunds", slug: "refunds", tags: ["billing"] });
|
|
124
|
+
await client.updateEvaluator(evaluatorId, { tags: ["billing", "regression"] });
|
|
125
|
+
const billing = await client.listEvalSets({ tags: ["billing"] });
|
|
126
|
+
```
|
|
127
|
+
|
|
110
128
|
## Content and result states
|
|
111
129
|
|
|
112
130
|
Both choices are required and independent:
|
|
@@ -146,7 +164,7 @@ A callback receives `{inputs,hasOutput,output?,hasExpected,expected?,metadata,ex
|
|
|
146
164
|
- `{state:"error",error:{type,message?}}`.
|
|
147
165
|
- `{state:"skipped",explanation:"reason"}`.
|
|
148
166
|
|
|
149
|
-
All declared metrics must appear exactly once and satisfy pinned types, bounds and categories. The binding must match language, entrypoint, SHA-256 source digest and metric definitions. The digest is an authenticated caller declaration; it does not attest closures, dependency versions or actual execution. Callback source is never downloaded or evaluated. Callbacks are trusted local code; they have **no execution timeout or side-effect cancellation**. Concurrency limits active cases to 1–16 (default 1), with scorers evaluated sequentially within each case.
|
|
167
|
+
All declared metrics must appear exactly once and satisfy pinned types, bounds and categories. Each metric may include an optional `description` (1–500 characters) shown with its check in Hue's run view. The binding must match language, entrypoint, SHA-256 source digest and metric definitions. The digest is an authenticated caller declaration; it does not attest closures, dependency versions or actual execution. Callback source is never downloaded or evaluated. Callbacks are trusted local code; they have **no execution timeout or side-effect cancellation**. Concurrency limits active cases to 1–16 (default 1), with scorers evaluated sequentially within each case.
|
|
150
168
|
|
|
151
169
|
`files` is present only when the runner handled files for that execution: every pinned input file
|
|
152
170
|
(all roles, including the evaluator-only `org_template`) and every generated output, each with its
|
|
@@ -279,12 +297,19 @@ acceptance remains a post-publication Fern gate.
|
|
|
279
297
|
|
|
280
298
|
## Direct cases and files
|
|
281
299
|
|
|
282
|
-
Cases without a simulated world —
|
|
300
|
+
Cases without a simulated world — answer-only tasks, or document workflows with pinned files —
|
|
283
301
|
run on the same runner under the same checkpoint rules. They shipped in `@hue-run/sdk` `0.5.0`
|
|
284
302
|
and require a Hue deployment that serves case `inputFiles` on experiment
|
|
285
303
|
items, subject `files`, and the artifact reservation, upload, completion and download APIs. The
|
|
286
304
|
Python SDK has no equivalent.
|
|
287
305
|
|
|
306
|
+
The TypeScript 0.13.2 `hue eval --worker --command "…"` wrapper supplies both callbacks, so one
|
|
307
|
+
command worker accepts direct and world cases, including mixed eval sets. It keeps the launch
|
|
308
|
+
directory for relative application entry points; private case inputs/output still use
|
|
309
|
+
`HUE_CASE_*`. An adapter-file worker opts into the direct callback with `--capability direct:v1`
|
|
310
|
+
and must handle `context.mode === "direct"`. Restart an existing environment-only command
|
|
311
|
+
registration with a new `--revision` when upgrading.
|
|
312
|
+
|
|
288
313
|
On the outbound worker, supply `directTarget` beside or instead of `target`. Supplying
|
|
289
314
|
`directTarget` registers the `direct:v1` capability and supplying `target` registers
|
|
290
315
|
`environment:v1` (both strings are exported as `localAgentCapabilities`). Declare the file
|
package/README.md
CHANGED
|
@@ -647,10 +647,12 @@ their agents with `runLocalAgent()`; setup does not register workers or launch s
|
|
|
647
647
|
The `hue eval` command, shipped in TypeScript `0.5.0` (`--case` since `0.6.0`, `--scenario`
|
|
648
648
|
before), wraps `runSimulation()` and `runLocalAgent()` for an adapter file or a shell command:
|
|
649
649
|
`hue eval --case "<name>" ./hue-agent.ts --content` creates a fresh run from a published case's
|
|
650
|
-
immutable pins, runs the agent
|
|
650
|
+
immutable pins, runs the agent with an isolated world when the case pins one, waits
|
|
651
651
|
for Hue's outcome checks and prints the run URL and per-case PASS/FAIL verdicts with an exit code;
|
|
652
|
-
`--worker` registers the same adapter for runs launched from Hue.
|
|
653
|
-
|
|
652
|
+
`--worker` registers the same adapter for runs launched from Hue. In TypeScript 0.13.2, a
|
|
653
|
+
command worker handles both world and answer-only cases; adapter workers opt into direct cases
|
|
654
|
+
with `--capability direct:v1`. Upgrade an existing command registration under a new `--revision`.
|
|
655
|
+
It needs a Read and write key in `HUE_API_KEY` (never printed). Content capture stays off unless `--content` is
|
|
654
656
|
passed; the example passes it so case spans carry content. Case outputs, error messages and
|
|
655
657
|
explanations are stored in Hue either way (`--no-output` keeps them out of a one-shot run's
|
|
656
658
|
results); a command's stdout is its stored answer, with the credentials `hue eval` handed the case
|
package/dist/cli/eval.js
CHANGED
|
@@ -60,6 +60,8 @@ Agent (exactly one):
|
|
|
60
60
|
|
|
61
61
|
Modes:
|
|
62
62
|
--worker Register the agent and poll for runs launched from Hue
|
|
63
|
+
Commands support cases with or without a world; adapter files
|
|
64
|
+
opt into direct cases with --capability direct:v1
|
|
63
65
|
--max-runs <n> Stop the worker after n completed runs
|
|
64
66
|
--agent-key <key> Agent key (default: slug of the adapter filename)
|
|
65
67
|
--agent-name <name> Agent display name (default: the key)
|
|
@@ -600,9 +602,11 @@ function redacting(adapter) {
|
|
|
600
602
|
function parentEnvironment(allowHueCredentials) {
|
|
601
603
|
// A world variable left in this process's environment belongs to another world: only the
|
|
602
604
|
// current case's world sets them, whether or not the case has one.
|
|
603
|
-
|
|
605
|
+
const parent = withoutWorldVariables(allowHueCredentials
|
|
604
606
|
? Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined))
|
|
605
607
|
: stripHueControlPlaneCredentials(process.env));
|
|
608
|
+
delete parent.HUE_ENVIRONMENT_RUN_ID;
|
|
609
|
+
return parent;
|
|
606
610
|
}
|
|
607
611
|
function commandAdapter(command, timeoutSeconds, options) {
|
|
608
612
|
return async (inputs, context) => {
|
|
@@ -683,7 +687,7 @@ function directCommandAdapter(command, timeoutSeconds, options) {
|
|
|
683
687
|
files: context.files,
|
|
684
688
|
});
|
|
685
689
|
const stdout = await spawnAgentCommand(command, {
|
|
686
|
-
cwd: layout.caseDirectory,
|
|
690
|
+
cwd: options.workingDirectory ?? layout.caseDirectory,
|
|
687
691
|
env: {
|
|
688
692
|
...parentEnvironment(options.allowHueCredentials),
|
|
689
693
|
HUE_CASE_DIR: layout.caseDirectory,
|
|
@@ -1286,7 +1290,7 @@ async function runDirect(run, connection) {
|
|
|
1286
1290
|
telemetryNotAccepted: report.telemetryNotAccepted,
|
|
1287
1291
|
}, run.baselineId, output, signal);
|
|
1288
1292
|
}
|
|
1289
|
-
async function runWorker(values, connection,
|
|
1293
|
+
async function runWorker(values, connection, agents, agent, hue, output, signal) {
|
|
1290
1294
|
const client = new ObservedClient(connection);
|
|
1291
1295
|
const environmentClient = createEnvironmentClient(connection);
|
|
1292
1296
|
const wait = integer("wait", values.wait, 300, 0, 86_400);
|
|
@@ -1305,6 +1309,9 @@ async function runWorker(values, connection, adapter, agent, hue, output, signal
|
|
|
1305
1309
|
output.log(`Claimed run ${claim.runId}: ${new URL(`/experiments/${claim.experimentId}`, connection.baseUrl).toString()}`);
|
|
1306
1310
|
};
|
|
1307
1311
|
const telemetry = telemetryReporter(output);
|
|
1312
|
+
// A command can use the same stdin contract for both case kinds. Existing adapter files
|
|
1313
|
+
// may require a world, so their direct context remains an explicit capability opt-in.
|
|
1314
|
+
const direct = values.command !== undefined || values.capability?.includes("direct:v1");
|
|
1308
1315
|
await runLocalAgent({
|
|
1309
1316
|
client,
|
|
1310
1317
|
environmentClient,
|
|
@@ -1339,7 +1346,7 @@ async function runWorker(values, connection, adapter, agent, hue, output, signal
|
|
|
1339
1346
|
},
|
|
1340
1347
|
target(inputs, tools, context) {
|
|
1341
1348
|
output.log(`[${context.item.externalKey}] agent started`);
|
|
1342
|
-
return
|
|
1349
|
+
return agents.simulation(inputs, {
|
|
1343
1350
|
config: context.config,
|
|
1344
1351
|
item: context.item,
|
|
1345
1352
|
executionId: context.executionId,
|
|
@@ -1355,6 +1362,22 @@ async function runWorker(values, connection, adapter, agent, hue, output, signal
|
|
|
1355
1362
|
signal: context.signal ?? signal,
|
|
1356
1363
|
});
|
|
1357
1364
|
},
|
|
1365
|
+
...(direct
|
|
1366
|
+
? {
|
|
1367
|
+
directTarget(inputs, context) {
|
|
1368
|
+
output.log(`[${context.item.externalKey}] agent started`);
|
|
1369
|
+
return agents.direct(inputs, {
|
|
1370
|
+
mode: "direct",
|
|
1371
|
+
config: context.config,
|
|
1372
|
+
item: context.item,
|
|
1373
|
+
executionId: context.executionId,
|
|
1374
|
+
files: context.files,
|
|
1375
|
+
outputDirectory: context.outputDirectory,
|
|
1376
|
+
signal: context.signal ?? signal,
|
|
1377
|
+
});
|
|
1378
|
+
},
|
|
1379
|
+
}
|
|
1380
|
+
: {}),
|
|
1358
1381
|
async onCompleted(report) {
|
|
1359
1382
|
output.log(`Run ${report.runId} completed: ${report.subjectIds.length} case${report.subjectIds.length === 1 ? "" : "s"}`);
|
|
1360
1383
|
telemetry.rest(report.telemetryNotAccepted);
|
|
@@ -1490,6 +1513,9 @@ export async function runEvalCommand(argv) {
|
|
|
1490
1513
|
direct: redacting(loaded ??
|
|
1491
1514
|
directCommandAdapter(values.command, timeout, {
|
|
1492
1515
|
allowHueCredentials: values["allow-hue-credentials"],
|
|
1516
|
+
// A worker's single command must resolve relative entry points the same way for
|
|
1517
|
+
// direct and world cases. One-shot direct runs keep their private-directory cwd.
|
|
1518
|
+
...(values.worker ? { workingDirectory: process.cwd() } : {}),
|
|
1493
1519
|
})),
|
|
1494
1520
|
};
|
|
1495
1521
|
hue = createHue({
|
|
@@ -1500,7 +1526,7 @@ export async function runEvalCommand(argv) {
|
|
|
1500
1526
|
timeoutMillis: CASE_TRACE_EXPORT_MILLIS,
|
|
1501
1527
|
});
|
|
1502
1528
|
return values.worker
|
|
1503
|
-
? await runWorker(values, connection, agents
|
|
1529
|
+
? await runWorker(values, connection, agents, agent, hue, output, controller.signal)
|
|
1504
1530
|
: await runOnce(values, connection, agents, agent, hue, output, controller.signal);
|
|
1505
1531
|
}
|
|
1506
1532
|
catch (error) {
|
|
@@ -15,7 +15,12 @@ export declare function worldHandoff(run: EnvironmentRun): WorldHandoff | null;
|
|
|
15
15
|
export declare function worldNow(source?: WorldHandoff | Record<string, string | undefined>): Date | null;
|
|
16
16
|
/** Variables that authenticate against Hue's control plane rather than a simulated provider. */
|
|
17
17
|
export declare const HUE_CONTROL_PLANE_VARIABLES: readonly string[];
|
|
18
|
-
/**
|
|
18
|
+
/** Prefixes of the Hue control-plane credentials the server issues: project and setup keys,
|
|
19
|
+
* attempt grants, the project MCP key, invocation and installation tokens, and OAuth tokens and
|
|
20
|
+
* secrets. World tokens (`hue_world_`) and MCP capabilities (`hue_sim_`) are what the agent holds. */
|
|
21
|
+
export declare const HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES: readonly string[];
|
|
22
|
+
/** True for a control-plane variable by name, or for a value carrying a Hue control-plane
|
|
23
|
+
* credential anywhere in it, including wrapped, percent-encoded or JSON-Unicode-escaped values. */
|
|
19
24
|
export declare function isHueControlPlaneCredential(name: string, value: string | undefined): boolean;
|
|
20
25
|
/** The parent's variables without Hue control-plane credentials; `undefined` values are dropped. */
|
|
21
26
|
export declare function stripHueControlPlaneCredentials(parent: Record<string, string | undefined>): Record<string, string>;
|
|
@@ -52,13 +52,53 @@ export const HUE_CONTROL_PLANE_VARIABLES = [
|
|
|
52
52
|
"HUE_PROJECT_KEY",
|
|
53
53
|
"HUE_SERVICE_KEY",
|
|
54
54
|
];
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
/** Prefixes of the Hue control-plane credentials the server issues: project and setup keys,
|
|
56
|
+
* attempt grants, the project MCP key, invocation and installation tokens, and OAuth tokens and
|
|
57
|
+
* secrets. World tokens (`hue_world_`) and MCP capabilities (`hue_sim_`) are what the agent holds. */
|
|
58
|
+
export const HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES = [
|
|
59
|
+
"hue_sk_",
|
|
60
|
+
"hue_attempt_",
|
|
61
|
+
"hue_mcp_",
|
|
62
|
+
"hue_setup_",
|
|
63
|
+
"hue_inv_",
|
|
64
|
+
"hue_install_",
|
|
65
|
+
"hue_at_",
|
|
66
|
+
"hue_rt_",
|
|
67
|
+
"hue_oauth_",
|
|
68
|
+
];
|
|
69
|
+
/** A Hue credential anywhere in a value, including JSON Unicode escapes, not inside a longer word. */
|
|
70
|
+
const HUE_CREDENTIAL_SHAPE = new RegExp(`(?:^|[^A-Za-z0-9_])(?:${HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES.join("|")})`);
|
|
71
|
+
const ASCII_PERCENT_ESCAPE = /%[0-7][0-9a-f]/i;
|
|
72
|
+
const ASCII_JSON_UNICODE_ESCAPE = /\\u00([0-7][0-9a-fA-F])/g;
|
|
73
|
+
function decodeAsciiJsonUnicodeEscapes(value) {
|
|
74
|
+
return value.replace(ASCII_JSON_UNICODE_ESCAPE, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
|
|
75
|
+
}
|
|
76
|
+
function hasHueControlPlaneCredential(value) {
|
|
77
|
+
let candidates = new Set([value]);
|
|
78
|
+
for (let layer = 0; layer <= 4; layer++) {
|
|
79
|
+
const jsonDecodedCandidates = new Set();
|
|
80
|
+
for (const candidate of candidates) {
|
|
81
|
+
jsonDecodedCandidates.add(candidate);
|
|
82
|
+
jsonDecodedCandidates.add(decodeAsciiJsonUnicodeEscapes(candidate));
|
|
83
|
+
}
|
|
84
|
+
if ([...jsonDecodedCandidates].some((candidate) => HUE_CREDENTIAL_SHAPE.test(candidate)))
|
|
85
|
+
return true;
|
|
86
|
+
if (layer === 4)
|
|
87
|
+
return [...jsonDecodedCandidates].some((candidate) => ASCII_PERCENT_ESCAPE.test(candidate));
|
|
88
|
+
candidates = new Set([...jsonDecodedCandidates]
|
|
89
|
+
.filter((candidate) => ASCII_PERCENT_ESCAPE.test(candidate))
|
|
90
|
+
.map((candidate) => candidate.replace(/%([0-7][0-9a-f])/gi, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)))));
|
|
91
|
+
if (candidates.size === 0)
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/** True for a control-plane variable by name, or for a value carrying a Hue control-plane
|
|
97
|
+
* credential anywhere in it, including wrapped, percent-encoded or JSON-Unicode-escaped values. */
|
|
58
98
|
export function isHueControlPlaneCredential(name, value) {
|
|
59
99
|
if (HUE_CONTROL_PLANE_VARIABLES.includes(name))
|
|
60
100
|
return true;
|
|
61
|
-
return typeof value === "string" &&
|
|
101
|
+
return typeof value === "string" && hasHueControlPlaneCredential(value);
|
|
62
102
|
}
|
|
63
103
|
/** The parent's variables without Hue control-plane credentials; `undefined` values are dropped. */
|
|
64
104
|
export function stripHueControlPlaneCredentials(parent) {
|
package/dist/environment.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export type { EnvironmentClientOptions } from "./environment/client.js";
|
|
|
3
3
|
export { bindEnvironmentTools } from "./environment/tools.js";
|
|
4
4
|
export type { BindEnvironmentToolsOptions, EnvironmentTool } from "./environment/tools.js";
|
|
5
5
|
export type * from "./environment/types.js";
|
|
6
|
-
export { agentEnvironment, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./environment/world.js";
|
|
6
|
+
export { agentEnvironment, HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./environment/world.js";
|
|
7
7
|
export type { AgentEnvironmentOptions, McpConfigFile } from "./environment/world.js";
|
package/dist/environment.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { createEnvironmentClient, EnvironmentClient, HueEnvironmentError, isTransientEnvironmentError, } from "./environment/client.js";
|
|
2
2
|
export { bindEnvironmentTools } from "./environment/tools.js";
|
|
3
|
-
export { agentEnvironment, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./environment/world.js";
|
|
3
|
+
export { agentEnvironment, HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./environment/world.js";
|
package/dist/evals/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ProjectConnection } from "../types.js";
|
|
2
2
|
import { type AttemptConnectionBundleV2, type PrepareAttemptRequestV2 } from "./attempt.js";
|
|
3
|
-
import type { ArtifactReservation, ArtifactUpload, CaseConversion, CaseConversionSummary, CaseWrite, CompleteExecution, Completion, Dataset, DatasetCase, DatasetVersion, EvalSet, EvalSetCase, EvalSetVersion, EvaluationItem, EvaluationRun, EvaluationRunSummary, EnvironmentEvidenceSnapshot, Execution, Experiment, ExperimentCase, ExperimentItem, Evaluator, EvaluatorVersion,
|
|
3
|
+
import type { ArtifactReservation, ArtifactUpload, CaseConversion, CaseConversionSummary, CaseWrite, CompleteExecution, Completion, Dataset, DatasetCase, DatasetVersion, EvalSet, EvalSetCase, EvalSetVersion, EvaluationItem, EvaluationRun, EvaluationRunSummary, EnvironmentEvidenceSnapshot, Execution, Experiment, ExperimentCase, ExperimentItem, Evaluator, EvaluatorVersion, JsonValue, LocalAgentClaim, LocalAgentRegistration, RegisteredLocalAgent, JudgeBudget, JudgeJob, Page, PageOptions, RegistryPageOptions, Result, ResultSummary, Run, RunCase, Scorer, ScorerDefinition, ScorerVersion, Scoring, ScoringResultInput, ScoringResultSummary, ScoringSubject, ScoringSummary, SimulationMcpCapability, StartExecution, Subject, StoredResult, StoredScoringResult, IdentityUpdate, Tag, TaggedIdentity, UpdatedRun } from "./types.js";
|
|
4
4
|
/** Connection options for {@link createEvaluationClient}. */
|
|
5
5
|
export interface EvaluationClientOptions {
|
|
6
6
|
/** Project service key sent as a Bearer token; server side only. */
|
|
@@ -79,8 +79,12 @@ export declare class EvaluationClient {
|
|
|
79
79
|
private registryPage;
|
|
80
80
|
/** Reads the current project to confirm the key and origin. */
|
|
81
81
|
checkConnection(): Promise<ProjectConnection>;
|
|
82
|
-
/** Creates a dataset with an initial draft version. */
|
|
83
|
-
createDataset(input:
|
|
82
|
+
/** Creates a dataset with an initial draft version; `tags` names the tags it starts with. */
|
|
83
|
+
createDataset(input: TaggedIdentity): Promise<Dataset>;
|
|
84
|
+
/** Renames a dataset or replaces its tags by name; the same values again change nothing. */
|
|
85
|
+
updateDataset(id: string, input: IdentityUpdate & {
|
|
86
|
+
slug?: string;
|
|
87
|
+
}): Promise<Omit<Dataset, "versions">>;
|
|
84
88
|
/** Reads a dataset and its versions. */
|
|
85
89
|
getDataset(id: string): Promise<Dataset>;
|
|
86
90
|
/** Lists datasets without their versions. */
|
|
@@ -103,7 +107,14 @@ export declare class EvaluationClient {
|
|
|
103
107
|
/** Freezes a draft version at the given revision; frozen versions are immutable. */
|
|
104
108
|
freezeDatasetVersion(id: string, expectedRevision: number): Promise<DatasetVersion>;
|
|
105
109
|
/** Creates a scorer identity; publish definitions with {@link publishScorerVersion}. */
|
|
106
|
-
createScorer(input:
|
|
110
|
+
createScorer(input: TaggedIdentity): Promise<Scorer>;
|
|
111
|
+
/** Renames a scorer or replaces its tags by name; the same values again change nothing. */
|
|
112
|
+
updateScorer(id: string, input: IdentityUpdate): Promise<Scorer>;
|
|
113
|
+
/** Lists the project's tags in its order: as people arranged them in Hue, then newer tags. */
|
|
114
|
+
listTags(): Promise<{
|
|
115
|
+
/** The project's tags. */
|
|
116
|
+
items: Tag[];
|
|
117
|
+
}>;
|
|
107
118
|
/** Reads a scorer and its published versions. */
|
|
108
119
|
getScorer(id: string): Promise<Scorer>;
|
|
109
120
|
/** Lists scorers. */
|
|
@@ -113,7 +124,11 @@ export declare class EvaluationClient {
|
|
|
113
124
|
/** Reads a published scorer version. */
|
|
114
125
|
getScorerVersion(id: string): Promise<ScorerVersion>;
|
|
115
126
|
/** Creates an eval set using the existing v1 registry path. */
|
|
116
|
-
createEvalSet(input:
|
|
127
|
+
createEvalSet(input: TaggedIdentity): Promise<EvalSet>;
|
|
128
|
+
/** Renames an eval set or replaces its tags by name. */
|
|
129
|
+
updateEvalSet(id: string, input: IdentityUpdate & {
|
|
130
|
+
slug?: string;
|
|
131
|
+
}): Promise<Omit<EvalSet, "versions">>;
|
|
117
132
|
/** Reads an eval set and its versions. */
|
|
118
133
|
getEvalSet(id: string): Promise<EvalSet>;
|
|
119
134
|
/** Lists eval sets. */
|
|
@@ -136,7 +151,9 @@ export declare class EvaluationClient {
|
|
|
136
151
|
/** Freezes a draft eval set version at its expected revision. */
|
|
137
152
|
freezeEvalSetVersion(id: string, expectedRevision: number): Promise<EvalSetVersion>;
|
|
138
153
|
/** Creates an evaluator identity. */
|
|
139
|
-
createEvaluator(input:
|
|
154
|
+
createEvaluator(input: TaggedIdentity): Promise<Evaluator>;
|
|
155
|
+
/** Renames an evaluator or replaces its tags by name. */
|
|
156
|
+
updateEvaluator(id: string, input: IdentityUpdate): Promise<Evaluator>;
|
|
140
157
|
/** Reads an evaluator and its published versions. */
|
|
141
158
|
getEvaluator(id: string): Promise<Evaluator>;
|
|
142
159
|
/** Lists evaluators. */
|
|
@@ -152,6 +169,8 @@ export declare class EvaluationClient {
|
|
|
152
169
|
datasetVersionId: string;
|
|
153
170
|
scorerVersionIds: string[];
|
|
154
171
|
config: JsonValue;
|
|
172
|
+
/** The experiment's own tag names; it also shows its dataset's tags. */
|
|
173
|
+
tags?: string[];
|
|
155
174
|
}): Promise<{
|
|
156
175
|
/** Experiment ID. */
|
|
157
176
|
id: string;
|
|
@@ -160,6 +179,11 @@ export declare class EvaluationClient {
|
|
|
160
179
|
}>;
|
|
161
180
|
/** Reads an experiment with its evaluation run and execution counts. */
|
|
162
181
|
getExperiment(id: string): Promise<Experiment>;
|
|
182
|
+
/** Renames an experiment or replaces its own tags by name. */
|
|
183
|
+
updateExperiment(id: string, input: {
|
|
184
|
+
name?: string;
|
|
185
|
+
tags?: string[];
|
|
186
|
+
}): Promise<UpdatedRun>;
|
|
163
187
|
/** Lists an experiment's cases with their latest executions. */
|
|
164
188
|
listExperimentItems(id: string, page?: PageOptions): Promise<Page<ExperimentItem>>;
|
|
165
189
|
/** Reads one frozen case of an experiment. */
|
|
@@ -261,6 +285,8 @@ export declare class EvaluationClient {
|
|
|
261
285
|
evalSetVersionId: string;
|
|
262
286
|
evaluatorVersionIds: string[];
|
|
263
287
|
config: JsonValue;
|
|
288
|
+
/** The run's own tag names; it also shows its eval set's tags. */
|
|
289
|
+
tags?: string[];
|
|
264
290
|
}): Promise<{
|
|
265
291
|
/** Run ID. */
|
|
266
292
|
id: string;
|
|
@@ -271,6 +297,11 @@ export declare class EvaluationClient {
|
|
|
271
297
|
}>;
|
|
272
298
|
/** Reads a run with its scoring progress. */
|
|
273
299
|
getRun(id: string): Promise<Run>;
|
|
300
|
+
/** Renames a run or replaces its own tags by name; its eval set's tags are not its own. */
|
|
301
|
+
updateRun(id: string, input: {
|
|
302
|
+
name?: string;
|
|
303
|
+
tags?: string[];
|
|
304
|
+
}): Promise<UpdatedRun>;
|
|
274
305
|
/** Lists a run's cases with their latest executions. */
|
|
275
306
|
listRunItems(id: string, page?: PageOptions): Promise<Page<ExperimentItem>>;
|
|
276
307
|
/** Reads one frozen case of a run. */
|
package/dist/evals/client.js
CHANGED
|
@@ -89,6 +89,15 @@ function productRegistryFields(value) {
|
|
|
89
89
|
}
|
|
90
90
|
return result;
|
|
91
91
|
}
|
|
92
|
+
/** Tag names travel as given; anything but a list of strings is a caller error. */
|
|
93
|
+
function tagNames(tags) {
|
|
94
|
+
if (!Array.isArray(tags) || !tags.every((tag) => typeof tag === "string"))
|
|
95
|
+
throw new TypeError("tags must be a list of tag names");
|
|
96
|
+
return tags;
|
|
97
|
+
}
|
|
98
|
+
function withTagNames(input) {
|
|
99
|
+
return input.tags === undefined ? input : { ...input, tags: tagNames(input.tags) };
|
|
100
|
+
}
|
|
92
101
|
const runResponseAliases = [
|
|
93
102
|
["datasetId", "evalSetId"],
|
|
94
103
|
["datasetName", "evalSetName"],
|
|
@@ -388,15 +397,21 @@ export class EvaluationClient {
|
|
|
388
397
|
const query = new URLSearchParams(this.page(options).slice(1));
|
|
389
398
|
if (options.includeArchived !== undefined)
|
|
390
399
|
query.set("includeArchived", String(options.includeArchived));
|
|
400
|
+
for (const tag of tagNames(options.tags ?? []))
|
|
401
|
+
query.append("tag", tag);
|
|
391
402
|
return query.size ? `?${query}` : "";
|
|
392
403
|
}
|
|
393
404
|
/** Reads the current project to confirm the key and origin. */
|
|
394
405
|
checkConnection() {
|
|
395
406
|
return this.request("GET", "/projects/current");
|
|
396
407
|
}
|
|
397
|
-
/** Creates a dataset with an initial draft version. */
|
|
408
|
+
/** Creates a dataset with an initial draft version; `tags` names the tags it starts with. */
|
|
398
409
|
createDataset(input) {
|
|
399
|
-
return this.request("POST", "/datasets", input);
|
|
410
|
+
return this.request("POST", "/datasets", withTagNames(input));
|
|
411
|
+
}
|
|
412
|
+
/** Renames a dataset or replaces its tags by name; the same values again change nothing. */
|
|
413
|
+
updateDataset(id, input) {
|
|
414
|
+
return this.request("PATCH", `/datasets/${uuid(id)}`, withTagNames(input), undefined, undefined, true);
|
|
400
415
|
}
|
|
401
416
|
/** Reads a dataset and its versions. */
|
|
402
417
|
getDataset(id) {
|
|
@@ -430,7 +445,15 @@ export class EvaluationClient {
|
|
|
430
445
|
}
|
|
431
446
|
/** Creates a scorer identity; publish definitions with {@link publishScorerVersion}. */
|
|
432
447
|
createScorer(input) {
|
|
433
|
-
return this.request("POST", "/scorers", input);
|
|
448
|
+
return this.request("POST", "/scorers", withTagNames(input));
|
|
449
|
+
}
|
|
450
|
+
/** Renames a scorer or replaces its tags by name; the same values again change nothing. */
|
|
451
|
+
updateScorer(id, input) {
|
|
452
|
+
return this.request("PATCH", `/scorers/${uuid(id)}`, withTagNames(input), undefined, undefined, true);
|
|
453
|
+
}
|
|
454
|
+
/** Lists the project's tags in its order: as people arranged them in Hue, then newer tags. */
|
|
455
|
+
listTags() {
|
|
456
|
+
return this.request("GET", "/tags");
|
|
434
457
|
}
|
|
435
458
|
/** Reads a scorer and its published versions. */
|
|
436
459
|
getScorer(id) {
|
|
@@ -452,6 +475,10 @@ export class EvaluationClient {
|
|
|
452
475
|
async createEvalSet(input) {
|
|
453
476
|
return productRegistryFields(await this.createDataset(input));
|
|
454
477
|
}
|
|
478
|
+
/** Renames an eval set or replaces its tags by name. */
|
|
479
|
+
async updateEvalSet(id, input) {
|
|
480
|
+
return productRegistryFields(await this.updateDataset(id, input));
|
|
481
|
+
}
|
|
455
482
|
/** Reads an eval set and its versions. */
|
|
456
483
|
async getEvalSet(id) {
|
|
457
484
|
return productRegistryFields(await this.getDataset(id));
|
|
@@ -484,6 +511,10 @@ export class EvaluationClient {
|
|
|
484
511
|
async createEvaluator(input) {
|
|
485
512
|
return productRegistryFields(await this.createScorer(input));
|
|
486
513
|
}
|
|
514
|
+
/** Renames an evaluator or replaces its tags by name. */
|
|
515
|
+
async updateEvaluator(id, input) {
|
|
516
|
+
return productRegistryFields(await this.updateScorer(id, input));
|
|
517
|
+
}
|
|
487
518
|
/** Reads an evaluator and its published versions. */
|
|
488
519
|
async getEvaluator(id) {
|
|
489
520
|
return productRegistryFields(await this.getScorer(id));
|
|
@@ -502,12 +533,16 @@ export class EvaluationClient {
|
|
|
502
533
|
}
|
|
503
534
|
/** Creates an experiment over a frozen dataset version with pinned scorer versions and a configuration. */
|
|
504
535
|
createExperiment(input) {
|
|
505
|
-
return this.request("POST", "/experiments", input);
|
|
536
|
+
return this.request("POST", "/experiments", withTagNames(input));
|
|
506
537
|
}
|
|
507
538
|
/** Reads an experiment with its evaluation run and execution counts. */
|
|
508
539
|
getExperiment(id) {
|
|
509
540
|
return this.request("GET", `/experiments/${uuid(id)}`);
|
|
510
541
|
}
|
|
542
|
+
/** Renames an experiment or replaces its own tags by name. */
|
|
543
|
+
updateExperiment(id, input) {
|
|
544
|
+
return this.request("PATCH", `/experiments/${uuid(id)}`, withTagNames(input), undefined, undefined, true);
|
|
545
|
+
}
|
|
511
546
|
/** Lists an experiment's cases with their latest executions. */
|
|
512
547
|
listExperimentItems(id, page) {
|
|
513
548
|
return this.request("GET", `/experiments/${uuid(id)}/items${this.page(page)}`);
|
|
@@ -656,12 +691,16 @@ export class EvaluationClient {
|
|
|
656
691
|
}
|
|
657
692
|
/** Creates a run using product request fields on the existing v1 path. */
|
|
658
693
|
async createRun(input) {
|
|
659
|
-
return productRunFields(await this.request("POST", "/experiments", input), "run");
|
|
694
|
+
return productRunFields(await this.request("POST", "/experiments", withTagNames(input)), "run");
|
|
660
695
|
}
|
|
661
696
|
/** Reads a run with its scoring progress. */
|
|
662
697
|
async getRun(id) {
|
|
663
698
|
return productRunFields(await this.getExperiment(id), "run");
|
|
664
699
|
}
|
|
700
|
+
/** Renames a run or replaces its own tags by name; its eval set's tags are not its own. */
|
|
701
|
+
async updateRun(id, input) {
|
|
702
|
+
return this.updateExperiment(id, input);
|
|
703
|
+
}
|
|
665
704
|
/** Lists a run's cases with their latest executions. */
|
|
666
705
|
async listRunItems(id, page) {
|
|
667
706
|
return productRunFields(await this.listExperimentItems(id, page), "run");
|
|
@@ -5,18 +5,25 @@ const metricName = z
|
|
|
5
5
|
.min(1)
|
|
6
6
|
.max(64)
|
|
7
7
|
.regex(/^[a-zA-Z][a-zA-Z0-9_]*$/);
|
|
8
|
+
const metricDescription = z.string().trim().min(1).max(500).optional();
|
|
8
9
|
const metric = z.discriminatedUnion("type", [
|
|
9
|
-
z.strictObject({
|
|
10
|
-
|
|
10
|
+
z.strictObject({
|
|
11
|
+
name: metricName,
|
|
12
|
+
type: z.literal("boolean"),
|
|
13
|
+
description: metricDescription,
|
|
14
|
+
}),
|
|
15
|
+
z.strictObject({ name: metricName, type: z.literal("text"), description: metricDescription }),
|
|
11
16
|
z.strictObject({
|
|
12
17
|
name: metricName,
|
|
13
18
|
type: z.literal("number"),
|
|
19
|
+
description: metricDescription,
|
|
14
20
|
min: z.number().finite().optional(),
|
|
15
21
|
max: z.number().finite().optional(),
|
|
16
22
|
}),
|
|
17
23
|
z.strictObject({
|
|
18
24
|
name: metricName,
|
|
19
25
|
type: z.literal("category"),
|
|
26
|
+
description: metricDescription,
|
|
20
27
|
categories: z.array(z.string()).min(1),
|
|
21
28
|
}),
|
|
22
29
|
]);
|
|
@@ -135,12 +142,17 @@ const sdkScorerPublication = z.union([
|
|
|
135
142
|
}),
|
|
136
143
|
z.strictObject({
|
|
137
144
|
kind: z.literal("world_outcome"),
|
|
138
|
-
entry: z.
|
|
145
|
+
entry: z.enum([
|
|
146
|
+
"hue.outcome_assertions.v2",
|
|
147
|
+
"hue.outcome_assertions.v4",
|
|
148
|
+
"hue.outcome_assertions.v5",
|
|
149
|
+
"hue.outcome_assertions.v6",
|
|
150
|
+
]),
|
|
139
151
|
metrics: fixedMetrics(outcomeAssertionMetrics),
|
|
140
152
|
}),
|
|
141
153
|
z.strictObject({
|
|
142
154
|
kind: z.literal("world_outcome"),
|
|
143
|
-
entry: z.
|
|
155
|
+
entry: z.enum(["hue.outcome_assertions.v3", "hue.answer_outcome.v1", "hue.answer_outcome.v2"]),
|
|
144
156
|
metrics: fixedMetrics(outcomeAssertionV3Metrics),
|
|
145
157
|
config: z.strictObject({ judge: outcomeJudgeConfig }),
|
|
146
158
|
}),
|
package/dist/evals/scorers.js
CHANGED
|
@@ -32,7 +32,9 @@ export function defineLocalScorer(options) {
|
|
|
32
32
|
language: "typescript",
|
|
33
33
|
entrypoint: options.entrypoint,
|
|
34
34
|
sourceDigest: sourceDigest(options.source),
|
|
35
|
-
metrics: options.metrics
|
|
35
|
+
metrics: options.metrics.map((metric) => metric.description === undefined
|
|
36
|
+
? metric
|
|
37
|
+
: { ...metric, description: metric.description.trim() }),
|
|
36
38
|
},
|
|
37
39
|
score: options.score,
|
|
38
40
|
};
|
package/dist/evals/types.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface PageOptions {
|
|
|
19
19
|
export interface RegistryPageOptions extends PageOptions {
|
|
20
20
|
/** Include archived identities so callers can diagnose slug conflicts explicitly. */
|
|
21
21
|
includeArchived?: boolean;
|
|
22
|
+
/** Only identities carrying any of these tag names, matched without case. */
|
|
23
|
+
tags?: string[];
|
|
22
24
|
}
|
|
23
25
|
/** Human-readable identity of a dataset or scorer. */
|
|
24
26
|
export interface Identity {
|
|
@@ -29,12 +31,50 @@ export interface Identity {
|
|
|
29
31
|
/** Optional description. */
|
|
30
32
|
description?: string;
|
|
31
33
|
}
|
|
34
|
+
/** A dataset or scorer to create, with the names of the tags it starts with. */
|
|
35
|
+
export interface TaggedIdentity extends Identity {
|
|
36
|
+
/** Tag names; a name the project has no tag for creates one. */
|
|
37
|
+
tags?: string[];
|
|
38
|
+
}
|
|
39
|
+
/** Identity fields and tags to change on a dataset or scorer; omitted fields stay. */
|
|
40
|
+
export interface IdentityUpdate {
|
|
41
|
+
/** New display name. */
|
|
42
|
+
name?: string;
|
|
43
|
+
/** New description. */
|
|
44
|
+
description?: string;
|
|
45
|
+
/** Every tag name the item should carry; replaces its tags. */
|
|
46
|
+
tags?: string[];
|
|
47
|
+
}
|
|
48
|
+
/** An experiment's (run's) name and tags after an update. */
|
|
49
|
+
export interface UpdatedRun {
|
|
50
|
+
/** Experiment (run) ID. */
|
|
51
|
+
id: string;
|
|
52
|
+
/** Display name. */
|
|
53
|
+
name: string;
|
|
54
|
+
/** The experiment's own tags. */
|
|
55
|
+
tags: Tag[];
|
|
56
|
+
/** Its dataset's (eval set's) tags. */
|
|
57
|
+
evalSetTags: Tag[];
|
|
58
|
+
}
|
|
59
|
+
/** The colors a tag can take. */
|
|
60
|
+
export type TagColor = "default" | "gray" | "brown" | "orange" | "yellow" | "green" | "blue" | "purple" | "pink" | "red";
|
|
61
|
+
/** A project's tag: a named, colored label that groups datasets, scorers and experiments. */
|
|
62
|
+
export interface Tag {
|
|
63
|
+
/** Tag ID. */
|
|
64
|
+
id: string;
|
|
65
|
+
/** Name, unique in the project without case. */
|
|
66
|
+
name: string;
|
|
67
|
+
/** Display color. */
|
|
68
|
+
color: TagColor;
|
|
69
|
+
}
|
|
32
70
|
/** A dataset and its versions. */
|
|
33
71
|
export interface Dataset extends Identity {
|
|
34
72
|
/** Dataset ID. */
|
|
35
73
|
id: string;
|
|
36
74
|
/** Archive timestamp on current servers; absent on older compatible responses. */
|
|
37
75
|
archivedAt?: string | null;
|
|
76
|
+
/** The dataset's tags on servers that support tags; absent on older responses. */
|
|
77
|
+
tags?: Tag[];
|
|
38
78
|
/** Versions of this dataset. */
|
|
39
79
|
versions: DatasetVersion[];
|
|
40
80
|
}
|
|
@@ -139,11 +179,15 @@ export type MetricDefinition = {
|
|
|
139
179
|
name: string;
|
|
140
180
|
/** Boolean or free-text metric. */
|
|
141
181
|
type: "boolean" | "text";
|
|
182
|
+
/** What the metric checks, shown with its check in Hue's run view (1–500 characters). */
|
|
183
|
+
description?: string;
|
|
142
184
|
} | {
|
|
143
185
|
/** Metric name. */
|
|
144
186
|
name: string;
|
|
145
187
|
/** Numeric metric. */
|
|
146
188
|
type: "number";
|
|
189
|
+
/** What the metric checks, shown with its check in Hue's run view (1–500 characters). */
|
|
190
|
+
description?: string;
|
|
147
191
|
/** Inclusive lower bound. */
|
|
148
192
|
min?: number;
|
|
149
193
|
/** Inclusive upper bound. */
|
|
@@ -153,6 +197,8 @@ export type MetricDefinition = {
|
|
|
153
197
|
name: string;
|
|
154
198
|
/** Categorical metric. */
|
|
155
199
|
type: "category";
|
|
200
|
+
/** What the metric checks, shown with its check in Hue's run view (1–500 characters). */
|
|
201
|
+
description?: string;
|
|
156
202
|
/** Allowed values. */
|
|
157
203
|
categories: string[];
|
|
158
204
|
};
|
|
@@ -229,6 +275,25 @@ export type ScorerDefinition = {
|
|
|
229
275
|
/** The judge every case scored by this version uses. */
|
|
230
276
|
judge: OutcomeJudgeConfig;
|
|
231
277
|
};
|
|
278
|
+
} | {
|
|
279
|
+
/** A case's reviewed assertions, scored inside Hue from gateway evidence. */
|
|
280
|
+
kind: "world_outcome";
|
|
281
|
+
/** Versioned gateway-ledger assertion semantics. */
|
|
282
|
+
entry: "hue.outcome_assertions.v4" | "hue.outcome_assertions.v5" | "hue.outcome_assertions.v6";
|
|
283
|
+
/** `task_success` and the fixed assertion counts defined by the entry. */
|
|
284
|
+
metrics: MetricDefinition[];
|
|
285
|
+
} | {
|
|
286
|
+
/** An answer-only case, scored inside Hue without world evidence. */
|
|
287
|
+
kind: "world_outcome";
|
|
288
|
+
/** Versioned answer semantics; the pinned judge is required to decide the case. */
|
|
289
|
+
entry: "hue.answer_outcome.v1" | "hue.answer_outcome.v2";
|
|
290
|
+
/** The assertion and judge counts defined by the entry. */
|
|
291
|
+
metrics: MetricDefinition[];
|
|
292
|
+
/** The judge pin used to score the answer. */
|
|
293
|
+
config: {
|
|
294
|
+
/** The required judge every case scored by this version uses. */
|
|
295
|
+
judge: OutcomeJudgeConfig;
|
|
296
|
+
};
|
|
232
297
|
} | {
|
|
233
298
|
/** Scored by a person in Hue; the local runner defers it. */
|
|
234
299
|
kind: "manual";
|
|
@@ -397,6 +462,8 @@ export interface Scorer extends Identity {
|
|
|
397
462
|
id: string;
|
|
398
463
|
/** Archive timestamp on current servers; absent on older compatible responses. */
|
|
399
464
|
archivedAt?: string | null;
|
|
465
|
+
/** The scorer's tags on servers that support tags; absent on older responses. */
|
|
466
|
+
tags?: Tag[];
|
|
400
467
|
/** Published versions, when included in the response. */
|
|
401
468
|
versions?: ScorerVersion[];
|
|
402
469
|
}
|
|
@@ -486,6 +553,10 @@ export interface Experiment {
|
|
|
486
553
|
name: string;
|
|
487
554
|
/** Frozen dataset version under test. */
|
|
488
555
|
datasetVersionId: string;
|
|
556
|
+
/** The experiment's own tags on servers that support tags; absent on older responses. */
|
|
557
|
+
tags?: Tag[];
|
|
558
|
+
/** Its dataset's tags, which tag filters match as well; absent on older responses. */
|
|
559
|
+
evalSetTags?: Tag[];
|
|
489
560
|
/** Configuration handed to the target. */
|
|
490
561
|
config: JsonValue;
|
|
491
562
|
/** Digest of `config`. */
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Package version shared by the instrumentation scope and the export User-Agent. */
|
|
2
|
-
export declare const sdkVersion = "0.13.
|
|
2
|
+
export declare const sdkVersion = "0.13.2";
|
package/dist/version.js
CHANGED