Skip to content

Commit 6fcadd6

Browse files
committed
CR Changes
1 parent 1a62402 commit 6fcadd6

File tree

5 files changed

+74
-62
lines changed

5 files changed

+74
-62
lines changed

packages/instrumentation-openai/src/image-wrappers.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ATTR_GEN_AI_OUTPUT_MESSAGES,
99
ATTR_GEN_AI_USAGE_INPUT_TOKENS,
1010
ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
11+
ATTR_GEN_AI_OPERATION_NAME,
1112
GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
1213
} from "@opentelemetry/semantic-conventions/incubating";
1314
import type { ImageUploadCallback } from "./types";
@@ -396,7 +397,6 @@ export async function setImageGenerationResponseAttributes(
396397
attributes[ATTR_GEN_AI_OUTPUT_MESSAGES] = JSON.stringify([
397398
{
398399
role: "assistant",
399-
finish_reason: "stop",
400400
parts: [{ type: "uri", modality: "image", uri: imageOutputUrl }],
401401
},
402402
]);
@@ -427,6 +427,7 @@ export function wrapImageGeneration(
427427
kind: SpanKind.CLIENT,
428428
attributes: {
429429
[ATTR_GEN_AI_PROVIDER_NAME]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
430+
[ATTR_GEN_AI_OPERATION_NAME]: "image_generation",
430431
"gen_ai.request.type": "image_generation",
431432
},
432433
});
@@ -486,6 +487,7 @@ export function wrapImageEdit(
486487
kind: SpanKind.CLIENT,
487488
attributes: {
488489
[ATTR_GEN_AI_PROVIDER_NAME]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
490+
[ATTR_GEN_AI_OPERATION_NAME]: "image_edit",
489491
"gen_ai.request.type": "image_edit",
490492
},
491493
});
@@ -553,6 +555,7 @@ export function wrapImageVariation(
553555
kind: SpanKind.CLIENT,
554556
attributes: {
555557
[ATTR_GEN_AI_PROVIDER_NAME]: GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
558+
[ATTR_GEN_AI_OPERATION_NAME]: "image_variation",
556559
"gen_ai.request.type": "image_variation",
557560
},
558561
});

