@hue-run/sdk 0.21.0 → 0.22.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.
Files changed (74) hide show
  1. package/CLI.md +255 -61
  2. package/README.md +61 -20
  3. package/WORLDS.md +6 -3
  4. package/dist/ai-sdk.d.ts +23 -1
  5. package/dist/ai-sdk.js +60 -2
  6. package/dist/cli/import-formats/adapter.d.ts +80 -0
  7. package/dist/cli/import-formats/adapter.js +1 -0
  8. package/dist/cli/import-formats/agent-events.d.ts +5 -0
  9. package/dist/cli/import-formats/agent-events.js +16 -0
  10. package/dist/cli/import-formats/atif.d.ts +3 -0
  11. package/dist/cli/import-formats/atif.js +15 -0
  12. package/dist/cli/import-formats/braintrust.d.ts +2 -0
  13. package/dist/cli/import-formats/braintrust.js +222 -0
  14. package/dist/cli/import-formats/containers.d.ts +32 -0
  15. package/dist/cli/import-formats/containers.js +399 -0
  16. package/dist/cli/import-formats/datadog.d.ts +2 -0
  17. package/dist/cli/import-formats/datadog.js +264 -0
  18. package/dist/cli/import-formats/helicone.d.ts +2 -0
  19. package/dist/cli/import-formats/helicone.js +161 -0
  20. package/dist/cli/import-formats/identity.d.ts +112 -0
  21. package/dist/cli/import-formats/identity.js +594 -0
  22. package/dist/cli/import-formats/jaeger.d.ts +3 -0
  23. package/dist/cli/import-formats/jaeger.js +266 -0
  24. package/dist/cli/import-formats/langfuse.d.ts +7 -0
  25. package/dist/cli/import-formats/langfuse.js +510 -0
  26. package/dist/cli/import-formats/langsmith.d.ts +2 -0
  27. package/dist/cli/import-formats/langsmith.js +244 -0
  28. package/dist/cli/import-formats/messages.d.ts +28 -0
  29. package/dist/cli/import-formats/messages.js +301 -0
  30. package/dist/cli/import-formats/mlflow.d.ts +2 -0
  31. package/dist/cli/import-formats/mlflow.js +260 -0
  32. package/dist/cli/import-formats/openai-agents.d.ts +2 -0
  33. package/dist/cli/import-formats/openai-agents.js +221 -0
  34. package/dist/cli/import-formats/opik.d.ts +2 -0
  35. package/dist/cli/import-formats/opik.js +193 -0
  36. package/dist/cli/import-formats/otel-family.d.ts +5 -0
  37. package/dist/cli/import-formats/otel-family.js +17 -0
  38. package/dist/cli/import-formats/otel-spans.d.ts +94 -0
  39. package/dist/cli/import-formats/otel-spans.js +262 -0
  40. package/dist/cli/import-formats/otlp-protobuf.d.ts +35 -0
  41. package/dist/cli/import-formats/otlp-protobuf.js +486 -0
  42. package/dist/cli/import-formats/otlp.d.ts +4 -0
  43. package/dist/cli/import-formats/otlp.js +15 -0
  44. package/dist/cli/import-formats/phoenix.d.ts +3 -0
  45. package/dist/cli/import-formats/phoenix.js +256 -0
  46. package/dist/cli/import-formats/raindrop.d.ts +21 -0
  47. package/dist/cli/import-formats/raindrop.js +255 -0
  48. package/dist/cli/import-formats/reader.d.ts +56 -0
  49. package/dist/cli/import-formats/reader.js +364 -0
  50. package/dist/cli/import-formats/registry.d.ts +36 -0
  51. package/dist/cli/import-formats/registry.js +71 -0
  52. package/dist/cli/import-formats/row-platform-fields.d.ts +32 -0
  53. package/dist/cli/import-formats/row-platform-fields.js +76 -0
  54. package/dist/cli/import-formats/rows.d.ts +165 -0
  55. package/dist/cli/import-formats/rows.js +638 -0
  56. package/dist/cli/import-formats/transcripts.d.ts +25 -0
  57. package/dist/cli/import-formats/transcripts.js +498 -0
  58. package/dist/cli/import-formats/weave.d.ts +2 -0
  59. package/dist/cli/import-formats/weave.js +159 -0
  60. package/dist/cli/import-formats/zipkin.d.ts +2 -0
  61. package/dist/cli/import-formats/zipkin.js +219 -0
  62. package/dist/cli/import.d.ts +1 -1
  63. package/dist/cli/import.js +4 -2
  64. package/dist/cli/mcp.d.ts +5 -1
  65. package/dist/cli/mcp.js +35 -23
  66. package/dist/cli/traces-import-convert.d.ts +65 -33
  67. package/dist/cli/traces-import-convert.js +211 -214
  68. package/dist/cli/traces-import-prepare.d.ts +26 -22
  69. package/dist/cli/traces-import-prepare.js +51 -122
  70. package/dist/cli/traces-import.d.ts +4 -4
  71. package/dist/cli/traces-import.js +63 -11
  72. package/dist/version.d.ts +1 -1
  73. package/dist/version.js +1 -1
  74. package/package.json +3 -3
