@ai-sdk/open-responses
Version:
The **[Open Responses provider](https://ai-sdk.dev/providers/ai-sdk-providers/open-responses)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for [Open Responses](https://www.openresponses.org/) compatible APIs.
212 lines (196 loc) • 6.59 kB
text/typescript
import type { LanguageModelV3Prompt, SharedV3Warning } from '@ai-sdk/provider';
import { convertToBase64 } from '@ai-sdk/provider-utils';
import type {
FunctionCallItemParam,
FunctionCallOutputItemParam,
InputFileContentParam,
InputImageContentParam,
InputTextContentParam,
OpenResponsesRequestBody,
OutputTextContentParam,
RefusalContentParam,
} from './open-responses-api';
export async function convertToOpenResponsesInput({
prompt,
}: {
prompt: LanguageModelV3Prompt;
}): Promise<{
input: OpenResponsesRequestBody['input'];
instructions: string | undefined;
warnings: Array<SharedV3Warning>;
}> {
const input: OpenResponsesRequestBody['input'] = [];
const warnings: Array<SharedV3Warning> = [];
const systemMessages: string[] = [];
for (const { role, content } of prompt) {
switch (role) {
case 'system': {
systemMessages.push(content);
break;
}
case 'user': {
const userContent: Array<
InputTextContentParam | InputImageContentParam | InputFileContentParam
> = [];
for (const part of content) {
switch (part.type) {
case 'text': {
userContent.push({ type: 'input_text', text: part.text });
break;
}
case 'file': {
const mediaType =
part.mediaType === 'image/*' ? 'image/jpeg' : part.mediaType;
if (part.mediaType.startsWith('image/')) {
userContent.push({
type: 'input_image',
...(part.data instanceof URL
? { image_url: part.data.toString() }
: {
image_url: `data:${mediaType};base64,${convertToBase64(part.data)}`,
}),
});
} else if (part.data instanceof URL) {
userContent.push({
type: 'input_file',
file_url: part.data.toString(),
});
} else {
userContent.push({
type: 'input_file',
filename: part.filename ?? 'data',
file_data: `data:${mediaType};base64,${convertToBase64(part.data)}`,
});
}
break;
}
}
}
input.push({ type: 'message', role: 'user', content: userContent });
break;
}
case 'assistant': {
const assistantContent: Array<
OutputTextContentParam | RefusalContentParam
> = [];
const toolCalls: Array<FunctionCallItemParam> = [];
for (const part of content) {
switch (part.type) {
case 'text': {
assistantContent.push({ type: 'output_text', text: part.text });
break;
}
case 'tool-call': {
const argumentsValue =
typeof part.input === 'string'
? part.input
: JSON.stringify(part.input);
toolCalls.push({
type: 'function_call',
call_id: part.toolCallId,
name: part.toolName,
arguments: argumentsValue,
});
break;
}
}
}
// Push assistant message with text content if any
if (assistantContent.length > 0) {
input.push({
type: 'message',
role: 'assistant',
content: assistantContent,
});
}
// Push function calls as separate items
for (const toolCall of toolCalls) {
input.push(toolCall);
}
break;
}
case 'tool': {
for (const part of content) {
if (part.type === 'tool-result') {
const output = part.output;
let contentValue: FunctionCallOutputItemParam['output'];
switch (output.type) {
case 'text':
case 'error-text':
contentValue = output.value;
break;
case 'execution-denied':
contentValue = output.reason ?? 'Tool execution denied.';
break;
case 'json':
case 'error-json':
contentValue = JSON.stringify(output.value);
break;
case 'content': {
const contentParts: Array<
| InputTextContentParam
| InputImageContentParam
| InputFileContentParam
> = [];
for (const item of output.value) {
switch (item.type) {
case 'text': {
contentParts.push({
type: 'input_text',
text: item.text,
});
break;
}
case 'image-data': {
contentParts.push({
type: 'input_image',
image_url: `data:${item.mediaType};base64,${item.data}`,
});
break;
}
case 'image-url': {
contentParts.push({
type: 'input_image',
image_url: item.url,
});
break;
}
case 'file-data': {
contentParts.push({
type: 'input_file',
filename: item.filename ?? 'data',
file_data: `data:${item.mediaType};base64,${item.data}`,
});
break;
}
default: {
warnings.push({
type: 'other',
message: `unsupported tool content part type: ${(item as { type: string }).type}`,
});
break;
}
}
}
contentValue = contentParts;
break;
}
}
input.push({
type: 'function_call_output',
call_id: part.toolCallId,
output: contentValue,
});
}
}
break;
}
}
}
return {
input,
instructions:
systemMessages.length > 0 ? systemMessages.join('\n') : undefined,
warnings,
};
}