model.ts104 lines · main
| 1 | import { openai } from '@ai-sdk/openai' |
| 2 | import { LanguageModel } from 'ai' |
| 3 | |
| 4 | import { checkAwsCredentials, createRoutedBedrock } from './bedrock' |
| 5 | import { |
| 6 | BedrockModel, |
| 7 | getDefaultModelForProvider, |
| 8 | Model, |
| 9 | OpenAIModelEntry, |
| 10 | OpenAIModelId, |
| 11 | ProviderModelConfig, |
| 12 | PROVIDERS, |
| 13 | } from './model.utils' |
| 14 | |
| 15 | type ProviderOptions = Record<string, any> |
| 16 | type SystemProviderOptions = Record<string, any> |
| 17 | |
| 18 | type ModelSuccess = { |
| 19 | /** Spread directly into AI SDK calls: `streamText({ ...modelParams, ... })` */ |
| 20 | modelParams: { model: LanguageModel; providerOptions?: ProviderOptions } |
| 21 | systemProviderOptions?: SystemProviderOptions |
| 22 | error?: never |
| 23 | } |
| 24 | |
| 25 | export type ModelError = { |
| 26 | modelParams?: never |
| 27 | systemProviderOptions?: never |
| 28 | error: Error |
| 29 | } |
| 30 | |
| 31 | type ModelResponse = ModelSuccess | ModelError |
| 32 | |
| 33 | export type GetModelParams = |
| 34 | | { |
| 35 | provider: 'openai' |
| 36 | /** |
| 37 | * Specifies which OpenAI model to use and its reasoning effort. |
| 38 | * Create entries via `openaiModelEntry()` — reasoning effort is validated against the model |
| 39 | * at compile time. Use `DEFAULT_COMPLETION_MODEL` for simple endpoints (minimal reasoning). |
| 40 | * Callers are responsible for resolving the correct entry (including throttling/entitlement |
| 41 | * fallbacks) before calling getModel. |
| 42 | */ |
| 43 | modelEntry: OpenAIModelEntry |
| 44 | } |
| 45 | | { |
| 46 | provider: 'bedrock' |
| 47 | /** Used for consistent hashing across Bedrock regions. */ |
| 48 | routingKey: string |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * Retrieves a LanguageModel from a specific provider and model entry. |
| 53 | * Callers are responsible for resolving the correct model entry (including throttling/entitlement |
| 54 | * fallbacks) before calling this function. |
| 55 | * Returns systemProviderOptions that callers can attach to the system message. |
| 56 | */ |
| 57 | export async function getModel(params: GetModelParams): Promise<ModelResponse> { |
| 58 | const { provider } = params |
| 59 | |
| 60 | const providerRegistry = PROVIDERS[provider] |
| 61 | if (!providerRegistry) { |
| 62 | return { error: new Error(`Unknown provider: ${provider}`) } |
| 63 | } |
| 64 | |
| 65 | const models = providerRegistry.models as Record<Model, ProviderModelConfig> |
| 66 | const modelEntry = params.provider === 'openai' ? params.modelEntry : undefined |
| 67 | |
| 68 | const useDefault = !modelEntry?.id || !models[modelEntry.id] |
| 69 | |
| 70 | const chosenModelId = useDefault ? getDefaultModelForProvider(provider) : modelEntry?.id |
| 71 | |
| 72 | if (provider === 'bedrock') { |
| 73 | const hasAwsCredentials = await checkAwsCredentials() |
| 74 | const hasAwsBedrockRoleArn = !!process.env.AWS_BEDROCK_ROLE_ARN |
| 75 | if (!hasAwsBedrockRoleArn || !hasAwsCredentials) { |
| 76 | return { error: new Error('AWS Bedrock credentials not available') } |
| 77 | } |
| 78 | const bedrock = createRoutedBedrock(params.routingKey) |
| 79 | const model = await bedrock(chosenModelId as BedrockModel) |
| 80 | const systemProviderOptions = ( |
| 81 | providerRegistry.models as Record<BedrockModel, ProviderModelConfig> |
| 82 | )[chosenModelId as BedrockModel]?.systemProviderOptions |
| 83 | return { modelParams: { model }, systemProviderOptions } |
| 84 | } |
| 85 | |
| 86 | if (provider === 'openai') { |
| 87 | if (!process.env.OPENAI_API_KEY) { |
| 88 | return { error: new Error('OPENAI_API_KEY not available') } |
| 89 | } |
| 90 | const baseProviderOptions = providerRegistry.providerOptions?.openai ?? {} |
| 91 | const openaiProviderOptions = modelEntry?.reasoningEffort |
| 92 | ? { ...baseProviderOptions, reasoningEffort: modelEntry.reasoningEffort } |
| 93 | : baseProviderOptions |
| 94 | return { |
| 95 | modelParams: { |
| 96 | model: openai(chosenModelId as OpenAIModelId), |
| 97 | providerOptions: { openai: openaiProviderOptions }, |
| 98 | }, |
| 99 | systemProviderOptions: models[chosenModelId as OpenAIModelId]?.systemProviderOptions, |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | return { error: new Error(`Unsupported provider: ${provider}`) } |
| 104 | } |