From 4d45e8eb759d2b47c6c84b6aebcdd92e36e746fb Mon Sep 17 00:00:00 2001 From: Anoyi <545544032@qq.com> Date: Fri, 10 May 2024 11:45:25 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20Azure=20OpenAI=20vision?= =?UTF-8?q?=20models=20issue=20(https://github.com/lobehub/lobe-chat/issue?= =?UTF-8?q?s/2207)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent-runtime/azureOpenai/index.test.ts | 53 +++++++++++++++++++ src/libs/agent-runtime/azureOpenai/index.ts | 43 ++++++++++----- 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/src/libs/agent-runtime/azureOpenai/index.test.ts b/src/libs/agent-runtime/azureOpenai/index.test.ts index 06e0ba55b1e..8920316ce39 100644 --- a/src/libs/agent-runtime/azureOpenai/index.test.ts +++ b/src/libs/agent-runtime/azureOpenai/index.test.ts @@ -163,4 +163,57 @@ describe('LobeAzureOpenAI', () => { }); }); }); + + describe('private method', () => { + + describe('tocamelCase', () => { + it('should convert string to camel case', () => { + const key = 'image_url'; + + const camelCaseKey = instance['tocamelCase'](key); + + expect(camelCaseKey).toEqual('imageUrl'); + }); + }); + + describe('camelCaseKeys', () => { + it('should convert object keys to camel case', () => { + const obj = { + "frequency_penalty": 0, + "messages": [ + { + "role": "user", + "content": [ + { + "type": "image_url", + "image_url": { + "url": "" + } + } + ] + } + ] + }; + + const newObj = instance['camelCaseKeys'](obj); + + expect(newObj).toEqual({ + "frequencyPenalty": 0, + "messages": [ + { + "role": "user", + "content": [ + { + "type": "image_url", + "imageUrl": { + "url": "" + } + } + ] + } + ] + }); + }); + }); + }) }); diff --git a/src/libs/agent-runtime/azureOpenai/index.ts b/src/libs/agent-runtime/azureOpenai/index.ts index b567b50b23a..590daea7b42 100644 --- a/src/libs/agent-runtime/azureOpenai/index.ts +++ b/src/libs/agent-runtime/azureOpenai/index.ts @@ -28,19 +28,8 @@ export class LobeAzureOpenAI implements LobeRuntimeAI { async chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions) { // ============ 1. preprocess messages ============ // - const { messages, model, ...params } = payload; - - // fix the issue: "The sample encountered an error: TypeError: Cannot read properties of undefined (reading 'url')" - messages.forEach(message => { - if (Array.isArray(message['content'])) { - message['content'].forEach((content: any) => { - if (content['type'] === 'image_url') { - content['imageUrl'] = content['image_url']; - delete content['image_url']; - } - }); - } - }); + const camelCasePayload = this.camelCaseKeys(payload); + const { messages, model, maxTokens = 2048, ...params } = camelCasePayload; // ============ 2. send api ============ // @@ -48,7 +37,7 @@ export class LobeAzureOpenAI implements LobeRuntimeAI { const response = await this.client.streamChatCompletions( model, messages as ChatRequestMessage[], - { ...params, abortSignal: options?.signal } as GetChatCompletionsOptions, + { ...params, maxTokens, abortSignal: options?.signal } as GetChatCompletionsOptions, ); const stream = OpenAIStream(response as any); @@ -89,4 +78,30 @@ export class LobeAzureOpenAI implements LobeRuntimeAI { }); } } + + // Convert object keys to camel case, copy from `@azure/openai` in `node_modules/@azure/openai/dist/index.cjs` + private camelCaseKeys = (obj: any): any => { + if (typeof obj !== "object" || !obj) + return obj; + if (Array.isArray(obj)) { + return obj.map((v) => this.camelCaseKeys(v)); + } + else { + for (const key of Object.keys(obj)) { + const value = obj[key]; + const newKey = this.tocamelCase(key); + if (newKey !== key) { + delete obj[key]; + } + obj[newKey] = typeof obj[newKey] === "object" ? this.camelCaseKeys(value) : value; + } + return obj; + } + } + + private tocamelCase = (str: string) => { + return str + .toLowerCase() + .replace(/([_][a-z])/g, (group) => group.toUpperCase().replace("_", "")); + } }