@@ -0,0 +1,266 @@
1
+ import { ConversionError, isObject, OTLP_SPANS, OtlpTraceCollector, } 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
+ /** The OTLP traces of an API v3 answer or stream line, when it holds any. */
6
+ function v3Traces(value) {
7
+ const result = object(object(value)?.result);
8
+ if (!result)
9
+ return undefined;
10
+ if (Array.isArray(result.resourceSpans) || Array.isArray(result.resource_spans))
11
+ return result;
12
+ const spans = object(result.spans);
13
+ if (spans && (Array.isArray(spans.resourceSpans) || Array.isArray(spans.resource_spans)))
14
+ return spans;
15
+ return undefined;
16
+ }
17
+ const hasOnly = (value, key) => Object.keys(value).length === 1 && key in value;
18
+ /** A stream's closing line, `{"end": true}`. */
19
+ const isEnd = (value) => {
20
+ const line = object(value);
21
+ return line !== undefined && hasOnly(line, "end") && line.end === true;
22
+ };
23
+ /** A gateway error, `{"error": {"httpCode": …, "message": …}}`. */
24
+ const gatewayError = (value) => {
25
+ const line = object(value);
26
+ const error = object(line?.error);
27
+ return line && hasOnly(line, "error") && error && typeof error.httpCode === "number"
28
+ ? error
29
+ : undefined;
30
+ };
31
+ export const jaegerApiV3 = {
32
+ id: "jaeger_api_v3",
33
+ manifestFormat: "otlp_json",
34
+ detect: (value) => v3Traces(value) !== undefined || isEnd(value) || gatewayError(value) !== undefined,
35
+ convert(value, context) {
36
+ const error = gatewayError(value);
37
+ if (error)
38
+ throw new ConversionError(`a Jaeger API error${typeof error.message === "string" ? `: ${error.message}` : ""}`);
39
+ const traces = v3Traces(value);
40
+ if (!traces)
41
+ return;
42
+ const collector = context.collector(() => new OtlpTraceCollector(), OTLP_SPANS);
43
+ for (const refusal of collector.add([traces], context.source.digest, jaegerApiV3.id))
44
+ context.refuse(refusal.reason, refusal.traceId);
45
+ },
46
+ };
47
+ const isSpan = (value) => isObject(value) &&
48
+ typeof value.spanID === "string" &&
49
+ typeof value.operationName === "string" &&
50
+ "startTime" in value;
51
+ const isTrace = (value) => isObject(value) && Array.isArray(value.spans) && value.spans.some(isSpan);
52
+ /** The traces a value holds: a `{data: […]}` answer, one trace, or one span with its process. */
53
+ function tracesOf(value) {
54
+ const document = object(value);
55
+ if (!document)
56
+ return undefined;
57
+ if (Array.isArray(document.data) && document.data.some(isTrace))
58
+ return document.data.map((trace) => object(trace) ?? {});
59
+ if (isTrace(document))
60
+ return [document];
61
+ if (isSpan(document) && typeof document.traceID === "string" && object(document.process))
62
+ return [{ traceID: document.traceID, spans: [document] }];
63
+ return undefined;
64
+ }
65
+ /** A tag's typed value as OTLP: Jaeger writes an int64 past 2^53 as its decimal text and binary
66
+ * as base64; the embedded-process form writes every value as text. */
67
+ function tagValue(tag) {
68
+ const value = tag.value;
69
+ switch (tag.type) {
70
+ case "string":
71
+ return { stringValue: typeof value === "string" ? value : JSON.stringify(value) };
72
+ case "bool":
73
+ return { boolValue: value === true || value === "true" };
74
+ case "int64":
75
+ if (typeof value === "number" && Number.isSafeInteger(value))
76
+ return { intValue: String(value) };
77
+ if (typeof value === "string" && /^-?\d+$/.test(value))
78
+ return { intValue: value };
79
+ break;
80
+ case "float64":
81
+ if (typeof value === "number")
82
+ return { doubleValue: value };
83
+ if (typeof value === "string" && value.trim() && Number.isFinite(Number(value)))
84
+ return { doubleValue: Number(value) };
85
+ break;
86
+ case "binary":
87
+ if (typeof value === "string")
88
+ return { bytesValue: value };
89
+ break;
90
+ case undefined:
91
+ if (typeof value === "string")
92
+ return { stringValue: value };
93
+ if (typeof value === "boolean")
94
+ return { boolValue: value };
95
+ if (typeof value === "number")
96
+ return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };
97
+ break;
98
+ }
99
+ throw new ConversionError(`tag ${String(tag.key)} has a value that is not its type`);
100
+ }
101
+ function tags(value, what) {
102
+ if (value === undefined || value === null)
103
+ return new Map();
104
+ if (!Array.isArray(value))
105
+ throw new ConversionError(`${what} is not a list`);
106
+ const map = new Map();
107
+ for (const item of value) {
108
+ const tag = object(item);
109
+ if (!tag || typeof tag.key !== "string")
110
+ throw new ConversionError(`${what} holds no key`);
111
+ map.set(tag.key, tagValue(tag));
112
+ }
113
+ return map;
114
+ }
115
+ const attributesOf = (map) => [...map].map(([key, value]) => ({ key, value }));
116
+ const textOf = (value) => typeof value?.stringValue === "string" ? value.stringValue : undefined;
117
+ /** Removes a tag and returns its text. */
118
+ const take = (map, key) => {
119
+ const value = textOf(map.get(key));
120
+ if (value !== undefined)
121
+ map.delete(key);
122
+ return value;
123
+ };
124
+ const KINDS = {
125
+ internal: 1,
126
+ server: 2,
127
+ client: 3,
128
+ producer: 4,
129
+ consumer: 5,
130
+ };
131
+ /** Microseconds as nanoseconds; undefined when absent, refused when malformed. */
132
+ function micros(value, what) {
133
+ if (value === undefined || value === null)
134
+ return undefined;
135
+ const count = unsigned(value);
136
+ if (count === undefined)
137
+ throw new ConversionError(`${what} is not a count of microseconds`);
138
+ return count * 1000n;
139
+ }
140
+ function readSpan(span, trace, context, collector) {
141
+ const traceId = typeof span.traceID === "string" ? span.traceID : trace.traceID;
142
+ if (typeof traceId !== "string" || !traceId)
143
+ throw new ConversionError("a span without a traceID");
144
+ const { spanID, operationName } = span;
145
+ if (!isSpan(span) || typeof spanID !== "string" || typeof operationName !== "string")
146
+ throw new ConversionError("a record that is not a Jaeger span");
147
+ const key = otelTraceKey(traceId);
148
+ const references = span.references ?? [];
149
+ if (!Array.isArray(references))
150
+ throw new ConversionError("references is not a list");
151
+ const refs = references.map((item) => {
152
+ const ref = object(item);
153
+ if (!ref || typeof ref.spanID !== "string" || !ref.spanID)
154
+ throw new ConversionError("a reference without a spanID");
155
+ const refTrace = typeof ref.traceID === "string" && ref.traceID ? ref.traceID : traceId;
156
+ return {
157
+ type: ref.refType,
158
+ traceId: refTrace,
159
+ spanId: ref.spanID,
160
+ same: otelTraceKey(refTrace) === key,
161
+ };
162
+ });
163
+ const parent = refs.find((ref) => ref.same && ref.type === "CHILD_OF") ??
164
+ refs.find((ref) => ref.same && ref.type === "FOLLOWS_FROM");
165
+ const legacyParent = typeof span.parentSpanID === "string" && !/^0*$/.test(span.parentSpanID)
166
+ ? span.parentSpanID
167
+ : undefined;
168
+ const attributes = tags(span.tags, "tags");
169
+ const kind = take(attributes, "span.kind");
170
+ let status;
171
+ const error = attributes.get("error");
172
+ if (error?.boolValue === true) {
173
+ attributes.delete("error");
174
+ const message = take(attributes, "otel.status_description");
175
+ status = { code: 2, ...(message ? { message } : {}) };
176
+ }
177
+ const code = take(attributes, "otel.status_code");
178
+ if (code !== undefined && !status) {
179
+ const message = take(attributes, "otel.status_description");
180
+ const upper = code.toUpperCase();
181
+ status = {
182
+ code: upper === "OK" ? 1 : upper === "ERROR" ? 2 : 0,
183
+ ...(message ? { message } : {}),
184
+ };
185
+ }
186
+ const scopeName = take(attributes, "otel.scope.name") ?? take(attributes, "otel.library.name");
187
+ const scopeVersion = take(attributes, "otel.scope.version") ?? take(attributes, "otel.library.version");
188
+ const traceState = take(attributes, "w3c.tracestate");
189
+ const processes = object(trace.processes);
190
+ const process = object(span.process) ??
191
+ (typeof span.processID === "string" && processes
192
+ ? object(processes[span.processID])
193
+ : undefined);
194
+ const resource = tags(process?.tags, "process tags");
195
+ if (typeof process?.serviceName === "string" && process.serviceName)
196
+ resource.set("service.name", { stringValue: process.serviceName });
197
+ const logs = span.logs ?? [];
198
+ if (!Array.isArray(logs))
199
+ throw new ConversionError("logs is not a list");
200
+ const start = micros(span.startTime, "startTime");
201
+ if (start === 0n)
202
+ throw new ConversionError("startTime is zero");
203
+ const duration = micros(span.duration, "duration");
204
+ const flags = unsigned(span.flags);
205
+ const otelSpan = {
206
+ traceId,
207
+ spanKey: spanID,
208
+ ...(parent ? { parentKey: parent.spanId } : legacyParent ? { parentKey: legacyParent } : {}),
209
+ name: operationName,
210
+ kind: kind !== undefined ? (KINDS[kind] ?? 0) : 0,
211
+ ...(traceState ? { traceState } : {}),
212
+ ...(flags !== undefined && flags <= 0xffffffffn ? { flags: Number(flags) } : {}),
213
+ ...(start !== undefined ? { start } : {}),
214
+ ...(start !== undefined && duration !== undefined ? { end: start + duration } : {}),
215
+ attributes: attributesOf(attributes),
216
+ events: logs.map((item) => {
217
+ const log = object(item);
218
+ if (!log)
219
+ throw new ConversionError("a log that is not an object");
220
+ const fields = tags(log.fields, "log fields");
221
+ const name = take(fields, "event") ?? "";
222
+ const time = micros(log.timestamp, "a log timestamp");
223
+ return { name, ...(time ? { time } : {}), attributes: attributesOf(fields) };
224
+ }),
225
+ links: refs
226
+ .filter((ref) => ref !== parent)
227
+ .map((ref) => ({
228
+ traceId: ref.traceId,
229
+ spanKey: ref.spanId,
230
+ attributes: [
231
+ {
232
+ key: "opentracing.ref_type",
233
+ value: { stringValue: ref.type === "FOLLOWS_FROM" ? "follows_from" : "child_of" },
234
+ },
235
+ ],
236
+ })),
237
+ ...(status ? { status } : {}),
238
+ resource: attributesOf(resource),
239
+ ...(scopeName
240
+ ? { scope: { name: scopeName, ...(scopeVersion ? { version: scopeVersion } : {}) } }
241
+ : {}),
242
+ record: span,
243
+ };
244
+ collector.add(otelSpan, context.source.digest);
245
+ }
246
+ export const jaeger = {
247
+ id: "jaeger",
248
+ manifestFormat: "otlp_json",
249
+ detect: (value) => tracesOf(value) !== undefined,
250
+ convert(value, context) {
251
+ const collector = context.collector(() => new OtelSpanCollector("jaeger"));
252
+ const records = (tracesOf(value) ?? []).flatMap((trace) => {
253
+ const spans = Array.isArray(trace.spans) ? trace.spans : [trace];
254
+ return spans.map((span) => ({ span, trace }));
255
+ });
256
+ readRecords(context, records, ({ span, trace }) => {
257
+ const traceId = object(span)?.traceID ?? trace.traceID;
258
+ return typeof traceId === "string" && traceId ? otelTraceKey(traceId) : undefined;
259
+ }, ({ span, trace }) => {
260
+ const record = object(span);
261
+ if (!record)
262
+ throw new ConversionError("a span that is not an object");
263
+ readSpan(record, trace, context, collector);
264
+ });
265
+ },
266
+ };
@@ -0,0 +1,7 @@
1
+ import type { ImportAdapter } from "./adapter.js";
2
+ /** Hue's documented observation list: a list page (`data`) or the native `observations`
3
+ * envelope, every record an observation, converted as Hue's own reader converts it. */
4
+ export declare const langfuseObservations: ImportAdapter;
5
+ /** Langfuse's own exports, from its API, UI downloads and blob storage (see the top of this
6
+ * file), as canonical rows. */
7
+ export declare const langfuse: ImportAdapter;