UNPKG

@ai-sdk/deepinfra

Version:

The **[DeepInfra provider](https://ai-sdk.dev/providers/ai-sdk-providers/deepinfra)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for the DeepInfra API, giving you access to models like Llama 3, Mixtral, and other state-of-th

195 lines (176 loc) 5.55 kB
import { ImageModelV3, ImageModelV3File, SharedV3Warning, } from '@ai-sdk/provider'; import { combineHeaders, convertBase64ToUint8Array, convertToFormData, createJsonErrorResponseHandler, createJsonResponseHandler, downloadBlob, FetchFunction, postFormDataToApi, postJsonToApi, } from '@ai-sdk/provider-utils'; import { DeepInfraImageModelId } from './deepinfra-image-settings'; import { z } from 'zod/v4'; interface DeepInfraImageModelConfig { provider: string; baseURL: string; headers: () => Record<string, string>; fetch?: FetchFunction; _internal?: { currentDate?: () => Date; }; } export class DeepInfraImageModel implements ImageModelV3 { readonly specificationVersion = 'v3'; readonly maxImagesPerCall = 1; get provider(): string { return this.config.provider; } constructor( readonly modelId: DeepInfraImageModelId, private config: DeepInfraImageModelConfig, ) {} async doGenerate({ prompt, n, size, aspectRatio, seed, providerOptions, headers, abortSignal, files, mask, }: Parameters<ImageModelV3['doGenerate']>[0]): Promise< Awaited<ReturnType<ImageModelV3['doGenerate']>> > { const warnings: Array<SharedV3Warning> = []; const currentDate = this.config._internal?.currentDate?.() ?? new Date(); // Image editing mode - use OpenAI-compatible /images/edits endpoint if (files != null && files.length > 0) { const { value: response, responseHeaders } = await postFormDataToApi({ url: this.getEditUrl(), headers: combineHeaders(this.config.headers(), headers), formData: convertToFormData<DeepInfraFormDataInput>( { model: this.modelId, prompt, image: await Promise.all(files.map(file => fileToBlob(file))), mask: mask != null ? await fileToBlob(mask) : undefined, n, size, ...(providerOptions.deepinfra ?? {}), }, { useArrayBrackets: false }, ), failedResponseHandler: createJsonErrorResponseHandler({ errorSchema: deepInfraEditErrorSchema, errorToMessage: error => error.error?.message ?? 'Unknown error', }), successfulResponseHandler: createJsonResponseHandler( deepInfraEditResponseSchema, ), abortSignal, fetch: this.config.fetch, }); return { images: response.data.map(item => item.b64_json), warnings, response: { timestamp: currentDate, modelId: this.modelId, headers: responseHeaders, }, }; } // Standard image generation mode // Some deepinfra models support size while others support aspect ratio. // Allow passing either and leave it up to the server to validate. const splitSize = size?.split('x'); const { value: response, responseHeaders } = await postJsonToApi({ url: `${this.config.baseURL}/${this.modelId}`, headers: combineHeaders(this.config.headers(), headers), body: { prompt, num_images: n, ...(aspectRatio && { aspect_ratio: aspectRatio }), ...(splitSize && { width: splitSize[0], height: splitSize[1] }), ...(seed != null && { seed }), ...(providerOptions.deepinfra ?? {}), }, failedResponseHandler: createJsonErrorResponseHandler({ errorSchema: deepInfraErrorSchema, errorToMessage: error => error.detail.error, }), successfulResponseHandler: createJsonResponseHandler( deepInfraImageResponseSchema, ), abortSignal, fetch: this.config.fetch, }); return { images: response.images.map(image => image.replace(/^data:image\/\w+;base64,/, ''), ), warnings, response: { timestamp: currentDate, modelId: this.modelId, headers: responseHeaders, }, }; } private getEditUrl(): string { // Use OpenAI-compatible endpoint for image editing // baseURL is typically https://api.deepinfra.com/v1/inference // We need to use https://api.deepinfra.com/v1/openai/images/edits const baseUrl = this.config.baseURL.replace('/inference', '/openai'); return `${baseUrl}/images/edits`; } } export const deepInfraErrorSchema = z.object({ detail: z.object({ error: z.string(), }), }); // limited version of the schema, focussed on what is needed for the implementation // this approach limits breakages when the API changes and increases efficiency export const deepInfraImageResponseSchema = z.object({ images: z.array(z.string()), }); // Schema for OpenAI-compatible image edit endpoint errors export const deepInfraEditErrorSchema = z.object({ error: z .object({ message: z.string(), }) .optional(), }); // Schema for OpenAI-compatible image edit endpoint response export const deepInfraEditResponseSchema = z.object({ data: z.array(z.object({ b64_json: z.string() })), }); type DeepInfraFormDataInput = { model: string; prompt: string | undefined; image: Blob | Blob[]; mask?: Blob; n: number; size: `${number}x${number}` | undefined; [key: string]: unknown; }; async function fileToBlob(file: ImageModelV3File): Promise<Blob> { if (file.type === 'url') { return downloadBlob(file.url); } const data = file.data instanceof Uint8Array ? file.data : convertBase64ToUint8Array(file.data); return new Blob([data as BlobPart], { type: file.mediaType }); }