@lmnr-ai/lmnr 0.8.37 → 0.8.38

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 (29) hide show
  1. package/dist/cli.cjs +2 -2
  2. package/dist/cli.d.cts +1 -1
  3. package/dist/cli.d.mts +1 -1
  4. package/dist/cli.mjs +2 -2
  5. package/dist/{dist-muQLQb9o.cjs → dist-DGDsKbQ8.cjs} +17 -3
  6. package/dist/dist-DGDsKbQ8.cjs.map +1 -0
  7. package/dist/{dist-CdR_Phvb.mjs → dist-DVpPVAC5.mjs} +6 -4
  8. package/dist/dist-DVpPVAC5.mjs.map +1 -0
  9. package/dist/{evaluations-DpDwGehc.d.mts → evaluations-CzqDQADS.d.mts} +2 -30
  10. package/dist/{evaluations-D6_c4gW-.d.cts → evaluations-F6Zzae_Y.d.cts} +2 -30
  11. package/dist/{file-utils-BE3NvLX0.mjs → file-utils-D3Jj4yAt.mjs} +2 -2
  12. package/dist/{file-utils-BE3NvLX0.mjs.map → file-utils-D3Jj4yAt.mjs.map} +1 -1
  13. package/dist/{file-utils-C0oHtdus.cjs → file-utils-wA-Z77kv.cjs} +2 -2
  14. package/dist/{file-utils-C0oHtdus.cjs.map → file-utils-wA-Z77kv.cjs.map} +1 -1
  15. package/dist/index.cjs +299 -496
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +13943 -7780
  18. package/dist/index.d.mts +13943 -7780
  19. package/dist/index.mjs +301 -498
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.cjs +71 -55
  22. package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.cjs.map +1 -1
  23. package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.d.cts +4410 -61
  24. package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.d.mts +4410 -61
  25. package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.mjs +71 -55
  26. package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.mjs.map +1 -1
  27. package/package.json +26 -26
  28. package/dist/dist-CdR_Phvb.mjs.map +0 -1
  29. package/dist/dist-muQLQb9o.cjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { r as __require } from "./rolldown-runtime-2rV9d50f.mjs";
2
- import { A as SPAN_OUTPUT, B as TracingLevel, C as PARENT_SPAN_IDS_PATH, D as SPAN_INPUT, E as SPAN_IDS_PATH, F as TRACE_TYPE, I as USER_ID, L as version, M as SPAN_SDK_VERSION, N as SPAN_TYPE, O as SPAN_INSTRUMENTATION_SOURCE, P as TRACE_HAS_BROWSER_SESSION, R as DEBUG_SESSION_DIR, S as LaminarAttributes, T as SESSION_ID, V as errorMessage, _ as tryToOtelSpanContext, b as ASSOCIATION_PROPERTIES_OVERRIDES, c as initializeLogger, d as newUUID, f as normalizeOtelSpanId, g as parseOtelHeaders, h as otelTraceIdToUUID, i as deserializeLaminarSpanContext, j as SPAN_PATH, k as SPAN_LANGUAGE_VERSION, l as loadEnv, m as otelSpanIdToUUID, n as NIL_UUID, o as getFrontendUrl, p as normalizeOtelTraceId, r as Semaphore, s as getOtelEnvVar, t as LaminarClient, u as metadataToAttributes, v as validateTracingConfig, w as PARENT_SPAN_PATH, x as HUMAN_EVALUATOR_OPTIONS, y as ASSOCIATION_PROPERTIES, z as DEBUG_SESSION_FILE } from "./dist-CdR_Phvb.mjs";
2
+ import { A as SPAN_INSTRUMENTATION_SOURCE, B as DEBUG_SESSION_DIR, C as PARENT_SPAN_IDS_PATH, D as SPAN_INPUT, E as SPAN_IDS_PATH, F as SPAN_TYPE, H as TracingLevel, I as TRACE_HAS_BROWSER_SESSION, L as TRACE_TYPE, M as SPAN_OUTPUT, N as SPAN_PATH, O as SPAN_INSTRUMENTATION_SCOPE_NAME, P as SPAN_SDK_VERSION, R as USER_ID, S as LaminarAttributes, T as SESSION_ID, U as errorMessage, V as DEBUG_SESSION_FILE, _ as tryToOtelSpanContext, b as ASSOCIATION_PROPERTIES_OVERRIDES, c as initializeLogger, d as newUUID, f as normalizeOtelSpanId, g as parseOtelHeaders, h as otelTraceIdToUUID, i as deserializeLaminarSpanContext, j as SPAN_LANGUAGE_VERSION, k as SPAN_INSTRUMENTATION_SCOPE_VERSION, l as loadEnv, m as otelSpanIdToUUID, n as NIL_UUID, o as getFrontendUrl, p as normalizeOtelTraceId, r as Semaphore, s as getOtelEnvVar, t as LaminarClient, u as metadataToAttributes, v as validateTracingConfig, w as PARENT_SPAN_PATH, x as HUMAN_EVALUATOR_OPTIONS, y as ASSOCIATION_PROPERTIES, z as version } from "./dist-DVpPVAC5.mjs";
3
3
  import { n as TRACEPARENT_HEADER, t as LAMINAR_SPAN_CONTEXT_HEADER } from "./consts-3AfXaMBI.mjs";
4
4
  import { DiagConsoleLogger, DiagLogLevel, ROOT_CONTEXT, SpanKind, SpanStatusCode, TraceFlags, context, createContextKey, diag, isSpanContextValid, trace } from "@opentelemetry/api";
5
5
  import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
@@ -17,11 +17,10 @@ import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
17
17
  import { PineconeInstrumentation } from "@traceloop/instrumentation-pinecone";
18
18
  import { QdrantInstrumentation } from "@traceloop/instrumentation-qdrant";
19
19
  import { TogetherInstrumentation } from "@traceloop/instrumentation-together";
20
- import { AIPlatformInstrumentation, VertexAIInstrumentation } from "@traceloop/instrumentation-vertexai";
21
20
  import { spawn } from "node:child_process";
22
21
  import { randomUUID } from "node:crypto";
