diff --git a/packages/ai-jsx/package.json b/packages/ai-jsx/package.json index 718bb56da..af4070c28 100644 --- a/packages/ai-jsx/package.json +++ b/packages/ai-jsx/package.json @@ -4,7 +4,7 @@ "repository": "fixie-ai/ai-jsx", "bugs": "https://github.com/fixie-ai/ai-jsx/issues", "homepage": "https://ai-jsx.com", - "version": "0.28.1", + "version": "0.28.2", "volta": { "extends": "../../package.json" }, diff --git a/packages/ai-jsx/src/lib/openai.tsx b/packages/ai-jsx/src/lib/openai.tsx index 48871a5ca..ccf7131ba 100644 --- a/packages/ai-jsx/src/lib/openai.tsx +++ b/packages/ai-jsx/src/lib/openai.tsx @@ -648,9 +648,19 @@ export async function* OpenAIChatModel( argsJson += toolCall.function.arguments; } - yield ( - - ); + let partialArgs: Record | undefined = undefined; + try { + partialArgs = JSON.parse(patchedUntruncateJson(argsJson || '{}')); + } catch (e: any) { + // If the JSON is incomplete and we get an error, we can ignore it. + const acceptedErrorPattern = /Unexpected .* JSON/; + if (!acceptedErrorPattern.test(e.message)) { + throw e; + } + } + if (partialArgs !== undefined) { + yield ; + } delta = await advance(); } diff --git a/packages/ai-jsx/src/lib/util.ts b/packages/ai-jsx/src/lib/util.ts index f383cd99a..056b2b302 100644 --- a/packages/ai-jsx/src/lib/util.ts +++ b/packages/ai-jsx/src/lib/util.ts @@ -36,4 +36,9 @@ export function getEnvVar(name: string, shouldThrow: boolean = true) { * There's an ESM issue with untruncate-json, so we need to do this to support running on both client & server. */ /** @hidden */ -export const patchedUntruncateJson = 'default' in untruncateJson ? untruncateJson.default : untruncateJson; +const _patchedUntruncateJson = 'default' in untruncateJson ? untruncateJson.default : untruncateJson; + +export function patchedUntruncateJson(str: string) { + // Remove partial unicode characters: e.g. "\\u5728\\u5fA" -> "\\u5728" + return _patchedUntruncateJson(str.replace(/\\u[\dA-F]{0,3}$/gi, '')); +} diff --git a/packages/ai-jsx/test/lib/util.test.ts b/packages/ai-jsx/test/lib/util.test.ts index b78f71eb1..9c104c7fb 100644 --- a/packages/ai-jsx/test/lib/util.test.ts +++ b/packages/ai-jsx/test/lib/util.test.ts @@ -1,4 +1,4 @@ -import { getEnvVar } from '../../dist/cjs/lib/util.cjs'; +import { getEnvVar, patchedUntruncateJson } from '../../dist/cjs/lib/util.cjs'; process.env.EXISTS = 'exists'; process.env.REACT_APP_ONLY = 'react-value'; @@ -42,3 +42,20 @@ test('env is not defined', () => { globalThis.process.env = originalEnv; }); + +test('Basic untrucation of JSON', () => { + expect(patchedUntruncateJson('{"a":')).toEqual('{}'); + expect(patchedUntruncateJson('{"a":"b')).toEqual('{"a":"b"}'); + expect(patchedUntruncateJson('{"a":"b"')).toEqual('{"a":"b"}'); +}); + +test('Partial unicode characters are removed', () => { + expect(patchedUntruncateJson('{"a":"\\u5728\\u5fA')).toEqual('{"a":"\\u5728"}'); + expect(patchedUntruncateJson('"\\u5728\\u')).toEqual('"\\u5728"'); + expect(patchedUntruncateJson('"\\u5728\\u0')).toEqual('"\\u5728"'); + expect(patchedUntruncateJson('"\\u5728\\u5fA')).toEqual('"\\u5728"'); +}); + +test('Unicode characters are allowed', () => { + expect(patchedUntruncateJson('{"a":"\\u5728什么是"}')).toEqual('{"a":"\\u5728什么是"}'); +}); diff --git a/packages/docs/docs/changelog.md b/packages/docs/docs/changelog.md index e09495104..2511ad966 100644 --- a/packages/docs/docs/changelog.md +++ b/packages/docs/docs/changelog.md @@ -1,6 +1,10 @@ # Changelog -## 0.28.1 +## 0.28.2 + +- Fix bug where partially streamed unicode characters (e.g. Chinese) would cause an error in OpenAI function calls. + +## [0.28.1](https://github.com/fixie-ai/ai-jsx/tree/4c67d845f48585dc3f26e90a9a656471f40c82ed) - Add `openai.finish_reason` span attribute for `OpenAIChatModel`