model.ts 3.6 KB

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