@hue-run/sdk 0.21.1 → 0.23.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 +246 -52
- package/README.md +69 -14
- package/WORLDS.md +6 -3
- package/dist/ai-sdk.d.ts +23 -1
- package/dist/ai-sdk.js +60 -2
- package/dist/cli/import-formats/adapter.d.ts +80 -0
- package/dist/cli/import-formats/adapter.js +1 -0
- package/dist/cli/import-formats/agent-events.d.ts +5 -0
- package/dist/cli/import-formats/agent-events.js +16 -0
- package/dist/cli/import-formats/atif.d.ts +3 -0
- package/dist/cli/import-formats/atif.js +15 -0
- package/dist/cli/import-formats/braintrust.d.ts +2 -0
- package/dist/cli/import-formats/braintrust.js +222 -0
- package/dist/cli/import-formats/containers.d.ts +32 -0
- package/dist/cli/import-formats/containers.js +399 -0
- package/dist/cli/import-formats/datadog.d.ts +2 -0
- package/dist/cli/import-formats/datadog.js +264 -0
- package/dist/cli/import-formats/helicone.d.ts +2 -0
- package/dist/cli/import-formats/helicone.js +161 -0
- package/dist/cli/import-formats/identity.d.ts +112 -0
- package/dist/cli/import-formats/identity.js +594 -0
- package/dist/cli/import-formats/jaeger.d.ts +3 -0
- package/dist/cli/import-formats/jaeger.js +266 -0
- package/dist/cli/import-formats/langfuse.d.ts +7 -0
- package/dist/cli/import-formats/langfuse.js +510 -0
- package/dist/cli/import-formats/langsmith.d.ts +2 -0
- package/dist/cli/import-formats/langsmith.js +244 -0
- package/dist/cli/import-formats/messages.d.ts +28 -0
- package/dist/cli/import-formats/messages.js +301 -0
- package/dist/cli/import-formats/mlflow.d.ts +2 -0
- package/dist/cli/import-formats/mlflow.js +260 -0
- package/dist/cli/import-formats/openai-agents.d.ts +2 -0
- package/dist/cli/import-formats/openai-agents.js +221 -0
- package/dist/cli/import-formats/opik.d.ts +2 -0
- package/dist/cli/import-formats/opik.js +193 -0
- package/dist/cli/import-formats/otel-family.d.ts +5 -0
- package/dist/cli/import-formats/otel-family.js +17 -0
- package/dist/cli/import-formats/otel-spans.d.ts +94 -0
- package/dist/cli/import-formats/otel-spans.js +262 -0
- package/dist/cli/import-formats/otlp-protobuf.d.ts +35 -0
- package/dist/cli/import-formats/otlp-protobuf.js +486 -0
- package/dist/cli/import-formats/otlp.d.ts +4 -0
- package/dist/cli/import-formats/otlp.js +15 -0
- package/dist/cli/import-formats/phoenix.d.ts +3 -0
- package/dist/cli/import-formats/phoenix.js +256 -0
- package/dist/cli/import-formats/raindrop.d.ts +21 -0
- package/dist/cli/import-formats/raindrop.js +255 -0
- package/dist/cli/import-formats/reader.d.ts +56 -0
- package/dist/cli/import-formats/reader.js +364 -0
- package/dist/cli/import-formats/registry.d.ts +36 -0
- package/dist/cli/import-formats/registry.js +71 -0
- package/dist/cli/import-formats/row-platform-fields.d.ts +32 -0
- package/dist/cli/import-formats/row-platform-fields.js +76 -0
- package/dist/cli/import-formats/rows.d.ts +165 -0
- package/dist/cli/import-formats/rows.js +638 -0
- package/dist/cli/import-formats/transcripts.d.ts +25 -0
- package/dist/cli/import-formats/transcripts.js +498 -0
- package/dist/cli/import-formats/weave.d.ts +2 -0
- package/dist/cli/import-formats/weave.js +159 -0
- package/dist/cli/import-formats/zipkin.d.ts +2 -0
- package/dist/cli/import-formats/zipkin.js +219 -0
- package/dist/cli/import.d.ts +1 -1
- package/dist/cli/import.js +4 -2
- package/dist/cli/traces-import-convert.d.ts +65 -33
- package/dist/cli/traces-import-convert.js +211 -214
- package/dist/cli/traces-import-prepare.d.ts +26 -22
- package/dist/cli/traces-import-prepare.js +51 -122
- package/dist/cli/traces-import.d.ts +4 -4
- package/dist/cli/traces-import.js +63 -11
- package/dist/transport.d.ts +12 -0
- package/dist/transport.js +139 -24
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { ConversionError, isObject, } from "../traces-import-convert.js";
|
|
2
|
+
import { otelTraceKey, OtelSpanCollector, unsigned } from "./otel-spans.js";
|
|
3
|
+
import { readRecords } from "./rows.js";
|
|
4
|
+
const object = (value) => isObject(value) ? value : undefined;
|
|
5
|
+
const text = (value) => typeof value === "string" && value ? value : undefined;
|
|
6
|
+
const KINDS = { SERVER: 2, CLIENT: 3, PRODUCER: 4, CONSUMER: 5 };
|
|
7
|
+
/** A record with a Zipkin v2 span's hex trace and span ids and none of the fields other formats
|
|
8
|
+
* name a span with (`type`, `startTime`, `spanId`, `traceID`, `span_id`). */
|
|
9
|
+
function hasSpanIds(value) {
|
|
10
|
+
if (!isObject(value))
|
|
11
|
+
return false;
|
|
12
|
+
const { traceId, id } = value;
|
|
13
|
+
if (typeof traceId !== "string" || typeof id !== "string")
|
|
14
|
+
return false;
|
|
15
|
+
if (!/^[0-9a-f]{16}(?:[0-9a-f]{16})?$/i.test(traceId) || !/^[0-9a-f]{16}$/i.test(id))
|
|
16
|
+
return false;
|
|
17
|
+
return !["type", "startTime", "spanId", "traceID", "span_id"].some((key) => key in value);
|
|
18
|
+
}
|
|
19
|
+
/** A Zipkin v2 span on its own: its ids and a field only a span records. */
|
|
20
|
+
const isSpan = (value) => hasSpanIds(value) &&
|
|
21
|
+
(typeof value.timestamp === "number" ||
|
|
22
|
+
typeof value.duration === "number" ||
|
|
23
|
+
isObject(value.localEndpoint) ||
|
|
24
|
+
(typeof value.kind === "string" && value.kind in KINDS) ||
|
|
25
|
+
Array.isArray(value.annotations) ||
|
|
26
|
+
isObject(value.tags));
|
|
27
|
+
/** Tags that describe the process, which the Collector keeps on the resource. */
|
|
28
|
+
const RESOURCE_PREFIXES = [
|
|
29
|
+
"service.",
|
|
30
|
+
"telemetry.",
|
|
31
|
+
"deployment.",
|
|
32
|
+
"host.",
|
|
33
|
+
"os.",
|
|
34
|
+
"process.",
|
|
35
|
+
"container.",
|
|
36
|
+
"k8s.",
|
|
37
|
+
"cloud.",
|
|
38
|
+
"faas.",
|
|
39
|
+
"device.",
|
|
40
|
+
];
|
|
41
|
+
const attribute = (key, value) => ({
|
|
42
|
+
key,
|
|
43
|
+
value: typeof value === "number" ? { intValue: String(value) } : { stringValue: value },
|
|
44
|
+
});
|
|
45
|
+
/** An endpoint's address and port under `prefix` (`net.host`, `net.peer`). */
|
|
46
|
+
function endpointAttributes(endpoint, prefix) {
|
|
47
|
+
if (!endpoint)
|
|
48
|
+
return [];
|
|
49
|
+
const address = text(endpoint.ipv4) ?? text(endpoint.ipv6);
|
|
50
|
+
const port = endpoint.port;
|
|
51
|
+
return [
|
|
52
|
+
...(address ? [attribute(`${prefix}.ip`, address)] : []),
|
|
53
|
+
...(typeof port === "number" && Number.isSafeInteger(port) && port > 0
|
|
54
|
+
? [attribute(`${prefix}.port`, port)]
|
|
55
|
+
: []),
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
function micros(value, what) {
|
|
59
|
+
if (value === undefined || value === null)
|
|
60
|
+
return undefined;
|
|
61
|
+
const count = unsigned(value);
|
|
62
|
+
if (count === undefined)
|
|
63
|
+
throw new ConversionError(`${what} is not a count of microseconds`);
|
|
64
|
+
return count * 1000n;
|
|
65
|
+
}
|
|
66
|
+
function readSpan(span, sequence) {
|
|
67
|
+
const { traceId, id } = span;
|
|
68
|
+
if (!hasSpanIds(span) || typeof traceId !== "string" || typeof id !== "string")
|
|
69
|
+
throw new ConversionError("a record that is not a Zipkin span");
|
|
70
|
+
const tagsValue = span.tags ?? {};
|
|
71
|
+
const tags = object(tagsValue);
|
|
72
|
+
if (!tags)
|
|
73
|
+
throw new ConversionError("tags is not an object");
|
|
74
|
+
const remaining = new Map();
|
|
75
|
+
for (const [key, value] of Object.entries(tags)) {
|
|
76
|
+
if (typeof value !== "string")
|
|
77
|
+
throw new ConversionError(`tag ${key} is not text`);
|
|
78
|
+
remaining.set(key, value);
|
|
79
|
+
}
|
|
80
|
+
const take = (key) => {
|
|
81
|
+
const value = remaining.get(key);
|
|
82
|
+
remaining.delete(key);
|
|
83
|
+
return value;
|
|
84
|
+
};
|
|
85
|
+
let kind = typeof span.kind === "string" ? (KINDS[span.kind] ?? 0) : 0;
|
|
86
|
+
const kindTag = take("span.kind");
|
|
87
|
+
if (!kind && kindTag === "internal")
|
|
88
|
+
kind = 1;
|
|
89
|
+
let status;
|
|
90
|
+
const code = take("otel.status_code");
|
|
91
|
+
const description = take("otel.status_description");
|
|
92
|
+
if (code !== undefined)
|
|
93
|
+
status = {
|
|
94
|
+
code: code === "OK" ? 1 : code === "ERROR" ? 2 : 0,
|
|
95
|
+
...(description ? { message: description } : {}),
|
|
96
|
+
};
|
|
97
|
+
const error = take("error");
|
|
98
|
+
if (error !== undefined) {
|
|
99
|
+
const message = error && error !== "true" ? error : status?.message;
|
|
100
|
+
status = { code: 2, ...(message ? { message } : {}) };
|
|
101
|
+
}
|
|
102
|
+
const scopeName = take("otel.scope.name") ?? take("otel.library.name");
|
|
103
|
+
const scopeVersion = take("otel.scope.version") ?? take("otel.library.version");
|
|
104
|
+
const traceState = take("w3c.tracestate");
|
|
105
|
+
const local = object(span.localEndpoint);
|
|
106
|
+
const remote = object(span.remoteEndpoint);
|
|
107
|
+
const resource = [];
|
|
108
|
+
const service = text(local?.serviceName);
|
|
109
|
+
if (service)
|
|
110
|
+
resource.push(attribute("service.name", service));
|
|
111
|
+
const attributes = [];
|
|
112
|
+
for (const [key, value] of remaining)
|
|
113
|
+
(RESOURCE_PREFIXES.some((prefix) => key.startsWith(prefix)) ? resource : attributes).push(attribute(key, value));
|
|
114
|
+
attributes.push(...endpointAttributes(local, "net.host"));
|
|
115
|
+
attributes.push(...endpointAttributes(remote, "net.peer"));
|
|
116
|
+
const peer = text(remote?.serviceName);
|
|
117
|
+
if (peer)
|
|
118
|
+
attributes.push(attribute("peer.service", peer));
|
|
119
|
+
const annotations = span.annotations ?? [];
|
|
120
|
+
if (!Array.isArray(annotations))
|
|
121
|
+
throw new ConversionError("annotations is not a list");
|
|
122
|
+
const start = micros(span.timestamp, "timestamp");
|
|
123
|
+
if (start === 0n)
|
|
124
|
+
throw new ConversionError("timestamp is zero");
|
|
125
|
+
const duration = micros(span.duration, "duration");
|
|
126
|
+
const parentId = text(span.parentId);
|
|
127
|
+
if (parentId !== undefined && !/^[0-9a-f]{1,16}$/i.test(parentId))
|
|
128
|
+
throw new ConversionError("parentId is not a span id");
|
|
129
|
+
return {
|
|
130
|
+
traceId,
|
|
131
|
+
spanKey: id.toLowerCase(),
|
|
132
|
+
...(parentId ? { parentKey: parentId.toLowerCase() } : {}),
|
|
133
|
+
name: text(span.name) ?? "",
|
|
134
|
+
kind,
|
|
135
|
+
...(traceState ? { traceState } : {}),
|
|
136
|
+
...(start !== undefined ? { start } : {}),
|
|
137
|
+
...(start !== undefined && duration !== undefined ? { end: start + duration } : {}),
|
|
138
|
+
attributes,
|
|
139
|
+
events: annotations.map((item) => {
|
|
140
|
+
const annotation = object(item);
|
|
141
|
+
if (!annotation || typeof annotation.value !== "string")
|
|
142
|
+
throw new ConversionError("an annotation without a value");
|
|
143
|
+
const time = micros(annotation.timestamp, "an annotation timestamp");
|
|
144
|
+
return { name: annotation.value, ...(time ? { time } : {}), attributes: [] };
|
|
145
|
+
}),
|
|
146
|
+
links: [],
|
|
147
|
+
...(status ? { status } : {}),
|
|
148
|
+
resource,
|
|
149
|
+
...(scopeName
|
|
150
|
+
? { scope: { name: scopeName, ...(scopeVersion ? { version: scopeVersion } : {}) } }
|
|
151
|
+
: {}),
|
|
152
|
+
record: span,
|
|
153
|
+
sequence,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const serviceOf = (span) => {
|
|
157
|
+
const local = object(object(span.record)?.localEndpoint);
|
|
158
|
+
return text(local?.serviceName) ?? "";
|
|
159
|
+
};
|
|
160
|
+
const isShared = (span) => object(span.record)?.shared === true;
|
|
161
|
+
/** Nests each server span that shares its id with its client under the client, and the server's
|
|
162
|
+
* children under the server when they ran on the same service, as Zipkin's UI builds its tree. */
|
|
163
|
+
function nestShared(spans) {
|
|
164
|
+
const unshared = new Set(spans.filter((span) => !isShared(span)).map((span) => span.spanKey));
|
|
165
|
+
const sharedCount = new Map();
|
|
166
|
+
for (const span of spans)
|
|
167
|
+
if (isShared(span))
|
|
168
|
+
sharedCount.set(span.spanKey, (sharedCount.get(span.spanKey) ?? 0) + 1);
|
|
169
|
+
// A shared span is renamed when another span holds its id: its client, or another server.
|
|
170
|
+
const renamed = new Map();
|
|
171
|
+
const keyed = spans.map((span) => {
|
|
172
|
+
if (!isShared(span) || (!unshared.has(span.spanKey) && sharedCount.get(span.spanKey) === 1))
|
|
173
|
+
return span;
|
|
174
|
+
const spanKey = `${span.spanKey}#shared:${serviceOf(span)}`;
|
|
175
|
+
renamed.set(`${span.spanKey}\u0000${serviceOf(span)}`, spanKey);
|
|
176
|
+
return {
|
|
177
|
+
...span,
|
|
178
|
+
spanKey,
|
|
179
|
+
sourceSpanId: span.spanKey,
|
|
180
|
+
...(unshared.has(span.spanKey)
|
|
181
|
+
? { parentKey: span.spanKey }
|
|
182
|
+
: span.parentKey !== undefined
|
|
183
|
+
? { parentKey: span.parentKey }
|
|
184
|
+
: {}),
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
return keyed.map((span) => {
|
|
188
|
+
if (span.parentKey === undefined || span.sourceSpanId !== undefined)
|
|
189
|
+
return span;
|
|
190
|
+
const server = renamed.get(`${span.parentKey}\u0000${serviceOf(span)}`);
|
|
191
|
+
return server ? { ...span, parentKey: server } : span;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/** The spans a value holds: one span, or a list of spans. */
|
|
195
|
+
const spansOf = (value) => isSpan(value)
|
|
196
|
+
? [value]
|
|
197
|
+
: Array.isArray(value) && value.length && value.every(isObject) && value.some(isSpan)
|
|
198
|
+
? value
|
|
199
|
+
: undefined;
|
|
200
|
+
export const zipkin = {
|
|
201
|
+
id: "zipkin",
|
|
202
|
+
manifestFormat: "otlp_json",
|
|
203
|
+
detect: (value) => spansOf(value) !== undefined,
|
|
204
|
+
convert(value, context) {
|
|
205
|
+
const collector = context.collector(() => new OtelSpanCollector("zipkin", nestShared));
|
|
206
|
+
const records = spansOf(value) ?? [];
|
|
207
|
+
// A span's position in its source orders a trace that records no instant at all.
|
|
208
|
+
const base = context.source.line ?? context.source.index ?? 0;
|
|
209
|
+
readRecords(context, records.map((record, index) => ({ record, sequence: base + index })), ({ record }) => {
|
|
210
|
+
const traceId = object(record)?.traceId;
|
|
211
|
+
return typeof traceId === "string" && traceId ? otelTraceKey(traceId) : undefined;
|
|
212
|
+
}, ({ record, sequence }) => {
|
|
213
|
+
const span = object(record);
|
|
214
|
+
if (!span)
|
|
215
|
+
throw new ConversionError("a span that is not an object");
|
|
216
|
+
collector.add(readSpan(span, sequence), context.source.digest);
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
};
|
package/dist/cli/import.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface ImportCommandIo {
|
|
|
26
26
|
* in flight do not hold it until their requests time out. */
|
|
27
27
|
exit?: (code: number) => void;
|
|
28
28
|
}
|
|
29
|
-
export declare const IMPORT_USAGE = "Usage: hue import langfuse [options]\n\nCopy traces from a Langfuse project into Hue. The import runs on this machine with your own\nLangfuse keys, which are sent only to Langfuse. Each observation becomes a span with the\nlangfuse.* attributes Hue reads; ids are kept, so running the same command again resumes an\nimport without duplicating what was already sent. Langfuse's environment is also sent, as\nrecorded, as hue.import.source_environment
|
|
29
|
+
export declare const IMPORT_USAGE = "Usage: hue import langfuse [options]\n\nCopy traces from a Langfuse project into Hue. The import runs on this machine with your own\nLangfuse keys, which are sent only to Langfuse. Each observation becomes a span with the\nlangfuse.* attributes Hue reads; ids are kept, so running the same command again resumes an\nimport without duplicating what was already sent. Langfuse's environment is also sent, as\nrecorded, as hue.import.source_environment. Ended traces with a resolved user can contribute to\nthat user's World regardless of deployment environment. Traces linked to Hue runs, World instances,\nor run executions never contribute. Hue's evaluation and simulation evidence and deliberately\nexcluded traces also stay excluded.\n\nLangfuse (keys from the environment or --env-path; the secret key never from the command line):\n LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY The Langfuse project's API keys\n LANGFUSE_BASE_URL or LANGFUSE_HOST Langfuse host (default https://cloud.langfuse.com)\n --langfuse-host URL Langfuse host, instead of the variables\n --langfuse-public-key KEY Public key, instead of LANGFUSE_PUBLIC_KEY\n\nHue:\n HUE_API_KEY The Hue project key the traces are sent with (environment, --env-path\n or ./.env.hue); not needed with --dry-run\n --origin URL Hue origin (default HUE_BASE_URL, then https://app.hue.run)\n --service-name NAME service.name of the imported spans (default langfuse)\n\nSelection (newest first):\n --from TIME Traces that started at or after TIME: an ISO 8601 date (midnight UTC)\n or a date and time with a zone, such as 2026-01-01T09:00:00Z\n --to TIME Traces that started before TIME (default: an hour before the command\n starts, so traces still running are left for a later run)\n --limit N At most N traces\n --trace-id ID This Langfuse trace (repeatable); not with --from, --to or --limit\n\nContent:\n --media Download the files Langfuse media references (@@@langfuseMedia:\u2026@@@)\n point to and upload them to Hue; without it, references stay as text\n --no-content Send metadata only: no inputs, outputs, status messages or model\n parameters (content is sent by default, as the SDK records it)\n\nRun:\n --dry-run Read Langfuse and count what would be imported; send nothing to Hue\n --yes Import without asking for confirmation (required without a terminal)\n --env-path PATH Load a dotenv file first (--env-file also works when it exists);\n ./.env.hue loads when HUE_API_KEY is not set. Variables already set win\n -h, --help Show this help\n\nExit codes: 0 every trace was imported, 1 a trace, span or file was not, 2 usage error,\n130 stopped (run the same command again to resume).";
|
|
30
30
|
/** An instant of the window: an ISO 8601 date (midnight UTC) or a date and time with a zone. */
|
|
31
31
|
export declare function parseInstant(option: string, value: string): string;
|
|
32
32
|
/** The Langfuse host as a base URL without a trailing slash, or why it is refused. */
|
package/dist/cli/import.js
CHANGED
|
@@ -45,8 +45,10 @@ Copy traces from a Langfuse project into Hue. The import runs on this machine wi
|
|
|
45
45
|
Langfuse keys, which are sent only to Langfuse. Each observation becomes a span with the
|
|
46
46
|
langfuse.* attributes Hue reads; ids are kept, so running the same command again resumes an
|
|
47
47
|
import without duplicating what was already sent. Langfuse's environment is also sent, as
|
|
48
|
-
recorded, as hue.import.source_environment
|
|
49
|
-
|
|
48
|
+
recorded, as hue.import.source_environment. Ended traces with a resolved user can contribute to
|
|
49
|
+
that user's World regardless of deployment environment. Traces linked to Hue runs, World instances,
|
|
50
|
+
or run executions never contribute. Hue's evaluation and simulation evidence and deliberately
|
|
51
|
+
excluded traces also stay excluded.
|
|
50
52
|
|
|
51
53
|
Langfuse (keys from the environment or --env-path; the secret key never from the command line):
|
|
52
54
|
LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY The Langfuse project's API keys
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The conversions behind `hue traces import prepare
|
|
3
|
-
* (
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* The conversions behind `hue traces import prepare` that predate the format registry
|
|
3
|
+
* (`import-formats/registry.ts`, which recognizes each value and hands it to its format): OTLP/JSON
|
|
4
|
+
* spans kept as recorded, complete Langfuse observation exports and text ATIF exports, each trace
|
|
5
|
+
* as OTLP/JSON entries, one span each, that keep the recorded ids, hierarchy, timestamps,
|
|
6
|
+
* content, errors and usage; the shared attribute, large-value and JSON helpers; and the packing
|
|
7
|
+
* of entries into deterministic gzip batches and the manifest. Nothing here depends on where a
|
|
8
|
+
* file sat or what it was called: a renamed or repacked bundle converts to byte-identical
|
|
9
|
+
* batches, so the manifest's digests name the same import.
|
|
8
10
|
*/
|
|
9
11
|
export declare const traceImportFormats: readonly ["otlp_json", "langfuse_observations", "atif"];
|
|
10
12
|
export type TraceImportFormat = (typeof traceImportFormats)[number];
|
|
@@ -89,15 +91,19 @@ export interface PreparedBlob {
|
|
|
89
91
|
}
|
|
90
92
|
export interface ConvertedTrace {
|
|
91
93
|
traceId: string;
|
|
92
|
-
format
|
|
94
|
+
/** The id of the format that read the trace (`import-formats/registry.ts`). */
|
|
95
|
+
format: string;
|
|
93
96
|
entries: ImportEntry[];
|
|
94
97
|
blobs: PreparedBlob[];
|
|
98
|
+
/** Who the trace ran for, read from its entries as Hue reads them (`import-formats/identity.ts`). */
|
|
95
99
|
identity?: TraceIdentity;
|
|
96
100
|
origin: TraceImportOrigin;
|
|
97
101
|
/** The SHA-256 of every source file that held part of the trace. */
|
|
98
102
|
sourceDigests: Set<string>;
|
|
99
103
|
remappedTraceId: boolean;
|
|
100
104
|
remappedSpans: number;
|
|
105
|
+
/** The rules that supplied instants the source did not record (`import-formats/rows.ts`). */
|
|
106
|
+
inferredTiming?: string[];
|
|
101
107
|
}
|
|
102
108
|
/** A trace, span or file that was not converted, and why. */
|
|
103
109
|
export interface Refusal {
|
|
@@ -106,7 +112,21 @@ export interface Refusal {
|
|
|
106
112
|
}
|
|
107
113
|
export declare class ConversionError extends Error {
|
|
108
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* A failure that refuses only what was being read: a ConversionError as itself, and a RangeError
|
|
117
|
+
* (a value too large or nested too deep for a reader to walk) as one. Anything else is a defect
|
|
118
|
+
* and is undefined, for the caller to rethrow.
|
|
119
|
+
*/
|
|
120
|
+
export declare function conversionFailure(error: unknown): ConversionError | undefined;
|
|
121
|
+
/** How deep a value read from a source may nest: far beyond any export's records, and well
|
|
122
|
+
* inside what the readers and serializers that walk a value can follow. */
|
|
123
|
+
export declare const MAX_SOURCE_NESTING = 512;
|
|
124
|
+
/** Whether a parsed JSON value nests deeper than `max` levels, walked without recursion and
|
|
125
|
+
* holding only the path to the value being looked at. */
|
|
126
|
+
export declare function nestedTooDeep(value: unknown, max?: number): boolean;
|
|
109
127
|
export declare const sha256Hex: (value: string | Uint8Array) => string;
|
|
128
|
+
export declare const digestHex: (text: string, length: number) => string;
|
|
129
|
+
export declare function isObject(value: unknown): value is Record<string, unknown>;
|
|
110
130
|
/**
|
|
111
131
|
* JSON text parsed with every integer too large for a JavaScript number kept as its decimal
|
|
112
132
|
* text, so an OTLP timestamp written as a number keeps its nanoseconds. `unsafeIntegers` says
|
|
@@ -116,29 +136,20 @@ export declare function parseJsonText(text: string): {
|
|
|
116
136
|
value: unknown;
|
|
117
137
|
unsafeIntegers: boolean;
|
|
118
138
|
};
|
|
139
|
+
/** JSON text a record holds (metadata values, tool arguments, a column a warehouse stores as text)
|
|
140
|
+
* parsed as {@link parseJsonText} parses it; text nested deeper than {@link MAX_SOURCE_NESTING}
|
|
141
|
+
* throws, so the caller keeps it as the text it is. */
|
|
142
|
+
export declare function parseNestedJsonText(text: string): {
|
|
143
|
+
value: unknown;
|
|
144
|
+
unsafeIntegers: boolean;
|
|
145
|
+
};
|
|
146
|
+
/** Langfuse's experiment and evaluator environments (`sdk-experiment`, `langfuse-…`): traffic
|
|
147
|
+
* Langfuse itself marks as evaluation. */
|
|
148
|
+
export declare const langfuseEvaluationEnvironment: (environment: unknown) => boolean;
|
|
119
149
|
/** Refuses a source value that JavaScript or OTLP cannot retain exactly; never omits it. */
|
|
120
150
|
export declare function validateSource(value: JsonValue, depth?: number): void;
|
|
121
151
|
/** JSON.parse otherwise loses duplicate members and can round decimal argument values. */
|
|
122
152
|
export declare function validateJsonText(raw: string): void;
|
|
123
|
-
export type DetectedSource = {
|
|
124
|
-
format: "otlp_json";
|
|
125
|
-
envelopes: Record<string, unknown>[];
|
|
126
|
-
} | {
|
|
127
|
-
format: "langfuse_observations";
|
|
128
|
-
observations: Record<string, JsonValue>[];
|
|
129
|
-
envelope: Record<string, JsonValue>;
|
|
130
|
-
unsafeIntegers: boolean;
|
|
131
|
-
} | {
|
|
132
|
-
format: "atif";
|
|
133
|
-
text: string;
|
|
134
|
-
document: unknown;
|
|
135
|
-
unsafeIntegers: boolean;
|
|
136
|
-
} | {
|
|
137
|
-
format: "unsupported";
|
|
138
|
-
reason: string;
|
|
139
|
-
};
|
|
140
|
-
/** What a file holds, read from its content alone; never from its name. */
|
|
141
|
-
export declare function detectFormat(bytes: Uint8Array): DetectedSource;
|
|
142
153
|
export declare function anyValue(value: JsonValue): OtlpValue;
|
|
143
154
|
/** The large values one trace stores apart from its spans, each once by digest. */
|
|
144
155
|
export declare class BlobSink {
|
|
@@ -166,25 +177,41 @@ export declare class AttributeList {
|
|
|
166
177
|
json(key: string, value: unknown): void;
|
|
167
178
|
finish(): OtlpAttribute[];
|
|
168
179
|
}
|
|
169
|
-
/**
|
|
170
|
-
*
|
|
180
|
+
/** The collector key of the formats that keep OTLP spans as recorded (OTLP/JSON, binary OTLP,
|
|
181
|
+
* Jaeger API v3): one {@link OtlpTraceCollector} gathers their spans together. */
|
|
182
|
+
export declare const OTLP_SPANS = "otlp_spans";
|
|
183
|
+
/** Gathers the spans of OTLP/JSON exports by trace, across files and the formats that carry
|
|
184
|
+
* them: an export writes a trace's spans in the requests that carried them, which need not be one
|
|
185
|
+
* file. Each trace is named by the format that read its first span. */
|
|
171
186
|
export declare class OtlpTraceCollector {
|
|
172
187
|
private readonly traces;
|
|
173
|
-
/** Adds every span of the envelopes; the spans refused, with their reasons. */
|
|
174
|
-
add(envelopes: readonly Record<string, unknown>[], sourceDigest: string): Refusal[];
|
|
188
|
+
/** Adds every span of the envelopes `format` read; the spans refused, with their reasons. */
|
|
189
|
+
add(envelopes: readonly Record<string, unknown>[], sourceDigest: string, format: string): Refusal[];
|
|
190
|
+
/** Refuses the trace a span names (its id as recorded), whatever else of it is read; a span
|
|
191
|
+
* that names no trace is refused alone. */
|
|
192
|
+
refuse(traceIdValue: unknown, reason: string): Refusal;
|
|
175
193
|
private addSpan;
|
|
194
|
+
private draft;
|
|
176
195
|
/** Every trace gathered so far, spans in time order, and the traces refused whole. */
|
|
177
196
|
finish(): {
|
|
178
197
|
traces: ConvertedTrace[];
|
|
179
198
|
refused: Refusal[];
|
|
180
199
|
};
|
|
181
200
|
}
|
|
201
|
+
/** A recorded span's string values over the inline limit stored apart from it, listed beside
|
|
202
|
+
* any values its exporter already listed. */
|
|
203
|
+
export declare function offloadOtlpValues(attributes: OtlpAttribute[], blobs: BlobSink): OtlpAttribute[];
|
|
182
204
|
export declare function unixNano(value: JsonValue | undefined): string;
|
|
205
|
+
/** JSON text with object keys sorted: equal for equal values whatever their key order. */
|
|
206
|
+
export declare const canonicalJson: (value: JsonValue) => string;
|
|
183
207
|
/** Gathers Langfuse observations by trace across files and converts each trace as Hue's reader
|
|
184
208
|
* converts an observation export: one span per observation, the record kept whole. */
|
|
185
209
|
export declare class LangfuseTraceCollector {
|
|
186
210
|
private readonly traces;
|
|
187
211
|
add(observations: readonly Record<string, JsonValue>[], envelope: Record<string, JsonValue>, sourceDigest: string): void;
|
|
212
|
+
/** Refuses a trace by its Langfuse id, whatever else of it is read. */
|
|
213
|
+
refuse(traceId: string, reason: string, sourceDigest: string): void;
|
|
214
|
+
private draft;
|
|
188
215
|
finish(): {
|
|
189
216
|
traces: ConvertedTrace[];
|
|
190
217
|
refused: Refusal[];
|
|
@@ -195,6 +222,13 @@ export declare const MAX_ATIF_FILE_BYTES: number;
|
|
|
195
222
|
export declare const MAX_ATIF_SPANS = 2000;
|
|
196
223
|
/** Retains nanoseconds and requires an explicit timezone, valid calendar date and OTLP uint64. */
|
|
197
224
|
export declare function atifUnixNano(value: string): string;
|
|
225
|
+
/** An ATIF document as read: its exact text, the parsed value and whether the text held an
|
|
226
|
+
* integer JavaScript cannot represent exactly. */
|
|
227
|
+
export interface AtifSource {
|
|
228
|
+
text: string;
|
|
229
|
+
document: unknown;
|
|
230
|
+
unsafeIntegers: boolean;
|
|
231
|
+
}
|
|
198
232
|
/**
|
|
199
233
|
* A text ATIF v1.7 or v1.8 export as one trace: an agent span for the run, a model span per
|
|
200
234
|
* agent step with the conversation so far, a tool span per call under it, and an embedded
|
|
@@ -203,9 +237,7 @@ export declare function atifUnixNano(value: string): string;
|
|
|
203
237
|
* document stays on the root; each model and tool span keeps its own record. ATIF supplies step
|
|
204
238
|
* instants, not call durations: model and tool spans are zero-duration point spans.
|
|
205
239
|
*/
|
|
206
|
-
export declare function convertAtifExport(source:
|
|
207
|
-
format: "atif";
|
|
208
|
-
}>, rawBytes: number): ConvertedTrace;
|
|
240
|
+
export declare function convertAtifExport(source: AtifSource, rawBytes: number): ConvertedTrace;
|
|
209
241
|
export interface PreparedBatch {
|
|
210
242
|
sha256: string;
|
|
211
243
|
body: Uint8Array;
|