Skip to content

Commit 546fd9e

Browse files
authored
fix(openai): structured output promise unwrapping exception (#474)
1 parent 59bc9e6 commit 546fd9e

File tree

4 files changed

+95
-74
lines changed

4 files changed

+95
-74
lines changed

package-lock.json

+10-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/instrumentation-openai/src/instrumentation.ts

+45-65
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,7 @@
1414
* limitations under the License.
1515
*/
1616
import type * as openai from "openai";
17-
import {
18-
context,
19-
trace,
20-
Span,
21-
Attributes,
22-
SpanKind,
23-
SpanStatusCode,
24-
} from "@opentelemetry/api";
17+
import { context, trace, Span, Attributes, SpanKind } from "@opentelemetry/api";
2518
import {
2619
InstrumentationBase,
2720
InstrumentationModuleDefinition,
@@ -46,6 +39,7 @@ import type {
4639
import type { Stream } from "openai/streaming";
4740
import { version } from "../package.json";
4841
import { encodingForModel, TiktokenModel, Tiktoken } from "js-tiktoken";
42+
import { APIPromise } from "openai/core";
4943

5044
export class OpenAIInstrumentation extends InstrumentationBase {
5145
protected declare _config: OpenAIInstrumentationConfig;
@@ -338,13 +332,13 @@ export class OpenAIInstrumentation extends InstrumentationBase {
338332
span: Span;
339333
type: "chat";
340334
params: ChatCompletionCreateParamsStreaming;
341-
promise: Promise<Stream<ChatCompletionChunk>>;
335+
promise: APIPromise<Stream<ChatCompletionChunk>>;
342336
}
343337
| {
344338
span: Span;
345339
params: CompletionCreateParamsStreaming;
346340
type: "completion";
347-
promise: Promise<Stream<Completion>>;
341+
promise: APIPromise<Stream<Completion>>;
348342
}) {
349343
if (type === "chat") {
350344
const result: ChatCompletion = {
@@ -532,63 +526,49 @@ export class OpenAIInstrumentation extends InstrumentationBase {
532526
type: "chat" | "completion",
533527
version: "v3" | "v4",
534528
span: Span,
535-
promise: Promise<T>,
536-
): Promise<T> {
537-
return promise
538-
.then((result) => {
539-
return new Promise<T>((resolve) => {
540-
if (version === "v3") {
541-
if (type === "chat") {
542-
this._addLogProbsEvent(
543-
span,
544-
((result as any).data as ChatCompletion).choices[0].logprobs,
545-
);
546-
this._endSpan({
547-
type,
548-
span,
549-
result: (result as any).data as ChatCompletion,
550-
});
551-
} else {
552-
this._addLogProbsEvent(
553-
span,
554-
((result as any).data as Completion).choices[0].logprobs,
555-
);
556-
this._endSpan({
557-
type,
558-
span,
559-
result: (result as any).data as Completion,
560-
});
561-
}
562-
} else {
563-
if (type === "chat") {
564-
this._addLogProbsEvent(
565-
span,
566-
(result as ChatCompletion).choices[0].logprobs,
567-
);
568-
this._endSpan({ type, span, result: result as ChatCompletion });
569-
} else {
570-
this._addLogProbsEvent(
571-
span,
572-
(result as Completion).choices[0].logprobs,
573-
);
574-
this._endSpan({ type, span, result: result as Completion });
575-
}
576-
}
577-
resolve(result);
578-
});
579-
})
580-
.catch((error: Error) => {
581-
return new Promise<T>((_, reject) => {
582-
span.setStatus({
583-
code: SpanStatusCode.ERROR,
584-
message: error.message,
529+
promise: APIPromise<T>,
530+
): APIPromise<T> {
531+
return promise._thenUnwrap((result) => {
532+
if (version === "v3") {
533+
if (type === "chat") {
534+
this._addLogProbsEvent(
535+
span,
536+
((result as any).data as ChatCompletion).choices[0].logprobs,
537+
);
538+
this._endSpan({
539+
type,
540+
span,
541+
result: (result as any).data as ChatCompletion,
542+
});
543+
} else {
544+
this._addLogProbsEvent(
545+
span,
546+
((result as any).data as Completion).choices[0].logprobs,
547+
);
548+
this._endSpan({
549+
type,
550+
span,
551+
result: (result as any).data as Completion,
585552
});
586-
span.recordException(error);
587-
span.end();
553+
}
554+
} else {
555+
if (type === "chat") {
556+
this._addLogProbsEvent(
557+
span,
558+
(result as ChatCompletion).choices[0].logprobs,
559+
);
560+
this._endSpan({ type, span, result: result as ChatCompletion });
561+
} else {
562+
this._addLogProbsEvent(
563+
span,
564+
(result as Completion).choices[0].logprobs,
565+
);
566+
this._endSpan({ type, span, result: result as Completion });
567+
}
568+
}
588569

589-
reject(error);
590-
});
591-
});
570+
return result;
571+
});
592572
}
593573

594574
private _endSpan({

packages/sample-app/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"run:llamaindex": "npm run build && node dist/src/sample_llamaindex.js",
2525
"run:pinecone": "npm run build && node dist/src/sample_pinecone.js",
2626
"run:langchain": "npm run build && node dist/src/sample_langchain.js",
27+
"run:sample_structured_output": "npm run build && node dist/src/sample_structured_output.js",
2728
"lint": "eslint . --ext .ts",
2829
"lint:fix": "eslint . --ext .ts --fix"
2930
},
@@ -46,7 +47,8 @@
4647
"cohere-ai": "^7.7.5",
4748
"langchain": "^0.2.12",
4849
"llamaindex": "^0.5.20",
49-
"openai": "^4.57.0"
50+
"openai": "^4.57.0",
51+
"zod": "^3.23.8"
5052
},
5153
"private": true,
5254
"gitHead": "ef1e70d6037f7b5c061056ef2be16e3f55f02ed5"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as traceloop from "@traceloop/node-server-sdk";
2+
import OpenAI from "openai";
3+
import { zodResponseFormat } from "openai/helpers/zod";
4+
import { z } from "zod";
5+
6+
traceloop.initialize({
7+
appName: "sample_same_structured_output",
8+
apiKey: process.env.TRACELOOP_API_KEY,
9+
disableBatch: true,
10+
});
11+
const openai = new OpenAI();
12+
13+
const CalendarEvent = z.object({
14+
name: z.string(),
15+
date: z.string(),
16+
participants: z.array(z.string()),
17+
});
18+
19+
async function create_event() {
20+
const completion = await openai.beta.chat.completions.parse({
21+
model: "gpt-4o",
22+
messages: [
23+
{ role: "system", content: "Extract the event information." },
24+
{
25+
role: "user",
26+
content: "Alice and Bob are going to a science fair on Friday.",
27+
},
28+
],
29+
response_format: zodResponseFormat(CalendarEvent, "event"),
30+
});
31+
32+
console.log(completion.choices[0].message.parsed);
33+
34+
return completion.choices[0].message.parsed;
35+
}
36+
37+
create_event();

0 commit comments

Comments
 (0)