@hue-run/sdk 0.13.2 → 0.15.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 +26 -19
- package/EVALUATIONS.md +1 -1
- package/README.md +117 -24
- package/dist/blobs.d.ts +164 -0
- package/dist/blobs.js +703 -0
- package/dist/cli/eval.js +9 -5
- package/dist/client.d.ts +15 -5
- package/dist/client.js +63 -13
- package/dist/config.d.ts +20 -0
- package/dist/config.js +31 -4
- package/dist/environment/client.d.ts +1 -1
- package/dist/environment/client.js +3 -3
- package/dist/evals/client.d.ts +1 -1
- package/dist/evals/client.js +4 -4
- package/dist/evals/verdicts.d.ts +10 -7
- package/dist/evals/verdicts.js +52 -16
- package/dist/inline-files.d.ts +44 -1
- package/dist/inline-files.js +65 -6
- package/dist/limits.d.ts +32 -0
- package/dist/limits.js +54 -0
- package/dist/privacy.d.ts +27 -5
- package/dist/privacy.js +140 -39
- package/dist/provider-tools.js +1 -1
- package/dist/safety.d.ts +2 -0
- package/dist/safety.js +10 -4
- package/dist/snapshot.d.ts +30 -8
- package/dist/snapshot.js +280 -49
- package/dist/tool-definitions.d.ts +4 -0
- package/dist/tool-definitions.js +11 -0
- package/dist/transport.d.ts +26 -0
- package/dist/transport.js +579 -177
- package/dist/types.d.ts +14 -5
- 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.15.0`, `@opentelemetry/api@1.9.1` and
|
|
46
46
|
`@opentelemetry/context-async-hooks@2.11.0`; Python setup uses its separately tested package pin.
|
|
47
47
|
Content capture requires an ordinary account-managed key and a later explicit application decision.
|
|
48
48
|
|
|
@@ -469,7 +469,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
|
|
|
469
469
|
# Set project to an existing supported fixture; use the same directory on resume.
|
|
470
470
|
project=/absolute/path/to/supported-fixture
|
|
471
471
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
472
|
-
--archive .artifacts/typescript/hue-run-sdk-0.
|
|
472
|
+
--archive .artifacts/typescript/hue-run-sdk-0.15.0.tgz \
|
|
473
473
|
--origin https://STAGING_ORIGIN \
|
|
474
474
|
--project "$project" --command setup \
|
|
475
475
|
--evidence .context/setup-staging-before-claim.json
|
|
@@ -480,7 +480,7 @@ the private local handoff and finish the real browser claim, then reconcile the
|
|
|
480
480
|
|
|
481
481
|
```sh
|
|
482
482
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
483
|
-
--archive .artifacts/typescript/hue-run-sdk-0.
|
|
483
|
+
--archive .artifacts/typescript/hue-run-sdk-0.15.0.tgz \
|
|
484
484
|
--origin https://STAGING_ORIGIN \
|
|
485
485
|
--project "$project" --command claim \
|
|
486
486
|
--evidence .context/setup-staging-after-claim.json
|
|
@@ -523,10 +523,10 @@ production agent lacks. Pin the SDK and bring its `zod` peer along, so the agent
|
|
|
523
523
|
gains no dependency:
|
|
524
524
|
|
|
525
525
|
```sh
|
|
526
|
-
npx --yes --package @hue-run/sdk@0.
|
|
527
|
-
npx --yes --package @hue-run/sdk@0.
|
|
528
|
-
npx --yes --package @hue-run/sdk@0.
|
|
529
|
-
npx --yes --package @hue-run/sdk@0.
|
|
526
|
+
npx --yes --package @hue-run/sdk@0.15.0 --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.15.0 --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.15.0 --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.15.0 --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
530
|
```
|
|
531
531
|
|
|
532
532
|
Without the second `--package`, `hue eval` exits with `hue eval needs zod`, even for `--help`;
|
|
@@ -562,7 +562,7 @@ export default function runMyAgent(inputs: JsonValue, context: SimulationTargetC
|
|
|
562
562
|
```
|
|
563
563
|
|
|
564
564
|
```sh
|
|
565
|
-
npx --yes --package @hue-run/sdk@0.
|
|
565
|
+
npx --yes --package @hue-run/sdk@0.15.0 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" ./hue-agent.ts --content --env-file .env.hue
|
|
566
566
|
```
|
|
567
567
|
|
|
568
568
|
`--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
|
|
@@ -581,11 +581,15 @@ Failing cases print the scorer explanation. An evaluator Hue records as not appl
|
|
|
581
581
|
that case's columns and neither passes nor fails it, so in a mixed eval set a case is decided by
|
|
582
582
|
the evaluators that apply to it; the pass line counts the not-applicable results. A case that no
|
|
583
583
|
pinned evaluator applies to is an error that names what they need. A result of a pinned Hue judge
|
|
584
|
-
(an evaluator of kind `world_judge`)
|
|
584
|
+
(an evaluator of kind `world_judge`) whose immutable version does not set `config.required: true`
|
|
585
|
+
and that Hue marks advisory is
|
|
585
586
|
shown in its column, marked `(advisory)`, but never decides a case or the exit code; the pass line
|
|
586
587
|
counts advisory failures as not counted, and a case only advisory results ran for is an error.
|
|
587
|
-
|
|
588
|
-
|
|
588
|
+
Applicable outcome evaluators and judge versions pinned with `config.required: true` need a
|
|
589
|
+
decision before a case can pass. A required result skipped without a decision is inconclusive;
|
|
590
|
+
missing results remain pending and evaluator errors remain errors. A recorded failure takes
|
|
591
|
+
precedence over an unavailable required evaluator. The Answer outcome evaluator uses its
|
|
592
|
+
required judge to decide answer-only cases. Any
|
|
589
593
|
other evaluator's result, an error, or a metric that carries `passed` is never advisory, whatever
|
|
590
594
|
its evidence says. A metric name that
|
|
591
595
|
several evaluators report, such as two judges' `verdict`, gets a column per evaluator, labelled
|
|
@@ -667,21 +671,21 @@ A Scenario or eval set whose dataset version is not saved cannot back an experim
|
|
|
667
671
|
exits 1 and asks for **Save eval-set version** in Hue or `--save-version`, which freezes that
|
|
668
672
|
version at its current revision. Connection settings are `HUE_API_KEY` and `HUE_BASE_URL`
|
|
669
673
|
(default `https://app.hue.run`), loaded from `--env-file <path>` (or its alias `--env-path`) first when given; `--origin`
|
|
670
|
-
overrides the origin.
|
|
671
|
-
|
|
674
|
+
overrides the origin. Since TypeScript 0.14.0, telemetry content capture defaults on; `--no-content` opts out.
|
|
675
|
+
Earlier releases require `--content`; the examples include it for compatibility. Model and tool spans inside the agent come
|
|
672
676
|
only from the agent's own instrumentation. Case outputs, error messages and explanations are
|
|
673
|
-
stored in Hue
|
|
677
|
+
stored in Hue independently of the telemetry capture setting, so a case's answer can be graded and read on
|
|
674
678
|
its run page. A `--command`'s stdout is its answer unless it writes a result file, and is stored,
|
|
675
679
|
so it must not print credentials or debug logs there; before storing, `hue eval` replaces the
|
|
676
680
|
credentials it handed the case (the world token and MCP headers, a legacy MCP token, attempt
|
|
677
681
|
bearers) and every Hue control-plane credential in its environment with `[redacted]`, in the
|
|
678
682
|
answer, an adapter's thrown message and name, generated file names and the local checkpoint.
|
|
679
683
|
`--no-output` keeps outputs, error messages and explanations out of a one-shot run's stored
|
|
680
|
-
results;
|
|
684
|
+
results; captured telemetry still carries the output. Pass `--no-output --no-content` to omit both. `--worker` always stores them, because a run launched from Hue is read
|
|
681
685
|
on its run page (that is `runLocalAgent()`'s contract), and refuses `--no-output`. An interrupted
|
|
682
686
|
one-shot run keeps the choice it started with, so one run never mixes stored and unstored outputs:
|
|
683
|
-
rerunning it with
|
|
684
|
-
|
|
687
|
+
rerunning it with another output or telemetry capture policy is refused with the flags that resume it.
|
|
688
|
+
To resume a metadata-only trace from an earlier release, add `--no-content` and keep its original `--no-output` choice.
|
|
685
689
|
|
|
686
690
|
Files the agent writes to `output/` are uploaded as generated documents with or without
|
|
687
691
|
`--no-output`, and they are not redacted the way the answer is: `hue eval` replaces the same
|
|
@@ -696,8 +700,11 @@ case is completed as failed (error `TelemetryNotAccepted`, evidence omitted as
|
|
|
696
700
|
`telemetry_not_accepted`, no output or generated files attached) instead of being left started,
|
|
697
701
|
and the run goes on. Each export of the CLI's own telemetry may take 30 seconds, retries
|
|
698
702
|
included: a slow acknowledgement is waited for, and a 429, 502, 503, 504 or network error is
|
|
699
|
-
retried within that time, honouring `Retry-After
|
|
700
|
-
the case
|
|
703
|
+
retried within that time, honouring `Retry-After`. A 429 whose `Retry-After` is longer than that
|
|
704
|
+
holds the case's records and sends them again once it has passed, for at most 60 seconds of holds
|
|
705
|
+
per export, so such a case can take that much longer; a longer wait fails the case as
|
|
706
|
+
`telemetry_not_accepted`. Any other response, or rejected records, fails the case at once. Stderr
|
|
707
|
+
names the case as it completes, with the export
|
|
701
708
|
issue counts, for example
|
|
702
709
|
`[refund] telemetry not accepted, case failed: telemetry_not_accepted: traces failed 1 (HTTP 400)`;
|
|
703
710
|
the case counts as an error in the table and JSON whatever its scores, and the command exits 1.
|
package/EVALUATIONS.md
CHANGED
|
@@ -303,7 +303,7 @@ and require a Hue deployment that serves case `inputFiles` on experiment
|
|
|
303
303
|
items, subject `files`, and the artifact reservation, upload, completion and download APIs. The
|
|
304
304
|
Python SDK has no equivalent.
|
|
305
305
|
|
|
306
|
-
|
|
306
|
+
Since TypeScript 0.13.2, the `hue eval --worker --command "…"` wrapper supplies both callbacks, so one
|
|
307
307
|
command worker accepts direct and world cases, including mixed eval sets. It keeps the launch
|
|
308
308
|
directory for relative application entry points; private case inputs/output still use
|
|
309
309
|
`HUE_CASE_*`. An adapter-file worker opts into the direct callback with `--capability direct:v1`
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ import { createHue, HueExportError } from "@hue-run/sdk";
|
|
|
35
35
|
const hue = createHue({
|
|
36
36
|
apiKey: process.env.HUE_API_KEY!, // a project service key, on the server only
|
|
37
37
|
serviceName: "my-agent",
|
|
38
|
-
captureContent: true, //
|
|
38
|
+
captureContent: true, // Also works with older releases; false sends metadata only
|
|
39
39
|
onExportIssue: (issue) => console.error(issue), // sanitized counts, never server bodies
|
|
40
40
|
});
|
|
41
41
|
|
|
@@ -57,7 +57,7 @@ try {
|
|
|
57
57
|
await hue.shutdown(); // flushes and releases providers owned by this client
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
`captureContent`
|
|
60
|
+
Starting with TypeScript 0.14.0, `captureContent` defaults to `true`. Earlier releases require an explicit choice. The example sets `true`: trace inspection,
|
|
61
61
|
evaluations and judges read the prompts, responses and tool inputs/outputs it records. Choose
|
|
62
62
|
`false` for metadata only when a policy forbids sending that content; see
|
|
63
63
|
[Privacy and content](#privacy-and-content).
|
|
@@ -324,11 +324,21 @@ there is no SDK retention timer or automatic content expiry. To redact strings
|
|
|
324
324
|
before export, supply `redact(value, path)`; it applies to supported strings in
|
|
325
325
|
attributes, resources, event/link attributes and log bodies. Return a string.
|
|
326
326
|
Invalid helper content is omitted with an instrumentation failure; the span can still be delivered.
|
|
327
|
-
A content value over Hue's
|
|
327
|
+
A content value over Hue's 1 MiB value cap (or the value limit Hue's receiver advertises) is not
|
|
328
|
+
omitted. A span's own string attribute over the cap is uploaded to Hue whole and replaced by its
|
|
329
|
+
first 16 KiB (see [Values over the inline limit](#values-over-the-inline-limit)); `redact` sees
|
|
330
|
+
the whole value first, and its answer is what is uploaded. Any other value over the cap, and one
|
|
331
|
+
that could not be uploaded, is cut: text to a UTF-8 prefix, and
|
|
328
332
|
bytes or a structured log body are replaced by the receiver's own marker
|
|
329
333
|
(`{ "hue.truncated": true, "hue.truncated_bytes": <size> }`), and the value's key is listed in
|
|
330
334
|
the record's `hue.truncated` attribute, where Hue's receiver lists the values it cuts itself,
|
|
331
335
|
so the span still carries the call, the recorded part of its value and the rest of its evidence.
|
|
336
|
+
`redact` sees text past the cap, so it can recognize a secret that crosses it. A string longer
|
|
337
|
+
than the cap and 64 Ki code units that is not held whole for upload is cut to that length when
|
|
338
|
+
its record is queued, so a value of millions of characters cannot drop its record for the queue
|
|
339
|
+
budget; for such a text the last 64 Ki code units of the redactor's answer are never exported,
|
|
340
|
+
and tool definitions and recorded requests have their credentials removed from the whole value
|
|
341
|
+
before that cut.
|
|
332
342
|
Export-time redactor failures reject the affected record and are reported by flush. Shared resources are redacted once per
|
|
333
343
|
export batch. Do not put user content or secrets in span names or scope names.
|
|
334
344
|
|
|
@@ -361,18 +371,24 @@ rotates. Only definitions another integration recorded can be summarized: `hueTe
|
|
|
361
371
|
inputs and exports through Hue's attached processors gets the summary.
|
|
362
372
|
Recorded messages can inline files: GenAI `blob` parts in `gen_ai.input.messages` /
|
|
363
373
|
`gen_ai.output.messages` (what the AI SDK 7 adapter records for a file part) and AI SDK 6 `file`
|
|
364
|
-
parts in `ai.prompt.messages`. A
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
374
|
+
parts in `ai.prompt.messages`. A file is measured by its own bytes whatever its media type (base64
|
|
375
|
+
content and `;base64` `data:` URLs decoded, other `data:` URLs percent-decoded, anything else as
|
|
376
|
+
UTF-8 text). When a span's own message attribute inlines a file larger than 64 KiB, export uploads
|
|
377
|
+
the file's bytes to Hue under the part's media type (`mime_type` or `mediaType`, else its `data:`
|
|
378
|
+
URL's) and keeps the first 16 KiB of the part's `content`/`data` in place, listing the file in the
|
|
379
|
+
span's `hue.blobs` as `<attribute>#<JSON pointer>`, for example
|
|
380
|
+
`gen_ai.input.messages#/0/parts/1/content` (see
|
|
381
|
+
[Values over the inline limit](#values-over-the-inline-limit)). A file that is not uploaded, and a
|
|
382
|
+
large inline file in a log record or a span event, keeps the earlier behavior: the part's
|
|
383
|
+
`content`/`data` is replaced by the file's `sha256` and `size`, keeping its other fields such as
|
|
384
|
+
`type`, `mime_type` and `mediaType`, when the record is queued (or, for a span's own messages, at
|
|
385
|
+
export). Smaller inline files are exported as recorded. The digest matches `hue.recordFile`'s
|
|
386
|
+
`hue.file.sha256` for the same bytes, so a file can be recognized wherever it appears. It is bounded:
|
|
387
|
+
a message attribute longer than 8 MiB of text is not inspected for files, and is uploaded or cut
|
|
388
|
+
like any value over the cap. Base64 grows a file by a third, so that ceiling is an inline file of
|
|
389
|
+
about 6 MiB. The same message text is inspected once per record, and a record inspects at most
|
|
390
|
+
16 MiB of message text in all. With `captureContent: false` the messages, which export removes,
|
|
391
|
+
are neither inspected nor charged.
|
|
376
392
|
|
|
377
393
|
Manual helpers encode JSON values without converting null into absence. Unknown
|
|
378
394
|
outputs and usage remain absent. This SDK does not estimate tokens or cost. A thrown
|
|
@@ -476,15 +492,92 @@ const hue = createHue({
|
|
|
476
492
|
## Delivery behavior
|
|
477
493
|
|
|
478
494
|
Exports retry temporary HTTP/network failures (429, 502, 503, 504 and connection errors,
|
|
479
|
-
honoring `Retry-After`) within the export timeout, by OpenTelemetry's OTLP/HTTP exporter rules.
|
|
480
|
-
|
|
481
|
-
|
|
495
|
+
honoring `Retry-After`) within the export timeout, by OpenTelemetry's OTLP/HTTP exporter rules. A
|
|
496
|
+
429 whose `Retry-After` outlasts the export timeout keeps its records queued, within the queue's
|
|
497
|
+
bounds, and sends them again once the wait has passed; an export holds its records this way for at
|
|
498
|
+
most 60 seconds in all, and a longer wait loses them, reported as a `failed` issue with status 429.
|
|
499
|
+
`flush()` and `shutdown()` wait for a held export, and a held export keeps the process running
|
|
500
|
+
until it is sent. When `shutdownSafe()`'s budget runs out first, a client that owns its providers
|
|
501
|
+
ends the hold: its records are reported lost to the rate limit and the process can exit. In attach
|
|
502
|
+
mode the application's providers drain with their own timeout (OpenTelemetry's default is 30
|
|
503
|
+
seconds), so a flush during a longer hold can report a provider flush failure while the held
|
|
504
|
+
export continues.
|
|
505
|
+
|
|
506
|
+
Requests are measured as Hue's receiver measures them: after gzip against its request limit and
|
|
507
|
+
after decompression against its decoded limit. Until a response advertises its own limits
|
|
508
|
+
(`Hue-Max-Request-Bytes`, `Hue-Max-Decoded-Bytes` and `Hue-Max-Value-Bytes`, on every
|
|
509
|
+
acknowledgement and refusal) these are 1 MiB on the wire, 4 MiB decoded and 1 MiB per value;
|
|
510
|
+
advertised limits are adopted, clamped to 1–64 MiB, 1–64 MiB and 256 KiB–16 MiB, and never
|
|
511
|
+
exceeded: a request that limits lowered while it was retried or held no longer fit is split and
|
|
512
|
+
shed again before it is sent. Batches are packed to 4 MiB before gzip (or the decoded limit, when lower) and split at
|
|
513
|
+
record boundaries; a batch that compresses worse than the limit allows is sent in halves. A record
|
|
514
|
+
over a limit on its own sheds its content values,
|
|
482
515
|
largest first, each replaced by the receiver's marker and listed under `hue.truncated`, until it
|
|
483
516
|
fits; only a record too large without any content value is lost, reported as an `invalid` issue,
|
|
484
517
|
and counted on its trace's root span as `hue.sdk.dropped_records` when the root is exported, so
|
|
485
|
-
Hue reads the trace as incomplete by that many records
|
|
518
|
+
Hue reads the trace as incomplete by that many records; so are records lost to a rate limit
|
|
519
|
+
longer than the hold. Each signal queues at most 2,048 records, including
|
|
486
520
|
exports in flight; overflow is reported through the callback, counters and next
|
|
487
|
-
flush. This is an in-memory queue, not durable storage.
|
|
521
|
+
flush. This is an in-memory queue, not durable storage. A client's own providers keep 2,000
|
|
522
|
+
attributes, events and links per span and 2,000 attributes per log record (OpenTelemetry's default
|
|
523
|
+
is 128); providers an application attaches Hue to keep their own limits. A span whose events or
|
|
524
|
+
links do not all fit the queue's byte budget keeps its newest ones and counts the rest in its
|
|
525
|
+
dropped event and link counts, rather than being dropped whole.
|
|
526
|
+
|
|
527
|
+
### Values over the inline limit
|
|
528
|
+
|
|
529
|
+
Hue keeps a value inline up to its value limit (1 MiB unless the receiver advertises another in
|
|
530
|
+
`Hue-Max-Value-Bytes`). A span's own string attribute larger than that (helper content such as
|
|
531
|
+
`input.value`, `output.value` and tool results, or another instrumentation's attribute), and an
|
|
532
|
+
inline file over 64 KiB in a span's own recorded messages, is uploaded to Hue apart from the span,
|
|
533
|
+
up to Hue's upload limit of 1,000,000,000 bytes:
|
|
534
|
+
|
|
535
|
+
1. Export computes the SHA-256 of the value's bytes (a string as UTF-8, a file as its own bytes)
|
|
536
|
+
and reserves it with `POST /api/v1/otlp/blobs`, authenticated with the project key. Hue answers
|
|
537
|
+
that it already stores the value under the trace, and nothing is uploaded, or with a presigned
|
|
538
|
+
URL.
|
|
539
|
+
2. The bytes go straight to the project's evidence store in one `PUT` to that URL, with exactly the
|
|
540
|
+
headers Hue signed and never the project key (over HTTPS, or plain HTTP to a loopback store or
|
|
541
|
+
with `allowInsecureHttp`), and
|
|
542
|
+
the upload is confirmed with `POST /api/v1/otlp/blobs/complete`. A PUT the store answers with
|
|
543
|
+
412 (the same value uploaded at the same time by another span) counts as stored.
|
|
544
|
+
3. The span keeps the value's first 16 KiB in the attribute it replaced and lists the value in its
|
|
545
|
+
`hue.blobs` attribute, one compact JSON object per value:
|
|
546
|
+
`{"key":"output.value","sha256":"<64 hex>","size":5242880,"content_type":"application/json"}`.
|
|
547
|
+
A whole value is listed under `hue.truncated` as well, since its inline text is a prefix.
|
|
548
|
+
|
|
549
|
+
Hue stores the value under the trace, so the trace's retention, deletion and erasure cover it.
|
|
550
|
+
Uploads run in the export, never on the application's thread: at most four at a time, each request
|
|
551
|
+
within the export timeout, and all of one export's uploads within six export timeouts; a
|
|
552
|
+
transient failure is retried once. A rate-limited reservation (429) waits out its `Retry-After`
|
|
553
|
+
within the export's 60-second hold. `flush()` and `shutdown()` wait for uploads like any export
|
|
554
|
+
work, and a `shutdownSafe()` whose budget runs out ends the uploads in flight.
|
|
555
|
+
|
|
556
|
+
A value that cannot be uploaded (Hue or the store refused it or failed, the network or a deadline
|
|
557
|
+
did, the receiver has no upload route, or the value is over 1 GB) is exported as before uploads
|
|
558
|
+
existed: cut to the limit and listed under `hue.truncated`, an inline file as its digest. Each
|
|
559
|
+
export reports such values in a `warning` issue per cause, whose `count` is the values and
|
|
560
|
+
`traceIds` their traces, and counts them in `report.uploadFallbacks`; uploaded values are counted
|
|
561
|
+
in `report.uploadedValues`. A warning never makes `flush()` throw. After a transient failure, or
|
|
562
|
+
a refusal that is not about the value alone, uploads pause for 30 seconds, and a receiver without
|
|
563
|
+
the upload route (a 404 that is not Hue's, a 405 or 501, or a 200 that is no reservation) is not
|
|
564
|
+
asked again for 10 minutes, so an unavailable store or another OTLP receiver meets no retry storm.
|
|
565
|
+
|
|
566
|
+
To upload a value whole, the queue holds it until export apart from `maxQueueBytes`, within a
|
|
567
|
+
128 MiB budget of held values (a string charged two bytes a code unit); a recorded message holding
|
|
568
|
+
an inline file over 64 KiB is held the same way. `maxQueueBytes` is still charged what the value
|
|
569
|
+
would cost cut (a message, its copy with the files as their digest), so one that cannot be
|
|
570
|
+
uploaded is exported exactly as it was before. A value the budget cannot hold, or one queued while
|
|
571
|
+
the receiver is known to lack the upload route, is cut (its files digested) when it is queued, as
|
|
572
|
+
before, and reported as not uploaded, once per file; a single value larger than the whole budget
|
|
573
|
+
is held while nothing else is. While values can be uploaded, helpers encode content whole, up to the
|
|
574
|
+
upload limit and 1,048,576 values, rather than cutting it to the value limit as they record it.
|
|
575
|
+
|
|
576
|
+
Nothing is uploaded with `captureContent: false` or a setup credential (`hue_setup_…`), nor from
|
|
577
|
+
log records (Hue does not read a log record's uploaded values yet), span events, links, resources
|
|
578
|
+
or in-progress placeholders: those values are cut as before. `redact` and the credential scrub of
|
|
579
|
+
tool definitions and recorded requests run on the whole value before it is hashed, so a value they
|
|
580
|
+
rewrite is uploaded as rewritten.
|
|
488
581
|
|
|
489
582
|
`flush()` waits for the current trace and log export work. A partial rejection,
|
|
490
583
|
invalid acknowledgement, queue drop or failure throws `HueExportError`; its
|
|
@@ -649,11 +742,11 @@ before), wraps `runSimulation()` and `runLocalAgent()` for an adapter file or a
|
|
|
649
742
|
`hue eval --case "<name>" ./hue-agent.ts --content` creates a fresh run from a published case's
|
|
650
743
|
immutable pins, runs the agent with an isolated world when the case pins one, waits
|
|
651
744
|
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.
|
|
745
|
+
`--worker` registers the same adapter for runs launched from Hue. Since TypeScript 0.13.2, a
|
|
653
746
|
command worker handles both world and answer-only cases; adapter workers opt into direct cases
|
|
654
747
|
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
|
|
656
|
-
|
|
748
|
+
It needs a Read and write key in `HUE_API_KEY` (never printed). Content capture defaults on; `--no-content` opts out. The example passes `--content` explicitly
|
|
749
|
+
for compatibility with older releases. Case outputs, error messages and
|
|
657
750
|
explanations are stored in Hue either way (`--no-output` keeps them out of a one-shot run's
|
|
658
751
|
results); a command's stdout is its stored answer, with the credentials `hue eval` handed the case
|
|
659
752
|
replaced by `[redacted]`.
|
|
@@ -702,6 +795,6 @@ existing-provider flush callbacks and recovery. Local/CI runners remain availabl
|
|
|
702
795
|
|
|
703
796
|
Use `createHueSafe(options)` for best-effort startup. Invalid initialization returns a disabled client that keeps your `onExportIssue` hook and records the reason as an instrumentation failure. Pass `enabled: false` to disable Hue without a key or a `captureContent` choice; disabled helpers still execute the application callback, and `inject()` keeps propagating the application's own trace context. `flushSafe({ timeoutMillis: 1000 })` and `shutdownSafe({ timeoutMillis: 1000 })` return `{ ok, timedOut, report }` without rejecting. Strict initialization, connection checks and `flush()` remain available for diagnostics; do not gate application readiness or responses on them.
|
|
704
797
|
|
|
705
|
-
Capture/serialization/redaction/provider failures omit unsafe telemetry, record failures, and preserve the original business result/error. Async diagnostic rejections are contained; diagnostics are rate-limited. The default `maxQueueBytes` is 8 MiB across traces/logs including in-flight work, alongside the existing record cap. `pendingBytes` is a current queue gauge; `droppedSpans`, `droppedLogs` and `instrumentationFailures` are cumulative failure counters. This is a telemetry budget, not a total process memory ceiling. A timeout bounds the caller and does not cancel a borrowed provider. Never retry the business operation to recover telemetry. See [production safety](https://docs.hue.run/guides/production-safety).
|
|
798
|
+
Capture/serialization/redaction/provider failures omit unsafe telemetry, record failures, and preserve the original business result/error. Async diagnostic rejections are contained; diagnostics are rate-limited. The default `maxQueueBytes` is 8 MiB across traces/logs including in-flight work, alongside the existing record cap. `pendingBytes` is a current queue gauge; `droppedSpans`, `droppedLogs` and `instrumentationFailures` are cumulative failure counters; `uploadedValues` and `uploadFallbacks` count the values over the inline limit uploaded apart from their spans and exported cut instead. This is a telemetry budget, not a total process memory ceiling. A timeout bounds the caller and does not cancel a borrowed provider. Never retry the business operation to recover telemetry. See [production safety](https://docs.hue.run/guides/production-safety).
|
|
706
799
|
|
|
707
800
|
Queued records snapshot supported telemetry values when a span ends or a log is emitted; later caller mutations cannot change queued data. Resource attributes still awaiting detection are omitted with a sanitized warning. Later records include them after detection finishes; await resource detection before instrumentation when those attributes are required.
|
package/dist/blobs.d.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Values larger than Hue's inline limit travel apart from their span: the SDK uploads the value's
|
|
3
|
+
* bytes straight to the project's evidence store through a presigned PUT that Hue answers at
|
|
4
|
+
* `POST /api/v1/otlp/blobs`, keeps the value's first 16 KiB in the attribute it replaced, and
|
|
5
|
+
* lists the value in the span attribute `hue.blobs`. A value that cannot be uploaded is exported
|
|
6
|
+
* as it was before uploads existed: cut to the inline limit and listed under `hue.truncated` (an
|
|
7
|
+
* inline file as its digest), reported as a warning and counted.
|
|
8
|
+
*/
|
|
9
|
+
/** The span attribute listing the values the SDK uploaded apart from the span: one compact JSON
|
|
10
|
+
* object per value, `{"key", "sha256", "size", "content_type"}`, as Hue reads it. */
|
|
11
|
+
export declare const BLOBS_KEY = "hue.blobs";
|
|
12
|
+
/** The largest value one upload may hold (`Hue-Max-Blob-Bytes`): a larger value is cut inline. */
|
|
13
|
+
export declare const MAX_BLOB_BYTES = 1000000000;
|
|
14
|
+
/** What an uploaded value keeps inline: its first 16 KiB, the receiver's search excerpt. */
|
|
15
|
+
export declare const BLOB_PREFIX_BYTES: number;
|
|
16
|
+
/** The entries one record's `hue.blobs` may list; Hue reads no more. */
|
|
17
|
+
export declare const MAX_BLOB_ENTRIES = 64;
|
|
18
|
+
/**
|
|
19
|
+
* The values over the inline limit that queued spans may hold whole for upload at once, charged
|
|
20
|
+
* apart from `maxQueueBytes` (a string as two bytes a code unit, bytes as themselves). A value
|
|
21
|
+
* the budget cannot hold is cut when it is queued, as before uploads existed; a single value
|
|
22
|
+
* larger than the whole budget is held while nothing else is.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MAX_HELD_BLOB_BYTES: number;
|
|
25
|
+
/** How long one export may spend uploading its values, on top of its requests. */
|
|
26
|
+
export declare function uploadBudgetMillis(timeoutMillis: number): number;
|
|
27
|
+
/** Why a value over the inline limit was exported cut instead of uploaded. */
|
|
28
|
+
export type FallbackReason = "unsupported" | "failed" | "tooLarge" | "budget";
|
|
29
|
+
/** The fixed description of each fallback, as an export issue reports it. */
|
|
30
|
+
export declare const FALLBACK_MESSAGES: Record<FallbackReason, string>;
|
|
31
|
+
/** A span's own attribute value the redaction left whole for the upload step to place. */
|
|
32
|
+
export interface OffloadCandidate {
|
|
33
|
+
key: string;
|
|
34
|
+
value: string | Uint8Array;
|
|
35
|
+
}
|
|
36
|
+
/** What redaction leaves to the upload step: the candidates, and the keys of the values over the
|
|
37
|
+
* inline limit the queue cut when the span was admitted (they cannot be uploaded whole). */
|
|
38
|
+
export interface OffloadCollector {
|
|
39
|
+
candidates: OffloadCandidate[];
|
|
40
|
+
cut: string[];
|
|
41
|
+
}
|
|
42
|
+
/** The outcome of one export's uploads, reported once the export's records are placed. */
|
|
43
|
+
export interface OffloadTally {
|
|
44
|
+
uploaded: number;
|
|
45
|
+
fallbacks: Map<FallbackReason, {
|
|
46
|
+
count: number;
|
|
47
|
+
traceIds: Set<string>;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
export declare function newTally(): OffloadTally;
|
|
51
|
+
export declare function isBlobContentType(value: unknown): value is string;
|
|
52
|
+
/** The media type an uploaded text is stored under: JSON when it reads as a JSON object or array
|
|
53
|
+
* (a structured value's text), plain UTF-8 text otherwise. Both are previewed as text. */
|
|
54
|
+
export declare function textContentType(text: string): string;
|
|
55
|
+
/** What an uploaded value keeps inline: its first 16 KiB (text cut back to a character), held
|
|
56
|
+
* as its own copy so the whole value is not kept alive by it. */
|
|
57
|
+
export declare function blobPrefix(value: string): string;
|
|
58
|
+
export declare function blobPrefix(value: Uint8Array): Uint8Array;
|
|
59
|
+
/** The SHA-256 (lowercase hex) and byte size of what an upload of `value` carries, yielding to
|
|
60
|
+
* the event loop between slices so a large value does not stall the application. */
|
|
61
|
+
export declare function digestOf(value: string | Uint8Array): Promise<{
|
|
62
|
+
sha256: string;
|
|
63
|
+
size: number;
|
|
64
|
+
}>;
|
|
65
|
+
/** What one export's uploads share: their deadline, and the export's hold for a receiver that
|
|
66
|
+
* limited the rate (`wait` resolves false when the hold may not take `millis` more). */
|
|
67
|
+
export interface UploadContext {
|
|
68
|
+
deadline: number;
|
|
69
|
+
wait(millis: number): Promise<boolean>;
|
|
70
|
+
}
|
|
71
|
+
type Outcome = {
|
|
72
|
+
ok: true;
|
|
73
|
+
contentType: string;
|
|
74
|
+
} | {
|
|
75
|
+
ok: false;
|
|
76
|
+
reason: FallbackReason;
|
|
77
|
+
};
|
|
78
|
+
/** The upload route's options, from the transport's validated options. */
|
|
79
|
+
export interface UploaderOptions {
|
|
80
|
+
baseUrl: string;
|
|
81
|
+
apiKey: string;
|
|
82
|
+
timeoutMillis: number;
|
|
83
|
+
allowInsecureHttp: boolean;
|
|
84
|
+
userAgent: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Uploads values to Hue: reserves each with `POST /api/v1/otlp/blobs`, PUTs its bytes to the
|
|
88
|
+
* presigned URL Hue answers (skipped when Hue already stores the value), and completes it. At
|
|
89
|
+
* most {@link UPLOAD_CONCURRENCY} uploads run at once; every request has a deadline and none
|
|
90
|
+
* follows a redirect. A receiver without the route (a 404 without Hue's JSON error, a 405 or
|
|
91
|
+
* 501, or a 200 that is no reservation) is remembered for {@link UNSUPPORTED_PAUSE_MILLIS}, and a
|
|
92
|
+
* transient failure or a refusal that is not about the value alone pauses uploads for
|
|
93
|
+
* {@link FAILURE_PAUSE_MILLIS}, so values fall back to the inline cut instead of retrying.
|
|
94
|
+
*/
|
|
95
|
+
export declare class BlobUploader {
|
|
96
|
+
private readonly options;
|
|
97
|
+
private unsupportedUntil;
|
|
98
|
+
private pausedUntil;
|
|
99
|
+
private closed;
|
|
100
|
+
private running;
|
|
101
|
+
private waiting;
|
|
102
|
+
private requests;
|
|
103
|
+
constructor(options: UploaderOptions);
|
|
104
|
+
/** Whether a value over the inline limit is worth holding whole for upload now. */
|
|
105
|
+
available(): boolean;
|
|
106
|
+
/** Whether the receiver lacks the upload route, as it last answered. */
|
|
107
|
+
unsupported(): boolean;
|
|
108
|
+
/** Ends uploads in flight and refuses new ones: their values fall back to the inline cut. */
|
|
109
|
+
close(): void;
|
|
110
|
+
/** Runs `work` once fewer than {@link UPLOAD_CONCURRENCY} uploads are in flight. */
|
|
111
|
+
limit<R>(work: () => Promise<R>): Promise<R>;
|
|
112
|
+
/** Why a value cannot be uploaded right now without trying, if it cannot. */
|
|
113
|
+
refusal(): FallbackReason | undefined;
|
|
114
|
+
private pause;
|
|
115
|
+
/**
|
|
116
|
+
* Stores `value` under `key` of the trace: reserved, uploaded unless Hue already holds it, and
|
|
117
|
+
* completed. The caller holds a {@link limit} slot.
|
|
118
|
+
*/
|
|
119
|
+
store(traceId: string, key: string, value: string | Uint8Array, contentType: string, digest: {
|
|
120
|
+
sha256: string;
|
|
121
|
+
size: number;
|
|
122
|
+
}, upload: UploadContext): Promise<Outcome>;
|
|
123
|
+
private jsonHeaders;
|
|
124
|
+
/** Reserves the value: Hue answers that it holds it already, or a presigned PUT. A rate limit
|
|
125
|
+
* is waited out within the export's hold, a transient refusal retried once. */
|
|
126
|
+
private reserve;
|
|
127
|
+
private delay;
|
|
128
|
+
/** A 200 answer read: `exists`, or an upload whose URL and headers are usable as given. */
|
|
129
|
+
private reservation;
|
|
130
|
+
/** The PUT of the value's bytes, with exactly the headers Hue signed. */
|
|
131
|
+
private put;
|
|
132
|
+
/**
|
|
133
|
+
* One HTTP request with a deadline and an idle timeout, never following a redirect, its
|
|
134
|
+
* response body bounded. Undefined for a network error, a timeout or a closed uploader. A text
|
|
135
|
+
* or byte body larger than one slice is written a slice at a time with backpressure; `length`
|
|
136
|
+
* is then its byte size.
|
|
137
|
+
*/
|
|
138
|
+
private request;
|
|
139
|
+
}
|
|
140
|
+
/** Where one export's values are uploaded, and what it learned of them. */
|
|
141
|
+
export interface OffloadOptions {
|
|
142
|
+
uploader: BlobUploader;
|
|
143
|
+
traceId: string;
|
|
144
|
+
/** The receiver's inline value limit. */
|
|
145
|
+
valueBytes: number;
|
|
146
|
+
upload: UploadContext;
|
|
147
|
+
tally: OffloadTally;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* A span's own attributes with each candidate placed: an inline file part over 64 KiB in a
|
|
151
|
+
* recorded message uploaded and replaced by its first 16 KiB (or by its digest, as before, when
|
|
152
|
+
* it could not be), and a value still over the inline limit uploaded and replaced by its first
|
|
153
|
+
* 16 KiB (or cut to the limit), each upload listed under `hue.blobs`, each whole value cut or
|
|
154
|
+
* uploaded listed under `hue.truncated`. Every fallback is counted in the tally.
|
|
155
|
+
*/
|
|
156
|
+
export declare function offloadAttributes(attributes: Record<string, unknown>, collector: OffloadCollector, options: OffloadOptions): Promise<Record<string, unknown>>;
|
|
157
|
+
/**
|
|
158
|
+
* The span's own attributes with every candidate placed as it was before uploads existed, for an
|
|
159
|
+
* upload step that failed unexpectedly: a message's large inline files as their digest, a value
|
|
160
|
+
* over the inline limit cut to it (bytes replaced by the receiver's marker) and listed under
|
|
161
|
+
* `hue.truncated`.
|
|
162
|
+
*/
|
|
163
|
+
export declare function fallbackAttributes(attributes: Record<string, unknown>, collector: OffloadCollector, valueBytes: number, tally: OffloadTally, traceId: string): Record<string, unknown>;
|
|
164
|
+
export {};
|