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