@arizeai/openinference-instrumentation-openai 4.4.4 → 4.4.5
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/esm/instrumentation.d.ts.map +1 -1
- package/dist/esm/instrumentation.js +84 -29
- package/dist/esm/instrumentation.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esnext/instrumentation.d.ts.map +1 -1
- package/dist/esnext/instrumentation.js +94 -38
- package/dist/esnext/instrumentation.js.map +1 -1
- package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -1
- package/dist/esnext/version.d.ts +1 -1
- package/dist/esnext/version.js +1 -1
- package/dist/src/instrumentation.d.ts.map +1 -1
- package/dist/src/instrumentation.js +94 -38
- package/dist/src/instrumentation.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/instrumentation.ts +91 -30
- package/src/version.ts +1 -1
package/package.json
CHANGED
package/src/instrumentation.ts
CHANGED
|
@@ -572,8 +572,17 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
572
572
|
// First split the stream via tee
|
|
573
573
|
const [leftStream, rightStream] = result.tee();
|
|
574
574
|
// take the right stream, consuming it and then recording the final chunk
|
|
575
|
-
// into the span
|
|
576
|
-
|
|
575
|
+
// into the span. If reading it fails, the caller gets the same error from
|
|
576
|
+
// the left stream and the span ends with status ERROR.
|
|
577
|
+
void consumeResponseStreamEvents(rightStream).then((response) => {
|
|
578
|
+
// An aborted request ends the stream without an error or a final
|
|
579
|
+
// response, so leave the status unset
|
|
580
|
+
if (!response && rightStream.controller.signal.aborted) {
|
|
581
|
+
span.end();
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
recordSpan(response);
|
|
585
|
+
}, recordError);
|
|
577
586
|
// give the left stream back to the caller
|
|
578
587
|
result = leftStream;
|
|
579
588
|
}
|
|
@@ -1001,7 +1010,10 @@ function getEmbeddingUsageAttributes(response: CreateEmbeddingResponse): Attribu
|
|
|
1001
1010
|
}
|
|
1002
1011
|
|
|
1003
1012
|
/**
|
|
1004
|
-
* Consumes the stream chunks and adds them to the span
|
|
1013
|
+
* Consumes the stream chunks and adds them to the span.
|
|
1014
|
+
* The span ends with status OK when the stream is read to the end, with status ERROR when
|
|
1015
|
+
* reading it fails, and with the status unset when the request is aborted. In every case it
|
|
1016
|
+
* keeps what was streamed so far.
|
|
1005
1017
|
*/
|
|
1006
1018
|
async function consumeChatCompletionStreamChunks(stream: Stream<ChatCompletionChunk>, span: Span) {
|
|
1007
1019
|
let streamResponse = "";
|
|
@@ -1010,34 +1022,44 @@ async function consumeChatCompletionStreamChunks(stream: Stream<ChatCompletionCh
|
|
|
1010
1022
|
// So the final tool and function calls need to be aggregated
|
|
1011
1023
|
// across chunks
|
|
1012
1024
|
const toolAndFunctionCallAttributes: Attributes = {};
|
|
1025
|
+
const getToolCallIndex = createStreamToolCallIndexResolver();
|
|
1013
1026
|
let finishReason: string | undefined;
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1027
|
+
let streamFailed = false;
|
|
1028
|
+
let streamError: unknown;
|
|
1029
|
+
try {
|
|
1030
|
+
for await (const chunk of stream) {
|
|
1031
|
+
// With `stream_options.include_usage`, the final chunk carries the request's
|
|
1032
|
+
// usage and an empty `choices` array, so read it before the guard below.
|
|
1033
|
+
if (chunk.usage) {
|
|
1034
|
+
span.setAttributes(getUsageAttributes(chunk));
|
|
1035
|
+
}
|
|
1036
|
+
if (chunk.choices.length <= 0) {
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
const choice = chunk.choices[0];
|
|
1040
|
+
if (choice.delta.content) {
|
|
1041
|
+
streamResponse += choice.delta.content;
|
|
1042
|
+
}
|
|
1043
|
+
if (choice.finish_reason) {
|
|
1044
|
+
finishReason = choice.finish_reason;
|
|
1045
|
+
}
|
|
1046
|
+
// Accumulate the tool and function call attributes
|
|
1047
|
+
const toolAndFunctionCallAttributesDiff = getToolAndFunctionCallAttributesFromStreamChunk(
|
|
1048
|
+
chunk,
|
|
1049
|
+
getToolCallIndex,
|
|
1050
|
+
);
|
|
1051
|
+
for (const [key, value] of Object.entries(toolAndFunctionCallAttributesDiff)) {
|
|
1052
|
+
if (isString(toolAndFunctionCallAttributes[key]) && isString(value)) {
|
|
1053
|
+
toolAndFunctionCallAttributes[key] += value;
|
|
1054
|
+
} else if (isString(value)) {
|
|
1055
|
+
toolAndFunctionCallAttributes[key] = value;
|
|
1056
|
+
}
|
|
1039
1057
|
}
|
|
1040
1058
|
}
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
// The caller reads the other branch of the tee and gets the same error
|
|
1061
|
+
streamFailed = true;
|
|
1062
|
+
streamError = error;
|
|
1041
1063
|
}
|
|
1042
1064
|
const messageIndexPrefix = `${SemanticConventions.LLM_OUTPUT_MESSAGES}.0.`;
|
|
1043
1065
|
|
|
@@ -1056,20 +1078,59 @@ async function consumeChatCompletionStreamChunks(stream: Stream<ChatCompletionCh
|
|
|
1056
1078
|
attributes[`${messageIndexPrefix}${key}`] = value;
|
|
1057
1079
|
}
|
|
1058
1080
|
span.setAttributes(attributes);
|
|
1081
|
+
if (streamFailed) {
|
|
1082
|
+
endSpanWithError(span, streamError);
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
// An aborted request ends the stream without an error, but did not run to completion
|
|
1086
|
+
if (!stream.controller.signal.aborted) {
|
|
1087
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1088
|
+
}
|
|
1059
1089
|
span.end();
|
|
1060
1090
|
}
|
|
1061
1091
|
|
|
1092
|
+
/**
|
|
1093
|
+
* Returns a function that gives the position of a streamed tool call delta within the message.
|
|
1094
|
+
* OpenAI sends an index with every delta. Some OpenAI-compatible providers (such as Gemini)
|
|
1095
|
+
* leave it out, so a delta with a new id, or a function name without an id, starts the next
|
|
1096
|
+
* tool call and any other delta continues the latest one.
|
|
1097
|
+
*/
|
|
1098
|
+
function createStreamToolCallIndexResolver() {
|
|
1099
|
+
let latestIndex = -1;
|
|
1100
|
+
let latestId: string | undefined;
|
|
1101
|
+
let nextIndex = 0;
|
|
1102
|
+
return (toolCall: ChatCompletionChunk.Choice.Delta.ToolCall): number => {
|
|
1103
|
+
// The SDK types the index as required, but some providers omit it
|
|
1104
|
+
if (typeof toolCall.index === "number") {
|
|
1105
|
+
latestIndex = toolCall.index;
|
|
1106
|
+
nextIndex = Math.max(nextIndex, toolCall.index + 1);
|
|
1107
|
+
return toolCall.index;
|
|
1108
|
+
}
|
|
1109
|
+
const startsNewToolCall =
|
|
1110
|
+
latestIndex < 0 || (toolCall.id ? toolCall.id !== latestId : toolCall.function?.name != null);
|
|
1111
|
+
if (startsNewToolCall) {
|
|
1112
|
+
latestIndex = nextIndex++;
|
|
1113
|
+
latestId = toolCall.id;
|
|
1114
|
+
}
|
|
1115
|
+
return latestIndex;
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1062
1119
|
/**
|
|
1063
1120
|
* Extracts the semantic attributes from the stream chunk for tool_calls and function_calls
|
|
1064
1121
|
*/
|
|
1065
|
-
function getToolAndFunctionCallAttributesFromStreamChunk(
|
|
1122
|
+
function getToolAndFunctionCallAttributesFromStreamChunk(
|
|
1123
|
+
chunk: ChatCompletionChunk,
|
|
1124
|
+
getToolCallIndex: (toolCall: ChatCompletionChunk.Choice.Delta.ToolCall) => number,
|
|
1125
|
+
): Attributes {
|
|
1066
1126
|
if (chunk.choices.length <= 0) {
|
|
1067
1127
|
return {};
|
|
1068
1128
|
}
|
|
1069
1129
|
const choice = chunk.choices[0];
|
|
1070
1130
|
const attributes: Attributes = {};
|
|
1071
1131
|
if (choice.delta.tool_calls) {
|
|
1072
|
-
choice.delta.tool_calls.forEach((toolCall
|
|
1132
|
+
choice.delta.tool_calls.forEach((toolCall) => {
|
|
1133
|
+
const index = getToolCallIndex(toolCall);
|
|
1073
1134
|
const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
|
|
1074
1135
|
// Add the tool call id if it exists
|
|
1075
1136
|
if (toolCall.id) {
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// this is an auto-generated file. see js/scripts/version-update.js
|
|
2
|
-
export const VERSION = "4.4.
|
|
2
|
+
export const VERSION = "4.4.5";
|