@arizeai/openinference-genai 0.3.1 → 0.3.2

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/src/attributes.ts CHANGED
@@ -121,8 +121,21 @@ const ATTR_GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input
121
121
  const ATTR_GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS =
122
122
  "gen_ai.usage.cache_creation.input_tokens" as const;
123
123
 
124
+ /**
125
+ * A reasoning (thinking) message part.
126
+ *
127
+ * Producers such as `@ai-sdk/otel` already emit `{ type: "reasoning", content: "..." }` parts in
128
+ * `gen_ai.input.messages` / `gen_ai.output.messages`, but the draft OTel GenAI message schemas the
129
+ * types in `__generated__` are derived from do not yet declare the part — hence the local type.
130
+ */
131
+ interface ReasoningPart {
132
+ type: "reasoning";
133
+ content?: unknown;
134
+ [k: string]: unknown;
135
+ }
136
+
124
137
  // Shared part parsing
125
- type AnyPart = GenAIInputMessagePart | GenAIOutputMessagePart;
138
+ type AnyPart = GenAIInputMessagePart | GenAIOutputMessagePart | ReasoningPart;
126
139
 
127
140
  /**
128
141
  * Type guard for a GenAI chat message
@@ -241,6 +254,20 @@ const processMessageParts = ({
241
254
  }
242
255
  continue;
243
256
  }
257
+ case "reasoning": {
258
+ // MESSAGE_CONTENTS entry carrying the reasoning text itself, not the serialized part
259
+ const contentPrefix = `${msgPrefix}${SemanticConventions.MESSAGE_CONTENTS}.${contentIndex}.`;
260
+ set(attrs, `${contentPrefix}${SemanticConventions.MESSAGE_CONTENT_TYPE}`, "reasoning");
261
+ if (part.content != null) {
262
+ set(
263
+ attrs,
264
+ `${contentPrefix}${SemanticConventions.MESSAGE_CONTENT_TEXT}`,
265
+ toStringContent(part.content),
266
+ );
267
+ }
268
+ contentIndex += 1;
269
+ continue;
270
+ }
244
271
  case "tool_call": {
245
272
  const id = part.id ?? undefined;
246
273
  const name = part.name;
@@ -265,13 +292,14 @@ const processMessageParts = ({
265
292
  continue;
266
293
  }
267
294
  default: {
268
- // Generic / unknown part type: capture as JSON text content
295
+ // Generic / unknown part type: capture as JSON text content. Only MESSAGE_CONTENTS is
296
+ // written — the flat MESSAGE_CONTENT is an alternative representation of the same message,
297
+ // so setting both duplicates the content in consumers that render each of them.
269
298
  const genericPart = part as GenericPart;
270
299
  const genericText = toStringContent(genericPart);
271
300
  const contentPrefix = `${msgPrefix}${SemanticConventions.MESSAGE_CONTENTS}.${contentIndex}.`;
272
301
  set(attrs, `${contentPrefix}${SemanticConventions.MESSAGE_CONTENT_TYPE}`, genericPart.type);
273
302
  set(attrs, `${contentPrefix}${SemanticConventions.MESSAGE_CONTENT_TEXT}`, genericText);
274
- set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_CONTENT}`, genericText);
275
303
  contentIndex += 1;
276
304
  }
277
305
  }
@@ -519,13 +547,16 @@ export const mapSystemInstructions = (spanAttributes: Attributes): Attributes =>
519
547
  if (systemInstructions) {
520
548
  set(attrs, `${SemanticConventions.METADATA}.gen_ai.system_instructions`, systemInstructions);
521
549
 
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
- });
550
+ // Only emit the synthetic system message when there are parts to put in it.
551
+ // mapInputMessages shifts its indexes by one on exactly this condition, so
552
+ // emitting here without parts would claim index 0 while the input messages
553
+ // still start at 0, overwriting the first message's role with "system".
554
+ const parts = getSystemInstructionParts(spanAttributes);
555
+ if (parts != null) {
556
+ const msgPrefix = `${SemanticConventions.LLM_INPUT_MESSAGES}.0.`;
557
+ set(attrs, `${msgPrefix}${SemanticConventions.MESSAGE_ROLE}`, "system");
558
+ processMessageParts({ attrs, msgPrefix, parts });
559
+ }
529
560
  }
530
561
  return attrs;
531
562
  };