@ai-sdk/google
Version:
151 lines (133 loc) • 3.29 kB
text/typescript
import type {
LanguageModelV4Prompt,
LanguageModelV4ToolResultOutput,
} from '@ai-sdk/provider';
import {
type FetchFunction,
detectMediaType,
downloadBlob,
isFullMediaType,
isUrlSupported,
} from '@ai-sdk/provider-utils';
/**
* Download tool result file URLs that Vertex cannot reference directly before
* converting the prompt to the Google request format.
*/
export async function downloadToolResultFiles(
prompt: LanguageModelV4Prompt,
{
abortSignal,
fetch,
maxBytes,
supportedUrls = {},
}: {
abortSignal: AbortSignal | undefined;
fetch?: FetchFunction;
maxBytes: number;
supportedUrls?: Record<string, RegExp[]>;
},
): Promise<LanguageModelV4Prompt> {
const result: LanguageModelV4Prompt = [];
for (const message of prompt) {
if (message.role === 'assistant') {
const content: typeof message.content = [];
for (const part of message.content) {
content.push(
part.type === 'tool-result'
? {
...part,
output: await downloadToolResultOutput(part.output, {
abortSignal,
fetch,
maxBytes,
supportedUrls,
}),
}
: part,
);
}
result.push({ ...message, content });
continue;
}
if (message.role === 'tool') {
const content: typeof message.content = [];
for (const part of message.content) {
if (part.type !== 'tool-result') {
content.push(part);
continue;
}
content.push({
...part,
output: await downloadToolResultOutput(part.output, {
abortSignal,
fetch,
maxBytes,
supportedUrls,
}),
});
}
result.push({ ...message, content });
continue;
}
result.push(message);
}
return result;
}
async function downloadToolResultOutput(
output: LanguageModelV4ToolResultOutput,
{
abortSignal,
fetch,
maxBytes,
supportedUrls,
}: {
abortSignal: AbortSignal | undefined;
fetch?: FetchFunction;
maxBytes: number;
supportedUrls: Record<string, RegExp[]>;
},
): Promise<LanguageModelV4ToolResultOutput> {
if (output.type !== 'content') {
return output;
}
const value: typeof output.value = [];
for (const part of output.value) {
if (part.type !== 'file' || part.data.type !== 'url') {
value.push(part);
continue;
}
if (
isUrlSupported({
url: part.data.url.toString(),
mediaType: part.mediaType,
supportedUrls,
})
) {
value.push(part);
continue;
}
const blob = await downloadBlob(part.data.url.toString(), {
abortSignal,
fetch,
maxBytes,
});
const data = new Uint8Array(await blob.arrayBuffer());
const detectedMediaType = detectMediaType({
data,
topLevelType: 'image',
});
value.push({
...part,
data: { type: 'data' as const, data },
mediaType:
detectedMediaType ??
(blob.type && !isFullMediaType(part.mediaType)
? blob.type
: part.mediaType),
});
}
return {
...output,
value,
};
}