@hue-run/sdk 0.5.0 → 0.6.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 +25 -20
- package/ENVIRONMENTS.md +2 -2
- package/EVALUATIONS.md +20 -1
- package/README.md +5 -5
- package/dist/cli/eval.js +14 -11
- package/dist/cli/login.d.ts +1 -1
- package/dist/cli/login.js +85 -53
- package/dist/evals/client.d.ts +99 -2
- package/dist/evals/client.js +196 -1
- package/dist/evals/scenarios.d.ts +2 -2
- package/dist/evals/scenarios.js +2 -2
- package/dist/evals/types.d.ts +77 -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.6.0`, `@opentelemetry/api@1.9.1` and
|
|
46
46
|
`@opentelemetry/context-async-hooks@2.11.0`; Python uses published `hue-run==0.2.2`.
|
|
47
47
|
Content capture requires an ordinary account-managed key and a later explicit application decision.
|
|
48
48
|
|
|
@@ -145,14 +145,17 @@ handoff does not create a trial, reset quota or rerun business work.
|
|
|
145
145
|
`hue login` stores keys that a person creates in Hue; it never mints one, because setup
|
|
146
146
|
credentials are deliberately isolated from ordinary project keys. It prints the key settings page
|
|
147
147
|
(`<origin>/settings/integrations`, opened in a browser only when a terminal is attached and
|
|
148
|
-
`--no-browser` is absent), then reads
|
|
149
|
-
|
|
150
|
-
`
|
|
151
|
-
MCP `tools/list` request
|
|
152
|
-
`
|
|
148
|
+
`--no-browser` is absent), then reads one **Read and write** key from stdin without echo. By
|
|
149
|
+
default that single key serves both uses: it is checked with `GET /api/v1/projects/current` and
|
|
150
|
+
`GET /api/v1/datasets` (a **Read** or **Tracing only** key is refused because it cannot use
|
|
151
|
+
evaluations) and with an MCP `tools/list` request, then stored as `HUE_API_KEY` with
|
|
152
|
+
`HUE_BASE_URL` and `HUE_MCP_KEY` with `HUE_MCP_URL` in one write. A rejected key (`401` or `403`)
|
|
153
|
+
exits `1` and stores nothing. `--keys evaluations` stores only `HUE_API_KEY`; `--keys coding-agent`
|
|
154
|
+
stores only `HUE_MCP_KEY` and also accepts a **Read** key, for an agent that should only inspect the
|
|
155
|
+
project. Run them separately with different keys if you want to revoke either use alone.
|
|
153
156
|
|
|
154
157
|
```sh
|
|
155
|
-
hue login # both
|
|
158
|
+
hue login # one key for both uses into ./.env.hue
|
|
156
159
|
hue login --keys coding-agent --gitignore # only HUE_MCP_KEY; add .env.hue to .gitignore
|
|
157
160
|
hue login --origin https://staging.hue.run --env-file .env.staging
|
|
158
161
|
```
|
|
@@ -163,7 +166,7 @@ replaced only with `--force`. Empty values, whitespace and URLs are refused befo
|
|
|
163
166
|
The MCP endpoint is `https://mcp.hue.run/mcp` for `https://app.hue.run`,
|
|
164
167
|
`https://mcp.staging.hue.run/mcp` for `https://staging.hue.run` and `<origin>/api/mcp` otherwise;
|
|
165
168
|
plain HTTP origins are accepted for loopback test servers only. Output names variables and lengths
|
|
166
|
-
(`Stored
|
|
169
|
+
(`Stored the key (NN chars) as HUE_API_KEY and HUE_MCP_KEY`), never values. When git does not ignore the env file, the command
|
|
167
170
|
warns; `--gitignore` appends the file name to the `.gitignore` next to it. Exit codes: `0` stored,
|
|
168
171
|
`1` failed, `2` usage error, `130` interrupted.
|
|
169
172
|
|
|
@@ -297,7 +300,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
|
|
|
297
300
|
# Set project to an existing supported fixture; use the same directory on resume.
|
|
298
301
|
project=/absolute/path/to/supported-fixture
|
|
299
302
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
300
|
-
--archive .artifacts/typescript/hue-run-sdk-0.
|
|
303
|
+
--archive .artifacts/typescript/hue-run-sdk-0.6.0.tgz \
|
|
301
304
|
--origin https://STAGING_ORIGIN \
|
|
302
305
|
--project "$project" --command setup \
|
|
303
306
|
--evidence .context/setup-staging-before-claim.json
|
|
@@ -308,7 +311,7 @@ the private local handoff and finish the real browser claim, then reconcile the
|
|
|
308
311
|
|
|
309
312
|
```sh
|
|
310
313
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
311
|
-
--archive .artifacts/typescript/hue-run-sdk-0.
|
|
314
|
+
--archive .artifacts/typescript/hue-run-sdk-0.6.0.tgz \
|
|
312
315
|
--origin https://STAGING_ORIGIN \
|
|
313
316
|
--project "$project" --command claim \
|
|
314
317
|
--evidence .context/setup-staging-after-claim.json
|
|
@@ -330,14 +333,14 @@ Publication under a candidate dist-tag, registry acceptance, production activati
|
|
|
330
333
|
the same version to `latest`, and clean-project literal `@latest` smoke remain separate gates; see
|
|
331
334
|
[RELEASING.md](../../RELEASING.md).
|
|
332
335
|
|
|
333
|
-
## Evaluate an agent against a
|
|
336
|
+
## Evaluate an agent against a case
|
|
334
337
|
|
|
335
|
-
`hue eval` runs a developer's local agent against a published Hue
|
|
338
|
+
`hue eval` runs a developer's local agent against a published Hue case or a saved eval set
|
|
336
339
|
and prints Hue's verdicts. It is the command-line form of `runSimulation()` (one-shot) and
|
|
337
340
|
`runLocalAgent()` (worker): the agent, its prompts and its provider credentials stay in the local
|
|
338
341
|
process, Hue creates one isolated simulated world per case, and Hue-owned outcome checks grade
|
|
339
|
-
the sealed world. Hue never executes the agent. `HUE_API_KEY` must be a **
|
|
340
|
-
|
|
342
|
+
the sealed world. Hue never executes the agent. `HUE_API_KEY` must be a **Read and
|
|
343
|
+
write** project key (a **Read** or **Tracing only** key cannot read cases or create runs);
|
|
341
344
|
the CLI never prints it. The optional `zod` peer of `@hue-run/sdk/evals` must be installed.
|
|
342
345
|
|
|
343
346
|
Write an adapter module that hands the case inputs and the world's tools or MCP connection to
|
|
@@ -359,16 +362,18 @@ export default function runMyAgent(inputs: JsonValue, context: SimulationTargetC
|
|
|
359
362
|
```
|
|
360
363
|
|
|
361
364
|
```sh
|
|
362
|
-
hue eval --
|
|
363
|
-
hue eval --
|
|
365
|
+
hue eval --case "Refund an eligible charge" ./hue-agent.ts --env-file .env.hue
|
|
366
|
+
hue eval --case https://app.hue.run/projects/demo/scenarios/<id> ./hue-agent.ts --baseline <run id>
|
|
364
367
|
hue eval --set "Billing regressions" --scorer-version <id> ./hue-agent.ts --save-version
|
|
365
|
-
hue eval --
|
|
368
|
+
hue eval --case "Refund an eligible charge" --command "python agent.py" --timeout 120 --content
|
|
366
369
|
hue eval --worker ./hue-agent.ts --agent-key support-agent --env-file .env.hue
|
|
367
370
|
```
|
|
368
371
|
|
|
369
|
-
|
|
372
|
+
`--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
|
|
373
|
+
|
|
374
|
+
The one-shot mode resolves the selection (`--case` by name, ID or URL; `--set` by name, ID or
|
|
370
375
|
URL with explicit `--scorer-version` pins; or `--dataset-version` with `--scorer-version`),
|
|
371
|
-
creates a fresh
|
|
376
|
+
creates a fresh run from those immutable pins named `<case> · <agent key> · <revision>`
|
|
372
377
|
(`--name` overrides), prints `Run: <url>` and `Experiment: <id>` as soon as the experiment exists,
|
|
373
378
|
one line per case event (world created, agent started, world sealed), then
|
|
374
379
|
`Waiting for Hue checks...` and a table with one row per case: boolean metrics as `PASS`/`FAIL`,
|
|
@@ -418,7 +423,7 @@ on any Node.js version.
|
|
|
418
423
|
|
|
419
424
|
Eval sets whose cases pin no simulated world — a task plus pinned input files, answered with
|
|
420
425
|
generated documents — run as **direct** cases through `runExperiment()`. `hue eval` detects this
|
|
421
|
-
from the saved version (`--mode direct|simulation` overrides the detection; `--
|
|
426
|
+
from the saved version (`--mode direct|simulation` overrides the detection; `--case` is always
|
|
422
427
|
a simulation). `--set` accepts the eval set's slug, name, ID or URL; `--set-version <n>` pins a
|
|
423
428
|
saved version other than the latest; `--scorer <slug|name|id>` pins an evaluator at its newest
|
|
424
429
|
published version, beside or instead of explicit `--scorer-version` IDs.
|
package/ENVIRONMENTS.md
CHANGED
|
@@ -66,7 +66,7 @@ to the callback and is never written to checkpoints.
|
|
|
66
66
|
published immutable pins, such as a Scenario's frozen case and Hue-owned outcome checks or a saved
|
|
67
67
|
eval set with explicitly chosen scorer versions. `resolveScenarioPins(client, selector)` reads
|
|
68
68
|
those pins from a Scenario ID, its Hue URL or its name (`listScenarios` and `getScenario` expose
|
|
69
|
-
the underlying reads; a
|
|
69
|
+
the underlying reads; a Read and write key is required), and `resolveEvalSetPins`
|
|
70
70
|
resolves an eval set to its latest saved version. `runSimulation` creates the experiment directly
|
|
71
71
|
(`name` defaults to the dataset name, `config` to `{}`) and binds the pins and configuration into
|
|
72
72
|
the checkpoint identity, so resuming with different pins is refused like the other kinds.
|
|
@@ -79,7 +79,7 @@ until every item has a terminal result for every pin or the budget elapses (`com
|
|
|
79
79
|
`summarizeVerdicts` turns results into per-case rows with `passed` and totals; `compareVerdicts`
|
|
80
80
|
diffs two summaries by case key; and `collectExperimentVerdicts` combines those reads for one
|
|
81
81
|
experiment. The `hue eval` command uses the same path; see
|
|
82
|
-
[Evaluate an agent against a
|
|
82
|
+
[Evaluate an agent against a case](CLI.md#evaluate-an-agent-against-a-case).
|
|
83
83
|
|
|
84
84
|
### Pinned provider-profile preflight
|
|
85
85
|
|
package/EVALUATIONS.md
CHANGED
|
@@ -9,7 +9,7 @@ npm install @hue-run/sdk zod
|
|
|
9
9
|
|
|
10
10
|
The SDK executes targets and scorers on your machine. Hue stores pinned definitions, experiment progress and results. It does not execute uploaded source code. Follow the [installation guide](https://docs.hue.run/installation) to add `@hue-run/sdk` to your application.
|
|
11
11
|
|
|
12
|
-
Create a **
|
|
12
|
+
Create a **Read and write** project service key under **Settings → Integrations & API keys** and expose it to this server-side process as `HUE_API_KEY`. A **Tracing only** key cannot author datasets or evaluation runs.
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
@@ -70,6 +70,25 @@ try {
|
|
|
70
70
|
}
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
For new registry code, use `createEvalSet`, `getEvalSet`, `listEvalSets`,
|
|
74
|
+
`createEvalSetVersion`, `getEvalSetVersion`, `listEvalSetCases`, `addEvalSetCase`,
|
|
75
|
+
and `freezeEvalSetVersion`. Evaluators use `createEvaluator`, `getEvaluator`,
|
|
76
|
+
`listEvaluators`, `publishEvaluatorVersion`, and `getEvaluatorVersion`. These
|
|
77
|
+
methods return product fields such as `evalSetId` and `evalSetVersionId` alongside
|
|
78
|
+
the existing v1 fields. Evaluator versions include `evaluatorId` when the server
|
|
79
|
+
supplies their owning identity; older v1 responses may omit it. They use the existing v1 paths;
|
|
80
|
+
the earlier method names remain callable for existing integrations.
|
|
81
|
+
|
|
82
|
+
For new run and scoring code, use `createRun`, `getRun`, `listRunItems`,
|
|
83
|
+
`getRunCase`, `startRunExecution`, `getRunExecution`,
|
|
84
|
+
`completeRunExecution`, and `finishRun`. Use `createScoring`, `getScoring`,
|
|
85
|
+
`listScorings`, `listScoringItems`, `getScoringSubject`,
|
|
86
|
+
`submitScoringResults`, `listScoringResults`, and `getScoringResult` to score
|
|
87
|
+
saved subjects. `createRun` accepts `evalSetVersionId` and
|
|
88
|
+
`evaluatorVersionIds`; `createScoring` and `submitScoringResults` use evaluator
|
|
89
|
+
version IDs. A run ID and a scoring ID identify different records. The methods
|
|
90
|
+
use the existing v1 paths and leave existing runner entry points callable.
|
|
91
|
+
|
|
73
92
|
Create another experiment with the same frozen version and different `config` to compare configurations. The runner reads the exact experiment case/version and scorer definitions; it never resolves a mutable latest version. `rescore` accepts an existing evaluation-run ID and has no target callback. Subject IDs refer to immutable saved outputs and trace evidence.
|
|
74
93
|
|
|
75
94
|
`rescore` preserves terminal scores already recorded for each item and evaluator version,
|
package/README.md
CHANGED
|
@@ -445,12 +445,12 @@ their agents with `runLocalAgent()`; setup does not register workers or launch s
|
|
|
445
445
|
### Command-line evaluation
|
|
446
446
|
|
|
447
447
|
The unreleased `hue eval` command wraps `runSimulation()` and `runLocalAgent()` for an adapter
|
|
448
|
-
file or a shell command: `hue eval --
|
|
449
|
-
from a published
|
|
448
|
+
file or a shell command: `hue eval --case "<name>" ./hue-agent.ts` creates a fresh run
|
|
449
|
+
from a published case's immutable pins, runs the agent in one isolated world per case, waits
|
|
450
450
|
for Hue's outcome checks and prints the run URL and per-case PASS/FAIL verdicts with an exit code;
|
|
451
|
-
`--worker` registers the same adapter for runs launched from Hue. It needs a
|
|
452
|
-
|
|
453
|
-
is passed. See [Evaluate an agent against a
|
|
451
|
+
`--worker` registers the same adapter for runs launched from Hue. It needs a Read and
|
|
452
|
+
write key in `HUE_API_KEY` (never printed) and keeps content capture off unless `--content`
|
|
453
|
+
is passed. See [Evaluate an agent against a case](CLI.md#evaluate-an-agent-against-a-case).
|
|
454
454
|
Eval sets whose cases pin files instead of a world run as direct cases through `runExperiment()`:
|
|
455
455
|
`hue eval --set <slug> --scorer <slug> --command "…"` hands the agent each case's pinned files in a
|
|
456
456
|
private directory, uploads the documents it writes and waits for Hue's grading executor to score
|
package/dist/cli/eval.js
CHANGED
|
@@ -18,10 +18,11 @@ import { runSimulation, } from "../evals/simulation.js";
|
|
|
18
18
|
import { collectExperimentVerdicts, compareVerdicts, metricPassed, } from "../evals/verdicts.js";
|
|
19
19
|
const USAGE = `Usage: hue eval [adapter-file] [options]
|
|
20
20
|
|
|
21
|
-
Run a local agent against a
|
|
21
|
+
Run a local agent against a published case or eval set, then print Hue's verdicts.
|
|
22
22
|
|
|
23
23
|
Selection (exactly one, not used with --worker):
|
|
24
|
-
--
|
|
24
|
+
--case <name|id|url> Published case to run
|
|
25
|
+
--scenario <name|id|url> Legacy alias for --case
|
|
25
26
|
--set <name|id|url> Saved eval set; requires --scorer or --scorer-version
|
|
26
27
|
--set-version <n> Saved version number of the eval set (default: latest saved)
|
|
27
28
|
--dataset-version <id> Frozen dataset version; requires --scorer or --scorer-version
|
|
@@ -51,7 +52,7 @@ Connection:
|
|
|
51
52
|
--origin <url> Hue origin (default: HUE_BASE_URL or https://app.hue.run)
|
|
52
53
|
|
|
53
54
|
Output and limits:
|
|
54
|
-
--name <run name>
|
|
55
|
+
--name <run name> Run name (default: <case> · <agent key> · <revision>)
|
|
55
56
|
--baseline <experiment id|url> Compare verdicts with a previous experiment
|
|
56
57
|
--json Print one JSON document on stdout; progress goes to stderr
|
|
57
58
|
--content Capture telemetry content; one-shot also persists
|
|
@@ -63,7 +64,7 @@ Output and limits:
|
|
|
63
64
|
--wait <seconds> Verdict wait after the run finishes (default: 300)
|
|
64
65
|
-h, --help Show this help
|
|
65
66
|
|
|
66
|
-
HUE_API_KEY must be a "
|
|
67
|
+
HUE_API_KEY must be a "Read and write" project key; it is never printed.
|
|
67
68
|
Code evaluators pinned to a direct run are graded by Hue's executor after the upload; the wait
|
|
68
69
|
covers them. Exit codes: 0 every case passed, 1 a case failed, errored or is incomplete,
|
|
69
70
|
2 usage error, 130 interrupted.
|
|
@@ -82,6 +83,7 @@ function parse(argv) {
|
|
|
82
83
|
allowPositionals: true,
|
|
83
84
|
strict: true,
|
|
84
85
|
options: {
|
|
86
|
+
case: { type: "string" },
|
|
85
87
|
scenario: { type: "string" },
|
|
86
88
|
set: { type: "string" },
|
|
87
89
|
"set-version": { type: "string" },
|
|
@@ -414,7 +416,7 @@ function redact(message, secrets) {
|
|
|
414
416
|
}
|
|
415
417
|
function explain(error) {
|
|
416
418
|
if (error instanceof HueApiError && (error.status === 401 || error.status === 403))
|
|
417
|
-
return `${error.message}. Check that HUE_API_KEY is a "
|
|
419
|
+
return `${error.message}. Check that HUE_API_KEY is a "Read and write" project key for this origin.`;
|
|
418
420
|
if (error instanceof Error) {
|
|
419
421
|
const causes = [];
|
|
420
422
|
let cause = error.cause;
|
|
@@ -508,15 +510,16 @@ async function resolveSelection(client, values) {
|
|
|
508
510
|
const extra = [...(values["scorer-version"] ?? [])];
|
|
509
511
|
for (const selector of values.scorer ?? [])
|
|
510
512
|
extra.push(await resolveScorerVersion(client, selector));
|
|
511
|
-
|
|
513
|
+
const caseSelector = values.case ?? values.scenario;
|
|
514
|
+
if (caseSelector) {
|
|
512
515
|
if (values["set-version"])
|
|
513
516
|
throw new UsageError("--set-version applies to --set only");
|
|
514
|
-
const pins = await resolveScenarioPins(client,
|
|
517
|
+
const pins = await resolveScenarioPins(client, caseSelector);
|
|
515
518
|
pins.scorerVersionIds = [...new Set([...pins.scorerVersionIds, ...extra])];
|
|
516
519
|
return pins;
|
|
517
520
|
}
|
|
518
521
|
if (!extra.length)
|
|
519
|
-
throw new UsageError(`${values.set ? "--set" : "--dataset-version"} needs at least one --scorer or --scorer-version; use --
|
|
522
|
+
throw new UsageError(`${values.set ? "--set" : "--dataset-version"} needs at least one --scorer or --scorer-version; use --case for published pins`);
|
|
520
523
|
if (values.set) {
|
|
521
524
|
const pins = await resolveEvalSetPins(client, values.set, { scorerVersionIds: extra });
|
|
522
525
|
if (values["set-version"] === undefined)
|
|
@@ -918,11 +921,11 @@ export async function runEvalCommand(argv) {
|
|
|
918
921
|
const adapterFile = positionals[0];
|
|
919
922
|
if ((adapterFile ? 1 : 0) + (values.command ? 1 : 0) !== 1)
|
|
920
923
|
throw new UsageError("Pass exactly one agent: an adapter file or --command");
|
|
921
|
-
const selections = [values.scenario, values.set, values["dataset-version"]].filter((value) => value !== undefined).length;
|
|
924
|
+
const selections = [values.case, values.scenario, values.set, values["dataset-version"]].filter((value) => value !== undefined).length;
|
|
922
925
|
if (values.worker && selections)
|
|
923
926
|
throw new UsageError("--worker takes no selection; Hue chooses the run to execute");
|
|
924
927
|
if (!values.worker && selections !== 1)
|
|
925
|
-
throw new UsageError("Pass exactly one of --
|
|
928
|
+
throw new UsageError("Pass exactly one of --case, --set or --dataset-version");
|
|
926
929
|
if (values["env-file"]) {
|
|
927
930
|
try {
|
|
928
931
|
process.loadEnvFile(resolve(values["env-file"]));
|
|
@@ -933,7 +936,7 @@ export async function runEvalCommand(argv) {
|
|
|
933
936
|
}
|
|
934
937
|
const apiKey = process.env.HUE_API_KEY?.trim();
|
|
935
938
|
if (!apiKey)
|
|
936
|
-
throw new UsageError('HUE_API_KEY is required: a "
|
|
939
|
+
throw new UsageError('HUE_API_KEY is required: a "Read and write" project key, set in the environment or an ignored --env-file');
|
|
937
940
|
secrets.push(apiKey);
|
|
938
941
|
const baseUrl = values.origin ?? process.env.HUE_BASE_URL?.trim() ?? "https://app.hue.run";
|
|
939
942
|
const connection = { apiKey, baseUrl };
|
package/dist/cli/login.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface LoginCommandIo {
|
|
|
14
14
|
fetch?: typeof fetch;
|
|
15
15
|
openBrowser?: (url: string) => Promise<boolean>;
|
|
16
16
|
}
|
|
17
|
-
export declare const LOGIN_USAGE = "Usage: hue login [--origin URL] [--env-file PATH] [--keys evaluations|coding-agent|both]\n [--no-browser] [--force] [--gitignore]\n\nStore
|
|
17
|
+
export declare const LOGIN_USAGE = "Usage: hue login [--origin URL] [--env-file PATH] [--keys evaluations|coding-agent|both]\n [--no-browser] [--force] [--gitignore]\n\nStore the key you created in Hue in a private env file. By default one \"Read and write\" key serves\nboth evaluations (HUE_API_KEY) and your coding agent (HUE_MCP_KEY); it is validated against Hue\nbefore it is stored, and key values are never printed.\n\nOptions:\n --origin URL Hue origin (default https://app.hue.run)\n --env-file PATH Env file to write (default .env.hue in the current directory)\n --keys KIND evaluations (HUE_API_KEY), coding-agent (HUE_MCP_KEY) or both from one key\n (default both)\n --no-browser Do not open the key settings page in a browser\n --force Replace an existing different value in the env file\n --gitignore Add the env file to .gitignore when a git repository does not ignore it\n -h, --help Show this help";
|
|
18
18
|
/** Hue MCP endpoint that pairs with an application origin. */
|
|
19
19
|
export declare function mcpUrlForOrigin(origin: string): string;
|
|
20
20
|
/** Normalizes a Hue origin: HTTPS, or HTTP for loopback only; no credentials, path, query or hash. */
|
package/dist/cli/login.js
CHANGED
|
@@ -19,24 +19,26 @@ const KEY_KINDS = {
|
|
|
19
19
|
evaluations: {
|
|
20
20
|
variable: "HUE_API_KEY",
|
|
21
21
|
urlVariable: "HUE_BASE_URL",
|
|
22
|
-
preset: "Tracing and evaluations",
|
|
23
22
|
},
|
|
24
23
|
"coding-agent": {
|
|
25
24
|
variable: "HUE_MCP_KEY",
|
|
26
25
|
urlVariable: "HUE_MCP_URL",
|
|
27
|
-
preset: "Coding agent (read + evaluations)",
|
|
28
26
|
},
|
|
29
27
|
};
|
|
28
|
+
/** Settings preset that authorizes both evaluations and the coding agent's MCP reads and writes. */
|
|
29
|
+
const KEY_PRESET = "Read and write";
|
|
30
30
|
export const LOGIN_USAGE = `Usage: hue login [--origin URL] [--env-file PATH] [--keys evaluations|coding-agent|both]
|
|
31
31
|
[--no-browser] [--force] [--gitignore]
|
|
32
32
|
|
|
33
|
-
Store
|
|
34
|
-
|
|
33
|
+
Store the key you created in Hue in a private env file. By default one "Read and write" key serves
|
|
34
|
+
both evaluations (HUE_API_KEY) and your coding agent (HUE_MCP_KEY); it is validated against Hue
|
|
35
|
+
before it is stored, and key values are never printed.
|
|
35
36
|
|
|
36
37
|
Options:
|
|
37
38
|
--origin URL Hue origin (default ${DEFAULT_ORIGIN})
|
|
38
39
|
--env-file PATH Env file to write (default ${DEFAULT_ENV_FILE} in the current directory)
|
|
39
|
-
--keys KIND evaluations (HUE_API_KEY), coding-agent (HUE_MCP_KEY) or both
|
|
40
|
+
--keys KIND evaluations (HUE_API_KEY), coding-agent (HUE_MCP_KEY) or both from one key
|
|
41
|
+
(default both)
|
|
40
42
|
--no-browser Do not open the key settings page in a browser
|
|
41
43
|
--force Replace an existing different value in the env file
|
|
42
44
|
--gitignore Add the env file to .gitignore when a git repository does not ignore it
|
|
@@ -216,7 +218,10 @@ async function readBoundedText(response) {
|
|
|
216
218
|
function isRecord(value) {
|
|
217
219
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
218
220
|
}
|
|
219
|
-
/**
|
|
221
|
+
/**
|
|
222
|
+
* Mirrors `checkConnection()` with `GET /api/v1/projects/current`, then confirms evaluation access
|
|
223
|
+
* with `GET /api/v1/datasets`. No redirects are followed.
|
|
224
|
+
*/
|
|
220
225
|
async function checkEvaluationsKey(fetchImpl, origin, apiKey) {
|
|
221
226
|
let response;
|
|
222
227
|
try {
|
|
@@ -246,15 +251,44 @@ async function checkEvaluationsKey(fetchImpl, origin, apiKey) {
|
|
|
246
251
|
detail: `Hue answered HTTP ${response.status} while checking the evaluations key.`,
|
|
247
252
|
};
|
|
248
253
|
}
|
|
254
|
+
let projectName;
|
|
249
255
|
try {
|
|
250
256
|
const project = JSON.parse(await readBoundedText(response));
|
|
251
257
|
if (!isRecord(project) || typeof project.name !== "string")
|
|
252
258
|
throw new Error("Invalid project");
|
|
253
|
-
|
|
259
|
+
projectName = project.name;
|
|
254
260
|
}
|
|
255
261
|
catch {
|
|
256
262
|
return { ok: false, rejected: false, detail: "Hue returned an unexpected project response." };
|
|
257
263
|
}
|
|
264
|
+
// Every valid key reaches the project check; only a key with write access can list eval sets,
|
|
265
|
+
// so a Read or Tracing only key is refused here instead of failing later in `hue eval`.
|
|
266
|
+
let evaluations;
|
|
267
|
+
try {
|
|
268
|
+
evaluations = await fetchImpl(`${origin}/api/v1/datasets`, {
|
|
269
|
+
method: "GET",
|
|
270
|
+
headers: { authorization: `Bearer ${apiKey}`, accept: "application/json" },
|
|
271
|
+
redirect: "error",
|
|
272
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MILLIS),
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return { ok: false, rejected: false, detail: `Could not reach ${origin}.` };
|
|
277
|
+
}
|
|
278
|
+
await evaluations.body?.cancel();
|
|
279
|
+
if (evaluations.status === 401 || evaluations.status === 403)
|
|
280
|
+
return {
|
|
281
|
+
ok: false,
|
|
282
|
+
rejected: true,
|
|
283
|
+
detail: `This key cannot use evaluations (HTTP ${evaluations.status}); it is a Read or Tracing only key.`,
|
|
284
|
+
};
|
|
285
|
+
if (!evaluations.ok)
|
|
286
|
+
return {
|
|
287
|
+
ok: false,
|
|
288
|
+
rejected: false,
|
|
289
|
+
detail: `Hue answered HTTP ${evaluations.status} while checking evaluation access.`,
|
|
290
|
+
};
|
|
291
|
+
return { ok: true, detail: projectName };
|
|
258
292
|
}
|
|
259
293
|
/** Reads JSON-RPC messages from a JSON body or a `text/event-stream` body. */
|
|
260
294
|
function parseJsonRpcMessages(text, contentType) {
|
|
@@ -574,6 +608,9 @@ export async function runLoginCommand(argv, io = {}) {
|
|
|
574
608
|
if (keysOption !== "evaluations" && keysOption !== "coding-agent" && keysOption !== "both")
|
|
575
609
|
return fail(`--keys must be evaluations, coding-agent or both.\n\n${LOGIN_USAGE}`, 2);
|
|
576
610
|
const kinds = keysOption === "both" ? ["evaluations", "coding-agent"] : [keysOption];
|
|
611
|
+
// Every requested variable comes from one pasted key: the same "Read and write" preset serves
|
|
612
|
+
// evaluations and the coding agent, so asking twice would only add a step.
|
|
613
|
+
const variables = kinds.map((kind) => KEY_KINDS[kind].variable).join(" and ");
|
|
577
614
|
const origin = parseHueOrigin(parsed.values.origin ?? DEFAULT_ORIGIN);
|
|
578
615
|
if (!origin)
|
|
579
616
|
return fail("--origin must be an HTTPS origin such as https://app.hue.run (plain HTTP is accepted for loopback test servers only).", 2);
|
|
@@ -588,10 +625,9 @@ export async function runLoginCommand(argv, io = {}) {
|
|
|
588
625
|
return fail(error.message);
|
|
589
626
|
}
|
|
590
627
|
const settingsUrl = `${origin}${KEY_SETTINGS_PATH}`;
|
|
591
|
-
out("Hue keys are created in the app; this command validates and stores
|
|
592
|
-
out(`Create
|
|
593
|
-
|
|
594
|
-
out(` ${KEY_KINDS[kind].variable}: a "${KEY_KINDS[kind].preset}" key`);
|
|
628
|
+
out("Hue keys are created in the app; this command validates and stores one locally.");
|
|
629
|
+
out(`Create a "${KEY_PRESET}" key at: ${settingsUrl}`);
|
|
630
|
+
out(` It is stored as ${variables}.`);
|
|
595
631
|
if (!parsed.values["no-browser"] && isTTY(stdout)) {
|
|
596
632
|
const opened = await openBrowser(settingsUrl).catch(() => false);
|
|
597
633
|
if (opened)
|
|
@@ -603,75 +639,71 @@ export async function runLoginCommand(argv, io = {}) {
|
|
|
603
639
|
out(`${variable} is already stored in ${envDisplay}; a different value requires --force.`);
|
|
604
640
|
}
|
|
605
641
|
const stored = [];
|
|
606
|
-
//
|
|
607
|
-
// the
|
|
642
|
+
// Validation and the write record why they stopped instead of returning, so a key that did land
|
|
643
|
+
// in the file still reaches the ignore protection below.
|
|
608
644
|
let failure;
|
|
609
645
|
const prompter = isTTY(stdin)
|
|
610
646
|
? createTerminalPrompter(stdin, stdout)
|
|
611
647
|
: createLinePrompter(stdin, stdout);
|
|
612
|
-
const nothingElse = () => (stored.length ? "No further key was stored." : "Nothing was stored.");
|
|
613
648
|
try {
|
|
649
|
+
const answer = await prompter.ask(`Paste the "${KEY_PRESET}" key (${variables}): `);
|
|
650
|
+
const value = answer?.trim() ?? "";
|
|
651
|
+
const reason = answer === null ? null : invalidKeyReason(value);
|
|
652
|
+
const updates = {};
|
|
614
653
|
for (const kind of kinds) {
|
|
615
|
-
const { variable, urlVariable
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
[variable, value],
|
|
633
|
-
[urlVariable, urlValue],
|
|
634
|
-
]) {
|
|
635
|
-
const current = readEnvValue(envFile.text, name);
|
|
636
|
-
if (current !== undefined && current !== next)
|
|
637
|
-
failure ??= {
|
|
638
|
-
message: `${name} in ${envDisplay} already has a different value; rerun with --force to replace it.`,
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
if (failure)
|
|
642
|
-
break;
|
|
654
|
+
const { variable, urlVariable } = KEY_KINDS[kind];
|
|
655
|
+
updates[variable] = value;
|
|
656
|
+
updates[urlVariable] = kind === "evaluations" ? origin : mcpUrl;
|
|
657
|
+
}
|
|
658
|
+
if (answer === null)
|
|
659
|
+
failure = { message: `No key was entered; input ended.` };
|
|
660
|
+
else if (reason)
|
|
661
|
+
failure = {
|
|
662
|
+
message: `${reason} Create a "${KEY_PRESET}" key at ${settingsUrl} and paste it.`,
|
|
663
|
+
};
|
|
664
|
+
else if (!parsed.values.force) {
|
|
665
|
+
for (const [name, next] of Object.entries(updates)) {
|
|
666
|
+
const current = readEnvValue(envFile.text, name);
|
|
667
|
+
if (current !== undefined && current !== next)
|
|
668
|
+
failure ??= {
|
|
669
|
+
message: `${name} in ${envDisplay} already has a different value; rerun with --force to replace it.`,
|
|
670
|
+
};
|
|
643
671
|
}
|
|
672
|
+
}
|
|
673
|
+
for (const kind of failure ? [] : kinds) {
|
|
644
674
|
const check = kind === "evaluations"
|
|
645
675
|
? await checkEvaluationsKey(fetchImpl, origin, value)
|
|
646
676
|
: await checkCodingAgentKey(fetchImpl, mcpUrl, value);
|
|
647
677
|
if (!check.ok) {
|
|
648
678
|
failure = {
|
|
649
679
|
message: check.rejected
|
|
650
|
-
? `${check.detail} Create a "${
|
|
651
|
-
: `${check.detail}
|
|
680
|
+
? `${check.detail} Create a "${KEY_PRESET}" key at ${settingsUrl} and try again. Nothing was stored.`
|
|
681
|
+
: `${check.detail} Nothing was stored.`,
|
|
652
682
|
};
|
|
653
683
|
break;
|
|
654
684
|
}
|
|
655
685
|
out(kind === "evaluations"
|
|
656
|
-
? `Evaluations
|
|
657
|
-
: `Coding-agent
|
|
658
|
-
|
|
686
|
+
? `Evaluations access accepted for project "${check.detail}".`
|
|
687
|
+
: `Coding-agent access accepted; the Hue MCP server lists ${check.detail} tools.`);
|
|
688
|
+
}
|
|
689
|
+
if (!failure) {
|
|
690
|
+
const text = mergeEnvText(envFile.text, updates);
|
|
659
691
|
try {
|
|
660
692
|
await writePrivateFile(envPath, text, 0o600);
|
|
693
|
+
envFile = { text, exists: true };
|
|
694
|
+
stored.push(...kinds);
|
|
695
|
+
const urls = kinds.map((kind) => KEY_KINDS[kind].urlVariable).join(" and ");
|
|
696
|
+
out(`Stored the key (${value.length} chars) as ${variables}, with ${urls}, in ${envDisplay}.`);
|
|
661
697
|
}
|
|
662
698
|
catch (error) {
|
|
663
699
|
failure = { message: `Could not write ${envDisplay}: ${error.message}` };
|
|
664
|
-
break;
|
|
665
700
|
}
|
|
666
|
-
envFile = { text, exists: true };
|
|
667
|
-
stored.push(kind);
|
|
668
|
-
out(`Stored ${variable} (${value.length} chars) and ${urlVariable} in ${envDisplay}.`);
|
|
669
701
|
}
|
|
670
702
|
}
|
|
671
703
|
catch (error) {
|
|
672
704
|
failure =
|
|
673
705
|
error instanceof PromptInterrupted
|
|
674
|
-
? { message:
|
|
706
|
+
? { message: "Interrupted; nothing was stored.", code: 130 }
|
|
675
707
|
: { message: `hue login failed: ${error.message}` };
|
|
676
708
|
}
|
|
677
709
|
finally {
|
|
@@ -707,6 +739,6 @@ export async function runLoginCommand(argv, io = {}) {
|
|
|
707
739
|
// `hue mcp install` defaults to production; a non-default origin needs its own endpoint.
|
|
708
740
|
out(` hue mcp install --client claude-code${mcpUrl === mcpUrlForOrigin(DEFAULT_ORIGIN) ? "" : ` --url ${mcpUrl}`}`);
|
|
709
741
|
if (stored.includes("evaluations"))
|
|
710
|
-
out(` hue eval --
|
|
742
|
+
out(` hue eval --case "<name>" ./hue-agent.ts --env-file ${envDisplay}`);
|
|
711
743
|
return 0;
|
|
712
744
|
}
|
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, EvaluationItem, EvaluationRun, EvaluationRunSummary, EnvironmentEvidenceSnapshot, Execution, Experiment, ExperimentCase, ExperimentItem, Identity, JsonValue, LocalAgentClaim, LocalAgentRegistration, RegisteredLocalAgent, JudgeBudget, JudgeJob, Page, PageOptions, RegistryPageOptions, Result, ResultSummary, Scorer, ScorerDefinition, ScorerVersion, SimulationMcpCapability, StartExecution, Subject, StoredResult } from "./types.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, Identity, 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 } 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. */
|
|
@@ -71,6 +71,39 @@ export declare class EvaluationClient {
|
|
|
71
71
|
publishScorerVersion(id: string, definition: ScorerDefinition): Promise<ScorerVersion>;
|
|
72
72
|
/** Reads a published scorer version. */
|
|
73
73
|
getScorerVersion(id: string): Promise<ScorerVersion>;
|
|
74
|
+
/** Creates an eval set using the existing v1 registry path. */
|
|
75
|
+
createEvalSet(input: Identity): Promise<EvalSet>;
|
|
76
|
+
/** Reads an eval set and its versions. */
|
|
77
|
+
getEvalSet(id: string): Promise<EvalSet>;
|
|
78
|
+
/** Lists eval sets. */
|
|
79
|
+
listEvalSets(page?: RegistryPageOptions): Promise<Page<Omit<EvalSet, "versions">>>;
|
|
80
|
+
/** Creates a draft eval set version, optionally copying cases from another version. */
|
|
81
|
+
createEvalSetVersion(id: string, input?: {
|
|
82
|
+
fromVersionId?: string;
|
|
83
|
+
}): Promise<EvalSetVersion>;
|
|
84
|
+
/** Reads an eval set version. */
|
|
85
|
+
getEvalSetVersion(id: string): Promise<EvalSetVersion>;
|
|
86
|
+
/** Lists cases in an eval set version. */
|
|
87
|
+
listEvalSetCases(id: string, page?: PageOptions): Promise<Page<EvalSetCase>>;
|
|
88
|
+
/** Adds a case to a draft eval set version at its expected revision. */
|
|
89
|
+
addEvalSetCase(id: string, input: CaseWrite): Promise<{
|
|
90
|
+
/** The stored case. */
|
|
91
|
+
item: EvalSetCase;
|
|
92
|
+
/** The version with its new revision. */
|
|
93
|
+
version: EvalSetVersion;
|
|
94
|
+
}>;
|
|
95
|
+
/** Freezes a draft eval set version at its expected revision. */
|
|
96
|
+
freezeEvalSetVersion(id: string, expectedRevision: number): Promise<EvalSetVersion>;
|
|
97
|
+
/** Creates an evaluator identity. */
|
|
98
|
+
createEvaluator(input: Identity): Promise<Evaluator>;
|
|
99
|
+
/** Reads an evaluator and its published versions. */
|
|
100
|
+
getEvaluator(id: string): Promise<Evaluator>;
|
|
101
|
+
/** Lists evaluators. */
|
|
102
|
+
listEvaluators(page?: RegistryPageOptions): Promise<Page<Evaluator>>;
|
|
103
|
+
/** Publishes an immutable evaluator version. */
|
|
104
|
+
publishEvaluatorVersion(id: string, definition: ScorerDefinition): Promise<EvaluatorVersion>;
|
|
105
|
+
/** Reads a published evaluator version. */
|
|
106
|
+
getEvaluatorVersion(id: string): Promise<EvaluatorVersion>;
|
|
74
107
|
/** Creates an experiment over a frozen dataset version with pinned scorer versions and a configuration. */
|
|
75
108
|
createExperiment(input: {
|
|
76
109
|
idempotencyKey: string;
|
|
@@ -175,6 +208,70 @@ export declare class EvaluationClient {
|
|
|
175
208
|
listResults(id: string, page?: PageOptions): Promise<Page<ResultSummary>>;
|
|
176
209
|
/** Reads a full stored result. */
|
|
177
210
|
getResult(id: string): Promise<StoredResult>;
|
|
211
|
+
/** Creates a run using product request fields on the existing v1 path. */
|
|
212
|
+
createRun(input: {
|
|
213
|
+
idempotencyKey: string;
|
|
214
|
+
name: string;
|
|
215
|
+
evalSetVersionId: string;
|
|
216
|
+
evaluatorVersionIds: string[];
|
|
217
|
+
config: JsonValue;
|
|
218
|
+
}): Promise<{
|
|
219
|
+
/** Run ID. */
|
|
220
|
+
id: string;
|
|
221
|
+
/** ID of the run's scoring pass. */
|
|
222
|
+
scoringId: string;
|
|
223
|
+
/** Existing v1 field for the scoring pass ID. */
|
|
224
|
+
evaluationRunId: string;
|
|
225
|
+
}>;
|
|
226
|
+
/** Reads a run with its scoring progress. */
|
|
227
|
+
getRun(id: string): Promise<Run>;
|
|
228
|
+
/** Lists a run's cases with their latest executions. */
|
|
229
|
+
listRunItems(id: string, page?: PageOptions): Promise<Page<ExperimentItem>>;
|
|
230
|
+
/** Reads one frozen case of a run. */
|
|
231
|
+
getRunCase(id: string, caseId: string): Promise<RunCase>;
|
|
232
|
+
/** Starts or replays a target execution for a run case. */
|
|
233
|
+
startRunExecution(id: string, caseId: string, input: StartExecution): Promise<Execution>;
|
|
234
|
+
/** Reads one run execution. */
|
|
235
|
+
getRunExecution(id: string): Promise<Execution>;
|
|
236
|
+
/** Saves a run execution's outcome. */
|
|
237
|
+
completeRunExecution(id: string, input: CompleteExecution): Promise<Completion>;
|
|
238
|
+
/** Marks a run finished. */
|
|
239
|
+
finishRun(id: string, idempotencyKey: string): Promise<{
|
|
240
|
+
/** Run ID. */
|
|
241
|
+
id: string;
|
|
242
|
+
/** When the run finished. */
|
|
243
|
+
finishedAt: string;
|
|
244
|
+
}>;
|
|
245
|
+
/** Creates a standalone scoring pass over saved subjects. */
|
|
246
|
+
createScoring(input: {
|
|
247
|
+
idempotencyKey: string;
|
|
248
|
+
name: string;
|
|
249
|
+
subjectIds: string[];
|
|
250
|
+
evaluatorVersionIds: string[];
|
|
251
|
+
}): Promise<{
|
|
252
|
+
/** Scoring pass ID. */
|
|
253
|
+
id: string;
|
|
254
|
+
}>;
|
|
255
|
+
/** Reads a scoring pass and its pinned evaluators. */
|
|
256
|
+
getScoring(id: string): Promise<Scoring>;
|
|
257
|
+
/** Lists scoring passes in the project. */
|
|
258
|
+
listScorings(page?: PageOptions): Promise<Page<ScoringSummary>>;
|
|
259
|
+
/** Lists the subjects of a scoring pass. */
|
|
260
|
+
listScoringItems(id: string, page?: PageOptions): Promise<Page<EvaluationItem>>;
|
|
261
|
+
/** Reads a saved subject with product-named source fields. */
|
|
262
|
+
getScoringSubject(id: string): Promise<ScoringSubject>;
|
|
263
|
+
/** Uploads evaluator results for a scoring pass using product request fields. */
|
|
264
|
+
submitScoringResults(id: string, input: {
|
|
265
|
+
idempotencyKey: string;
|
|
266
|
+
results: ScoringResultInput[];
|
|
267
|
+
}): Promise<{
|
|
268
|
+
/** Stored result IDs, in input order. */
|
|
269
|
+
ids: string[];
|
|
270
|
+
}>;
|
|
271
|
+
/** Lists result summaries for a scoring pass. */
|
|
272
|
+
listScoringResults(id: string, page?: PageOptions): Promise<Page<ScoringResultSummary>>;
|
|
273
|
+
/** Reads a stored evaluator result. */
|
|
274
|
+
getScoringResult(id: string): Promise<StoredScoringResult>;
|
|
178
275
|
/** Dispatches hosted judge jobs for `llm_judge` pins; check {@link getJudgeBudget} first. */
|
|
179
276
|
createJudgeJobs(id: string, input: {
|
|
180
277
|
idempotencyKey: string;
|
|
@@ -230,7 +327,7 @@ export declare class EvaluationClient {
|
|
|
230
327
|
/** Acknowledged terminal queue state. */
|
|
231
328
|
state: "completed" | "attention";
|
|
232
329
|
}>;
|
|
233
|
-
/** Lists Scenarios (draft and published) of the project; requires a
|
|
330
|
+
/** Lists Scenarios (draft and published) of the project; requires a Read and write key. */
|
|
234
331
|
listCaseConversions(page?: PageOptions): Promise<Page<CaseConversionSummary>>;
|
|
235
332
|
/** Reads one Scenario with its immutable publication pins. */
|
|
236
333
|
getCaseConversion(id: string): Promise<CaseConversion>;
|
package/dist/evals/client.js
CHANGED
|
@@ -12,6 +12,85 @@ export class HueApiError extends Error {
|
|
|
12
12
|
this.name = "HueApiError";
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
+
const registryFieldAliases = [
|
|
16
|
+
["datasetId", "evalSetId"],
|
|
17
|
+
["datasetVersionId", "evalSetVersionId"],
|
|
18
|
+
["scorerId", "evaluatorId"],
|
|
19
|
+
["scorerVersionId", "evaluatorVersionId"],
|
|
20
|
+
];
|
|
21
|
+
const registryEnvelopes = new Set(["items", "item", "versions", "version"]);
|
|
22
|
+
// Only Hue response envelopes are traversed. Case inputs, metadata, and evaluator
|
|
23
|
+
// definitions are customer JSON and must retain their original field names.
|
|
24
|
+
function productRegistryFields(value) {
|
|
25
|
+
if (Array.isArray(value))
|
|
26
|
+
return value.map((item) => productRegistryFields(item));
|
|
27
|
+
if (value === null || typeof value !== "object")
|
|
28
|
+
return value;
|
|
29
|
+
const result = { ...value };
|
|
30
|
+
for (const key of registryEnvelopes) {
|
|
31
|
+
if (Object.hasOwn(result, key))
|
|
32
|
+
result[key] = productRegistryFields(result[key]);
|
|
33
|
+
}
|
|
34
|
+
for (const [legacy, product] of registryFieldAliases) {
|
|
35
|
+
if (Object.hasOwn(result, legacy)) {
|
|
36
|
+
if (Object.hasOwn(result, product) && result[legacy] !== result[product])
|
|
37
|
+
throw new HueApiError();
|
|
38
|
+
result[product] = result[legacy];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
const runResponseAliases = [
|
|
44
|
+
["datasetId", "evalSetId"],
|
|
45
|
+
["datasetName", "evalSetName"],
|
|
46
|
+
["datasetDisplayName", "evalSetDisplayName"],
|
|
47
|
+
["datasetVersion", "evalSetVersion"],
|
|
48
|
+
["datasetVersionId", "evalSetVersionId"],
|
|
49
|
+
["datasetVersionIds", "evalSetVersionIds"],
|
|
50
|
+
["scorerId", "evaluatorId"],
|
|
51
|
+
["scorerName", "evaluatorName"],
|
|
52
|
+
["scorerVersion", "evaluatorVersion"],
|
|
53
|
+
["scorerVersionId", "evaluatorVersionId"],
|
|
54
|
+
["scorerVersionIds", "evaluatorVersionIds"],
|
|
55
|
+
["scorerVersions", "evaluatorVersions"],
|
|
56
|
+
["evaluationRunId", "scoringId"],
|
|
57
|
+
];
|
|
58
|
+
const runResponseEnvelopes = new Set([
|
|
59
|
+
"items",
|
|
60
|
+
"item",
|
|
61
|
+
"versions",
|
|
62
|
+
"version",
|
|
63
|
+
"scorerVersions",
|
|
64
|
+
"evaluatorVersions",
|
|
65
|
+
]);
|
|
66
|
+
function productRunFields(value, kind) {
|
|
67
|
+
if (Array.isArray(value))
|
|
68
|
+
return value.map((item) => productRunFields(item, kind));
|
|
69
|
+
if (value === null || typeof value !== "object")
|
|
70
|
+
return value;
|
|
71
|
+
const result = { ...value };
|
|
72
|
+
for (const key of runResponseEnvelopes) {
|
|
73
|
+
if (Object.hasOwn(result, key))
|
|
74
|
+
result[key] = productRunFields(result[key], kind);
|
|
75
|
+
}
|
|
76
|
+
for (const key of ["evaluation", "scoring"]) {
|
|
77
|
+
if (Object.hasOwn(result, key))
|
|
78
|
+
result[key] = productRunFields(result[key], "scoring");
|
|
79
|
+
}
|
|
80
|
+
const identityAlias = kind === "result" ? ["runId", "scoringId"] : ["experimentId", "runId"];
|
|
81
|
+
const aliases = [...runResponseAliases, identityAlias];
|
|
82
|
+
for (const [legacy, product] of aliases) {
|
|
83
|
+
if (Object.hasOwn(result, legacy)) {
|
|
84
|
+
if (Object.hasOwn(result, product) &&
|
|
85
|
+
JSON.stringify(result[legacy]) !== JSON.stringify(result[product]))
|
|
86
|
+
throw new HueApiError();
|
|
87
|
+
result[product] = result[legacy];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (Object.hasOwn(result, "evaluation") && !Object.hasOwn(result, "scoring"))
|
|
91
|
+
result.scoring = result.evaluation;
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
15
94
|
/**
|
|
16
95
|
* Typed client for Hue's evaluation REST API: datasets, scorers, experiments, executions, runs,
|
|
17
96
|
* results and hosted judge jobs. No implicit mutation retry: callers retain stable idempotency keys
|
|
@@ -225,6 +304,58 @@ export class EvaluationClient {
|
|
|
225
304
|
getScorerVersion(id) {
|
|
226
305
|
return this.request("GET", `/scorer-versions/${uuid(id)}`);
|
|
227
306
|
}
|
|
307
|
+
/** Creates an eval set using the existing v1 registry path. */
|
|
308
|
+
async createEvalSet(input) {
|
|
309
|
+
return productRegistryFields(await this.createDataset(input));
|
|
310
|
+
}
|
|
311
|
+
/** Reads an eval set and its versions. */
|
|
312
|
+
async getEvalSet(id) {
|
|
313
|
+
return productRegistryFields(await this.getDataset(id));
|
|
314
|
+
}
|
|
315
|
+
/** Lists eval sets. */
|
|
316
|
+
async listEvalSets(page) {
|
|
317
|
+
return productRegistryFields(await this.listDatasets(page));
|
|
318
|
+
}
|
|
319
|
+
/** Creates a draft eval set version, optionally copying cases from another version. */
|
|
320
|
+
async createEvalSetVersion(id, input = {}) {
|
|
321
|
+
return productRegistryFields(await this.createDatasetVersion(id, input));
|
|
322
|
+
}
|
|
323
|
+
/** Reads an eval set version. */
|
|
324
|
+
async getEvalSetVersion(id) {
|
|
325
|
+
return productRegistryFields(await this.getDatasetVersion(id));
|
|
326
|
+
}
|
|
327
|
+
/** Lists cases in an eval set version. */
|
|
328
|
+
async listEvalSetCases(id, page) {
|
|
329
|
+
return productRegistryFields(await this.listCases(id, page));
|
|
330
|
+
}
|
|
331
|
+
/** Adds a case to a draft eval set version at its expected revision. */
|
|
332
|
+
async addEvalSetCase(id, input) {
|
|
333
|
+
return productRegistryFields(await this.addCase(id, input));
|
|
334
|
+
}
|
|
335
|
+
/** Freezes a draft eval set version at its expected revision. */
|
|
336
|
+
async freezeEvalSetVersion(id, expectedRevision) {
|
|
337
|
+
return productRegistryFields(await this.freezeDatasetVersion(id, expectedRevision));
|
|
338
|
+
}
|
|
339
|
+
/** Creates an evaluator identity. */
|
|
340
|
+
async createEvaluator(input) {
|
|
341
|
+
return productRegistryFields(await this.createScorer(input));
|
|
342
|
+
}
|
|
343
|
+
/** Reads an evaluator and its published versions. */
|
|
344
|
+
async getEvaluator(id) {
|
|
345
|
+
return productRegistryFields(await this.getScorer(id));
|
|
346
|
+
}
|
|
347
|
+
/** Lists evaluators. */
|
|
348
|
+
async listEvaluators(page) {
|
|
349
|
+
return productRegistryFields(await this.listScorers(page));
|
|
350
|
+
}
|
|
351
|
+
/** Publishes an immutable evaluator version. */
|
|
352
|
+
async publishEvaluatorVersion(id, definition) {
|
|
353
|
+
return productRegistryFields(await this.publishScorerVersion(id, definition));
|
|
354
|
+
}
|
|
355
|
+
/** Reads a published evaluator version. */
|
|
356
|
+
async getEvaluatorVersion(id) {
|
|
357
|
+
return productRegistryFields(await this.getScorerVersion(id));
|
|
358
|
+
}
|
|
228
359
|
/** Creates an experiment over a frozen dataset version with pinned scorer versions and a configuration. */
|
|
229
360
|
createExperiment(input) {
|
|
230
361
|
return this.request("POST", "/experiments", input);
|
|
@@ -376,6 +507,70 @@ export class EvaluationClient {
|
|
|
376
507
|
getResult(id) {
|
|
377
508
|
return this.request("GET", `/evaluation-results/${uuid(id)}`);
|
|
378
509
|
}
|
|
510
|
+
/** Creates a run using product request fields on the existing v1 path. */
|
|
511
|
+
async createRun(input) {
|
|
512
|
+
return productRunFields(await this.request("POST", "/experiments", input), "run");
|
|
513
|
+
}
|
|
514
|
+
/** Reads a run with its scoring progress. */
|
|
515
|
+
async getRun(id) {
|
|
516
|
+
return productRunFields(await this.getExperiment(id), "run");
|
|
517
|
+
}
|
|
518
|
+
/** Lists a run's cases with their latest executions. */
|
|
519
|
+
async listRunItems(id, page) {
|
|
520
|
+
return productRunFields(await this.listExperimentItems(id, page), "run");
|
|
521
|
+
}
|
|
522
|
+
/** Reads one frozen case of a run. */
|
|
523
|
+
async getRunCase(id, caseId) {
|
|
524
|
+
return productRunFields(await this.getExperimentCase(id, caseId), "run");
|
|
525
|
+
}
|
|
526
|
+
/** Starts or replays a target execution for a run case. */
|
|
527
|
+
async startRunExecution(id, caseId, input) {
|
|
528
|
+
return productRunFields(await this.startExecution(id, caseId, input), "run");
|
|
529
|
+
}
|
|
530
|
+
/** Reads one run execution. */
|
|
531
|
+
async getRunExecution(id) {
|
|
532
|
+
return productRunFields(await this.getExecution(id), "run");
|
|
533
|
+
}
|
|
534
|
+
/** Saves a run execution's outcome. */
|
|
535
|
+
async completeRunExecution(id, input) {
|
|
536
|
+
return productRunFields(await this.completeExecution(id, input), "run");
|
|
537
|
+
}
|
|
538
|
+
/** Marks a run finished. */
|
|
539
|
+
async finishRun(id, idempotencyKey) {
|
|
540
|
+
return productRunFields(await this.finishExperiment(id, idempotencyKey), "run");
|
|
541
|
+
}
|
|
542
|
+
/** Creates a standalone scoring pass over saved subjects. */
|
|
543
|
+
async createScoring(input) {
|
|
544
|
+
return productRunFields(await this.request("POST", "/evaluation-runs", input), "scoring");
|
|
545
|
+
}
|
|
546
|
+
/** Reads a scoring pass and its pinned evaluators. */
|
|
547
|
+
async getScoring(id) {
|
|
548
|
+
return productRunFields(await this.getEvaluationRun(id), "scoring");
|
|
549
|
+
}
|
|
550
|
+
/** Lists scoring passes in the project. */
|
|
551
|
+
async listScorings(page) {
|
|
552
|
+
return productRunFields(await this.listEvaluationRuns(page), "scoring");
|
|
553
|
+
}
|
|
554
|
+
/** Lists the subjects of a scoring pass. */
|
|
555
|
+
async listScoringItems(id, page) {
|
|
556
|
+
return productRunFields(await this.listEvaluationItems(id, page), "scoring");
|
|
557
|
+
}
|
|
558
|
+
/** Reads a saved subject with product-named source fields. */
|
|
559
|
+
async getScoringSubject(id) {
|
|
560
|
+
return productRunFields(await this.getSubject(id), "run");
|
|
561
|
+
}
|
|
562
|
+
/** Uploads evaluator results for a scoring pass using product request fields. */
|
|
563
|
+
async submitScoringResults(id, input) {
|
|
564
|
+
return productRunFields(await this.request("POST", `/evaluation-runs/${uuid(id)}/results`, input), "result");
|
|
565
|
+
}
|
|
566
|
+
/** Lists result summaries for a scoring pass. */
|
|
567
|
+
async listScoringResults(id, page) {
|
|
568
|
+
return productRunFields(await this.listResults(id, page), "result");
|
|
569
|
+
}
|
|
570
|
+
/** Reads a stored evaluator result. */
|
|
571
|
+
async getScoringResult(id) {
|
|
572
|
+
return productRunFields(await this.getResult(id), "result");
|
|
573
|
+
}
|
|
379
574
|
/** Dispatches hosted judge jobs for `llm_judge` pins; check {@link getJudgeBudget} first. */
|
|
380
575
|
createJudgeJobs(id, input) {
|
|
381
576
|
return this.request("POST", `/evaluation-runs/${uuid(id)}/judge-jobs`, input);
|
|
@@ -412,7 +607,7 @@ export class EvaluationClient {
|
|
|
412
607
|
completeLocalAgentRun(input) {
|
|
413
608
|
return this.request("POST", "/local-agent-worker/runs/complete", input);
|
|
414
609
|
}
|
|
415
|
-
/** Lists Scenarios (draft and published) of the project; requires a
|
|
610
|
+
/** Lists Scenarios (draft and published) of the project; requires a Read and write key. */
|
|
416
611
|
listCaseConversions(page) {
|
|
417
612
|
return this.request("GET", `/case-conversions${this.page(page)}`);
|
|
418
613
|
}
|
|
@@ -21,9 +21,9 @@ export interface ScenarioPins {
|
|
|
21
21
|
}
|
|
22
22
|
/** Subset of {@link EvaluationClient} used to resolve Scenario pins. */
|
|
23
23
|
export type ScenarioClient = Pick<EvaluationClient, "listCaseConversions" | "getCaseConversion" | "getDataset" | "getDatasetVersion" | "listDatasets">;
|
|
24
|
-
/** Lists Scenarios of the project; requires a
|
|
24
|
+
/** Lists Scenarios of the project; requires a Read and write key. */
|
|
25
25
|
export declare function listScenarios(client: Pick<EvaluationClient, "listCaseConversions">, page?: PageOptions): Promise<Page<CaseConversionSummary>>;
|
|
26
|
-
/** Reads one Scenario with its publication pins; requires a
|
|
26
|
+
/** Reads one Scenario with its publication pins; requires a Read and write key. */
|
|
27
27
|
export declare function getScenario(client: Pick<EvaluationClient, "getCaseConversion">, id: string): Promise<CaseConversion>;
|
|
28
28
|
/** How a selector was interpreted: a UUID, a Hue URL or a display name. */
|
|
29
29
|
export type ScenarioSelector = {
|
package/dist/evals/scenarios.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2
2
|
/** Scenarios listed while resolving a name; bounds the registry reads of one selection. */
|
|
3
3
|
const MAX_LISTED = 200;
|
|
4
|
-
/** Lists Scenarios of the project; requires a
|
|
4
|
+
/** Lists Scenarios of the project; requires a Read and write key. */
|
|
5
5
|
export function listScenarios(client, page) {
|
|
6
6
|
return client.listCaseConversions(page);
|
|
7
7
|
}
|
|
8
|
-
/** Reads one Scenario with its publication pins; requires a
|
|
8
|
+
/** Reads one Scenario with its publication pins; requires a Read and write key. */
|
|
9
9
|
export function getScenario(client, id) {
|
|
10
10
|
return client.getCaseConversion(id);
|
|
11
11
|
}
|
package/dist/evals/types.d.ts
CHANGED
|
@@ -76,6 +76,21 @@ export interface DatasetCase {
|
|
|
76
76
|
/** Immutable input-file manifest identity, when files are attached. */
|
|
77
77
|
artifactManifestId?: string | null;
|
|
78
78
|
}
|
|
79
|
+
/** An eval set and its versions. Both field names remain available during v1 compatibility. */
|
|
80
|
+
export type EvalSet = Omit<Dataset, "versions"> & {
|
|
81
|
+
/** Versions of this eval set. */
|
|
82
|
+
versions: EvalSetVersion[];
|
|
83
|
+
};
|
|
84
|
+
/** A version of an eval set, with its product field name. */
|
|
85
|
+
export type EvalSetVersion = DatasetVersion & {
|
|
86
|
+
/** Owning eval set ID. */
|
|
87
|
+
evalSetId: string;
|
|
88
|
+
};
|
|
89
|
+
/** A stored case in an eval set version. */
|
|
90
|
+
export type EvalSetCase = DatasetCase & {
|
|
91
|
+
/** Eval set version containing this case. */
|
|
92
|
+
evalSetVersionId: string;
|
|
93
|
+
};
|
|
79
94
|
/** One pinned input file of a case or subject, as recorded in Hue's immutable manifest. */
|
|
80
95
|
export interface CaseFile {
|
|
81
96
|
/** Hue artifact identity of the pinned bytes. */
|
|
@@ -346,6 +361,16 @@ export interface ScorerVersion {
|
|
|
346
361
|
/** The pinned definition. */
|
|
347
362
|
definition: ScorerDefinition;
|
|
348
363
|
}
|
|
364
|
+
/** An evaluator and its published versions. */
|
|
365
|
+
export type Evaluator = Omit<Scorer, "versions"> & {
|
|
366
|
+
/** Published versions, when included in the response. */
|
|
367
|
+
versions?: EvaluatorVersion[];
|
|
368
|
+
};
|
|
369
|
+
/** An immutable published evaluator definition. */
|
|
370
|
+
export type EvaluatorVersion = ScorerVersion & {
|
|
371
|
+
/** Owning evaluator ID when the server supplies it; older v1 responses may omit it. */
|
|
372
|
+
evaluatorId?: string;
|
|
373
|
+
};
|
|
349
374
|
/** Final state of a target execution. */
|
|
350
375
|
export type TerminalState = "succeeded" | "error" | "cancelled";
|
|
351
376
|
/** One attempt to run the target for a case. */
|
|
@@ -439,6 +464,30 @@ export interface Experiment {
|
|
|
439
464
|
cancelled: number;
|
|
440
465
|
};
|
|
441
466
|
}
|
|
467
|
+
/** A run over a frozen eval set version and configuration. */
|
|
468
|
+
export type Run = Experiment & {
|
|
469
|
+
/** Frozen eval set version under test. */
|
|
470
|
+
evalSetVersionId: string;
|
|
471
|
+
/** Scoring pass created for this run. */
|
|
472
|
+
scoring: Scoring;
|
|
473
|
+
};
|
|
474
|
+
/** One frozen case in a run. */
|
|
475
|
+
export type RunCase = ExperimentCase & {
|
|
476
|
+
/** Source eval set version ID. */
|
|
477
|
+
evalSetVersionId: string;
|
|
478
|
+
};
|
|
479
|
+
/** A scoring pass over saved subjects with pinned evaluators. */
|
|
480
|
+
export type Scoring = EvaluationRun & {
|
|
481
|
+
/** Evaluator versions pinned to this scoring pass. */
|
|
482
|
+
evaluatorVersions: EvaluatorVersion[];
|
|
483
|
+
/** Linked run ID, or null for standalone scoring. */
|
|
484
|
+
runId?: string | null;
|
|
485
|
+
};
|
|
486
|
+
/** One row in the project's scoring list. */
|
|
487
|
+
export type ScoringSummary = EvaluationRunSummary & {
|
|
488
|
+
/** Linked run ID, or null for standalone scoring. */
|
|
489
|
+
runId: string | null;
|
|
490
|
+
};
|
|
442
491
|
/** A sanitized error type with an optional bounded message. */
|
|
443
492
|
export interface TypedError {
|
|
444
493
|
/** Stable error type. */
|
|
@@ -549,6 +598,13 @@ export interface Subject {
|
|
|
549
598
|
/** The target's declared primary generated artifact, or `null`. */
|
|
550
599
|
primaryArtifactId?: string | null;
|
|
551
600
|
}
|
|
601
|
+
/** An immutable saved subject with product-named source fields. */
|
|
602
|
+
export type ScoringSubject = Subject & {
|
|
603
|
+
/** Source eval set version ID. */
|
|
604
|
+
evalSetVersionId: string;
|
|
605
|
+
/** Source run ID. */
|
|
606
|
+
runId: string;
|
|
607
|
+
};
|
|
552
608
|
/** A reported metric value. */
|
|
553
609
|
export interface Metric {
|
|
554
610
|
/** Declared metric name. */
|
|
@@ -588,6 +644,27 @@ export type Result = Score & {
|
|
|
588
644
|
/** Source digest of the local scorer, for `local_code` pins. */
|
|
589
645
|
sourceDigest?: string;
|
|
590
646
|
};
|
|
647
|
+
/** A score uploaded through the product-named scoring method. */
|
|
648
|
+
export type ScoringResultInput = Score & {
|
|
649
|
+
/** Scoring item this result belongs to. */
|
|
650
|
+
evaluationItemId: string;
|
|
651
|
+
/** Evaluator version that produced the score. */
|
|
652
|
+
evaluatorVersionId: string;
|
|
653
|
+
/** Source digest for a local code evaluator. */
|
|
654
|
+
sourceDigest?: string;
|
|
655
|
+
};
|
|
656
|
+
/** A result as listed by {@link EvaluationClient.listScoringResults}. */
|
|
657
|
+
export type ScoringResultSummary = ResultSummary & {
|
|
658
|
+
/** Evaluator version that produced this result. */
|
|
659
|
+
evaluatorVersionId: string;
|
|
660
|
+
};
|
|
661
|
+
/** A full stored result from {@link EvaluationClient.getScoringResult}. */
|
|
662
|
+
export type StoredScoringResult = StoredResult & {
|
|
663
|
+
/** Scoring pass containing this result. */
|
|
664
|
+
scoringId: string;
|
|
665
|
+
/** Evaluator version that produced this result. */
|
|
666
|
+
evaluatorVersionId: string;
|
|
667
|
+
};
|
|
591
668
|
/** What a local scorer callback receives. */
|
|
592
669
|
export interface ScoreContext {
|
|
593
670
|
/** Case inputs. */
|
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.
|
|
2
|
+
export declare const sdkVersion = "0.6.0";
|
package/dist/version.js
CHANGED