@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.
- package/dist/cli.cjs +2 -2
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/{dist-muQLQb9o.cjs → dist-DGDsKbQ8.cjs} +17 -3
- package/dist/dist-DGDsKbQ8.cjs.map +1 -0
- package/dist/{dist-CdR_Phvb.mjs → dist-DVpPVAC5.mjs} +6 -4
- package/dist/dist-DVpPVAC5.mjs.map +1 -0
- package/dist/{evaluations-DpDwGehc.d.mts → evaluations-CzqDQADS.d.mts} +2 -30
- package/dist/{evaluations-D6_c4gW-.d.cts → evaluations-F6Zzae_Y.d.cts} +2 -30
- package/dist/{file-utils-BE3NvLX0.mjs → file-utils-D3Jj4yAt.mjs} +2 -2
- package/dist/{file-utils-BE3NvLX0.mjs.map → file-utils-D3Jj4yAt.mjs.map} +1 -1
- package/dist/{file-utils-C0oHtdus.cjs → file-utils-wA-Z77kv.cjs} +2 -2
- package/dist/{file-utils-C0oHtdus.cjs.map → file-utils-wA-Z77kv.cjs.map} +1 -1
- package/dist/index.cjs +299 -496
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13943 -7780
- package/dist/index.d.mts +13943 -7780
- package/dist/index.mjs +301 -498
- package/dist/index.mjs.map +1 -1
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.cjs +71 -55
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.cjs.map +1 -1
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.d.cts +4410 -61
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.d.mts +4410 -61
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.mjs +71 -55
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.mjs.map +1 -1
- package/package.json +26 -26
- package/dist/dist-CdR_Phvb.mjs.map +0 -1
- 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
|
|
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-
|
|
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`
|
|
7583
|
-
* - `
|
|
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
|
|
7587
|
-
* `role === "system"` message (
|
|
7588
|
-
* remaining array, then blake3 over its UTF-8 bytes and hex-encode the
|
|
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
|
-
*
|
|
7345
|
+
* Drop EVERY `role === "system"` message, matching the server.
|
|
7610
7346
|
*
|
|
7611
|
-
*
|
|
7612
|
-
*
|
|
7613
|
-
*
|
|
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
|
|
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
|
-
*
|
|
7354
|
+
* Compute the hex blake3 `input_hash` for a message array.
|
|
7635
7355
|
*
|
|
7636
|
-
*
|
|
7637
|
-
*
|
|
7638
|
-
*
|
|
7639
|
-
|
|
7640
|
-
|
|
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(
|
|
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 =
|
|
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")
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
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 (
|
|
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
|
-
|
|
7910
|
-
|
|
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
|
-
|
|
8012
|
-
|
|
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
|
-
|
|
8114
|
-
|
|
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/
|
|
8203
|
-
|
|
8204
|
-
|
|
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
|
-
|
|
8043
|
+
const pkg = __require(`${specifier}/package.json`);
|
|
8044
|
+
return typeof pkg?.version === "string" ? pkg.version : void 0;
|
|
8210
8045
|
} catch {
|
|
8211
|
-
return
|
|
8046
|
+
return;
|
|
8212
8047
|
}
|
|
8213
8048
|
};
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
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
|
-
|
|
8281
|
-
return JSON.stringify([{
|
|
8282
|
-
role: "assistant",
|
|
8283
|
-
parts
|
|
8284
|
-
}]);
|
|
8075
|
+
return [];
|
|
8285
8076
|
};
|
|
8286
|
-
const
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
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
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
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
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
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
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
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)
|
|
8385
|
-
|
|
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)
|
|
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)
|
|
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
|
|
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 =
|
|
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
|
|
8503
|
-
if (
|
|
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 =
|
|
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
|
|
8642
|
-
if (
|
|
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 =
|
|
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
|
};
|