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`