@hue-run/sdk 0.20.1 → 0.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLI.md +102 -10
- package/ENVIRONMENTS.md +1 -1
- package/README.md +30 -2
- package/dist/cli/traces-import-convert.d.ts +262 -0
- package/dist/cli/traces-import-convert.js +1779 -0
- package/dist/cli/traces-import-prepare.d.ts +64 -0
- package/dist/cli/traces-import-prepare.js +443 -0
- package/dist/cli/traces-import-upload.d.ts +129 -0
- package/dist/cli/traces-import-upload.js +838 -0
- package/dist/cli/traces-import-zip.d.ts +52 -0
- package/dist/cli/traces-import-zip.js +278 -0
- package/dist/cli/traces-import.d.ts +18 -0
- package/dist/cli/traces-import.js +231 -0
- package/dist/setup/cli.js +7 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/world/world.d.ts +3 -2
- package/dist/world/world.js +4 -2
- 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.20.
|
|
45
|
+
not a completed integration. TypeScript uses `@hue-run/sdk@0.20.2`, `@opentelemetry/api@1.9.1` and
|
|
46
46
|
`@opentelemetry/context-async-hooks@2.11.0`; Python setup uses its separately tested package pin.
|
|
47
47
|
Content capture requires an ordinary account-managed key and a later explicit application decision.
|
|
48
48
|
|
|
@@ -115,6 +115,8 @@ hue login # validate keys created in Hue and store them in ./.en
|
|
|
115
115
|
hue mcp install --client claude-code # write the Hue MCP configuration for a coding agent
|
|
116
116
|
hue listen --subscription <id> --forward-to http://localhost:3000/slack/events # deliver events to a local bot
|
|
117
117
|
hue import langfuse --from 2026-01-01 --dry-run # count a Langfuse project's traces, then import them
|
|
118
|
+
hue traces import prepare ./exports --out ./exports.hue-import --json # convert exports into a bundle
|
|
119
|
+
hue traces import upload ./exports.hue-import --authorization-file ./authorization.json --json # deliver it
|
|
118
120
|
```
|
|
119
121
|
|
|
120
122
|
Hosted setup accepts HTTPS origins only. HTTP is accepted solely for `localhost`, `127.0.0.1` and
|
|
@@ -379,8 +381,8 @@ and Hue never receives or stores them. The command is part of the `hue` executab
|
|
|
379
381
|
# LANGFUSE_PUBLIC_KEY=pk-lf-...
|
|
380
382
|
# LANGFUSE_SECRET_KEY=sk-lf-...
|
|
381
383
|
# LANGFUSE_BASE_URL=https://cloud.langfuse.com
|
|
382
|
-
npx -y @hue-run/sdk@0.20.
|
|
383
|
-
npx -y @hue-run/sdk@0.20.
|
|
384
|
+
npx -y @hue-run/sdk@0.20.2 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
|
|
385
|
+
npx -y @hue-run/sdk@0.20.2 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
|
|
384
386
|
```
|
|
385
387
|
|
|
386
388
|
`LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` come from the environment or `--env-path`
|
|
@@ -491,6 +493,96 @@ once, abandoning exports still in flight for the next run to send. Exit codes: `
|
|
|
491
493
|
trace was imported, `1` a trace, span or file was not (or a key was refused), `2` usage error, `130`
|
|
492
494
|
stopped.
|
|
493
495
|
|
|
496
|
+
## Import a bundle of existing traces
|
|
497
|
+
|
|
498
|
+
`hue traces import` brings historical traces into Hue from exports you already hold: OTLP/JSON
|
|
499
|
+
trace exports (a `{"resourceSpans": […]}` document, a list of them, or one per line), complete
|
|
500
|
+
Langfuse observation exports (the `data` or native `observations` envelope) and text ATIF v1.7 and
|
|
501
|
+
v1.8 exports, embedded subagents included. It needs no project key. `prepare` runs on the machine
|
|
502
|
+
that holds the exports and sends nothing; a Hue MCP connection then authorizes the delivery
|
|
503
|
+
(`prepare_trace_import`); `upload` delivers the bundle with that authorization, and
|
|
504
|
+
`get_trace_import` reports what arrived.
|
|
505
|
+
|
|
506
|
+
```sh
|
|
507
|
+
npx -y @hue-run/sdk@latest traces import prepare ./exports --out ./exports.hue-import --json
|
|
508
|
+
npx -y @hue-run/sdk@latest traces import upload ./exports.hue-import --authorization-file ./authorization.json --json
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Prepare.** `prepare <path>` reads a file, a directory tree or a ZIP archive (members are inflated
|
|
512
|
+
in memory; encrypted members, unsafe paths and nested archives are listed, not read) and
|
|
513
|
+
recognizes each file by its content, never by its name. Every trace becomes gzip OTLP/JSON batches
|
|
514
|
+
of at most 3 MiB decoded and 900 KiB on the wire, written to `--out` (default `<path>.hue-import`,
|
|
515
|
+
created with mode 0700) beside `manifest.json`, which declares each trace's id, span count, batches
|
|
516
|
+
(by the SHA-256 of each gzip body), large values, history owner and origin. A text value over 1 MiB
|
|
517
|
+
is stored as a value of its own and listed in `hue.blobs`, as the exporter lists the values it
|
|
518
|
+
uploads. OTLP spans are kept as recorded; a trace or span id that is not an OpenTelemetry id (an
|
|
519
|
+
exporter's UUID, for example) is mapped to one derived from it by SHA-256, with the original kept
|
|
520
|
+
as `hue.import.original_trace_id` or `hue.import.original_span_id`. A Langfuse observation becomes
|
|
521
|
+
one span keeping its whole record (`hue.import.source_record`) beside the `langfuse.*` and GenAI
|
|
522
|
+
attributes Hue reads; an id that is no OpenTelemetry id is mapped as `hue import langfuse` maps it,
|
|
523
|
+
so an export and a live import of the same trace agree. An ATIF export becomes one trace: an agent
|
|
524
|
+
span for the run, a model span per agent step with the conversation so far, a tool span per call,
|
|
525
|
+
each embedded subagent as its own agent span under the call that spawned it, and the export's
|
|
526
|
+
`extra.user_id` as the trace's `user.id`. ATIF records instants, not durations, so model and tool
|
|
527
|
+
spans are points in time, and a tool's result is kept as text with no error status of the
|
|
528
|
+
importer's making. The same trace in two files is one trace when the copies agree and a refusal
|
|
529
|
+
when they differ; a trace with more than 100,000 spans, 64 batches or 1,000 values is refused and
|
|
530
|
+
named. Nothing about where a file sat or what it was called reaches a span: a renamed, reordered
|
|
531
|
+
or re-zipped copy of the bundle prepares to byte-identical batches and the same manifest digest.
|
|
532
|
+
|
|
533
|
+
The report (`--json` prints it as one object) names `manifest_digest`, `totals` (traces, batches,
|
|
534
|
+
spans, blobs, bytes) and `source`, which `prepare_trace_import` takes as given; the formats
|
|
535
|
+
recognized and the traces each file held; the files it could not read, with the reason; the traces
|
|
536
|
+
and spans it refused; the traces without a history owner (a root `user.id`, a Langfuse `userId`, an
|
|
537
|
+
ATIF `extra.user_id`, or an explicit `hue.world.entity.namespace` and `hue.world.entity.id`), which
|
|
538
|
+
import like any other trace but leave a gap in continuous history; the origin each source recorded
|
|
539
|
+
(`production`, `evaluation`, `simulation` or `unknown`; evaluation and simulation traffic is
|
|
540
|
+
imported and kept out of production history); and the traces whose vendor ids were mapped. An
|
|
541
|
+
existing `--out` directory is accepted only while it holds nothing but an earlier preparation and
|
|
542
|
+
no upload has started in it.
|
|
543
|
+
|
|
544
|
+
**Authorize.** Call `prepare_trace_import` over the Hue MCP with the report's `manifest_digest`,
|
|
545
|
+
`totals` and `source`. It answers the import id, `upload_url` and an `authorization` (`hue_it_` and
|
|
546
|
+
43 characters) valid for an hour. Save its answer, or the authorization alone, in a private file
|
|
547
|
+
(mode 0600; never in a transcript, a message or a shell history). The same digest in the same
|
|
548
|
+
project is the same import: calling again renews the authorization for an interrupted upload and
|
|
549
|
+
revokes the earlier one.
|
|
550
|
+
|
|
551
|
+
**Upload.** `upload <directory> --authorization-file <file>` reads the authorization from the file
|
|
552
|
+
(its JSON answer, or the authorization on the first line and the `upload_url` on the second;
|
|
553
|
+
`--import-url` names the URL otherwise, and `--origin` refuses a URL on any other Hue origin),
|
|
554
|
+
never from the command line or the environment, and never prints it. It registers the manifest's
|
|
555
|
+
exact bytes, then, trace by trace in declared order and two requests at a time, reserves each large
|
|
556
|
+
value, sends it to the URL Hue answers and completes it, posts each batch with its SHA-256 in
|
|
557
|
+
`Hue-Import-Batch`, and reads the trace's receipt for every span id: a trace counts as verified only
|
|
558
|
+
when Hue confirms every span. It then reconciles and completes the import. Progress is written to
|
|
559
|
+
`progress.json` in the bundle as it goes; running the same command again resumes there, and what
|
|
560
|
+
Hue already holds is not sent twice (a batch sent again after a lost acknowledgement is taken as
|
|
561
|
+
duplicates). Connection failures, timeouts, `408`, `429` and `5xx` answers are retried after 1, 2,
|
|
562
|
+
4, 8, 16 and 30 seconds, waiting for `Retry-After`; after eight retries the command stops and the
|
|
563
|
+
next run resumes. Ctrl+C or `SIGTERM` finishes the requests in flight, saves progress and exits
|
|
564
|
+
`130`; a second signal stops at once.
|
|
565
|
+
|
|
566
|
+
**What Hue refuses.** Every refusal carries a fixed code, which the report names in `error.code`.
|
|
567
|
+
An expired or replaced authorization (`401`), a lapsed connection (`403` `authority_lapsed`) and an
|
|
568
|
+
expired import (`410`) stop the upload: call `prepare_trace_import` again with the same
|
|
569
|
+
`manifest_digest` and `totals`, write the new authorization to the file and run the same command.
|
|
570
|
+
A manifest whose digest or totals are not the import's (`409`), an authorization of another import
|
|
571
|
+
(`403`), an unknown import (`404`) and a canceled import (`410`) are final. A batch or value Hue
|
|
572
|
+
refuses for the trace's own sake (undeclared, another digest, another trace's records, fewer
|
|
573
|
+
finished spans than declared, a size or type the manifest did not declare), or a record Hue already
|
|
574
|
+
holds under the same id with other content, is a conflict: it is recorded, the trace is reported,
|
|
575
|
+
none of its later records are sent, and the same bytes are never sent again. An import Hue reports
|
|
576
|
+
already completed is done: the command exits `0` and points to `get_trace_import`.
|
|
577
|
+
|
|
578
|
+
**Output and exit codes.** Without `--json`, the command prints the registration, every hundredth
|
|
579
|
+
verified trace, each conflict and a summary. With `--json`, one progress object per line goes to
|
|
580
|
+
stderr and the report to stdout: the state (`completed`, `incomplete`, `stopped` or `failed`), the
|
|
581
|
+
traces verified and conflicted, the batches landed and values stored, Hue's own note and the traces
|
|
582
|
+
it still lists when completion was refused, the conflicts with their codes, and `next_action`. Exit
|
|
583
|
+
codes: `0` prepared or completed, `1` not completed (the report says what to do next), `2` usage
|
|
584
|
+
error, `130` stopped.
|
|
585
|
+
|
|
494
586
|
## Local state and conflicts
|
|
495
587
|
|
|
496
588
|
Before its first network write, setup creates a lowercase UUIDv4 and a 32-byte random installation
|
|
@@ -595,7 +687,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
|
|
|
595
687
|
# Set project to an existing supported fixture; use the same directory on resume.
|
|
596
688
|
project=/absolute/path/to/supported-fixture
|
|
597
689
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
598
|
-
--archive .artifacts/typescript/hue-run-sdk-0.20.
|
|
690
|
+
--archive .artifacts/typescript/hue-run-sdk-0.20.2.tgz \
|
|
599
691
|
--origin https://STAGING_ORIGIN \
|
|
600
692
|
--project "$project" --command setup \
|
|
601
693
|
--evidence .context/setup-staging-before-claim.json
|
|
@@ -606,7 +698,7 @@ the private local handoff and finish the real browser claim, then reconcile the
|
|
|
606
698
|
|
|
607
699
|
```sh
|
|
608
700
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
609
|
-
--archive .artifacts/typescript/hue-run-sdk-0.20.
|
|
701
|
+
--archive .artifacts/typescript/hue-run-sdk-0.20.2.tgz \
|
|
610
702
|
--origin https://STAGING_ORIGIN \
|
|
611
703
|
--project "$project" --command claim \
|
|
612
704
|
--evidence .context/setup-staging-after-claim.json
|
|
@@ -648,10 +740,10 @@ otherwise. Never hand the agent Hue-native tools (`context.tools`, `context.mcp`
|
|
|
648
740
|
production agent lacks. Pin the SDK, so the agent's repository gains no dependency:
|
|
649
741
|
|
|
650
742
|
```sh
|
|
651
|
-
npx -y @hue-run/sdk@0.20.
|
|
652
|
-
npx -y @hue-run/sdk@0.20.
|
|
653
|
-
npx -y @hue-run/sdk@0.20.
|
|
654
|
-
npx -y @hue-run/sdk@0.20.
|
|
743
|
+
npx -y @hue-run/sdk@0.20.2 eval --case "Refund an eligible charge" --command "node agent.js"
|
|
744
|
+
npx -y @hue-run/sdk@0.20.2 eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <run id>
|
|
745
|
+
npx -y @hue-run/sdk@0.20.2 eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
|
|
746
|
+
npx -y @hue-run/sdk@0.20.2 eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision>
|
|
655
747
|
```
|
|
656
748
|
|
|
657
749
|
One invocation creates one run holding every selected case; it prints the run URL
|
|
@@ -689,7 +781,7 @@ export default function runMyAgent(inputs: JsonValue, context: SimulationTargetC
|
|
|
689
781
|
```
|
|
690
782
|
|
|
691
783
|
```sh
|
|
692
|
-
npx -y @hue-run/sdk@0.20.
|
|
784
|
+
npx -y @hue-run/sdk@0.20.2 eval --case "Refund an eligible charge" ./hue-agent.ts --content
|
|
693
785
|
```
|
|
694
786
|
|
|
695
787
|
`--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
|
package/ENVIRONMENTS.md
CHANGED
|
@@ -332,7 +332,7 @@ A refusal such as 409 `simulation_gateway_required` exposes its validated server
|
|
|
332
332
|
|
|
333
333
|
`agentEnvironment` removes Hue control-plane credentials from the child by default: `HUE_API_KEY`,
|
|
334
334
|
`HUE_MCP_KEY`, and any value carrying a Hue control-plane credential (`hue_sk_`, `hue_setup_`,
|
|
335
|
-
`hue_attempt_`, `hue_mcp_`, `hue_inv_`, `hue_install_`, `hue_at_`, `hue_rt_`, `hue_oauth_`)
|
|
335
|
+
`hue_attempt_`, `hue_mcp_`, `hue_inv_`, `hue_install_`, `hue_at_`, `hue_rt_`, `hue_it_`, `hue_oauth_`)
|
|
336
336
|
anywhere in it, including wrapped, percent-encoded or JSON-Unicode-escaped values. World tokens and
|
|
337
337
|
`hue_sim_` capabilities pass. Pass `includeHueCredentials: true` only for an agent that must call
|
|
338
338
|
Hue's own API.
|
package/README.md
CHANGED
|
@@ -536,8 +536,8 @@ the Langfuse project's own keys (`LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, a
|
|
|
536
536
|
`HUE_API_KEY`:
|
|
537
537
|
|
|
538
538
|
```sh
|
|
539
|
-
npx -y @hue-run/sdk@0.20.
|
|
540
|
-
npx -y @hue-run/sdk@0.20.
|
|
539
|
+
npx -y @hue-run/sdk@0.20.2 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
|
|
540
|
+
npx -y @hue-run/sdk@0.20.2 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
|
|
541
541
|
```
|
|
542
542
|
|
|
543
543
|
- It reads Langfuse Cloud and self-hosted v4 through the Observations API v2, and self-hosted v3
|
|
@@ -558,6 +558,34 @@ The command has no Python counterpart; it runs with `npx` for an application in
|
|
|
558
558
|
[Import traces from Langfuse](CLI.md#import-traces-from-langfuse) for every option, the attribute
|
|
559
559
|
mapping, media handling, limits and exit codes.
|
|
560
560
|
|
|
561
|
+
## Import a bundle of existing traces
|
|
562
|
+
|
|
563
|
+
`hue traces import` brings exports you already hold into Hue in two steps, with no project key.
|
|
564
|
+
`prepare` converts the bundle on your machine and sends nothing; a Hue MCP connection authorizes
|
|
565
|
+
the delivery with `prepare_trace_import`; `upload` delivers it with that authorization. Sources
|
|
566
|
+
are files, directories or ZIP archives of OTLP/JSON trace exports, complete Langfuse observation
|
|
567
|
+
exports and text ATIF v1.7 and v1.8 exports, recognized by content:
|
|
568
|
+
|
|
569
|
+
```sh
|
|
570
|
+
npx -y @hue-run/sdk@latest traces import prepare ./exports --out ./exports.hue-import --json
|
|
571
|
+
# Call prepare_trace_import with the report's manifest_digest, totals and source, and save its
|
|
572
|
+
# answer (or the authorization alone) in a private file.
|
|
573
|
+
npx -y @hue-run/sdk@latest traces import upload ./exports.hue-import --authorization-file ./authorization.json --json
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
- `prepare` writes each trace as gzip OTLP/JSON batches beside a manifest and reports the
|
|
577
|
+
manifest's digest and totals, the formats recognized, the files it could not read, invalid vendor
|
|
578
|
+
ids it mapped to OpenTelemetry ids, and the traces without a history owner (a root `user.id` or an
|
|
579
|
+
explicit entity). Nothing about a file's name or place reaches a span.
|
|
580
|
+
- `upload` registers the manifest, sends each trace's large values and batches in order, two
|
|
581
|
+
requests at a time, checks every span's receipt, reconciles and completes the import. Interrupted,
|
|
582
|
+
run it again: it resumes from `progress.json` in the bundle. An expired authorization is renewed
|
|
583
|
+
by calling `prepare_trace_import` again with the same digest and totals.
|
|
584
|
+
- The authorization (`hue_it_…`) is read only from the file and never printed.
|
|
585
|
+
|
|
586
|
+
See [Import a bundle of existing traces](CLI.md#import-a-bundle-of-existing-traces) for the
|
|
587
|
+
formats, the report, what Hue refuses and the exit codes.
|
|
588
|
+
|
|
561
589
|
## Vercel AI SDK 6
|
|
562
590
|
|
|
563
591
|
AI SDK 6 accepts a per-call tracer through `experimental_telemetry`. Pass
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The conversions behind `hue traces import prepare`: a source file is recognized by its content
|
|
3
|
+
* (an OTLP/JSON trace export, a complete Langfuse observation export or a text ATIF export),
|
|
4
|
+
* each trace becomes OTLP/JSON entries, one span each, that keep the recorded ids, hierarchy,
|
|
5
|
+
* timestamps, content, errors and usage, and the entries are packed into deterministic gzip
|
|
6
|
+
* batches. Nothing here depends on where a file sat or what it was called: a renamed or repacked
|
|
7
|
+
* bundle converts to byte-identical batches, so the manifest's digests name the same import.
|
|
8
|
+
*/
|
|
9
|
+
export declare const traceImportFormats: readonly ["otlp_json", "langfuse_observations", "atif"];
|
|
10
|
+
export type TraceImportFormat = (typeof traceImportFormats)[number];
|
|
11
|
+
export declare const traceImportOrigins: readonly ["production", "evaluation", "simulation", "unknown"];
|
|
12
|
+
export type TraceImportOrigin = (typeof traceImportOrigins)[number];
|
|
13
|
+
/** The bounds the import routes hold a manifest to. */
|
|
14
|
+
export declare const traceImportLimits: {
|
|
15
|
+
readonly traces: 10000;
|
|
16
|
+
readonly batchesPerTrace: 64;
|
|
17
|
+
readonly blobsPerTrace: 1000;
|
|
18
|
+
readonly spansPerTrace: 100000;
|
|
19
|
+
readonly manifestBytes: number;
|
|
20
|
+
readonly batchBytes: number;
|
|
21
|
+
readonly blobBytes: 1000000000;
|
|
22
|
+
};
|
|
23
|
+
/** One batch, decoded, and on the wire: well inside the receiver's request bounds. */
|
|
24
|
+
export declare const MAX_BATCH_DECODED_BYTES: number;
|
|
25
|
+
export declare const MAX_BATCH_WIRE_BYTES: number;
|
|
26
|
+
/** The largest value a record keeps inline; a longer text is stored apart from its span. */
|
|
27
|
+
export declare const MAX_INLINE_VALUE_BYTES: number;
|
|
28
|
+
export declare const SOURCE_RECORD = "hue.import.source_record";
|
|
29
|
+
export declare const SOURCE_ENVELOPE = "hue.import.source_envelope";
|
|
30
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
31
|
+
[key: string]: JsonValue;
|
|
32
|
+
};
|
|
33
|
+
export interface OtlpValue {
|
|
34
|
+
stringValue?: string;
|
|
35
|
+
boolValue?: boolean;
|
|
36
|
+
intValue?: string;
|
|
37
|
+
doubleValue?: number;
|
|
38
|
+
arrayValue?: {
|
|
39
|
+
values: OtlpValue[];
|
|
40
|
+
};
|
|
41
|
+
kvlistValue?: {
|
|
42
|
+
values: OtlpAttribute[];
|
|
43
|
+
};
|
|
44
|
+
bytesValue?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface OtlpAttribute {
|
|
47
|
+
key: string;
|
|
48
|
+
value: OtlpValue;
|
|
49
|
+
}
|
|
50
|
+
export type ImportSpan = {
|
|
51
|
+
traceId: string;
|
|
52
|
+
spanId: string;
|
|
53
|
+
parentSpanId?: string;
|
|
54
|
+
name: string;
|
|
55
|
+
kind: number;
|
|
56
|
+
startTimeUnixNano: string;
|
|
57
|
+
endTimeUnixNano: string;
|
|
58
|
+
attributes: OtlpAttribute[];
|
|
59
|
+
status: {
|
|
60
|
+
code: number;
|
|
61
|
+
message?: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
/** One resource group holding one scope group holding one span: the unit the packer serializes.
|
|
65
|
+
* A converted span is an {@link ImportSpan}; an OTLP/JSON span is kept as recorded. */
|
|
66
|
+
export interface ImportEntry {
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
scopeSpans: {
|
|
69
|
+
[key: string]: unknown;
|
|
70
|
+
spans: Record<string, unknown>[];
|
|
71
|
+
}[];
|
|
72
|
+
}
|
|
73
|
+
export interface TraceIdentity {
|
|
74
|
+
user?: string;
|
|
75
|
+
entity?: {
|
|
76
|
+
namespace: string;
|
|
77
|
+
id: string;
|
|
78
|
+
};
|
|
79
|
+
session?: string;
|
|
80
|
+
}
|
|
81
|
+
export interface PreparedBlob {
|
|
82
|
+
sha256: string;
|
|
83
|
+
byteSize: number;
|
|
84
|
+
contentType: string;
|
|
85
|
+
bytes: Uint8Array;
|
|
86
|
+
}
|
|
87
|
+
export interface ConvertedTrace {
|
|
88
|
+
traceId: string;
|
|
89
|
+
format: TraceImportFormat;
|
|
90
|
+
entries: ImportEntry[];
|
|
91
|
+
blobs: PreparedBlob[];
|
|
92
|
+
identity?: TraceIdentity;
|
|
93
|
+
origin: TraceImportOrigin;
|
|
94
|
+
/** The SHA-256 of every source file that held part of the trace. */
|
|
95
|
+
sourceDigests: Set<string>;
|
|
96
|
+
remappedTraceId: boolean;
|
|
97
|
+
remappedSpans: number;
|
|
98
|
+
}
|
|
99
|
+
/** A trace, span or file that was not converted, and why. */
|
|
100
|
+
export interface Refusal {
|
|
101
|
+
traceId?: string;
|
|
102
|
+
reason: string;
|
|
103
|
+
}
|
|
104
|
+
export declare class ConversionError extends Error {
|
|
105
|
+
}
|
|
106
|
+
export declare const sha256Hex: (value: string | Uint8Array) => string;
|
|
107
|
+
/**
|
|
108
|
+
* JSON text parsed with every integer too large for a JavaScript number kept as its decimal
|
|
109
|
+
* text, so an OTLP timestamp written as a number keeps its nanoseconds. `unsafeIntegers` says
|
|
110
|
+
* whether any was found; the Langfuse and ATIF readers refuse such a file, as Hue's do.
|
|
111
|
+
*/
|
|
112
|
+
export declare function parseJsonText(text: string): {
|
|
113
|
+
value: unknown;
|
|
114
|
+
unsafeIntegers: boolean;
|
|
115
|
+
};
|
|
116
|
+
/** Refuses a source value that JavaScript or OTLP cannot retain exactly; never omits it. */
|
|
117
|
+
export declare function validateSource(value: JsonValue, depth?: number): void;
|
|
118
|
+
/** JSON.parse otherwise loses duplicate members and can round decimal argument values. */
|
|
119
|
+
export declare function validateJsonText(raw: string): void;
|
|
120
|
+
export type DetectedSource = {
|
|
121
|
+
format: "otlp_json";
|
|
122
|
+
envelopes: Record<string, unknown>[];
|
|
123
|
+
} | {
|
|
124
|
+
format: "langfuse_observations";
|
|
125
|
+
observations: Record<string, JsonValue>[];
|
|
126
|
+
envelope: Record<string, JsonValue>;
|
|
127
|
+
unsafeIntegers: boolean;
|
|
128
|
+
} | {
|
|
129
|
+
format: "atif";
|
|
130
|
+
text: string;
|
|
131
|
+
document: unknown;
|
|
132
|
+
unsafeIntegers: boolean;
|
|
133
|
+
} | {
|
|
134
|
+
format: "unsupported";
|
|
135
|
+
reason: string;
|
|
136
|
+
};
|
|
137
|
+
/** What a file holds, read from its content alone; never from its name. */
|
|
138
|
+
export declare function detectFormat(bytes: Uint8Array): DetectedSource;
|
|
139
|
+
export declare function anyValue(value: JsonValue): OtlpValue;
|
|
140
|
+
/** The large values one trace stores apart from its spans, each once by digest. */
|
|
141
|
+
export declare class BlobSink {
|
|
142
|
+
readonly blobs: Map<string, PreparedBlob>;
|
|
143
|
+
/** Stores a text whole and returns what its record keeps: the first 16 KiB and the `hue.blobs`
|
|
144
|
+
* entry; undefined when the text is over the limit of one stored value. */
|
|
145
|
+
place(key: string, text: string): {
|
|
146
|
+
prefix: string;
|
|
147
|
+
entry: string;
|
|
148
|
+
} | undefined;
|
|
149
|
+
/** The values in digest order: the manifest lists a trace's values the same whatever order its
|
|
150
|
+
* sources were read in, so renaming or repacking them keeps the manifest digest. */
|
|
151
|
+
sorted(): PreparedBlob[];
|
|
152
|
+
}
|
|
153
|
+
/** A converted span's attributes: a value over the inline limit is stored apart from the span
|
|
154
|
+
* through the trace's {@link BlobSink} and listed in `hue.blobs`, as the SDK's exporter lists
|
|
155
|
+
* the values it uploads, with the key in `hue.truncated`. */
|
|
156
|
+
export declare class AttributeList {
|
|
157
|
+
private readonly blobs;
|
|
158
|
+
readonly values: OtlpAttribute[];
|
|
159
|
+
private readonly listed;
|
|
160
|
+
private readonly truncated;
|
|
161
|
+
constructor(blobs: BlobSink);
|
|
162
|
+
add(key: string, value: JsonValue): void;
|
|
163
|
+
json(key: string, value: unknown): void;
|
|
164
|
+
finish(): OtlpAttribute[];
|
|
165
|
+
}
|
|
166
|
+
/** Gathers the spans of OTLP/JSON exports by trace, across files: an export writes a trace's
|
|
167
|
+
* spans in the requests that carried them, which need not be one file. */
|
|
168
|
+
export declare class OtlpTraceCollector {
|
|
169
|
+
private readonly traces;
|
|
170
|
+
/** Adds every span of the envelopes; the spans refused, with their reasons. */
|
|
171
|
+
add(envelopes: readonly Record<string, unknown>[], sourceDigest: string): Refusal[];
|
|
172
|
+
private addSpan;
|
|
173
|
+
/** Every trace gathered so far, spans in time order, and the traces refused whole. */
|
|
174
|
+
finish(): {
|
|
175
|
+
traces: ConvertedTrace[];
|
|
176
|
+
refused: Refusal[];
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
export declare function unixNano(value: JsonValue | undefined): string;
|
|
180
|
+
/** Gathers Langfuse observations by trace across files and converts each trace as Hue's reader
|
|
181
|
+
* converts an observation export: one span per observation, the record kept whole. */
|
|
182
|
+
export declare class LangfuseTraceCollector {
|
|
183
|
+
private readonly traces;
|
|
184
|
+
add(observations: readonly Record<string, JsonValue>[], envelope: Record<string, JsonValue>, sourceDigest: string): void;
|
|
185
|
+
finish(): {
|
|
186
|
+
traces: ConvertedTrace[];
|
|
187
|
+
refused: Refusal[];
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/** The ATIF reader's bounds: a text export of at most 4 MiB and 2,000 spans. */
|
|
191
|
+
export declare const MAX_ATIF_FILE_BYTES: number;
|
|
192
|
+
export declare const MAX_ATIF_SPANS = 2000;
|
|
193
|
+
/** Retains nanoseconds and requires an explicit timezone, valid calendar date and OTLP uint64. */
|
|
194
|
+
export declare function atifUnixNano(value: string): string;
|
|
195
|
+
/**
|
|
196
|
+
* A text ATIF v1.7 or v1.8 export as one trace: an agent span for the run, a model span per
|
|
197
|
+
* agent step with the conversation so far, a tool span per call under it, and an embedded
|
|
198
|
+
* subagent trajectory as its own agent span under the tool span of the call that spawned it.
|
|
199
|
+
* Identity is run scoped (`session_id` and `trajectory_id`), never a path. The complete original
|
|
200
|
+
* document stays on the root; each model and tool span keeps its own record. ATIF supplies step
|
|
201
|
+
* instants, not call durations: model and tool spans are zero-duration point spans.
|
|
202
|
+
*/
|
|
203
|
+
export declare function convertAtifExport(source: Extract<DetectedSource, {
|
|
204
|
+
format: "atif";
|
|
205
|
+
}>, rawBytes: number): ConvertedTrace;
|
|
206
|
+
export interface PreparedBatch {
|
|
207
|
+
sha256: string;
|
|
208
|
+
body: Uint8Array;
|
|
209
|
+
decodedBytes: number;
|
|
210
|
+
spanIds: string[];
|
|
211
|
+
}
|
|
212
|
+
/** `Math.floor(numerator / divisor)` for safe nonnegative integers, without a division: the
|
|
213
|
+
* published package's import-boundary check reads a `/` after a name as a regular expression, so
|
|
214
|
+
* the quotient is estimated from the reciprocal and corrected by whole steps, which makes it
|
|
215
|
+
* exact on every engine. */
|
|
216
|
+
export declare function integerQuotient(numerator: number, divisor: number): number;
|
|
217
|
+
/**
|
|
218
|
+
* The trace's entries packed into gzip OTLP/JSON batches in order, each at most 3 MiB decoded
|
|
219
|
+
* and 900 KiB on the wire. Packing is linear and the output depends on the entries alone, so the
|
|
220
|
+
* same trace packs to the same batches whatever bundle it came from.
|
|
221
|
+
*/
|
|
222
|
+
export declare function batchEntries(entries: readonly ImportEntry[]): PreparedBatch[];
|
|
223
|
+
export interface ManifestTrace {
|
|
224
|
+
trace_id: string;
|
|
225
|
+
span_count: number;
|
|
226
|
+
batches: {
|
|
227
|
+
sha256: string;
|
|
228
|
+
bytes: number;
|
|
229
|
+
span_count: number;
|
|
230
|
+
}[];
|
|
231
|
+
blobs: {
|
|
232
|
+
sha256: string;
|
|
233
|
+
byte_size: number;
|
|
234
|
+
content_type: string;
|
|
235
|
+
}[];
|
|
236
|
+
identity?: TraceIdentity;
|
|
237
|
+
origin: TraceImportOrigin;
|
|
238
|
+
source_digest?: string;
|
|
239
|
+
}
|
|
240
|
+
export interface TraceImportManifest {
|
|
241
|
+
version: 1;
|
|
242
|
+
source: {
|
|
243
|
+
formats: TraceImportFormat[];
|
|
244
|
+
helper: string;
|
|
245
|
+
helper_version?: string;
|
|
246
|
+
};
|
|
247
|
+
traces: ManifestTrace[];
|
|
248
|
+
}
|
|
249
|
+
export interface TraceImportTotals {
|
|
250
|
+
traces: number;
|
|
251
|
+
batches: number;
|
|
252
|
+
spans: number;
|
|
253
|
+
blobs: number;
|
|
254
|
+
bytes: number;
|
|
255
|
+
}
|
|
256
|
+
/** The totals a manifest adds up to, as Hue computes them. */
|
|
257
|
+
export declare function manifestTotals(manifest: TraceImportManifest): TraceImportTotals;
|
|
258
|
+
export declare function manifestTraceHasIdentity(trace: Pick<ManifestTrace, "identity">): boolean;
|
|
259
|
+
/** A manifest's problem, or undefined: the shape and cross-checks Hue's registration applies
|
|
260
|
+
* (every trace id once, every batch digest once, each trace's batches adding up to its span
|
|
261
|
+
* count, each trace's values distinct) so a bundle is refused here, before any upload. */
|
|
262
|
+
export declare function validateManifest(value: unknown): string | undefined;
|