packages/instrumentation-openai/src/instrumentation.ts

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ import {
3232
ATTR_GEN_AI_REQUEST_TEMPERATURE,
3333
ATTR_GEN_AI_REQUEST_TOP_P,
3434
ATTR_GEN_AI_RESPONSE_MODEL,
35+
ATTR_GEN_AI_RESPONSE_ID,
3536
ATTR_GEN_AI_PROVIDER_NAME,
3637
ATTR_GEN_AI_OPERATION_NAME,
3738
ATTR_GEN_AI_INPUT_MESSAGES,
3839
ATTR_GEN_AI_OUTPUT_MESSAGES,
3940
ATTR_GEN_AI_USAGE_INPUT_TOKENS,
4041
ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
4142
ATTR_GEN_AI_RESPONSE_FINISH_REASONS,
43+
ATTR_GEN_AI_TOOL_DEFINITIONS,
4244
GEN_AI_OPERATION_NAME_VALUE_CHAT,
4345
GEN_AI_OPERATION_NAME_VALUE_TEXT_COMPLETION,
4446
GEN_AI_PROVIDER_NAME_VALUE_OPENAI,
@@ -371,37 +373,32 @@ export class OpenAIInstrumentation extends InstrumentationBase {
371373
attributes[ATTR_GEN_AI_INPUT_MESSAGES] =
372374
JSON.stringify(inputMessages);
373375

374-
// Function/tool definitions (custom attrs — no OTel equivalent)
375-
params.functions?.forEach((func, index) => {
376-
attributes[
377-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.${index}.name`
378-
] = func.name;
379-
attributes[
380-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.${index}.description`
381-
] = func.description;
382-
attributes[
383-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.${index}.arguments`
384-
] = JSON.stringify(func.parameters);
376+
// Tool/function definitions as single JSON attribute (OTel 1.40)
377+
const toolDefs: object[] = [];
378+
params.functions?.forEach((func) => {
379+
toolDefs.push({
380+
name: func.name,
381+
description: func.description,
382+
parameters: func.parameters,
383+
});
385384
});
386-
params.tools?.forEach((tool, index) => {
385+
params.tools?.forEach((tool) => {
387386
if (
388387
tool.type !== "function" ||
389388
!("function" in tool) ||
390389
!tool.function
391390
) {
392391
return;
393392
}
394-
395-
attributes[
396-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.${index}.name`
397-
] = tool.function.name;
398-
attributes[
399-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.${index}.description`
400-
] = tool.function.description;
401-
attributes[
402-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.${index}.arguments`
403-
] = JSON.stringify(tool.function.parameters);
393+
toolDefs.push({
394+
name: tool.function.name,
395+
description: tool.function.description,
396+
parameters: tool.function.parameters,
397+
});
404398
});
399+
if (toolDefs.length > 0) {
400+
attributes[ATTR_GEN_AI_TOOL_DEFINITIONS] = JSON.stringify(toolDefs);
401+
}
405402
} else {
406403
attributes[ATTR_GEN_AI_INPUT_MESSAGES] =
407404
formatInputMessagesFromPrompt(
@@ -449,7 +446,7 @@ export class OpenAIInstrumentation extends InstrumentationBase {
449446
{
450447
index: 0,
451448
logprobs: null,
452-
finish_reason: "stop",
449+
finish_reason: null as any,
453450
message: {
454451
role: "assistant",
455452
content: "",
@@ -561,7 +558,7 @@ export class OpenAIInstrumentation extends InstrumentationBase {
561558
{
562559
index: 0,
563560
logprobs: null,
564-
finish_reason: "stop",
561+
finish_reason: null as any,
565562
text: "",
566563
},
567564
],
@@ -689,6 +686,9 @@ export class OpenAIInstrumentation extends InstrumentationBase {
689686
}) {
690687
try {
691688
span.setAttribute(ATTR_GEN_AI_RESPONSE_MODEL, result.model);
689+
if (result.id) {
690+
span.setAttribute(ATTR_GEN_AI_RESPONSE_ID, result.id);
691+
}
692692
if (result.usage) {
693693
span.setAttribute(
694694
SpanAttributes.GEN_AI_USAGE_TOTAL_TOKENS,
@@ -865,10 +865,18 @@ export class OpenAIInstrumentation extends InstrumentationBase {
865865
return { provider: GEN_AI_PROVIDER_NAME_VALUE_AWS_BEDROCK };
866866
}
867867

868-
if (baseURL.includes("googleapis.com")) {
868+
if (baseURL.includes("aiplatform.googleapis.com")) {
869869
return { provider: GEN_AI_PROVIDER_NAME_VALUE_GCP_VERTEX_AI };
870870
}
871871

872+
if (baseURL.includes("generativelanguage.googleapis.com")) {
873+
return { provider: "gcp.gemini" };
874+
}
875+
876+
if (baseURL.includes("googleapis.com")) {
877+
return { provider: "gcp.gen_ai" }; // fallback for other Google APIs
878+
}
879+
872880
if (baseURL.includes("openrouter")) {
873881
return { provider: "openrouter" };
874882
}

packages/instrumentation-openai/src/message-helpers.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ interface OTelChatMessage {
2424

2525
interface OTelOutputMessage {
2626
role: string;
27-
finish_reason: string;
27+
finish_reason?: string;
2828
parts: object[];
2929
}
3030

@@ -175,7 +175,7 @@ export function buildOpenAIInputMessages(messages: any[]): OTelChatMessage[] {
175175
parts: [
176176
{
177177
type: "tool_call_response",
178-
name: msg.name,
178+
id: msg.name,
179179
response: msg.content,
180180
},
181181
],
@@ -272,15 +272,17 @@ export function buildOpenAIOutputMessage(
272272
parts.push({
273273
type: "blob",
274274
modality: "audio",
275+
mime_type: "audio/mp3",
275276
content: message.audio.data,
276277
});
277278
}
278279

279-
const finishReason = choice.finish_reason
280-
? (finishReasonMap[choice.finish_reason] ?? choice.finish_reason)
281-
: FinishReasons.STOP;
282-
283-
return [{ role: "assistant", finish_reason: finishReason, parts }];
280+
const outputMsg: OTelOutputMessage = { role: "assistant", parts };
281+
if (choice.finish_reason) {
282+
outputMsg.finish_reason =
283+
finishReasonMap[choice.finish_reason] ?? choice.finish_reason;
284+
}
285+
return [outputMsg];
284286
}
285287

286288
/**
@@ -294,17 +296,15 @@ export function buildOpenAICompletionOutputMessage(
294296
choice: any,
295297
finishReasonMap: Record<string, string>,
296298
): OTelOutputMessage[] {
297-
const finishReason = choice.finish_reason
298-
? (finishReasonMap[choice.finish_reason] ?? choice.finish_reason)
299-
: FinishReasons.STOP;
300-
301-
return [
302-
{
303-
role: "assistant",
304-
finish_reason: finishReason,
305-
parts: [{ type: "text", content: choice.text ?? "" }],
306-
},
307-
];
299+
const outputMsg: OTelOutputMessage = {
300+
role: "assistant",
301+
parts: [{ type: "text", content: choice.text ?? "" }],
302+
};
303+
if (choice.finish_reason) {
304+
outputMsg.finish_reason =
305+
finishReasonMap[choice.finish_reason] ?? choice.finish_reason;
306+
}
307+
return [outputMsg];
308308
}
309309

310310
// =============================================================================

packages/instrumentation-openai/test/instrumentation.test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ describe("Test OpenAI instrumentation", async function () {
176176
"Tell me a joke about OpenTelemetry",
177177
);
178178

179+
// Finish reasons
180+
assert.deepEqual(
181+
completionSpan.attributes[ATTR_GEN_AI_RESPONSE_FINISH_REASONS],
182+
["stop"],
183+
);
184+
179185
assert.ok(
180186
completionSpan.attributes[SpanAttributes.GEN_AI_USAGE_TOTAL_TOKENS],
181187
);
@@ -376,17 +382,13 @@ describe("Test OpenAI instrumentation", async function () {
376382
"What's the weather like in Boston?",
377383
);
378384

379-
// Function definitions (custom attrs)
380-
assert.strictEqual(
381-
completionSpan.attributes[
382-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.0.name`
383-
],
384-
"get_current_weather",
385+
// Tool definitions (OTel 1.40 gen_ai.tool.definitions)
386+
const toolDefs = JSON.parse(
387+
completionSpan.attributes["gen_ai.tool.definitions"] as string,
385388
);
389+
assert.strictEqual(toolDefs[0].name, "get_current_weather");
386390
assert.strictEqual(
387-
completionSpan.attributes[
388-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.0.description`
389-
],
391+
toolDefs[0].description,
390392
"Get the current weather in a given location",
391393
);
392394

@@ -462,13 +464,11 @@ describe("Test OpenAI instrumentation", async function () {
462464
"What's the weather like in Boston?",
463465
);
464466

465-
// Function definitions (custom attrs)
466-
assert.strictEqual(
467-
completionSpan.attributes[
468-
`${SpanAttributes.LLM_REQUEST_FUNCTIONS}.0.name`
469-
],
470-
"get_current_weather",
467+
// Tool definitions (OTel 1.40)
468+
const toolDefs = JSON.parse(
469+
completionSpan.attributes["gen_ai.tool.definitions"] as string,
471470
);
471+
assert.strictEqual(toolDefs[0].name, "get_current_weather");
472472

473473
// Output messages with tool calls
474474
const outputMessages = JSON.parse(
@@ -598,7 +598,7 @@ describe("Test OpenAI instrumentation", async function () {
598598
"openai",
599599
);
600600
assert.strictEqual(
601-
imageSpan.attributes["gen_ai.request.type"],
601+
imageSpan.attributes[ATTR_GEN_AI_OPERATION_NAME],
602602
"image_generation",
603603
);
604604
assert.strictEqual(

packages/instrumentation-utils/src/content-block-mappers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,11 @@ export function mapOpenAIContentBlock(block: any): object {
225225
};
226226
}
227227
if (block.file?.file_data) {
228+
// Inline file data → BlobPart. OTel FilePart is a reference type (file_id only).
228229
return {
229-
type: "file",
230+
type: "blob",
231+
mime_type: block.file.mime_type || "application/octet-stream",
230232
content: block.file.file_data,
231-
...(block.file.filename && { filename: block.file.filename }),
232233
};
233234
}
234235
return { type: block.type, ...block };

0 commit comments

Comments
 (0)