@breadcrumb-sdk/core 0.0.2 → 0.0.4

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/dist/index.js CHANGED
@@ -3,7 +3,38 @@ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
3
3
  import { BatchSpanProcessor, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
4
4
  import { ExportResultCode } from "@opentelemetry/core";
5
5
 
6
- //#region src/exporter.ts
6
+ //#region src/mappers/utils.ts
7
+ function tryJson(s) {
8
+ try {
9
+ return JSON.parse(s);
10
+ } catch {
11
+ return void 0;
12
+ }
13
+ }
14
+ function strAttr(attrs, ...keys) {
15
+ for (const key of keys) {
16
+ const v = attrs[key];
17
+ if (typeof v === "string" && v !== "") return v;
18
+ }
19
+ return void 0;
20
+ }
21
+ function intAttr(attrs, ...keys) {
22
+ for (const key of keys) {
23
+ const v = attrs[key];
24
+ if (typeof v === "number") return Math.round(v);
25
+ }
26
+ return void 0;
27
+ }
28
+ function floatAttr(attrs, ...keys) {
29
+ for (const key of keys) {
30
+ const v = attrs[key];
31
+ if (typeof v === "number") return v;
32
+ }
33
+ return void 0;
34
+ }
35
+
36
+ //#endregion
37
+ //#region src/mappers/ai-sdk.ts
7
38
  const LLM_SPAN_NAMES = new Set([
8
39
  "ai.generateText",
9
40
  "ai.generateText.doGenerate",
@@ -18,18 +49,15 @@ const TOOL_SPAN_NAMES = new Set([
18
49
  "ai.toolExecution",
19
50
  "ai.executeToolCall"
20
51
  ]);
21
- const HANDLED_ATTRS = new Set([
22
- "breadcrumb.span.type",
23
- "breadcrumb.input",
24
- "breadcrumb.output",
25
- "breadcrumb.model",
26
- "breadcrumb.provider",
27
- "breadcrumb.input_tokens",
28
- "breadcrumb.output_tokens",
29
- "breadcrumb.input_cost_usd",
30
- "breadcrumb.output_cost_usd",
52
+ const HANDLED = new Set([
53
+ "resource.name",
54
+ "ai.prompt",
31
55
  "ai.prompt.messages",
32
56
  "ai.response.text",
57
+ "ai.response.toolCalls",
58
+ "ai.toolCall.name",
59
+ "ai.toolCall.args",
60
+ "ai.toolCall.result",
33
61
  "ai.model.id",
34
62
  "ai.model.provider",
35
63
  "ai.response.model",
@@ -44,61 +72,25 @@ const HANDLED_ATTRS = new Set([
44
72
  "ai.response.providerMetadata",
45
73
  "ai.response.finishReason"
46
74
  ]);
47
- const DROP_ATTRS = new Set([
75
+ const DROP = new Set([
48
76
  "operation.name",
49
- "resource.name",
50
77
  "ai.operationId",
51
78
  "ai.telemetry.functionId",
52
79
  "gen_ai.response.finish_reasons",
53
80
  "gen_ai.response.id",
54
81
  "gen_ai.response.model",
55
82
  "ai.response.id",
56
- "ai.response.timestamp"
83
+ "ai.response.timestamp",
84
+ "ai.prompt.tools",
85
+ "ai.prompt.toolChoice",
86
+ "ai.toolCall.id"
57
87
  ]);
58
- function hrTimeToISO(hrTime) {
59
- return new Date(hrTime[0] * 1e3 + hrTime[1] / 1e6).toISOString();
60
- }
61
- function spanStatus(span) {
62
- return span.status.code === SpanStatusCode.ERROR ? "error" : "ok";
63
- }
64
- function strAttr(span, ...keys) {
65
- for (const key of keys) {
66
- const v = span.attributes[key];
67
- if (typeof v === "string" && v !== "") return v;
68
- }
69
- return void 0;
70
- }
71
- function intAttr(span, ...keys) {
72
- for (const key of keys) {
73
- const v = span.attributes[key];
74
- if (typeof v === "number") return Math.round(v);
75
- }
76
- return void 0;
77
- }
78
- function floatAttr(span, ...keys) {
79
- for (const key of keys) {
80
- const v = span.attributes[key];
81
- if (typeof v === "number") return v;
82
- }
83
- return void 0;
84
- }
85
- function tryJson(s) {
86
- try {
87
- return JSON.parse(s);
88
- } catch {
89
- return void 0;
90
- }
91
- }
92
- function inferSpanType(span) {
93
- const explicit = span.attributes["breadcrumb.span.type"];
94
- if (typeof explicit === "string" && explicit !== "") return explicit;
95
- if (LLM_SPAN_NAMES.has(span.name)) return "llm";
96
- if (TOOL_SPAN_NAMES.has(span.name)) return "tool";
88
+ function inferType(spanName) {
89
+ if (LLM_SPAN_NAMES.has(spanName)) return "llm";
90
+ if (TOOL_SPAN_NAMES.has(spanName)) return "tool";
97
91
  return "custom";
98
92
  }
99
- function extractCost(span) {
100
- const raw = span.attributes["ai.response.providerMetadata"];
101
- if (typeof raw !== "string") return {};
93
+ function extractCost(raw) {
102
94
  try {
103
95
  const parsed = tryJson(raw);
104
96
  if (!parsed || typeof parsed !== "object") return {};
@@ -119,10 +111,164 @@ function extractCost(span) {
119
111
  } catch {}
120
112
  return {};
121
113
  }
114
+ function mapAiSdk(span) {
115
+ const attrs = span.attributes;
116
+ const result = {};
117
+ const toolCallName = strAttr(attrs, "ai.toolCall.name");
118
+ const resourceName = strAttr(attrs, "resource.name");
119
+ if (toolCallName) result.name = toolCallName;
120
+ else if (resourceName) result.name = resourceName;
121
+ result.type = inferType(span.name);
122
+ const toolArgs = attrs["ai.toolCall.args"];
123
+ const aiMessages = attrs["ai.prompt.messages"];
124
+ const aiPrompt = attrs["ai.prompt"];
125
+ if (typeof toolArgs === "string") result.input = tryJson(toolArgs) ?? toolArgs;
126
+ else if (typeof aiMessages === "string") result.input = tryJson(aiMessages) ?? aiMessages;
127
+ else if (typeof aiPrompt === "string") {
128
+ const parsed = tryJson(aiPrompt);
129
+ if (parsed && typeof parsed === "object") {
130
+ const messages = [];
131
+ if (parsed["system"]) messages.push({
132
+ role: "system",
133
+ content: parsed["system"]
134
+ });
135
+ if (Array.isArray(parsed["messages"])) messages.push(...parsed["messages"]);
136
+ else if (parsed["prompt"] !== void 0) messages.push({
137
+ role: "user",
138
+ content: parsed["prompt"]
139
+ });
140
+ result.input = messages.length > 0 ? messages : parsed;
141
+ } else result.input = aiPrompt;
142
+ }
143
+ const toolResult = attrs["ai.toolCall.result"];
144
+ const aiText = attrs["ai.response.text"];
145
+ const aiToolCalls = attrs["ai.response.toolCalls"];
146
+ if (typeof toolResult === "string") result.output = tryJson(toolResult) ?? toolResult;
147
+ else if (typeof aiText === "string") result.output = aiText;
148
+ else if (typeof aiToolCalls === "string") {
149
+ const parsed = tryJson(aiToolCalls);
150
+ if (Array.isArray(parsed)) result.output = parsed.map((tc) => ({
151
+ ...tc,
152
+ input: typeof tc["input"] === "string" ? tryJson(tc["input"]) ?? tc["input"] : tc["input"]
153
+ }));
154
+ else result.output = parsed ?? aiToolCalls;
155
+ }
156
+ const model = strAttr(attrs, "ai.model.id", "ai.response.model", "gen_ai.request.model");
157
+ if (model) result.model = model;
158
+ const provider = strAttr(attrs, "ai.model.provider", "gen_ai.system");
159
+ if (provider) result.provider = provider;
160
+ const input_tokens = intAttr(attrs, "ai.usage.inputTokens", "ai.usage.promptTokens", "gen_ai.usage.input_tokens");
161
+ if (input_tokens != null) result.input_tokens = input_tokens;
162
+ const output_tokens = intAttr(attrs, "ai.usage.outputTokens", "ai.usage.completionTokens", "gen_ai.usage.output_tokens");
163
+ if (output_tokens != null) result.output_tokens = output_tokens;
164
+ const providerMeta = attrs["ai.response.providerMetadata"];
165
+ if (typeof providerMeta === "string") {
166
+ const cost = extractCost(providerMeta);
167
+ if (cost.input_cost_usd != null) result.input_cost_usd = cost.input_cost_usd;
168
+ if (cost.output_cost_usd != null) result.output_cost_usd = cost.output_cost_usd;
169
+ }
170
+ const metadata = {};
171
+ const finishReason = attrs["ai.response.finishReason"];
172
+ if (typeof finishReason === "string" && finishReason !== "") metadata["finish_reason"] = finishReason;
173
+ for (const [k, v] of Object.entries(attrs)) {
174
+ if (v == null) continue;
175
+ if (HANDLED.has(k) || DROP.has(k)) continue;
176
+ if (k.startsWith("ai.settings.") || k.startsWith("ai.request.headers.")) continue;
177
+ if (k.startsWith("ai.telemetry.metadata.")) {
178
+ metadata[k.slice(22)] = String(v);
179
+ continue;
180
+ }
181
+ if (k.startsWith("ai.") || k.startsWith("gen_ai.")) metadata[k] = String(v);
182
+ }
183
+ if (Object.keys(metadata).length > 0) result.metadata = metadata;
184
+ return result;
185
+ }
186
+
187
+ //#endregion
188
+ //#region src/mappers/breadcrumb.ts
189
+ const VALID_TYPES = new Set([
190
+ "llm",
191
+ "tool",
192
+ "retrieval",
193
+ "step",
194
+ "custom"
195
+ ]);
196
+ function mapBreadcrumb(span) {
197
+ const attrs = span.attributes;
198
+ const result = {};
199
+ const explicitType = attrs["breadcrumb.span.type"];
200
+ if (typeof explicitType === "string" && VALID_TYPES.has(explicitType)) result.type = explicitType;
201
+ const rawInput = attrs["breadcrumb.input"];
202
+ if (rawInput != null) result.input = typeof rawInput === "string" ? tryJson(rawInput) ?? rawInput : rawInput;
203
+ const rawOutput = attrs["breadcrumb.output"];
204
+ if (rawOutput != null) result.output = typeof rawOutput === "string" ? tryJson(rawOutput) ?? rawOutput : rawOutput;
205
+ const model = strAttr(attrs, "breadcrumb.model");
206
+ if (model) result.model = model;
207
+ const provider = strAttr(attrs, "breadcrumb.provider");
208
+ if (provider) result.provider = provider;
209
+ const input_tokens = intAttr(attrs, "breadcrumb.input_tokens");
210
+ if (input_tokens != null) result.input_tokens = input_tokens;
211
+ const output_tokens = intAttr(attrs, "breadcrumb.output_tokens");
212
+ if (output_tokens != null) result.output_tokens = output_tokens;
213
+ const input_cost_usd = floatAttr(attrs, "breadcrumb.input_cost_usd");
214
+ if (input_cost_usd != null) result.input_cost_usd = input_cost_usd;
215
+ const output_cost_usd = floatAttr(attrs, "breadcrumb.output_cost_usd");
216
+ if (output_cost_usd != null) result.output_cost_usd = output_cost_usd;
217
+ const metadata = {};
218
+ for (const [k, v] of Object.entries(attrs)) {
219
+ if (v == null) continue;
220
+ if (k.startsWith("breadcrumb.meta.")) metadata[k.slice(16)] = String(v);
221
+ }
222
+ if (Object.keys(metadata).length > 0) result.metadata = metadata;
223
+ return result;
224
+ }
225
+
226
+ //#endregion
227
+ //#region src/exporter.ts
228
+ const MAPPER_NAMESPACES = [
229
+ "ai.",
230
+ "gen_ai.",
231
+ "breadcrumb.",
232
+ "resource.name"
233
+ ];
234
+ const GLOBAL_DROP = new Set(["operation.name"]);
235
+ function hrTimeToISO(hrTime) {
236
+ return new Date(hrTime[0] * 1e3 + hrTime[1] / 1e6).toISOString();
237
+ }
238
+ function spanStatus(span) {
239
+ return span.status.code === SpanStatusCode.ERROR ? "error" : "ok";
240
+ }
241
+ function mergeMappers(span) {
242
+ const aiSdk = mapAiSdk(span);
243
+ const bc = mapBreadcrumb(span);
244
+ return {
245
+ ...aiSdk,
246
+ ...bc,
247
+ metadata: aiSdk.metadata || bc.metadata ? {
248
+ ...aiSdk.metadata,
249
+ ...bc.metadata
250
+ } : void 0
251
+ };
252
+ }
253
+ function passthroughMetadata(span, existing) {
254
+ const extra = {};
255
+ for (const [k, v] of Object.entries(span.attributes)) {
256
+ if (v == null) continue;
257
+ const handled = MAPPER_NAMESPACES.some((ns) => k === ns || k.startsWith(ns));
258
+ if (handled || GLOBAL_DROP.has(k)) continue;
259
+ extra[k] = String(v);
260
+ }
261
+ if (Object.keys(extra).length === 0) return existing || void 0;
262
+ return {
263
+ ...extra,
264
+ ...existing
265
+ };
266
+ }
122
267
  var BreadcrumbSpanExporter = class {
123
- constructor(apiKey, baseUrl) {
268
+ constructor(apiKey, baseUrl, environment) {
124
269
  this.apiKey = apiKey;
125
270
  this.baseUrl = baseUrl;
271
+ this.environment = environment;
126
272
  }
127
273
  export(spans, resultCallback) {
128
274
  this._export(spans).then(() => resultCallback({ code: ExportResultCode.SUCCESS }), () => resultCallback({ code: ExportResultCode.SUCCESS }));
@@ -138,78 +284,40 @@ var BreadcrumbSpanExporter = class {
138
284
  }
139
285
  _mapSpan(span) {
140
286
  const ctx = span.spanContext();
141
- const attrs = span.attributes;
142
- let input;
143
- const bcInput = attrs["breadcrumb.input"];
144
- const aiMessages = attrs["ai.prompt.messages"];
145
- if (bcInput != null) input = typeof bcInput === "string" ? tryJson(bcInput) ?? bcInput : bcInput;
146
- else if (typeof aiMessages === "string") input = tryJson(aiMessages) ?? aiMessages;
147
- let output;
148
- const bcOutput = attrs["breadcrumb.output"];
149
- const aiText = attrs["ai.response.text"];
150
- if (bcOutput != null) output = typeof bcOutput === "string" ? tryJson(bcOutput) ?? bcOutput : bcOutput;
151
- else if (typeof aiText === "string") output = aiText;
152
- const model = strAttr(span, "breadcrumb.model", "ai.model.id", "ai.response.model", "gen_ai.request.model");
153
- const provider = strAttr(span, "breadcrumb.provider", "ai.model.provider", "gen_ai.system");
154
- const input_tokens = intAttr(span, "breadcrumb.input_tokens", "ai.usage.inputTokens", "ai.usage.promptTokens", "gen_ai.usage.input_tokens");
155
- const output_tokens = intAttr(span, "breadcrumb.output_tokens", "ai.usage.outputTokens", "ai.usage.completionTokens", "gen_ai.usage.output_tokens");
156
- let input_cost_usd = floatAttr(span, "breadcrumb.input_cost_usd");
157
- let output_cost_usd = floatAttr(span, "breadcrumb.output_cost_usd");
158
- if (input_cost_usd == null && output_cost_usd == null) {
159
- const cost = extractCost(span);
160
- input_cost_usd = cost.input_cost_usd;
161
- output_cost_usd = cost.output_cost_usd;
162
- }
163
- const metadata = {};
164
- for (const [k, v] of Object.entries(attrs)) {
165
- if (v == null) continue;
166
- if (HANDLED_ATTRS.has(k) || DROP_ATTRS.has(k)) continue;
167
- if (k.startsWith("ai.settings.")) continue;
168
- if (k.startsWith("ai.request.headers.")) continue;
169
- if (k.startsWith("breadcrumb.")) {
170
- if (k.startsWith("breadcrumb.meta.")) metadata[k.slice(16)] = String(v);
171
- continue;
172
- }
173
- if (k.startsWith("ai.telemetry.metadata.")) {
174
- metadata[k.slice(22)] = String(v);
175
- continue;
176
- }
177
- if (k === "ai.response.finishReason") {
178
- metadata["finish_reason"] = String(v);
179
- continue;
180
- }
181
- metadata[k] = String(v);
182
- }
287
+ const mapped = mergeMappers(span);
288
+ const metadata = passthroughMetadata(span, mapped.metadata);
183
289
  return {
184
290
  id: ctx.spanId,
185
291
  trace_id: ctx.traceId,
186
292
  parent_span_id: span.parentSpanId || void 0,
187
- name: span.name,
188
- type: inferSpanType(span),
293
+ name: mapped.name ?? span.name,
294
+ type: mapped.type ?? "custom",
189
295
  start_time: hrTimeToISO(span.startTime),
190
296
  end_time: hrTimeToISO(span.endTime),
191
297
  status: spanStatus(span),
192
298
  status_message: span.status.message || void 0,
193
- input: input !== void 0 ? input : void 0,
194
- output: output !== void 0 ? output : void 0,
195
- provider,
196
- model,
197
- input_tokens,
198
- output_tokens,
199
- input_cost_usd,
200
- output_cost_usd,
201
- metadata: Object.keys(metadata).length > 0 ? metadata : void 0
299
+ input: mapped.input !== void 0 ? mapped.input : void 0,
300
+ output: mapped.output !== void 0 ? mapped.output : void 0,
301
+ provider: mapped.provider,
302
+ model: mapped.model,
303
+ input_tokens: mapped.input_tokens,
304
+ output_tokens: mapped.output_tokens,
305
+ input_cost_usd: mapped.input_cost_usd,
306
+ output_cost_usd: mapped.output_cost_usd,
307
+ metadata: metadata && Object.keys(metadata).length > 0 ? metadata : void 0
202
308
  };
203
309
  }
204
310
  async _sendTrace(span) {
205
311
  const ctx = span.spanContext();
312
+ const mapped = mergeMappers(span);
206
313
  await this._post("/v1/traces", {
207
314
  id: ctx.traceId,
208
- name: span.name,
315
+ name: mapped.name ?? span.name,
209
316
  start_time: hrTimeToISO(span.startTime),
210
317
  end_time: hrTimeToISO(span.endTime),
211
318
  status: spanStatus(span),
212
- status_message: span.status.message || void 0
319
+ status_message: span.status.message || void 0,
320
+ environment: this.environment
213
321
  });
214
322
  }
215
323
  async _post(path, body) {
@@ -235,7 +343,7 @@ var BreadcrumbSpanExporter = class {
235
343
  //#endregion
236
344
  //#region src/index.ts
237
345
  function init(options) {
238
- const exporter = new BreadcrumbSpanExporter(options.apiKey, options.baseUrl);
346
+ const exporter = new BreadcrumbSpanExporter(options.apiKey, options.baseUrl, options.environment);
239
347
  const batchOpts = typeof options.batching === "object" ? options.batching : void 0;
240
348
  const processor = options.batching === false ? new SimpleSpanProcessor(exporter) : new BatchSpanProcessor(exporter, {
241
349
  scheduledDelayMillis: batchOpts?.flushInterval ?? 5e3,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["hrTime: [number, number]","span: ReadableSpan","s: string","apiKey: string","baseUrl: string","spans: ReadableSpan[]","resultCallback: (result: ExportResult) => void","sends: Promise<void>[]","input: unknown","output: unknown","metadata: Record<string, string>","path: string","body: unknown","options: InitOptions","otelSpan: OtelSpan","activeCtx: typeof ROOT_CONTEXT","fn: (span: BreadcrumbSpan) => Promise<T>","bcSpan: BreadcrumbSpan","name: string","options?: SpanOptions","options"],"sources":["../src/exporter.ts","../src/index.ts"],"sourcesContent":["import type { ExportResult } from \"@opentelemetry/core\";\nimport { ExportResultCode } from \"@opentelemetry/core\";\nimport type { SpanExporter, ReadableSpan } from \"@opentelemetry/sdk-trace-base\";\nimport { SpanStatusCode } from \"@opentelemetry/api\";\n\ntype SpanType = \"llm\" | \"tool\" | \"retrieval\" | \"step\" | \"custom\";\n\nconst LLM_SPAN_NAMES = new Set([\n \"ai.generateText\",\n \"ai.generateText.doGenerate\",\n \"ai.streamText\",\n \"ai.streamText.doStream\",\n \"ai.generateObject\",\n \"ai.generateObject.doGenerate\",\n \"ai.generateObject.doStream\",\n]);\n\nconst TOOL_SPAN_NAMES = new Set([\n \"ai.toolCall\",\n \"ai.toolExecution\",\n \"ai.executeToolCall\",\n]);\n\n// Attributes handled explicitly — excluded from metadata pass-through\nconst HANDLED_ATTRS = new Set([\n // Breadcrumb native\n \"breadcrumb.span.type\",\n \"breadcrumb.input\",\n \"breadcrumb.output\",\n \"breadcrumb.model\",\n \"breadcrumb.provider\",\n \"breadcrumb.input_tokens\",\n \"breadcrumb.output_tokens\",\n \"breadcrumb.input_cost_usd\",\n \"breadcrumb.output_cost_usd\",\n // AI SDK - input/output\n \"ai.prompt.messages\",\n \"ai.response.text\",\n // AI SDK - model/provider\n \"ai.model.id\",\n \"ai.model.provider\",\n \"ai.response.model\",\n \"gen_ai.request.model\",\n \"gen_ai.system\",\n // AI SDK - tokens\n \"ai.usage.inputTokens\",\n \"ai.usage.promptTokens\",\n \"ai.usage.outputTokens\",\n \"ai.usage.completionTokens\",\n \"gen_ai.usage.input_tokens\",\n \"gen_ai.usage.output_tokens\",\n // AI SDK - cost source\n \"ai.response.providerMetadata\",\n // AI SDK - finish reason (renamed in metadata)\n \"ai.response.finishReason\",\n]);\n\n// Attributes to drop entirely\nconst DROP_ATTRS = new Set([\n \"operation.name\",\n \"resource.name\",\n \"ai.operationId\",\n \"ai.telemetry.functionId\",\n \"gen_ai.response.finish_reasons\",\n \"gen_ai.response.id\",\n \"gen_ai.response.model\",\n \"ai.response.id\",\n \"ai.response.timestamp\",\n]);\n\nfunction hrTimeToISO(hrTime: [number, number]): string {\n return new Date(hrTime[0] * 1000 + hrTime[1] / 1_000_000).toISOString();\n}\n\nfunction spanStatus(span: ReadableSpan): \"ok\" | \"error\" {\n return span.status.code === SpanStatusCode.ERROR ? \"error\" : \"ok\";\n}\n\nfunction strAttr(span: ReadableSpan, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const v = span.attributes[key];\n if (typeof v === \"string\" && v !== \"\") return v;\n }\n return undefined;\n}\n\nfunction intAttr(span: ReadableSpan, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const v = span.attributes[key];\n if (typeof v === \"number\") return Math.round(v);\n }\n return undefined;\n}\n\nfunction floatAttr(span: ReadableSpan, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const v = span.attributes[key];\n if (typeof v === \"number\") return v;\n }\n return undefined;\n}\n\nfunction tryJson(s: string): unknown {\n try {\n return JSON.parse(s);\n } catch {\n return undefined;\n }\n}\n\nfunction inferSpanType(span: ReadableSpan): SpanType {\n const explicit = span.attributes[\"breadcrumb.span.type\"];\n if (typeof explicit === \"string\" && explicit !== \"\") return explicit as SpanType;\n if (LLM_SPAN_NAMES.has(span.name)) return \"llm\";\n if (TOOL_SPAN_NAMES.has(span.name)) return \"tool\";\n return \"custom\";\n}\n\nfunction extractCost(\n span: ReadableSpan,\n): { input_cost_usd?: number; output_cost_usd?: number } {\n const raw = span.attributes[\"ai.response.providerMetadata\"];\n if (typeof raw !== \"string\") return {};\n try {\n const parsed = tryJson(raw) as Record<string, unknown> | null;\n if (!parsed || typeof parsed !== \"object\") return {};\n for (const providerData of Object.values(parsed)) {\n const usage = (providerData as Record<string, unknown>)?.usage as\n | Record<string, unknown>\n | undefined;\n if (!usage) continue;\n const totalCost = usage[\"cost\"];\n if (typeof totalCost !== \"number\") continue;\n const prompt = typeof usage[\"promptTokens\"] === \"number\" ? usage[\"promptTokens\"] : 0;\n const completion =\n typeof usage[\"completionTokens\"] === \"number\" ? usage[\"completionTokens\"] : 0;\n const total = prompt + completion;\n if (total > 0) {\n return {\n input_cost_usd: totalCost * (prompt / total),\n output_cost_usd: totalCost * (completion / total),\n };\n }\n return { input_cost_usd: totalCost };\n }\n } catch {\n // ignore\n }\n return {};\n}\n\nexport class BreadcrumbSpanExporter implements SpanExporter {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string,\n ) {}\n\n export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): void {\n this._export(spans).then(\n () => resultCallback({ code: ExportResultCode.SUCCESS }),\n () => resultCallback({ code: ExportResultCode.SUCCESS }), // fail silently\n );\n }\n\n private async _export(spans: ReadableSpan[]): Promise<void> {\n try {\n const roots = spans.filter((s) => !s.parentSpanId);\n const spanPayloads = spans.map((s) => this._mapSpan(s));\n\n const sends: Promise<void>[] = roots.map((s) => this._sendTrace(s));\n if (spanPayloads.length > 0) {\n sends.push(this._post(\"/v1/spans\", spanPayloads));\n }\n\n await Promise.all(sends);\n } catch {\n // Fail silently\n }\n }\n\n private _mapSpan(span: ReadableSpan) {\n const ctx = span.spanContext();\n const attrs = span.attributes;\n\n // ── Input ────────────────────────────────────────────────────────────────\n let input: unknown;\n const bcInput = attrs[\"breadcrumb.input\"];\n const aiMessages = attrs[\"ai.prompt.messages\"];\n if (bcInput != null) {\n input = typeof bcInput === \"string\" ? (tryJson(bcInput) ?? bcInput) : bcInput;\n } else if (typeof aiMessages === \"string\") {\n input = tryJson(aiMessages) ?? aiMessages;\n }\n\n // ── Output ───────────────────────────────────────────────────────────────\n let output: unknown;\n const bcOutput = attrs[\"breadcrumb.output\"];\n const aiText = attrs[\"ai.response.text\"];\n if (bcOutput != null) {\n output = typeof bcOutput === \"string\" ? (tryJson(bcOutput) ?? bcOutput) : bcOutput;\n } else if (typeof aiText === \"string\") {\n output = aiText;\n }\n\n // ── Model / provider ─────────────────────────────────────────────────────\n const model = strAttr(span, \"breadcrumb.model\", \"ai.model.id\", \"ai.response.model\", \"gen_ai.request.model\");\n const provider = strAttr(span, \"breadcrumb.provider\", \"ai.model.provider\", \"gen_ai.system\");\n\n // ── Tokens ───────────────────────────────────────────────────────────────\n const input_tokens = intAttr(\n span,\n \"breadcrumb.input_tokens\",\n \"ai.usage.inputTokens\",\n \"ai.usage.promptTokens\",\n \"gen_ai.usage.input_tokens\",\n );\n const output_tokens = intAttr(\n span,\n \"breadcrumb.output_tokens\",\n \"ai.usage.outputTokens\",\n \"ai.usage.completionTokens\",\n \"gen_ai.usage.output_tokens\",\n );\n\n // ── Cost ─────────────────────────────────────────────────────────────────\n let input_cost_usd = floatAttr(span, \"breadcrumb.input_cost_usd\");\n let output_cost_usd = floatAttr(span, \"breadcrumb.output_cost_usd\");\n if (input_cost_usd == null && output_cost_usd == null) {\n const cost = extractCost(span);\n input_cost_usd = cost.input_cost_usd;\n output_cost_usd = cost.output_cost_usd;\n }\n\n // ── Metadata ─────────────────────────────────────────────────────────────\n const metadata: Record<string, string> = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (v == null) continue;\n if (HANDLED_ATTRS.has(k) || DROP_ATTRS.has(k)) continue;\n if (k.startsWith(\"ai.settings.\")) continue;\n if (k.startsWith(\"ai.request.headers.\")) continue;\n if (k.startsWith(\"breadcrumb.\")) {\n // breadcrumb.meta.* keys from span.set({ metadata: {...} })\n if (k.startsWith(\"breadcrumb.meta.\")) {\n metadata[k.slice(\"breadcrumb.meta.\".length)] = String(v);\n }\n continue;\n }\n if (k.startsWith(\"ai.telemetry.metadata.\")) {\n metadata[k.slice(\"ai.telemetry.metadata.\".length)] = String(v);\n continue;\n }\n if (k === \"ai.response.finishReason\") {\n metadata[\"finish_reason\"] = String(v);\n continue;\n }\n metadata[k] = String(v);\n }\n\n return {\n id: ctx.spanId,\n trace_id: ctx.traceId,\n parent_span_id: span.parentSpanId || undefined,\n name: span.name,\n type: inferSpanType(span),\n start_time: hrTimeToISO(span.startTime),\n end_time: hrTimeToISO(span.endTime),\n status: spanStatus(span),\n status_message: span.status.message || undefined,\n input: input !== undefined ? input : undefined,\n output: output !== undefined ? output : undefined,\n provider,\n model,\n input_tokens,\n output_tokens,\n input_cost_usd,\n output_cost_usd,\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n };\n }\n\n private async _sendTrace(span: ReadableSpan): Promise<void> {\n const ctx = span.spanContext();\n await this._post(\"/v1/traces\", {\n id: ctx.traceId,\n name: span.name,\n start_time: hrTimeToISO(span.startTime),\n end_time: hrTimeToISO(span.endTime),\n status: spanStatus(span),\n status_message: span.status.message || undefined,\n });\n }\n\n private async _post(path: string, body: unknown): Promise<void> {\n try {\n await fetch(`${this.baseUrl}${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n } catch {\n // Fail silently — backend unreachable\n }\n }\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n","import {\n context,\n trace,\n ROOT_CONTEXT,\n SpanStatusCode,\n type Span as OtelSpan,\n} from \"@opentelemetry/api\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport {\n SimpleSpanProcessor,\n BatchSpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { BreadcrumbSpanExporter } from \"./exporter.js\";\nimport type { Breadcrumb, BreadcrumbSpan, SpanOptions } from \"./types.js\";\n\nexport type { Breadcrumb, BreadcrumbSpan, SpanOptions };\n\nexport interface InitOptions {\n apiKey: string;\n baseUrl: string;\n batching?:\n | false\n | {\n flushInterval?: number;\n maxBatchSize?: number;\n };\n}\n\nexport function init(options: InitOptions): Breadcrumb {\n const exporter = new BreadcrumbSpanExporter(options.apiKey, options.baseUrl);\n\n const batchOpts =\n typeof options.batching === \"object\" ? options.batching : undefined;\n\n const processor =\n options.batching === false\n ? new SimpleSpanProcessor(exporter)\n : new BatchSpanProcessor(exporter, {\n scheduledDelayMillis: batchOpts?.flushInterval ?? 5000,\n maxExportBatchSize: batchOpts?.maxBatchSize ?? 100,\n });\n\n const provider = new NodeTracerProvider({ spanProcessors: [processor] });\n provider.register();\n\n process.once(\"beforeExit\", async () => {\n await provider.shutdown().catch(() => {});\n });\n\n const tracer = provider.getTracer(\"@breadcrumb-sdk/core\");\n\n function runSpan<T>(\n otelSpan: OtelSpan,\n activeCtx: typeof ROOT_CONTEXT,\n fn: (span: BreadcrumbSpan) => Promise<T>,\n ): Promise<T> {\n const bcSpan: BreadcrumbSpan = {\n set(data) {\n const { metadata, ...semantic } = data;\n for (const [key, value] of Object.entries(semantic)) {\n if (value == null) continue;\n const attrKey = `breadcrumb.${key}`;\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n otelSpan.setAttribute(attrKey, value);\n } else {\n otelSpan.setAttribute(attrKey, JSON.stringify(value));\n }\n }\n if (metadata) {\n for (const [key, value] of Object.entries(metadata)) {\n if (value == null) continue;\n otelSpan.setAttribute(`breadcrumb.meta.${key}`, value);\n }\n }\n },\n };\n\n return context.with(trace.setSpan(activeCtx, otelSpan), async () => {\n try {\n const result = await fn(bcSpan);\n otelSpan.setStatus({ code: SpanStatusCode.OK });\n otelSpan.end();\n return result;\n } catch (err) {\n otelSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n otelSpan.end();\n throw err;\n }\n });\n }\n\n return {\n trace<T>(\n name: string,\n fn: (span: BreadcrumbSpan) => Promise<T>,\n ): Promise<T> {\n const otelSpan = tracer.startSpan(name, {}, ROOT_CONTEXT);\n return runSpan(otelSpan, ROOT_CONTEXT, fn);\n },\n\n span<T>(\n name: string,\n fn: (span: BreadcrumbSpan) => Promise<T>,\n options?: SpanOptions,\n ): Promise<T> {\n const activeCtx = context.active();\n const otelSpan = tracer.startSpan(name, {}, activeCtx);\n if (options?.type) {\n otelSpan.setAttribute(\"breadcrumb.span.type\", options.type);\n }\n return runSpan(otelSpan, activeCtx, fn);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["s: string","attrs: Attributes","spanName: string","raw: string","span: ReadableSpan","result: Partial<MappedSpanData>","messages: { role: string; content: unknown }[]","tc: Record<string, unknown>","metadata: Record<string, string>","span: ReadableSpan","result: Partial<MappedSpanData>","metadata: Record<string, string>","hrTime: [number, number]","span: ReadableSpan","existing: Record<string, string> | undefined","extra: Record<string, string>","apiKey: string","baseUrl: string","environment?: string","spans: ReadableSpan[]","resultCallback: (result: ExportResult) => void","sends: Promise<void>[]","path: string","body: unknown","options: InitOptions","otelSpan: OtelSpan","activeCtx: typeof ROOT_CONTEXT","fn: (span: BreadcrumbSpan) => Promise<T>","bcSpan: BreadcrumbSpan","name: string","options?: SpanOptions","options"],"sources":["../src/mappers/utils.ts","../src/mappers/ai-sdk.ts","../src/mappers/breadcrumb.ts","../src/exporter.ts","../src/index.ts"],"sourcesContent":["import type { Attributes } from \"@opentelemetry/api\";\n\nexport function tryJson(s: string): unknown {\n try {\n return JSON.parse(s);\n } catch {\n return undefined;\n }\n}\n\nexport function strAttr(attrs: Attributes, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const v = attrs[key];\n if (typeof v === \"string\" && v !== \"\") return v;\n }\n return undefined;\n}\n\nexport function intAttr(attrs: Attributes, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const v = attrs[key];\n if (typeof v === \"number\") return Math.round(v);\n }\n return undefined;\n}\n\nexport function floatAttr(attrs: Attributes, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const v = attrs[key];\n if (typeof v === \"number\") return v;\n }\n return undefined;\n}\n","import type { ReadableSpan } from \"@opentelemetry/sdk-trace-base\";\nimport type { MappedSpanData, SpanType } from \"./types.js\";\nimport { tryJson, strAttr, intAttr, floatAttr } from \"./utils.js\";\n\nconst LLM_SPAN_NAMES = new Set([\n \"ai.generateText\",\n \"ai.generateText.doGenerate\",\n \"ai.streamText\",\n \"ai.streamText.doStream\",\n \"ai.generateObject\",\n \"ai.generateObject.doGenerate\",\n \"ai.generateObject.doStream\",\n]);\n\nconst TOOL_SPAN_NAMES = new Set([\n \"ai.toolCall\",\n \"ai.toolExecution\",\n \"ai.executeToolCall\",\n]);\n\n// Attributes extracted into first-class fields — excluded from metadata pass-through\nconst HANDLED = new Set([\n \"resource.name\",\n \"ai.prompt\",\n \"ai.prompt.messages\",\n \"ai.response.text\",\n \"ai.response.toolCalls\",\n \"ai.toolCall.name\",\n \"ai.toolCall.args\",\n \"ai.toolCall.result\",\n \"ai.model.id\",\n \"ai.model.provider\",\n \"ai.response.model\",\n \"gen_ai.request.model\",\n \"gen_ai.system\",\n \"ai.usage.inputTokens\",\n \"ai.usage.promptTokens\",\n \"ai.usage.outputTokens\",\n \"ai.usage.completionTokens\",\n \"gen_ai.usage.input_tokens\",\n \"gen_ai.usage.output_tokens\",\n \"ai.response.providerMetadata\",\n \"ai.response.finishReason\",\n // Metadata with prefix stripped — handled in the metadata loop below\n // (ai.telemetry.metadata.*)\n]);\n\n// Attributes to drop entirely (not extracted, not in metadata)\nconst DROP = new Set([\n \"operation.name\",\n \"ai.operationId\",\n \"ai.telemetry.functionId\",\n \"gen_ai.response.finish_reasons\",\n \"gen_ai.response.id\",\n \"gen_ai.response.model\",\n \"ai.response.id\",\n \"ai.response.timestamp\",\n \"ai.prompt.tools\",\n \"ai.prompt.toolChoice\",\n \"ai.toolCall.id\",\n]);\n\nfunction inferType(spanName: string): SpanType {\n if (LLM_SPAN_NAMES.has(spanName)) return \"llm\";\n if (TOOL_SPAN_NAMES.has(spanName)) return \"tool\";\n return \"custom\";\n}\n\nfunction extractCost(\n raw: string,\n): { input_cost_usd?: number; output_cost_usd?: number } {\n try {\n const parsed = tryJson(raw) as Record<string, unknown> | null;\n if (!parsed || typeof parsed !== \"object\") return {};\n for (const providerData of Object.values(parsed)) {\n const usage = (providerData as Record<string, unknown>)?.usage as\n | Record<string, unknown>\n | undefined;\n if (!usage) continue;\n const totalCost = usage[\"cost\"];\n if (typeof totalCost !== \"number\") continue;\n const prompt = typeof usage[\"promptTokens\"] === \"number\" ? usage[\"promptTokens\"] : 0;\n const completion =\n typeof usage[\"completionTokens\"] === \"number\" ? usage[\"completionTokens\"] : 0;\n const total = prompt + completion;\n if (total > 0) {\n return {\n input_cost_usd: totalCost * (prompt / total),\n output_cost_usd: totalCost * (completion / total),\n };\n }\n return { input_cost_usd: totalCost };\n }\n } catch {\n // ignore\n }\n return {};\n}\n\nexport function mapAiSdk(span: ReadableSpan): Partial<MappedSpanData> {\n const attrs = span.attributes;\n const result: Partial<MappedSpanData> = {};\n\n // ── Name ─────────────────────────────────────────────────────────────────\n const toolCallName = strAttr(attrs, \"ai.toolCall.name\");\n const resourceName = strAttr(attrs, \"resource.name\");\n if (toolCallName) result.name = toolCallName;\n else if (resourceName) result.name = resourceName;\n\n // ── Type ─────────────────────────────────────────────────────────────────\n result.type = inferType(span.name);\n\n // ── Input ─────────────────────────────────────────────────────────────────\n const toolArgs = attrs[\"ai.toolCall.args\"];\n const aiMessages = attrs[\"ai.prompt.messages\"];\n const aiPrompt = attrs[\"ai.prompt\"];\n\n if (typeof toolArgs === \"string\") {\n result.input = tryJson(toolArgs) ?? toolArgs;\n } else if (typeof aiMessages === \"string\") {\n result.input = tryJson(aiMessages) ?? aiMessages;\n } else if (typeof aiPrompt === \"string\") {\n const parsed = tryJson(aiPrompt) as Record<string, unknown> | null;\n if (parsed && typeof parsed === \"object\") {\n const messages: { role: string; content: unknown }[] = [];\n if (parsed[\"system\"]) messages.push({ role: \"system\", content: parsed[\"system\"] });\n if (Array.isArray(parsed[\"messages\"])) {\n messages.push(...(parsed[\"messages\"] as { role: string; content: unknown }[]));\n } else if (parsed[\"prompt\"] !== undefined) {\n messages.push({ role: \"user\", content: parsed[\"prompt\"] });\n }\n result.input = messages.length > 0 ? messages : parsed;\n } else {\n result.input = aiPrompt;\n }\n }\n\n // ── Output ────────────────────────────────────────────────────────────────\n const toolResult = attrs[\"ai.toolCall.result\"];\n const aiText = attrs[\"ai.response.text\"];\n const aiToolCalls = attrs[\"ai.response.toolCalls\"];\n\n if (typeof toolResult === \"string\") {\n result.output = tryJson(toolResult) ?? toolResult;\n } else if (typeof aiText === \"string\") {\n result.output = aiText;\n } else if (typeof aiToolCalls === \"string\") {\n const parsed = tryJson(aiToolCalls);\n if (Array.isArray(parsed)) {\n result.output = parsed.map((tc: Record<string, unknown>) => ({\n ...tc,\n input:\n typeof tc[\"input\"] === \"string\"\n ? (tryJson(tc[\"input\"]) ?? tc[\"input\"])\n : tc[\"input\"],\n }));\n } else {\n result.output = parsed ?? aiToolCalls;\n }\n }\n\n // ── Model / provider ──────────────────────────────────────────────────────\n const model = strAttr(attrs, \"ai.model.id\", \"ai.response.model\", \"gen_ai.request.model\");\n if (model) result.model = model;\n\n const provider = strAttr(attrs, \"ai.model.provider\", \"gen_ai.system\");\n if (provider) result.provider = provider;\n\n // ── Tokens ────────────────────────────────────────────────────────────────\n const input_tokens = intAttr(\n attrs,\n \"ai.usage.inputTokens\",\n \"ai.usage.promptTokens\",\n \"gen_ai.usage.input_tokens\",\n );\n if (input_tokens != null) result.input_tokens = input_tokens;\n\n const output_tokens = intAttr(\n attrs,\n \"ai.usage.outputTokens\",\n \"ai.usage.completionTokens\",\n \"gen_ai.usage.output_tokens\",\n );\n if (output_tokens != null) result.output_tokens = output_tokens;\n\n // ── Cost ──────────────────────────────────────────────────────────────────\n const providerMeta = attrs[\"ai.response.providerMetadata\"];\n if (typeof providerMeta === \"string\") {\n const cost = extractCost(providerMeta);\n if (cost.input_cost_usd != null) result.input_cost_usd = cost.input_cost_usd;\n if (cost.output_cost_usd != null) result.output_cost_usd = cost.output_cost_usd;\n }\n\n // ── Cost from explicit breadcrumb attrs (none here — handled by mapBreadcrumb) ─\n\n // ── Metadata pass-through ─────────────────────────────────────────────────\n // Collect unrecognised ai.* / gen_ai.* attrs as metadata.\n const metadata: Record<string, string> = {};\n\n // ai.response.finishReason is in HANDLED (excluded from pass-through) but\n // still needs to appear in metadata under a clean key.\n const finishReason = attrs[\"ai.response.finishReason\"];\n if (typeof finishReason === \"string\" && finishReason !== \"\") {\n metadata[\"finish_reason\"] = finishReason;\n }\n\n for (const [k, v] of Object.entries(attrs)) {\n if (v == null) continue;\n if (HANDLED.has(k) || DROP.has(k)) continue;\n if (k.startsWith(\"ai.settings.\") || k.startsWith(\"ai.request.headers.\")) continue;\n\n if (k.startsWith(\"ai.telemetry.metadata.\")) {\n metadata[k.slice(\"ai.telemetry.metadata.\".length)] = String(v);\n continue;\n }\n // Only pass through ai.* and gen_ai.* here; other namespaces are handled\n // by the exporter's own pass-through for truly unknown attributes.\n if (k.startsWith(\"ai.\") || k.startsWith(\"gen_ai.\")) {\n metadata[k] = String(v);\n }\n }\n\n if (Object.keys(metadata).length > 0) result.metadata = metadata;\n\n return result;\n}\n","import type { ReadableSpan } from \"@opentelemetry/sdk-trace-base\";\nimport type { MappedSpanData, SpanType } from \"./types.js\";\nimport { tryJson, strAttr, intAttr, floatAttr } from \"./utils.js\";\n\nconst VALID_TYPES = new Set<string>([\"llm\", \"tool\", \"retrieval\", \"step\", \"custom\"]);\n\nexport function mapBreadcrumb(span: ReadableSpan): Partial<MappedSpanData> {\n const attrs = span.attributes;\n const result: Partial<MappedSpanData> = {};\n\n // ── Type override ─────────────────────────────────────────────────────────\n const explicitType = attrs[\"breadcrumb.span.type\"];\n if (typeof explicitType === \"string\" && VALID_TYPES.has(explicitType)) {\n result.type = explicitType as SpanType;\n }\n\n // ── Input ─────────────────────────────────────────────────────────────────\n const rawInput = attrs[\"breadcrumb.input\"];\n if (rawInput != null) {\n result.input =\n typeof rawInput === \"string\" ? (tryJson(rawInput) ?? rawInput) : rawInput;\n }\n\n // ── Output ────────────────────────────────────────────────────────────────\n const rawOutput = attrs[\"breadcrumb.output\"];\n if (rawOutput != null) {\n result.output =\n typeof rawOutput === \"string\" ? (tryJson(rawOutput) ?? rawOutput) : rawOutput;\n }\n\n // ── Model / provider ──────────────────────────────────────────────────────\n const model = strAttr(attrs, \"breadcrumb.model\");\n if (model) result.model = model;\n\n const provider = strAttr(attrs, \"breadcrumb.provider\");\n if (provider) result.provider = provider;\n\n // ── Tokens ────────────────────────────────────────────────────────────────\n const input_tokens = intAttr(attrs, \"breadcrumb.input_tokens\");\n if (input_tokens != null) result.input_tokens = input_tokens;\n\n const output_tokens = intAttr(attrs, \"breadcrumb.output_tokens\");\n if (output_tokens != null) result.output_tokens = output_tokens;\n\n // ── Cost ──────────────────────────────────────────────────────────────────\n const input_cost_usd = floatAttr(attrs, \"breadcrumb.input_cost_usd\");\n if (input_cost_usd != null) result.input_cost_usd = input_cost_usd;\n\n const output_cost_usd = floatAttr(attrs, \"breadcrumb.output_cost_usd\");\n if (output_cost_usd != null) result.output_cost_usd = output_cost_usd;\n\n // ── Metadata ──────────────────────────────────────────────────────────────\n const metadata: Record<string, string> = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (v == null) continue;\n if (k.startsWith(\"breadcrumb.meta.\")) {\n metadata[k.slice(\"breadcrumb.meta.\".length)] = String(v);\n }\n }\n if (Object.keys(metadata).length > 0) result.metadata = metadata;\n\n return result;\n}\n","import type { ExportResult } from \"@opentelemetry/core\";\nimport { ExportResultCode } from \"@opentelemetry/core\";\nimport type { SpanExporter, ReadableSpan } from \"@opentelemetry/sdk-trace-base\";\nimport { SpanStatusCode } from \"@opentelemetry/api\";\nimport { mapAiSdk } from \"./mappers/ai-sdk.js\";\nimport { mapBreadcrumb } from \"./mappers/breadcrumb.js\";\nimport type { MappedSpanData } from \"./mappers/types.js\";\n\n// Attribute namespaces fully handled by mappers — excluded from the generic\n// pass-through that collects remaining unknown attrs into metadata.\nconst MAPPER_NAMESPACES = [\"ai.\", \"gen_ai.\", \"breadcrumb.\", \"resource.name\"];\n\n\n// Non-namespaced keys that should be dropped entirely (not in metadata).\n// These come from AI SDK / OTel conventions and are not user-meaningful.\nconst GLOBAL_DROP = new Set([\"operation.name\"]);\n\nfunction hrTimeToISO(hrTime: [number, number]): string {\n return new Date(hrTime[0] * 1000 + hrTime[1] / 1_000_000).toISOString();\n}\n\nfunction spanStatus(span: ReadableSpan): \"ok\" | \"error\" {\n return span.status.code === SpanStatusCode.ERROR ? \"error\" : \"ok\";\n}\n\nfunction mergeMappers(span: ReadableSpan): MappedSpanData {\n const aiSdk = mapAiSdk(span);\n const bc = mapBreadcrumb(span);\n\n // breadcrumb (user-set) wins for all scalar fields; metadata is merged with\n // breadcrumb keys overriding matching AI SDK keys.\n return {\n ...aiSdk,\n ...bc,\n metadata:\n aiSdk.metadata || bc.metadata\n ? { ...aiSdk.metadata, ...bc.metadata }\n : undefined,\n };\n}\n\n// Collect attributes not handled by any mapper (unknown namespaces like\n// \"custom.key\", bare keys like \"score\") into metadata as strings.\nfunction passthroughMetadata(\n span: ReadableSpan,\n existing: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n const extra: Record<string, string> = {};\n for (const [k, v] of Object.entries(span.attributes)) {\n if (v == null) continue;\n const handled = MAPPER_NAMESPACES.some((ns) =>\n k === ns || k.startsWith(ns),\n );\n if (handled || GLOBAL_DROP.has(k)) continue;\n extra[k] = String(v);\n }\n if (Object.keys(extra).length === 0) return existing || undefined;\n return { ...extra, ...existing }; // existing (breadcrumb) still wins\n}\n\nexport class BreadcrumbSpanExporter implements SpanExporter {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string,\n private readonly environment?: string,\n ) {}\n\n export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): void {\n this._export(spans).then(\n () => resultCallback({ code: ExportResultCode.SUCCESS }),\n () => resultCallback({ code: ExportResultCode.SUCCESS }), // fail silently\n );\n }\n\n private async _export(spans: ReadableSpan[]): Promise<void> {\n try {\n const roots = spans.filter((s) => !s.parentSpanId);\n const spanPayloads = spans.map((s) => this._mapSpan(s));\n\n const sends: Promise<void>[] = roots.map((s) => this._sendTrace(s));\n if (spanPayloads.length > 0) {\n sends.push(this._post(\"/v1/spans\", spanPayloads));\n }\n\n await Promise.all(sends);\n } catch {\n // Fail silently\n }\n }\n\n private _mapSpan(span: ReadableSpan) {\n const ctx = span.spanContext();\n const mapped = mergeMappers(span);\n const metadata = passthroughMetadata(span, mapped.metadata);\n\n return {\n id: ctx.spanId,\n trace_id: ctx.traceId,\n parent_span_id: span.parentSpanId || undefined,\n name: mapped.name ?? span.name,\n type: mapped.type ?? \"custom\",\n start_time: hrTimeToISO(span.startTime),\n end_time: hrTimeToISO(span.endTime),\n status: spanStatus(span),\n status_message: span.status.message || undefined,\n input: mapped.input !== undefined ? mapped.input : undefined,\n output: mapped.output !== undefined ? mapped.output : undefined,\n provider: mapped.provider,\n model: mapped.model,\n input_tokens: mapped.input_tokens,\n output_tokens: mapped.output_tokens,\n input_cost_usd: mapped.input_cost_usd,\n output_cost_usd: mapped.output_cost_usd,\n metadata: metadata && Object.keys(metadata).length > 0 ? metadata : undefined,\n };\n }\n\n private async _sendTrace(span: ReadableSpan): Promise<void> {\n const ctx = span.spanContext();\n const mapped = mergeMappers(span);\n await this._post(\"/v1/traces\", {\n id: ctx.traceId,\n name: mapped.name ?? span.name,\n start_time: hrTimeToISO(span.startTime),\n end_time: hrTimeToISO(span.endTime),\n status: spanStatus(span),\n status_message: span.status.message || undefined,\n environment: this.environment,\n });\n }\n\n private async _post(path: string, body: unknown): Promise<void> {\n try {\n await fetch(`${this.baseUrl}${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n } catch {\n // Fail silently — backend unreachable\n }\n }\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n","import {\n context,\n trace,\n ROOT_CONTEXT,\n SpanStatusCode,\n type Span as OtelSpan,\n} from \"@opentelemetry/api\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport {\n SimpleSpanProcessor,\n BatchSpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { BreadcrumbSpanExporter } from \"./exporter.js\";\nimport type { Breadcrumb, BreadcrumbSpan, SpanOptions, SpanData, Message } from \"./types.js\";\n\nexport type { Breadcrumb, BreadcrumbSpan, SpanOptions, SpanData, Message };\n\nexport interface InitOptions {\n apiKey: string;\n baseUrl: string;\n environment?: string;\n batching?:\n | false\n | {\n flushInterval?: number;\n maxBatchSize?: number;\n };\n}\n\nexport function init(options: InitOptions): Breadcrumb {\n const exporter = new BreadcrumbSpanExporter(\n options.apiKey,\n options.baseUrl,\n options.environment,\n );\n\n const batchOpts =\n typeof options.batching === \"object\" ? options.batching : undefined;\n\n const processor =\n options.batching === false\n ? new SimpleSpanProcessor(exporter)\n : new BatchSpanProcessor(exporter, {\n scheduledDelayMillis: batchOpts?.flushInterval ?? 5000,\n maxExportBatchSize: batchOpts?.maxBatchSize ?? 100,\n });\n\n const provider = new NodeTracerProvider({ spanProcessors: [processor] });\n provider.register();\n\n process.once(\"beforeExit\", async () => {\n await provider.shutdown().catch(() => {});\n });\n\n const tracer = provider.getTracer(\"@breadcrumb-sdk/core\");\n\n function runSpan<T>(\n otelSpan: OtelSpan,\n activeCtx: typeof ROOT_CONTEXT,\n fn: (span: BreadcrumbSpan) => Promise<T>,\n ): Promise<T> {\n const bcSpan: BreadcrumbSpan = {\n set(data) {\n const { metadata, ...semantic } = data;\n for (const [key, value] of Object.entries(semantic)) {\n if (value == null) continue;\n const attrKey = `breadcrumb.${key}`;\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n otelSpan.setAttribute(attrKey, value);\n } else {\n otelSpan.setAttribute(attrKey, JSON.stringify(value));\n }\n }\n if (metadata) {\n for (const [key, value] of Object.entries(metadata)) {\n if (value == null) continue;\n otelSpan.setAttribute(`breadcrumb.meta.${key}`, value);\n }\n }\n },\n };\n\n return context.with(trace.setSpan(activeCtx, otelSpan), async () => {\n try {\n const result = await fn(bcSpan);\n otelSpan.setStatus({ code: SpanStatusCode.OK });\n otelSpan.end();\n return result;\n } catch (err) {\n otelSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n otelSpan.end();\n throw err;\n }\n });\n }\n\n return {\n trace<T>(\n name: string,\n fn: (span: BreadcrumbSpan) => Promise<T>,\n ): Promise<T> {\n const otelSpan = tracer.startSpan(name, {}, ROOT_CONTEXT);\n return runSpan(otelSpan, ROOT_CONTEXT, fn);\n },\n\n span<T>(\n name: string,\n fn: (span: BreadcrumbSpan) => Promise<T>,\n options?: SpanOptions,\n ): Promise<T> {\n const activeCtx = context.active();\n const otelSpan = tracer.startSpan(name, {}, activeCtx);\n if (options?.type) {\n otelSpan.setAttribute(\"breadcrumb.span.type\", options.type);\n }\n return runSpan(otelSpan, activeCtx, fn);\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breadcrumb-sdk/core",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"