UNPKG

@ai-sdk/google-vertex

Version:
644 lines (624 loc) • 18.8 kB
import { getGoogleVertexRequestConfig as requestConfig, type VertexRequestConfig, } from './google-vertex-model-catalog-config'; import type { ModelEntry, ProviderOptionDefinition, ProviderOptionDefinitions, } from '@ai-sdk/provider'; import { getGoogleLanguageModelEntry, googleProviderOptions, } from '@ai-sdk/google/internal'; import type { GoogleVertexEmbeddingModelOptions } from './google-vertex-embedding-model-options'; import type { GoogleVertexTranscriptionModelOptions } from './google-vertex-transcription-model-options'; import type { GoogleVertexTranscriptionModelGeminiOptions } from './gemini-transcription/google-vertex-gemini-transcription-model-options'; import type { GoogleVertexSpeechModelOptions } from './google-vertex-speech-model-options'; const documentedImageDetails: Record< string, { imageGeneration: boolean; aspectRatios: readonly string[] } > = { 'gemini-nano-banana-2.1': { imageGeneration: true, aspectRatios: [ '1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9', '1:8', '8:1', '1:4', '4:1', ], }, 'gemini-3.1-flash-image-preview': { imageGeneration: true, aspectRatios: [ '1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9', ], }, 'gemini-3-pro-image-preview': { imageGeneration: true, aspectRatios: [ '1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9', ], }, 'gemini-2.5-flash-image': { imageGeneration: true, aspectRatios: [ '1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9', ], }, }; const documentedTranscriptionDetails: Record< string, { wordTimestampDescription: string; languageDetectionDescription: string } > = { chirp_2: { wordTimestampDescription: 'Available with a potential quality and speed tradeoff', languageDetectionDescription: "Auto detection with `['auto']`", }, chirp_3: { wordTimestampDescription: 'Available with a potential transcription quality tradeoff', languageDetectionDescription: "Auto detection with `['auto']`", }, telephony: { wordTimestampDescription: 'Available', languageDetectionDescription: 'Explicit supported language codes, with alternative language detection support', }, }; export const googleVertexProviderOptions = { language: googleProviderOptions.language, image: Object.fromEntries( Object.entries(googleProviderOptions.language).filter( ([key]) => key !== 'responseModalities', ), ), embedding: { outputDimensionality: { description: 'Reduced output dimensions.' }, taskType: { description: 'Task for which embeddings are optimized.' }, title: { description: 'Document title; requires RETRIEVAL_DOCUMENT.' }, autoTruncate: { description: 'Truncate inputs exceeding the model context.', }, } satisfies Record< keyof GoogleVertexEmbeddingModelOptions, ProviderOptionDefinition >, speech: { speechMetadata: { description: 'Directions for Gemini 3.8 speech.' }, turns: { description: 'Structured transcript for Gemini 3.8 speech.' }, multiSpeakerVoiceConfig: { description: 'Voice configuration for dialogue speakers.', }, } satisfies Record< keyof GoogleVertexSpeechModelOptions, ProviderOptionDefinition >, transcription: { languageCodes: { description: 'Language hints or automatic language detection.', }, enableAutomaticPunctuation: { description: 'Include punctuation in Cloud Speech transcripts.', }, enableWordTimeOffsets: { description: 'Include Cloud Speech word timestamps.', }, region: { description: 'Cloud Speech region.' }, customVocabulary: { description: 'Vocabulary hints for Gemini transcription.', }, wordTimestamp: { description: 'Include Gemini word timestamps.' }, diarization: { description: 'Enable Gemini speaker diarization.' }, mode: { description: 'Verbatim or smart Gemini transcription formatting.' }, } satisfies Record< | keyof GoogleVertexTranscriptionModelOptions | keyof GoogleVertexTranscriptionModelGeminiOptions, ProviderOptionDefinition >, video: { pollIntervalMs: { description: 'Polling interval.' }, pollTimeoutMs: { description: 'Polling timeout.' }, personGeneration: { description: 'Policy for generating people.' }, negativePrompt: { description: 'Content to avoid.' }, generateAudio: { description: 'Generate synchronized audio.' }, gcsOutputDirectory: { description: 'Cloud Storage output directory.' }, referenceImages: { description: 'Reference images for video generation.' }, }, } as const satisfies ProviderOptionDefinitions; type VertexEntry = ModelEntry<VertexRequestConfig>; function languageEntry(modelId: string): VertexEntry { const entry = getGoogleLanguageModelEntry(modelId); return { capabilities: { language: entry.capabilities.language }, providerOptions: { language: { ...entry.providerOptions.language, serviceTier: false }, }, requestConfig: { language: requestConfig(modelId).language }, }; } export function getGoogleVertexImageModelEntry(modelId: string): VertexEntry { const known = imageModelIds.some(id => id === modelId); return { capabilities: { image: { sizes: { description: 'Use aspectRatio; the size option is unsupported.', }, fileInput: known ? true : 'unknown', maskInput: known ? false : 'unknown', maxImagesPerCall: 1, ...documentedImageDetails[modelId], }, }, providerOptions: { image: Object.fromEntries( Object.keys(googleVertexProviderOptions.image).map(key => [ key, key === 'serviceTier' ? false : { condition: 'Support follows the Gemini language model and image configuration.', }, ]), ), }, requestConfig: { image: requestConfig(modelId).image }, }; } function embeddingEntry(modelId: string): VertexEntry { const gemini = modelId.startsWith('gemini-embedding-'); return { capabilities: { embedding: { defaultDimensions: gemini ? 3072 : 768, customDimensions: 'unknown', ...{ maxEmbeddingsPerCall: requestConfig(modelId).embedding!.maxEmbeddingsPerCall, supportsParallelCalls: true, multimodal: requestConfig(modelId).embedding!.endpoint === 'embedContent', }, }, }, providerOptions: { embedding: { outputDimensionality: 'unknown', taskType: 'unknown', title: { condition: 'Requires RETRIEVAL_DOCUMENT.' }, autoTruncate: true, }, }, requestConfig: { embedding: requestConfig(modelId).embedding }, }; } function speechEntry(modelId: string): VertexEntry { const chirp = requestConfig(modelId).speech?.api === 'cloud-tts'; return { capabilities: { speech: { instructions: !chirp, ...{ multiSpeaker: !chirp } }, }, providerOptions: { speech: { multiSpeakerVoiceConfig: chirp ? false : true, speechMetadata: chirp ? false : { condition: 'Requires Gemini 3.8 TTS.' }, turns: chirp ? false : { condition: 'Requires Gemini 3.8 TTS.' }, }, }, requestConfig: { speech: requestConfig(modelId).speech }, }; } function transcriptionEntry(modelId: string): VertexEntry { const config = requestConfig(modelId).transcription!; const gemini = config.api === 'gemini'; return { capabilities: { transcription: { transcription: !config.live, streaming: config.live, duration: gemini ? false : true, segments: config.live ? false : { condition: 'Requires word timestamps.' }, language: true, ...documentedTranscriptionDetails[modelId], }, }, providerOptions: { transcription: { languageCodes: true, enableAutomaticPunctuation: !gemini, enableWordTimeOffsets: !gemini, region: !gemini, customVocabulary: gemini, wordTimestamp: gemini, diarization: gemini, mode: gemini, }, }, requestConfig: { transcription: config }, }; } function videoEntry(modelId: string): VertexEntry { const veo3 = modelId.startsWith('veo-3'); return { capabilities: { video: { inputs: { text: true, image: true, firstFrame: true, lastFrame: { condition: 'Requires a Veo model supporting frame interpolation.', }, referenceImage: { condition: 'Requires a Veo model supporting reference images.', }, referenceVideo: false, }, generateAudio: veo3, aspectRatio: true, resolution: true, duration: true, fps: false, seed: true, maxVideosPerCall: 4, }, }, providerOptions: { video: { pollIntervalMs: true, pollTimeoutMs: true, personGeneration: true, negativePrompt: true, generateAudio: veo3, gcsOutputDirectory: true, referenceImages: { condition: 'Requires a Veo model supporting reference images.', }, }, }, }; } const languageModelIds = [ 'gemini-nano-banana-2.1', 'gemini-4-argon', 'gemini-4-carbon', 'gemini-4-hydrogen', 'gemini-3.8-flash', 'gemini-3.7-flash', 'gemini-3.6-flash', 'gemini-3.5-flash', 'gemini-3.5-flash-lite', 'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite', 'gemini-2.0-flash-lite', 'gemini-2.0-flash', 'gemini-2.0-flash-001', 'gemini-1.5-flash', 'gemini-1.5-flash-001', 'gemini-1.5-flash-002', 'gemini-1.5-pro', 'gemini-1.5-pro-001', 'gemini-1.5-pro-002', 'gemini-1.0-pro-001', 'gemini-1.0-pro-vision-001', 'gemini-1.0-pro', 'gemini-1.0-pro-002', 'gemini-2.0-flash-lite-preview-02-05', 'gemini-2.5-flash-preview-09-2025', 'gemini-3-pro-preview', 'gemini-3-pro-image-preview', 'gemini-3-flash-preview', 'gemini-3.1-pro-preview', 'gemini-3.1-flash-image-preview', 'gemini-3.1-flash-lite-preview', 'gemini-2.0-pro-exp-02-05', 'gemini-2.0-flash-exp', ] as const; const imageModelIds = [ 'gemini-2.5-flash-image', 'gemini-3-pro-image-preview', 'gemini-3.1-flash-image-preview', 'gemini-nano-banana-2.1', ] as const; const embeddingModelIds = [ 'textembedding-gecko', 'textembedding-gecko@001', 'textembedding-gecko@003', 'textembedding-gecko-multilingual', 'textembedding-gecko-multilingual@001', 'text-multilingual-embedding-002', 'text-embedding-004', 'text-embedding-005', 'gemini-embedding-001', 'gemini-embedding-2', 'gemini-embedding-2-preview', ] as const; const videoModelIds = [ 'veo-2.0-generate-preview', 'veo-2.0-generate-exp', 'veo-2.0-generate-001', 'veo-3.0-generate-001', 'veo-3.0-fast-generate-001', 'veo-3.0-generate-preview', 'veo-3.0-fast-generate-preview', 'veo-3.1-generate-001', 'veo-3.1-fast-generate-001', 'veo-3.1-generate-preview', 'veo-3.1-fast-generate-preview', ] as const; const speechModelIds = [ 'gemini-2.5-flash-tts', 'gemini-2.5-pro-tts', 'gemini-2.5-flash-lite-preview-tts', 'gemini-3.1-flash-tts-preview', 'chirp-3-hd', ] as const; const transcriptionModelIds = ['chirp_2', 'chirp_3', 'telephony'] as const; const geminiTranscriptionModelIds = [ 'gemini-3.5-transcribe', 'gemini-3.5-transcribe-live', ] as const; const entries: [string, VertexEntry][] = [ ...languageModelIds.map((id): [string, VertexEntry] => [ id, languageEntry(id), ]), ...imageModelIds.map((id): [string, VertexEntry] => [ id, getGoogleVertexImageModelEntry(id), ]), ...embeddingModelIds.map((id): [string, VertexEntry] => [ id, embeddingEntry(id), ]), ...videoModelIds.map((id): [string, VertexEntry] => [id, videoEntry(id)]), ...speechModelIds.map((id): [string, VertexEntry] => [id, speechEntry(id)]), ...transcriptionModelIds.map((id): [string, VertexEntry] => [ id, transcriptionEntry(id), ]), ...geminiTranscriptionModelIds.map((id): [string, VertexEntry] => [ id, transcriptionEntry(id), ]), ]; /** A model may expose more than one operation (for example Gemini image models). */ export const googleVertexModelCatalog: Readonly<Record<string, VertexEntry>> = entries.reduce<Record<string, VertexEntry>>((catalog, [id, entry]) => { const previous = catalog[id]; catalog[id] = { capabilities: { ...previous?.capabilities, ...entry.capabilities }, providerOptions: { ...previous?.providerOptions, ...entry.providerOptions, }, requestConfig: { ...previous?.requestConfig, ...entry.requestConfig }, }; return catalog; }, {}); const vertexAnthropicModels = { 'claude-fable-5-1': { image: true, objectGeneration: true, tools: true, streaming: true, computerUse: true, }, 'claude-fable-5-2': { image: true, objectGeneration: true, tools: true, streaming: true, computerUse: true, }, 'claude-3-7-sonnet@20250219': { image: true, objectGeneration: true, tools: true, streaming: true, computerUse: true, }, 'claude-3-5-sonnet-v2@20241022': { image: true, objectGeneration: true, tools: true, streaming: true, computerUse: true, }, 'claude-3-5-sonnet@20240620': { image: true, objectGeneration: true, tools: true, streaming: true, computerUse: false, }, 'claude-3-5-haiku@20241022': { image: false, objectGeneration: true, tools: true, streaming: true, computerUse: false, }, 'claude-3-sonnet@20240229': { image: true, objectGeneration: true, tools: true, streaming: true, computerUse: false, }, 'claude-3-haiku@20240307': { image: true, objectGeneration: true, tools: true, streaming: true, computerUse: false, }, 'claude-3-opus@20240229': { image: true, objectGeneration: true, tools: true, streaming: true, computerUse: false, }, // Known IDs without declared capabilities retain the custom-model fallback. 'claude-fable-5': undefined, 'claude-haiku-5-5': undefined, 'claude-opus-4-1@20250805': undefined, 'claude-opus-4-5@20251101': undefined, 'claude-opus-4-6': undefined, 'claude-opus-4-7': undefined, 'claude-opus-4-8': undefined, 'claude-opus-4@20250514': undefined, 'claude-opus-5': undefined, 'claude-opus-5-5': undefined, 'claude-sonnet-4-5@20250929': undefined, 'claude-sonnet-4-6': undefined, 'claude-sonnet-4@20250514': undefined, 'claude-sonnet-5': undefined, 'claude-sonnet-5-5': undefined, }; export const googleVertexAnthropicModelCatalog = Object.fromEntries( Object.entries(vertexAnthropicModels).flatMap(([id, support]) => { if (support == null) return []; const entry = { capabilities: { language: { inputs: { image: support.image, audio: false }, objectGeneration: support.objectGeneration, tools: { usage: support.tools, streaming: support.streaming, computerUse: support.computerUse, }, reasoning: { support: 'unknown' }, sampling: { temperature: 'unknown', topP: 'unknown', logprobs: false, }, }, }, } as const; return [[id, entry satisfies ModelEntry]]; }), ); const vertexXaiModels = { 'xai/grok-4.20-reasoning': { reasoning: true }, 'xai/grok-4.20-non-reasoning': { reasoning: false }, 'xai/grok-4.1-fast-reasoning': { reasoning: true }, 'xai/grok-4.1-fast-non-reasoning': { reasoning: false }, }; export const googleVertexXaiModelCatalog = Object.fromEntries( Object.entries(vertexXaiModels).map(([id, support]) => { const entry = { capabilities: { language: { inputs: { image: 'unknown', audio: 'unknown' }, objectGeneration: 'unknown', tools: { usage: 'unknown', streaming: 'unknown' }, reasoning: { support: support.reasoning }, sampling: { temperature: 'unknown', topP: 'unknown', logprobs: 'unknown', }, }, }, } as const; return [id, entry satisfies ModelEntry]; }), ); const vertexMaasModels = { 'deepseek-ai/deepseek-r1-0528-maas': { publisher: 'DeepSeek' }, 'deepseek-ai/deepseek-v3.1-maas': { publisher: 'DeepSeek' }, 'deepseek-ai/deepseek-v3.2-maas': { publisher: 'DeepSeek' }, 'openai/gpt-oss-120b-maas': { publisher: 'OpenAI' }, 'openai/gpt-oss-20b-maas': { publisher: 'OpenAI' }, 'meta/llama-4-maverick-17b-128e-instruct-maas': { publisher: 'Meta' }, 'meta/llama-4-scout-17b-16e-instruct-maas': { publisher: 'Meta' }, 'minimax/minimax-m2-maas': { publisher: 'MiniMax' }, 'qwen/qwen3-coder-480b-a35b-instruct-maas': { publisher: 'Qwen' }, 'qwen/qwen3-next-80b-a3b-instruct-maas': { publisher: 'Qwen' }, 'qwen/qwen3-next-80b-a3b-thinking-maas': { publisher: 'Qwen' }, 'moonshotai/kimi-k2-thinking-maas': { publisher: 'Moonshot' }, }; export const googleVertexMaasModelCatalog = Object.fromEntries( Object.entries(vertexMaasModels).map(([id, support]) => { const entry = { capabilities: { language: { inputs: { image: 'unknown', audio: 'unknown' }, objectGeneration: 'unknown', tools: { usage: 'unknown', streaming: 'unknown' }, reasoning: { support: 'unknown' }, sampling: { temperature: 'unknown', topP: 'unknown', logprobs: 'unknown', }, publisher: support.publisher, }, }, requestConfig: requestConfig(id), } as const; return [id, entry satisfies ModelEntry<VertexRequestConfig>]; }), ); /** Known model IDs, derived from the declarations used to build the catalog. */ export type GoogleVertexModelIds = { language: (typeof languageModelIds)[number]; image: (typeof imageModelIds)[number]; embedding: (typeof embeddingModelIds)[number]; video: (typeof videoModelIds)[number]; speech: (typeof speechModelIds)[number]; transcription: (typeof transcriptionModelIds)[number]; geminiTranscription: (typeof geminiTranscriptionModelIds)[number]; anthropic: keyof typeof vertexAnthropicModels; xai: keyof typeof vertexXaiModels; maas: keyof typeof vertexMaasModels; };