23
- import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
24
- import { dirname, join, parse, resolve } from "node:path";
22
+ import { mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
23
+ import { dirname, join, parse, resolve, sep } from "node:path";
25
24
  import * as fs from "fs";
26
25
  import * as net from "net";
27
26
  import * as os from "os";
@@ -88,7 +87,7 @@ var LaminarDataset = class extends EvaluationDataset {
88
87
  */
89
88
  async push(paths, recursive = false) {
90
89
  if (!this.client) throw new Error("Client not set");
91
- const { loadFromPaths } = await import("./file-utils-BE3NvLX0.mjs").then((n) => n.t);
90
+ const { loadFromPaths } = await import("./file-utils-D3Jj4yAt.mjs").then((n) => n.t);
92
91
  const data = await loadFromPaths(Array.isArray(paths) ? paths : [paths], recursive);
93
92
  if (data.length === 0) {
94
93
  console.warn("No data to push. Skipping");
@@ -160,7 +159,10 @@ const stringifyPromptForTelemetry = (prompt) => JSON.stringify(prompt.map((messa
160
159
  ...message,
161
160
  content: typeof message.content === "string" ? message.content : message.content.map((part) => part.type === "file" ? {
162
161
  ...part,
163
- data: part.data instanceof Uint8Array ? convertDataContentToBase64String(part.data) : part.data
162
+ data: part.data instanceof Uint8Array ? convertDataContentToBase64String(part.data) : part.data && typeof part.data === "object" && part.data.type === "data" && part.data.data instanceof Uint8Array ? {
163
+ ...part.data,
164
+ data: convertDataContentToBase64String(part.data.data)
165
+ } : part.data
164
166
  } : part)
165
167
  })));
166
168
  /**
@@ -6231,8 +6233,6 @@ const initInstrumentations = (client, suppressContentTracing, sessionRecordingOp
6231
6233
  }));
6232
6234
  instrumentations.push(new AnthropicInstrumentation({ traceContent: !suppressContentTracing }));
6233
6235
  instrumentations.push(new CohereInstrumentation({ traceContent: !suppressContentTracing }));
6234
- instrumentations.push(new VertexAIInstrumentation({ traceContent: !suppressContentTracing }));
6235
- instrumentations.push(new AIPlatformInstrumentation({ traceContent: !suppressContentTracing }));
6236
6236
  instrumentations.push(new BedrockInstrumentation({ traceContent: !suppressContentTracing }));
6237
6237
  instrumentations.push(new PineconeInstrumentation());
6238
6238
  instrumentations.push(new LangChainInstrumentation({ traceContent: !suppressContentTracing }));
@@ -6284,16 +6284,6 @@ const manuallyInitInstrumentations = (client, instrumentModules, suppressContent
6284
6284
  instrumentations.push(cohereInstrumentation);
6285
6285
  cohereInstrumentation.manuallyInstrument(instrumentModules.cohere);
6286
6286
  }
6287
- if (instrumentModules?.google_vertexai) {
6288
- const vertexaiInstrumentation = new VertexAIInstrumentation({ traceContent: !suppressContentTracing });
6289
- instrumentations.push(vertexaiInstrumentation);
6290
- vertexaiInstrumentation.manuallyInstrument(instrumentModules.google_vertexai);
6291
- }
6292
- if (instrumentModules?.google_aiplatform) {
6293
- const aiplatformInstrumentation = new AIPlatformInstrumentation({ traceContent: !suppressContentTracing });
6294
- instrumentations.push(aiplatformInstrumentation);
6295
- aiplatformInstrumentation.manuallyInstrument(instrumentModules.google_aiplatform);
6296
- }
6297
6287
  if (instrumentModules?.bedrock) {
6298
6288
  const bedrockInstrumentation = new BedrockInstrumentation({ traceContent: !suppressContentTracing });
6299
6289
  instrumentations.push(bedrockInstrumentation);
@@ -7318,260 +7308,6 @@ async function evaluate({ data, executor, evaluators, groupName, name, metadata,
7318
7308
  else return await evaluation.run();
7319
7309
  }
7320
7310
  //#endregion
7321
- //#region src/debug/aisdk-normalize.ts
7322
- const isRecord = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
7323
- /** Mirror Rust `is_url`. */
7324
- const isUrl = (data) => data.startsWith("http://") || data.startsWith("https://") || data.startsWith("data:");
7325
- /** Mirror Rust `infer_image_type`. */
7326
- const inferImageType = (base64) => {
7327
- if (base64.startsWith("/9j/")) return "image/jpeg";
7328
- if (base64.startsWith("iVBORw0KGgo")) return "image/png";
7329
- if (base64.startsWith("R0lGODlh")) return "image/gif";
7330
- if (base64.startsWith("UklGR")) return "image/webp";
7331
- if (base64.startsWith("PHN2Zz")) return "image/svg+xml";
7332
- return "image/png";
7333
- };
7334
- const base64FromBytes = (bytes) => {
7335
- let latin1 = "";
7336
- for (const b of bytes) latin1 += String.fromCharCode(b & 255);
7337
- return btoa(latin1);
7338
- };
7339
- /**
7340
- * Mirror Rust `json_value_to_string`: a JSON string stays itself; anything else
7341
- * is compact-serialized. With serde `preserve_order` on, `JSON.stringify` matches
7342
- * `serde_json::Value::to_string()` byte-for-byte for object key order.
7343
- */
7344
- const jsonValueToString = (value) => typeof value === "string" ? value : JSON.stringify(value);
7345
- /** First string among the candidates (ports serde field aliases). */
7346
- const firstString = (...candidates) => {
7347
- for (const c of candidates) if (typeof c === "string") return c;
7348
- };
7349
- /**
7350
- * Port of `InstrumentationChatMessageImage` (untagged) →
7351
- * `from_instrumentation_content_part`'s `Image` arm. Returns the output
7352
- * `ChatMessageContentPart` object, or `null` if no untagged variant matches.
7353
- */
7354
- const parseImage = (e) => {
7355
- if (isRecord(e.source)) {
7356
- const src = e.source;
7357
- if (typeof src.media_type === "string" && typeof src.data === "string") return {
7358
- type: "image",
7359
- mediaType: src.media_type,
7360
- data: src.data
7361
- };
7362
- if (typeof src.url === "string") return {
7363
- type: "image_url",
7364
- url: src.url,
7365
- detail: typeof src.detail === "string" ? src.detail : null
7366
- };
7367
- }
7368
- if (Array.isArray(e.image) && e.image.every((n) => typeof n === "number")) {
7369
- const b64 = base64FromBytes(e.image);
7370
- return {
7371
- type: "image_url",
7372
- url: `data:${typeof e.mime_type === "string" ? e.mime_type : inferImageType(b64)};base64,${b64}`,
7373
- detail: null
7374
- };
7375
- }
7376
- if (typeof e.image === "string") {
7377
- if (isUrl(e.image)) return {
7378
- type: "image_url",
7379
- url: e.image,
7380
- detail: null
7381
- };
7382
- return {
7383
- type: "image",
7384
- data: e.image,
7385
- mediaType: typeof e.mime_type === "string" ? e.mime_type : "image/png"
7386
- };
7387
- }
7388
- return null;
7389
- };
7390
- /** Port of the `Document` arm (only the Base64 source shape exists). */
7391
- const parseDocument = (e) => {
7392
- if (!isRecord(e.source) || e.source.type !== "base64") return null;
7393
- const data = e.source.data;
7394
- if (typeof data !== "string") return null;
7395
- return {
7396
- type: "document",
7397
- source: {
7398
- type: "base64",
7399
- media_type: firstString(e.source.media_type, e.source.mediaType) ?? "application/octet-stream",
7400
- data
7401
- }
7402
- };
7403
- };
7404
- const DATA_URL_RE = /^data:((?:application|image)\/[a-zA-Z-]+);base64,/;
7405
- /**
7406
- * Port of the `File` arm (OpenAI file-id / base64, AI SDK v1/v2).
7407
- *
7408
- * Known parity gap (v7 + image-by-URL only): on AI SDK v7 the recording path
7409
- * stores `ai.prompt.messages` from the user-facing `ModelMessage[]` (an image
7410
- * passed by URL is a `{type:"image", image:"https://…"}` part), which the
7411
- * server reshapes via `parseImage` into an `image_url`. Replay instead hashes
7412
- * the LanguageModel-level prompt, where that same image is already a
7413
- * `{type:"file", data:"https://…", mediaType:"image/*"}` part — this arm maps
7414
- * it to `image`, not `image_url`, so the two hashes diverge and the lookup
7415
- * MISSes (degrading safely to a live call). This can't be reconciled here: the
7416
- * arm is a faithful port of the server's Rust `parseFile`, so on v6 (where the
7417
- * server itself reshapes a file part) emitting `image_url` would instead BREAK
7418
- * parity, and the replay wrapper has no signal for which SDK version recorded
7419
- * the span. Plain text, multi-part text, tool calls, and base64 images all hash
7420
- * identically across both paths; only URL-referenced images on v7 are affected.
7421
- */
7422
- const parseFile = (e) => {
7423
- if (isRecord(e.file)) {
7424
- const file = e.file;
7425
- if (typeof file.file_id === "string") return {
7426
- type: "text",
7427
- text: JSON.stringify({ file_id: file.file_id })
7428
- };
7429
- if (typeof file.file_data === "string") {
7430
- const m = DATA_URL_RE.exec(file.file_data.slice(0, 50));
7431
- return {
7432
- type: "document",
7433
- source: {
7434
- type: "base64",
7435
- media_type: m ? m[1] : "application/octet-stream",
7436
- data: m ? file.file_data.slice(file.file_data.indexOf(",") + 1) : file.file_data
7437
- }
7438
- };
7439
- }
7440
- return null;
7441
- }
7442
- if (typeof e.data === "string") {
7443
- const mediaType = firstString(e.media_type, e.mediaType, e.mime_type);
7444
- if (mediaType === void 0) return null;
7445
- if (mediaType.startsWith("image/")) return {
7446
- type: "image",
7447
- data: e.data,
7448
- mediaType
7449
- };
7450
- return {
7451
- type: "document",
7452
- source: {
7453
- type: "base64",
7454
- media_type: mediaType,
7455
- data: e.data
7456
- }
7457
- };
7458
- }
7459
- return null;
7460
- };
7461
- /**
7462
- * Port of `serde_json::from_value::<InstrumentationChatMessageContentPart>` for a
7463
- * single part, returning the mapped output `ChatMessageContentPart` object or
7464
- * `null` when no variant matches (an unrecognized `type`, or a recognized tag
7465
- * whose inner shape fails). `null` propagates to an all-or-nothing Text fallback.
7466
- *
7467
- * Output objects carry serde's serialized field names; `skip_serializing_if` is
7468
- * reproduced by omitting the field (ChatMessage.tool_call_id only), and `Option`
7469
- * fields WITHOUT skip (`detail`, tool-call `id`/`arguments`) always emit `null`.
7470
- */
7471
- const parsePart = (el) => {
7472
- if (!isRecord(el)) return null;
7473
- switch (el.type) {
7474
- case "text": {
7475
- const text = firstString(el.text, el.input_text, el.output_text);
7476
- return text === void 0 ? null : {
7477
- type: "text",
7478
- text
7479
- };
7480
- }
7481
- case "image_url": {
7482
- if (!isRecord(el.image_url) || typeof el.image_url.url !== "string") return null;
7483
- const detail = el.image_url.detail;
7484
- return {
7485
- type: "image_url",
7486
- url: el.image_url.url,
7487
- detail: typeof detail === "string" ? detail : null
7488
- };
7489
- }
7490
- case "image": return parseImage(el);
7491
- case "document": return parseDocument(el);
7492
- case "file": return parseFile(el);
7493
- case "tool-call": {
7494
- if (typeof el.toolName !== "string") return null;
7495
- const rawInput = "input" in el ? el.input : el.args;
7496
- return {
7497
- type: "tool_call",
7498
- name: el.toolName,
7499
- id: typeof el.toolCallId === "string" ? el.toolCallId : null,
7500
- arguments: rawInput === void 0 || rawInput === null ? null : rawInput
7501
- };
7502
- }
7503
- case "tool-result":
7504
- if (typeof el.toolCallId !== "string" || typeof el.toolName !== "string") return null;
7505
- if (!("output" in el || "result" in el)) return null;
7506
- return {
7507
- type: "tool-result",
7508
- toolCallId: el.toolCallId,
7509
- output: "output" in el ? el.output : el.result,
7510
- toolName: el.toolName
7511
- };
7512
- default: return null;
7513
- }
7514
- };
7515
- /**
7516
- * Port of `input_chat_messages_from_json`. `input` is the parsed
7517
- * `ai.prompt.messages` array. Each message's `content` becomes either a
7518
- * `ContentPartList` (when EVERY element parses as an instrumentation part) or a
7519
- * Text blob (`json_value_to_string` of the whole content) otherwise. A message
7520
- * missing `role`/`content` would be a server error; we skip it defensively so the
7521
- * hash is still computed over the well-formed remainder.
7522
- */
7523
- const inputChatMessagesFromJson = (input) => {
7524
- if (!Array.isArray(input)) return [];
7525
- const messages = [];
7526
- for (const message of input) {
7527
- if (!isRecord(message)) continue;
7528
- const role = message.role;
7529
- if (typeof role !== "string" || !("content" in message)) continue;
7530
- const otelContent = message.content;
7531
- let parts = null;
7532
- if (Array.isArray(otelContent)) {
7533
- parts = [];
7534
- for (const el of otelContent) {
7535
- const parsed = parsePart(el);
7536
- if (parsed === null) {
7537
- parts = null;
7538
- break;
7539
- }
7540
- parts.push(parsed);
7541
- }
7542
- }
7543
- const out = {
7544
- role,
7545
- content: parts !== null ? parts : jsonValueToString(otelContent)
7546
- };
7547
- if (typeof message.tool_call_id === "string") out.tool_call_id = message.tool_call_id;
7548
- messages.push(out);
7549
- }
7550
- return messages;
7551
- };
7552
- /**
7553
- * Reshape an AI SDK call's `options.prompt` into the reconstructed message array
7554
- * the server hashes. Version-agnostic: `stringifyPromptForTelemetry` already
7555
- * normalizes V2/V3/V4 prompts (Uint8Array file `data` → base64) into the stored
7556
- * `ai.prompt.messages` JSON, and the reshape operates on that JSON identically
7557
- * across versions.
7558
- *
7559
- * Returns `null` when the prompt cannot be reshaped at all (a `stringify` /
7560
- * `JSON.parse` failure). The caller MUST treat `null` as "can't compute a real
7561
- * key" and degrade to a live call WITHOUT latching: hashing a default/empty
7562
- * payload here would key the lookup off the wrong bytes, force a spurious MISS,
7563
- * and wrongly latch live mode for the rest of the replay.
7564
- */
7565
- const extractInputMessages = (options) => {
7566
- let aiPromptMessages;
7567
- try {
7568
- aiPromptMessages = JSON.parse(stringifyPromptForTelemetry(options.prompt));
7569
- } catch {
7570
- return null;
7571
- }
7572
- return inputChatMessagesFromJson(aiPromptMessages);
7573
- };
7574
- //#endregion
7575
7311
  //#region src/debug/hash.ts
7576
7312
  /**
7577
7313
  * Debug-replay input hashing.
@@ -7579,13 +7315,13 @@ const extractInputMessages = (options) => {
7579
7315
  * Computes the per-LLM-call `input_hash` the server-side debugger cache (v2) is
7580
7316
  * keyed by. The SDK and the Rust app-server MUST produce byte-identical hashes,
7581
7317
  * so this is a faithful port of the app-server primitives:
7582
- * - `canonical_json` — `app-server/src/traces/input_dedup.rs`
7583
- * - `extract_system_message`— `app-server/src/traces/prompt_hash.rs`
7584
- * - `debug_input_hash` — `app-server/src/traces/debug_cache.rs` (plan 01 §3)
7318
+ * - `canonical_json` — `app-server/src/traces/input_dedup.rs`
7319
+ * - `debug_input_hash` — `app-server/src/traces/input_dedup.rs`
7585
7320
  *
7586
- * The whole non-system message array is hashed as ONE blob: strip the first
7587
- * `role === "system"` message (when its text is non-empty), canonicalize the
7588
- * remaining array, then blake3 over its UTF-8 bytes and hex-encode the digest.
7321
+ * The whole non-system message array is hashed as ONE blob: strip EVERY
7322
+ * `role === "system"` message (regardless of content or position), canonicalize
7323
+ * the remaining array, then blake3 over its UTF-8 bytes and hex-encode the
7324
+ * digest.
7589
7325
  *
7590
7326
  * Keep this file line-comparable with the Python `debug/hash.py`.
7591
7327
  */
@@ -7606,53 +7342,25 @@ const canonicalJson = (value) => {
7606
7342
  return JSON.stringify(value);
7607
7343
  };
7608
7344
  /**
7609
- * Extract the system prompt text from a system-role message, or "" when none.
7345
+ * Drop EVERY `role === "system"` message, matching the server.
7610
7346
  *
7611
- * Faithful port of Rust `extract_system_message`'s text resolution: a plain
7612
- * string content, else an array of `{text}` blocks joined by a space (only when
7613
- * the join is non-empty), else `parts[0].text` / `parts[0].content`.
7347
+ * Content and position are irrelevant: an empty system string, a block-array
7348
+ * system, or repeated/trailing system messages are all stripped. The coding
7349
+ * agent may add/remove/edit its own system prompt(s) between iterations and
7350
+ * those edits must not change the cache key.
7614
7351
  */
7615
- const systemMessageText = (sysMsg) => {
7616
- const content = sysMsg.content;
7617
- if (typeof content === "string") return content;
7618
- if (Array.isArray(content)) {
7619
- const joined = content.map((block) => block !== null && typeof block === "object" ? block.text : void 0).filter((t) => typeof t === "string").join(" ");
7620
- if (joined.length > 0) return joined;
7621
- }
7622
- const parts = sysMsg.parts;
7623
- if (Array.isArray(parts) && parts.length > 0) {
7624
- const first = parts[0];
7625
- if (first !== null && typeof first === "object") {
7626
- const f = first;
7627
- const t = typeof f.text === "string" ? f.text : f.content;
7628
- if (typeof t === "string") return t;
7629
- }
7630
- }
7631
- return "";
7632
- };
7352
+ const stripSystemMessages = (messages) => messages.filter((m) => !(m !== null && typeof m === "object" && m.role === "system"));
7633
7353
  /**
7634
- * Drop the first `role === "system"` message, matching the server.
7354
+ * Compute the hex blake3 `input_hash` for a message array.
7635
7355
  *
7636
- * Returns the array unchanged when there is no system message OR when the system
7637
- * message's resolved text is empty (Rust `extract_system_message` returns `None`
7638
- * in both cases, leaving the system message in the hashed array).
7639
- */
7640
- const stripSystemMessage = (messages) => {
7641
- const sysIdx = messages.findIndex((m) => m !== null && typeof m === "object" && m.role === "system");
7642
- if (sysIdx === -1) return messages;
7643
- if (systemMessageText(messages[sysIdx]).length === 0) return messages;
7644
- return messages.filter((_, i) => i !== sysIdx);
7645
- };
7646
- /**
7647
- * Compute the hex blake3 `input_hash` for a reconstructed message array.
7648
- *
7649
- * `messages` must already be the server's reconstructed shape (the SDK reshapes
7650
- * provider-specific prompts via `aisdk-normalize.ts` so the hashed bytes match
7651
- * what `spans_v0` stores). The system message is stripped, the remainder
7652
- * canonicalized, and the digest hex-encoded (64 chars).
7356
+ * `messages` is the verbatim LanguageModel-level prompt
7357
+ * (`JSON.parse(stringifyPromptForTelemetry(options.prompt))`) the same bytes
7358
+ * the SDK records as `gen_ai.input.messages` and the server hashes on ingest.
7359
+ * System messages are stripped, the remainder canonicalized, and the digest
7360
+ * hex-encoded (64 chars).
7653
7361
  */
7654
7362
  const debugInputHash = (messages) => {
7655
- const canonical = canonicalJson(stripSystemMessage(messages));
7363
+ const canonical = canonicalJson(stripSystemMessages(messages));
7656
7364
  return bytesToHex(blake3(new TextEncoder().encode(canonical)));
7657
7365
  };
7658
7366
  //#endregion
@@ -7712,6 +7420,107 @@ const removeActiveLlmSpan = (span) => {
7712
7420
  /** Most recently pushed still-open LLM span, or undefined if none. */
7713
7421
  const peekActiveLlmSpan = () => openLlmSpans.length > 0 ? openLlmSpans[openLlmSpans.length - 1] : void 0;
7714
7422
  //#endregion
7423
+ //#region src/opentelemetry-lib/instrumentation/aisdk/v7-integration/utils.ts
7424
+ const serializeJSON$1 = (value) => {
7425
+ if (typeof value === "string") return value;
7426
+ try {
7427
+ return JSON.stringify(value);
7428
+ } catch {
7429
+ return "[unserializable]";
7430
+ }
7431
+ };
7432
+ const normalizeProvider$1 = (provider) => {
7433
+ if (!provider) return void 0;
7434
+ return provider.split(".").shift()?.toLowerCase().trim() || provider.toLowerCase().trim() || void 0;
7435
+ };
7436
+ const applyUsage = (attributes, usage) => {
7437
+ if (!usage || typeof usage !== "object") return;
7438
+ if (typeof usage.inputTokens === "number") attributes[LaminarAttributes.INPUT_TOKEN_COUNT] = usage.inputTokens;
7439
+ if (typeof usage.outputTokens === "number") attributes[LaminarAttributes.OUTPUT_TOKEN_COUNT] = usage.outputTokens;
7440
+ if (typeof usage.totalTokens === "number") attributes[LaminarAttributes.TOTAL_TOKEN_COUNT] = usage.totalTokens;
7441
+ else if (typeof usage.inputTokens === "number" || typeof usage.outputTokens === "number") attributes[LaminarAttributes.TOTAL_TOKEN_COUNT] = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
7442
+ if (typeof usage.reasoningTokens === "number") attributes["gen_ai.usage.reasoning_tokens"] = usage.reasoningTokens;
7443
+ else if (typeof usage.outputTokenDetails?.reasoningTokens === "number") attributes["gen_ai.usage.reasoning_tokens"] = usage.outputTokenDetails.reasoningTokens;
7444
+ if (typeof usage.cachedInputTokens === "number") attributes["gen_ai.usage.cache_read_input_tokens"] = usage.cachedInputTokens;
7445
+ else if (typeof usage.inputTokenDetails?.cacheReadTokens === "number") attributes["gen_ai.usage.cache_read_input_tokens"] = usage.inputTokenDetails.cacheReadTokens;
7446
+ if (typeof usage.inputTokenDetails?.cacheWriteTokens === "number") attributes["gen_ai.usage.cache_creation_input_tokens"] = usage.inputTokenDetails.cacheWriteTokens;
7447
+ };
7448
+ const verbatimStandardizedMessages = (event) => {
7449
+ const messages = [];
7450
+ const sys = event.instructions || event.system;
7451
+ if (typeof sys === "string" && sys.length > 0) messages.push({
7452
+ role: "system",
7453
+ content: sys
7454
+ });
7455
+ else if (Array.isArray(sys)) for (const m of sys) messages.push(m);
7456
+ else if (sys && typeof sys === "object") messages.push(sys);
7457
+ if (Array.isArray(event.messages)) for (const m of event.messages) messages.push(m);
7458
+ return messages;
7459
+ };
7460
+ const buildVerbatimOutputMessages = (content) => {
7461
+ if (!Array.isArray(content) || content.length === 0) return void 0;
7462
+ try {
7463
+ return JSON.stringify([{
7464
+ role: "assistant",
7465
+ content
7466
+ }]);
7467
+ } catch {
7468
+ return;
7469
+ }
7470
+ };
7471
+ const buildTextOutputMessages = (text) => text.length > 0 ? buildVerbatimOutputMessages([{
7472
+ type: "text",
7473
+ text
7474
+ }]) : void 0;
7475
+ const applyUsageToSpan = (span, usage) => {
7476
+ const attrs = {};
7477
+ applyUsage(attrs, usage);
7478
+ for (const [k, v] of Object.entries(attrs)) if (v !== void 0) span.setAttribute(k, v);
7479
+ };
7480
+ const applyFinishReason = (span, finishReason) => {
7481
+ span.setAttribute("gen_ai.response.finish_reason", finishReason);
7482
+ span.setAttribute("ai.response.finishReason", finishReason);
7483
+ };
7484
+ const applyRequestModelAttributes = (span, event) => {
7485
+ if (typeof event.provider === "string") {
7486
+ const provider = normalizeProvider$1(event.provider);
7487
+ if (provider) span.setAttribute(LaminarAttributes.PROVIDER, provider);
7488
+ span.setAttribute("ai.model.provider", event.provider);
7489
+ }
7490
+ if (typeof event.modelId === "string") {
7491
+ span.setAttribute(LaminarAttributes.REQUEST_MODEL, event.modelId);
7492
+ span.setAttribute("ai.model.id", event.modelId);
7493
+ }
7494
+ };
7495
+ const compareHrTime = (a, b) => {
7496
+ if (a[0] !== b[0]) return a[0] - b[0];
7497
+ return a[1] - b[1];
7498
+ };
7499
+ const readSpanEndTime = (span) => {
7500
+ const innerEnd = span._span?.endTime;
7501
+ if (innerEnd && (innerEnd[0] !== 0 || innerEnd[1] !== 0)) return innerEnd;
7502
+ const outerEnd = span.endTime;
7503
+ if (outerEnd && (outerEnd[0] !== 0 || outerEnd[1] !== 0)) return outerEnd;
7504
+ };
7505
+ const verbatimPromptString = (prompt) => {
7506
+ try {
7507
+ const serialized = stringifyPromptForTelemetry(prompt);
7508
+ return typeof serialized === "string" ? serialized : null;
7509
+ } catch {
7510
+ return null;
7511
+ }
7512
+ };
7513
+ const verbatimPromptMessages = (prompt) => {
7514
+ const serialized = verbatimPromptString(prompt);
7515
+ if (serialized === null) return null;
7516
+ try {
7517
+ const parsed = JSON.parse(serialized);
7518
+ return Array.isArray(parsed) ? parsed : null;
7519
+ } catch {
7520
+ return null;
7521
+ }
7522
+ };
7523
+ //#endregion
7715
7524
  //#region src/opentelemetry-lib/instrumentation/aisdk/base-language-model.ts
7716
7525
  /**
7717
7526
  * Base class for Laminar language model wrappers.
@@ -7754,7 +7563,7 @@ var BaseLaminarLanguageModel = class {
7754
7563
  async doGenerateOrStreamWithCaching(options, originalFn, buildFromCached) {
7755
7564
  if (!replayEnabled() || Laminar.debugRunLive) return originalFn(options);
7756
7565
  const spanToMark = peekActiveLlmSpan() ?? Laminar.getCurrentSpan();
7757
- const messages = extractInputMessages(options);
7566
+ const messages = verbatimPromptMessages(options.prompt);
7758
7567
  if (messages === null || messages.length === 0) return originalFn(options);
7759
7568
  const inputHash = debugInputHash(messages);
7760
7569
  const outcome = await getRuntime()?.lookupCache(inputHash) ?? { kind: "live" };
@@ -7802,17 +7611,45 @@ var BaseLaminarLanguageModel = class {
7802
7611
  }];
7803
7612
  if (output === null || typeof output !== "object") return [];
7804
7613
  const handleItem = (item) => {
7805
- if (item.type === "text") return [{
7806
- type: "text",
7807
- text: item.text ?? item.content ?? ""
7808
- }];
7809
- if (["tool-call", "tool_call"].includes(item.type)) return [{
7614
+ if (item.type === "text") {
7615
+ if (typeof item.text === "string") return [{
7616
+ ...item,
7617
+ type: "text",
7618
+ text: item.text
7619
+ }];
7620
+ return [{
7621
+ type: "text",
7622
+ text: item.content ?? ""
7623
+ }];
7624
+ }
7625
+ if (item.type === "tool-call") return [{
7626
+ ...item,
7810
7627
  type: "tool-call",
7811
7628
  toolCallId: item.toolCallId ?? item.id,
7812
7629
  toolName: item.toolName ?? item.name,
7813
- input: JSON.stringify(item.input ?? item.arguments)
7630
+ input: typeof item.input === "string" ? item.input : JSON.stringify(item.input ?? item.arguments)
7814
7631
  }];
7815
- if (["reasoning", "thinking"].includes(item.type)) return [{
7632
+ if (item.type === "tool_call") {
7633
+ const rawInput = item.input ?? item.arguments;
7634
+ return [{
7635
+ type: "tool-call",
7636
+ toolCallId: item.toolCallId ?? item.id,
7637
+ toolName: item.toolName ?? item.name,
7638
+ input: typeof rawInput === "string" ? rawInput : JSON.stringify(rawInput)
7639
+ }];
7640
+ }
7641
+ if (item.type === "reasoning") {
7642
+ if (typeof item.text === "string") return [{
7643
+ ...item,
7644
+ type: "reasoning",
7645
+ text: item.text
7646
+ }];
7647
+ return [{
7648
+ type: "reasoning",
7649
+ text: item.content ?? ""
7650
+ }];
7651
+ }
7652
+ if (item.type === "thinking") return [{
7816
7653
  type: "reasoning",
7817
7654
  text: item.text ?? item.content ?? ""
7818
7655
  }];
@@ -7878,7 +7715,8 @@ var LaminarLanguageModelV2 = class extends BaseLaminarLanguageModel {
7878
7715
  const id = `text-${textIndex++}`;
7879
7716
  parts.push({
7880
7717
  type: "text-start",
7881
- id
7718
+ id,
7719
+ providerMetadata: block.providerMetadata
7882
7720
  });
7883
7721
  parts.push({
7884
7722
  type: "text-delta",
@@ -7906,16 +7744,15 @@ var LaminarLanguageModelV2 = class extends BaseLaminarLanguageModel {
7906
7744
  id
7907
7745
  });
7908
7746
  parts.push({
7909
- type: "tool-call",
7910
- toolCallId: block.toolCallId,
7911
- toolName: block.toolName,
7912
- input: block.input
7747
+ ...block,
7748
+ type: "tool-call"
7913
7749
  });
7914
7750
  } else if (block.type === "reasoning") {
7915
7751
  const id = `reasoning-${reasoningIndex++}`;
7916
7752
  parts.push({
7917
7753
  type: "reasoning-start",
7918
- id
7754
+ id,
7755
+ providerMetadata: block.providerMetadata
7919
7756
  });
7920
7757
  parts.push({
7921
7758
  type: "reasoning-delta",
@@ -7980,7 +7817,8 @@ var LaminarLanguageModelV3 = class extends BaseLaminarLanguageModel {
7980
7817
  const id = `text-${textIndex++}`;
7981
7818
  parts.push({
7982
7819
  type: "text-start",
7983
- id
7820
+ id,
7821
+ providerMetadata: block.providerMetadata
7984
7822
  });
7985
7823
  parts.push({
7986
7824
  type: "text-delta",
@@ -8008,16 +7846,15 @@ var LaminarLanguageModelV3 = class extends BaseLaminarLanguageModel {
8008
7846
  id
8009
7847
  });
8010
7848
  parts.push({
8011
- type: "tool-call",
8012
- toolCallId: block.toolCallId,
8013
- toolName: block.toolName,
8014
- input: block.input
7849
+ ...block,
7850
+ type: "tool-call"
8015
7851
  });
8016
7852
  } else if (block.type === "reasoning") {
8017
7853
  const id = `reasoning-${reasoningIndex++}`;
8018
7854
  parts.push({
8019
7855
  type: "reasoning-start",
8020
- id
7856
+ id,
7857
+ providerMetadata: block.providerMetadata
8021
7858
  });
8022
7859
  parts.push({
8023
7860
  type: "reasoning-delta",
@@ -8082,7 +7919,8 @@ var LaminarLanguageModelV4 = class extends BaseLaminarLanguageModel {
8082
7919
  const id = `text-${textIndex++}`;
8083
7920
  parts.push({
8084
7921
  type: "text-start",
8085
- id
7922
+ id,
7923
+ providerMetadata: block.providerMetadata
8086
7924
  });
8087
7925
  parts.push({
8088
7926
  type: "text-delta",
@@ -8110,16 +7948,15 @@ var LaminarLanguageModelV4 = class extends BaseLaminarLanguageModel {
8110
7948
  id
8111
7949
  });
8112
7950
  parts.push({
8113
- type: "tool-call",
8114
- toolCallId: block.toolCallId,
8115
- toolName: block.toolName,
8116
- input: block.input
7951
+ ...block,
7952
+ type: "tool-call"
8117
7953
  });
8118
7954
  } else if (block.type === "reasoning") {
8119
7955
  const id = `reasoning-${reasoningIndex++}`;
8120
7956
  parts.push({
8121
7957
  type: "reasoning-start",
8122
- id
7958
+ id,
7959
+ providerMetadata: block.providerMetadata
8123
7960
  });
8124
7961
  parts.push({
8125
7962
  type: "reasoning-delta",
@@ -8199,136 +8036,94 @@ function wrapLanguageModel(languageModel) {
8199
8036
  return new LaminarLanguageModelV2(languageModel);
8200
8037
  }
8201
8038
  //#endregion
8202
- //#region src/opentelemetry-lib/instrumentation/aisdk/v7-integration/types.ts
8203
- const stepKey = (callId, stepNumber) => `${callId}:${stepNumber}`;
8204
- //#endregion
8205
- //#region src/opentelemetry-lib/instrumentation/aisdk/v7-integration/utils.ts
8206
- const serializeJSON$1 = (value) => {
8207
- if (typeof value === "string") return value;
8039
+ //#region src/opentelemetry-lib/instrumentation/aisdk/v7-integration/package-versions.ts
8040
+ let cached;
8041
+ const readPackageVersion = (specifier) => {
8208
8042
  try {
8209
- return JSON.stringify(value);
8043
+ const pkg = __require(`${specifier}/package.json`);
8044
+ return typeof pkg?.version === "string" ? pkg.version : void 0;
8210
8045
  } catch {
8211
- return "[unserializable]";
8046
+ return;
8212
8047
  }
8213
8048
  };
8214
- const normalizeProvider$1 = (provider) => {
8215
- if (!provider) return void 0;
8216
- return provider.split(".").shift()?.toLowerCase().trim() || provider.toLowerCase().trim() || void 0;
8217
- };
8218
- const applyUsage = (attributes, usage) => {
8219
- if (!usage || typeof usage !== "object") return;
8220
- if (typeof usage.inputTokens === "number") attributes[LaminarAttributes.INPUT_TOKEN_COUNT] = usage.inputTokens;
8221
- if (typeof usage.outputTokens === "number") attributes[LaminarAttributes.OUTPUT_TOKEN_COUNT] = usage.outputTokens;
8222
- if (typeof usage.totalTokens === "number") attributes[LaminarAttributes.TOTAL_TOKEN_COUNT] = usage.totalTokens;
8223
- else if (typeof usage.inputTokens === "number" || typeof usage.outputTokens === "number") attributes[LaminarAttributes.TOTAL_TOKEN_COUNT] = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
8224
- if (typeof usage.reasoningTokens === "number") attributes["gen_ai.usage.reasoning_tokens"] = usage.reasoningTokens;
8225
- else if (typeof usage.outputTokenDetails?.reasoningTokens === "number") attributes["gen_ai.usage.reasoning_tokens"] = usage.outputTokenDetails.reasoningTokens;
8226
- if (typeof usage.cachedInputTokens === "number") attributes["gen_ai.usage.cache_read_input_tokens"] = usage.cachedInputTokens;
8227
- else if (typeof usage.inputTokenDetails?.cacheReadTokens === "number") attributes["gen_ai.usage.cache_read_input_tokens"] = usage.inputTokenDetails.cacheReadTokens;
8228
- if (typeof usage.inputTokenDetails?.cacheWriteTokens === "number") attributes["gen_ai.usage.cache_creation_input_tokens"] = usage.inputTokenDetails.cacheWriteTokens;
8229
- };
8230
- const standardizedPromptToMessages = (event) => {
8231
- const messages = [];
8232
- const sys = event.instructions || event.system;
8233
- if (typeof sys === "string" && sys.length > 0) messages.push({
8234
- role: "system",
8235
- content: sys
8236
- });
8237
- else if (Array.isArray(sys)) for (const m of sys) messages.push(m);
8238
- else if (sys && typeof sys === "object") messages.push(sys);
8239
- if (Array.isArray(event.messages)) for (const m of event.messages) messages.push(convertRawStringTextToTextBlock(m));
8240
- return messages;
8241
- };
8242
- const convertRawStringTextToTextBlock = (m) => ({
8243
- ...m,
8244
- content: typeof m?.content === "string" ? [{
8245
- type: "text",
8246
- text: m.content
8247
- }] : m?.content
8248
- });
8249
- const normalizeToolCalls = (toolCalls) => {
8250
- if (!Array.isArray(toolCalls)) return [];
8251
- return toolCalls.map((tc) => ({
8252
- toolCallType: "function",
8253
- toolCallId: tc?.toolCallId,
8254
- toolName: tc?.toolName,
8255
- args: typeof tc?.input === "string" ? tc.input : serializeJSON$1(tc?.input ?? {})
8256
- }));
8257
- };
8258
- const buildGenAiOutputMessages = (args) => {
8259
- const parts = [];
8260
- if (args.reasoningText && args.reasoningText.length > 0) parts.push({
8261
- type: "thinking",
8262
- content: args.reasoningText
8263
- });
8264
- if (args.text && args.text.length > 0) parts.push({
8265
- type: "text",
8266
- content: args.text
8267
- });
8268
- for (const tc of args.toolCalls ?? []) {
8269
- let argsObj = tc.args;
8270
- if (typeof argsObj === "string") try {
8271
- argsObj = JSON.parse(argsObj);
8049
+ /**
8050
+ * A `node_modules` search-path entry is a package-manager-internal virtual
8051
+ * store (pnpm's `.pnpm`, yarn's `.yarn`, etc.) if any path segment starts
8052
+ * with `.` — excluding the `.`/`..` relative-path segments themselves, which
8053
+ * are unrelated. Packages resolved from inside such a store are some
8054
+ * dependency's OWN transitive deps (e.g. `ai`'s `@ai-sdk/provider`), not
8055
+ * what the host project declared, so those entries must be skipped.
8056
+ */
8057
+ const isPackageManagerInternalPath = (path) => path.split(sep).some((segment) => segment.startsWith(".") && segment !== "." && segment !== "..");
8058
+ /**
8059
+ * Given the ordered `node_modules` search-path list Node would use to
8060
+ * resolve a module from this file's location, find the closest directory
8061
+ * that actually has an `@ai-sdk` scope — skipping package-manager-internal
8062
+ * stores and return the packages listed under it. The first path with a
8063
+ * usable `@ai-sdk` directory wins; levels are not merged, so an unrelated
8064
+ * ancestor/monorepo-root's `@ai-sdk` scope is never mixed in once a closer
8065
+ * one has already matched.
8066
+ */
8067
+ const findAiSdkScopeEntries = (searchPaths) => {
8068
+ for (const path of searchPaths) {
8069
+ if (isPackageManagerInternalPath(path)) continue;
8070
+ try {
8071
+ const entries = readdirSync(join(path, "@ai-sdk"));
8072
+ if (entries.length > 0) return entries;
8272
8073
  } catch {}
8273
- parts.push({
8274
- type: "tool_call",
8275
- id: tc.toolCallId,
8276
- name: tc.toolName,
8277
- arguments: argsObj
8278
- });
8279
8074
  }
8280
- if (parts.length === 0) return void 0;
8281
- return JSON.stringify([{
8282
- role: "assistant",
8283
- parts
8284
- }]);
8075
+ return [];
8285
8076
  };
8286
- const extractTextFromContent = (content) => {
8287
- if (!Array.isArray(content)) return "";
8288
- return content.filter((p) => p && p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
8289
- };
8290
- const extractReasoningFromContent = (content) => {
8291
- if (!Array.isArray(content)) return "";
8292
- return content.filter((p) => p && p.type === "reasoning" && typeof p.text === "string").map((p) => p.text).join("");
8293
- };
8294
- const applyUsageToSpan = (span, usage) => {
8295
- const attrs = {};
8296
- applyUsage(attrs, usage);
8297
- for (const [k, v] of Object.entries(attrs)) if (v !== void 0) span.setAttribute(k, v);
8298
- };
8299
- const applyFinishReason = (span, finishReason) => {
8300
- span.setAttribute("gen_ai.response.finish_reason", finishReason);
8301
- span.setAttribute("ai.response.finishReason", finishReason);
8302
- };
8303
- const applyRequestModelAttributes = (span, event) => {
8304
- if (typeof event.provider === "string") {
8305
- const provider = normalizeProvider$1(event.provider);
8306
- if (provider) span.setAttribute(LaminarAttributes.PROVIDER, provider);
8307
- span.setAttribute("ai.model.provider", event.provider);
8077
+ const discoverAiSdkPackages = () => {
8078
+ const packages = {};
8079
+ let searchPaths;
8080
+ try {
8081
+ searchPaths = __require.resolve.paths("@ai-sdk/__probe__") ?? [];
8082
+ } catch {
8083
+ searchPaths = [];
8308
8084
  }
8309
- if (typeof event.modelId === "string") {
8310
- span.setAttribute(LaminarAttributes.REQUEST_MODEL, event.modelId);
8311
- span.setAttribute("ai.model.id", event.modelId);
8085
+ for (const entry of findAiSdkScopeEntries(searchPaths)) {
8086
+ const specifier = `@ai-sdk/${entry}`;
8087
+ const version = readPackageVersion(specifier);
8088
+ if (version) packages[specifier] = version;
8312
8089
  }
8090
+ return packages;
8313
8091
  };
8314
- const applyPromptMessages = (span, event) => {
8315
- const msgs = standardizedPromptToMessages(event);
8316
- if (msgs.length === 0) return;
8317
- const serialized = serializeJSON$1(msgs);
8318
- span.setAttribute("ai.prompt.messages", serialized);
8319
- span.setAttribute(SPAN_INPUT, serialized);
8320
- };
8321
- const compareHrTime = (a, b) => {
8322
- if (a[0] !== b[0]) return a[0] - b[0];
8323
- return a[1] - b[1];
8092
+ /**
8093
+ * Reads installed `ai` / `@ai-sdk/*` package versions once and caches the
8094
+ * result for the lifetime of the process — this is expected to be called on
8095
+ * every span creation, so the actual `require`/`readdirSync` work must not
8096
+ * repeat.
8097
+ */
8098
+ const getAiSdkPackageVersions = () => {
8099
+ if (cached) return cached;
8100
+ try {
8101
+ cached = {
8102
+ aiVersion: readPackageVersion("ai"),
8103
+ aiSdkPackages: discoverAiSdkPackages()
8104
+ };
8105
+ } catch {
8106
+ cached = { aiSdkPackages: {} };
8107
+ }
8108
+ return cached;
8324
8109
  };
8325
- const readSpanEndTime = (span) => {
8326
- const innerEnd = span._span?.endTime;
8327
- if (innerEnd && (innerEnd[0] !== 0 || innerEnd[1] !== 0)) return innerEnd;
8328
- const outerEnd = span.endTime;
8329
- if (outerEnd && (outerEnd[0] !== 0 || outerEnd[1] !== 0)) return outerEnd;
8110
+ /**
8111
+ * Flat span-attribute object for the `ai` package's instrumentation scope
8112
+ * plus one `lmnr.span.instrumentation.@ai-sdk/<pkg>.version` entry per
8113
+ * resolvable `@ai-sdk/*` package. Merge this into every span this
8114
+ * integration creates.
8115
+ */
8116
+ const buildAiSdkInstrumentationAttributes = () => {
8117
+ const { aiVersion, aiSdkPackages } = getAiSdkPackageVersions();
8118
+ const attributes = { [SPAN_INSTRUMENTATION_SCOPE_NAME]: "ai" };
8119
+ if (aiVersion) attributes[SPAN_INSTRUMENTATION_SCOPE_VERSION] = aiVersion;
8120
+ for (const [pkg, version] of Object.entries(aiSdkPackages)) attributes[`lmnr.span.instrumentation.${pkg}.version`] = version;
8121
+ return attributes;
8330
8122
  };
8331
8123
  //#endregion
8124
+ //#region src/opentelemetry-lib/instrumentation/aisdk/v7-integration/types.ts
8125
+ const stepKey = (callId, stepNumber) => `${callId}:${stepNumber}`;
8126
+ //#endregion
8332
8127
  //#region src/opentelemetry-lib/instrumentation/aisdk/v7-integration/index.ts
8333
8128
  /**
8334
8129
  * The AI SDK v7 diagnostics channel name. Every lifecycle event fires
@@ -8350,6 +8145,8 @@ var LaminarAiSdkTelemetry = class {
8350
8145
  this.llmByKey = /* @__PURE__ */ new Map();
8351
8146
  this.toolByCallId = /* @__PURE__ */ new Map();
8352
8147
  this.activeStreamStepByCallId = /* @__PURE__ */ new Map();
8148
+ this.promptByCallId = /* @__PURE__ */ new Map();
8149
+ this.logger = initializeLogger();
8353
8150
  this.onStart = (event) => {
8354
8151
  const callId = event?.callId;
8355
8152
  if (!callId) return;
@@ -8361,6 +8158,7 @@ var LaminarAiSdkTelemetry = class {
8361
8158
  kind: SpanKind.CLIENT,
8362
8159
  attributes: {
8363
8160
  [SPAN_TYPE]: "DEFAULT",
8161
+ ...buildAiSdkInstrumentationAttributes(),
8364
8162
  ...typeof event.functionId === "string" ? {
8365
8163
  "operation.name": event.functionId,
8366
8164
  "ai.prompt": "_"
@@ -8381,8 +8179,10 @@ var LaminarAiSdkTelemetry = class {
8381
8179
  if (typeof event.functionId === "string") span.setAttribute("ai.functionId", event.functionId);
8382
8180
  if (event.operationId === "ai.embed" || event.operationId === "ai.embedMany" || event.operationId === "ai.rerank") span.setAttribute(SPAN_TYPE, "LLM");
8383
8181
  if (this.recordInputs) {
8384
- if (Array.isArray(event.messages) || event.system !== void 0 || event.instructions !== void 0) applyPromptMessages(span, event);
8385
- else if (event.value !== void 0) span.setAttribute(SPAN_INPUT, serializeJSON$1(event.value));
8182
+ if (Array.isArray(event.messages) || event.system !== void 0 || event.instructions !== void 0) {
8183
+ const msgs = verbatimStandardizedMessages(event);
8184
+ if (msgs.length > 0) span.setAttribute(SPAN_INPUT, serializeJSON$1(msgs));
8185
+ } else if (event.value !== void 0) span.setAttribute(SPAN_INPUT, serializeJSON$1(event.value));
8386
8186
  else if (event.query !== void 0 || event.documents !== void 0) span.setAttribute(SPAN_INPUT, serializeJSON$1({
8387
8187
  query: event.query,
8388
8188
  documents: event.documents
@@ -8397,17 +8197,25 @@ var LaminarAiSdkTelemetry = class {
8397
8197
  });
8398
8198
  };
8399
8199
  this.onStepStart = (event) => {
8400
- if (!this.createStepSpan) return;
8401
8200
  const callId = event?.callId;
8402
8201
  const stepNumber = event?.stepNumber ?? 0;
8403
8202
  if (!callId) return;
8203
+ if (Array.isArray(event?.promptMessages)) {
8204
+ const serialized = verbatimPromptString(event.promptMessages);
8205
+ if (serialized !== null) this.promptByCallId.set(callId, serialized);
8206
+ }
8207
+ if (!this.createStepSpan) return;
8404
8208
  const op = this.operationByCallId.get(callId);
8405
8209
  if (!op) return;
8406
8210
  const span = getTracer().startSpan(`ai.step`, { kind: SpanKind.CLIENT }, op.ctx);
8407
8211
  const spanCtx = trace.setSpan(op.ctx, span);
8408
8212
  span.setAttribute(SPAN_TYPE, "DEFAULT");
8213
+ span.setAttributes(buildAiSdkInstrumentationAttributes());
8409
8214
  span.setAttribute("ai.step.number", stepNumber);
8410
- if (this.recordInputs) applyPromptMessages(span, event);
8215
+ if (this.recordInputs) {
8216
+ const msgs = verbatimStandardizedMessages(event);
8217
+ if (msgs.length > 0) span.setAttribute(SPAN_INPUT, serializeJSON$1(msgs));
8218
+ }
8411
8219
  this.stepByKey.set(stepKey(callId, stepNumber), {
8412
8220
  span,
8413
8221
  ctx: spanCtx,
@@ -8423,8 +8231,17 @@ var LaminarAiSdkTelemetry = class {
8423
8231
  if (!parentCtx) return;
8424
8232
  const span = getTracer().startSpan(`ai.llm ${event.provider ?? ""}:${event.modelId ?? ""}`, { kind: SpanKind.CLIENT }, parentCtx);
8425
8233
  span.setAttribute(SPAN_TYPE, "LLM");
8234
+ span.setAttributes(buildAiSdkInstrumentationAttributes());
8426
8235
  applyRequestModelAttributes(span, event);
8427
- if (this.recordInputs) applyPromptMessages(span, event);
8236
+ if (this.recordInputs) {
8237
+ if (event.tools) try {
8238
+ span.setAttribute("gen_ai.tool.definitions", JSON.stringify(event.tools));
8239
+ } catch (e) {
8240
+ this.logger.debug(`Failed to record span tool definitions: ${errorMessage(e)}`);
8241
+ }
8242
+ const serialized = this.promptByCallId.get(callId);
8243
+ if (serialized !== void 0) span.setAttribute("gen_ai.input.messages", serialized);
8244
+ }
8428
8245
  this.llmByKey.set(stepKey(callId, stepNumber), {
8429
8246
  span,
8430
8247
  textDeltas: []
@@ -8447,18 +8264,14 @@ var LaminarAiSdkTelemetry = class {
8447
8264
  if (typeof event.responseId === "string") span.setAttribute("gen_ai.response.id", event.responseId);
8448
8265
  applyUsageToSpan(span, event.usage);
8449
8266
  if (this.recordOutputs) {
8450
- const content = Array.isArray(event.content) ? event.content : void 0;
8451
- const outputMessages = buildGenAiOutputMessages({
8452
- text: extractTextFromContent(content),
8453
- reasoningText: extractReasoningFromContent(content),
8454
- toolCalls: normalizeToolCalls(content ? content.filter((p) => p && p.type === "tool-call") : [])
8455
- });
8267
+ const outputMessages = buildVerbatimOutputMessages(Array.isArray(event.content) ? event.content : void 0);
8456
8268
  if (outputMessages) span.setAttribute("gen_ai.output.messages", outputMessages);
8457
8269
  }
8458
8270
  span.setStatus({ code: SpanStatusCode.OK });
8459
8271
  span.end();
8460
8272
  removeActiveLlmSpan(span);
8461
8273
  this.llmByKey.delete(stepKey(callId, stepNumber));
8274
+ this.promptByCallId.delete(callId);
8462
8275
  };
8463
8276
  this.onChunk = (event) => {
8464
8277
  const chunk = event?.chunk;
@@ -8487,7 +8300,7 @@ var LaminarAiSdkTelemetry = class {
8487
8300
  const llm = this.llmByKey.get(key);
8488
8301
  if (llm) {
8489
8302
  if (this.recordOutputs && llm.textDeltas.length > 0) {
8490
- const outputMessages = buildGenAiOutputMessages({ text: llm.textDeltas.join("") });
8303
+ const outputMessages = buildTextOutputMessages(llm.textDeltas.join(""));
8491
8304
  if (outputMessages) llm.span.setAttribute("gen_ai.output.messages", outputMessages);
8492
8305
  }
8493
8306
  applyUsageToSpan(llm.span, event.usage);
@@ -8495,21 +8308,13 @@ var LaminarAiSdkTelemetry = class {
8495
8308
  removeActiveLlmSpan(llm.span);
8496
8309
  this.llmByKey.delete(key);
8497
8310
  }
8311
+ this.promptByCallId.delete(callId);
8498
8312
  const step = this.stepByKey.get(key);
8499
8313
  if (!step) return;
8500
8314
  if (typeof event.finishReason === "string") applyFinishReason(step.span, event.finishReason);
8501
8315
  if (this.recordOutputs) {
8502
- const outputPayload = {};
8503
- if (typeof event.text === "string" && event.text.length > 0) {
8504
- outputPayload.text = event.text;
8505
- step.span.setAttribute("ai.response.text", event.text);
8506
- }
8507
- const normalizedToolCallsList = normalizeToolCalls(event.toolCalls);
8508
- if (normalizedToolCallsList.length > 0) {
8509
- outputPayload.toolCalls = normalizedToolCallsList;
8510
- step.span.setAttribute("ai.response.toolCalls", serializeJSON$1(normalizedToolCallsList));
8511
- }
8512
- if (Object.keys(outputPayload).length > 0) step.span.setAttribute(SPAN_OUTPUT, serializeJSON$1(outputPayload));
8316
+ const outputMessages = buildVerbatimOutputMessages(Array.isArray(event.content) ? event.content : void 0);
8317
+ if (outputMessages) step.span.setAttribute(SPAN_OUTPUT, outputMessages);
8513
8318
  }
8514
8319
  step.span.setStatus({ code: SpanStatusCode.OK });
8515
8320
  step.span.end();
@@ -8522,7 +8327,12 @@ var LaminarAiSdkTelemetry = class {
8522
8327
  if (!op) return;
8523
8328
  const span = getTracer().startSpan(`ai.llm ${event.provider ?? ""}:${event.modelId ?? ""}`, { kind: SpanKind.CLIENT }, op.ctx);
8524
8329
  span.setAttribute(SPAN_TYPE, "LLM");
8330
+ span.setAttributes(buildAiSdkInstrumentationAttributes());
8525
8331
  applyRequestModelAttributes(span, event);
8332
+ if (this.recordInputs && Array.isArray(event?.promptMessages)) {
8333
+ const serialized = verbatimPromptString(event.promptMessages);
8334
+ if (serialized !== null) span.setAttribute("gen_ai.input.messages", serialized);
8335
+ }
8526
8336
  this.llmByKey.set(stepKey(callId, 0), {
8527
8337
  span,
8528
8338
  textDeltas: []
@@ -8542,7 +8352,7 @@ var LaminarAiSdkTelemetry = class {
8542
8352
  }
8543
8353
  applyUsageToSpan(llm.span, event.usage);
8544
8354
  if (this.recordOutputs && typeof event.objectText === "string") {
8545
- const outputMessages = buildGenAiOutputMessages({ text: event.objectText });
8355
+ const outputMessages = buildTextOutputMessages(event.objectText);
8546
8356
  if (outputMessages) llm.span.setAttribute("gen_ai.output.messages", outputMessages);
8547
8357
  }
8548
8358
  llm.span.setStatus({ code: SpanStatusCode.OK });
@@ -8561,6 +8371,7 @@ var LaminarAiSdkTelemetry = class {
8561
8371
  const span = getTracer().startSpan(`ai.tool ${toolName ?? "unknown"}`, { kind: SpanKind.CLIENT }, parentCtx);
8562
8372
  const spanCtx = trace.setSpan(parentCtx, span);
8563
8373
  span.setAttribute(SPAN_TYPE, "TOOL");
8374
+ span.setAttributes(buildAiSdkInstrumentationAttributes());
8564
8375
  if (toolName) span.setAttribute("ai.toolCall.name", toolName);
8565
8376
  span.setAttribute("ai.toolCall.id", toolCallId);
8566
8377
  if (this.recordInputs && toolCall?.input !== void 0) {
@@ -8638,17 +8449,8 @@ var LaminarAiSdkTelemetry = class {
8638
8449
  if (typeof event.finishReason === "string") applyFinishReason(op.span, event.finishReason);
8639
8450
  if (op.operationId === "ai.embed" || op.operationId === "ai.embedMany" || op.operationId === "ai.rerank") applyUsageToSpan(op.span, event.totalUsage ?? event.usage);
8640
8451
  if (this.recordOutputs) {
8641
- const hasText = typeof event.text === "string" && event.text.length > 0;
8642
- if (hasText) {
8643
- op.span.setAttribute("ai.response.text", event.text);
8644
- op.span.setAttribute(SPAN_OUTPUT, event.text);
8645
- }
8646
- const normalizedToolCallsList = normalizeToolCalls(event.toolCalls);
8647
- if (normalizedToolCallsList.length > 0) {
8648
- const serialized = serializeJSON$1(normalizedToolCallsList);
8649
- op.span.setAttribute("ai.response.toolCalls", serialized);
8650
- if (!hasText) op.span.setAttribute(SPAN_OUTPUT, serialized);
8651
- }
8452
+ const outputMessages = buildVerbatimOutputMessages(Array.isArray(event.content) ? event.content : void 0);
8453
+ if (outputMessages) op.span.setAttribute(SPAN_OUTPUT, outputMessages);
8652
8454
  if (event.embedding !== void 0) {
8653
8455
  const emb = event.embedding;
8654
8456
  if (Array.isArray(emb) && Array.isArray(emb[0])) op.span.setAttribute("ai.embed.count", emb.length);
@@ -8752,7 +8554,7 @@ var LaminarAiSdkTelemetry = class {
8752
8554
  };
8753
8555
  for (const [key, llm] of this.llmByKey) if (key.startsWith(prefix)) {
8754
8556
  if (this.recordOutputs && llm.textDeltas.length > 0) {
8755
- const outputMessages = buildGenAiOutputMessages({ text: llm.textDeltas.join("") });
8557
+ const outputMessages = buildTextOutputMessages(llm.textDeltas.join(""));
8756
8558
  if (outputMessages) llm.span.setAttribute("gen_ai.output.messages", outputMessages);
8757
8559
  }
8758
8560
  llm.span.end();
@@ -8770,6 +8572,7 @@ var LaminarAiSdkTelemetry = class {
8770
8572
  for (const key of this.llmByKey.keys()) if (key.startsWith(prefix)) this.llmByKey.delete(key);
8771
8573
  for (const [toolCallId, tool] of this.toolByCallId) if (tool.callId === callId) this.toolByCallId.delete(toolCallId);
8772
8574
  for (const key of this.stepByKey.keys()) if (key.startsWith(prefix)) this.stepByKey.delete(key);
8575
+ this.promptByCallId.delete(callId);
8773
8576
  return latest;
8774
8577
  }
8775
8578
  };