@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/README.md CHANGED
@@ -16,6 +16,7 @@ import { init } from "@breadcrumb-sdk/core";
16
16
  const bc = init({
17
17
  apiKey: "bc_...",
18
18
  baseUrl: "https://your-breadcrumb-instance.com",
19
+ environment: "production",
19
20
  });
20
21
 
21
22
  const answer = await bc.trace("answer-question", async (root) => {
@@ -54,6 +55,7 @@ Call once at startup. Returns a `bc` instance you use to create traces and spans
54
55
  const bc = init({
55
56
  apiKey: string,
56
57
  baseUrl: string,
58
+ environment?: string, // e.g. "production", "staging", "development"
57
59
  batching?: false | {
58
60
  flushInterval?: number, // ms between sends (default: 5000)
59
61
  maxBatchSize?: number, // spans per send (default: 100)
@@ -61,6 +63,8 @@ const bc = init({
61
63
  })
62
64
  ```
63
65
 
66
+ Set `environment` once at init time to attach it to every root trace created by this SDK instance. This powers environment filtering in the Breadcrumb UI.
67
+
64
68
  Set `batching: false` to send each span as it finishes. The default batches them. Either way, everything is flushed before the process exits.
65
69
 
66
70
  ---
@@ -123,17 +127,31 @@ span.set({
123
127
  output_cost_usd: 0.00087,
124
128
  metadata: {
125
129
  score: 0.95,
126
- environment: "production",
130
+ region: "eu-central-1",
127
131
  },
128
132
  });
129
133
  ```
130
134
 
131
135
  All fields are optional. `null` and `undefined` are ignored.
132
136
 
137
+ For `input`, passing a `Message[]` array renders the conversation with role labels in the UI — the same way AI SDK spans appear:
138
+
139
+ ```ts
140
+ import type { Message } from "@breadcrumb-sdk/core";
141
+
142
+ span.set({
143
+ input: [
144
+ { role: "system", content: "You are a helpful assistant." },
145
+ { role: "user", content: "What is TypeScript?" },
146
+ ] satisfies Message[],
147
+ output: "TypeScript is a typed superset of JavaScript.",
148
+ });
149
+ ```
150
+
133
151
  | Field | Type | Description |
134
152
  |-------|------|-------------|
135
- | `input` | `unknown` | Input passed to this step |
136
- | `output` | `unknown` | Output produced by this step |
153
+ | `input` | `string \| Message[] \| object` | Input passed to this step |
154
+ | `output` | `string \| object` | Output produced by this step |
137
155
  | `model` | `string` | Model name, e.g. `"gpt-4o"` |
138
156
  | `provider` | `string` | Provider, e.g. `"openai"` |
139
157
  | `input_tokens` | `number` | Input token count |
@@ -141,3 +159,7 @@ All fields are optional. `null` and `undefined` are ignored.
141
159
  | `input_cost_usd` | `number` | Input cost in USD |
142
160
  | `output_cost_usd` | `number` | Output cost in USD |
143
161
  | `metadata` | `Record<string, string \| number \| boolean>` | Any extra data |
162
+
163
+ ## License
164
+
165
+ AGPL-3.0 — see [LICENSE](./LICENSE) for details.
package/dist/index.cjs CHANGED
@@ -27,7 +27,38 @@ const __opentelemetry_sdk_trace_node = __toESM(require("@opentelemetry/sdk-trace
27
27
  const __opentelemetry_sdk_trace_base = __toESM(require("@opentelemetry/sdk-trace-base"));
28
28
  const __opentelemetry_core = __toESM(require("@opentelemetry/core"));
29
29
 
30
- //#region src/exporter.ts
30
+ //#region src/mappers/utils.ts
31
+ function tryJson(s) {
32
+ try {
33
+ return JSON.parse(s);
34
+ } catch {
35
+ return void 0;
36
+ }
37
+ }
38
+ function strAttr(attrs, ...keys) {
39
+ for (const key of keys) {
40
+ const v = attrs[key];
41
+ if (typeof v === "string" && v !== "") return v;
42
+ }
43
+ return void 0;
44
+ }
45
+ function intAttr(attrs, ...keys) {
46
+ for (const key of keys) {
47
+ const v = attrs[key];
48
+ if (typeof v === "number") return Math.round(v);
49
+ }
50
+ return void 0;
51
+ }
52
+ function floatAttr(attrs, ...keys) {
53
+ for (const key of keys) {
54
+ const v = attrs[key];
55
+ if (typeof v === "number") return v;
56
+ }
57
+ return void 0;
58
+ }
59
+
60
+ //#endregion
61
+ //#region src/mappers/ai-sdk.ts
31
62
  const LLM_SPAN_NAMES = new Set([
32
63
  "ai.generateText",
33
64
  "ai.generateText.doGenerate",
@@ -42,18 +73,15 @@ const TOOL_SPAN_NAMES = new Set([
42
73
  "ai.toolExecution",
43
74
  "ai.executeToolCall"
44
75
  ]);
45
- const HANDLED_ATTRS = new Set([
46
- "breadcrumb.span.type",
47
- "breadcrumb.input",
48
- "breadcrumb.output",
49
- "breadcrumb.model",
50
- "breadcrumb.provider",
51
- "breadcrumb.input_tokens",
52
- "breadcrumb.output_tokens",
53
- "breadcrumb.input_cost_usd",
54
- "breadcrumb.output_cost_usd",
76
+ const HANDLED = new Set([
77
+ "resource.name",
78
+ "ai.prompt",
55
79
  "ai.prompt.messages",
56
80
  "ai.response.text",
81
+ "ai.response.toolCalls",
82
+ "ai.toolCall.name",
83
+ "ai.toolCall.args",
84
+ "ai.toolCall.result",
57
85
  "ai.model.id",
58
86
  "ai.model.provider",
59
87
  "ai.response.model",
@@ -68,61 +96,25 @@ const HANDLED_ATTRS = new Set([
68
96
  "ai.response.providerMetadata",
69
97
  "ai.response.finishReason"
70
98
  ]);
71
- const DROP_ATTRS = new Set([
99
+ const DROP = new Set([
72
100
  "operation.name",
73
- "resource.name",
74
101
  "ai.operationId",
75
102
  "ai.telemetry.functionId",
76
103
  "gen_ai.response.finish_reasons",
77
104
  "gen_ai.response.id",
78
105
  "gen_ai.response.model",
79
106
  "ai.response.id",
80
- "ai.response.timestamp"
107
+ "ai.response.timestamp",
108
+ "ai.prompt.tools",
109
+ "ai.prompt.toolChoice",
110
+ "ai.toolCall.id"
81
111
  ]);
82
- function hrTimeToISO(hrTime) {
83
- return new Date(hrTime[0] * 1e3 + hrTime[1] / 1e6).toISOString();
84
- }
85
- function spanStatus(span) {
86
- return span.status.code === __opentelemetry_api.SpanStatusCode.ERROR ? "error" : "ok";
87
- }
88
- function strAttr(span, ...keys) {
89
- for (const key of keys) {
90
- const v = span.attributes[key];
91
- if (typeof v === "string" && v !== "") return v;
92
- }
93
- return void 0;
94
- }
95
- function intAttr(span, ...keys) {
96
- for (const key of keys) {
97
- const v = span.attributes[key];
98
- if (typeof v === "number") return Math.round(v);
99
- }
100
- return void 0;
101
- }
102
- function floatAttr(span, ...keys) {
103
- for (const key of keys) {
104
- const v = span.attributes[key];
105
- if (typeof v === "number") return v;
106
- }
107
- return void 0;
108
- }
109
- function tryJson(s) {
110
- try {
111
- return JSON.parse(s);
112
- } catch {
113
- return void 0;
114
- }
115
- }
116
- function inferSpanType(span) {
117
- const explicit = span.attributes["breadcrumb.span.type"];
118
- if (typeof explicit === "string" && explicit !== "") return explicit;
119
- if (LLM_SPAN_NAMES.has(span.name)) return "llm";
120
- if (TOOL_SPAN_NAMES.has(span.name)) return "tool";
112
+ function inferType(spanName) {
113
+ if (LLM_SPAN_NAMES.has(spanName)) return "llm";
114
+ if (TOOL_SPAN_NAMES.has(spanName)) return "tool";
121
115
  return "custom";
122
116
  }
123
- function extractCost(span) {
124
- const raw = span.attributes["ai.response.providerMetadata"];
125
- if (typeof raw !== "string") return {};
117
+ function extractCost(raw) {
126
118
  try {
127
119
  const parsed = tryJson(raw);
128
120
  if (!parsed || typeof parsed !== "object") return {};
@@ -143,10 +135,164 @@ function extractCost(span) {
143
135
  } catch {}
144
136
  return {};
145
137
  }
138
+ function mapAiSdk(span) {
139
+ const attrs = span.attributes;
140
+ const result = {};
141
+ const toolCallName = strAttr(attrs, "ai.toolCall.name");
142
+ const resourceName = strAttr(attrs, "resource.name");
143
+ if (toolCallName) result.name = toolCallName;
144
+ else if (resourceName) result.name = resourceName;
145
+ result.type = inferType(span.name);
146
+ const toolArgs = attrs["ai.toolCall.args"];
147
+ const aiMessages = attrs["ai.prompt.messages"];
148
+ const aiPrompt = attrs["ai.prompt"];
149
+ if (typeof toolArgs === "string") result.input = tryJson(toolArgs) ?? toolArgs;
150
+ else if (typeof aiMessages === "string") result.input = tryJson(aiMessages) ?? aiMessages;
151
+ else if (typeof aiPrompt === "string") {
152
+ const parsed = tryJson(aiPrompt);
153
+ if (parsed && typeof parsed === "object") {
154
+ const messages = [];
155
+ if (parsed["system"]) messages.push({
156
+ role: "system",
157
+ content: parsed["system"]
158
+ });
159
+ if (Array.isArray(parsed["messages"])) messages.push(...parsed["messages"]);
160
+ else if (parsed["prompt"] !== void 0) messages.push({
161
+ role: "user",
162
+ content: parsed["prompt"]
163
+ });
164
+ result.input = messages.length > 0 ? messages : parsed;
165
+ } else result.input = aiPrompt;
166
+ }
167
+ const toolResult = attrs["ai.toolCall.result"];
168
+ const aiText = attrs["ai.response.text"];
169
+ const aiToolCalls = attrs["ai.response.toolCalls"];
170
+ if (typeof toolResult === "string") result.output = tryJson(toolResult) ?? toolResult;
171
+ else if (typeof aiText === "string") result.output = aiText;
172
+ else if (typeof aiToolCalls === "string") {
173
+ const parsed = tryJson(aiToolCalls);
174
+ if (Array.isArray(parsed)) result.output = parsed.map((tc) => ({
175
+ ...tc,
176
+ input: typeof tc["input"] === "string" ? tryJson(tc["input"]) ?? tc["input"] : tc["input"]
177
+ }));
178
+ else result.output = parsed ?? aiToolCalls;
179
+ }
180
+ const model = strAttr(attrs, "ai.model.id", "ai.response.model", "gen_ai.request.model");
181
+ if (model) result.model = model;
182
+ const provider = strAttr(attrs, "ai.model.provider", "gen_ai.system");
183
+ if (provider) result.provider = provider;
184
+ const input_tokens = intAttr(attrs, "ai.usage.inputTokens", "ai.usage.promptTokens", "gen_ai.usage.input_tokens");
185
+ if (input_tokens != null) result.input_tokens = input_tokens;
186
+ const output_tokens = intAttr(attrs, "ai.usage.outputTokens", "ai.usage.completionTokens", "gen_ai.usage.output_tokens");
187
+ if (output_tokens != null) result.output_tokens = output_tokens;
188
+ const providerMeta = attrs["ai.response.providerMetadata"];
189
+ if (typeof providerMeta === "string") {
190
+ const cost = extractCost(providerMeta);
191
+ if (cost.input_cost_usd != null) result.input_cost_usd = cost.input_cost_usd;
192
+ if (cost.output_cost_usd != null) result.output_cost_usd = cost.output_cost_usd;
193
+ }
194
+ const metadata = {};
195
+ const finishReason = attrs["ai.response.finishReason"];
196
+ if (typeof finishReason === "string" && finishReason !== "") metadata["finish_reason"] = finishReason;
197
+ for (const [k, v] of Object.entries(attrs)) {
198
+ if (v == null) continue;
199
+ if (HANDLED.has(k) || DROP.has(k)) continue;
200
+ if (k.startsWith("ai.settings.") || k.startsWith("ai.request.headers.")) continue;
201
+ if (k.startsWith("ai.telemetry.metadata.")) {
202
+ metadata[k.slice(22)] = String(v);
203
+ continue;
204
+ }
205
+ if (k.startsWith("ai.") || k.startsWith("gen_ai.")) metadata[k] = String(v);
206
+ }
207
+ if (Object.keys(metadata).length > 0) result.metadata = metadata;
208
+ return result;
209
+ }
210
+
211
+ //#endregion
212
+ //#region src/mappers/breadcrumb.ts
213
+ const VALID_TYPES = new Set([
214
+ "llm",
215
+ "tool",
216
+ "retrieval",
217
+ "step",
218
+ "custom"
219
+ ]);
220
+ function mapBreadcrumb(span) {
221
+ const attrs = span.attributes;
222
+ const result = {};
223
+ const explicitType = attrs["breadcrumb.span.type"];
224
+ if (typeof explicitType === "string" && VALID_TYPES.has(explicitType)) result.type = explicitType;
225
+ const rawInput = attrs["breadcrumb.input"];
226
+ if (rawInput != null) result.input = typeof rawInput === "string" ? tryJson(rawInput) ?? rawInput : rawInput;
227
+ const rawOutput = attrs["breadcrumb.output"];
228
+ if (rawOutput != null) result.output = typeof rawOutput === "string" ? tryJson(rawOutput) ?? rawOutput : rawOutput;
229
+ const model = strAttr(attrs, "breadcrumb.model");
230
+ if (model) result.model = model;
231
+ const provider = strAttr(attrs, "breadcrumb.provider");
232
+ if (provider) result.provider = provider;
233
+ const input_tokens = intAttr(attrs, "breadcrumb.input_tokens");
234
+ if (input_tokens != null) result.input_tokens = input_tokens;
235
+ const output_tokens = intAttr(attrs, "breadcrumb.output_tokens");
236
+ if (output_tokens != null) result.output_tokens = output_tokens;
237
+ const input_cost_usd = floatAttr(attrs, "breadcrumb.input_cost_usd");
238
+ if (input_cost_usd != null) result.input_cost_usd = input_cost_usd;
239
+ const output_cost_usd = floatAttr(attrs, "breadcrumb.output_cost_usd");
240
+ if (output_cost_usd != null) result.output_cost_usd = output_cost_usd;
241
+ const metadata = {};
242
+ for (const [k, v] of Object.entries(attrs)) {
243
+ if (v == null) continue;
244
+ if (k.startsWith("breadcrumb.meta.")) metadata[k.slice(16)] = String(v);
245
+ }
246
+ if (Object.keys(metadata).length > 0) result.metadata = metadata;
247
+ return result;
248
+ }
249
+
250
+ //#endregion
251
+ //#region src/exporter.ts
252
+ const MAPPER_NAMESPACES = [
253
+ "ai.",
254
+ "gen_ai.",
255
+ "breadcrumb.",
256
+ "resource.name"
257
+ ];
258
+ const GLOBAL_DROP = new Set(["operation.name"]);
259
+ function hrTimeToISO(hrTime) {
260
+ return new Date(hrTime[0] * 1e3 + hrTime[1] / 1e6).toISOString();
261
+ }
262
+ function spanStatus(span) {
263
+ return span.status.code === __opentelemetry_api.SpanStatusCode.ERROR ? "error" : "ok";
264
+ }
265
+ function mergeMappers(span) {
266
+ const aiSdk = mapAiSdk(span);
267
+ const bc = mapBreadcrumb(span);
268
+ return {
269
+ ...aiSdk,
270
+ ...bc,
271
+ metadata: aiSdk.metadata || bc.metadata ? {
272
+ ...aiSdk.metadata,
273
+ ...bc.metadata
274
+ } : void 0
275
+ };
276
+ }
277
+ function passthroughMetadata(span, existing) {
278
+ const extra = {};
279
+ for (const [k, v] of Object.entries(span.attributes)) {
280
+ if (v == null) continue;
281
+ const handled = MAPPER_NAMESPACES.some((ns) => k === ns || k.startsWith(ns));
282
+ if (handled || GLOBAL_DROP.has(k)) continue;
283
+ extra[k] = String(v);
284
+ }
285
+ if (Object.keys(extra).length === 0) return existing || void 0;
286
+ return {
287
+ ...extra,
288
+ ...existing
289
+ };
290
+ }
146
291
  var BreadcrumbSpanExporter = class {
147
- constructor(apiKey, baseUrl) {
292
+ constructor(apiKey, baseUrl, environment) {
148
293
  this.apiKey = apiKey;
149
294
  this.baseUrl = baseUrl;
295
+ this.environment = environment;
150
296
  }
151
297
  export(spans, resultCallback) {
152
298
  this._export(spans).then(() => resultCallback({ code: __opentelemetry_core.ExportResultCode.SUCCESS }), () => resultCallback({ code: __opentelemetry_core.ExportResultCode.SUCCESS }));
@@ -162,78 +308,40 @@ var BreadcrumbSpanExporter = class {
162
308
  }
163
309
  _mapSpan(span) {
164
310
  const ctx = span.spanContext();
165
- const attrs = span.attributes;
166
- let input;
167
- const bcInput = attrs["breadcrumb.input"];
168
- const aiMessages = attrs["ai.prompt.messages"];
169
- if (bcInput != null) input = typeof bcInput === "string" ? tryJson(bcInput) ?? bcInput : bcInput;
170
- else if (typeof aiMessages === "string") input = tryJson(aiMessages) ?? aiMessages;
171
- let output;
172
- const bcOutput = attrs["breadcrumb.output"];
173
- const aiText = attrs["ai.response.text"];
174
- if (bcOutput != null) output = typeof bcOutput === "string" ? tryJson(bcOutput) ?? bcOutput : bcOutput;
175
- else if (typeof aiText === "string") output = aiText;
176
- const model = strAttr(span, "breadcrumb.model", "ai.model.id", "ai.response.model", "gen_ai.request.model");
177
- const provider = strAttr(span, "breadcrumb.provider", "ai.model.provider", "gen_ai.system");
178
- const input_tokens = intAttr(span, "breadcrumb.input_tokens", "ai.usage.inputTokens", "ai.usage.promptTokens", "gen_ai.usage.input_tokens");
179
- const output_tokens = intAttr(span, "breadcrumb.output_tokens", "ai.usage.outputTokens", "ai.usage.completionTokens", "gen_ai.usage.output_tokens");
180
- let input_cost_usd = floatAttr(span, "breadcrumb.input_cost_usd");
181
- let output_cost_usd = floatAttr(span, "breadcrumb.output_cost_usd");
182
- if (input_cost_usd == null && output_cost_usd == null) {
183
- const cost = extractCost(span);
184
- input_cost_usd = cost.input_cost_usd;
185
- output_cost_usd = cost.output_cost_usd;
186
- }
187
- const metadata = {};
188
- for (const [k, v] of Object.entries(attrs)) {
189
- if (v == null) continue;
190
- if (HANDLED_ATTRS.has(k) || DROP_ATTRS.has(k)) continue;
191
- if (k.startsWith("ai.settings.")) continue;
192
- if (k.startsWith("ai.request.headers.")) continue;
193
- if (k.startsWith("breadcrumb.")) {
194
- if (k.startsWith("breadcrumb.meta.")) metadata[k.slice(16)] = String(v);
195
- continue;
196
- }
197
- if (k.startsWith("ai.telemetry.metadata.")) {
198
- metadata[k.slice(22)] = String(v);
199
- continue;
200
- }
201
- if (k === "ai.response.finishReason") {
202
- metadata["finish_reason"] = String(v);
203
- continue;
204
- }
205
- metadata[k] = String(v);
206
- }
311
+ const mapped = mergeMappers(span);
312
+ const metadata = passthroughMetadata(span, mapped.metadata);
207
313
  return {
208
314
  id: ctx.spanId,
209
315
  trace_id: ctx.traceId,
210
316
  parent_span_id: span.parentSpanId || void 0,
211
- name: span.name,
212
- type: inferSpanType(span),
317
+ name: mapped.name ?? span.name,
318
+ type: mapped.type ?? "custom",
213
319
  start_time: hrTimeToISO(span.startTime),
214
320
  end_time: hrTimeToISO(span.endTime),
215
321
  status: spanStatus(span),
216
322
  status_message: span.status.message || void 0,
217
- input: input !== void 0 ? input : void 0,
218
- output: output !== void 0 ? output : void 0,
219
- provider,
220
- model,
221
- input_tokens,
222
- output_tokens,
223
- input_cost_usd,
224
- output_cost_usd,
225
- metadata: Object.keys(metadata).length > 0 ? metadata : void 0
323
+ input: mapped.input !== void 0 ? mapped.input : void 0,
324
+ output: mapped.output !== void 0 ? mapped.output : void 0,
325
+ provider: mapped.provider,
326
+ model: mapped.model,
327
+ input_tokens: mapped.input_tokens,
328
+ output_tokens: mapped.output_tokens,
329
+ input_cost_usd: mapped.input_cost_usd,
330
+ output_cost_usd: mapped.output_cost_usd,
331
+ metadata: metadata && Object.keys(metadata).length > 0 ? metadata : void 0
226
332
  };
227
333
  }
228
334
  async _sendTrace(span) {
229
335
  const ctx = span.spanContext();
336
+ const mapped = mergeMappers(span);
230
337
  await this._post("/v1/traces", {
231
338
  id: ctx.traceId,
232
- name: span.name,
339
+ name: mapped.name ?? span.name,
233
340
  start_time: hrTimeToISO(span.startTime),
234
341
  end_time: hrTimeToISO(span.endTime),
235
342
  status: spanStatus(span),
236
- status_message: span.status.message || void 0
343
+ status_message: span.status.message || void 0,
344
+ environment: this.environment
237
345
  });
238
346
  }
239
347
  async _post(path, body) {
@@ -259,7 +367,7 @@ var BreadcrumbSpanExporter = class {
259
367
  //#endregion
260
368
  //#region src/index.ts
261
369
  function init(options) {
262
- const exporter = new BreadcrumbSpanExporter(options.apiKey, options.baseUrl);
370
+ const exporter = new BreadcrumbSpanExporter(options.apiKey, options.baseUrl, options.environment);
263
371
  const batchOpts = typeof options.batching === "object" ? options.batching : void 0;
264
372
  const processor = options.batching === false ? new __opentelemetry_sdk_trace_base.SimpleSpanProcessor(exporter) : new __opentelemetry_sdk_trace_base.BatchSpanProcessor(exporter, {
265
373
  scheduledDelayMillis: batchOpts?.flushInterval ?? 5e3,
package/dist/index.d.cts CHANGED
@@ -1,7 +1,25 @@
1
1
  //#region src/types.d.ts
2
+ /**
3
+ * A single message in a conversation. Pass an array of these as `input` to
4
+ * display the conversation in the Breadcrumb UI the same way LLM spans appear.
5
+ */
6
+ interface Message {
7
+ role: "system" | "user" | "assistant" | "tool";
8
+ content: string;
9
+ }
2
10
  interface SpanData {
3
- input?: unknown;
4
- output?: unknown;
11
+ /**
12
+ * The input to this step. Use a plain string for simple inputs, or a
13
+ * `Message[]` array for conversation-style inputs — the UI renders both
14
+ * with role labels (system / user / assistant).
15
+ * Any other JSON-serializable value is also accepted and shown as raw data.
16
+ */
17
+ input?: string | Message[] | Record<string, unknown>;
18
+ /**
19
+ * The output from this step. Use a plain string for text outputs.
20
+ * Any JSON-serializable value is accepted.
21
+ */
22
+ output?: string | Record<string, unknown>;
5
23
  model?: string;
6
24
  provider?: string;
7
25
  input_tokens?: number;
@@ -21,10 +39,12 @@ interface Breadcrumb {
21
39
  span<T>(name: string, fn: (span: BreadcrumbSpan) => Promise<T>, options?: SpanOptions): Promise<T>;
22
40
  } //#endregion
23
41
  //#region src/index.d.ts
42
+
24
43
  //# sourceMappingURL=types.d.ts.map
25
44
  interface InitOptions {
26
45
  apiKey: string;
27
46
  baseUrl: string;
47
+ environment?: string;
28
48
  batching?: false | {
29
49
  flushInterval?: number;
30
50
  maxBatchSize?: number;
@@ -35,5 +55,5 @@ declare function init(options: InitOptions): Breadcrumb;
35
55
  //#endregion
36
56
  //# sourceMappingURL=index.d.ts.map
37
57
 
38
- export { Breadcrumb, BreadcrumbSpan, InitOptions, SpanOptions, init };
58
+ export { Breadcrumb, BreadcrumbSpan, InitOptions, Message, SpanData, SpanOptions, init };
39
59
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/index.ts"],"sourcesContent":null,"mappings":";UAAiB,QAAA;EAAA,KAAA,CAAA,EAAA,OAAQ;EAYR,MAAA,CAAA,EAAA,OAAA;EAIA,KAAA,CAAA,EAAA,MAAW;EAIX,QAAA,CAAA,EAAA,MAAU;EAAA,YAAA,CAAA,EAAA,MAAA;EAAA,aACS,CAAA,EAAA,MAAA;EAAc,cAAa,CAAA,EAAA,MAAA;EAAC,eAAT,CAAA,EAAA,MAAA;EAAO,QAAc,CAAA,EAZ/D,MAY+D,CAAA,MAAA,EAAA,MAAA,GAAA,MAAA,GAAA,OAAA,CAAA;;AAG7D,UAZE,cAAA,CAYF;EAAc,GAAa,CAAA,IAAA,EAX9B,QAW8B,CAAA,EAAA,IAAA;;AAC5B,UATG,WAAA,CASH;EAAW,IACZ,CAAA,EAAA,KAAA,GAAA,MAAA,GAAA,WAAA,GAAA,MAAA;;AAAD,UANK,UAAA,CAML;oCALwB,mBAAmB,QAAQ,KAAK,QAAQ;mCAG7D,mBAAmB,QAAQ,cAC5B,cACT,QAAQ;;;ACTb;ADLiB,UCKA,WAAA,CDLc;EAId,MAAA,EAAA,MAAW;EAIX,OAAA,EAAA,MAAU;EAAA,QAAA,CAAA,EAAA,KAAA,GAAA;IACS,aAAA,CAAA,EAAA,MAAA;IAA2B,YAAA,CAAA,EAAA,MAAA;EAAC,CAAA;;AAAI,iBCOpD,IAAA,CDPoD,OAAA,ECOtC,WDPsC,CAAA,ECOxB,UDPwB"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/index.ts"],"sourcesContent":null,"mappings":";;;;;UAIiB,OAAA;EAAA,IAAA,EAAA,QAAO,GAAA,MAAA,GAAA,WAAA,GAAA,MAAA;EAKP,OAAA,EAAA,MAAQ;;AAON,UAPF,QAAA,CAOE;EAAO;;;AAYP;AAGnB;AAIA;EAIiB,KAAA,CAAA,EAAA,MAAU,GAvBR,OAuBQ,EAAA,GAvBI,MAuBJ,CAAA,MAAA,EAAA,OAAA,CAAA;EAAA;;;;EACmC,MAAc,CAAA,EAAA,MAAA,GAnBxD,MAmBwD,CAAA,MAAA,EAAA,OAAA,CAAA;EAAC,KAAT,CAAA,EAAA,MAAA;EAAO,QAG5D,CAAA,EAAA,MAAA;EAAc,YAAa,CAAA,EAAA,MAAA;EAAC,aAAT,CAAA,EAAA,MAAA;EAAO,cAC3B,CAAA,EAAA,MAAA;EAAW,eACZ,CAAA,EAAA,MAAA;EAAC,QAAT,CAAA,EAjBQ,MAiBR,CAAA,MAAA,EAAA,MAAA,GAAA,MAAA,GAAA,OAAA,CAAA;AAAO;UAdK,cAAA;YACL;;ACfK,UDkBA,WAAA,CClBW;EAYZ,IAAA,CAAA,EAAI,KAAA,GAAA,MAAA,GAAA,WAAA,GAAA,MAAA;;AAAU,UDUb,UAAA,CCVa;EAAW,KAAG,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,MAAA,EAAA,EAAA,EAAA,CAAA,IAAA,EDWR,cCXQ,EAAA,GDWW,OCXX,CDWmB,CCXnB,CAAA,CAAA,EDWwB,OCXxB,CDWgC,CCXhC,CAAA;EAAU,IAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,MAAA,EAAA,EAAA,EAAA,CAAA,IAAA,EDcvC,cCduC,EAAA,GDcpB,OCdoB,CDcZ,CCdY,CAAA,EAAA,OAAA,CAAA,EDexC,WCfwC,CAAA,EDgBjD,OChBiD,CDgBzC,CChByC,CAAA;;;;;UAZrC,WAAA;;;EDbA,WAAO,CAAA,EAAA,MAAA;EAKP,QAAA,CAAA,EAAQ,KAAA,GAAA;IAAA,aAAA,CAAA,EAAA,MAAA;IAON,YAAA,CAAA,EAAA,MAAA;EAAO,CAAA;;AAYb,iBCCG,IAAA,CDDH,OAAA,ECCiB,WDDjB,CAAA,ECC+B,UDD/B;;;AAAM"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,25 @@
1
1
  //#region src/types.d.ts
2
+ /**
3
+ * A single message in a conversation. Pass an array of these as `input` to
4
+ * display the conversation in the Breadcrumb UI the same way LLM spans appear.
5
+ */
6
+ interface Message {
7
+ role: "system" | "user" | "assistant" | "tool";
8
+ content: string;
9
+ }
2
10
  interface SpanData {
3
- input?: unknown;
4
- output?: unknown;
11
+ /**
12
+ * The input to this step. Use a plain string for simple inputs, or a
13
+ * `Message[]` array for conversation-style inputs — the UI renders both
14
+ * with role labels (system / user / assistant).
15
+ * Any other JSON-serializable value is also accepted and shown as raw data.
16
+ */
17
+ input?: string | Message[] | Record<string, unknown>;
18
+ /**
19
+ * The output from this step. Use a plain string for text outputs.
20
+ * Any JSON-serializable value is accepted.
21
+ */
22
+ output?: string | Record<string, unknown>;
5
23
  model?: string;
6
24
  provider?: string;
7
25
  input_tokens?: number;
@@ -21,10 +39,12 @@ interface Breadcrumb {
21
39
  span<T>(name: string, fn: (span: BreadcrumbSpan) => Promise<T>, options?: SpanOptions): Promise<T>;
22
40
  } //#endregion
23
41
  //#region src/index.d.ts
42
+
24
43
  //# sourceMappingURL=types.d.ts.map
25
44
  interface InitOptions {
26
45
  apiKey: string;
27
46
  baseUrl: string;
47
+ environment?: string;
28
48
  batching?: false | {
29
49
  flushInterval?: number;
30
50
  maxBatchSize?: number;
@@ -35,5 +55,5 @@ declare function init(options: InitOptions): Breadcrumb;
35
55
  //#endregion
36
56
  //# sourceMappingURL=index.d.ts.map
37
57
 
38
- export { Breadcrumb, BreadcrumbSpan, InitOptions, SpanOptions, init };
58
+ export { Breadcrumb, BreadcrumbSpan, InitOptions, Message, SpanData, SpanOptions, init };
39
59
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/index.ts"],"sourcesContent":null,"mappings":";UAAiB,QAAA;EAAA,KAAA,CAAA,EAAA,OAAQ;EAYR,MAAA,CAAA,EAAA,OAAA;EAIA,KAAA,CAAA,EAAA,MAAW;EAIX,QAAA,CAAA,EAAA,MAAU;EAAA,YAAA,CAAA,EAAA,MAAA;EAAA,aACS,CAAA,EAAA,MAAA;EAAc,cAAa,CAAA,EAAA,MAAA;EAAC,eAAT,CAAA,EAAA,MAAA;EAAO,QAAc,CAAA,EAZ/D,MAY+D,CAAA,MAAA,EAAA,MAAA,GAAA,MAAA,GAAA,OAAA,CAAA;;AAG7D,UAZE,cAAA,CAYF;EAAc,GAAa,CAAA,IAAA,EAX9B,QAW8B,CAAA,EAAA,IAAA;;AAC5B,UATG,WAAA,CASH;EAAW,IACZ,CAAA,EAAA,KAAA,GAAA,MAAA,GAAA,WAAA,GAAA,MAAA;;AAAD,UANK,UAAA,CAML;oCALwB,mBAAmB,QAAQ,KAAK,QAAQ;mCAG7D,mBAAmB,QAAQ,cAC5B,cACT,QAAQ;;;ACTb;ADLiB,UCKA,WAAA,CDLc;EAId,MAAA,EAAA,MAAW;EAIX,OAAA,EAAA,MAAU;EAAA,QAAA,CAAA,EAAA,KAAA,GAAA;IACS,aAAA,CAAA,EAAA,MAAA;IAA2B,YAAA,CAAA,EAAA,MAAA;EAAC,CAAA;;AAAI,iBCOpD,IAAA,CDPoD,OAAA,ECOtC,WDPsC,CAAA,ECOxB,UDPwB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/index.ts"],"sourcesContent":null,"mappings":";;;;;UAIiB,OAAA;EAAA,IAAA,EAAA,QAAO,GAAA,MAAA,GAAA,WAAA,GAAA,MAAA;EAKP,OAAA,EAAA,MAAQ;;AAON,UAPF,QAAA,CAOE;EAAO;;;AAYP;AAGnB;AAIA;EAIiB,KAAA,CAAA,EAAA,MAAU,GAvBR,OAuBQ,EAAA,GAvBI,MAuBJ,CAAA,MAAA,EAAA,OAAA,CAAA;EAAA;;;;EACmC,MAAc,CAAA,EAAA,MAAA,GAnBxD,MAmBwD,CAAA,MAAA,EAAA,OAAA,CAAA;EAAC,KAAT,CAAA,EAAA,MAAA;EAAO,QAG5D,CAAA,EAAA,MAAA;EAAc,YAAa,CAAA,EAAA,MAAA;EAAC,aAAT,CAAA,EAAA,MAAA;EAAO,cAC3B,CAAA,EAAA,MAAA;EAAW,eACZ,CAAA,EAAA,MAAA;EAAC,QAAT,CAAA,EAjBQ,MAiBR,CAAA,MAAA,EAAA,MAAA,GAAA,MAAA,GAAA,OAAA,CAAA;AAAO;UAdK,cAAA;YACL;;ACfK,UDkBA,WAAA,CClBW;EAYZ,IAAA,CAAA,EAAI,KAAA,GAAA,MAAA,GAAA,WAAA,GAAA,MAAA;;AAAU,UDUb,UAAA,CCVa;EAAW,KAAG,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,MAAA,EAAA,EAAA,EAAA,CAAA,IAAA,EDWR,cCXQ,EAAA,GDWW,OCXX,CDWmB,CCXnB,CAAA,CAAA,EDWwB,OCXxB,CDWgC,CCXhC,CAAA;EAAU,IAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,MAAA,EAAA,EAAA,EAAA,CAAA,IAAA,EDcvC,cCduC,EAAA,GDcpB,OCdoB,CDcZ,CCdY,CAAA,EAAA,OAAA,CAAA,EDexC,WCfwC,CAAA,EDgBjD,OChBiD,CDgBzC,CChByC,CAAA;;;;;UAZrC,WAAA;;;EDbA,WAAO,CAAA,EAAA,MAAA;EAKP,QAAA,CAAA,EAAQ,KAAA,GAAA;IAAA,aAAA,CAAA,EAAA,MAAA;IAON,YAAA,CAAA,EAAA,MAAA;EAAO,CAAA;;AAYb,iBCCG,IAAA,CDDH,OAAA,ECCiB,WDDjB,CAAA,ECC+B,UDD/B;;;AAAM"}