From 42ca1d0affc137f5b2b45182205f91d4ed7c77a0 Mon Sep 17 00:00:00 2001 From: Yuzhong Zhang Date: Tue, 25 Aug 2026 06:10:30 +0000 Subject: [PATCH] fix: do not crash isWrapped on plain OpenAI-compatible clients --- js/oai.test.ts | 45 +++++++++++++++++++++++++++++++++++++++++++++ js/oai.ts | 4 ++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/js/oai.test.ts b/js/oai.test.ts index 5adf9ba7..751b3733 100644 --- a/js/oai.test.ts +++ b/js/oai.test.ts @@ -357,6 +357,51 @@ describe("OAI", () => { expect(Object.is(builtClient, client)).toBe(true); expect(getDefaultModel()).toBe("gpt-4-turbo"); }); + + const makePlainBridgeClient = () => ({ + chat: { + completions: { + create: async () => MOCK_OPENAI_COMPLETION_RESPONSE, + }, + }, + }); + + test("plain object client works when wrap openai global is unset", async () => { + const originalWrapper = globalThis.__inherited_braintrust_wrap_openai; + globalThis.__inherited_braintrust_wrap_openai = undefined; + try { + const client = makePlainBridgeClient(); + const builtClient = buildOpenAIClient({ client: client as OpenAI }); + const response = await builtClient.chat.completions.create({ + model: "gpt-4", + messages: [{ role: "user", content: "Hello" }], + }); + expect(response.choices[0].message.content).toBe( + "Hello, I am a mock response!", + ); + } finally { + globalThis.__inherited_braintrust_wrap_openai = originalWrapper; + } + }); + + test("plain object client does not throw TypeError when wrap openai global is set", async () => { + const originalWrapper = globalThis.__inherited_braintrust_wrap_openai; + // What `import "braintrust"` does as a side effect. + globalThis.__inherited_braintrust_wrap_openai = (c) => c; + try { + const client = makePlainBridgeClient(); + const builtClient = buildOpenAIClient({ client: client as OpenAI }); + const response = await builtClient.chat.completions.create({ + model: "gpt-4", + messages: [{ role: "user", content: "Hello" }], + }); + expect(response.choices[0].message.content).toBe( + "Hello, I am a mock response!", + ); + } finally { + globalThis.__inherited_braintrust_wrap_openai = originalWrapper; + } + }); }); const withMockWrapper = async ( diff --git a/js/oai.ts b/js/oai.ts index 6da1dee0..56c8bd4f 100644 --- a/js/oai.ts +++ b/js/oai.ts @@ -159,8 +159,8 @@ const isWrapped = ( dangerouslyAllowBrowser, }); return ( - String(client.chat.completions.create) !== - String(clean.chat.completions.create) + String(client.chat?.completions?.create) !== + String(clean.chat?.completions?.create) ); };