Skip to content

Commit 89caf28

Browse files
fix(openai-compat): decode base64 string data (#13006)
## Background reported in issue #12965 we decoded the base64 data properly in the anthropic provider but not in openai-compat ## Summary - added `convertBase64ToUint8Array` to properly decode the string before converting to text - replaced `Buffer.from(data, 'base64').toString('utf-8')` with the edge-runtime-safe equivalent using `convertBase64ToUint8Array` ## Manual Verification na ## Checklist - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Related Issues fixes #12965
1 parent 0084ca4 commit 89caf28

File tree

4 files changed

+48
-6
lines changed

4 files changed

+48
-6
lines changed

.changeset/fifty-beers-scream.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@ai-sdk/openai-compatible': patch
3+
'@ai-sdk/anthropic': patch
4+
---
5+
6+
fix(openai-compat): decode base64 string data

packages/anthropic/src/convert-to-anthropic-messages-prompt.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
UnsupportedFunctionalityError,
88
} from '@ai-sdk/provider';
99
import {
10+
convertBase64ToUint8Array,
1011
convertToBase64,
1112
parseProviderOptions,
1213
validateTypes,
@@ -31,7 +32,7 @@ import { webSearch_20250305OutputSchema } from './tool/web-search_20250305';
3132

3233
function convertToString(data: LanguageModelV3DataContent): string {
3334
if (typeof data === 'string') {
34-
return Buffer.from(data, 'base64').toString('utf-8');
35+
return new TextDecoder().decode(convertBase64ToUint8Array(data));
3536
}
3637

3738
if (data instanceof Uint8Array) {

packages/openai-compatible/src/chat/convert-to-openai-compatible-chat-messages.test.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,15 +295,18 @@ describe('user messages', () => {
295295
).toThrow("'PDF file parts with URLs' functionality not supported");
296296
});
297297

298-
it('should convert messages with text/markdown parts', async () => {
298+
it('should convert messages with base64-encoded text/markdown parts', async () => {
299+
const markdownText = '# Hello World\n\nThis is **markdown** content.';
300+
const base64Data = btoa(markdownText);
301+
299302
const result = convertToOpenAICompatibleChatMessages([
300303
{
301304
role: 'user',
302305
content: [
303306
{ type: 'text', text: 'Summarize this document' },
304307
{
305308
type: 'file',
306-
data: '# Hello World\n\nThis is **markdown** content.',
309+
data: base64Data,
307310
mediaType: 'text/markdown',
308311
},
309312
],
@@ -317,7 +320,7 @@ describe('user messages', () => {
317320
{ type: 'text', text: 'Summarize this document' },
318321
{
319322
type: 'text',
320-
text: '# Hello World\n\nThis is **markdown** content.',
323+
text: markdownText,
321324
},
322325
],
323326
},
@@ -352,6 +355,33 @@ describe('user messages', () => {
352355
]);
353356
});
354357

358+
it('should decode base64 string data for text/* file parts', async () => {
359+
const result = convertToOpenAICompatibleChatMessages([
360+
{
361+
role: 'user',
362+
content: [
363+
{
364+
type: 'file',
365+
data: Buffer.from('Plain text content').toString('base64'),
366+
mediaType: 'text/plain',
367+
},
368+
],
369+
},
370+
]);
371+
372+
expect(result).toEqual([
373+
{
374+
role: 'user',
375+
content: [
376+
{
377+
type: 'text',
378+
text: 'Plain text content',
379+
},
380+
],
381+
},
382+
]);
383+
});
384+
355385
it('should convert text file URL to string', async () => {
356386
const result = convertToOpenAICompatibleChatMessages([
357387
{

packages/openai-compatible/src/chat/convert-to-openai-compatible-chat-messages.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {
44
UnsupportedFunctionalityError,
55
} from '@ai-sdk/provider';
66
import { OpenAICompatibleChatPrompt } from './openai-compatible-api-types';
7-
import { convertToBase64 } from '@ai-sdk/provider-utils';
7+
import {
8+
convertBase64ToUint8Array,
9+
convertToBase64,
10+
} from '@ai-sdk/provider-utils';
811

912
function getOpenAIMetadata(message: {
1013
providerOptions?: SharedV3ProviderMetadata;
@@ -119,7 +122,9 @@ export function convertToOpenAICompatibleChatMessages(
119122
part.data instanceof URL
120123
? part.data.toString()
121124
: typeof part.data === 'string'
122-
? part.data
125+
? new TextDecoder().decode(
126+
convertBase64ToUint8Array(part.data),
127+
)
123128
: new TextDecoder().decode(part.data);
124129

125130
return {

0 commit comments

Comments
 (0)