@arizeai/openinference-genai 0.1.10 → 0.3.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/dist/commonjs/attributes.d.ts +29 -0
- package/dist/commonjs/attributes.d.ts.map +1 -1
- package/dist/commonjs/attributes.js +234 -11
- package/dist/commonjs/attributes.js.map +1 -1
- package/dist/commonjs/tsconfig.commonjs.tsbuildinfo +1 -1
- package/dist/esm/attributes.d.ts +29 -0
- package/dist/esm/attributes.d.ts.map +1 -1
- package/dist/esm/attributes.js +227 -11
- package/dist/esm/attributes.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/attributes.ts +289 -9
package/src/attributes.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
ATTR_GEN_AI_AGENT_NAME,
|
|
6
6
|
ATTR_GEN_AI_COMPLETION,
|
|
7
7
|
ATTR_GEN_AI_INPUT_MESSAGES,
|
|
8
|
+
ATTR_GEN_AI_OPERATION_NAME,
|
|
8
9
|
ATTR_GEN_AI_OUTPUT_MESSAGES,
|
|
9
10
|
ATTR_GEN_AI_PROMPT,
|
|
10
11
|
ATTR_GEN_AI_PROVIDER_NAME,
|
|
@@ -24,6 +25,15 @@ import {
|
|
|
24
25
|
ATTR_GEN_AI_TOOL_TYPE,
|
|
25
26
|
ATTR_GEN_AI_USAGE_INPUT_TOKENS,
|
|
26
27
|
ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
28
|
+
GEN_AI_OPERATION_NAME_VALUE_CHAT,
|
|
29
|
+
GEN_AI_OPERATION_NAME_VALUE_CREATE_AGENT,
|
|
30
|
+
GEN_AI_OPERATION_NAME_VALUE_EMBEDDINGS,
|
|
31
|
+
GEN_AI_OPERATION_NAME_VALUE_EXECUTE_TOOL,
|
|
32
|
+
GEN_AI_OPERATION_NAME_VALUE_GENERATE_CONTENT,
|
|
33
|
+
GEN_AI_OPERATION_NAME_VALUE_INVOKE_AGENT,
|
|
34
|
+
GEN_AI_OPERATION_NAME_VALUE_INVOKE_WORKFLOW,
|
|
35
|
+
GEN_AI_OPERATION_NAME_VALUE_RETRIEVAL,
|
|
36
|
+
GEN_AI_OPERATION_NAME_VALUE_TEXT_COMPLETION,
|
|
27
37
|
} from "@opentelemetry/semantic-conventions/incubating";
|
|
28
38
|
|
|
29
39
|
import {
|
|
@@ -59,6 +69,43 @@ const AGENT_KIND_PREFIXES = [
|
|
|
59
69
|
ATTR_GEN_AI_AGENT_DESCRIPTION,
|
|
60
70
|
] as const;
|
|
61
71
|
|
|
72
|
+
// "plan" was added to the OTel GenAI semantic conventions
|
|
73
|
+
// (open-telemetry/semantic-conventions-genai#97, merged 2026-05-11) but is not yet released,
|
|
74
|
+
// so @opentelemetry/semantic-conventions exports no constant for it — hence the literal.
|
|
75
|
+
const GEN_AI_OPERATION_NAME_VALUE_PLAN = "plan";
|
|
76
|
+
|
|
77
|
+
// gen_ai.operation.name values that classify a span by OpenInference span kind. Mirrors
|
|
78
|
+
// AGENT_KIND_PREFIXES / TOOL_EXECUTION_PREFIXES. These operations are recognized by value
|
|
79
|
+
// because the conversion target — OpenInferenceSpanKind — is a closed enum with no per-
|
|
80
|
+
// operation member, so each operation must be mapped into our vocabulary; the original
|
|
81
|
+
// operation name is not preserved on the OI span.
|
|
82
|
+
const AGENT_OPERATIONS: ReadonlySet<string> = new Set([
|
|
83
|
+
GEN_AI_OPERATION_NAME_VALUE_CREATE_AGENT,
|
|
84
|
+
GEN_AI_OPERATION_NAME_VALUE_INVOKE_AGENT,
|
|
85
|
+
GEN_AI_OPERATION_NAME_VALUE_PLAN,
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
// "invoke_workflow" maps to CHAIN rather than AGENT: a workflow is a fixed orchestration of
|
|
89
|
+
// steps (a chain), whereas AGENT is reserved for autonomous, LLM-driven control flow. An
|
|
90
|
+
// explicit agent identity (agent.* attribute) still takes precedence and yields AGENT; see
|
|
91
|
+
// mapSpanKind.
|
|
92
|
+
const CHAIN_OPERATIONS: ReadonlySet<string> = new Set([
|
|
93
|
+
GEN_AI_OPERATION_NAME_VALUE_INVOKE_WORKFLOW,
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
// Terminal operations describe a single concrete model/data call (a leaf in the span tree),
|
|
97
|
+
// each with a direct OpenInference span kind. operation.name is authoritative for these — an
|
|
98
|
+
// agent.* identity on them is context (which agent owns the call), not a signal to upgrade
|
|
99
|
+
// the span to AGENT (see mapSpanKind).
|
|
100
|
+
const TERMINAL_OPERATION_SPAN_KINDS: ReadonlyMap<string, OpenInferenceSpanKind> = new Map([
|
|
101
|
+
[GEN_AI_OPERATION_NAME_VALUE_CHAT, OpenInferenceSpanKind.LLM],
|
|
102
|
+
[GEN_AI_OPERATION_NAME_VALUE_TEXT_COMPLETION, OpenInferenceSpanKind.LLM],
|
|
103
|
+
[GEN_AI_OPERATION_NAME_VALUE_GENERATE_CONTENT, OpenInferenceSpanKind.LLM],
|
|
104
|
+
[GEN_AI_OPERATION_NAME_VALUE_EMBEDDINGS, OpenInferenceSpanKind.EMBEDDING],
|
|
105
|
+
[GEN_AI_OPERATION_NAME_VALUE_RETRIEVAL, OpenInferenceSpanKind.RETRIEVER],
|
|
106
|
+
[GEN_AI_OPERATION_NAME_VALUE_EXECUTE_TOOL, OpenInferenceSpanKind.TOOL],
|
|
107
|
+
]);
|
|
108
|
+
|
|
62
109
|
const TOOL_EXECUTION_PREFIXES = [
|
|
63
110
|
ATTR_GEN_AI_TOOL_NAME,
|
|
64
111
|
ATTR_GEN_AI_TOOL_DESCRIPTION,
|
|
@@ -66,6 +113,14 @@ const TOOL_EXECUTION_PREFIXES = [
|
|
|
66
113
|
ATTR_GEN_AI_TOOL_TYPE,
|
|
67
114
|
] as const;
|
|
68
115
|
|
|
116
|
+
const ATTR_GEN_AI_SYSTEM_INSTRUCTIONS = "gen_ai.system_instructions" as const;
|
|
117
|
+
const ATTR_GEN_AI_TOOL_DEFINITIONS = "gen_ai.tool.definitions" as const;
|
|
118
|
+
const ATTR_GEN_AI_TOOL_CALL_ARGUMENTS = "gen_ai.tool.call.arguments" as const;
|
|
119
|
+
const ATTR_GEN_AI_TOOL_CALL_RESULT = "gen_ai.tool.call.result" as const;
|
|
120
|
+
const ATTR_GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens" as const;
|
|
121
|
+
const ATTR_GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS =
|
|
122
|
+
"gen_ai.usage.cache_creation.input_tokens" as const;
|
|
123
|
+
|
|
69
124
|
// Shared part parsing
|
|
70
125
|
type AnyPart = GenAIInputMessagePart | GenAIOutputMessagePart;
|
|
71
126
|
|
|
@@ -82,6 +137,70 @@ const isGenAIChatMessage = (value: unknown): value is ChatMessage => {
|
|
|
82
137
|
return true;
|
|
83
138
|
};
|
|
84
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Normalize a GenAI tool definition into the OpenAI-style tool schema shape expected by OpenInference.
|
|
142
|
+
* @param toolDefinition - The tool definition to normalize
|
|
143
|
+
* @returns The normalized tool definition, or the original value when it cannot be normalized
|
|
144
|
+
*/
|
|
145
|
+
const normalizeToolDefinition = (toolDefinition: unknown): unknown => {
|
|
146
|
+
if (
|
|
147
|
+
typeof toolDefinition !== "object" ||
|
|
148
|
+
toolDefinition === null ||
|
|
149
|
+
Array.isArray(toolDefinition)
|
|
150
|
+
) {
|
|
151
|
+
return toolDefinition;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const definition = toolDefinition as Record<string, unknown>;
|
|
155
|
+
if (typeof definition.function === "object" && definition.function !== null) {
|
|
156
|
+
return definition;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (typeof definition.name !== "string") {
|
|
160
|
+
return definition;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const parameters = definition.parameters ?? definition.inputSchema ?? definition.input_schema;
|
|
164
|
+
const normalizedFunction: Record<string, unknown> = { name: definition.name };
|
|
165
|
+
if (typeof definition.description === "string") {
|
|
166
|
+
normalizedFunction.description = definition.description;
|
|
167
|
+
}
|
|
168
|
+
if (parameters != null) {
|
|
169
|
+
normalizedFunction.parameters = parameters;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
type: typeof definition.type === "string" ? definition.type : "function",
|
|
174
|
+
function: normalizedFunction,
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const getSystemInstructionParts = (spanAttributes: Attributes): GenAIInputMessagePart[] | null => {
|
|
179
|
+
const systemInstructions = getString(spanAttributes[ATTR_GEN_AI_SYSTEM_INSTRUCTIONS]);
|
|
180
|
+
if (systemInstructions == null) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const parsedInstructions = safelyParseJSON(systemInstructions);
|
|
185
|
+
if (Array.isArray(parsedInstructions)) {
|
|
186
|
+
const parts = parsedInstructions.flatMap((part): GenAIInputMessagePart[] => {
|
|
187
|
+
if (
|
|
188
|
+
typeof part === "object" &&
|
|
189
|
+
part !== null &&
|
|
190
|
+
"type" in part &&
|
|
191
|
+
part.type === "text" &&
|
|
192
|
+
"content" in part
|
|
193
|
+
) {
|
|
194
|
+
return [{ type: "text", content: toStringContent(part.content) }];
|
|
195
|
+
}
|
|
196
|
+
return [];
|
|
197
|
+
});
|
|
198
|
+
return parts.length > 0 ? parts : null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return [{ type: "text", content: systemInstructions }];
|
|
202
|
+
};
|
|
203
|
+
|
|
85
204
|
/**
|
|
86
205
|
* Process genai message parts into openinference attributes
|
|
87
206
|
*
|
|
@@ -142,10 +261,7 @@ const processMessageParts = ({
|
|
|
142
261
|
const response = toStringContent(part.response);
|
|
143
262
|
|
|
144
263
|
set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_TOOL_CALL_ID}`, id);
|
|
145
|
-
|
|
146
|
-
set(attrs, `${contentPrefix}${SemanticConventions.MESSAGE_CONTENT_TYPE}`, "text");
|
|
147
|
-
set(attrs, `${contentPrefix}${SemanticConventions.MESSAGE_CONTENT_TEXT}`, response);
|
|
148
|
-
contentIndex += 1;
|
|
264
|
+
set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_CONTENT}`, response);
|
|
149
265
|
continue;
|
|
150
266
|
}
|
|
151
267
|
default: {
|
|
@@ -162,6 +278,30 @@ const processMessageParts = ({
|
|
|
162
278
|
}
|
|
163
279
|
};
|
|
164
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Set OpenInference attributes for a single tool response message.
|
|
283
|
+
*
|
|
284
|
+
* GenAI can group multiple tool_call_response parts under one tool-role message;
|
|
285
|
+
* each response needs its own OpenInference message to avoid overwriting siblings.
|
|
286
|
+
* @param params - The tool response message mapping parameters
|
|
287
|
+
*/
|
|
288
|
+
const setToolCallResponseMessage = ({
|
|
289
|
+
attrs,
|
|
290
|
+
msgPrefix,
|
|
291
|
+
part,
|
|
292
|
+
}: {
|
|
293
|
+
attrs: Attributes;
|
|
294
|
+
msgPrefix: string;
|
|
295
|
+
part: Extract<AnyPart, { type: "tool_call_response" }>;
|
|
296
|
+
}): void => {
|
|
297
|
+
const id = part.id ?? undefined;
|
|
298
|
+
const response = toStringContent(part.response);
|
|
299
|
+
|
|
300
|
+
set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_ROLE}`, "tool");
|
|
301
|
+
set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_TOOL_CALL_ID}`, id);
|
|
302
|
+
set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_CONTENT}`, response);
|
|
303
|
+
};
|
|
304
|
+
|
|
165
305
|
/**
|
|
166
306
|
* Convert GenAI span attributes to OpenInference span attributes
|
|
167
307
|
* @param spanAttributes - The span attributes containing GenAI span attributes to convert
|
|
@@ -172,11 +312,15 @@ export const convertGenAISpanAttributesToOpenInferenceSpanAttributes = (
|
|
|
172
312
|
): Attributes => {
|
|
173
313
|
return merge(
|
|
174
314
|
mapProviderAndSystem(spanAttributes),
|
|
315
|
+
mapAgentAttributes(spanAttributes),
|
|
175
316
|
mapModels(spanAttributes),
|
|
176
317
|
mapSpanKind(spanAttributes),
|
|
177
318
|
mapInvocationParameters(spanAttributes),
|
|
178
319
|
mapInputMessages(spanAttributes),
|
|
179
320
|
mapOutputMessages(spanAttributes),
|
|
321
|
+
mapAgent(spanAttributes),
|
|
322
|
+
mapSystemInstructions(spanAttributes),
|
|
323
|
+
mapToolDefinitions(spanAttributes),
|
|
180
324
|
mapTokenCounts(spanAttributes),
|
|
181
325
|
mapToolExecution(spanAttributes),
|
|
182
326
|
mapInputValue(spanAttributes),
|
|
@@ -193,10 +337,28 @@ export const convertGenAISpanAttributesToOpenInferenceSpanAttributes = (
|
|
|
193
337
|
export const mapProviderAndSystem = (spanAttributes: Attributes): Attributes => {
|
|
194
338
|
const attrs: Attributes = {};
|
|
195
339
|
const provider = getString(spanAttributes[ATTR_GEN_AI_PROVIDER_NAME]);
|
|
340
|
+
set(attrs, SemanticConventions.LLM_SYSTEM, provider);
|
|
196
341
|
set(attrs, SemanticConventions.LLM_PROVIDER, provider);
|
|
197
342
|
return attrs;
|
|
198
343
|
};
|
|
199
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Map GenAI agent attributes to OpenInference agent attributes.
|
|
347
|
+
*
|
|
348
|
+
* Currently only gen_ai.agent.name -> agent.name. gen_ai.agent.id and
|
|
349
|
+
* gen_ai.agent.description (which also drive AGENT span-kind detection in mapSpanKind) are
|
|
350
|
+
* intentionally not mapped, as OpenInference has no corresponding agent attributes for them.
|
|
351
|
+
*
|
|
352
|
+
* @param spanAttributes - The GenAI span attributes to read agent attributes from
|
|
353
|
+
* @returns The mapped OpenInference agent attributes
|
|
354
|
+
*/
|
|
355
|
+
export const mapAgentAttributes = (spanAttributes: Attributes): Attributes => {
|
|
356
|
+
const attrs: Attributes = {};
|
|
357
|
+
const agentName = getString(spanAttributes[ATTR_GEN_AI_AGENT_NAME]);
|
|
358
|
+
set(attrs, SemanticConventions.AGENT_NAME, agentName);
|
|
359
|
+
return attrs;
|
|
360
|
+
};
|
|
361
|
+
|
|
200
362
|
/**
|
|
201
363
|
* Map model name to openinference attributes
|
|
202
364
|
* @param spanAttributes - The span attributes containing model name to map
|
|
@@ -220,11 +382,28 @@ export const mapSpanKind = (spanAttributes: Attributes): Attributes => {
|
|
|
220
382
|
const attrs: Attributes = {};
|
|
221
383
|
// default to LLM for now
|
|
222
384
|
let spanKind = OpenInferenceSpanKind.LLM;
|
|
223
|
-
|
|
224
|
-
|
|
385
|
+
const operationName = getString(spanAttributes[ATTR_GEN_AI_OPERATION_NAME]);
|
|
386
|
+
const terminalSpanKind =
|
|
387
|
+
operationName !== undefined ? TERMINAL_OPERATION_SPAN_KINDS.get(operationName) : undefined;
|
|
388
|
+
const hasAgentAttributes = AGENT_KIND_PREFIXES.some((prefix) => spanAttributes[prefix]);
|
|
389
|
+
if (terminalSpanKind !== undefined) {
|
|
390
|
+
// Concrete leaf operation (chat/text_completion/generate_content/embeddings/retrieval/
|
|
391
|
+
// execute_tool): authoritative. An agent.* identity here is just context (which agent
|
|
392
|
+
// owns the call) and does not upgrade it to AGENT.
|
|
393
|
+
spanKind = terminalSpanKind;
|
|
394
|
+
} else if (
|
|
395
|
+
hasAgentAttributes ||
|
|
396
|
+
(operationName !== undefined && AGENT_OPERATIONS.has(operationName))
|
|
397
|
+
) {
|
|
398
|
+
// Control-flow span: an agent.* identity, or an agent-class operation, makes it AGENT —
|
|
399
|
+
// even over a CHAIN-class operation like invoke_workflow.
|
|
225
400
|
spanKind = OpenInferenceSpanKind.AGENT;
|
|
401
|
+
} else if (operationName !== undefined && CHAIN_OPERATIONS.has(operationName)) {
|
|
402
|
+
// CHAIN-class operation, only reached when no agent identity is present.
|
|
403
|
+
spanKind = OpenInferenceSpanKind.CHAIN;
|
|
226
404
|
}
|
|
227
|
-
//
|
|
405
|
+
// Tool attributes force TOOL even when the operation says otherwise — covers tool-execution
|
|
406
|
+
// spans not labeled with operation.name = execute_tool.
|
|
228
407
|
if (TOOL_EXECUTION_PREFIXES.some((prefix) => spanAttributes[prefix])) {
|
|
229
408
|
spanKind = OpenInferenceSpanKind.TOOL;
|
|
230
409
|
}
|
|
@@ -284,6 +463,9 @@ export const mapInputValue = (spanAttributes: Attributes): Attributes => {
|
|
|
284
463
|
// fallback to deprecated prompt attribute if input is not present
|
|
285
464
|
input = getString(spanAttributes[ATTR_GEN_AI_PROMPT]);
|
|
286
465
|
}
|
|
466
|
+
if (!input) {
|
|
467
|
+
input = getString(spanAttributes[ATTR_GEN_AI_INPUT_MESSAGES]);
|
|
468
|
+
}
|
|
287
469
|
// only set input value and mime type if input is present
|
|
288
470
|
if (input) {
|
|
289
471
|
set(attrs, SemanticConventions.INPUT_VALUE, input);
|
|
@@ -304,6 +486,9 @@ export const mapOutputValue = (spanAttributes: Attributes): Attributes => {
|
|
|
304
486
|
// fallback to deprecated completion attribute if output is not present
|
|
305
487
|
output = getString(spanAttributes[ATTR_GEN_AI_COMPLETION]);
|
|
306
488
|
}
|
|
489
|
+
if (!output) {
|
|
490
|
+
output = getString(spanAttributes[ATTR_GEN_AI_OUTPUT_MESSAGES]);
|
|
491
|
+
}
|
|
307
492
|
// only set output value and mime type if output is present
|
|
308
493
|
if (output) {
|
|
309
494
|
set(attrs, SemanticConventions.OUTPUT_VALUE, output);
|
|
@@ -312,6 +497,60 @@ export const mapOutputValue = (spanAttributes: Attributes): Attributes => {
|
|
|
312
497
|
return attrs;
|
|
313
498
|
};
|
|
314
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Map GenAI agent attributes to OpenInference attributes.
|
|
502
|
+
* @param spanAttributes - The span attributes containing agent details
|
|
503
|
+
* @returns The mapped agent attributes
|
|
504
|
+
*/
|
|
505
|
+
export const mapAgent = (spanAttributes: Attributes): Attributes => {
|
|
506
|
+
const attrs: Attributes = {};
|
|
507
|
+
set(attrs, SemanticConventions.AGENT_NAME, getString(spanAttributes[ATTR_GEN_AI_AGENT_NAME]));
|
|
508
|
+
return attrs;
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Map GenAI system instructions into metadata for preservation.
|
|
513
|
+
* @param spanAttributes - The span attributes containing system instructions
|
|
514
|
+
* @returns The mapped metadata attributes
|
|
515
|
+
*/
|
|
516
|
+
export const mapSystemInstructions = (spanAttributes: Attributes): Attributes => {
|
|
517
|
+
const attrs: Attributes = {};
|
|
518
|
+
const systemInstructions = getString(spanAttributes[ATTR_GEN_AI_SYSTEM_INSTRUCTIONS]);
|
|
519
|
+
if (systemInstructions) {
|
|
520
|
+
set(attrs, `${SemanticConventions.METADATA}.gen_ai.system_instructions`, systemInstructions);
|
|
521
|
+
|
|
522
|
+
const msgPrefix = `${SemanticConventions.LLM_INPUT_MESSAGES}.0.`;
|
|
523
|
+
set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_ROLE}`, "system");
|
|
524
|
+
processMessageParts({
|
|
525
|
+
attrs,
|
|
526
|
+
msgPrefix,
|
|
527
|
+
parts: getSystemInstructionParts(spanAttributes) ?? [],
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
return attrs;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Map GenAI tool definitions to OpenInference LLM tool schema attributes.
|
|
535
|
+
* @param spanAttributes - The span attributes containing tool definitions
|
|
536
|
+
* @returns The mapped tool schema attributes
|
|
537
|
+
*/
|
|
538
|
+
export const mapToolDefinitions = (spanAttributes: Attributes): Attributes => {
|
|
539
|
+
const attrs: Attributes = {};
|
|
540
|
+
const toolDefinitions = getString(spanAttributes[ATTR_GEN_AI_TOOL_DEFINITIONS]);
|
|
541
|
+
const parsedToolDefinitions = safelyParseJSON(toolDefinitions);
|
|
542
|
+
if (Array.isArray(parsedToolDefinitions)) {
|
|
543
|
+
parsedToolDefinitions.forEach((toolDefinition, index) => {
|
|
544
|
+
set(
|
|
545
|
+
attrs,
|
|
546
|
+
`${SemanticConventions.LLM_TOOLS}.${index}.${SemanticConventions.TOOL_JSON_SCHEMA}`,
|
|
547
|
+
safelyJSONStringify(normalizeToolDefinition(toolDefinition)),
|
|
548
|
+
);
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
return attrs;
|
|
552
|
+
};
|
|
553
|
+
|
|
315
554
|
/**
|
|
316
555
|
* Map input messages to openinference attributes
|
|
317
556
|
* @param spanAttributes - The span attributes containing input messages to map
|
|
@@ -322,13 +561,30 @@ export const mapInputMessages = (spanAttributes: Attributes): Attributes => {
|
|
|
322
561
|
const genAIInputMessages = safelyParseJSON(spanAttributes[ATTR_GEN_AI_INPUT_MESSAGES]);
|
|
323
562
|
|
|
324
563
|
if (Array.isArray(genAIInputMessages)) {
|
|
325
|
-
|
|
564
|
+
let msgIndex = getSystemInstructionParts(spanAttributes) != null ? 1 : 0;
|
|
565
|
+
(genAIInputMessages as unknown[]).forEach((msg) => {
|
|
326
566
|
if (!isGenAIChatMessage(msg)) return;
|
|
567
|
+
|
|
568
|
+
const toolCallResponses = msg.parts.filter(
|
|
569
|
+
(part): part is Extract<AnyPart, { type: "tool_call_response" }> =>
|
|
570
|
+
part?.type === "tool_call_response",
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
if (msg.role === "tool" && toolCallResponses.length > 0) {
|
|
574
|
+
toolCallResponses.forEach((part) => {
|
|
575
|
+
const msgPrefix = `${SemanticConventions.LLM_INPUT_MESSAGES}.${msgIndex}.`;
|
|
576
|
+
setToolCallResponseMessage({ attrs, msgPrefix, part });
|
|
577
|
+
msgIndex += 1;
|
|
578
|
+
});
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
327
582
|
const msgPrefix = `${SemanticConventions.LLM_INPUT_MESSAGES}.${msgIndex}.`;
|
|
328
583
|
// set the message role
|
|
329
584
|
set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_ROLE}`, msg.role);
|
|
330
585
|
// process and set the rest of the message parts
|
|
331
586
|
processMessageParts({ attrs, msgPrefix, parts: msg.parts });
|
|
587
|
+
msgIndex += 1;
|
|
332
588
|
});
|
|
333
589
|
}
|
|
334
590
|
|
|
@@ -368,6 +624,10 @@ export const mapTokenCounts = (spanAttributes: Attributes): Attributes => {
|
|
|
368
624
|
const attrs: Attributes = {};
|
|
369
625
|
const inputTokens = getNumber(spanAttributes[ATTR_GEN_AI_USAGE_INPUT_TOKENS]);
|
|
370
626
|
const outputTokens = getNumber(spanAttributes[ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]);
|
|
627
|
+
const cacheReadInputTokens = getNumber(spanAttributes[ATTR_GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS]);
|
|
628
|
+
const cacheCreationInputTokens = getNumber(
|
|
629
|
+
spanAttributes[ATTR_GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS],
|
|
630
|
+
);
|
|
371
631
|
if (typeof inputTokens === "number") {
|
|
372
632
|
set(attrs, SemanticConventions.LLM_TOKEN_COUNT_PROMPT, inputTokens);
|
|
373
633
|
}
|
|
@@ -377,6 +637,16 @@ export const mapTokenCounts = (spanAttributes: Attributes): Attributes => {
|
|
|
377
637
|
if (typeof inputTokens === "number" && typeof outputTokens === "number") {
|
|
378
638
|
set(attrs, SemanticConventions.LLM_TOKEN_COUNT_TOTAL, inputTokens + outputTokens);
|
|
379
639
|
}
|
|
640
|
+
if (typeof cacheReadInputTokens === "number") {
|
|
641
|
+
set(attrs, SemanticConventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ, cacheReadInputTokens);
|
|
642
|
+
}
|
|
643
|
+
if (typeof cacheCreationInputTokens === "number") {
|
|
644
|
+
set(
|
|
645
|
+
attrs,
|
|
646
|
+
SemanticConventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE,
|
|
647
|
+
cacheCreationInputTokens,
|
|
648
|
+
);
|
|
649
|
+
}
|
|
380
650
|
return attrs;
|
|
381
651
|
};
|
|
382
652
|
|
|
@@ -391,11 +661,21 @@ export const mapToolExecution = (spanAttributes: Attributes): Attributes => {
|
|
|
391
661
|
const toolName = getString(spanAttributes[ATTR_GEN_AI_TOOL_NAME]);
|
|
392
662
|
const toolDescription = getString(spanAttributes[ATTR_GEN_AI_TOOL_DESCRIPTION]);
|
|
393
663
|
const toolCallId = getString(spanAttributes[ATTR_GEN_AI_TOOL_CALL_ID]);
|
|
664
|
+
const toolCallArguments = getString(spanAttributes[ATTR_GEN_AI_TOOL_CALL_ARGUMENTS]);
|
|
665
|
+
const toolCallResult = getString(spanAttributes[ATTR_GEN_AI_TOOL_CALL_RESULT]);
|
|
394
666
|
// parse supported tool details
|
|
395
|
-
// note: while openinference can track parameters, gen_ai does not provide this information
|
|
396
667
|
set(attrs, SemanticConventions.TOOL_NAME, toolName);
|
|
397
668
|
set(attrs, SemanticConventions.TOOL_DESCRIPTION, toolDescription);
|
|
398
669
|
set(attrs, SemanticConventions.TOOL_CALL_ID, toolCallId);
|
|
670
|
+
set(attrs, SemanticConventions.TOOL_PARAMETERS, toolCallArguments);
|
|
671
|
+
if (toolCallArguments) {
|
|
672
|
+
set(attrs, SemanticConventions.INPUT_VALUE, toolCallArguments);
|
|
673
|
+
set(attrs, SemanticConventions.INPUT_MIME_TYPE, getMimeType(toolCallArguments));
|
|
674
|
+
}
|
|
675
|
+
if (toolCallResult) {
|
|
676
|
+
set(attrs, SemanticConventions.OUTPUT_VALUE, toolCallResult);
|
|
677
|
+
set(attrs, SemanticConventions.OUTPUT_MIME_TYPE, getMimeType(toolCallResult));
|
|
678
|
+
}
|
|
399
679
|
|
|
400
680
|
return attrs;
|
|
401
681
|
};
|