@glassflow-ai/rius 0.8.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -7
- package/dist/index.cjs +2566 -483
- package/dist/index.d.cts +284 -14
- package/dist/index.d.ts +284 -14
- package/dist/index.js +2543 -449
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -17,7 +17,73 @@ var init_esm_shims = __esm({
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
// node_modules/@opentelemetry/core/build/esm/platform/node/environment.js
|
|
21
|
+
import { diag } from "@opentelemetry/api";
|
|
22
|
+
import { inspect } from "util";
|
|
23
|
+
function getNumberFromEnv(key) {
|
|
24
|
+
const raw = process.env[key];
|
|
25
|
+
if (raw == null || raw.trim() === "") {
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
const value = Number(raw);
|
|
29
|
+
if (isNaN(value)) {
|
|
30
|
+
diag.warn(`Unknown value ${inspect(raw)} for ${key}, expected a number, using defaults`);
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
var init_environment = __esm({
|
|
36
|
+
"node_modules/@opentelemetry/core/build/esm/platform/node/environment.js"() {
|
|
37
|
+
"use strict";
|
|
38
|
+
init_esm_shims();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// node_modules/@opentelemetry/core/build/esm/platform/node/index.js
|
|
43
|
+
var init_node = __esm({
|
|
44
|
+
"node_modules/@opentelemetry/core/build/esm/platform/node/index.js"() {
|
|
45
|
+
"use strict";
|
|
46
|
+
init_esm_shims();
|
|
47
|
+
init_environment();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// node_modules/@opentelemetry/core/build/esm/platform/index.js
|
|
52
|
+
var init_platform = __esm({
|
|
53
|
+
"node_modules/@opentelemetry/core/build/esm/platform/index.js"() {
|
|
54
|
+
"use strict";
|
|
55
|
+
init_esm_shims();
|
|
56
|
+
init_node();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// node_modules/@opentelemetry/core/build/esm/ExportResult.js
|
|
61
|
+
var ExportResultCode;
|
|
62
|
+
var init_ExportResult = __esm({
|
|
63
|
+
"node_modules/@opentelemetry/core/build/esm/ExportResult.js"() {
|
|
64
|
+
"use strict";
|
|
65
|
+
init_esm_shims();
|
|
66
|
+
(function(ExportResultCode2) {
|
|
67
|
+
ExportResultCode2[ExportResultCode2["SUCCESS"] = 0] = "SUCCESS";
|
|
68
|
+
ExportResultCode2[ExportResultCode2["FAILED"] = 1] = "FAILED";
|
|
69
|
+
})(ExportResultCode || (ExportResultCode = {}));
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// node_modules/@opentelemetry/core/build/esm/index.js
|
|
74
|
+
var init_esm = __esm({
|
|
75
|
+
"node_modules/@opentelemetry/core/build/esm/index.js"() {
|
|
76
|
+
"use strict";
|
|
77
|
+
init_esm_shims();
|
|
78
|
+
init_ExportResult();
|
|
79
|
+
init_platform();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
20
83
|
// src/config.ts
|
|
84
|
+
function namedAgent(agentName) {
|
|
85
|
+
return agentName === void 0 || agentName === DEFAULT_SERVICE_NAME ? void 0 : agentName;
|
|
86
|
+
}
|
|
21
87
|
function bool(name, raw, fallback) {
|
|
22
88
|
if (raw === void 0) return fallback;
|
|
23
89
|
const value = raw.trim().toLowerCase();
|
|
@@ -50,6 +116,21 @@ function clamp(name, value, min, max, fallback) {
|
|
|
50
116
|
console.warn(`[rius] ${name}=${value} is outside [${min}, ${max}]; clamped to ${clamped}.`);
|
|
51
117
|
return clamped;
|
|
52
118
|
}
|
|
119
|
+
function serviceVersionFromOtelEnv(raw) {
|
|
120
|
+
if (raw === void 0) return void 0;
|
|
121
|
+
for (const pair of raw.split(",")) {
|
|
122
|
+
const separator = pair.indexOf("=");
|
|
123
|
+
if (separator === -1) continue;
|
|
124
|
+
if (pair.slice(0, separator).trim() !== "service.version") continue;
|
|
125
|
+
const value = pair.slice(separator + 1).trim();
|
|
126
|
+
try {
|
|
127
|
+
return decodeURIComponent(value) || void 0;
|
|
128
|
+
} catch {
|
|
129
|
+
return value || void 0;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
53
134
|
function resolveConfig(options = {}, env = process.env) {
|
|
54
135
|
const endpoint = (options.endpoint ?? env.RIUS_ENDPOINT ?? DEFAULT_ENDPOINT).replace(/\/+$/, "");
|
|
55
136
|
const serviceName = options.serviceName ?? env.RIUS_SERVICE_NAME ?? DEFAULT_SERVICE_NAME;
|
|
@@ -71,6 +152,11 @@ function resolveConfig(options = {}, env = process.env) {
|
|
|
71
152
|
endpoint,
|
|
72
153
|
apiKey: options.apiKey ?? env.RIUS_API_KEY,
|
|
73
154
|
serviceName,
|
|
155
|
+
// Empty is unset, as it is for the agent name and the session id: a blank
|
|
156
|
+
// version would stamp `service.version=""` on every span, which reads as
|
|
157
|
+
// a real (and wrong) answer rather than as no answer. Falls through to
|
|
158
|
+
// the OTel environment last, and to nothing after that.
|
|
159
|
+
serviceVersion: options.serviceVersion || env.RIUS_SERVICE_VERSION || serviceVersionFromOtelEnv(env.OTEL_RESOURCE_ATTRIBUTES) || void 0,
|
|
74
160
|
disabled: options.disabled ?? bool("RIUS_DISABLED", env.RIUS_DISABLED, false),
|
|
75
161
|
sampleRate: options.sampleRate ?? rate(env.RIUS_SAMPLE_RATE, 1),
|
|
76
162
|
captureContent: options.captureContent ?? bool("RIUS_CAPTURE_CONTENT", env.RIUS_CAPTURE_CONTENT, true),
|
|
@@ -81,11 +167,22 @@ function resolveConfig(options = {}, env = process.env) {
|
|
|
81
167
|
// Empty falls through to serviceName rather than shipping a blank identity
|
|
82
168
|
// on every heartbeat payload, so `??` would be wrong here.
|
|
83
169
|
agentName: options.agentName || env.RIUS_AGENT_NAME || serviceName,
|
|
170
|
+
// Derived from the SAME value, not resolved a second time: the
|
|
171
|
+
// main-agent name is a move of the existing agent name into our
|
|
172
|
+
// namespace, so a new option here would let the two disagree.
|
|
173
|
+
mainAgentName: namedAgent(options.agentName || env.RIUS_AGENT_NAME || serviceName),
|
|
174
|
+
// Empty is unset, as everywhere above. None of the three has a fallback:
|
|
175
|
+
// there is no service-level id, description or agent version to borrow,
|
|
176
|
+
// and inventing one would repeat the `unknown_service` mistake.
|
|
177
|
+
mainAgentId: options.mainAgentId || env.RIUS_MAIN_AGENT_ID || void 0,
|
|
178
|
+
mainAgentDescription: options.mainAgentDescription || env.RIUS_MAIN_AGENT_DESCRIPTION || void 0,
|
|
179
|
+
mainAgentVersion: options.mainAgentVersion || env.RIUS_MAIN_AGENT_VERSION || void 0,
|
|
84
180
|
partialSpans: options.partialSpans ?? bool("RIUS_PARTIAL_SPANS", env.RIUS_PARTIAL_SPANS, false),
|
|
85
181
|
partialSpansDelayMs: partialSpansDelaySeconds * 1e3,
|
|
86
182
|
// Empty is unset, like agentName: a blank session id would group every
|
|
87
183
|
// span under the meaningless session "".
|
|
88
|
-
sessionId: options.sessionId || env.RIUS_SESSION_ID || void 0
|
|
184
|
+
sessionId: options.sessionId || env.RIUS_SESSION_ID || void 0,
|
|
185
|
+
bridgeForeignProvider: options.bridgeForeignProvider ?? bool("RIUS_BRIDGE_FOREIGN_PROVIDER", env.RIUS_BRIDGE_FOREIGN_PROVIDER, false)
|
|
89
186
|
};
|
|
90
187
|
}
|
|
91
188
|
var DEFAULT_ENDPOINT, DEFAULT_SERVICE_NAME, HEARTBEAT_INTERVAL_MIN, HEARTBEAT_INTERVAL_MAX, DEFAULT_HEARTBEAT_INTERVAL, PARTIAL_SPANS_DELAY_MIN, PARTIAL_SPANS_DELAY_MAX, DEFAULT_PARTIAL_SPANS_DELAY, AFFIRMATIVE, NEGATIVE;
|
|
@@ -106,6 +203,511 @@ var init_config = __esm({
|
|
|
106
203
|
}
|
|
107
204
|
});
|
|
108
205
|
|
|
206
|
+
// src/semconv.ts
|
|
207
|
+
import { SpanKind as OtelSpanKind } from "@opentelemetry/api";
|
|
208
|
+
import { ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
209
|
+
function requestAttributeKey(parameter) {
|
|
210
|
+
return Object.hasOwn(GEN_AI_REQUEST_PARAMETERS, parameter) ? GEN_AI_REQUEST_PARAMETERS[parameter] : `${RIUS_REQUEST_PREFIX}${parameter}`;
|
|
211
|
+
}
|
|
212
|
+
function operationForKind(kind) {
|
|
213
|
+
return Object.hasOwn(OPERATION_BY_KIND, kind) ? OPERATION_BY_KIND[kind] : void 0;
|
|
214
|
+
}
|
|
215
|
+
function kindForOperation(operation) {
|
|
216
|
+
return Object.hasOwn(KIND_BY_OPERATION, operation) ? KIND_BY_OPERATION[operation] : void 0;
|
|
217
|
+
}
|
|
218
|
+
function otelSpanKind(kind) {
|
|
219
|
+
return OTEL_KIND_BY_KIND[kind];
|
|
220
|
+
}
|
|
221
|
+
function kindAttributes(kind, toolName2) {
|
|
222
|
+
const attributes = { [OPENINFERENCE_SPAN_KIND]: kind };
|
|
223
|
+
const operation = OPERATION_BY_KIND[kind];
|
|
224
|
+
if (operation !== void 0) attributes[GEN_AI_OPERATION_NAME] = operation;
|
|
225
|
+
if (kind === "TOOL" /* TOOL */ && toolName2 !== void 0) attributes[GEN_AI_TOOL_NAME] = toolName2;
|
|
226
|
+
return attributes;
|
|
227
|
+
}
|
|
228
|
+
function composeSpanName(kind, attributes) {
|
|
229
|
+
const operation = attributes[GEN_AI_OPERATION_NAME];
|
|
230
|
+
if (typeof operation !== "string") return CHAIN_SPAN_NAME;
|
|
231
|
+
const targetKey = NAME_TARGET_BY_KIND[kind];
|
|
232
|
+
const target = targetKey === void 0 ? void 0 : attributes[targetKey];
|
|
233
|
+
return typeof target === "string" && target !== "" ? `${operation} ${target}` : operation;
|
|
234
|
+
}
|
|
235
|
+
var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_RESPONSE_ID, GEN_AI_OUTPUT_TYPE, GEN_AI_REQUEST_STREAM, GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK, GEN_AI_REQUEST_TEMPERATURE, GEN_AI_REQUEST_TOP_P, GEN_AI_REQUEST_TOP_K, GEN_AI_REQUEST_MAX_TOKENS, GEN_AI_REQUEST_FREQUENCY_PENALTY, GEN_AI_REQUEST_PRESENCE_PENALTY, GEN_AI_REQUEST_SEED, GEN_AI_REQUEST_STOP_SEQUENCES, GEN_AI_REQUEST_CHOICE_COUNT, GEN_AI_REQUEST_ENCODING_FORMATS, GEN_AI_REQUEST_PREVIOUS_RESPONSE_ID, GEN_AI_REQUEST_STREAM_CURSOR, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_CALL_ID, GEN_AI_TOOL_TYPE, GEN_AI_DATA_SOURCE_ID, GEN_AI_RETRIEVAL_TOP_K, GEN_AI_RETRIEVAL_DOCUMENTS, GEN_AI_AGENT_NAME, GEN_AI_AGENT_ID, GEN_AI_AGENT_VERSION, RIUS_MAIN_AGENT_NAME, RIUS_MAIN_AGENT_ID, RIUS_MAIN_AGENT_DESCRIPTION, RIUS_MAIN_AGENT_VERSION, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, RIUS_REQUEST_PREFIX, RIUS_REQUEST_TOOL_CHOICE, GEN_AI_REQUEST_PARAMETERS, MCP_METHOD_NAME, MCP_METHOD_TOOLS_CALL, MCP_PROTOCOL_VERSION, ERROR_TYPE, ERROR_TYPE_TOOL_ERROR, EXCEPTION_EVENT, EXCEPTION_TYPE, MCP_RESULT_TYPE, RIUS_CONTEXT_SIZES, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, KIND_BY_OPERATION, OTEL_KIND_BY_KIND, NAME_TARGET_BY_KIND, CHAIN_SPAN_NAME, LLM_INPUT_MESSAGES_PREFIX, LLM_OUTPUT_MESSAGES_PREFIX, LLM_MESSAGE_ROLE, LLM_MESSAGE_CONTENT, LLM_MESSAGE_TOOL_CALL_ID, LLM_MESSAGE_TOOL_CALLS_PREFIX, LLM_TOOL_CALL_PREFIX, LLM_TOOL_CALL_ID, LLM_TOOL_CALL_FUNCTION_NAME, LLM_TOOL_CALL_FUNCTION_ARGUMENTS, LLM_MESSAGE_CONTENTS_PREFIX, LLM_MESSAGE_CONTENT_PREFIX, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, THIRD_PARTY_CONTENT_ATTRIBUTES, CONTENT_ATTRIBUTES, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_PREFIX_IDENTITY_ATTRIBUTES, CONTENT_ATTRIBUTE_SUFFIXES, CONTENT_ATTRIBUTE_PREFIXED_SUFFIXES, RIUS_SPAN_PENDING, RIUS_PARENT_FOREIGN, RIUS_SDK_GLOBAL_PROVIDER, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
|
|
236
|
+
var init_semconv = __esm({
|
|
237
|
+
"src/semconv.ts"() {
|
|
238
|
+
"use strict";
|
|
239
|
+
init_esm_shims();
|
|
240
|
+
TRACER_NAME = "rius";
|
|
241
|
+
SERVICE_INSTANCE_ID = "service.instance.id";
|
|
242
|
+
OPENINFERENCE_SPAN_KIND = "openinference.span.kind";
|
|
243
|
+
INPUT_VALUE = "input.value";
|
|
244
|
+
OUTPUT_VALUE = "output.value";
|
|
245
|
+
SESSION_ID = "session.id";
|
|
246
|
+
USER_ID = "user.id";
|
|
247
|
+
WORKSPACE_ROUTE = "rius.workspace";
|
|
248
|
+
GEN_AI_OPERATION_NAME = "gen_ai.operation.name";
|
|
249
|
+
GEN_AI_PROVIDER_NAME = "gen_ai.provider.name";
|
|
250
|
+
GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
|
|
251
|
+
GEN_AI_REQUEST_REASONING_LEVEL = "gen_ai.request.reasoning.level";
|
|
252
|
+
GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
|
|
253
|
+
GEN_AI_RESPONSE_ID = "gen_ai.response.id";
|
|
254
|
+
GEN_AI_OUTPUT_TYPE = "gen_ai.output.type";
|
|
255
|
+
GEN_AI_REQUEST_STREAM = "gen_ai.request.stream";
|
|
256
|
+
GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK = "gen_ai.response.time_to_first_chunk";
|
|
257
|
+
GEN_AI_REQUEST_TEMPERATURE = "gen_ai.request.temperature";
|
|
258
|
+
GEN_AI_REQUEST_TOP_P = "gen_ai.request.top_p";
|
|
259
|
+
GEN_AI_REQUEST_TOP_K = "gen_ai.request.top_k";
|
|
260
|
+
GEN_AI_REQUEST_MAX_TOKENS = "gen_ai.request.max_tokens";
|
|
261
|
+
GEN_AI_REQUEST_FREQUENCY_PENALTY = "gen_ai.request.frequency_penalty";
|
|
262
|
+
GEN_AI_REQUEST_PRESENCE_PENALTY = "gen_ai.request.presence_penalty";
|
|
263
|
+
GEN_AI_REQUEST_SEED = "gen_ai.request.seed";
|
|
264
|
+
GEN_AI_REQUEST_STOP_SEQUENCES = "gen_ai.request.stop_sequences";
|
|
265
|
+
GEN_AI_REQUEST_CHOICE_COUNT = "gen_ai.request.choice.count";
|
|
266
|
+
GEN_AI_REQUEST_ENCODING_FORMATS = "gen_ai.request.encoding_formats";
|
|
267
|
+
GEN_AI_REQUEST_PREVIOUS_RESPONSE_ID = "gen_ai.request.previous_response.id";
|
|
268
|
+
GEN_AI_REQUEST_STREAM_CURSOR = "gen_ai.request.stream_cursor";
|
|
269
|
+
GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens";
|
|
270
|
+
GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
|
|
271
|
+
GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens";
|
|
272
|
+
GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS = "gen_ai.usage.cache_write.input_tokens";
|
|
273
|
+
GEN_AI_USAGE_REASONING_OUTPUT_TOKENS = "gen_ai.usage.reasoning.output_tokens";
|
|
274
|
+
GEN_AI_INPUT_MESSAGES = "gen_ai.input.messages";
|
|
275
|
+
GEN_AI_OUTPUT_MESSAGES = "gen_ai.output.messages";
|
|
276
|
+
GEN_AI_RESPONSE_FINISH_REASONS = "gen_ai.response.finish_reasons";
|
|
277
|
+
GEN_AI_TOOL_NAME = "gen_ai.tool.name";
|
|
278
|
+
GEN_AI_TOOL_CALL_ID = "gen_ai.tool.call.id";
|
|
279
|
+
GEN_AI_TOOL_TYPE = "gen_ai.tool.type";
|
|
280
|
+
GEN_AI_DATA_SOURCE_ID = "gen_ai.data_source.id";
|
|
281
|
+
GEN_AI_RETRIEVAL_TOP_K = "gen_ai.retrieval.top_k";
|
|
282
|
+
GEN_AI_RETRIEVAL_DOCUMENTS = "gen_ai.retrieval.documents";
|
|
283
|
+
GEN_AI_AGENT_NAME = "gen_ai.agent.name";
|
|
284
|
+
GEN_AI_AGENT_ID = "gen_ai.agent.id";
|
|
285
|
+
GEN_AI_AGENT_VERSION = "gen_ai.agent.version";
|
|
286
|
+
RIUS_MAIN_AGENT_NAME = "rius.main_agent.name";
|
|
287
|
+
RIUS_MAIN_AGENT_ID = "rius.main_agent.id";
|
|
288
|
+
RIUS_MAIN_AGENT_DESCRIPTION = "rius.main_agent.description";
|
|
289
|
+
RIUS_MAIN_AGENT_VERSION = "rius.main_agent.version";
|
|
290
|
+
GEN_AI_TOOL_DEFINITIONS = "gen_ai.tool.definitions";
|
|
291
|
+
GEN_AI_REQUEST_PREFIX = "gen_ai.request.";
|
|
292
|
+
RIUS_REQUEST_PREFIX = "rius.request.";
|
|
293
|
+
RIUS_REQUEST_TOOL_CHOICE = "rius.request.tool_choice";
|
|
294
|
+
GEN_AI_REQUEST_PARAMETERS = {
|
|
295
|
+
// model: the request's model; also settable via the `model` option.
|
|
296
|
+
model: GEN_AI_REQUEST_MODEL,
|
|
297
|
+
// max_tokens: OpenAI Chat Completions `max_tokens`, its successor
|
|
298
|
+
// `max_completion_tokens`, the Responses API's `max_output_tokens`, and
|
|
299
|
+
// Google's `maxOutputTokens`.
|
|
300
|
+
max_tokens: GEN_AI_REQUEST_MAX_TOKENS,
|
|
301
|
+
maxTokens: GEN_AI_REQUEST_MAX_TOKENS,
|
|
302
|
+
max_completion_tokens: GEN_AI_REQUEST_MAX_TOKENS,
|
|
303
|
+
maxCompletionTokens: GEN_AI_REQUEST_MAX_TOKENS,
|
|
304
|
+
max_output_tokens: GEN_AI_REQUEST_MAX_TOKENS,
|
|
305
|
+
maxOutputTokens: GEN_AI_REQUEST_MAX_TOKENS,
|
|
306
|
+
// choice.count: "the target number of candidate completions to return":
|
|
307
|
+
// OpenAI `n`, Google `candidateCount`, Cohere `num_generations`.
|
|
308
|
+
"choice.count": GEN_AI_REQUEST_CHOICE_COUNT,
|
|
309
|
+
n: GEN_AI_REQUEST_CHOICE_COUNT,
|
|
310
|
+
candidate_count: GEN_AI_REQUEST_CHOICE_COUNT,
|
|
311
|
+
candidateCount: GEN_AI_REQUEST_CHOICE_COUNT,
|
|
312
|
+
num_generations: GEN_AI_REQUEST_CHOICE_COUNT,
|
|
313
|
+
temperature: GEN_AI_REQUEST_TEMPERATURE,
|
|
314
|
+
// top_p: Google `topP`, Cohere `p`.
|
|
315
|
+
top_p: GEN_AI_REQUEST_TOP_P,
|
|
316
|
+
topP: GEN_AI_REQUEST_TOP_P,
|
|
317
|
+
p: GEN_AI_REQUEST_TOP_P,
|
|
318
|
+
// top_k: the registry's own note names Anthropic `top_k`, Cohere `k` and
|
|
319
|
+
// Google `topK`, and says OpenAI's `top_logprobs` MUST NOT be reported here
|
|
320
|
+
// (it shapes the response, not the sampling). So top_logprobs is
|
|
321
|
+
// deliberately absent and lands in rius.request.*.
|
|
322
|
+
top_k: GEN_AI_REQUEST_TOP_K,
|
|
323
|
+
topK: GEN_AI_REQUEST_TOP_K,
|
|
324
|
+
k: GEN_AI_REQUEST_TOP_K,
|
|
325
|
+
// stop_sequences: OpenAI `stop`, Google `stopSequences`. `stop` is listed
|
|
326
|
+
// FIRST because the llm.invocation_parameters rule prefers it, and both
|
|
327
|
+
// paths must keep the same spelling when a request carries two.
|
|
328
|
+
stop: GEN_AI_REQUEST_STOP_SEQUENCES,
|
|
329
|
+
stop_sequences: GEN_AI_REQUEST_STOP_SEQUENCES,
|
|
330
|
+
stopSequences: GEN_AI_REQUEST_STOP_SEQUENCES,
|
|
331
|
+
frequency_penalty: GEN_AI_REQUEST_FREQUENCY_PENALTY,
|
|
332
|
+
frequencyPenalty: GEN_AI_REQUEST_FREQUENCY_PENALTY,
|
|
333
|
+
presence_penalty: GEN_AI_REQUEST_PRESENCE_PENALTY,
|
|
334
|
+
presencePenalty: GEN_AI_REQUEST_PRESENCE_PENALTY,
|
|
335
|
+
// encoding_formats: plural in the registry; OpenAI's embeddings endpoint
|
|
336
|
+
// sends the singular `encoding_format`, and the registry's note says some
|
|
337
|
+
// systems call these "embedding types" (Cohere `embedding_types`).
|
|
338
|
+
encoding_formats: GEN_AI_REQUEST_ENCODING_FORMATS,
|
|
339
|
+
encoding_format: GEN_AI_REQUEST_ENCODING_FORMATS,
|
|
340
|
+
encodingFormat: GEN_AI_REQUEST_ENCODING_FORMATS,
|
|
341
|
+
embedding_types: GEN_AI_REQUEST_ENCODING_FORMATS,
|
|
342
|
+
seed: GEN_AI_REQUEST_SEED,
|
|
343
|
+
stream: GEN_AI_REQUEST_STREAM,
|
|
344
|
+
// reasoning.level: "the exact string value sent to the provider"; OpenAI
|
|
345
|
+
// sends it as `reasoning_effort`.
|
|
346
|
+
"reasoning.level": GEN_AI_REQUEST_REASONING_LEVEL,
|
|
347
|
+
reasoning_level: GEN_AI_REQUEST_REASONING_LEVEL,
|
|
348
|
+
reasoning_effort: GEN_AI_REQUEST_REASONING_LEVEL,
|
|
349
|
+
reasoningEffort: GEN_AI_REQUEST_REASONING_LEVEL,
|
|
350
|
+
// previous_response.id: the registry names OpenAI's `previous_response_id`
|
|
351
|
+
// and Google's `previous_interaction_id`.
|
|
352
|
+
"previous_response.id": GEN_AI_REQUEST_PREVIOUS_RESPONSE_ID,
|
|
353
|
+
previous_response_id: GEN_AI_REQUEST_PREVIOUS_RESPONSE_ID,
|
|
354
|
+
previousResponseId: GEN_AI_REQUEST_PREVIOUS_RESPONSE_ID,
|
|
355
|
+
previous_interaction_id: GEN_AI_REQUEST_PREVIOUS_RESPONSE_ID,
|
|
356
|
+
// stream_cursor: the registry names OpenAI's `starting_after` and Google's
|
|
357
|
+
// `last_event_id`.
|
|
358
|
+
stream_cursor: GEN_AI_REQUEST_STREAM_CURSOR,
|
|
359
|
+
starting_after: GEN_AI_REQUEST_STREAM_CURSOR,
|
|
360
|
+
last_event_id: GEN_AI_REQUEST_STREAM_CURSOR
|
|
361
|
+
};
|
|
362
|
+
MCP_METHOD_NAME = "mcp.method.name";
|
|
363
|
+
MCP_METHOD_TOOLS_CALL = "tools/call";
|
|
364
|
+
MCP_PROTOCOL_VERSION = "mcp.protocol.version";
|
|
365
|
+
ERROR_TYPE = "error.type";
|
|
366
|
+
ERROR_TYPE_TOOL_ERROR = "tool_error";
|
|
367
|
+
EXCEPTION_EVENT = "exception";
|
|
368
|
+
EXCEPTION_TYPE = "exception.type";
|
|
369
|
+
MCP_RESULT_TYPE = "mcp.result_type";
|
|
370
|
+
RIUS_CONTEXT_SIZES = "rius.context.sizes";
|
|
371
|
+
GEN_AI_FIRST_TOKEN_EVENT = "gen_ai.first_token";
|
|
372
|
+
SpanKind = /* @__PURE__ */ ((SpanKind2) => {
|
|
373
|
+
SpanKind2["AGENT"] = "AGENT";
|
|
374
|
+
SpanKind2["LLM"] = "LLM";
|
|
375
|
+
SpanKind2["TOOL"] = "TOOL";
|
|
376
|
+
SpanKind2["RETRIEVER"] = "RETRIEVER";
|
|
377
|
+
SpanKind2["EMBEDDING"] = "EMBEDDING";
|
|
378
|
+
SpanKind2["CHAIN"] = "CHAIN";
|
|
379
|
+
return SpanKind2;
|
|
380
|
+
})(SpanKind || {});
|
|
381
|
+
OPERATION_BY_KIND = {
|
|
382
|
+
["LLM" /* LLM */]: "chat",
|
|
383
|
+
["TOOL" /* TOOL */]: "execute_tool",
|
|
384
|
+
["EMBEDDING" /* EMBEDDING */]: "embeddings",
|
|
385
|
+
["AGENT" /* AGENT */]: "invoke_agent",
|
|
386
|
+
["RETRIEVER" /* RETRIEVER */]: "retrieval"
|
|
387
|
+
};
|
|
388
|
+
KIND_BY_OPERATION = {
|
|
389
|
+
chat: "LLM" /* LLM */,
|
|
390
|
+
text_completion: "LLM" /* LLM */,
|
|
391
|
+
generate_content: "LLM" /* LLM */,
|
|
392
|
+
execute_tool: "TOOL" /* TOOL */,
|
|
393
|
+
embeddings: "EMBEDDING" /* EMBEDDING */,
|
|
394
|
+
invoke_agent: "AGENT" /* AGENT */,
|
|
395
|
+
create_agent: "AGENT" /* AGENT */,
|
|
396
|
+
retrieval: "RETRIEVER" /* RETRIEVER */,
|
|
397
|
+
invoke_workflow: "CHAIN" /* CHAIN */,
|
|
398
|
+
plan: "CHAIN" /* CHAIN */
|
|
399
|
+
};
|
|
400
|
+
OTEL_KIND_BY_KIND = {
|
|
401
|
+
["LLM" /* LLM */]: OtelSpanKind.CLIENT,
|
|
402
|
+
["EMBEDDING" /* EMBEDDING */]: OtelSpanKind.CLIENT,
|
|
403
|
+
["RETRIEVER" /* RETRIEVER */]: OtelSpanKind.CLIENT,
|
|
404
|
+
["TOOL" /* TOOL */]: OtelSpanKind.INTERNAL,
|
|
405
|
+
["AGENT" /* AGENT */]: OtelSpanKind.INTERNAL,
|
|
406
|
+
["CHAIN" /* CHAIN */]: OtelSpanKind.INTERNAL
|
|
407
|
+
};
|
|
408
|
+
NAME_TARGET_BY_KIND = {
|
|
409
|
+
["LLM" /* LLM */]: GEN_AI_REQUEST_MODEL,
|
|
410
|
+
["EMBEDDING" /* EMBEDDING */]: GEN_AI_REQUEST_MODEL,
|
|
411
|
+
["TOOL" /* TOOL */]: GEN_AI_TOOL_NAME,
|
|
412
|
+
["AGENT" /* AGENT */]: GEN_AI_AGENT_NAME,
|
|
413
|
+
["RETRIEVER" /* RETRIEVER */]: GEN_AI_DATA_SOURCE_ID
|
|
414
|
+
};
|
|
415
|
+
CHAIN_SPAN_NAME = "chain";
|
|
416
|
+
LLM_INPUT_MESSAGES_PREFIX = "llm.input_messages.";
|
|
417
|
+
LLM_OUTPUT_MESSAGES_PREFIX = "llm.output_messages.";
|
|
418
|
+
LLM_MESSAGE_ROLE = "message.role";
|
|
419
|
+
LLM_MESSAGE_CONTENT = "message.content";
|
|
420
|
+
LLM_MESSAGE_TOOL_CALL_ID = "message.tool_call_id";
|
|
421
|
+
LLM_MESSAGE_TOOL_CALLS_PREFIX = "message.tool_calls.";
|
|
422
|
+
LLM_TOOL_CALL_PREFIX = "tool_call.";
|
|
423
|
+
LLM_TOOL_CALL_ID = "id";
|
|
424
|
+
LLM_TOOL_CALL_FUNCTION_NAME = "function.name";
|
|
425
|
+
LLM_TOOL_CALL_FUNCTION_ARGUMENTS = "function.arguments";
|
|
426
|
+
LLM_MESSAGE_CONTENTS_PREFIX = "message.contents.";
|
|
427
|
+
LLM_MESSAGE_CONTENT_PREFIX = "message_content.";
|
|
428
|
+
LLM_INVOCATION_PARAMETERS = "llm.invocation_parameters";
|
|
429
|
+
INVOCATION_PARAMETERS_CONTENT_MEMBERS = ["tools", "functions"];
|
|
430
|
+
THIRD_PARTY_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
431
|
+
// langfuse 3.15.0, langfuse/_client/attributes.py. Metadata is the caller's
|
|
432
|
+
// arbitrary payload (one key per member, see the prefixes below), and the
|
|
433
|
+
// OpenAI wrapper writes the exception's message into status_message.
|
|
434
|
+
"langfuse.observation.input",
|
|
435
|
+
"langfuse.observation.output",
|
|
436
|
+
"langfuse.trace.input",
|
|
437
|
+
"langfuse.trace.output",
|
|
438
|
+
"langfuse.observation.metadata",
|
|
439
|
+
"langfuse.trace.metadata",
|
|
440
|
+
"langfuse.observation.status_message",
|
|
441
|
+
"langfuse.experiment.metadata",
|
|
442
|
+
"langfuse.experiment.item.metadata",
|
|
443
|
+
"langfuse.experiment.item.expected_output",
|
|
444
|
+
// traceloop-sdk 0.62.3 (opentelemetry-semantic-conventions-ai 0.5.1):
|
|
445
|
+
// workflow/task I/O and the managed prompt's template and variables; the
|
|
446
|
+
// LangChain instrumentation mirrors entity I/O into gen_ai.task.*, and the
|
|
447
|
+
// MCP one writes each tool result to mcp.response.value.
|
|
448
|
+
"traceloop.entity.input",
|
|
449
|
+
"traceloop.entity.output",
|
|
450
|
+
"traceloop.prompt.template",
|
|
451
|
+
"traceloop.prompt.template_variables",
|
|
452
|
+
"gen_ai.task.input",
|
|
453
|
+
"gen_ai.task.output",
|
|
454
|
+
"mcp.response.value",
|
|
455
|
+
// logfire 5.1.1: the OpenAI/Anthropic integrations put the request and the
|
|
456
|
+
// reply in request_data/response_data and the messages in events; the OpenAI
|
|
457
|
+
// Agents one adds raw_input, the Response object, and a function span's
|
|
458
|
+
// arguments and result under bare input/output.
|
|
459
|
+
"request_data",
|
|
460
|
+
"response_data",
|
|
461
|
+
"events",
|
|
462
|
+
"all_messages_events",
|
|
463
|
+
"pydantic_ai.all_messages",
|
|
464
|
+
"raw_input",
|
|
465
|
+
"response",
|
|
466
|
+
"input",
|
|
467
|
+
"output",
|
|
468
|
+
"logfire.msg",
|
|
469
|
+
// mlflow-tracing 3.16.1, mlflow/tracing/constant.py. chunk.value is a
|
|
470
|
+
// streamed chunk, written on span events.
|
|
471
|
+
"mlflow.spanInputs",
|
|
472
|
+
"mlflow.spanOutputs",
|
|
473
|
+
"mlflow.chat.tools",
|
|
474
|
+
"mlflow.trace.intermediate_outputs",
|
|
475
|
+
"mlflow.chunk.value",
|
|
476
|
+
// openlit 1.45.0, openlit/semcov/__init__.py, each one written by at least
|
|
477
|
+
// one of its instrumentations. gen_ai.retrieval.query.text is also a GenAI
|
|
478
|
+
// convention key: the user's query, verbatim.
|
|
479
|
+
"gen_ai.retrieval.query.text",
|
|
480
|
+
"gen_ai.content.reasoning",
|
|
481
|
+
"gen_ai.content.revised_prompt",
|
|
482
|
+
"gen_ai.tool.args",
|
|
483
|
+
"gen_ai.response.tool_calls",
|
|
484
|
+
"gen_ai.workflow.input",
|
|
485
|
+
"gen_ai.workflow.output",
|
|
486
|
+
"gen_ai.framework.pipeline.input_data",
|
|
487
|
+
"gen_ai.framework.pipeline.output_data",
|
|
488
|
+
"gen_ai.framework.error.message",
|
|
489
|
+
"gen_ai.agent.context",
|
|
490
|
+
"gen_ai.agent.instructions",
|
|
491
|
+
"gen_ai.agent.goal",
|
|
492
|
+
"gen_ai.agent.action.tool_input",
|
|
493
|
+
"gen_ai.agent.final_result",
|
|
494
|
+
"gen_ai.agent.next_goal",
|
|
495
|
+
"gen_ai.agent.step_messages",
|
|
496
|
+
"gen_ai.memory.search.query",
|
|
497
|
+
"gen_ai.vectordb.search.query",
|
|
498
|
+
"gen_ai.extraction.instruction",
|
|
499
|
+
"mcp.tool.arguments",
|
|
500
|
+
"mcp.tool.result",
|
|
501
|
+
"mcp.result",
|
|
502
|
+
"mcp.params",
|
|
503
|
+
"mcp.sampling.messages",
|
|
504
|
+
"mcp.fastmcp.prompt.arguments",
|
|
505
|
+
"mcp.completion.argument.value",
|
|
506
|
+
"mcp.completion.context.arguments",
|
|
507
|
+
"mcp.completion.values",
|
|
508
|
+
"mcp.error.message",
|
|
509
|
+
// lmnr 0.7.64 (Laminar; the TypeScript SDK writes the same wire keys): the
|
|
510
|
+
// observed function's arguments and return value, the caller's metadata
|
|
511
|
+
// (one key per member, see the prefixes below), and the provider's whole
|
|
512
|
+
// reply its Anthropic and LiteLLM instrumentations write.
|
|
513
|
+
"lmnr.span.input",
|
|
514
|
+
"lmnr.span.output",
|
|
515
|
+
"lmnr.association.properties.metadata",
|
|
516
|
+
"lmnr.sdk.raw.response"
|
|
517
|
+
]);
|
|
518
|
+
CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
519
|
+
INPUT_VALUE,
|
|
520
|
+
OUTPUT_VALUE,
|
|
521
|
+
GEN_AI_INPUT_MESSAGES,
|
|
522
|
+
GEN_AI_OUTPUT_MESSAGES,
|
|
523
|
+
// Sensitive per the GenAI conventions (semconv-genai#431): tool definitions
|
|
524
|
+
// routinely embed proprietary prompt engineering, and sometimes credentials
|
|
525
|
+
// or internal URLs in parameter defaults. gen_ai.tool.name stays: it is
|
|
526
|
+
// identity, not content.
|
|
527
|
+
"gen_ai.tool.description",
|
|
528
|
+
GEN_AI_TOOL_DEFINITIONS,
|
|
529
|
+
// GenAI semconv keys emitted by OTel-native instrumentations (@ai-sdk/otel,
|
|
530
|
+
// which init() registers for ai v7, among them; pinned 2026-09-14): the
|
|
531
|
+
// system prompt and each tool call's input and output are content in the
|
|
532
|
+
// same sense messages are. gen_ai.tool.call.id stays: identity.
|
|
533
|
+
"gen_ai.system_instructions",
|
|
534
|
+
"gen_ai.tool.call.arguments",
|
|
535
|
+
"gen_ai.tool.call.result",
|
|
536
|
+
// OpenInference TOOL-kind spans carry the definition under these two bare
|
|
537
|
+
// keys, sensitive for the reason gen_ai.tool.description is.
|
|
538
|
+
"tool.description",
|
|
539
|
+
"tool.parameters",
|
|
540
|
+
"gen_ai.prompt",
|
|
541
|
+
"gen_ai.completion",
|
|
542
|
+
"llm.input_messages",
|
|
543
|
+
"llm.output_messages",
|
|
544
|
+
// The prefix list below covers the flattened "llm.prompts." and
|
|
545
|
+
// "llm.prompt_template." forms, but an instrumentation may emit either as a
|
|
546
|
+
// single unflattened array attribute under the bare key, which no prefix
|
|
547
|
+
// matches. Both bare keys are therefore listed here as well.
|
|
548
|
+
"llm.prompts",
|
|
549
|
+
"llm.prompt_template",
|
|
550
|
+
// Every bundled OpenInference instrumentation emits tool definitions as
|
|
551
|
+
// llm.tools.{i}.tool.json_schema (pinned empirically 2026-09-14); covered
|
|
552
|
+
// by prefix below, bare key listed per the bare-key rule.
|
|
553
|
+
"llm.tools",
|
|
554
|
+
// The Vercel AI SDK's own telemetry keys survive the OpenInference
|
|
555
|
+
// transform untouched, and they carry the full prompt (messages AND tool
|
|
556
|
+
// definitions), response content, and tool-call I/O. Names, ids and models
|
|
557
|
+
// (ai.toolCall.name, ai.response.model, ...) are identity and stay.
|
|
558
|
+
"ai.prompt",
|
|
559
|
+
"ai.response.text",
|
|
560
|
+
"ai.response.object",
|
|
561
|
+
"ai.toolCall.args",
|
|
562
|
+
"ai.toolCall.result",
|
|
563
|
+
// Same family, keys enumerated from the ai v7 telemetry surface
|
|
564
|
+
// (2026-09-14): model reasoning, tool calls in the response, response
|
|
565
|
+
// files, embedding inputs, rerank documents, and the generateObject schema
|
|
566
|
+
// (a tool definition by another name).
|
|
567
|
+
"ai.response.reasoning",
|
|
568
|
+
"ai.response.toolCalls",
|
|
569
|
+
"ai.response.files",
|
|
570
|
+
"ai.value",
|
|
571
|
+
"ai.values",
|
|
572
|
+
"ai.documents",
|
|
573
|
+
"ai.schema",
|
|
574
|
+
"ai.schema.description",
|
|
575
|
+
// The OpenInference request-parameters bag, whole. Its membership is open
|
|
576
|
+
// and provider-defined (litellm and langchain put the request's tools array
|
|
577
|
+
// in it, and providers are free to add anything else), so a per-member
|
|
578
|
+
// blocklist is always one provider behind. Treating the whole bag as
|
|
579
|
+
// content is only safe because normalization runs BEFORE masking and has
|
|
580
|
+
// already promoted every member the rule table recognises onto
|
|
581
|
+
// gen_ai.request.*, so what reaches masking is exactly the set nothing has
|
|
582
|
+
// classified. A knob that matters is recovered by teaching the rule table
|
|
583
|
+
// its name, which is a reviewed decision; the default is that unclassified
|
|
584
|
+
// request data does not leave a process that asked for no content.
|
|
585
|
+
LLM_INVOCATION_PARAMETERS,
|
|
586
|
+
// The NAMED EXCEPTION to the rule that request parameters export in clear.
|
|
587
|
+
// That rule is right for scalar knobs (temperature, top_p, seed), but a
|
|
588
|
+
// parameter bag is not typed: a caller passing modelParameters: { tools }
|
|
589
|
+
// would otherwise export proprietary prompt engineering verbatim with
|
|
590
|
+
// captureContent: false explicitly set, while the very same array is
|
|
591
|
+
// stripped when it arrives as gen_ai.tool.definitions or inside
|
|
592
|
+
// llm.invocation_parameters. Per KEY, not per namespace: both namespaces
|
|
593
|
+
// stay non-content wholesale, which is what keeps their tie intact.
|
|
594
|
+
...[GEN_AI_REQUEST_PREFIX, RIUS_REQUEST_PREFIX].flatMap(
|
|
595
|
+
(prefix) => INVOCATION_PARAMETERS_CONTENT_MEMBERS.map((member) => `${prefix}${member}`)
|
|
596
|
+
),
|
|
597
|
+
...THIRD_PARTY_CONTENT_ATTRIBUTES
|
|
598
|
+
]);
|
|
599
|
+
CONTENT_ATTRIBUTE_PREFIXES = [
|
|
600
|
+
"llm.input_messages.",
|
|
601
|
+
"llm.output_messages.",
|
|
602
|
+
"gen_ai.prompt.",
|
|
603
|
+
"gen_ai.completion.",
|
|
604
|
+
"llm.prompts.",
|
|
605
|
+
"llm.prompt_template.",
|
|
606
|
+
"llm.tools.",
|
|
607
|
+
"ai.prompt.",
|
|
608
|
+
"langfuse.observation.metadata.",
|
|
609
|
+
"langfuse.trace.metadata.",
|
|
610
|
+
"traceloop.prompt.template_variables.",
|
|
611
|
+
"lmnr.association.properties.metadata."
|
|
612
|
+
];
|
|
613
|
+
CONTENT_PREFIX_IDENTITY_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
614
|
+
"lmnr.association.properties.metadata.tool_call_id",
|
|
615
|
+
"lmnr.association.properties.metadata.agent.name",
|
|
616
|
+
"lmnr.association.properties.metadata.service.name"
|
|
617
|
+
]);
|
|
618
|
+
CONTENT_ATTRIBUTE_SUFFIXES = [
|
|
619
|
+
".document.content",
|
|
620
|
+
".embedding.text"
|
|
621
|
+
];
|
|
622
|
+
CONTENT_ATTRIBUTE_PREFIXED_SUFFIXES = [["llm.request.functions.", [".description", ".parameters", ".arguments"]]];
|
|
623
|
+
RIUS_SPAN_PENDING = "rius.span.pending";
|
|
624
|
+
RIUS_PARENT_FOREIGN = "rius.parent.foreign";
|
|
625
|
+
RIUS_SDK_GLOBAL_PROVIDER = "rius.sdk.global_provider";
|
|
626
|
+
PENDING_IDENTITY_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
627
|
+
OPENINFERENCE_SPAN_KIND,
|
|
628
|
+
GEN_AI_OPERATION_NAME,
|
|
629
|
+
GEN_AI_PROVIDER_NAME,
|
|
630
|
+
GEN_AI_TOOL_NAME,
|
|
631
|
+
// The two other halves of a tool call's identity, both caller-supplied at
|
|
632
|
+
// creation: which model tool-call this execution answers, and what kind of
|
|
633
|
+
// tool it is. A tool that hangs is exactly the span a live view needs to
|
|
634
|
+
// show attached to its call, so both must survive the snapshot.
|
|
635
|
+
GEN_AI_TOOL_CALL_ID,
|
|
636
|
+
GEN_AI_TOOL_TYPE,
|
|
637
|
+
// A property of the request, like the model parameters the prefix rule
|
|
638
|
+
// covers — but spelled outside `gen_ai.request.`, so it needs its own
|
|
639
|
+
// entry here or it would be silently dropped from every snapshot.
|
|
640
|
+
GEN_AI_OUTPUT_TYPE,
|
|
641
|
+
// The retrieval target is identity in the same sense the tool name is: a
|
|
642
|
+
// still-running retrieval must be attributable to the index it is hitting.
|
|
643
|
+
GEN_AI_DATA_SOURCE_ID,
|
|
644
|
+
// Equally a property of the request, so equally knowable at start. Its
|
|
645
|
+
// counterpart GEN_AI_RETRIEVAL_DOCUMENTS is deliberately absent: what came
|
|
646
|
+
// back cannot be known while the span is open.
|
|
647
|
+
GEN_AI_RETRIEVAL_TOP_K,
|
|
648
|
+
// Which agent a span invokes is chosen before the work starts, so a
|
|
649
|
+
// still-running agent is attributable in the live view. Distinct from the
|
|
650
|
+
// RESOURCE key of the same name, which says which PROCESS is running; these
|
|
651
|
+
// say which agent that process invoked here.
|
|
652
|
+
GEN_AI_AGENT_NAME,
|
|
653
|
+
GEN_AI_AGENT_ID,
|
|
654
|
+
// Same callee, same reason: the version of the agent being invoked is
|
|
655
|
+
// chosen before the work starts, so a still-running agent span can say
|
|
656
|
+
// which version of it is running.
|
|
657
|
+
GEN_AI_AGENT_VERSION,
|
|
658
|
+
// Protocol identity, not content: a still-running MCP call must be
|
|
659
|
+
// distinguishable from a local tool in the live view — the one place
|
|
660
|
+
// setting the marker at creation pays off.
|
|
661
|
+
MCP_METHOD_NAME,
|
|
662
|
+
MCP_PROTOCOL_VERSION,
|
|
663
|
+
// Identity, not content: a pending span must be groupable into its
|
|
664
|
+
// session while still running, that is the live view's whole point.
|
|
665
|
+
SESSION_ID,
|
|
666
|
+
// Same reason: a crashed run's partial spans must still be attributable
|
|
667
|
+
// to the user they served.
|
|
668
|
+
USER_ID,
|
|
669
|
+
// Routing, not content: a crashed run's snapshot must land in the same
|
|
670
|
+
// workspace its final span would have. Stripped at export either way.
|
|
671
|
+
WORKSPACE_ROUTE
|
|
672
|
+
]);
|
|
673
|
+
PENDING_IDENTITY_PREFIXES = [
|
|
674
|
+
GEN_AI_REQUEST_PREFIX,
|
|
675
|
+
RIUS_REQUEST_PREFIX
|
|
676
|
+
];
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
// src/agent.ts
|
|
681
|
+
import { context as apiContext, createContextKey } from "@opentelemetry/api";
|
|
682
|
+
function setConfiguredAgentName(name) {
|
|
683
|
+
configured = name;
|
|
684
|
+
}
|
|
685
|
+
function resolveAgentName(agentName, kind) {
|
|
686
|
+
if (kind !== "AGENT" /* AGENT */) return void 0;
|
|
687
|
+
if (agentName !== void 0) return agentName;
|
|
688
|
+
return namedConfiguredAgent();
|
|
689
|
+
}
|
|
690
|
+
function namedConfiguredAgent() {
|
|
691
|
+
return namedAgent(configured);
|
|
692
|
+
}
|
|
693
|
+
function withAgentScope(agentName, fn) {
|
|
694
|
+
return apiContext.with(apiContext.active().setValue(AGENT_SCOPE_KEY, agentName), fn);
|
|
695
|
+
}
|
|
696
|
+
function executingAgentName() {
|
|
697
|
+
const scoped = apiContext.active().getValue(AGENT_SCOPE_KEY);
|
|
698
|
+
return typeof scoped === "string" ? scoped : namedConfiguredAgent();
|
|
699
|
+
}
|
|
700
|
+
var configured, AGENT_SCOPE_KEY;
|
|
701
|
+
var init_agent = __esm({
|
|
702
|
+
"src/agent.ts"() {
|
|
703
|
+
"use strict";
|
|
704
|
+
init_esm_shims();
|
|
705
|
+
init_config();
|
|
706
|
+
init_semconv();
|
|
707
|
+
AGENT_SCOPE_KEY = createContextKey("rius-enclosing-agent-name");
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
|
|
109
711
|
// src/delegatingProcessor.ts
|
|
110
712
|
var DelegatingSpanProcessor;
|
|
111
713
|
var init_delegatingProcessor = __esm({
|
|
@@ -144,32 +746,10 @@ var init_delegatingProcessor = __esm({
|
|
|
144
746
|
}
|
|
145
747
|
});
|
|
146
748
|
|
|
147
|
-
//
|
|
148
|
-
var
|
|
149
|
-
var
|
|
150
|
-
"
|
|
151
|
-
"use strict";
|
|
152
|
-
init_esm_shims();
|
|
153
|
-
(function(ExportResultCode2) {
|
|
154
|
-
ExportResultCode2[ExportResultCode2["SUCCESS"] = 0] = "SUCCESS";
|
|
155
|
-
ExportResultCode2[ExportResultCode2["FAILED"] = 1] = "FAILED";
|
|
156
|
-
})(ExportResultCode || (ExportResultCode = {}));
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// node_modules/@opentelemetry/core/build/esm/index.js
|
|
161
|
-
var init_esm = __esm({
|
|
162
|
-
"node_modules/@opentelemetry/core/build/esm/index.js"() {
|
|
163
|
-
"use strict";
|
|
164
|
-
init_esm_shims();
|
|
165
|
-
init_ExportResult();
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// src/exportHealth.ts
|
|
170
|
-
var ExportOutcomeExporter;
|
|
171
|
-
var init_exportHealth = __esm({
|
|
172
|
-
"src/exportHealth.ts"() {
|
|
749
|
+
// src/exportHealth.ts
|
|
750
|
+
var ExportOutcomeExporter;
|
|
751
|
+
var init_exportHealth = __esm({
|
|
752
|
+
"src/exportHealth.ts"() {
|
|
173
753
|
"use strict";
|
|
174
754
|
init_esm_shims();
|
|
175
755
|
init_esm();
|
|
@@ -214,6 +794,372 @@ var init_exportHealth = __esm({
|
|
|
214
794
|
}
|
|
215
795
|
});
|
|
216
796
|
|
|
797
|
+
// src/foreign.ts
|
|
798
|
+
import {
|
|
799
|
+
ProxyTracerProvider,
|
|
800
|
+
SpanStatusCode,
|
|
801
|
+
isSpanContextValid,
|
|
802
|
+
trace
|
|
803
|
+
} from "@opentelemetry/api";
|
|
804
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
805
|
+
import { SamplingDecision } from "@opentelemetry/sdk-trace-base";
|
|
806
|
+
function delegating(provider) {
|
|
807
|
+
return typeof provider.getDelegate === "function";
|
|
808
|
+
}
|
|
809
|
+
function rememberOwnProvider(provider) {
|
|
810
|
+
ownProviders.add(provider);
|
|
811
|
+
}
|
|
812
|
+
function globalTracerProvider() {
|
|
813
|
+
let provider = trace.getTracerProvider();
|
|
814
|
+
for (let hop = 0; hop < 8 && delegating(provider); hop += 1) {
|
|
815
|
+
const delegate = provider.getDelegate();
|
|
816
|
+
if (delegate === provider) break;
|
|
817
|
+
provider = delegate;
|
|
818
|
+
}
|
|
819
|
+
if (provider === NOOP_PROVIDER || provider.constructor?.name === "NoopTracerProvider") {
|
|
820
|
+
return void 0;
|
|
821
|
+
}
|
|
822
|
+
return provider;
|
|
823
|
+
}
|
|
824
|
+
function foreignGlobalProviderName() {
|
|
825
|
+
const provider = globalTracerProvider();
|
|
826
|
+
if (provider === void 0 || ownProviders.has(provider)) return void 0;
|
|
827
|
+
return provider.constructor?.name || "unknown";
|
|
828
|
+
}
|
|
829
|
+
function twinOf(span) {
|
|
830
|
+
if (span === void 0 || span === null) return span;
|
|
831
|
+
return shadows.get(span)?.twin ?? span;
|
|
832
|
+
}
|
|
833
|
+
function riusDecision(span) {
|
|
834
|
+
if (span === void 0 || span === null) return void 0;
|
|
835
|
+
if (dropped.has(span)) return false;
|
|
836
|
+
return shadows.has(span) ? true : void 0;
|
|
837
|
+
}
|
|
838
|
+
function stamped(span, shadow) {
|
|
839
|
+
const attributes = { ...span.attributes };
|
|
840
|
+
for (const [key, value] of Object.entries(shadow.twin.attributes)) {
|
|
841
|
+
const started = shadow.seed[key];
|
|
842
|
+
const own = attributes[key];
|
|
843
|
+
if (value !== started && own === started) attributes[key] = value;
|
|
844
|
+
}
|
|
845
|
+
return {
|
|
846
|
+
name: span.name,
|
|
847
|
+
kind: span.kind,
|
|
848
|
+
spanContext: () => span.spanContext(),
|
|
849
|
+
parentSpanContext: span.parentSpanContext,
|
|
850
|
+
startTime: span.startTime,
|
|
851
|
+
endTime: span.endTime,
|
|
852
|
+
duration: span.duration,
|
|
853
|
+
status: { ...span.status },
|
|
854
|
+
attributes,
|
|
855
|
+
links: span.links.map((link) => ({ ...link, attributes: { ...link.attributes } })),
|
|
856
|
+
events: span.events.map((event) => ({ ...event, attributes: { ...event.attributes } })),
|
|
857
|
+
resource: span.resource,
|
|
858
|
+
instrumentationScope: span.instrumentationScope,
|
|
859
|
+
droppedAttributesCount: span.droppedAttributesCount,
|
|
860
|
+
droppedEventsCount: span.droppedEventsCount,
|
|
861
|
+
droppedLinksCount: span.droppedLinksCount,
|
|
862
|
+
ended: span.ended
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
function attachProcessor(provider, processor) {
|
|
866
|
+
const legacy = provider;
|
|
867
|
+
if (typeof legacy.addSpanProcessor === "function") {
|
|
868
|
+
legacy.addSpanProcessor(processor);
|
|
869
|
+
return true;
|
|
870
|
+
}
|
|
871
|
+
const list = provider._activeSpanProcessor?._spanProcessors;
|
|
872
|
+
if (Array.isArray(list)) {
|
|
873
|
+
list.push(processor);
|
|
874
|
+
return true;
|
|
875
|
+
}
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
878
|
+
function bridge(provider, pipeline, sampler, limits) {
|
|
879
|
+
const shadow = new ShadowPipeline(pipeline, sampler, limits);
|
|
880
|
+
let forwarder = forwarders.get(provider);
|
|
881
|
+
if (forwarder === void 0) {
|
|
882
|
+
forwarder = new Forwarder();
|
|
883
|
+
if (!attachProcessor(provider, forwarder)) return void 0;
|
|
884
|
+
forwarders.set(provider, forwarder);
|
|
885
|
+
}
|
|
886
|
+
forwarder.attach(shadow);
|
|
887
|
+
return new Bridge(forwarder, shadow);
|
|
888
|
+
}
|
|
889
|
+
var ownProviders, NOOP_PROVIDER, ForeignParentDetector, shadows, dropped, ALWAYS_ON, ALWAYS_OFF, BridgeAwareSampler, TwinSpan, ShadowPipeline, Forwarder, forwarders, Bridge, ResourceAdoptingExporter;
|
|
890
|
+
var init_foreign = __esm({
|
|
891
|
+
"src/foreign.ts"() {
|
|
892
|
+
"use strict";
|
|
893
|
+
init_esm_shims();
|
|
894
|
+
init_semconv();
|
|
895
|
+
ownProviders = /* @__PURE__ */ new WeakSet();
|
|
896
|
+
NOOP_PROVIDER = new ProxyTracerProvider().getDelegate();
|
|
897
|
+
ForeignParentDetector = class {
|
|
898
|
+
seen = /* @__PURE__ */ new WeakSet();
|
|
899
|
+
openIds = /* @__PURE__ */ new Set();
|
|
900
|
+
globalProvider;
|
|
901
|
+
/** Spans flagged since this detector was built. Cumulative; never reset. */
|
|
902
|
+
count = 0;
|
|
903
|
+
constructor(globalProvider) {
|
|
904
|
+
this.globalProvider = globalProvider;
|
|
905
|
+
}
|
|
906
|
+
onStart(span, parentContext) {
|
|
907
|
+
const parent = span.parentSpanContext;
|
|
908
|
+
const foreign = parent !== void 0 && isSpanContextValid(parent) && // A remote parent belongs to another PROCESS: ordinary distributed
|
|
909
|
+
// tracing, where the parent was never expected here.
|
|
910
|
+
parent.isRemote !== true && !this.startedHere(parent, parentContext);
|
|
911
|
+
this.remember(span);
|
|
912
|
+
if (!foreign) return;
|
|
913
|
+
this.count += 1;
|
|
914
|
+
span.setAttribute(RIUS_PARENT_FOREIGN, true);
|
|
915
|
+
if (this.count === 1) this.warn(span.name);
|
|
916
|
+
}
|
|
917
|
+
startedHere(parent, parentContext) {
|
|
918
|
+
if (this.seen.has(twinOf(trace.getSpan(parentContext)))) return true;
|
|
919
|
+
return this.openIds.has(parent.spanId);
|
|
920
|
+
}
|
|
921
|
+
remember(span) {
|
|
922
|
+
this.seen.add(span);
|
|
923
|
+
this.openIds.add(span.spanContext().spanId);
|
|
924
|
+
}
|
|
925
|
+
warn(name) {
|
|
926
|
+
const owner = this.globalProvider === void 0 ? "another OpenTelemetry provider in this process" : `another OpenTelemetry SDK (${this.globalProvider})`;
|
|
927
|
+
const first = `first: ${JSON.stringify(name)}, flagged ${RIUS_PARENT_FOREIGN}`;
|
|
928
|
+
console.warn(
|
|
929
|
+
`[rius] ${owner} owns the global tracer provider; Rius spans have parents it never receives (${first}). Set RIUS_BRIDGE_FOREIGN_PROVIDER=true or init({ bridgeForeignProvider: true }) to send them to Rius.`
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
onEnd(span) {
|
|
933
|
+
this.openIds.delete(span.spanContext().spanId);
|
|
934
|
+
}
|
|
935
|
+
async forceFlush() {
|
|
936
|
+
}
|
|
937
|
+
async shutdown() {
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
shadows = /* @__PURE__ */ new WeakMap();
|
|
941
|
+
dropped = /* @__PURE__ */ new WeakSet();
|
|
942
|
+
ALWAYS_ON = {
|
|
943
|
+
shouldSample: () => ({ decision: SamplingDecision.RECORD_AND_SAMPLED }),
|
|
944
|
+
toString: () => "AlwaysOnSampler"
|
|
945
|
+
};
|
|
946
|
+
ALWAYS_OFF = {
|
|
947
|
+
shouldSample: () => ({ decision: SamplingDecision.NOT_RECORD }),
|
|
948
|
+
toString: () => "AlwaysOffSampler"
|
|
949
|
+
};
|
|
950
|
+
BridgeAwareSampler = class {
|
|
951
|
+
inner;
|
|
952
|
+
constructor(inner) {
|
|
953
|
+
this.inner = inner;
|
|
954
|
+
}
|
|
955
|
+
shouldSample(context2, traceId, spanName, spanKind, attributes, links) {
|
|
956
|
+
const kept = riusDecision(trace.getSpan(context2));
|
|
957
|
+
const sampler = kept === void 0 ? this.inner : kept ? ALWAYS_ON : ALWAYS_OFF;
|
|
958
|
+
return sampler.shouldSample(context2, traceId, spanName, spanKind, attributes, links);
|
|
959
|
+
}
|
|
960
|
+
toString() {
|
|
961
|
+
return `BridgeAware{${this.inner.toString()}}`;
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
TwinSpan = class {
|
|
965
|
+
attributes = {};
|
|
966
|
+
links;
|
|
967
|
+
events = [];
|
|
968
|
+
startTime;
|
|
969
|
+
endTime = [0, 0];
|
|
970
|
+
duration = [0, 0];
|
|
971
|
+
resource;
|
|
972
|
+
instrumentationScope;
|
|
973
|
+
kind;
|
|
974
|
+
parentSpanContext;
|
|
975
|
+
droppedAttributesCount = 0;
|
|
976
|
+
droppedEventsCount = 0;
|
|
977
|
+
droppedLinksCount = 0;
|
|
978
|
+
name;
|
|
979
|
+
status = { code: SpanStatusCode.UNSET };
|
|
980
|
+
ended = false;
|
|
981
|
+
context;
|
|
982
|
+
attributeCountLimit;
|
|
983
|
+
attributeValueLengthLimit;
|
|
984
|
+
constructor(span, limits) {
|
|
985
|
+
this.context = span.spanContext();
|
|
986
|
+
this.name = span.name;
|
|
987
|
+
this.kind = span.kind;
|
|
988
|
+
this.parentSpanContext = span.parentSpanContext;
|
|
989
|
+
this.startTime = span.startTime;
|
|
990
|
+
this.resource = span.resource;
|
|
991
|
+
this.instrumentationScope = span.instrumentationScope;
|
|
992
|
+
this.links = [...span.links];
|
|
993
|
+
this.attributeCountLimit = limits?.attributeCountLimit ?? Number.POSITIVE_INFINITY;
|
|
994
|
+
this.attributeValueLengthLimit = limits?.attributeValueLengthLimit ?? Number.POSITIVE_INFINITY;
|
|
995
|
+
Object.assign(this.attributes, span.attributes);
|
|
996
|
+
}
|
|
997
|
+
spanContext() {
|
|
998
|
+
return this.context;
|
|
999
|
+
}
|
|
1000
|
+
setAttribute(key, value) {
|
|
1001
|
+
if (value === void 0 || key.length === 0) return this;
|
|
1002
|
+
if (!(key in this.attributes) && Object.keys(this.attributes).length >= this.attributeCountLimit) {
|
|
1003
|
+
this.droppedAttributesCount += 1;
|
|
1004
|
+
return this;
|
|
1005
|
+
}
|
|
1006
|
+
this.attributes[key] = this.truncate(value);
|
|
1007
|
+
return this;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* The value-length limit, applied as the SDK's own Span applies it: strings
|
|
1011
|
+
* are cut, string arrays are cut element by element, everything else passes.
|
|
1012
|
+
*/
|
|
1013
|
+
truncate(value) {
|
|
1014
|
+
const limit = this.attributeValueLengthLimit;
|
|
1015
|
+
if (limit === Number.POSITIVE_INFINITY) return value;
|
|
1016
|
+
if (typeof value === "string") return value.substring(0, limit);
|
|
1017
|
+
if (Array.isArray(value)) {
|
|
1018
|
+
return value.map((item) => typeof item === "string" ? item.substring(0, limit) : item);
|
|
1019
|
+
}
|
|
1020
|
+
return value;
|
|
1021
|
+
}
|
|
1022
|
+
setAttributes(attributes) {
|
|
1023
|
+
for (const [key, value] of Object.entries(attributes)) this.setAttribute(key, value);
|
|
1024
|
+
return this;
|
|
1025
|
+
}
|
|
1026
|
+
addEvent(name, attributesOrStartTime, _timeStamp) {
|
|
1027
|
+
const attributes = attributesOrStartTime !== void 0 && typeof attributesOrStartTime === "object" ? attributesOrStartTime : void 0;
|
|
1028
|
+
this.events.push({ name, attributes, time: this.startTime, droppedAttributesCount: 0 });
|
|
1029
|
+
return this;
|
|
1030
|
+
}
|
|
1031
|
+
addLink(link) {
|
|
1032
|
+
this.links.push(link);
|
|
1033
|
+
return this;
|
|
1034
|
+
}
|
|
1035
|
+
addLinks(links) {
|
|
1036
|
+
this.links.push(...links);
|
|
1037
|
+
return this;
|
|
1038
|
+
}
|
|
1039
|
+
setStatus(status) {
|
|
1040
|
+
this.status = status;
|
|
1041
|
+
return this;
|
|
1042
|
+
}
|
|
1043
|
+
updateName(name) {
|
|
1044
|
+
this.name = name;
|
|
1045
|
+
return this;
|
|
1046
|
+
}
|
|
1047
|
+
end(_endTime) {
|
|
1048
|
+
this.ended = true;
|
|
1049
|
+
}
|
|
1050
|
+
isRecording() {
|
|
1051
|
+
return !this.ended;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* An own property rather than a prototype method on purpose: errorType.ts
|
|
1055
|
+
* replaces it per span instance, which a readonly method would refuse.
|
|
1056
|
+
*/
|
|
1057
|
+
recordException = () => {
|
|
1058
|
+
};
|
|
1059
|
+
};
|
|
1060
|
+
ShadowPipeline = class {
|
|
1061
|
+
pipeline;
|
|
1062
|
+
sampler;
|
|
1063
|
+
limits;
|
|
1064
|
+
constructor(pipeline, sampler, limits) {
|
|
1065
|
+
this.pipeline = pipeline;
|
|
1066
|
+
this.sampler = sampler;
|
|
1067
|
+
this.limits = limits;
|
|
1068
|
+
}
|
|
1069
|
+
onStart(span, parentContext) {
|
|
1070
|
+
const result = this.sampler.shouldSample(
|
|
1071
|
+
parentContext,
|
|
1072
|
+
span.spanContext().traceId,
|
|
1073
|
+
span.name,
|
|
1074
|
+
span.kind,
|
|
1075
|
+
span.attributes,
|
|
1076
|
+
span.links
|
|
1077
|
+
);
|
|
1078
|
+
if (result.decision !== SamplingDecision.RECORD_AND_SAMPLED) {
|
|
1079
|
+
dropped.add(span);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const twin = new TwinSpan(span, this.limits);
|
|
1083
|
+
shadows.set(span, { twin, seed: { ...span.attributes } });
|
|
1084
|
+
this.pipeline.onStart(twin, parentContext);
|
|
1085
|
+
}
|
|
1086
|
+
onEnd(span) {
|
|
1087
|
+
const shadow = shadows.get(span);
|
|
1088
|
+
if (shadow !== void 0) this.pipeline.onEnd(stamped(span, shadow));
|
|
1089
|
+
}
|
|
1090
|
+
async forceFlush() {
|
|
1091
|
+
}
|
|
1092
|
+
async shutdown() {
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
Forwarder = class {
|
|
1096
|
+
target;
|
|
1097
|
+
attach(target) {
|
|
1098
|
+
this.target = target;
|
|
1099
|
+
}
|
|
1100
|
+
release(target) {
|
|
1101
|
+
if (this.target === target) this.target = void 0;
|
|
1102
|
+
}
|
|
1103
|
+
onStart(span, parentContext) {
|
|
1104
|
+
this.target?.onStart(span, parentContext);
|
|
1105
|
+
}
|
|
1106
|
+
onEnd(span) {
|
|
1107
|
+
this.target?.onEnd(span);
|
|
1108
|
+
}
|
|
1109
|
+
async forceFlush() {
|
|
1110
|
+
}
|
|
1111
|
+
async shutdown() {
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
forwarders = /* @__PURE__ */ new WeakMap();
|
|
1115
|
+
Bridge = class {
|
|
1116
|
+
forwarder;
|
|
1117
|
+
pipeline;
|
|
1118
|
+
constructor(forwarder, pipeline) {
|
|
1119
|
+
this.forwarder = forwarder;
|
|
1120
|
+
this.pipeline = pipeline;
|
|
1121
|
+
}
|
|
1122
|
+
/** Stop feeding the other provider's spans into this pipeline. Idempotent. */
|
|
1123
|
+
release() {
|
|
1124
|
+
this.forwarder.release(this.pipeline);
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1127
|
+
ResourceAdoptingExporter = class {
|
|
1128
|
+
inner;
|
|
1129
|
+
resource;
|
|
1130
|
+
adoptedResources = /* @__PURE__ */ new WeakMap();
|
|
1131
|
+
constructor(inner, resource) {
|
|
1132
|
+
this.inner = inner;
|
|
1133
|
+
this.resource = resource;
|
|
1134
|
+
}
|
|
1135
|
+
export(spans, resultCallback) {
|
|
1136
|
+
this.inner.export(
|
|
1137
|
+
spans.map((span) => this.adopted(span)),
|
|
1138
|
+
resultCallback
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
adopted(span) {
|
|
1142
|
+
if (span.resource === this.resource) return span;
|
|
1143
|
+
let merged = this.adoptedResources.get(span.resource);
|
|
1144
|
+
if (merged === void 0) {
|
|
1145
|
+
merged = resourceFromAttributes(
|
|
1146
|
+
{ ...span.resource.attributes, ...this.resource.attributes },
|
|
1147
|
+
{ schemaUrl: this.resource.schemaUrl ?? span.resource.schemaUrl }
|
|
1148
|
+
);
|
|
1149
|
+
this.adoptedResources.set(span.resource, merged);
|
|
1150
|
+
}
|
|
1151
|
+
return { ...span, spanContext: () => span.spanContext(), resource: merged };
|
|
1152
|
+
}
|
|
1153
|
+
shutdown() {
|
|
1154
|
+
return this.inner.shutdown();
|
|
1155
|
+
}
|
|
1156
|
+
forceFlush() {
|
|
1157
|
+
return this.inner.forceFlush?.() ?? Promise.resolve();
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
|
|
217
1163
|
// src/version.ts
|
|
218
1164
|
import { createRequire } from "module";
|
|
219
1165
|
var SDK_VERSION;
|
|
@@ -295,6 +1241,7 @@ var init_heartbeat = __esm({
|
|
|
295
1241
|
pingTimeoutMs;
|
|
296
1242
|
finalPingTimeoutMs;
|
|
297
1243
|
send;
|
|
1244
|
+
foreignParentSpans;
|
|
298
1245
|
timer;
|
|
299
1246
|
stopped = false;
|
|
300
1247
|
deliveryWarned = false;
|
|
@@ -306,6 +1253,7 @@ var init_heartbeat = __esm({
|
|
|
306
1253
|
this.pingTimeoutMs = opts.pingTimeoutMs ?? DEFAULT_PING_TIMEOUT_MS;
|
|
307
1254
|
this.finalPingTimeoutMs = opts.finalPingTimeoutMs ?? DEFAULT_FINAL_PING_TIMEOUT_MS;
|
|
308
1255
|
this.send = opts.transport ?? httpTransport(opts.url, opts.headers);
|
|
1256
|
+
this.foreignParentSpans = opts.foreignParentSpans;
|
|
309
1257
|
}
|
|
310
1258
|
/** Starts pinging: an immediate first ping, then every `intervalMs`. */
|
|
311
1259
|
start() {
|
|
@@ -319,213 +1267,40 @@ var init_heartbeat = __esm({
|
|
|
319
1267
|
async stop() {
|
|
320
1268
|
if (this.stopped) return;
|
|
321
1269
|
this.stopped = true;
|
|
322
|
-
if (this.timer !== void 0) clearInterval(this.timer);
|
|
323
|
-
await this.ping(this.finalPingTimeoutMs, true);
|
|
324
|
-
}
|
|
325
|
-
buildPayload(stopped) {
|
|
326
|
-
const openIds = this.tracker.openTraceIds();
|
|
327
|
-
const payload = {
|
|
328
|
-
v: PAYLOAD_VERSION,
|
|
329
|
-
instance_id: this.instanceId,
|
|
330
|
-
agent_name: this.agentName,
|
|
331
|
-
// RFC3339 UTC with millisecond precision and a Z suffix, already.
|
|
332
|
-
sent_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
333
|
-
sdk_language: "typescript",
|
|
334
|
-
sdk_version: SDK_VERSION,
|
|
335
|
-
open_traces: openIds.slice(0, OPEN_TRACES_CAP),
|
|
336
|
-
open_trace_count: openIds.length
|
|
337
|
-
};
|
|
338
|
-
if (
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// src/semconv.ts
|
|
357
|
-
import { SpanKind as OtelSpanKind } from "@opentelemetry/api";
|
|
358
|
-
function otelSpanKind(kind) {
|
|
359
|
-
return OTEL_KIND_BY_KIND[kind];
|
|
360
|
-
}
|
|
361
|
-
function kindAttributes(kind, name) {
|
|
362
|
-
const attributes = { [OPENINFERENCE_SPAN_KIND]: kind };
|
|
363
|
-
const operation = OPERATION_BY_KIND[kind];
|
|
364
|
-
if (operation !== void 0) attributes[GEN_AI_OPERATION_NAME] = operation;
|
|
365
|
-
if (kind === "TOOL" /* TOOL */ && name !== void 0) attributes[GEN_AI_TOOL_NAME] = name;
|
|
366
|
-
return attributes;
|
|
367
|
-
}
|
|
368
|
-
var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_REQUEST_STREAM, GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_METHOD_NAME, MCP_METHOD_TOOLS_CALL, MCP_PROTOCOL_VERSION, ERROR_TYPE, ERROR_TYPE_TOOL_ERROR, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, OTEL_KIND_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
|
|
369
|
-
var init_semconv = __esm({
|
|
370
|
-
"src/semconv.ts"() {
|
|
371
|
-
"use strict";
|
|
372
|
-
init_esm_shims();
|
|
373
|
-
TRACER_NAME = "glassflow";
|
|
374
|
-
SERVICE_INSTANCE_ID = "service.instance.id";
|
|
375
|
-
OPENINFERENCE_SPAN_KIND = "openinference.span.kind";
|
|
376
|
-
INPUT_VALUE = "input.value";
|
|
377
|
-
OUTPUT_VALUE = "output.value";
|
|
378
|
-
SESSION_ID = "session.id";
|
|
379
|
-
USER_ID = "user.id";
|
|
380
|
-
WORKSPACE_ROUTE = "rius.workspace";
|
|
381
|
-
GEN_AI_OPERATION_NAME = "gen_ai.operation.name";
|
|
382
|
-
GEN_AI_PROVIDER_NAME = "gen_ai.provider.name";
|
|
383
|
-
GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
|
|
384
|
-
GEN_AI_REQUEST_REASONING_LEVEL = "gen_ai.request.reasoning.level";
|
|
385
|
-
GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
|
|
386
|
-
GEN_AI_REQUEST_STREAM = "gen_ai.request.stream";
|
|
387
|
-
GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK = "gen_ai.response.time_to_first_chunk";
|
|
388
|
-
GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens";
|
|
389
|
-
GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
|
|
390
|
-
GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens";
|
|
391
|
-
GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS = "gen_ai.usage.cache_write.input_tokens";
|
|
392
|
-
GEN_AI_USAGE_REASONING_OUTPUT_TOKENS = "gen_ai.usage.reasoning.output_tokens";
|
|
393
|
-
GEN_AI_INPUT_MESSAGES = "gen_ai.input.messages";
|
|
394
|
-
GEN_AI_OUTPUT_MESSAGES = "gen_ai.output.messages";
|
|
395
|
-
GEN_AI_RESPONSE_FINISH_REASONS = "gen_ai.response.finish_reasons";
|
|
396
|
-
GEN_AI_TOOL_NAME = "gen_ai.tool.name";
|
|
397
|
-
GEN_AI_TOOL_DEFINITIONS = "gen_ai.tool.definitions";
|
|
398
|
-
GEN_AI_REQUEST_PREFIX = "gen_ai.request.";
|
|
399
|
-
MCP_METHOD_NAME = "mcp.method.name";
|
|
400
|
-
MCP_METHOD_TOOLS_CALL = "tools/call";
|
|
401
|
-
MCP_PROTOCOL_VERSION = "mcp.protocol.version";
|
|
402
|
-
ERROR_TYPE = "error.type";
|
|
403
|
-
ERROR_TYPE_TOOL_ERROR = "tool_error";
|
|
404
|
-
MCP_RESULT_TYPE = "mcp.result_type";
|
|
405
|
-
GEN_AI_FIRST_TOKEN_EVENT = "gen_ai.first_token";
|
|
406
|
-
SpanKind = /* @__PURE__ */ ((SpanKind2) => {
|
|
407
|
-
SpanKind2["AGENT"] = "AGENT";
|
|
408
|
-
SpanKind2["LLM"] = "LLM";
|
|
409
|
-
SpanKind2["TOOL"] = "TOOL";
|
|
410
|
-
SpanKind2["RETRIEVER"] = "RETRIEVER";
|
|
411
|
-
SpanKind2["EMBEDDING"] = "EMBEDDING";
|
|
412
|
-
SpanKind2["CHAIN"] = "CHAIN";
|
|
413
|
-
return SpanKind2;
|
|
414
|
-
})(SpanKind || {});
|
|
415
|
-
OPERATION_BY_KIND = {
|
|
416
|
-
["LLM" /* LLM */]: "chat",
|
|
417
|
-
["TOOL" /* TOOL */]: "execute_tool",
|
|
418
|
-
["EMBEDDING" /* EMBEDDING */]: "embeddings",
|
|
419
|
-
["AGENT" /* AGENT */]: "invoke_agent"
|
|
420
|
-
};
|
|
421
|
-
OTEL_KIND_BY_KIND = {
|
|
422
|
-
["LLM" /* LLM */]: OtelSpanKind.CLIENT,
|
|
423
|
-
["EMBEDDING" /* EMBEDDING */]: OtelSpanKind.CLIENT,
|
|
424
|
-
["RETRIEVER" /* RETRIEVER */]: OtelSpanKind.CLIENT,
|
|
425
|
-
["TOOL" /* TOOL */]: OtelSpanKind.INTERNAL,
|
|
426
|
-
["AGENT" /* AGENT */]: OtelSpanKind.INTERNAL,
|
|
427
|
-
["CHAIN" /* CHAIN */]: OtelSpanKind.INTERNAL
|
|
428
|
-
};
|
|
429
|
-
CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
430
|
-
INPUT_VALUE,
|
|
431
|
-
OUTPUT_VALUE,
|
|
432
|
-
GEN_AI_INPUT_MESSAGES,
|
|
433
|
-
GEN_AI_OUTPUT_MESSAGES,
|
|
434
|
-
// Sensitive per the GenAI conventions (semconv-genai#431): tool definitions
|
|
435
|
-
// routinely embed proprietary prompt engineering, and sometimes credentials
|
|
436
|
-
// or internal URLs in parameter defaults. gen_ai.tool.name stays: it is
|
|
437
|
-
// identity, not content.
|
|
438
|
-
"gen_ai.tool.description",
|
|
439
|
-
GEN_AI_TOOL_DEFINITIONS,
|
|
440
|
-
// GenAI semconv keys emitted by OTel-native instrumentations (@ai-sdk/otel,
|
|
441
|
-
// which init() registers for ai v7, among them; pinned 2026-09-14): the
|
|
442
|
-
// system prompt and each tool call's input and output are content in the
|
|
443
|
-
// same sense messages are. gen_ai.tool.call.id stays: identity.
|
|
444
|
-
"gen_ai.system_instructions",
|
|
445
|
-
"gen_ai.tool.call.arguments",
|
|
446
|
-
"gen_ai.tool.call.result",
|
|
447
|
-
// OpenInference TOOL-kind spans carry the definition under these two bare
|
|
448
|
-
// keys, sensitive for the reason gen_ai.tool.description is.
|
|
449
|
-
"tool.description",
|
|
450
|
-
"tool.parameters",
|
|
451
|
-
"gen_ai.prompt",
|
|
452
|
-
"gen_ai.completion",
|
|
453
|
-
"llm.input_messages",
|
|
454
|
-
"llm.output_messages",
|
|
455
|
-
// The prefix list below covers the flattened "llm.prompts." and
|
|
456
|
-
// "llm.prompt_template." forms, but an instrumentation may emit either as a
|
|
457
|
-
// single unflattened array attribute under the bare key, which no prefix
|
|
458
|
-
// matches. Both bare keys are therefore listed here as well.
|
|
459
|
-
"llm.prompts",
|
|
460
|
-
"llm.prompt_template",
|
|
461
|
-
"mlflow.spanInputs",
|
|
462
|
-
"mlflow.spanOutputs",
|
|
463
|
-
"traceloop.entity.input",
|
|
464
|
-
"traceloop.entity.output",
|
|
465
|
-
// Every bundled OpenInference instrumentation emits tool definitions as
|
|
466
|
-
// llm.tools.{i}.tool.json_schema (pinned empirically 2026-09-14); covered
|
|
467
|
-
// by prefix below, bare key listed per the bare-key rule.
|
|
468
|
-
"llm.tools",
|
|
469
|
-
// The Vercel AI SDK's own telemetry keys survive the OpenInference
|
|
470
|
-
// transform untouched, and they carry the full prompt (messages AND tool
|
|
471
|
-
// definitions), response content, and tool-call I/O. Names, ids and models
|
|
472
|
-
// (ai.toolCall.name, ai.response.model, ...) are identity and stay.
|
|
473
|
-
"ai.prompt",
|
|
474
|
-
"ai.response.text",
|
|
475
|
-
"ai.response.object",
|
|
476
|
-
"ai.toolCall.args",
|
|
477
|
-
"ai.toolCall.result",
|
|
478
|
-
// Same family, keys enumerated from the ai v7 telemetry surface
|
|
479
|
-
// (2026-09-14): model reasoning, tool calls in the response, response
|
|
480
|
-
// files, embedding inputs, rerank documents, and the generateObject schema
|
|
481
|
-
// (a tool definition by another name).
|
|
482
|
-
"ai.response.reasoning",
|
|
483
|
-
"ai.response.toolCalls",
|
|
484
|
-
"ai.response.files",
|
|
485
|
-
"ai.value",
|
|
486
|
-
"ai.values",
|
|
487
|
-
"ai.documents",
|
|
488
|
-
"ai.schema",
|
|
489
|
-
"ai.schema.description"
|
|
490
|
-
]);
|
|
491
|
-
LLM_INVOCATION_PARAMETERS = "llm.invocation_parameters";
|
|
492
|
-
INVOCATION_PARAMETERS_CONTENT_MEMBERS = ["tools", "functions"];
|
|
493
|
-
CONTENT_ATTRIBUTE_PREFIXES = [
|
|
494
|
-
"llm.input_messages.",
|
|
495
|
-
"llm.output_messages.",
|
|
496
|
-
"gen_ai.prompt.",
|
|
497
|
-
"gen_ai.completion.",
|
|
498
|
-
"llm.prompts.",
|
|
499
|
-
"llm.prompt_template.",
|
|
500
|
-
"llm.tools.",
|
|
501
|
-
"ai.prompt."
|
|
502
|
-
];
|
|
503
|
-
CONTENT_ATTRIBUTE_SUFFIXES = [
|
|
504
|
-
".document.content",
|
|
505
|
-
".embedding.text"
|
|
506
|
-
];
|
|
507
|
-
GLASSFLOW_SPAN_PENDING = "glassflow.span.pending";
|
|
508
|
-
PENDING_IDENTITY_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
509
|
-
OPENINFERENCE_SPAN_KIND,
|
|
510
|
-
GEN_AI_OPERATION_NAME,
|
|
511
|
-
GEN_AI_PROVIDER_NAME,
|
|
512
|
-
GEN_AI_TOOL_NAME,
|
|
513
|
-
// Protocol identity, not content: a still-running MCP call must be
|
|
514
|
-
// distinguishable from a local tool in the live view — the one place
|
|
515
|
-
// setting the marker at creation pays off.
|
|
516
|
-
MCP_METHOD_NAME,
|
|
517
|
-
MCP_PROTOCOL_VERSION,
|
|
518
|
-
// Identity, not content: a pending span must be groupable into its
|
|
519
|
-
// session while still running, that is the live view's whole point.
|
|
520
|
-
SESSION_ID,
|
|
521
|
-
// Same reason: a crashed run's partial spans must still be attributable
|
|
522
|
-
// to the user they served.
|
|
523
|
-
USER_ID,
|
|
524
|
-
// Routing, not content: a crashed run's snapshot must land in the same
|
|
525
|
-
// workspace its final span would have. Stripped at export either way.
|
|
526
|
-
WORKSPACE_ROUTE
|
|
527
|
-
]);
|
|
528
|
-
PENDING_IDENTITY_PREFIXES = [GEN_AI_REQUEST_PREFIX];
|
|
1270
|
+
if (this.timer !== void 0) clearInterval(this.timer);
|
|
1271
|
+
await this.ping(this.finalPingTimeoutMs, true);
|
|
1272
|
+
}
|
|
1273
|
+
buildPayload(stopped) {
|
|
1274
|
+
const openIds = this.tracker.openTraceIds();
|
|
1275
|
+
const payload = {
|
|
1276
|
+
v: PAYLOAD_VERSION,
|
|
1277
|
+
instance_id: this.instanceId,
|
|
1278
|
+
agent_name: this.agentName,
|
|
1279
|
+
// RFC3339 UTC with millisecond precision and a Z suffix, already.
|
|
1280
|
+
sent_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1281
|
+
sdk_language: "typescript",
|
|
1282
|
+
sdk_version: SDK_VERSION,
|
|
1283
|
+
open_traces: openIds.slice(0, OPEN_TRACES_CAP),
|
|
1284
|
+
open_trace_count: openIds.length
|
|
1285
|
+
};
|
|
1286
|
+
if (this.foreignParentSpans !== void 0) {
|
|
1287
|
+
payload.foreign_parent_spans = this.foreignParentSpans();
|
|
1288
|
+
}
|
|
1289
|
+
if (stopped) payload.stopped = true;
|
|
1290
|
+
return payload;
|
|
1291
|
+
}
|
|
1292
|
+
async ping(timeoutMs, stopped) {
|
|
1293
|
+
try {
|
|
1294
|
+
await this.send(this.buildPayload(stopped), timeoutMs);
|
|
1295
|
+
} catch (error) {
|
|
1296
|
+
if (!this.deliveryWarned) {
|
|
1297
|
+
this.deliveryWarned = true;
|
|
1298
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1299
|
+
console.warn(`[rius] heartbeat delivery failed (${message}); further failures are silent.`);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
529
1304
|
}
|
|
530
1305
|
});
|
|
531
1306
|
|
|
@@ -570,8 +1345,14 @@ function toAttributeValue(value) {
|
|
|
570
1345
|
return "[unserializable]";
|
|
571
1346
|
}
|
|
572
1347
|
}
|
|
573
|
-
function
|
|
574
|
-
|
|
1348
|
+
function attributeValue(value) {
|
|
1349
|
+
if (value === void 0 || value === null) return void 0;
|
|
1350
|
+
if (Array.isArray(value)) {
|
|
1351
|
+
if (value.every((v) => typeof v === "string")) return value;
|
|
1352
|
+
if (value.every((v) => typeof v === "number")) return value;
|
|
1353
|
+
if (value.every((v) => typeof v === "boolean")) return value;
|
|
1354
|
+
}
|
|
1355
|
+
return toAttributeValue(value);
|
|
575
1356
|
}
|
|
576
1357
|
var MAX_ATTR_CHARS, TRUNCATION_MARKER;
|
|
577
1358
|
var init_serde = __esm({
|
|
@@ -583,10 +1364,67 @@ var init_serde = __esm({
|
|
|
583
1364
|
}
|
|
584
1365
|
});
|
|
585
1366
|
|
|
1367
|
+
// src/errorType.ts
|
|
1368
|
+
function className(value) {
|
|
1369
|
+
const ctor = Object.getPrototypeOf(value)?.constructor;
|
|
1370
|
+
return typeof ctor === "function" && ctor.name !== "" ? ctor.name : void 0;
|
|
1371
|
+
}
|
|
1372
|
+
function packageOf(error) {
|
|
1373
|
+
for (let proto = Object.getPrototypeOf(error); proto !== null; ) {
|
|
1374
|
+
const ctor = Object.hasOwn(proto, "constructor") ? proto.constructor : void 0;
|
|
1375
|
+
if (typeof ctor === "function" && Object.hasOwn(PACKAGE_OF_BASE_CLASS, ctor.name)) {
|
|
1376
|
+
return PACKAGE_OF_BASE_CLASS[ctor.name];
|
|
1377
|
+
}
|
|
1378
|
+
proto = Object.getPrototypeOf(proto);
|
|
1379
|
+
}
|
|
1380
|
+
return void 0;
|
|
1381
|
+
}
|
|
1382
|
+
function errorType(error) {
|
|
1383
|
+
if (!(error instanceof Error)) return typeof error;
|
|
1384
|
+
try {
|
|
1385
|
+
const cls = className(error);
|
|
1386
|
+
const bare = error.name === "Error" && cls !== void 0 ? cls : error.name;
|
|
1387
|
+
const pkg = packageOf(error);
|
|
1388
|
+
return pkg === void 0 ? bare : `${pkg}.${bare}`;
|
|
1389
|
+
} catch {
|
|
1390
|
+
return error.name;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
function exceptionForRecording(exception) {
|
|
1394
|
+
if (!(exception instanceof Error)) return exception;
|
|
1395
|
+
const type = errorType(exception);
|
|
1396
|
+
if (type === exception.name) return exception;
|
|
1397
|
+
return { name: type, message: exception.message, stack: exception.stack };
|
|
1398
|
+
}
|
|
1399
|
+
function qualifyRecordedExceptions(span) {
|
|
1400
|
+
const original = span.recordException;
|
|
1401
|
+
if (typeof original !== "function" || original[WRAPPED] === true) return;
|
|
1402
|
+
const wrapped = Object.assign(
|
|
1403
|
+
(exception, time) => original.call(span, exceptionForRecording(exception), time),
|
|
1404
|
+
{ [WRAPPED]: true }
|
|
1405
|
+
);
|
|
1406
|
+
try {
|
|
1407
|
+
span.recordException = wrapped;
|
|
1408
|
+
} catch {
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
var PACKAGE_OF_BASE_CLASS, WRAPPED;
|
|
1412
|
+
var init_errorType = __esm({
|
|
1413
|
+
"src/errorType.ts"() {
|
|
1414
|
+
"use strict";
|
|
1415
|
+
init_esm_shims();
|
|
1416
|
+
PACKAGE_OF_BASE_CLASS = {
|
|
1417
|
+
AnthropicError: "anthropic",
|
|
1418
|
+
OpenAIError: "openai"
|
|
1419
|
+
};
|
|
1420
|
+
WRAPPED = /* @__PURE__ */ Symbol("rius.recordException");
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
|
|
586
1424
|
// src/user.ts
|
|
587
|
-
import { context as
|
|
1425
|
+
import { context as apiContext2, createContextKey as createContextKey2 } from "@opentelemetry/api";
|
|
588
1426
|
function withUser(userId, fn) {
|
|
589
|
-
return
|
|
1427
|
+
return apiContext2.with(apiContext2.active().setValue(USER_KEY, userId), () => fn(userId));
|
|
590
1428
|
}
|
|
591
1429
|
var USER_KEY, UserSpanProcessor;
|
|
592
1430
|
var init_user = __esm({
|
|
@@ -594,7 +1432,7 @@ var init_user = __esm({
|
|
|
594
1432
|
"use strict";
|
|
595
1433
|
init_esm_shims();
|
|
596
1434
|
init_semconv();
|
|
597
|
-
USER_KEY =
|
|
1435
|
+
USER_KEY = createContextKey2("rius-user-id");
|
|
598
1436
|
UserSpanProcessor = class {
|
|
599
1437
|
onStart(span, parentContext) {
|
|
600
1438
|
const value = parentContext.getValue(USER_KEY);
|
|
@@ -611,36 +1449,86 @@ var init_user = __esm({
|
|
|
611
1449
|
});
|
|
612
1450
|
|
|
613
1451
|
// src/spans.ts
|
|
614
|
-
import {
|
|
1452
|
+
import {
|
|
1453
|
+
SpanStatusCode as SpanStatusCode2
|
|
1454
|
+
} from "@opentelemetry/api";
|
|
615
1455
|
function configure(observation, options) {
|
|
616
1456
|
if (options.input !== void 0) observation.setInput(options.input);
|
|
617
1457
|
return observation;
|
|
618
1458
|
}
|
|
1459
|
+
function resolveToolName(options, name) {
|
|
1460
|
+
if (options.toolName !== void 0) return options.toolName;
|
|
1461
|
+
if (name === void 0) return void 0;
|
|
1462
|
+
if (options.kind === "TOOL" /* TOOL */) {
|
|
1463
|
+
warnOnce(
|
|
1464
|
+
`A span named "${name}" with kind TOOL is naming the tool as well as the span; gen_ai.tool.name will be "${name}". Pass toolName to set them separately. A future major release will stop deriving the tool name from the span name.`
|
|
1465
|
+
);
|
|
1466
|
+
}
|
|
1467
|
+
return name;
|
|
1468
|
+
}
|
|
1469
|
+
function warnOnce(message) {
|
|
1470
|
+
if (warned.has(message)) return;
|
|
1471
|
+
warned.add(message);
|
|
1472
|
+
console.warn(`[rius] ${message}`);
|
|
1473
|
+
}
|
|
619
1474
|
function creationAttributes(name, options) {
|
|
1475
|
+
const kind = options.kind ?? "CHAIN" /* CHAIN */;
|
|
620
1476
|
const attributes = {
|
|
621
|
-
...kindAttributes(
|
|
1477
|
+
...kindAttributes(kind, resolveToolName(options, name)),
|
|
622
1478
|
...options.attributes
|
|
623
1479
|
};
|
|
624
1480
|
if (options.userId !== void 0) attributes[USER_ID] = options.userId;
|
|
1481
|
+
if (kind === "RETRIEVER" /* RETRIEVER */) {
|
|
1482
|
+
if (options.dataSourceId !== void 0)
|
|
1483
|
+
attributes[GEN_AI_DATA_SOURCE_ID] = options.dataSourceId;
|
|
1484
|
+
if (options.topK !== void 0) attributes[GEN_AI_RETRIEVAL_TOP_K] = options.topK;
|
|
1485
|
+
}
|
|
1486
|
+
if (kind === "TOOL" /* TOOL */) {
|
|
1487
|
+
const executedBy = executingAgentName();
|
|
1488
|
+
if (executedBy !== void 0) attributes[GEN_AI_AGENT_NAME] = executedBy;
|
|
1489
|
+
if (options.toolCallId !== void 0) attributes[GEN_AI_TOOL_CALL_ID] = options.toolCallId;
|
|
1490
|
+
if (options.toolType !== void 0) attributes[GEN_AI_TOOL_TYPE] = options.toolType;
|
|
1491
|
+
}
|
|
1492
|
+
if (kind === "AGENT" /* AGENT */) {
|
|
1493
|
+
const agentName = resolveAgentName(options.agentName, kind);
|
|
1494
|
+
if (agentName !== void 0) attributes[GEN_AI_AGENT_NAME] = agentName;
|
|
1495
|
+
if (options.agentId !== void 0) attributes[GEN_AI_AGENT_ID] = options.agentId;
|
|
1496
|
+
if (options.agentVersion !== void 0) attributes[GEN_AI_AGENT_VERSION] = options.agentVersion;
|
|
1497
|
+
}
|
|
625
1498
|
return attributes;
|
|
626
1499
|
}
|
|
627
|
-
function
|
|
628
|
-
|
|
1500
|
+
function resolveName(name, kind, attributes) {
|
|
1501
|
+
return name ?? composeSpanName(kind ?? "CHAIN" /* CHAIN */, attributes);
|
|
1502
|
+
}
|
|
1503
|
+
function splitScopedArgs(first, second, third) {
|
|
1504
|
+
if (typeof first === "function") return { name: void 0, options: {}, fn: first };
|
|
1505
|
+
if (typeof first === "string") {
|
|
1506
|
+
return typeof second === "function" ? { name: first, options: {}, fn: second } : { name: first, options: second ?? {}, fn: third };
|
|
1507
|
+
}
|
|
1508
|
+
return { name: void 0, options: first, fn: second };
|
|
1509
|
+
}
|
|
1510
|
+
function startSpan(nameOrOptions, maybeOptions) {
|
|
1511
|
+
const [name, options] = typeof nameOrOptions === "string" ? [nameOrOptions, maybeOptions ?? {}] : [void 0, nameOrOptions ?? {}];
|
|
1512
|
+
const attributes = creationAttributes(name, options);
|
|
1513
|
+
const span = getTracer().startSpan(resolveName(name, options.kind, attributes), {
|
|
629
1514
|
kind: options.otelKind ?? otelSpanKind(options.kind ?? "CHAIN" /* CHAIN */),
|
|
630
|
-
attributes
|
|
1515
|
+
attributes
|
|
631
1516
|
});
|
|
632
1517
|
return configure(new Observation(span), options);
|
|
633
1518
|
}
|
|
634
|
-
function startAsCurrentSpan(
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
1519
|
+
function startAsCurrentSpan(first, second, third) {
|
|
1520
|
+
const { name, options, fn } = splitScopedArgs(first, second, third);
|
|
1521
|
+
const attributes = creationAttributes(name, options);
|
|
1522
|
+
const run = () => runActive(
|
|
1523
|
+
resolveName(name, options.kind, attributes),
|
|
1524
|
+
attributes,
|
|
639
1525
|
options.userId,
|
|
640
1526
|
(span) => configure(new Observation(span), options),
|
|
641
1527
|
fn,
|
|
642
1528
|
options.otelKind ?? otelSpanKind(options.kind ?? "CHAIN" /* CHAIN */)
|
|
643
1529
|
);
|
|
1530
|
+
const scopeName = (options.kind ?? "CHAIN" /* CHAIN */) === "AGENT" /* AGENT */ ? attributes[GEN_AI_AGENT_NAME] : void 0;
|
|
1531
|
+
return typeof scopeName === "string" ? withAgentScope(scopeName, run) : run();
|
|
644
1532
|
}
|
|
645
1533
|
function runActive(name, attributes, userId, makeHandle, fn, otelKind) {
|
|
646
1534
|
const run = () => getTracer().startActiveSpan(name, { kind: otelKind, attributes }, async (span) => {
|
|
@@ -656,12 +1544,14 @@ function runActive(name, attributes, userId, makeHandle, fn, otelKind) {
|
|
|
656
1544
|
});
|
|
657
1545
|
return userId !== void 0 ? withUser(userId, run) : run();
|
|
658
1546
|
}
|
|
659
|
-
var Observation;
|
|
1547
|
+
var Observation, warned;
|
|
660
1548
|
var init_spans = __esm({
|
|
661
1549
|
"src/spans.ts"() {
|
|
662
1550
|
"use strict";
|
|
663
1551
|
init_esm_shims();
|
|
1552
|
+
init_agent();
|
|
664
1553
|
init_client();
|
|
1554
|
+
init_errorType();
|
|
665
1555
|
init_semconv();
|
|
666
1556
|
init_serde();
|
|
667
1557
|
init_user();
|
|
@@ -680,6 +1570,23 @@ var init_spans = __esm({
|
|
|
680
1570
|
this.span.setAttribute(OUTPUT_VALUE, toAttributeValue(value));
|
|
681
1571
|
return this;
|
|
682
1572
|
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Record what a retrieval returned, as `gen_ai.retrieval.documents`.
|
|
1575
|
+
*
|
|
1576
|
+
* The conventions define this as an array of objects, each with an optional
|
|
1577
|
+
* `id` and an optional `score`. Identifiers and relevance, never document
|
|
1578
|
+
* text, which is why it is not treated as content: it survives
|
|
1579
|
+
* `captureContent: false` the way token counts do. Put the retrieved text in
|
|
1580
|
+
* `setOutput` if you want it captured, and masking applies to it there.
|
|
1581
|
+
*
|
|
1582
|
+
* Unlike `dataSourceId` and `topK`, which describe the request and are
|
|
1583
|
+
* passed at span creation, this is only knowable once the search has run, so
|
|
1584
|
+
* it never reaches a pending snapshot.
|
|
1585
|
+
*/
|
|
1586
|
+
setRetrievedDocuments(documents) {
|
|
1587
|
+
this.span.setAttribute(GEN_AI_RETRIEVAL_DOCUMENTS, toAttributeValue(documents));
|
|
1588
|
+
return this;
|
|
1589
|
+
}
|
|
683
1590
|
/**
|
|
684
1591
|
* Set an arbitrary attribute. Primitives and homogeneous primitive arrays
|
|
685
1592
|
* are passed through as the OTel values they are; objects are JSON-encoded
|
|
@@ -687,20 +1594,8 @@ var init_spans = __esm({
|
|
|
687
1594
|
* an empty string.
|
|
688
1595
|
*/
|
|
689
1596
|
setAttribute(key, value) {
|
|
690
|
-
|
|
691
|
-
if (
|
|
692
|
-
this.span.setAttribute(key, value);
|
|
693
|
-
return this;
|
|
694
|
-
}
|
|
695
|
-
if (Array.isArray(value) && value.every((v) => typeof v === "number")) {
|
|
696
|
-
this.span.setAttribute(key, value);
|
|
697
|
-
return this;
|
|
698
|
-
}
|
|
699
|
-
if (Array.isArray(value) && value.every((v) => typeof v === "boolean")) {
|
|
700
|
-
this.span.setAttribute(key, value);
|
|
701
|
-
return this;
|
|
702
|
-
}
|
|
703
|
-
this.span.setAttribute(key, toAttributeValue(value));
|
|
1597
|
+
const coerced = attributeValue(value);
|
|
1598
|
+
if (coerced !== void 0) this.span.setAttribute(key, coerced);
|
|
704
1599
|
return this;
|
|
705
1600
|
}
|
|
706
1601
|
/**
|
|
@@ -711,16 +1606,17 @@ var init_spans = __esm({
|
|
|
711
1606
|
*
|
|
712
1607
|
* `error.type` is Conditionally Required by the GenAI conventions on every
|
|
713
1608
|
* span that ends in an error, and every helper's throw path funnels through
|
|
714
|
-
* here, so this is the one place that sets it. The error's name
|
|
715
|
-
* the message: it must stay low-cardinality and free
|
|
1609
|
+
* here, so this is the one place that sets it. The error's name or class
|
|
1610
|
+
* (see errorType), never the message: it must stay low-cardinality and free
|
|
1611
|
+
* of echoed content.
|
|
716
1612
|
*
|
|
717
1613
|
* Accepts `unknown` because that is what a `catch` binding is; a non-Error
|
|
718
1614
|
* throwable is wrapped so `recordException` still gets a real Error.
|
|
719
1615
|
*/
|
|
720
1616
|
recordException(error) {
|
|
721
1617
|
const wrapped = error instanceof Error ? error : new Error(String(error));
|
|
722
|
-
this.span.recordException(wrapped);
|
|
723
|
-
this.span.setStatus({ code:
|
|
1618
|
+
this.span.recordException(exceptionForRecording(wrapped));
|
|
1619
|
+
this.span.setStatus({ code: SpanStatusCode2.ERROR, message: wrapped.message });
|
|
724
1620
|
this.span.setAttribute(ERROR_TYPE, errorType(error));
|
|
725
1621
|
return this;
|
|
726
1622
|
}
|
|
@@ -734,6 +1630,7 @@ var init_spans = __esm({
|
|
|
734
1630
|
this.end();
|
|
735
1631
|
}
|
|
736
1632
|
};
|
|
1633
|
+
warned = /* @__PURE__ */ new Set();
|
|
737
1634
|
}
|
|
738
1635
|
});
|
|
739
1636
|
|
|
@@ -742,7 +1639,7 @@ var instrumentationMcp_exports = {};
|
|
|
742
1639
|
__export(instrumentationMcp_exports, {
|
|
743
1640
|
instrumentMcpClient: () => instrumentMcpClient
|
|
744
1641
|
});
|
|
745
|
-
import { SpanKind as OtelSpanKind2, SpanStatusCode as
|
|
1642
|
+
import { SpanKind as OtelSpanKind2, SpanStatusCode as SpanStatusCode3 } from "@opentelemetry/api";
|
|
746
1643
|
function serializeResult(result) {
|
|
747
1644
|
const record = asRecord(result);
|
|
748
1645
|
if (record === void 0) return toAttributeValue(result);
|
|
@@ -770,7 +1667,7 @@ function recordResult(observation, result) {
|
|
|
770
1667
|
observation.setAttribute(OUTPUT_VALUE, serializeResult(result));
|
|
771
1668
|
if (resultIsError(result)) {
|
|
772
1669
|
observation.span.setStatus({
|
|
773
|
-
code:
|
|
1670
|
+
code: SpanStatusCode3.ERROR,
|
|
774
1671
|
message: "tool returned an error result"
|
|
775
1672
|
});
|
|
776
1673
|
observation.setAttribute(ERROR_TYPE, ERROR_TYPE_TOOL_ERROR);
|
|
@@ -780,9 +1677,8 @@ function negotiatedProtocolVersion(client) {
|
|
|
780
1677
|
const version = asRecord(asRecord(client)?.transport)?.protocolVersion;
|
|
781
1678
|
return typeof version === "string" ? version : void 0;
|
|
782
1679
|
}
|
|
783
|
-
function callAttributes(
|
|
1680
|
+
function callAttributes(protocolVersion) {
|
|
784
1681
|
const attributes = {
|
|
785
|
-
[GEN_AI_TOOL_NAME]: toolName,
|
|
786
1682
|
[MCP_METHOD_NAME]: MCP_METHOD_TOOLS_CALL
|
|
787
1683
|
};
|
|
788
1684
|
if (protocolVersion !== void 0) attributes[MCP_PROTOCOL_VERSION] = protocolVersion;
|
|
@@ -799,15 +1695,15 @@ function instrumentMcpClient(ClientClass) {
|
|
|
799
1695
|
const original = ClientClass.prototype.callTool;
|
|
800
1696
|
const instrumented = function instrumentedCallTool(params, ...rest) {
|
|
801
1697
|
return startAsCurrentSpan(
|
|
802
|
-
`execute_tool ${params.name}`,
|
|
803
1698
|
{
|
|
804
1699
|
kind: "TOOL" /* TOOL */,
|
|
805
1700
|
// The OTel SpanKind FIELD (orthogonal to our taxonomy attribute above):
|
|
806
1701
|
// a remote tool call is CLIENT under both the MCP and the GenAI
|
|
807
1702
|
// execute-tool conventions.
|
|
808
1703
|
otelKind: OtelSpanKind2.CLIENT,
|
|
1704
|
+
toolName: params.name,
|
|
809
1705
|
input: params.arguments,
|
|
810
|
-
attributes: callAttributes(
|
|
1706
|
+
attributes: callAttributes(negotiatedProtocolVersion(this))
|
|
811
1707
|
},
|
|
812
1708
|
async (observation) => {
|
|
813
1709
|
const result = await original.call(this, params, ...rest);
|
|
@@ -837,8 +1733,12 @@ var init_instrumentationMcp = __esm({
|
|
|
837
1733
|
// src/instrumentation.ts
|
|
838
1734
|
import { createRequire as createRequire2 } from "module";
|
|
839
1735
|
import { join, sep } from "path";
|
|
1736
|
+
import {
|
|
1737
|
+
SpanStatusCode as SpanStatusCode4,
|
|
1738
|
+
trace as trace2
|
|
1739
|
+
} from "@opentelemetry/api";
|
|
840
1740
|
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
|
841
|
-
function
|
|
1741
|
+
function packageOf2(specifier) {
|
|
842
1742
|
const segments = specifier.split("/");
|
|
843
1743
|
const take = specifier.startsWith("@") ? 2 : 1;
|
|
844
1744
|
return segments.slice(0, take).join("/");
|
|
@@ -850,7 +1750,7 @@ function isUnresolved(error, specifier) {
|
|
|
850
1750
|
if (typeof error !== "object" || error === null) return false;
|
|
851
1751
|
const message = error.message;
|
|
852
1752
|
if (typeof message !== "string") return false;
|
|
853
|
-
const alternatives = [specifier,
|
|
1753
|
+
const alternatives = [specifier, packageOf2(specifier)].map(escapeRegExp).join("|");
|
|
854
1754
|
return new RegExp(
|
|
855
1755
|
`(?:cannot find (?:module|package)|could not resolve|failed to load url)\\s*['"\`]?(?:${alternatives})['"\`]?(?![\\w./@-])`,
|
|
856
1756
|
"i"
|
|
@@ -919,6 +1819,73 @@ function anthropicPatchable(exports) {
|
|
|
919
1819
|
const cls = exports.default ?? exports.Anthropic;
|
|
920
1820
|
return cls?.Messages?.prototype?.create ? { default: cls } : void 0;
|
|
921
1821
|
}
|
|
1822
|
+
function openaiPrototypes(moduleExports) {
|
|
1823
|
+
const cls = moduleExports?.OpenAI;
|
|
1824
|
+
const prototypes = [];
|
|
1825
|
+
for (const path2 of OPENAI_INSTRUMENTED_RESOURCES) {
|
|
1826
|
+
let resource = cls;
|
|
1827
|
+
for (const segment of path2) {
|
|
1828
|
+
resource = resource?.[segment];
|
|
1829
|
+
}
|
|
1830
|
+
const proto = resource?.prototype;
|
|
1831
|
+
if (typeof proto?.create === "function") prototypes.push(proto);
|
|
1832
|
+
}
|
|
1833
|
+
return prototypes;
|
|
1834
|
+
}
|
|
1835
|
+
function settlementOf(result) {
|
|
1836
|
+
const raw = result?.responsePromise;
|
|
1837
|
+
if (typeof raw?.then === "function") {
|
|
1838
|
+
return raw;
|
|
1839
|
+
}
|
|
1840
|
+
return result instanceof Promise ? result : void 0;
|
|
1841
|
+
}
|
|
1842
|
+
function failSpan(span, error) {
|
|
1843
|
+
if (!span.isRecording()) return;
|
|
1844
|
+
span.recordException(error instanceof Error ? error : String(error));
|
|
1845
|
+
span.setStatus({
|
|
1846
|
+
code: SpanStatusCode4.ERROR,
|
|
1847
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1848
|
+
});
|
|
1849
|
+
span.end();
|
|
1850
|
+
}
|
|
1851
|
+
function guardRejections(original) {
|
|
1852
|
+
const guarded = function(...args) {
|
|
1853
|
+
const span = trace2.getActiveSpan();
|
|
1854
|
+
const result = original.apply(this, args);
|
|
1855
|
+
if (span?.isRecording()) {
|
|
1856
|
+
settlementOf(result)?.then(void 0, (error) => failSpan(span, error));
|
|
1857
|
+
}
|
|
1858
|
+
return result;
|
|
1859
|
+
};
|
|
1860
|
+
guarded[GUARDED_ORIGINAL] = original;
|
|
1861
|
+
return guarded;
|
|
1862
|
+
}
|
|
1863
|
+
function withRejectedCallsEnded(Base) {
|
|
1864
|
+
return class extends Base {
|
|
1865
|
+
patch(moduleExports, moduleVersion) {
|
|
1866
|
+
const installed = [];
|
|
1867
|
+
for (const proto of openaiPrototypes(moduleExports)) {
|
|
1868
|
+
const create = proto.create;
|
|
1869
|
+
if (create[GUARDED_ORIGINAL] !== void 0) continue;
|
|
1870
|
+
const guarded = guardRejections(create);
|
|
1871
|
+
proto.create = guarded;
|
|
1872
|
+
installed.push([proto, guarded]);
|
|
1873
|
+
}
|
|
1874
|
+
const patched = super.patch(moduleExports, moduleVersion);
|
|
1875
|
+
for (const [proto, guarded] of installed) {
|
|
1876
|
+
if (proto.create === guarded) proto.create = guarded[GUARDED_ORIGINAL];
|
|
1877
|
+
}
|
|
1878
|
+
return patched;
|
|
1879
|
+
}
|
|
1880
|
+
unpatch(moduleExports, moduleVersion) {
|
|
1881
|
+
super.unpatch(moduleExports, moduleVersion);
|
|
1882
|
+
for (const proto of openaiPrototypes(moduleExports)) {
|
|
1883
|
+
const original = proto.create[GUARDED_ORIGINAL];
|
|
1884
|
+
if (original !== void 0) proto.create = original;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
922
1889
|
function mcpClientPatchable(exports) {
|
|
923
1890
|
const cls = exports.Client;
|
|
924
1891
|
return typeof cls?.prototype?.callTool === "function" ? { Client: cls } : void 0;
|
|
@@ -956,6 +1923,10 @@ async function registerVercelTelemetry(tracerProvider, teardown) {
|
|
|
956
1923
|
});
|
|
957
1924
|
return true;
|
|
958
1925
|
}
|
|
1926
|
+
function isVercelDialectSpan(span) {
|
|
1927
|
+
const operationId = span.attributes[VERCEL_OPERATION_ID];
|
|
1928
|
+
return typeof operationId === "string" && operationId.startsWith("ai.");
|
|
1929
|
+
}
|
|
959
1930
|
async function enableInstrumentations(sink, tracerProvider, names, teardown) {
|
|
960
1931
|
const wanted = names ? REGISTRY.filter((e) => names.includes(e.name)) : REGISTRY;
|
|
961
1932
|
const enabled = [];
|
|
@@ -980,7 +1951,7 @@ async function enableInstrumentations(sink, tracerProvider, names, teardown) {
|
|
|
980
1951
|
}
|
|
981
1952
|
return enabled;
|
|
982
1953
|
}
|
|
983
|
-
var dynamicImport, vercelTelemetryIntegration, REGISTRY;
|
|
1954
|
+
var dynamicImport, OPENAI_INSTRUMENTED_RESOURCES, GUARDED_ORIGINAL, vercelTelemetryIntegration, VERCEL_OPERATION_ID, REGISTRY;
|
|
984
1955
|
var init_instrumentation = __esm({
|
|
985
1956
|
"src/instrumentation.ts"() {
|
|
986
1957
|
"use strict";
|
|
@@ -988,6 +1959,14 @@ var init_instrumentation = __esm({
|
|
|
988
1959
|
init_semconv();
|
|
989
1960
|
init_version();
|
|
990
1961
|
dynamicImport = new Function("s", "return import(s)");
|
|
1962
|
+
OPENAI_INSTRUMENTED_RESOURCES = [
|
|
1963
|
+
["Chat", "Completions"],
|
|
1964
|
+
["Completions"],
|
|
1965
|
+
["Embeddings"],
|
|
1966
|
+
["Responses"]
|
|
1967
|
+
];
|
|
1968
|
+
GUARDED_ORIGINAL = /* @__PURE__ */ Symbol("rius.openai.guardedOriginal");
|
|
1969
|
+
VERCEL_OPERATION_ID = "ai.operationId";
|
|
991
1970
|
REGISTRY = [
|
|
992
1971
|
{
|
|
993
1972
|
name: "vercel-ai",
|
|
@@ -1006,7 +1985,7 @@ var init_instrumentation = __esm({
|
|
|
1006
1985
|
onStart() {
|
|
1007
1986
|
},
|
|
1008
1987
|
onEnd(span) {
|
|
1009
|
-
add
|
|
1988
|
+
if (add !== void 0 && isVercelDialectSpan(span)) add(span);
|
|
1010
1989
|
},
|
|
1011
1990
|
async forceFlush() {
|
|
1012
1991
|
},
|
|
@@ -1022,7 +2001,7 @@ var init_instrumentation = __esm({
|
|
|
1022
2001
|
const mod = await optional("@arizeai/openinference-instrumentation-openai");
|
|
1023
2002
|
const Ctor = mod?.OpenAIInstrumentation;
|
|
1024
2003
|
if (Ctor === void 0) return void 0;
|
|
1025
|
-
const instrumentation = new Ctor();
|
|
2004
|
+
const instrumentation = new (withRejectedCallsEnded(Ctor))();
|
|
1026
2005
|
await patchActiveBuild(instrumentation, "openai", openaiPatchable);
|
|
1027
2006
|
return instrumentation;
|
|
1028
2007
|
}
|
|
@@ -1071,134 +2050,871 @@ var init_instrumentation = __esm({
|
|
|
1071
2050
|
return true;
|
|
1072
2051
|
}
|
|
1073
2052
|
}
|
|
1074
|
-
];
|
|
2053
|
+
];
|
|
2054
|
+
}
|
|
2055
|
+
});
|
|
2056
|
+
|
|
2057
|
+
// src/masking.ts
|
|
2058
|
+
import { SpanStatusCode as SpanStatusCode5 } from "@opentelemetry/api";
|
|
2059
|
+
function isContentKey(key) {
|
|
2060
|
+
if (CONTENT_ATTRIBUTES.has(key)) return true;
|
|
2061
|
+
if (CONTENT_ATTRIBUTE_PREFIXES.some((p) => key.startsWith(p)) && !CONTENT_PREFIX_IDENTITY_ATTRIBUTES.has(key))
|
|
2062
|
+
return true;
|
|
2063
|
+
if (CONTENT_ATTRIBUTE_SUFFIXES.some((s) => key.endsWith(s))) return true;
|
|
2064
|
+
if (CONTENT_ATTRIBUTE_PREFIXED_SUFFIXES.some(
|
|
2065
|
+
([prefix, leaves]) => key.startsWith(prefix) && leaves.some((leaf) => key.endsWith(leaf))
|
|
2066
|
+
))
|
|
2067
|
+
return true;
|
|
2068
|
+
return key.startsWith(METADATA_PREFIX) && isContentKey(key.slice(METADATA_PREFIX.length));
|
|
2069
|
+
}
|
|
2070
|
+
var METADATA_PREFIX, EXCEPTION_CONTENT_KEYS, STATUS_DESCRIPTION_KEY, MaskingSpanExporter;
|
|
2071
|
+
var init_masking = __esm({
|
|
2072
|
+
"src/masking.ts"() {
|
|
2073
|
+
"use strict";
|
|
2074
|
+
init_esm_shims();
|
|
2075
|
+
init_semconv();
|
|
2076
|
+
init_serde();
|
|
2077
|
+
METADATA_PREFIX = "metadata.";
|
|
2078
|
+
EXCEPTION_CONTENT_KEYS = ["exception.message", "exception.stacktrace"];
|
|
2079
|
+
STATUS_DESCRIPTION_KEY = "status.description";
|
|
2080
|
+
MaskingSpanExporter = class {
|
|
2081
|
+
constructor(inner, opts) {
|
|
2082
|
+
this.inner = inner;
|
|
2083
|
+
this.opts = opts;
|
|
2084
|
+
}
|
|
2085
|
+
inner;
|
|
2086
|
+
opts;
|
|
2087
|
+
export(spans, resultCallback) {
|
|
2088
|
+
this.inner.export(
|
|
2089
|
+
spans.map((s) => this.sanitized(s)),
|
|
2090
|
+
resultCallback
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
sanitized(span) {
|
|
2094
|
+
this.sanitizeAttributes(span.attributes);
|
|
2095
|
+
for (const event of span.events ?? []) {
|
|
2096
|
+
const attributes = event.attributes;
|
|
2097
|
+
this.sanitizeAttributes(attributes);
|
|
2098
|
+
if (this.opts.captureContent || attributes === void 0) continue;
|
|
2099
|
+
if (event.name !== EXCEPTION_EVENT) continue;
|
|
2100
|
+
for (const key of EXCEPTION_CONTENT_KEYS) delete attributes[key];
|
|
2101
|
+
}
|
|
2102
|
+
for (const link of span.links ?? []) {
|
|
2103
|
+
this.sanitizeAttributes(link.attributes);
|
|
2104
|
+
}
|
|
2105
|
+
this.sanitizeStatus(span);
|
|
2106
|
+
return span;
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Drops or masks the status description, in place. `status` is mutable and
|
|
2110
|
+
* exposed by reference on the SDK span, same as `attributes`, so mutating it
|
|
2111
|
+
* here reaches the span about to be handed to the inner exporter. The ERROR
|
|
2112
|
+
* code itself is left alone so the failure stays visible and classifiable,
|
|
2113
|
+
* the same policy as `exception.type`.
|
|
2114
|
+
*/
|
|
2115
|
+
sanitizeStatus(span) {
|
|
2116
|
+
const status = span.status;
|
|
2117
|
+
if (status?.code !== SpanStatusCode5.ERROR || !status.message) return;
|
|
2118
|
+
if (!this.opts.captureContent) {
|
|
2119
|
+
status.message = void 0;
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
if (this.opts.mask === void 0) return;
|
|
2123
|
+
try {
|
|
2124
|
+
const masked = toAttributeValue(
|
|
2125
|
+
this.opts.mask(status.message, { key: STATUS_DESCRIPTION_KEY })
|
|
2126
|
+
);
|
|
2127
|
+
status.message = typeof masked === "string" ? masked : String(masked);
|
|
2128
|
+
} catch {
|
|
2129
|
+
status.message = "[mask error]";
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
/** Strips or masks the content keys of one attribute bag, in place. */
|
|
2133
|
+
sanitizeAttributes(attributes) {
|
|
2134
|
+
if (attributes === void 0) return;
|
|
2135
|
+
for (const key of Object.keys(attributes)) {
|
|
2136
|
+
if (!isContentKey(key)) continue;
|
|
2137
|
+
if (!this.opts.captureContent) {
|
|
2138
|
+
delete attributes[key];
|
|
2139
|
+
continue;
|
|
2140
|
+
}
|
|
2141
|
+
if (this.opts.mask === void 0) continue;
|
|
2142
|
+
try {
|
|
2143
|
+
attributes[key] = toAttributeValue(this.opts.mask(attributes[key], { key }));
|
|
2144
|
+
} catch {
|
|
2145
|
+
attributes[key] = "[mask error]";
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
shutdown() {
|
|
2150
|
+
return this.inner.shutdown();
|
|
2151
|
+
}
|
|
2152
|
+
forceFlush() {
|
|
2153
|
+
return this.inner.forceFlush?.() ?? Promise.resolve();
|
|
2154
|
+
}
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
});
|
|
2158
|
+
|
|
2159
|
+
// src/normalize.ts
|
|
2160
|
+
import {
|
|
2161
|
+
SpanStatusCode as SpanStatusCode6
|
|
2162
|
+
} from "@opentelemetry/api";
|
|
2163
|
+
function isExpanding(rule) {
|
|
2164
|
+
return "expand" in rule;
|
|
2165
|
+
}
|
|
2166
|
+
function asNumber(value) {
|
|
2167
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2168
|
+
}
|
|
2169
|
+
function asCount(value) {
|
|
2170
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : void 0;
|
|
2171
|
+
}
|
|
2172
|
+
function asText(value) {
|
|
2173
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
2174
|
+
}
|
|
2175
|
+
function asFlag(value) {
|
|
2176
|
+
return typeof value === "boolean" ? value : void 0;
|
|
2177
|
+
}
|
|
2178
|
+
function asTextList(value) {
|
|
2179
|
+
if (typeof value === "string") return [value];
|
|
2180
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string")) return [...value];
|
|
2181
|
+
return void 0;
|
|
2182
|
+
}
|
|
2183
|
+
function wellFormed(text) {
|
|
2184
|
+
return text.replace(LONE_SURROGATE, "\uFFFD");
|
|
2185
|
+
}
|
|
2186
|
+
function jsonString(text) {
|
|
2187
|
+
return JSON.stringify(wellFormed(text));
|
|
2188
|
+
}
|
|
2189
|
+
function compactJson(value) {
|
|
2190
|
+
if (typeof value === "string") return jsonString(value);
|
|
2191
|
+
if (Array.isArray(value)) return `[${value.map(compactJson).join(",")}]`;
|
|
2192
|
+
if (value !== null && typeof value === "object") {
|
|
2193
|
+
const members = Object.entries(value).map(([k, v]) => `${jsonString(k)}:${compactJson(v)}`);
|
|
2194
|
+
return `{${members.join(",")}}`;
|
|
2195
|
+
}
|
|
2196
|
+
return JSON.stringify(value);
|
|
2197
|
+
}
|
|
2198
|
+
function asToolChoice(value) {
|
|
2199
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
2200
|
+
return compactJson(value);
|
|
2201
|
+
}
|
|
2202
|
+
return typeof value === "string" ? asText(wellFormed(value)) : void 0;
|
|
2203
|
+
}
|
|
2204
|
+
function invocationParameters(raw) {
|
|
2205
|
+
if (typeof raw !== "string") return { [LLM_INVOCATION_PARAMETERS]: raw };
|
|
2206
|
+
let payload;
|
|
2207
|
+
try {
|
|
2208
|
+
payload = JSON.parse(raw);
|
|
2209
|
+
} catch {
|
|
2210
|
+
return { [LLM_INVOCATION_PARAMETERS]: raw };
|
|
2211
|
+
}
|
|
2212
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
2213
|
+
return { [LLM_INVOCATION_PARAMETERS]: raw };
|
|
2214
|
+
}
|
|
2215
|
+
const leftover = { ...payload };
|
|
2216
|
+
const produced = {};
|
|
2217
|
+
for (const [member, target, convert] of INVOCATION_PARAMETER_MEMBERS) {
|
|
2218
|
+
if (!Object.hasOwn(leftover, member) || produced[target] !== void 0) continue;
|
|
2219
|
+
const value = convert(leftover[member]);
|
|
2220
|
+
if (value === void 0) continue;
|
|
2221
|
+
produced[target] = value;
|
|
2222
|
+
delete leftover[member];
|
|
2223
|
+
}
|
|
2224
|
+
if (Object.keys(produced).length === 0) return { [LLM_INVOCATION_PARAMETERS]: raw };
|
|
2225
|
+
const expansion = { ...produced };
|
|
2226
|
+
if (Object.keys(leftover).length > 0) {
|
|
2227
|
+
expansion[LLM_INVOCATION_PARAMETERS] = toAttributeValue(leftover);
|
|
2228
|
+
}
|
|
2229
|
+
return expansion;
|
|
2230
|
+
}
|
|
2231
|
+
function taxonomyFromKind(raw) {
|
|
2232
|
+
if (typeof raw !== "string") return {};
|
|
2233
|
+
const operation = operationForKind(raw);
|
|
2234
|
+
const produced = { [OPENINFERENCE_SPAN_KIND]: raw };
|
|
2235
|
+
if (operation !== void 0) produced[GEN_AI_OPERATION_NAME] = operation;
|
|
2236
|
+
return produced;
|
|
2237
|
+
}
|
|
2238
|
+
function taxonomyFromOperation(raw) {
|
|
2239
|
+
if (typeof raw !== "string") return {};
|
|
2240
|
+
const kind = kindForOperation(raw);
|
|
2241
|
+
const produced = { [GEN_AI_OPERATION_NAME]: raw };
|
|
2242
|
+
if (kind !== void 0) produced[OPENINFERENCE_SPAN_KIND] = kind;
|
|
2243
|
+
return produced;
|
|
2244
|
+
}
|
|
2245
|
+
function sourceKeys(rule) {
|
|
2246
|
+
return typeof rule.source === "string" ? [rule.source] : rule.source;
|
|
2247
|
+
}
|
|
2248
|
+
function sourcePrefixes(rules) {
|
|
2249
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
2250
|
+
for (const rule of rules) {
|
|
2251
|
+
for (const key of sourceKeys(rule)) {
|
|
2252
|
+
const dot = key.indexOf(".");
|
|
2253
|
+
prefixes.add(dot === -1 ? key : key.slice(0, dot + 1));
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
return [...prefixes];
|
|
2257
|
+
}
|
|
2258
|
+
function applyRules(attributes, rules) {
|
|
2259
|
+
const mapped = [];
|
|
2260
|
+
const rewritten = [];
|
|
2261
|
+
for (const rule of rules) {
|
|
2262
|
+
const keys = sourceKeys(rule);
|
|
2263
|
+
const present = keys.filter((key) => attributes[key] !== void 0);
|
|
2264
|
+
if (present.length === 0) continue;
|
|
2265
|
+
if (isExpanding(rule)) {
|
|
2266
|
+
let expansion;
|
|
2267
|
+
try {
|
|
2268
|
+
expansion = rule.expand(attributes[rule.source]);
|
|
2269
|
+
} catch {
|
|
2270
|
+
continue;
|
|
2271
|
+
}
|
|
2272
|
+
for (const [key, value2] of Object.entries(expansion)) {
|
|
2273
|
+
if (value2 === void 0) continue;
|
|
2274
|
+
const ownSource = key === rule.source;
|
|
2275
|
+
if (!ownSource && attributes[key] !== void 0) continue;
|
|
2276
|
+
attributes[key] = value2;
|
|
2277
|
+
if (ownSource) rewritten.push(key);
|
|
2278
|
+
}
|
|
2279
|
+
if (expansion[rule.source] === void 0) {
|
|
2280
|
+
const targets = Object.keys(expansion).filter((key) => expansion[key] !== void 0);
|
|
2281
|
+
mapped.push({ sources: [rule.source], targets });
|
|
2282
|
+
}
|
|
2283
|
+
continue;
|
|
2284
|
+
}
|
|
2285
|
+
if (attributes[rule.target] !== void 0) {
|
|
2286
|
+
mapped.push({ sources: present, targets: [rule.target] });
|
|
2287
|
+
continue;
|
|
2288
|
+
}
|
|
2289
|
+
const value = rule.convert(present.map((key) => attributes[key]));
|
|
2290
|
+
if (value === void 0) continue;
|
|
2291
|
+
attributes[rule.target] = value;
|
|
2292
|
+
mapped.push({ sources: present, targets: [rule.target] });
|
|
2293
|
+
}
|
|
2294
|
+
return { mapped, rewritten };
|
|
2295
|
+
}
|
|
2296
|
+
function normalizeFirstTokenEvent(span) {
|
|
2297
|
+
const events = span.events;
|
|
2298
|
+
if (events === void 0 || events.length === 0) return {};
|
|
2299
|
+
if (!events.some((event) => event.name === OPENINFERENCE_FIRST_TOKEN_EVENT)) return {};
|
|
2300
|
+
const attributes = span.attributes ?? {};
|
|
2301
|
+
let canonicalSeen = events.some((event) => event.name === GEN_AI_FIRST_TOKEN_EVENT);
|
|
2302
|
+
let firstTokenTime;
|
|
2303
|
+
const rebuilt = [];
|
|
2304
|
+
for (const event of events) {
|
|
2305
|
+
if (event.name !== OPENINFERENCE_FIRST_TOKEN_EVENT) {
|
|
2306
|
+
rebuilt.push(event);
|
|
2307
|
+
continue;
|
|
2308
|
+
}
|
|
2309
|
+
firstTokenTime ??= event.time;
|
|
2310
|
+
if (canonicalSeen) continue;
|
|
2311
|
+
canonicalSeen = true;
|
|
2312
|
+
rebuilt.push({ ...event, name: GEN_AI_FIRST_TOKEN_EVENT });
|
|
2313
|
+
}
|
|
2314
|
+
span.events.splice(0, events.length, ...rebuilt);
|
|
2315
|
+
const added = {};
|
|
2316
|
+
if (attributes[GEN_AI_REQUEST_STREAM] === void 0) added[GEN_AI_REQUEST_STREAM] = true;
|
|
2317
|
+
if (attributes[GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK] === void 0 && firstTokenTime) {
|
|
2318
|
+
const elapsedNanos = hrTimeToNanos(firstTokenTime) - hrTimeToNanos(span.startTime);
|
|
2319
|
+
added[GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK] = Math.max(elapsedNanos, 0) / 1e9;
|
|
2320
|
+
}
|
|
2321
|
+
return added;
|
|
2322
|
+
}
|
|
2323
|
+
function hrTimeToNanos(time) {
|
|
2324
|
+
return time[0] * 1e9 + time[1];
|
|
2325
|
+
}
|
|
2326
|
+
function errorTypeFromExceptionEvent(span) {
|
|
2327
|
+
if (span.status?.code !== SpanStatusCode6.ERROR) return {};
|
|
2328
|
+
if (span.attributes?.[ERROR_TYPE] !== void 0) return {};
|
|
2329
|
+
for (const event of span.events ?? []) {
|
|
2330
|
+
if (event.name !== EXCEPTION_EVENT) continue;
|
|
2331
|
+
const type = event.attributes?.[EXCEPTION_TYPE];
|
|
2332
|
+
if (typeof type === "string" && type !== "") return { [ERROR_TYPE]: type };
|
|
2333
|
+
}
|
|
2334
|
+
return {};
|
|
2335
|
+
}
|
|
2336
|
+
function parseIndex(text) {
|
|
2337
|
+
if (!INDEX.test(text)) return void 0;
|
|
2338
|
+
const value = BigInt(text.startsWith("+") ? text.slice(1) : text);
|
|
2339
|
+
return value >= 0n && value < INT64_LIMIT ? value : void 0;
|
|
2340
|
+
}
|
|
2341
|
+
function byIndex(entries) {
|
|
2342
|
+
return [...entries].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([, value]) => value);
|
|
2343
|
+
}
|
|
2344
|
+
function byCodePoint(a, b) {
|
|
2345
|
+
const x = [...a];
|
|
2346
|
+
const y = [...b];
|
|
2347
|
+
for (let i = 0; i < Math.min(x.length, y.length); i++) {
|
|
2348
|
+
const diff = (x[i].codePointAt(0) ?? 0) - (y[i].codePointAt(0) ?? 0);
|
|
2349
|
+
if (diff !== 0) return diff;
|
|
2350
|
+
}
|
|
2351
|
+
return x.length - y.length;
|
|
2352
|
+
}
|
|
2353
|
+
function cutIndexed(text) {
|
|
2354
|
+
const dot = text.indexOf(".");
|
|
2355
|
+
if (dot < 0) return void 0;
|
|
2356
|
+
const index = parseIndex(text.slice(0, dot));
|
|
2357
|
+
return index === void 0 ? void 0 : [index, text.slice(dot + 1)];
|
|
2358
|
+
}
|
|
2359
|
+
function fieldRecord() {
|
|
2360
|
+
return /* @__PURE__ */ Object.create(null);
|
|
2361
|
+
}
|
|
2362
|
+
function contentPart(item, toolCalls) {
|
|
2363
|
+
const names = Object.keys(item).sort(byCodePoint);
|
|
2364
|
+
if (names.length === 2 && item.type === "text" && Object.hasOwn(item, "text")) {
|
|
2365
|
+
return { type: "text", content: item.text };
|
|
2366
|
+
}
|
|
2367
|
+
if (item.type === "tool_use" && repeatsToolCall(item, toolCalls)) return void 0;
|
|
2368
|
+
const sorted = fieldRecord();
|
|
2369
|
+
for (const name of names) sorted[name] = item[name];
|
|
2370
|
+
return { type: "text", content: toAttributeValue(sorted) };
|
|
2371
|
+
}
|
|
2372
|
+
function repeatsToolCall(item, toolCalls) {
|
|
2373
|
+
const fields = Object.keys(item).filter((name) => name !== "type");
|
|
2374
|
+
if (!fields.every((name) => TOOL_USE_ITEM_FIELDS.has(name))) return false;
|
|
2375
|
+
return [...toolCalls.values()].some(
|
|
2376
|
+
(call) => fields.every((name) => {
|
|
2377
|
+
const field = TOOL_USE_ITEM_FIELDS.get(name);
|
|
2378
|
+
return (Object.hasOwn(call, field) ? call[field] : "") === item[name];
|
|
2379
|
+
})
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
function encodeMessage(message) {
|
|
2383
|
+
const parts = [];
|
|
2384
|
+
if (message.content !== void 0) {
|
|
2385
|
+
parts.push(
|
|
2386
|
+
message.toolCallId ? { type: "tool_call_response", id: message.toolCallId, response: message.content } : { type: "text", content: message.content }
|
|
2387
|
+
);
|
|
2388
|
+
}
|
|
2389
|
+
for (const item of byIndex(message.contents)) {
|
|
2390
|
+
const part = contentPart(item, message.toolCalls);
|
|
2391
|
+
if (part !== void 0) parts.push(part);
|
|
2392
|
+
}
|
|
2393
|
+
for (const call of byIndex(message.toolCalls)) {
|
|
2394
|
+
const part = { type: "tool_call" };
|
|
2395
|
+
for (const [field, name] of TOOL_CALL_FIELDS) {
|
|
2396
|
+
if (Object.hasOwn(call, field) && call[field]) part[name] = call[field];
|
|
2397
|
+
}
|
|
2398
|
+
parts.push(part);
|
|
2399
|
+
}
|
|
2400
|
+
return message.role ? { role: message.role, parts } : { parts };
|
|
2401
|
+
}
|
|
2402
|
+
function reassembleFamily(attributes, prefix) {
|
|
2403
|
+
const messages = /* @__PURE__ */ new Map();
|
|
2404
|
+
const consumed = [];
|
|
2405
|
+
for (const key of Object.keys(attributes)) {
|
|
2406
|
+
if (!key.startsWith(prefix)) continue;
|
|
2407
|
+
const indexed = cutIndexed(key.slice(prefix.length));
|
|
2408
|
+
if (indexed === void 0) continue;
|
|
2409
|
+
const [index, field] = indexed;
|
|
2410
|
+
let call;
|
|
2411
|
+
let item;
|
|
2412
|
+
if (field.startsWith(LLM_MESSAGE_TOOL_CALLS_PREFIX)) {
|
|
2413
|
+
const sub = cutIndexed(field.slice(LLM_MESSAGE_TOOL_CALLS_PREFIX.length));
|
|
2414
|
+
if (sub?.[1].startsWith(LLM_TOOL_CALL_PREFIX)) {
|
|
2415
|
+
call = [sub[0], sub[1].slice(LLM_TOOL_CALL_PREFIX.length)];
|
|
2416
|
+
}
|
|
2417
|
+
} else if (field.startsWith(LLM_MESSAGE_CONTENTS_PREFIX)) {
|
|
2418
|
+
const sub = cutIndexed(field.slice(LLM_MESSAGE_CONTENTS_PREFIX.length));
|
|
2419
|
+
if (sub?.[1].startsWith(LLM_MESSAGE_CONTENT_PREFIX)) {
|
|
2420
|
+
item = [sub[0], sub[1].slice(LLM_MESSAGE_CONTENT_PREFIX.length)];
|
|
2421
|
+
} else if (sub?.[1].startsWith(LLM_TOOL_CALL_PREFIX)) {
|
|
2422
|
+
item = [sub[0], sub[1]];
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
if (call === void 0 && item === void 0 && field !== LLM_MESSAGE_ROLE && field !== LLM_MESSAGE_CONTENT && field !== LLM_MESSAGE_TOOL_CALL_ID) {
|
|
2426
|
+
continue;
|
|
2427
|
+
}
|
|
2428
|
+
const value = attributes[key];
|
|
2429
|
+
if (typeof value !== "string") return void 0;
|
|
2430
|
+
let message = messages.get(index);
|
|
2431
|
+
if (message === void 0) {
|
|
2432
|
+
message = {
|
|
2433
|
+
role: "",
|
|
2434
|
+
content: void 0,
|
|
2435
|
+
toolCallId: "",
|
|
2436
|
+
toolCalls: /* @__PURE__ */ new Map(),
|
|
2437
|
+
contents: /* @__PURE__ */ new Map()
|
|
2438
|
+
};
|
|
2439
|
+
messages.set(index, message);
|
|
2440
|
+
}
|
|
2441
|
+
if (call !== void 0) {
|
|
2442
|
+
const fields = message.toolCalls.get(call[0]) ?? fieldRecord();
|
|
2443
|
+
fields[call[1]] = value;
|
|
2444
|
+
message.toolCalls.set(call[0], fields);
|
|
2445
|
+
} else if (item !== void 0) {
|
|
2446
|
+
const fields = message.contents.get(item[0]) ?? fieldRecord();
|
|
2447
|
+
fields[item[1]] = value;
|
|
2448
|
+
message.contents.set(item[0], fields);
|
|
2449
|
+
} else if (field === LLM_MESSAGE_ROLE) {
|
|
2450
|
+
message.role = value;
|
|
2451
|
+
} else if (field === LLM_MESSAGE_TOOL_CALL_ID) {
|
|
2452
|
+
message.toolCallId = value;
|
|
2453
|
+
} else {
|
|
2454
|
+
message.content = value;
|
|
2455
|
+
}
|
|
2456
|
+
consumed.push(key);
|
|
1075
2457
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
if (
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
2458
|
+
if (messages.size === 0) return void 0;
|
|
2459
|
+
return { messages: byIndex(messages).map(encodeMessage), consumed };
|
|
2460
|
+
}
|
|
2461
|
+
function reassembleOpenInferenceMessages(attributes) {
|
|
2462
|
+
const keys = Object.keys(attributes);
|
|
2463
|
+
if (!keys.some((key) => MESSAGE_FAMILIES.some(([prefix]) => key.startsWith(prefix)))) {
|
|
2464
|
+
return false;
|
|
2465
|
+
}
|
|
2466
|
+
const writes = [];
|
|
2467
|
+
for (const [prefix, target] of MESSAGE_FAMILIES) {
|
|
2468
|
+
if (Object.hasOwn(attributes, target) && attributes[target] !== void 0) continue;
|
|
2469
|
+
const family = reassembleFamily(attributes, prefix);
|
|
2470
|
+
if (family === void 0) continue;
|
|
2471
|
+
writes.push({ target, value: toAttributeValue(family.messages), consumed: family.consumed });
|
|
2472
|
+
}
|
|
2473
|
+
for (const { target, value, consumed } of writes) {
|
|
2474
|
+
for (const key of consumed) delete attributes[key];
|
|
2475
|
+
attributes[target] = value;
|
|
2476
|
+
}
|
|
2477
|
+
return writes.length > 0;
|
|
1085
2478
|
}
|
|
1086
|
-
function
|
|
1087
|
-
|
|
1088
|
-
|
|
2479
|
+
function normalizeToolDefinitions(attributes) {
|
|
2480
|
+
const native = attributes[GEN_AI_TOOL_DEFINITIONS] !== void 0;
|
|
2481
|
+
const indexed = /* @__PURE__ */ new Map();
|
|
2482
|
+
for (const key of Object.keys(attributes)) {
|
|
2483
|
+
if (!key.startsWith("llm.tools.")) continue;
|
|
2484
|
+
const match = LLM_TOOL_SCHEMA.exec(key);
|
|
2485
|
+
if (match !== null) indexed.set(Number(match[1]), key);
|
|
2486
|
+
}
|
|
2487
|
+
if (indexed.size > 0) {
|
|
2488
|
+
const schemas = [];
|
|
2489
|
+
for (const index of [...indexed.keys()].sort((a, b) => a - b)) {
|
|
2490
|
+
const raw = attributes[indexed.get(index)];
|
|
2491
|
+
if (typeof raw !== "string") return;
|
|
2492
|
+
try {
|
|
2493
|
+
schemas.push(JSON.parse(raw));
|
|
2494
|
+
} catch {
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
for (const key of indexed.values()) delete attributes[key];
|
|
2499
|
+
if (!native) attributes[GEN_AI_TOOL_DEFINITIONS] = toAttributeValue(schemas);
|
|
2500
|
+
return;
|
|
2501
|
+
}
|
|
2502
|
+
const bag = attributes[LLM_INVOCATION_PARAMETERS];
|
|
2503
|
+
if (typeof bag !== "string") return;
|
|
2504
|
+
let payload;
|
|
1089
2505
|
try {
|
|
1090
|
-
|
|
2506
|
+
payload = JSON.parse(bag);
|
|
1091
2507
|
} catch {
|
|
1092
|
-
return
|
|
2508
|
+
return;
|
|
1093
2509
|
}
|
|
1094
|
-
if (
|
|
1095
|
-
|
|
2510
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return;
|
|
2511
|
+
const members = payload;
|
|
2512
|
+
const present = INVOCATION_PARAMETERS_CONTENT_MEMBERS.filter((m) => Object.hasOwn(members, m));
|
|
2513
|
+
if (present.length === 0 || !present.every((m) => Array.isArray(members[m]))) return;
|
|
2514
|
+
const definitions = present.flatMap((m) => members[m]);
|
|
2515
|
+
const leftover = Object.fromEntries(
|
|
2516
|
+
Object.entries(members).filter(([key]) => !present.includes(key))
|
|
2517
|
+
);
|
|
2518
|
+
if (Object.keys(leftover).length > 0) {
|
|
2519
|
+
attributes[LLM_INVOCATION_PARAMETERS] = toAttributeValue(leftover);
|
|
2520
|
+
} else {
|
|
2521
|
+
delete attributes[LLM_INVOCATION_PARAMETERS];
|
|
2522
|
+
}
|
|
2523
|
+
if (!native) attributes[GEN_AI_TOOL_DEFINITIONS] = toAttributeValue(definitions);
|
|
2524
|
+
}
|
|
2525
|
+
function sortedCompactJson(value) {
|
|
2526
|
+
if (typeof value === "string") return jsonString(value);
|
|
2527
|
+
if (Array.isArray(value)) return `[${value.map(sortedCompactJson).join(",")}]`;
|
|
2528
|
+
if (value !== null && typeof value === "object") {
|
|
2529
|
+
const record = value;
|
|
2530
|
+
const members = Object.keys(record).sort(byCodePoint).map((k) => `${jsonString(k)}:${sortedCompactJson(record[k])}`);
|
|
2531
|
+
return `{${members.join(",")}}`;
|
|
1096
2532
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
2533
|
+
return JSON.stringify(value);
|
|
2534
|
+
}
|
|
2535
|
+
function systemPart(block) {
|
|
2536
|
+
if (block !== null && typeof block === "object" && !Array.isArray(block)) {
|
|
2537
|
+
const record = block;
|
|
2538
|
+
if (record.type === "text" && typeof record.text === "string") {
|
|
2539
|
+
return { type: "text", content: record.text };
|
|
2540
|
+
}
|
|
1100
2541
|
}
|
|
1101
|
-
|
|
1102
|
-
return JSON.stringify(bag);
|
|
2542
|
+
return { type: "text", content: sortedCompactJson(block) };
|
|
1103
2543
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
"
|
|
2544
|
+
function promoteSystemInstruction(attributes) {
|
|
2545
|
+
const bag = attributes[LLM_INVOCATION_PARAMETERS];
|
|
2546
|
+
if (typeof bag !== "string") return false;
|
|
2547
|
+
let payload;
|
|
2548
|
+
try {
|
|
2549
|
+
payload = JSON.parse(bag);
|
|
2550
|
+
} catch {
|
|
2551
|
+
return false;
|
|
2552
|
+
}
|
|
2553
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return false;
|
|
2554
|
+
const members = payload;
|
|
2555
|
+
if (!Object.hasOwn(members, "system")) return false;
|
|
2556
|
+
const system = members.system;
|
|
2557
|
+
let parts;
|
|
2558
|
+
if (typeof system === "string" && system !== "") {
|
|
2559
|
+
parts = [{ type: "text", content: system }];
|
|
2560
|
+
} else if (Array.isArray(system) && system.length > 0) {
|
|
2561
|
+
parts = system.map(systemPart);
|
|
2562
|
+
} else {
|
|
2563
|
+
return false;
|
|
2564
|
+
}
|
|
2565
|
+
let messages = [];
|
|
2566
|
+
const existing = attributes[GEN_AI_INPUT_MESSAGES];
|
|
2567
|
+
if (existing !== void 0) {
|
|
2568
|
+
if (typeof existing !== "string") return false;
|
|
2569
|
+
try {
|
|
2570
|
+
const parsed = JSON.parse(existing);
|
|
2571
|
+
if (!Array.isArray(parsed)) return false;
|
|
2572
|
+
messages = parsed;
|
|
2573
|
+
} catch {
|
|
2574
|
+
return false;
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
if (messages.some((m) => m?.role === "system")) return false;
|
|
2578
|
+
attributes[GEN_AI_INPUT_MESSAGES] = toAttributeValue([{ role: "system", parts }, ...messages]);
|
|
2579
|
+
const leftover = Object.fromEntries(Object.entries(members).filter(([key]) => key !== "system"));
|
|
2580
|
+
if (Object.keys(leftover).length > 0) {
|
|
2581
|
+
attributes[LLM_INVOCATION_PARAMETERS] = toAttributeValue(leftover);
|
|
2582
|
+
} else {
|
|
2583
|
+
delete attributes[LLM_INVOCATION_PARAMETERS];
|
|
2584
|
+
}
|
|
2585
|
+
return true;
|
|
2586
|
+
}
|
|
2587
|
+
function dropEmbeddingVectors(attributes) {
|
|
2588
|
+
const doomed = Object.keys(attributes).filter(
|
|
2589
|
+
(key) => key.startsWith("embedding.") && EMBEDDING_VECTOR.test(key)
|
|
2590
|
+
);
|
|
2591
|
+
for (const key of doomed) delete attributes[key];
|
|
2592
|
+
}
|
|
2593
|
+
function responseIdFromOutput(attributes) {
|
|
2594
|
+
if (attributes[OPENINFERENCE_SPAN_KIND] !== "LLM" /* LLM */) return {};
|
|
2595
|
+
if (attributes[GEN_AI_RESPONSE_ID] !== void 0) return {};
|
|
2596
|
+
const mime = attributes["output.mime_type"];
|
|
2597
|
+
if (mime !== void 0 && mime !== "application/json") return {};
|
|
2598
|
+
const output = attributes[OUTPUT_VALUE];
|
|
2599
|
+
if (typeof output !== "string" || !output.startsWith("{")) return {};
|
|
2600
|
+
let payload;
|
|
2601
|
+
try {
|
|
2602
|
+
payload = JSON.parse(output);
|
|
2603
|
+
} catch {
|
|
2604
|
+
return {};
|
|
2605
|
+
}
|
|
2606
|
+
const id = payload.id;
|
|
2607
|
+
return typeof id === "string" && id !== "" ? { [GEN_AI_RESPONSE_ID]: id } : {};
|
|
2608
|
+
}
|
|
2609
|
+
var copy, toInt, wrapInList, PROVIDER_NAME_ALIASES, providerName, textValue, LONE_SURROGATE, INVOCATION_PARAMETER_MEMBERS, REQUEST_PARAMETER_GUARDS, TAXONOMY_RULES, OPENINFERENCE_RULES, NORMALIZATION_RULES, OPENINFERENCE_FIRST_TOKEN_EVENT, MESSAGE_FAMILIES, TOOL_CALL_FIELDS, INDEX, INT64_LIMIT, TOOL_USE_ITEM_FIELDS, LLM_TOOL_SCHEMA, EMBEDDING_VECTOR, NormalizingSpanProcessor;
|
|
2610
|
+
var init_normalize = __esm({
|
|
2611
|
+
"src/normalize.ts"() {
|
|
1107
2612
|
"use strict";
|
|
1108
2613
|
init_esm_shims();
|
|
2614
|
+
init_errorType();
|
|
1109
2615
|
init_semconv();
|
|
1110
2616
|
init_serde();
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
2617
|
+
copy = (values) => values[0];
|
|
2618
|
+
toInt = (values) => {
|
|
2619
|
+
const value = values[0];
|
|
2620
|
+
if (typeof value === "number") return Number.isFinite(value) ? Math.trunc(value) : void 0;
|
|
2621
|
+
if (typeof value !== "string") return void 0;
|
|
2622
|
+
const parsed = Number.parseInt(value.trim(), 10);
|
|
2623
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
2624
|
+
};
|
|
2625
|
+
wrapInList = (values) => {
|
|
2626
|
+
const value = values[0];
|
|
2627
|
+
if (Array.isArray(value)) return value;
|
|
2628
|
+
if (typeof value === "string") return [value];
|
|
2629
|
+
if (typeof value === "number") return [value];
|
|
2630
|
+
if (typeof value === "boolean") return [value];
|
|
2631
|
+
return void 0;
|
|
2632
|
+
};
|
|
2633
|
+
PROVIDER_NAME_ALIASES = {
|
|
2634
|
+
// OpenInference enum values
|
|
2635
|
+
mistralai: "mistral_ai",
|
|
2636
|
+
xai: "x_ai",
|
|
2637
|
+
// legacy gen_ai.system values
|
|
2638
|
+
vertex_ai: "gcp.vertex_ai",
|
|
2639
|
+
gemini: "gcp.gemini",
|
|
2640
|
+
"az.ai.inference": "azure.ai.inference",
|
|
2641
|
+
"az.ai.openai": "azure.ai.openai"
|
|
2642
|
+
};
|
|
2643
|
+
providerName = (values) => {
|
|
2644
|
+
const value = values[0];
|
|
2645
|
+
if (typeof value !== "string") return void 0;
|
|
2646
|
+
const key = value.trim().toLowerCase();
|
|
2647
|
+
return Object.hasOwn(PROVIDER_NAME_ALIASES, key) ? PROVIDER_NAME_ALIASES[key] : value;
|
|
2648
|
+
};
|
|
2649
|
+
textValue = (values) => asText(values[0]);
|
|
2650
|
+
LONE_SURROGATE = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g;
|
|
2651
|
+
INVOCATION_PARAMETER_MEMBERS = [
|
|
2652
|
+
// Every instrumentation but the anthropic one keeps `model` in the bag, and
|
|
2653
|
+
// it is the literal request field — unlike llm.model_name, see the
|
|
2654
|
+
// omissions below.
|
|
2655
|
+
["model", GEN_AI_REQUEST_MODEL, asText],
|
|
2656
|
+
["temperature", GEN_AI_REQUEST_TEMPERATURE, asNumber],
|
|
2657
|
+
["top_p", GEN_AI_REQUEST_TOP_P, asNumber],
|
|
2658
|
+
["top_k", GEN_AI_REQUEST_TOP_K, asCount],
|
|
2659
|
+
["max_tokens", GEN_AI_REQUEST_MAX_TOKENS, asCount],
|
|
2660
|
+
// OpenAI's replacement for max_tokens on the reasoning models: "an upper
|
|
2661
|
+
// bound for the number of tokens that can be generated for a completion",
|
|
2662
|
+
// which is what gen_ai.request.max_tokens means. Listed second so a request
|
|
2663
|
+
// carrying both keeps the one the provider would honour.
|
|
2664
|
+
["max_completion_tokens", GEN_AI_REQUEST_MAX_TOKENS, asCount],
|
|
2665
|
+
["frequency_penalty", GEN_AI_REQUEST_FREQUENCY_PENALTY, asNumber],
|
|
2666
|
+
["presence_penalty", GEN_AI_REQUEST_PRESENCE_PENALTY, asNumber],
|
|
2667
|
+
["seed", GEN_AI_REQUEST_SEED, asCount],
|
|
2668
|
+
["n", GEN_AI_REQUEST_CHOICE_COUNT, asCount],
|
|
2669
|
+
["stop", GEN_AI_REQUEST_STOP_SEQUENCES, asTextList],
|
|
2670
|
+
["stop_sequences", GEN_AI_REQUEST_STOP_SEQUENCES, asTextList],
|
|
2671
|
+
["stream", GEN_AI_REQUEST_STREAM, asFlag],
|
|
2672
|
+
// Not a convention key: the GenAI conventions define no tool_choice, so it
|
|
2673
|
+
// goes where an unnamed request parameter goes, rius.request.*. Promoted
|
|
2674
|
+
// because context attribution reads it to tell a forced tool call from an
|
|
2675
|
+
// automatic one, and the bag is content: left inside, it goes wherever
|
|
2676
|
+
// masking sends the bag. It is a routing parameter, not content, and this
|
|
2677
|
+
// runs before masking.
|
|
2678
|
+
["tool_choice", RIUS_REQUEST_TOOL_CHOICE, asToolChoice]
|
|
2679
|
+
];
|
|
2680
|
+
REQUEST_PARAMETER_GUARDS = {
|
|
2681
|
+
[GEN_AI_REQUEST_MODEL]: asText,
|
|
2682
|
+
// string
|
|
2683
|
+
[GEN_AI_REQUEST_MAX_TOKENS]: asCount,
|
|
2684
|
+
// int
|
|
2685
|
+
[GEN_AI_REQUEST_CHOICE_COUNT]: asCount,
|
|
2686
|
+
// int
|
|
2687
|
+
[GEN_AI_REQUEST_TEMPERATURE]: asNumber,
|
|
2688
|
+
// double
|
|
2689
|
+
[GEN_AI_REQUEST_TOP_P]: asNumber,
|
|
2690
|
+
// double
|
|
2691
|
+
[GEN_AI_REQUEST_TOP_K]: asCount,
|
|
2692
|
+
// int
|
|
2693
|
+
[GEN_AI_REQUEST_STOP_SEQUENCES]: asTextList,
|
|
2694
|
+
// string[]
|
|
2695
|
+
[GEN_AI_REQUEST_FREQUENCY_PENALTY]: asNumber,
|
|
2696
|
+
// double
|
|
2697
|
+
[GEN_AI_REQUEST_PRESENCE_PENALTY]: asNumber,
|
|
2698
|
+
// double
|
|
2699
|
+
[GEN_AI_REQUEST_ENCODING_FORMATS]: asTextList,
|
|
2700
|
+
// string[]
|
|
2701
|
+
[GEN_AI_REQUEST_SEED]: asCount,
|
|
2702
|
+
// int
|
|
2703
|
+
[GEN_AI_REQUEST_STREAM]: asFlag,
|
|
2704
|
+
// boolean
|
|
2705
|
+
[GEN_AI_REQUEST_REASONING_LEVEL]: asText,
|
|
2706
|
+
// string
|
|
2707
|
+
[GEN_AI_REQUEST_PREVIOUS_RESPONSE_ID]: asText,
|
|
2708
|
+
// string
|
|
2709
|
+
[GEN_AI_REQUEST_STREAM_CURSOR]: asText
|
|
2710
|
+
// string
|
|
2711
|
+
};
|
|
2712
|
+
TAXONOMY_RULES = [
|
|
2713
|
+
{ source: OPENINFERENCE_SPAN_KIND, expand: taxonomyFromKind, identity: true },
|
|
2714
|
+
{ source: GEN_AI_OPERATION_NAME, expand: taxonomyFromOperation, identity: true }
|
|
2715
|
+
];
|
|
2716
|
+
OPENINFERENCE_RULES = [
|
|
2717
|
+
// Provider. One rule, two spellings, first present wins: llm.provider is
|
|
2718
|
+
// OpenInference's and the more specific (azure/aws/google rather than the
|
|
2719
|
+
// product), gen_ai.system is the deprecated GenAI key, which is mapped here
|
|
2720
|
+
// and never emitted. That is why gen_ai.system is spelled inline rather
|
|
2721
|
+
// than in semconv.ts: that module is the set of keys we EMIT.
|
|
2722
|
+
{
|
|
2723
|
+
source: ["llm.provider", "gen_ai.system"],
|
|
2724
|
+
target: GEN_AI_PROVIDER_NAME,
|
|
2725
|
+
convert: providerName,
|
|
2726
|
+
identity: true
|
|
2727
|
+
},
|
|
2728
|
+
// Model. Only the anthropic instrumentation emits this unambiguous pair;
|
|
2729
|
+
// see the omission note on llm.model_name.
|
|
2730
|
+
{
|
|
2731
|
+
source: "llm.request.model_name",
|
|
2732
|
+
target: GEN_AI_REQUEST_MODEL,
|
|
2733
|
+
convert: copy,
|
|
2734
|
+
identity: true
|
|
2735
|
+
},
|
|
2736
|
+
{ source: "llm.response.model_name", target: GEN_AI_RESPONSE_MODEL, convert: copy },
|
|
2737
|
+
// Tool identity. OpenInference writes the bare key only on TOOL spans (on an
|
|
2738
|
+
// LLM span the same name sits under llm.tools.N.tool.name instead), so the
|
|
2739
|
+
// rule needs no kind guard. Identity, so it also runs at start and a
|
|
2740
|
+
// still-running third-party tool call is named on its pending snapshot, as
|
|
2741
|
+
// a native one is.
|
|
2742
|
+
//
|
|
2743
|
+
// Deliberately NO fallback to the span name. The native path stopped
|
|
2744
|
+
// deriving the tool name from the span name because a span name is not a
|
|
2745
|
+
// tool name, and a wrong name silently groups unrelated calls, which is
|
|
2746
|
+
// worse than an absent one. A third-party span offers no better guarantee.
|
|
2747
|
+
{ source: "tool.name", target: GEN_AI_TOOL_NAME, convert: textValue, identity: true },
|
|
2748
|
+
// Why the model stopped. The source is a SCALAR and the canonical key is an
|
|
2749
|
+
// array (one entry per generation), so wrap rather than copy.
|
|
2750
|
+
//
|
|
2751
|
+
// Known limit: the source only ever holds ONE generation's reason. The
|
|
2752
|
+
// OpenInference OpenAI instrumentation reads choices[0].finish_reason on a
|
|
2753
|
+
// completed response, and whichever choice finished last on a stream. With
|
|
2754
|
+
// n > 1 the array therefore has one entry, not n: a request whose choices
|
|
2755
|
+
// finish ["stop", "length", "stop"] records ["stop"], and nothing on the span
|
|
2756
|
+
// says the list is short. Accepted rather than skipped: n > 1 is rare in
|
|
2757
|
+
// agent code, and the first reason is still the most useful single fact.
|
|
2758
|
+
//
|
|
2759
|
+
// The VALUE is passed through untouched, deliberately. The registry defines
|
|
2760
|
+
// this key as a free-form string array with no enum, and says
|
|
2761
|
+
// instrumentations report whatever the provider supplied. The JS
|
|
2762
|
+
// instrumentations already do: they emit the provider's value verbatim.
|
|
2763
|
+
// OpenInference's PYTHON conversion layer is what lowercases and folds
|
|
2764
|
+
// tool_calls/function_call into tool_call; following it would replace the
|
|
2765
|
+
// string OpenAI actually returned with one neither the provider nor the
|
|
2766
|
+
// conventions use, while leaving Anthropic's end_turn and tool_use alone, so
|
|
2767
|
+
// it would cost fidelity and unify nothing. Grouping "stop" with "end_turn"
|
|
2768
|
+
// is a question for a reader who still has both, not for the SDK that would
|
|
2769
|
+
// destroy one of them. The native path (Generation.setFinishReasons) records
|
|
2770
|
+
// verbatim too, so both paths agree.
|
|
2771
|
+
{ source: "llm.finish_reason", target: GEN_AI_RESPONSE_FINISH_REASONS, convert: wrapInList },
|
|
2772
|
+
// Usage. toInt rather than copy: a count under a canonical key must be a
|
|
2773
|
+
// count, and a converter that yields nothing is how a wrongly-shaped value
|
|
2774
|
+
// stays off the wire.
|
|
2775
|
+
{ source: "llm.token_count.prompt", target: GEN_AI_USAGE_INPUT_TOKENS, convert: toInt },
|
|
2776
|
+
{ source: "llm.token_count.completion", target: GEN_AI_USAGE_OUTPUT_TOKENS, convert: toInt },
|
|
2777
|
+
{
|
|
2778
|
+
source: "llm.token_count.prompt_details.cache_read",
|
|
2779
|
+
target: GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS,
|
|
2780
|
+
convert: toInt
|
|
2781
|
+
},
|
|
2782
|
+
{
|
|
2783
|
+
source: "llm.token_count.prompt_details.cache_write",
|
|
2784
|
+
target: GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS,
|
|
2785
|
+
convert: toInt
|
|
2786
|
+
},
|
|
2787
|
+
{
|
|
2788
|
+
source: "llm.token_count.completion_details.reasoning",
|
|
2789
|
+
target: GEN_AI_USAGE_REASONING_OUTPUT_TOKENS,
|
|
2790
|
+
convert: toInt
|
|
2791
|
+
},
|
|
2792
|
+
// Two GenAI-shaped spellings of the same counts, each its own rule AFTER
|
|
2793
|
+
// the OpenInference one for its target: the OpenInference count keeps the
|
|
2794
|
+
// target where a span somehow carries both, and a separate rule rather than
|
|
2795
|
+
// a second source means an OpenInference count that won't parse still lets
|
|
2796
|
+
// a usable alternate through (a rule converts only its first present
|
|
2797
|
+
// source). Spelled inline for the reason gen_ai.system is: source spellings
|
|
2798
|
+
// we map and never emit.
|
|
2799
|
+
//
|
|
2800
|
+
// cache_creation is the cache-write count's name before the upstream rename
|
|
2801
|
+
// to cache_write. Permanent, not a transition aid: current third-party
|
|
2802
|
+
// releases still emit it (@ai-sdk/otel for every Vercel AI SDK app, and
|
|
2803
|
+
// pydantic-ai), and the backend prices cache writes from the canonical key
|
|
2804
|
+
// alone. The rename is exact, so nothing is lost.
|
|
2805
|
+
{
|
|
2806
|
+
source: "gen_ai.usage.cache_creation.input_tokens",
|
|
2807
|
+
target: GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS,
|
|
2808
|
+
convert: toInt
|
|
2809
|
+
},
|
|
2810
|
+
// pydantic-ai writes OpenAI's reasoning count as one entry of its usage
|
|
2811
|
+
// details namespace. The other providers' names there for the same split
|
|
2812
|
+
// (Anthropic's thinking_tokens, Google's thoughts_tokens) are not mapped,
|
|
2813
|
+
// matching the sink; the rest of the namespace has no canonical key.
|
|
2814
|
+
{
|
|
2815
|
+
source: "gen_ai.usage.details.reasoning_tokens",
|
|
2816
|
+
target: GEN_AI_USAGE_REASONING_OUTPUT_TOKENS,
|
|
2817
|
+
convert: toInt
|
|
2818
|
+
},
|
|
2819
|
+
// The request bag, last: the dedicated model rules above take precedence
|
|
2820
|
+
// over its `model` member. Identity, because what it promotes is.
|
|
2821
|
+
{ source: LLM_INVOCATION_PARAMETERS, expand: invocationParameters, identity: true },
|
|
2822
|
+
// The embedding model: OpenInference's EMBEDDING spans name it here. A
|
|
2823
|
+
// non-empty string only, like the tool name. LAST, as in the Python SDK, so
|
|
2824
|
+
// it only fills the key when no LLM spelling above produced one. Identity,
|
|
2825
|
+
// as the request model is, so a pending embedding span is classifiable.
|
|
2826
|
+
{
|
|
2827
|
+
source: "embedding.model_name",
|
|
2828
|
+
target: GEN_AI_REQUEST_MODEL,
|
|
2829
|
+
convert: textValue,
|
|
2830
|
+
identity: true
|
|
1127
2831
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
2832
|
+
];
|
|
2833
|
+
NORMALIZATION_RULES = [
|
|
2834
|
+
...TAXONOMY_RULES,
|
|
2835
|
+
...OPENINFERENCE_RULES
|
|
2836
|
+
];
|
|
2837
|
+
OPENINFERENCE_FIRST_TOKEN_EVENT = "First Token Stream Event";
|
|
2838
|
+
MESSAGE_FAMILIES = [
|
|
2839
|
+
[LLM_INPUT_MESSAGES_PREFIX, GEN_AI_INPUT_MESSAGES],
|
|
2840
|
+
[LLM_OUTPUT_MESSAGES_PREFIX, GEN_AI_OUTPUT_MESSAGES]
|
|
2841
|
+
];
|
|
2842
|
+
TOOL_CALL_FIELDS = [
|
|
2843
|
+
[LLM_TOOL_CALL_ID, "id"],
|
|
2844
|
+
[LLM_TOOL_CALL_FUNCTION_NAME, "name"],
|
|
2845
|
+
[LLM_TOOL_CALL_FUNCTION_ARGUMENTS, "arguments"]
|
|
2846
|
+
];
|
|
2847
|
+
INDEX = /^[+-]?[0-9]+$/;
|
|
2848
|
+
INT64_LIMIT = 2n ** 63n;
|
|
2849
|
+
TOOL_USE_ITEM_FIELDS = new Map(
|
|
2850
|
+
TOOL_CALL_FIELDS.map(([field]) => [`${LLM_TOOL_CALL_PREFIX}${field}`, field])
|
|
2851
|
+
);
|
|
2852
|
+
LLM_TOOL_SCHEMA = /^llm\.tools\.([0-9]+)\.tool\.json_schema$/;
|
|
2853
|
+
EMBEDDING_VECTOR = /^embedding\.embeddings\.[0-9]+\.embedding\.vector$/;
|
|
2854
|
+
NormalizingSpanProcessor = class {
|
|
2855
|
+
rules;
|
|
2856
|
+
identityRules;
|
|
2857
|
+
prefixes;
|
|
2858
|
+
constructor(rules = NORMALIZATION_RULES) {
|
|
2859
|
+
this.rules = rules;
|
|
2860
|
+
this.identityRules = rules.filter((rule) => rule.identity === true);
|
|
2861
|
+
this.prefixes = sourcePrefixes(rules);
|
|
1142
2862
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
sanitizeStatus(span) {
|
|
1151
|
-
const status = span.status;
|
|
1152
|
-
if (status?.code !== SpanStatusCode3.ERROR || !status.message) return;
|
|
1153
|
-
if (!this.opts.captureContent) {
|
|
1154
|
-
status.message = void 0;
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
if (this.opts.mask === void 0) return;
|
|
1158
|
-
try {
|
|
1159
|
-
const masked = toAttributeValue(
|
|
1160
|
-
this.opts.mask(status.message, { key: STATUS_DESCRIPTION_KEY })
|
|
1161
|
-
);
|
|
1162
|
-
status.message = typeof masked === "string" ? masked : String(masked);
|
|
1163
|
-
} catch {
|
|
1164
|
-
status.message = "[mask error]";
|
|
1165
|
-
}
|
|
2863
|
+
onStart(span, _parentContext) {
|
|
2864
|
+
this.normalize(
|
|
2865
|
+
span.attributes,
|
|
2866
|
+
this.identityRules,
|
|
2867
|
+
(key, value) => span.setAttribute(key, value)
|
|
2868
|
+
);
|
|
2869
|
+
qualifyRecordedExceptions(span);
|
|
1166
2870
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
2871
|
+
onEnd(span) {
|
|
2872
|
+
const attributes = span.attributes;
|
|
2873
|
+
this.normalize(attributes, this.rules, (key, value) => {
|
|
2874
|
+
attributes[key] = value;
|
|
2875
|
+
});
|
|
2876
|
+
normalizeToolDefinitions(attributes);
|
|
2877
|
+
dropEmbeddingVectors(attributes);
|
|
2878
|
+
for (const [key, value] of Object.entries({
|
|
2879
|
+
...normalizeFirstTokenEvent(span),
|
|
2880
|
+
...errorTypeFromExceptionEvent(span),
|
|
2881
|
+
...responseIdFromOutput(attributes)
|
|
2882
|
+
})) {
|
|
2883
|
+
if (attributes[key] === void 0) attributes[key] = value;
|
|
1175
2884
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
2885
|
+
reassembleOpenInferenceMessages(attributes);
|
|
2886
|
+
promoteSystemInstruction(attributes);
|
|
2887
|
+
}
|
|
2888
|
+
normalize(attributes, rules, write) {
|
|
2889
|
+
if (rules.length === 0) return;
|
|
2890
|
+
const bag = attributes;
|
|
2891
|
+
const keys = Object.keys(bag);
|
|
2892
|
+
if (!keys.some((key) => this.prefixes.some((prefix) => key.startsWith(prefix)))) return;
|
|
2893
|
+
const staged = { ...bag };
|
|
2894
|
+
const { mapped, rewritten } = applyRules(staged, rules);
|
|
2895
|
+
const rewrote = new Set(rewritten);
|
|
2896
|
+
for (const key of Object.keys(staged)) {
|
|
2897
|
+
if (staged[key] === void 0) continue;
|
|
2898
|
+
if (bag[key] === void 0 || rewrote.has(key) && bag[key] !== staged[key]) {
|
|
2899
|
+
write(key, staged[key]);
|
|
1181
2900
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
attributes[key] = "[mask error]";
|
|
2901
|
+
}
|
|
2902
|
+
for (const { sources, targets } of mapped) {
|
|
2903
|
+
if (targets.every((key) => bag[key] !== void 0)) {
|
|
2904
|
+
for (const key of sources) delete bag[key];
|
|
1187
2905
|
}
|
|
1188
2906
|
}
|
|
1189
2907
|
}
|
|
1190
|
-
|
|
1191
|
-
return this.inner.shutdown();
|
|
2908
|
+
async forceFlush() {
|
|
1192
2909
|
}
|
|
1193
|
-
|
|
1194
|
-
return this.inner.forceFlush?.() ?? Promise.resolve();
|
|
2910
|
+
async shutdown() {
|
|
1195
2911
|
}
|
|
1196
2912
|
};
|
|
1197
2913
|
}
|
|
1198
2914
|
});
|
|
1199
2915
|
|
|
1200
2916
|
// src/pending.ts
|
|
1201
|
-
import { SpanStatusCode as
|
|
2917
|
+
import { SpanStatusCode as SpanStatusCode7 } from "@opentelemetry/api";
|
|
1202
2918
|
function identityAttributes(attributes) {
|
|
1203
2919
|
if (!attributes) return {};
|
|
1204
2920
|
const result = {};
|
|
@@ -1211,7 +2927,7 @@ function identityAttributes(attributes) {
|
|
|
1211
2927
|
}
|
|
1212
2928
|
function buildSnapshot(span) {
|
|
1213
2929
|
const attributes = identityAttributes(span.attributes);
|
|
1214
|
-
attributes[
|
|
2930
|
+
attributes[RIUS_SPAN_PENDING] = true;
|
|
1215
2931
|
return {
|
|
1216
2932
|
name: span.name,
|
|
1217
2933
|
kind: span.kind,
|
|
@@ -1220,7 +2936,7 @@ function buildSnapshot(span) {
|
|
|
1220
2936
|
startTime: span.startTime,
|
|
1221
2937
|
endTime: span.startTime,
|
|
1222
2938
|
duration: [0, 0],
|
|
1223
|
-
status: { code:
|
|
2939
|
+
status: { code: SpanStatusCode7.UNSET },
|
|
1224
2940
|
attributes,
|
|
1225
2941
|
links: [],
|
|
1226
2942
|
events: [],
|
|
@@ -1286,18 +3002,19 @@ var init_pending = __esm({
|
|
|
1286
3002
|
|
|
1287
3003
|
// src/session.ts
|
|
1288
3004
|
import { randomUUID } from "crypto";
|
|
1289
|
-
import { context as
|
|
3005
|
+
import { context as apiContext3, createContextKey as createContextKey3, trace as trace3 } from "@opentelemetry/api";
|
|
1290
3006
|
function withSession(sessionIdOrFn, maybeFn) {
|
|
1291
3007
|
const [sessionId, fn] = typeof sessionIdOrFn === "function" ? [randomUUID(), sessionIdOrFn] : [sessionIdOrFn, maybeFn];
|
|
1292
|
-
return
|
|
3008
|
+
return apiContext3.with(apiContext3.active().setValue(SESSION_KEY, sessionId), () => fn(sessionId));
|
|
1293
3009
|
}
|
|
1294
3010
|
var SESSION_KEY, SessionSpanProcessor;
|
|
1295
3011
|
var init_session = __esm({
|
|
1296
3012
|
"src/session.ts"() {
|
|
1297
3013
|
"use strict";
|
|
1298
3014
|
init_esm_shims();
|
|
3015
|
+
init_foreign();
|
|
1299
3016
|
init_semconv();
|
|
1300
|
-
SESSION_KEY =
|
|
3017
|
+
SESSION_KEY = createContextKey3("rius-session-id");
|
|
1301
3018
|
SessionSpanProcessor = class {
|
|
1302
3019
|
constructor(defaultSessionId) {
|
|
1303
3020
|
this.defaultSessionId = defaultSessionId;
|
|
@@ -1305,7 +3022,7 @@ var init_session = __esm({
|
|
|
1305
3022
|
defaultSessionId;
|
|
1306
3023
|
onStart(span, parentContext) {
|
|
1307
3024
|
const value = parentContext.getValue(SESSION_KEY);
|
|
1308
|
-
const parent =
|
|
3025
|
+
const parent = twinOf(trace3.getSpan(parentContext));
|
|
1309
3026
|
const inherited = parent?.attributes?.[SESSION_ID];
|
|
1310
3027
|
const sessionId = typeof value === "string" ? value : typeof inherited === "string" ? inherited : this.defaultSessionId;
|
|
1311
3028
|
if (sessionId !== void 0) span.setAttribute(SESSION_ID, sessionId);
|
|
@@ -1321,10 +3038,10 @@ var init_session = __esm({
|
|
|
1321
3038
|
});
|
|
1322
3039
|
|
|
1323
3040
|
// src/workspace.ts
|
|
1324
|
-
import { context as
|
|
3041
|
+
import { context as apiContext4, createContextKey as createContextKey4, trace as trace4 } from "@opentelemetry/api";
|
|
1325
3042
|
function withWorkspace(alias, fn) {
|
|
1326
3043
|
if (!alias) throw new Error("workspace alias must be a non-empty string");
|
|
1327
|
-
return
|
|
3044
|
+
return apiContext4.with(apiContext4.active().setValue(WORKSPACE_KEY, alias), () => fn(alias));
|
|
1328
3045
|
}
|
|
1329
3046
|
function setGlobalRouting(routing) {
|
|
1330
3047
|
globalRouting = routing;
|
|
@@ -1343,13 +3060,14 @@ var init_workspace = __esm({
|
|
|
1343
3060
|
"use strict";
|
|
1344
3061
|
init_esm_shims();
|
|
1345
3062
|
init_esm();
|
|
3063
|
+
init_foreign();
|
|
1346
3064
|
init_semconv();
|
|
1347
|
-
WORKSPACE_KEY =
|
|
3065
|
+
WORKSPACE_KEY = createContextKey4("rius-workspace-alias");
|
|
1348
3066
|
WorkspaceSpanProcessor = class {
|
|
1349
3067
|
/** Straddles already warned about, as "parentAlias->alias"; one warning each, not one per span. */
|
|
1350
3068
|
warnedStraddles = /* @__PURE__ */ new Set();
|
|
1351
3069
|
onStart(span, parentContext) {
|
|
1352
|
-
const parent =
|
|
3070
|
+
const parent = twinOf(trace4.getSpan(parentContext));
|
|
1353
3071
|
const parentAlias = parent?.attributes?.[WORKSPACE_ROUTE];
|
|
1354
3072
|
const scoped = parentContext.getValue(WORKSPACE_KEY);
|
|
1355
3073
|
const alias = typeof scoped === "string" ? scoped : parentAlias;
|
|
@@ -1451,16 +3169,29 @@ var init_workspace = __esm({
|
|
|
1451
3169
|
|
|
1452
3170
|
// src/client.ts
|
|
1453
3171
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1454
|
-
import { context, propagation, trace as
|
|
3172
|
+
import { context, propagation, trace as trace5 } from "@opentelemetry/api";
|
|
1455
3173
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
1456
|
-
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
3174
|
+
import { resourceFromAttributes as resourceFromAttributes2 } from "@opentelemetry/resources";
|
|
1457
3175
|
import {
|
|
1458
3176
|
BatchSpanProcessor,
|
|
1459
3177
|
ParentBasedSampler,
|
|
1460
3178
|
TraceIdRatioBasedSampler
|
|
1461
3179
|
} from "@opentelemetry/sdk-trace-base";
|
|
1462
3180
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
1463
|
-
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
3181
|
+
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION as ATTR_SERVICE_VERSION2 } from "@opentelemetry/semantic-conventions";
|
|
3182
|
+
function resolveSpanLimits() {
|
|
3183
|
+
const fromEnv = (names) => {
|
|
3184
|
+
for (const name of names) {
|
|
3185
|
+
const value = getNumberFromEnv(name);
|
|
3186
|
+
if (value !== void 0) return value;
|
|
3187
|
+
}
|
|
3188
|
+
return void 0;
|
|
3189
|
+
};
|
|
3190
|
+
return {
|
|
3191
|
+
attributeCountLimit: fromEnv(ATTRIBUTE_COUNT_LIMIT_ENV) ?? DEFAULT_SPAN_ATTRIBUTE_COUNT_LIMIT,
|
|
3192
|
+
attributeValueLengthLimit: fromEnv(ATTRIBUTE_VALUE_LENGTH_LIMIT_ENV) ?? Number.POSITIVE_INFINITY
|
|
3193
|
+
};
|
|
3194
|
+
}
|
|
1464
3195
|
function init(options = {}) {
|
|
1465
3196
|
if (globalClient !== void 0) {
|
|
1466
3197
|
console.warn(
|
|
@@ -1470,9 +3201,59 @@ function init(options = {}) {
|
|
|
1470
3201
|
}
|
|
1471
3202
|
const config = resolveConfig(options);
|
|
1472
3203
|
const processors = new DelegatingSpanProcessor();
|
|
3204
|
+
const foreignGlobal = foreignGlobalProviderName();
|
|
3205
|
+
const instanceId = randomUUID2();
|
|
3206
|
+
const resource = resourceFromAttributes2({
|
|
3207
|
+
[ATTR_SERVICE_NAME]: config.serviceName,
|
|
3208
|
+
[SERVICE_INSTANCE_ID]: instanceId,
|
|
3209
|
+
// The agent name the heartbeats already carry. Without it here, spans
|
|
3210
|
+
// fall back to service.name downstream while heartbeats group under the
|
|
3211
|
+
// agent name, so a process that configures the two differently sees its
|
|
3212
|
+
// agents view and its trace list disagree. Resolution defaults the agent
|
|
3213
|
+
// name to the service name, so nothing changes when they are the same.
|
|
3214
|
+
[GEN_AI_AGENT_NAME]: config.agentName,
|
|
3215
|
+
// The same fact in our own namespace, and the one the sink reads first.
|
|
3216
|
+
// `gen_ai.agent.name` above is kept ADDITIVELY and on purpose: it has no
|
|
3217
|
+
// resource-level meaning in the conventions and on a span it names the
|
|
3218
|
+
// agent being INVOKED, so it was one key answering two questions — but
|
|
3219
|
+
// dropping it here would be a flag day, blanking agent identity for
|
|
3220
|
+
// every deployment sitting between this SDK release and the sink
|
|
3221
|
+
// release. A resource rides once per OTLP batch, not once per span, so
|
|
3222
|
+
// carrying both costs essentially nothing.
|
|
3223
|
+
//
|
|
3224
|
+
// Spread like `service.version` below, for the same reason: a process
|
|
3225
|
+
// that named nothing resolves to the `unknown_service` placeholder, and
|
|
3226
|
+
// the main-agent name must then be ABSENT rather than claim an identity
|
|
3227
|
+
// the caller never gave — exactly what the span helpers already do.
|
|
3228
|
+
...config.mainAgentName === void 0 ? {} : { [RIUS_MAIN_AGENT_NAME]: config.mainAgentName },
|
|
3229
|
+
// Optional and never defaulted; see config.ts. `service.instance.id`
|
|
3230
|
+
// above is the process identity, these describe the AGENT the process
|
|
3231
|
+
// runs, which outlives any one process.
|
|
3232
|
+
...config.mainAgentId === void 0 ? {} : { [RIUS_MAIN_AGENT_ID]: config.mainAgentId },
|
|
3233
|
+
...config.mainAgentDescription === void 0 ? {} : { [RIUS_MAIN_AGENT_DESCRIPTION]: config.mainAgentDescription },
|
|
3234
|
+
// The version of the AGENT DEFINITION, never derived from (nor deriving)
|
|
3235
|
+
// `service.version` below: the build and the prompt/tools/policy it runs
|
|
3236
|
+
// move independently.
|
|
3237
|
+
...config.mainAgentVersion === void 0 ? {} : { [RIUS_MAIN_AGENT_VERSION]: config.mainAgentVersion },
|
|
3238
|
+
// `foreign:<Class>` when another SDK already held the OpenTelemetry global
|
|
3239
|
+
// at init(); absent when Rius registered it. Spread, because the ABSENCE of
|
|
3240
|
+
// the key is what says "Rius owns the global here" — a placeholder value
|
|
3241
|
+
// would make every ordinary process look like a resolved conflict.
|
|
3242
|
+
...foreignGlobal === void 0 ? {} : { [RIUS_SDK_GLOBAL_PROVIDER]: `foreign:${foreignGlobal}` },
|
|
3243
|
+
"telemetry.distro.name": "glassflow-rius",
|
|
3244
|
+
"telemetry.distro.version": SDK_VERSION,
|
|
3245
|
+
// Spread rather than assigned so an unresolved version leaves the key
|
|
3246
|
+
// OFF the resource entirely. `resourceFromAttributes` keeps an explicit
|
|
3247
|
+
// `undefined` as a raw attribute, and there is no placeholder to fall
|
|
3248
|
+
// back on by design: `service.name`'s `unknown_service` is the standing
|
|
3249
|
+
// argument against inventing one, since every unversioned process would
|
|
3250
|
+
// then claim the same fake version.
|
|
3251
|
+
...config.serviceVersion === void 0 ? {} : { [ATTR_SERVICE_VERSION2]: config.serviceVersion }
|
|
3252
|
+
});
|
|
1473
3253
|
const authHeaders = config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {};
|
|
1474
3254
|
let health;
|
|
1475
3255
|
let routing;
|
|
3256
|
+
let detector;
|
|
1476
3257
|
if (!config.disabled) {
|
|
1477
3258
|
let base = options.spanExporter ?? new OTLPTraceExporter({
|
|
1478
3259
|
url: `${config.endpoint}/v1/traces`,
|
|
@@ -1491,7 +3272,11 @@ function init(options = {}) {
|
|
|
1491
3272
|
captureContent: config.captureContent,
|
|
1492
3273
|
mask: config.mask
|
|
1493
3274
|
}) : health;
|
|
1494
|
-
const
|
|
3275
|
+
const adopting = new ResourceAdoptingExporter(exporter, resource);
|
|
3276
|
+
const batch = new BatchSpanProcessor(adopting);
|
|
3277
|
+
detector = new ForeignParentDetector(foreignGlobal);
|
|
3278
|
+
processors.add(detector);
|
|
3279
|
+
processors.add(new NormalizingSpanProcessor());
|
|
1495
3280
|
processors.add(new SessionSpanProcessor(config.sessionId));
|
|
1496
3281
|
processors.add(new UserSpanProcessor());
|
|
1497
3282
|
if (routing !== void 0) {
|
|
@@ -1502,27 +3287,31 @@ function init(options = {}) {
|
|
|
1502
3287
|
}
|
|
1503
3288
|
processors.add(batch);
|
|
1504
3289
|
}
|
|
1505
|
-
const
|
|
3290
|
+
const sampler = new BridgeAwareSampler(
|
|
3291
|
+
new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(config.sampleRate) })
|
|
3292
|
+
);
|
|
3293
|
+
const spanLimits = resolveSpanLimits();
|
|
1506
3294
|
const provider = new NodeTracerProvider({
|
|
1507
|
-
|
|
1508
|
-
// distribution via telemetry.distro.*, the same two keys Python stamps.
|
|
1509
|
-
resource: resourceFromAttributes({
|
|
1510
|
-
[ATTR_SERVICE_NAME]: config.serviceName,
|
|
1511
|
-
[SERVICE_INSTANCE_ID]: instanceId,
|
|
1512
|
-
"telemetry.distro.name": "glassflow-rius",
|
|
1513
|
-
"telemetry.distro.version": SDK_VERSION
|
|
1514
|
-
}),
|
|
3295
|
+
resource,
|
|
1515
3296
|
// Always ParentBased, with no AlwaysOn shortcut at rate 1. They are not
|
|
1516
3297
|
// equivalent: ParentBased honours a remote UNSAMPLED parent and drops,
|
|
1517
3298
|
// while AlwaysOn records regardless, producing children of a span the
|
|
1518
3299
|
// upstream service dropped. Rate 1 is the default, so the shortcut would
|
|
1519
3300
|
// have been the default path for every user.
|
|
1520
|
-
|
|
3301
|
+
// Wrapped because ParentBased follows the PARENT's sampled flag, and a
|
|
3302
|
+
// bridged parent's flag is the other SDK's decision (usually always-on),
|
|
3303
|
+
// which would ship every trace it touches whatever sampleRate says.
|
|
3304
|
+
sampler,
|
|
3305
|
+
// Already resolved against the environment; see resolveSpanLimits().
|
|
3306
|
+
spanLimits,
|
|
1521
3307
|
spanProcessors: [processors]
|
|
1522
3308
|
});
|
|
1523
3309
|
const teardown = [];
|
|
1524
3310
|
const ready = config.disabled ? Promise.resolve([]) : enableInstrumentations(processors, provider, void 0, teardown);
|
|
3311
|
+
rememberOwnProvider(provider);
|
|
3312
|
+
ownTracerProvider = provider;
|
|
1525
3313
|
provider.register();
|
|
3314
|
+
const bridged = config.disabled ? void 0 : bridgeOrWarn(provider, processors, sampler, spanLimits, config.bridgeForeignProvider);
|
|
1526
3315
|
let heartbeat;
|
|
1527
3316
|
const heartbeatCanAuthenticate = config.apiKey !== void 0 || options.heartbeatTransport !== void 0;
|
|
1528
3317
|
if (config.heartbeat && !config.disabled && heartbeatCanAuthenticate) {
|
|
@@ -1535,7 +3324,8 @@ function init(options = {}) {
|
|
|
1535
3324
|
agentName: config.agentName,
|
|
1536
3325
|
instanceId,
|
|
1537
3326
|
tracker,
|
|
1538
|
-
transport: options.heartbeatTransport
|
|
3327
|
+
transport: options.heartbeatTransport,
|
|
3328
|
+
foreignParentSpans: detector === void 0 ? void 0 : () => detector.count
|
|
1539
3329
|
});
|
|
1540
3330
|
sender.start();
|
|
1541
3331
|
const beforeExitHandler = () => {
|
|
@@ -1544,24 +3334,56 @@ function init(options = {}) {
|
|
|
1544
3334
|
process.once("beforeExit", beforeExitHandler);
|
|
1545
3335
|
heartbeat = { sender, beforeExitHandler };
|
|
1546
3336
|
}
|
|
1547
|
-
globalClient = createClient({
|
|
3337
|
+
globalClient = createClient({
|
|
3338
|
+
provider,
|
|
3339
|
+
processors,
|
|
3340
|
+
health,
|
|
3341
|
+
ready,
|
|
3342
|
+
teardown,
|
|
3343
|
+
heartbeat,
|
|
3344
|
+
bridge: bridged
|
|
3345
|
+
});
|
|
1548
3346
|
setGlobalRouting(routing);
|
|
3347
|
+
setConfiguredAgentName(config.agentName);
|
|
1549
3348
|
return globalClient;
|
|
1550
3349
|
}
|
|
3350
|
+
function bridgeOrWarn(own, pipeline, sampler, spanLimits, bridgeForeignProvider) {
|
|
3351
|
+
const existing = globalTracerProvider();
|
|
3352
|
+
if (existing === void 0 || existing === own) return void 0;
|
|
3353
|
+
const foreignGlobal = existing.constructor?.name || "unknown";
|
|
3354
|
+
if (bridgeForeignProvider) {
|
|
3355
|
+
const attached = bridge(existing, pipeline, sampler, spanLimits);
|
|
3356
|
+
if (attached !== void 0) {
|
|
3357
|
+
console.info(
|
|
3358
|
+
`[rius] the OpenTelemetry global tracer provider was already set (${foreignGlobal}); Rius attached its span pipeline to it, so spans started through it are exported to Rius too.`
|
|
3359
|
+
);
|
|
3360
|
+
return attached;
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
const why = bridgeForeignProvider ? "it takes no additional span processor" : "bridgeForeignProvider is off";
|
|
3364
|
+
console.warn(
|
|
3365
|
+
`[rius] could not register the Rius tracer provider as the OpenTelemetry global (${foreignGlobal} is already set, or a previous init() claimed it), and Rius is not attached to it (${why}). Rius' own helpers (observe, startSpan, generations) follow this client regardless; third-party code using trace.getTracer() keeps the pre-existing provider, and LLM spans started inside its spans arrive without their parent. Set RIUS_BRIDGE_FOREIGN_PROVIDER=true or init({ bridgeForeignProvider: true }) to send that provider's spans to Rius as well.`
|
|
3366
|
+
);
|
|
3367
|
+
return void 0;
|
|
3368
|
+
}
|
|
1551
3369
|
function getTracer() {
|
|
1552
|
-
return
|
|
3370
|
+
return (ownTracerProvider ?? trace5).getTracer(TRACER_NAME, SDK_VERSION);
|
|
1553
3371
|
}
|
|
1554
|
-
var sinks, heartbeats, createClient, RiusClient, globalClient;
|
|
3372
|
+
var sinks, heartbeats, bridges, createClient, RiusClient, globalClient, DEFAULT_SPAN_ATTRIBUTE_COUNT_LIMIT, ATTRIBUTE_COUNT_LIMIT_ENV, ATTRIBUTE_VALUE_LENGTH_LIMIT_ENV, ownTracerProvider;
|
|
1555
3373
|
var init_client = __esm({
|
|
1556
3374
|
"src/client.ts"() {
|
|
1557
3375
|
"use strict";
|
|
1558
3376
|
init_esm_shims();
|
|
3377
|
+
init_esm();
|
|
3378
|
+
init_agent();
|
|
1559
3379
|
init_config();
|
|
1560
3380
|
init_delegatingProcessor();
|
|
1561
3381
|
init_exportHealth();
|
|
3382
|
+
init_foreign();
|
|
1562
3383
|
init_heartbeat();
|
|
1563
3384
|
init_instrumentation();
|
|
1564
3385
|
init_masking();
|
|
3386
|
+
init_normalize();
|
|
1565
3387
|
init_pending();
|
|
1566
3388
|
init_semconv();
|
|
1567
3389
|
init_session();
|
|
@@ -1570,6 +3392,7 @@ var init_client = __esm({
|
|
|
1570
3392
|
init_workspace();
|
|
1571
3393
|
sinks = /* @__PURE__ */ new WeakMap();
|
|
1572
3394
|
heartbeats = /* @__PURE__ */ new WeakMap();
|
|
3395
|
+
bridges = /* @__PURE__ */ new WeakMap();
|
|
1573
3396
|
RiusClient = class _RiusClient {
|
|
1574
3397
|
provider;
|
|
1575
3398
|
health;
|
|
@@ -1583,6 +3406,7 @@ var init_client = __esm({
|
|
|
1583
3406
|
this.teardown = parts.teardown;
|
|
1584
3407
|
sinks.set(this, parts.processors);
|
|
1585
3408
|
if (parts.heartbeat) heartbeats.set(this, parts.heartbeat);
|
|
3409
|
+
if (parts.bridge) bridges.set(this, parts.bridge);
|
|
1586
3410
|
}
|
|
1587
3411
|
static {
|
|
1588
3412
|
createClient = (parts) => new _RiusClient(parts);
|
|
@@ -1615,19 +3439,28 @@ var init_client = __esm({
|
|
|
1615
3439
|
} catch {
|
|
1616
3440
|
}
|
|
1617
3441
|
}
|
|
3442
|
+
bridges.get(this)?.release();
|
|
1618
3443
|
try {
|
|
1619
3444
|
await this.provider.shutdown();
|
|
1620
3445
|
} finally {
|
|
1621
3446
|
if (globalClient === this) {
|
|
1622
3447
|
globalClient = void 0;
|
|
3448
|
+
ownTracerProvider = void 0;
|
|
1623
3449
|
setGlobalRouting(void 0);
|
|
1624
|
-
|
|
3450
|
+
setConfiguredAgentName(void 0);
|
|
3451
|
+
trace5.disable();
|
|
1625
3452
|
context.disable();
|
|
1626
3453
|
propagation.disable();
|
|
1627
3454
|
}
|
|
1628
3455
|
}
|
|
1629
3456
|
}
|
|
1630
3457
|
};
|
|
3458
|
+
DEFAULT_SPAN_ATTRIBUTE_COUNT_LIMIT = 4096;
|
|
3459
|
+
ATTRIBUTE_COUNT_LIMIT_ENV = ["OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", "OTEL_ATTRIBUTE_COUNT_LIMIT"];
|
|
3460
|
+
ATTRIBUTE_VALUE_LENGTH_LIMIT_ENV = [
|
|
3461
|
+
"OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT",
|
|
3462
|
+
"OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT"
|
|
3463
|
+
];
|
|
1631
3464
|
}
|
|
1632
3465
|
});
|
|
1633
3466
|
|
|
@@ -1639,30 +3472,185 @@ init_client();
|
|
|
1639
3472
|
init_esm_shims();
|
|
1640
3473
|
init_client();
|
|
1641
3474
|
|
|
3475
|
+
// src/contextSizes.ts
|
|
3476
|
+
init_esm_shims();
|
|
3477
|
+
var DETAIL_WINDOW = 50;
|
|
3478
|
+
var MAX_SIZES_BYTES = 8192;
|
|
3479
|
+
var SIZES_VERSION = 1;
|
|
3480
|
+
function isRecord(value) {
|
|
3481
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3482
|
+
}
|
|
3483
|
+
function canonicalBytes(value) {
|
|
3484
|
+
try {
|
|
3485
|
+
const text = JSON.stringify(value);
|
|
3486
|
+
return text === void 0 ? 0 : Buffer.byteLength(text);
|
|
3487
|
+
} catch {
|
|
3488
|
+
return 0;
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
function toolName(tool) {
|
|
3492
|
+
if (!isRecord(tool)) return null;
|
|
3493
|
+
const fn = tool.function;
|
|
3494
|
+
if (isRecord(fn) && typeof fn.name === "string") return fn.name;
|
|
3495
|
+
return typeof tool.name === "string" ? tool.name : null;
|
|
3496
|
+
}
|
|
3497
|
+
function asToolName(name) {
|
|
3498
|
+
return typeof name === "string" ? name : null;
|
|
3499
|
+
}
|
|
3500
|
+
function partsOf(message) {
|
|
3501
|
+
if (!isRecord(message)) return [];
|
|
3502
|
+
return Array.isArray(message.parts) ? message.parts : [];
|
|
3503
|
+
}
|
|
3504
|
+
function toolKey(name) {
|
|
3505
|
+
return name === null ? "\0null" : `name:${name}`;
|
|
3506
|
+
}
|
|
3507
|
+
var Context = class {
|
|
3508
|
+
callNames = /* @__PURE__ */ new Map();
|
|
3509
|
+
constructor(messageLists) {
|
|
3510
|
+
for (const messages of messageLists) {
|
|
3511
|
+
for (const message of messages) {
|
|
3512
|
+
for (const part of partsOf(message)) {
|
|
3513
|
+
if (isRecord(part) && part.type === "tool_call" && typeof part.id === "string" && !this.callNames.has(part.id)) {
|
|
3514
|
+
this.callNames.set(part.id, part.name);
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
partEntry(part) {
|
|
3521
|
+
if (!isRecord(part)) return { type: "unknown" };
|
|
3522
|
+
switch (part.type) {
|
|
3523
|
+
case "text": {
|
|
3524
|
+
const content = part.content ?? null;
|
|
3525
|
+
const bytes = typeof content === "string" ? Buffer.byteLength(content) : canonicalBytes(content);
|
|
3526
|
+
return { type: "text", bytes };
|
|
3527
|
+
}
|
|
3528
|
+
case "tool_call":
|
|
3529
|
+
return { type: "tool_call", tool: asToolName(part.name), bytes: canonicalBytes(part) };
|
|
3530
|
+
case "tool_call_response": {
|
|
3531
|
+
const name = typeof part.id === "string" ? this.callNames.get(part.id) : void 0;
|
|
3532
|
+
return { type: "tool_call_response", tool: asToolName(name), bytes: canonicalBytes(part) };
|
|
3533
|
+
}
|
|
3534
|
+
default:
|
|
3535
|
+
return { type: typeof part.type === "string" ? part.type : "unknown" };
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
messageEntry(message) {
|
|
3539
|
+
const role = isRecord(message) ? message.role : void 0;
|
|
3540
|
+
return {
|
|
3541
|
+
role: typeof role === "string" ? role : String(role),
|
|
3542
|
+
parts: partsOf(message).map((part) => this.partEntry(part))
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
};
|
|
3546
|
+
function hasCacheMarker(message) {
|
|
3547
|
+
return partsOf(message).some(
|
|
3548
|
+
(part) => isRecord(part) && part.cache_control !== void 0 && part.cache_control !== null
|
|
3549
|
+
);
|
|
3550
|
+
}
|
|
3551
|
+
function fold(entries) {
|
|
3552
|
+
let system = 0;
|
|
3553
|
+
let user = 0;
|
|
3554
|
+
let assistant = 0;
|
|
3555
|
+
let multimodal = 0;
|
|
3556
|
+
const tools = /* @__PURE__ */ new Map();
|
|
3557
|
+
for (const { role, parts } of entries) {
|
|
3558
|
+
for (const part of parts) {
|
|
3559
|
+
if (!("bytes" in part)) {
|
|
3560
|
+
multimodal += 1;
|
|
3561
|
+
} else if (part.type === "text") {
|
|
3562
|
+
if (role === "system" || role === "developer") system += part.bytes;
|
|
3563
|
+
else if (role === "assistant") assistant += part.bytes;
|
|
3564
|
+
else user += part.bytes;
|
|
3565
|
+
} else if ("tool" in part) {
|
|
3566
|
+
const key = toolKey(part.tool);
|
|
3567
|
+
const sum = tools.get(key);
|
|
3568
|
+
if (sum) sum.bytes += part.bytes;
|
|
3569
|
+
else tools.set(key, { tool: part.tool, bytes: part.bytes });
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
const result = { messages: entries.length };
|
|
3574
|
+
if (system > 0) result.system_bytes = system;
|
|
3575
|
+
if (user > 0) result.user_bytes = user;
|
|
3576
|
+
if (assistant > 0) result.assistant_bytes = assistant;
|
|
3577
|
+
if (tools.size > 0) result.tools = [...tools.values()];
|
|
3578
|
+
if (multimodal > 0) result.multimodal_parts = multimodal;
|
|
3579
|
+
return result;
|
|
3580
|
+
}
|
|
3581
|
+
function assemble(toolEntries, inputEntries, outputEntries, cacheIndex, window) {
|
|
3582
|
+
const sizes = { version: SIZES_VERSION };
|
|
3583
|
+
if (toolEntries !== void 0) sizes.tool_definitions = toolEntries;
|
|
3584
|
+
let folded;
|
|
3585
|
+
if (inputEntries !== void 0) {
|
|
3586
|
+
const excess = inputEntries.length - window;
|
|
3587
|
+
if (excess > 0) {
|
|
3588
|
+
folded = fold(inputEntries.slice(0, excess));
|
|
3589
|
+
sizes.input_messages = inputEntries.slice(excess);
|
|
3590
|
+
} else {
|
|
3591
|
+
sizes.input_messages = inputEntries;
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
if (outputEntries !== void 0) sizes.output_messages = outputEntries;
|
|
3595
|
+
if (cacheIndex !== void 0) sizes.cache_marker = cacheIndex;
|
|
3596
|
+
if (folded !== void 0) sizes.folded = folded;
|
|
3597
|
+
return sizes;
|
|
3598
|
+
}
|
|
3599
|
+
function asList(value) {
|
|
3600
|
+
return Array.isArray(value) ? value : void 0;
|
|
3601
|
+
}
|
|
3602
|
+
function contextSizes(tools, inputMessages, outputMessages) {
|
|
3603
|
+
try {
|
|
3604
|
+
const toolList = asList(tools);
|
|
3605
|
+
const inputs = asList(inputMessages);
|
|
3606
|
+
const outputs = asList(outputMessages);
|
|
3607
|
+
const context2 = new Context([inputs ?? [], outputs ?? []]);
|
|
3608
|
+
const toolEntries = toolList?.map(
|
|
3609
|
+
(tool) => ({ name: toolName(tool), bytes: canonicalBytes(tool) })
|
|
3610
|
+
);
|
|
3611
|
+
const inputEntries = inputs?.map((message) => context2.messageEntry(message));
|
|
3612
|
+
const outputEntries = outputs?.map((message) => context2.messageEntry(message));
|
|
3613
|
+
let cacheIndex;
|
|
3614
|
+
inputs?.forEach((message, index) => {
|
|
3615
|
+
if (hasCacheMarker(message)) cacheIndex = index;
|
|
3616
|
+
});
|
|
3617
|
+
let window = DETAIL_WINDOW;
|
|
3618
|
+
for (; ; ) {
|
|
3619
|
+
const text = JSON.stringify(
|
|
3620
|
+
assemble(toolEntries, inputEntries, outputEntries, cacheIndex, window)
|
|
3621
|
+
);
|
|
3622
|
+
if (window === 0 || Buffer.byteLength(text) <= MAX_SIZES_BYTES) return text;
|
|
3623
|
+
window = Math.floor(window / 2);
|
|
3624
|
+
}
|
|
3625
|
+
} catch {
|
|
3626
|
+
return JSON.stringify({ version: SIZES_VERSION });
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
|
|
1642
3630
|
// src/messages.ts
|
|
1643
3631
|
init_esm_shims();
|
|
1644
3632
|
init_serde();
|
|
1645
|
-
function
|
|
3633
|
+
function isRecord2(value) {
|
|
1646
3634
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1647
3635
|
}
|
|
1648
|
-
function
|
|
3636
|
+
function asText2(value) {
|
|
1649
3637
|
if (typeof value === "string") return value;
|
|
1650
3638
|
const text = toAttributeValue(value);
|
|
1651
3639
|
return typeof text === "string" ? text : String(text);
|
|
1652
3640
|
}
|
|
1653
3641
|
function normalizePart(item) {
|
|
1654
|
-
if (
|
|
3642
|
+
if (isRecord2(item)) {
|
|
1655
3643
|
if (item.type === "text" && "text" in item) return { type: "text", content: item.text };
|
|
1656
3644
|
if ("type" in item) return item;
|
|
1657
3645
|
}
|
|
1658
|
-
return { type: "text", content:
|
|
3646
|
+
return { type: "text", content: asText2(item) };
|
|
1659
3647
|
}
|
|
1660
3648
|
function normalizeMessage(message, defaultRole) {
|
|
1661
3649
|
if (typeof message === "string") {
|
|
1662
3650
|
return { role: defaultRole, parts: [{ type: "text", content: message }] };
|
|
1663
3651
|
}
|
|
1664
|
-
if (!
|
|
1665
|
-
return { role: defaultRole, parts: [{ type: "text", content:
|
|
3652
|
+
if (!isRecord2(message)) {
|
|
3653
|
+
return { role: defaultRole, parts: [{ type: "text", content: asText2(message) }] };
|
|
1666
3654
|
}
|
|
1667
3655
|
if ("parts" in message) {
|
|
1668
3656
|
return { ...message, role: message.role ?? defaultRole };
|
|
@@ -1671,7 +3659,16 @@ function normalizeMessage(message, defaultRole) {
|
|
|
1671
3659
|
if (role === "tool" && "tool_call_id" in message) {
|
|
1672
3660
|
return {
|
|
1673
3661
|
role: "tool",
|
|
1674
|
-
|
|
3662
|
+
// Missing fields become null, never absent: Python's dict.get() writes
|
|
3663
|
+
// null for them, and the two SDKs' parts must serialize to the same
|
|
3664
|
+
// bytes for the context sizes to agree.
|
|
3665
|
+
parts: [
|
|
3666
|
+
{
|
|
3667
|
+
type: "tool_call_response",
|
|
3668
|
+
id: message.tool_call_id ?? null,
|
|
3669
|
+
response: message.content ?? null
|
|
3670
|
+
}
|
|
3671
|
+
]
|
|
1675
3672
|
};
|
|
1676
3673
|
}
|
|
1677
3674
|
const parts = [];
|
|
@@ -1681,24 +3678,30 @@ function normalizeMessage(message, defaultRole) {
|
|
|
1681
3678
|
} else if (Array.isArray(content)) {
|
|
1682
3679
|
for (const item of content) parts.push(normalizePart(item));
|
|
1683
3680
|
} else if (content !== null && content !== void 0) {
|
|
1684
|
-
parts.push({ type: "text", content:
|
|
3681
|
+
parts.push({ type: "text", content: asText2(content) });
|
|
1685
3682
|
}
|
|
1686
3683
|
const toolCalls = message.tool_calls;
|
|
1687
3684
|
if (Array.isArray(toolCalls)) {
|
|
1688
3685
|
for (const call of toolCalls) {
|
|
1689
|
-
if (!
|
|
1690
|
-
const fn =
|
|
1691
|
-
parts.push({
|
|
3686
|
+
if (!isRecord2(call)) continue;
|
|
3687
|
+
const fn = isRecord2(call.function) ? call.function : {};
|
|
3688
|
+
parts.push({
|
|
3689
|
+
type: "tool_call",
|
|
3690
|
+
id: call.id ?? null,
|
|
3691
|
+
name: fn.name ?? null,
|
|
3692
|
+
arguments: fn.arguments ?? null
|
|
3693
|
+
});
|
|
1692
3694
|
}
|
|
1693
3695
|
}
|
|
1694
3696
|
return { role, parts };
|
|
1695
3697
|
}
|
|
1696
|
-
function
|
|
3698
|
+
function normalizeMessages(messages, defaultRole) {
|
|
1697
3699
|
const list = Array.isArray(messages) ? messages : [messages];
|
|
1698
|
-
return
|
|
3700
|
+
return list.map((message) => normalizeMessage(message, defaultRole));
|
|
1699
3701
|
}
|
|
1700
3702
|
|
|
1701
3703
|
// src/generation.ts
|
|
3704
|
+
init_normalize();
|
|
1702
3705
|
init_semconv();
|
|
1703
3706
|
init_serde();
|
|
1704
3707
|
init_spans();
|
|
@@ -1710,6 +3713,7 @@ var Generation = class extends Observation {
|
|
|
1710
3713
|
constructor(span, provider) {
|
|
1711
3714
|
super(span);
|
|
1712
3715
|
this.provider = provider;
|
|
3716
|
+
this.span.setAttribute(RIUS_CONTEXT_SIZES, contextSizes(void 0, void 0, void 0));
|
|
1713
3717
|
}
|
|
1714
3718
|
provider;
|
|
1715
3719
|
firstTokenRecorded = false;
|
|
@@ -1719,6 +3723,32 @@ var Generation = class extends Observation {
|
|
|
1719
3723
|
* time, so this is what `recordFirstToken` measures the first chunk against.
|
|
1720
3724
|
*/
|
|
1721
3725
|
startedAt = performance.now();
|
|
3726
|
+
/**
|
|
3727
|
+
* The latest normalized input / output and the tool definitions, kept so
|
|
3728
|
+
* `rius.context.sizes` can be computed from all three once, in {@link end}.
|
|
3729
|
+
*/
|
|
3730
|
+
inputMessages;
|
|
3731
|
+
outputMessages;
|
|
3732
|
+
tools;
|
|
3733
|
+
/**
|
|
3734
|
+
* Computed once, as the span ends, rather than on every content write: a
|
|
3735
|
+
* generation with tools, input and output would otherwise pay for it three
|
|
3736
|
+
* times, and only the last result matters. Derived from the normalized
|
|
3737
|
+
* messages BEFORE truncation, which is what makes it trustworthy when the
|
|
3738
|
+
* content attributes are not.
|
|
3739
|
+
*/
|
|
3740
|
+
setContextSizes() {
|
|
3741
|
+
if (!this.inputMessages && !this.outputMessages && !this.tools) return;
|
|
3742
|
+
this.span.setAttribute(
|
|
3743
|
+
RIUS_CONTEXT_SIZES,
|
|
3744
|
+
contextSizes(this.tools, this.inputMessages, this.outputMessages)
|
|
3745
|
+
);
|
|
3746
|
+
}
|
|
3747
|
+
/** Ends the span after recording the context sizes; idempotent like the base. */
|
|
3748
|
+
end() {
|
|
3749
|
+
if (!this.ended) this.setContextSizes();
|
|
3750
|
+
super.end();
|
|
3751
|
+
}
|
|
1722
3752
|
/**
|
|
1723
3753
|
* Record the request messages (`gen_ai.input.messages`), normalised to the
|
|
1724
3754
|
* GenAI `{role, parts}` shape like the Python SDK does: bare strings, OpenAI
|
|
@@ -1726,12 +3756,14 @@ var Generation = class extends Observation {
|
|
|
1726
3756
|
* lists are all accepted. Bare strings default to the `user` role.
|
|
1727
3757
|
*/
|
|
1728
3758
|
setInput(value) {
|
|
1729
|
-
this.
|
|
3759
|
+
this.inputMessages = normalizeMessages(value, "user");
|
|
3760
|
+
this.span.setAttribute(GEN_AI_INPUT_MESSAGES, toAttributeValue(this.inputMessages));
|
|
1730
3761
|
return this;
|
|
1731
3762
|
}
|
|
1732
3763
|
/** Record the response messages (`gen_ai.output.messages`); bare strings default to `assistant`. */
|
|
1733
3764
|
setOutput(value) {
|
|
1734
|
-
this.
|
|
3765
|
+
this.outputMessages = normalizeMessages(value, "assistant");
|
|
3766
|
+
this.span.setAttribute(GEN_AI_OUTPUT_MESSAGES, toAttributeValue(this.outputMessages));
|
|
1735
3767
|
return this;
|
|
1736
3768
|
}
|
|
1737
3769
|
/**
|
|
@@ -1743,6 +3775,7 @@ var Generation = class extends Observation {
|
|
|
1743
3775
|
* masked/stripped under `captureContent: false` like messages are.
|
|
1744
3776
|
*/
|
|
1745
3777
|
setToolDefinitions(tools) {
|
|
3778
|
+
this.tools = tools;
|
|
1746
3779
|
this.span.setAttribute(GEN_AI_TOOL_DEFINITIONS, toAttributeValue(tools));
|
|
1747
3780
|
return this;
|
|
1748
3781
|
}
|
|
@@ -1750,6 +3783,18 @@ var Generation = class extends Observation {
|
|
|
1750
3783
|
this.span.setAttribute(GEN_AI_RESPONSE_MODEL, model);
|
|
1751
3784
|
return this;
|
|
1752
3785
|
}
|
|
3786
|
+
/**
|
|
3787
|
+
* The provider's identifier for this completion (`gen_ai.response.id`) —
|
|
3788
|
+
* OpenAI's `id`, Anthropic's `id`, and so on. A post-call setter and not a
|
|
3789
|
+
* creation option for the reason `setModel` is one: the value arrives WITH
|
|
3790
|
+
* the response, so no span can carry it at creation and no pending snapshot
|
|
3791
|
+
* can either. Recorded verbatim; it is an opaque provider string, and it is
|
|
3792
|
+
* not content, so it survives `captureContent: false`.
|
|
3793
|
+
*/
|
|
3794
|
+
setResponseId(id) {
|
|
3795
|
+
this.span.setAttribute(GEN_AI_RESPONSE_ID, id);
|
|
3796
|
+
return this;
|
|
3797
|
+
}
|
|
1753
3798
|
/**
|
|
1754
3799
|
* Token usage (`gen_ai.usage.*`). Pass provider-reported values as-is;
|
|
1755
3800
|
* never pre-add anything. Per the GenAI conventions, `inputTokens` is the
|
|
@@ -1814,37 +3859,69 @@ var Generation = class extends Observation {
|
|
|
1814
3859
|
return this;
|
|
1815
3860
|
}
|
|
1816
3861
|
};
|
|
3862
|
+
function requestAttributes(modelParameters) {
|
|
3863
|
+
const parameters = new Map(
|
|
3864
|
+
Object.entries(modelParameters ?? {}).filter(([, value]) => value != null)
|
|
3865
|
+
);
|
|
3866
|
+
const attributes = {};
|
|
3867
|
+
for (const [spelling, canonical] of Object.entries(GEN_AI_REQUEST_PARAMETERS)) {
|
|
3868
|
+
if (!parameters.has(spelling)) continue;
|
|
3869
|
+
const value = parameters.get(spelling);
|
|
3870
|
+
parameters.delete(spelling);
|
|
3871
|
+
const guarded = REQUEST_PARAMETER_GUARDS[canonical](value);
|
|
3872
|
+
if (guarded !== void 0 && !Object.hasOwn(attributes, canonical)) {
|
|
3873
|
+
attributes[canonical] = guarded;
|
|
3874
|
+
} else {
|
|
3875
|
+
const coerced = attributeValue(value);
|
|
3876
|
+
if (coerced !== void 0) attributes[`${RIUS_REQUEST_PREFIX}${spelling}`] = coerced;
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
for (const [key, value] of parameters) {
|
|
3880
|
+
const coerced = attributeValue(value);
|
|
3881
|
+
if (coerced !== void 0) attributes[requestAttributeKey(key)] = coerced;
|
|
3882
|
+
}
|
|
3883
|
+
return attributes;
|
|
3884
|
+
}
|
|
1817
3885
|
function attributesFor(options) {
|
|
1818
3886
|
const attributes = { ...kindAttributes("LLM" /* LLM */) };
|
|
1819
3887
|
if (options.operation !== void 0) attributes[GEN_AI_OPERATION_NAME] = options.operation;
|
|
3888
|
+
Object.assign(attributes, requestAttributes(options.modelParameters));
|
|
3889
|
+
if (options.reasoningLevel !== void 0) {
|
|
3890
|
+
attributes[GEN_AI_REQUEST_REASONING_LEVEL] = options.reasoningLevel;
|
|
3891
|
+
}
|
|
1820
3892
|
if (options.model !== void 0) attributes[GEN_AI_REQUEST_MODEL] = options.model;
|
|
1821
3893
|
if (options.provider !== void 0) attributes[GEN_AI_PROVIDER_NAME] = options.provider;
|
|
3894
|
+
if (options.outputType !== void 0) attributes[GEN_AI_OUTPUT_TYPE] = options.outputType;
|
|
1822
3895
|
if (options.userId !== void 0) attributes[USER_ID] = options.userId;
|
|
1823
3896
|
return attributes;
|
|
1824
3897
|
}
|
|
1825
3898
|
function configure2(generation, options) {
|
|
1826
|
-
for (const [key, value] of Object.entries(options.modelParameters ?? {})) {
|
|
1827
|
-
generation.setAttribute(`${GEN_AI_REQUEST_PREFIX}${key}`, value);
|
|
1828
|
-
}
|
|
1829
|
-
if (options.reasoningLevel !== void 0) {
|
|
1830
|
-
generation.setAttribute(GEN_AI_REQUEST_REASONING_LEVEL, options.reasoningLevel);
|
|
1831
|
-
}
|
|
1832
3899
|
if (options.tools !== void 0) generation.setToolDefinitions(options.tools);
|
|
1833
3900
|
if (options.input !== void 0) generation.setInput(options.input);
|
|
1834
3901
|
return generation;
|
|
1835
3902
|
}
|
|
1836
|
-
function
|
|
1837
|
-
|
|
3903
|
+
function resolveName2(name, attributes) {
|
|
3904
|
+
return name ?? composeSpanName("LLM" /* LLM */, attributes);
|
|
3905
|
+
}
|
|
3906
|
+
function startGeneration(nameOrOptions, maybeOptions) {
|
|
3907
|
+
const [name, options] = typeof nameOrOptions === "string" ? [nameOrOptions, maybeOptions ?? {}] : [void 0, nameOrOptions ?? {}];
|
|
3908
|
+
const attributes = attributesFor(options);
|
|
3909
|
+
const span = getTracer().startSpan(resolveName2(name, attributes), {
|
|
1838
3910
|
kind: otelSpanKind("LLM" /* LLM */),
|
|
1839
|
-
attributes
|
|
3911
|
+
attributes
|
|
1840
3912
|
});
|
|
1841
3913
|
return configure2(new Generation(span, options.provider), options);
|
|
1842
3914
|
}
|
|
1843
|
-
function startAsCurrentGeneration(
|
|
1844
|
-
const
|
|
3915
|
+
function startAsCurrentGeneration(first, second, third) {
|
|
3916
|
+
const { name, options, fn } = splitScopedArgs(
|
|
3917
|
+
first,
|
|
3918
|
+
second,
|
|
3919
|
+
third
|
|
3920
|
+
);
|
|
3921
|
+
const attributes = attributesFor(options);
|
|
1845
3922
|
return runActive(
|
|
1846
|
-
name,
|
|
1847
|
-
|
|
3923
|
+
resolveName2(name, attributes),
|
|
3924
|
+
attributes,
|
|
1848
3925
|
options.userId,
|
|
1849
3926
|
(span) => configure2(new Generation(span, options.provider), options),
|
|
1850
3927
|
fn,
|
|
@@ -1854,24 +3931,41 @@ function startAsCurrentGeneration(name, optionsOrFn, maybeFn) {
|
|
|
1854
3931
|
|
|
1855
3932
|
// src/observe.ts
|
|
1856
3933
|
init_esm_shims();
|
|
3934
|
+
init_semconv();
|
|
1857
3935
|
init_spans();
|
|
1858
3936
|
function observe(fn, options = {}) {
|
|
1859
|
-
const
|
|
3937
|
+
const kind = options.kind ?? "CHAIN" /* CHAIN */;
|
|
3938
|
+
const name = options.name ?? (kind === "CHAIN" /* CHAIN */ ? fn.name || "anonymous" : void 0);
|
|
3939
|
+
const toolName2 = options.toolName ?? (kind === "TOOL" /* TOOL */ && name === void 0 ? fn.name || void 0 : void 0);
|
|
1860
3940
|
const captureInput = options.captureInput ?? true;
|
|
1861
3941
|
const captureOutput = options.captureOutput ?? true;
|
|
1862
|
-
const wrapped = (...args) =>
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
3942
|
+
const wrapped = (...args) => {
|
|
3943
|
+
const spanOptions = {
|
|
3944
|
+
kind: options.kind,
|
|
3945
|
+
toolName: toolName2,
|
|
3946
|
+
toolCallId: options.toolCallId,
|
|
3947
|
+
toolType: options.toolType,
|
|
3948
|
+
dataSourceId: options.dataSourceId,
|
|
3949
|
+
topK: options.topK,
|
|
3950
|
+
agentName: options.agentName,
|
|
3951
|
+
agentId: options.agentId,
|
|
3952
|
+
agentVersion: options.agentVersion,
|
|
3953
|
+
// {args, kwargs} is the Python SDK's shape; JavaScript has no keyword
|
|
3954
|
+
// arguments, so kwargs is always empty, but the key stays so a saved
|
|
3955
|
+
// search or a console view reads both SDKs' input.value the same way.
|
|
3956
|
+
input: captureInput ? { args, kwargs: {} } : void 0
|
|
3957
|
+
};
|
|
3958
|
+
const body = async (observation) => {
|
|
1869
3959
|
const result = await fn(...args);
|
|
1870
3960
|
if (captureOutput && result !== void 0) observation.setOutput(result);
|
|
1871
3961
|
return result;
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
|
|
3962
|
+
};
|
|
3963
|
+
return name === void 0 ? startAsCurrentSpan(spanOptions, body) : startAsCurrentSpan(name, spanOptions, body);
|
|
3964
|
+
};
|
|
3965
|
+
Object.defineProperty(wrapped, "name", {
|
|
3966
|
+
value: name ?? (fn.name || "anonymous"),
|
|
3967
|
+
configurable: true
|
|
3968
|
+
});
|
|
1875
3969
|
return wrapped;
|
|
1876
3970
|
}
|
|
1877
3971
|
|
|
@@ -1881,7 +3975,7 @@ init_session();
|
|
|
1881
3975
|
init_user();
|
|
1882
3976
|
init_spans();
|
|
1883
3977
|
init_workspace();
|
|
1884
|
-
var VERSION = "0.
|
|
3978
|
+
var VERSION = "1.0.0";
|
|
1885
3979
|
export {
|
|
1886
3980
|
Generation,
|
|
1887
3981
|
Observation,
|