feat(ai): refresh AI models with deprecation support and multi-provider defaults (BREAKING: deploy server before frontend please) (#16503)

## Summary

- Add latest AI models from OpenAI (GPT-4.1, o3, o4-mini), Anthropic
(Claude 4.5 Opus/Sonnet/Haiku), and xAI (Grok 4.1)
- Mark deprecated models (GPT-4o, GPT-4o-mini, GPT-4-turbo, Claude Opus
4, Claude Sonnet 4) with a `deprecated` flag
- Split AI models into separate files per provider for better
maintainability
- Support comma-separated default model lists for automatic fallback
across providers (works out of the box for self-hosters regardless of
which provider they configure)
- Filter deprecated models from dropdown selection while keeping them
functional for existing agents

## Changes

### New Models Added
| Provider | Models |
|----------|--------|
| OpenAI | gpt-4.1, gpt-4.1-mini, o3, o4-mini |
| Anthropic | claude-opus-4-5, claude-sonnet-4-5, claude-haiku-4-5 |
| xAI | grok-4-1-fast-reasoning |

### Deprecated Models
- gpt-4o, gpt-4o-mini, gpt-4-turbo (OpenAI)
- claude-opus-4-20250514, claude-sonnet-4-20250514 (Anthropic)

### Config Changes
Default model configs now support comma-separated fallback lists:
-
`DEFAULT_AI_SPEED_MODEL_ID=gpt-4.1-mini,claude-haiku-4-5-20251001,grok-3-mini`
-
`DEFAULT_AI_PERFORMANCE_MODEL_ID=gpt-4.1,claude-sonnet-4-5-20250929,grok-4`

## Test plan

- [x] Unit tests pass
- [x] Typecheck passes
- [x] Lint passes
- [ ] Verify deprecated models don't appear in model dropdowns
- [ ] Verify agents with deprecated models still work correctly
- [ ] Verify default model fallback works when only one provider is
configured
This commit is contained in:
Félix Malfait
2025-12-11 21:47:38 +01:00
committed by GitHub
parent 999bc84b17
commit a13727335b
15 changed files with 655 additions and 264 deletions
@@ -0,0 +1,64 @@
export enum ModelProvider {
NONE = 'none',
OPENAI = 'openai',
ANTHROPIC = 'anthropic',
OPENAI_COMPATIBLE = 'open_ai_compatible',
XAI = 'xai',
}
export const DEFAULT_FAST_MODEL = 'default-fast-model' as const;
export const DEFAULT_SMART_MODEL = 'default-smart-model' as const;
export type ModelId =
| typeof DEFAULT_FAST_MODEL
| typeof DEFAULT_SMART_MODEL
// OpenAI models
| 'gpt-4o'
| 'gpt-4o-mini'
| 'gpt-4-turbo'
| 'gpt-4.1'
| 'gpt-4.1-mini'
| 'o3'
| 'o4-mini'
// Anthropic models
| 'claude-opus-4-20250514'
| 'claude-sonnet-4-20250514'
| 'claude-3-5-haiku-20241022'
| 'claude-opus-4-5-20251101'
| 'claude-sonnet-4-5-20250929'
| 'claude-haiku-4-5-20251001'
// xAI models
| 'grok-3'
| 'grok-3-mini'
| 'grok-4'
| 'grok-4-1-fast-reasoning'
| string; // Allow custom model names
export type SupportedFileType =
| 'image/png'
| 'image/jpeg'
| 'image/gif'
| 'image/webp'
| 'application/pdf'
| 'text/plain'
| 'text/html'
| 'text/csv'
| 'application/json';
export interface AIModelConfig {
modelId: ModelId;
label: string;
description: string;
provider: ModelProvider;
inputCostPer1kTokensInCents: number;
outputCostPer1kTokensInCents: number;
contextWindowTokens: number;
maxOutputTokens: number;
supportedFileTypes?: SupportedFileType[];
doesSupportThinking?: boolean;
nativeCapabilities?: {
webSearch?: boolean;
twitterSearch?: boolean;
};
deprecated?: boolean;
}
@@ -10,19 +10,56 @@ import {
} from './ai-models.const';
describe('AI_MODELS', () => {
it('should contain all expected models', () => {
expect(AI_MODELS).toHaveLength(9);
expect(AI_MODELS.map((model) => model.modelId)).toEqual([
'gpt-4o',
'gpt-4o-mini',
'gpt-4-turbo',
'claude-opus-4-20250514',
'claude-sonnet-4-20250514',
'claude-3-5-haiku-20241022',
'grok-3',
'grok-3-mini',
'grok-4',
]);
it('should have at least one model per provider', () => {
const providers = [
ModelProvider.OPENAI,
ModelProvider.ANTHROPIC,
ModelProvider.XAI,
];
providers.forEach((provider) => {
const modelsForProvider = AI_MODELS.filter(
(model) => model.provider === provider,
);
expect(modelsForProvider.length).toBeGreaterThan(0);
});
});
it('should have all required fields for each model', () => {
AI_MODELS.forEach((model) => {
expect(model.modelId).toBeDefined();
expect(model.label).toBeDefined();
expect(model.description).toBeDefined();
expect(model.provider).toBeDefined();
expect(model.inputCostPer1kTokensInCents).toBeDefined();
expect(model.outputCostPer1kTokensInCents).toBeDefined();
expect(model.contextWindowTokens).toBeGreaterThan(0);
expect(model.maxOutputTokens).toBeGreaterThan(0);
});
});
it('should have unique model IDs', () => {
const modelIds = AI_MODELS.map((model) => model.modelId);
const uniqueModelIds = new Set(modelIds);
expect(uniqueModelIds.size).toBe(modelIds.length);
});
it('should have at least one non-deprecated model per provider', () => {
const providers = [
ModelProvider.OPENAI,
ModelProvider.ANTHROPIC,
ModelProvider.XAI,
];
providers.forEach((provider) => {
const activeModelsForProvider = AI_MODELS.filter(
(model) => model.provider === provider && !model.deprecated,
);
expect(activeModelsForProvider.length).toBeGreaterThan(0);
});
});
});
@@ -140,4 +177,50 @@ describe('AiModelRegistryService', () => {
'Model with ID non-existent-model not found',
);
});
it('should find first available model from comma-separated list', () => {
// First model not available, second model available
MOCK_CONFIG_SERVICE.get.mockReturnValue(
'gpt-4.1-mini,claude-haiku-4-5-20251001,grok-3-mini',
);
const getModelSpy = jest
.spyOn(SERVICE, 'getModel')
.mockImplementation((modelId: string) => {
if (modelId === 'claude-haiku-4-5-20251001') {
return {
modelId: 'claude-haiku-4-5-20251001',
provider: ModelProvider.ANTHROPIC,
model: {} as any,
};
}
return undefined;
});
const result = SERVICE.getDefaultSpeedModel();
expect(result).toBeDefined();
expect(result.modelId).toBe('claude-haiku-4-5-20251001');
expect(getModelSpy).toHaveBeenCalledWith('gpt-4.1-mini');
expect(getModelSpy).toHaveBeenCalledWith('claude-haiku-4-5-20251001');
});
it('should fall back to any available model if none in list are available', () => {
MOCK_CONFIG_SERVICE.get.mockReturnValue('model-a,model-b,model-c');
jest.spyOn(SERVICE, 'getModel').mockReturnValue(undefined);
jest.spyOn(SERVICE, 'getAvailableModels').mockReturnValue([
{
modelId: 'fallback-model',
provider: ModelProvider.OPENAI_COMPATIBLE,
model: {} as any,
},
]);
const result = SERVICE.getDefaultSpeedModel();
expect(result).toBeDefined();
expect(result.modelId).toBe('fallback-model');
});
});
@@ -1,223 +1,19 @@
export enum ModelProvider {
NONE = 'none',
OPENAI = 'openai',
ANTHROPIC = 'anthropic',
OPENAI_COMPATIBLE = 'open_ai_compatible',
XAI = 'xai',
}
export {
DEFAULT_FAST_MODEL,
DEFAULT_SMART_MODEL,
ModelProvider,
type AIModelConfig,
type ModelId,
type SupportedFileType,
} from './ai-models-types.const';
export const DEFAULT_FAST_MODEL = 'default-fast-model' as const;
export const DEFAULT_SMART_MODEL = 'default-smart-model' as const;
export type ModelId =
| typeof DEFAULT_FAST_MODEL
| typeof DEFAULT_SMART_MODEL
| 'gpt-4o'
| 'gpt-4o-mini'
| 'gpt-4-turbo'
| 'claude-opus-4-20250514'
| 'claude-sonnet-4-20250514'
| 'claude-3-5-haiku-20241022'
| 'grok-3'
| 'grok-3-mini'
| 'grok-4'
| string; // Allow custom model names
export type SupportedFileType =
| 'image/png'
| 'image/jpeg'
| 'image/gif'
| 'image/webp'
| 'application/pdf'
| 'text/plain'
| 'text/html'
| 'text/csv'
| 'application/json';
export interface AIModelConfig {
modelId: ModelId;
label: string;
description: string;
provider: ModelProvider;
inputCostPer1kTokensInCents: number;
outputCostPer1kTokensInCents: number;
contextWindowTokens: number;
maxOutputTokens: number;
supportedFileTypes?: SupportedFileType[];
doesSupportThinking?: boolean;
nativeCapabilities?: {
webSearch?: boolean;
twitterSearch?: boolean;
};
}
import { type AIModelConfig } from './ai-models-types.const';
import { ANTHROPIC_MODELS } from './anthropic-models.const';
import { OPENAI_MODELS } from './openai-models.const';
import { XAI_MODELS } from './xai-models.const';
export const AI_MODELS: AIModelConfig[] = [
{
modelId: 'gpt-4o',
label: 'GPT-4o',
description:
'Most advanced multimodal model with strong reasoning, vision, and coding capabilities',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 0.25,
outputCostPer1kTokensInCents: 1.0,
contextWindowTokens: 128000,
maxOutputTokens: 16384,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'gpt-4o-mini',
label: 'GPT-4o Mini',
description:
'Fast and cost-efficient model for lightweight tasks and high-volume operations',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 0.015,
outputCostPer1kTokensInCents: 0.06,
contextWindowTokens: 128000,
maxOutputTokens: 16384,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'gpt-4-turbo',
label: 'GPT-4 Turbo',
description:
'Previous generation high-performance model with vision capabilities',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 1.0,
outputCostPer1kTokensInCents: 3.0,
contextWindowTokens: 128000,
maxOutputTokens: 4096,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: false,
},
},
{
modelId: 'claude-opus-4-20250514',
label: 'Claude Opus 4',
description:
'Most powerful Claude model with extended thinking for complex reasoning tasks',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 1.5,
outputCostPer1kTokensInCents: 7.5,
contextWindowTokens: 200000,
maxOutputTokens: 8192,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'claude-sonnet-4-20250514',
label: 'Claude Sonnet 4',
description:
'Balanced model with strong performance and extended thinking capabilities',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 0.3,
outputCostPer1kTokensInCents: 1.5,
contextWindowTokens: 200000,
maxOutputTokens: 8192,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'claude-3-5-haiku-20241022',
label: 'Claude Haiku 3.5',
description:
'Fast and efficient model optimized for speed and cost-effectiveness',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 0.08,
outputCostPer1kTokensInCents: 0.4,
contextWindowTokens: 200000,
maxOutputTokens: 8192,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: false,
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'grok-3',
label: 'Grok-3',
description:
'Advanced model with web and Twitter search, optimized for real-time information',
provider: ModelProvider.XAI,
inputCostPer1kTokensInCents: 0.3,
outputCostPer1kTokensInCents: 1.5,
contextWindowTokens: 131072,
maxOutputTokens: 8192,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
twitterSearch: true,
},
},
{
modelId: 'grok-3-mini',
label: 'Grok-3 Mini',
description:
'Lightweight model with web and Twitter search for fast, cost-effective operations',
provider: ModelProvider.XAI,
inputCostPer1kTokensInCents: 0.03,
outputCostPer1kTokensInCents: 0.05,
contextWindowTokens: 131072,
maxOutputTokens: 8192,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
twitterSearch: true,
},
},
{
modelId: 'grok-4',
label: 'Grok-4',
description:
'Most capable Grok model with enhanced reasoning, web and Twitter search',
provider: ModelProvider.XAI,
inputCostPer1kTokensInCents: 0.5,
outputCostPer1kTokensInCents: 2.5,
contextWindowTokens: 131072,
maxOutputTokens: 8192,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
twitterSearch: true,
},
},
...OPENAI_MODELS,
...ANTHROPIC_MODELS,
...XAI_MODELS,
];
@@ -0,0 +1,159 @@
import { type AIModelConfig, ModelProvider } from './ai-models-types.const';
export const ANTHROPIC_MODELS: AIModelConfig[] = [
// Active models
{
modelId: 'claude-opus-4-5-20251101',
label: 'Claude Opus 4.5',
description:
'Most powerful Claude model excelling in complex reasoning, coding, and agentic tasks',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 0.5,
outputCostPer1kTokensInCents: 2.5,
contextWindowTokens: 200000,
maxOutputTokens: 64000,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'claude-sonnet-4-5-20250929',
label: 'Claude Sonnet 4.5',
description:
'Advanced model for coding tasks and complex agent-based workflows with 1M context',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 0.3,
outputCostPer1kTokensInCents: 1.5,
contextWindowTokens: 1000000,
maxOutputTokens: 64000,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'claude-haiku-4-5-20251001',
label: 'Claude Haiku 4.5',
description:
'Fast and cost-effective model optimized for high-speed processing',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 0.1,
outputCostPer1kTokensInCents: 0.5,
contextWindowTokens: 200000,
maxOutputTokens: 64000,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: false,
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'claude-3-5-haiku-20241022',
label: 'Claude Haiku 3.5',
description:
'Fast and efficient model optimized for speed and cost-effectiveness',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 0.08,
outputCostPer1kTokensInCents: 0.4,
contextWindowTokens: 200000,
maxOutputTokens: 8192,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: false,
nativeCapabilities: {
webSearch: true,
},
},
// Deprecated models - kept for backward compatibility with existing agents
{
modelId: 'claude-opus-4-20250514',
label: 'Claude Opus 4',
description:
'Most powerful Claude model with extended thinking for complex reasoning tasks',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 1.5,
outputCostPer1kTokensInCents: 7.5,
contextWindowTokens: 200000,
maxOutputTokens: 8192,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
},
deprecated: true,
},
{
modelId: 'claude-sonnet-4-20250514',
label: 'Claude Sonnet 4',
description:
'Balanced model with strong performance and extended thinking capabilities',
provider: ModelProvider.ANTHROPIC,
inputCostPer1kTokensInCents: 0.3,
outputCostPer1kTokensInCents: 1.5,
contextWindowTokens: 200000,
maxOutputTokens: 8192,
supportedFileTypes: [
'image/png',
'image/jpeg',
'image/gif',
'image/webp',
'application/pdf',
'text/plain',
'text/html',
'text/csv',
],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
},
deprecated: true,
},
];
@@ -0,0 +1,117 @@
import { type AIModelConfig, ModelProvider } from './ai-models-types.const';
export const OPENAI_MODELS: AIModelConfig[] = [
// Active models
{
modelId: 'gpt-4.1',
label: 'GPT-4.1',
description:
'Advanced model excelling in coding, instruction following, and long-context comprehension',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 0.2,
outputCostPer1kTokensInCents: 0.8,
contextWindowTokens: 1047576,
maxOutputTokens: 32768,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'gpt-4.1-mini',
label: 'GPT-4.1 Mini',
description:
'Fast and cost-efficient version of GPT-4.1 optimized for low latency',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 0.04,
outputCostPer1kTokensInCents: 0.16,
contextWindowTokens: 1047576,
maxOutputTokens: 32768,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'o3',
label: 'o3',
description:
'Powerful reasoning model excelling in complex queries, coding, math, and science',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 0.2,
outputCostPer1kTokensInCents: 0.8,
contextWindowTokens: 200000,
maxOutputTokens: 100000,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
},
},
{
modelId: 'o4-mini',
label: 'o4-mini',
description:
'Cost-effective reasoning model excelling in math, coding, and visual tasks',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 0.11,
outputCostPer1kTokensInCents: 0.44,
contextWindowTokens: 200000,
maxOutputTokens: 100000,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
},
},
// Deprecated models - kept for backward compatibility with existing agents
{
modelId: 'gpt-4o',
label: 'GPT-4o',
description:
'Most advanced multimodal model with strong reasoning, vision, and coding capabilities',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 0.25,
outputCostPer1kTokensInCents: 1.0,
contextWindowTokens: 128000,
maxOutputTokens: 16384,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
},
deprecated: true,
},
{
modelId: 'gpt-4o-mini',
label: 'GPT-4o Mini',
description:
'Fast and cost-efficient model for lightweight tasks and high-volume operations',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 0.015,
outputCostPer1kTokensInCents: 0.06,
contextWindowTokens: 128000,
maxOutputTokens: 16384,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
},
deprecated: true,
},
{
modelId: 'gpt-4-turbo',
label: 'GPT-4 Turbo',
description:
'Previous generation high-performance model with vision capabilities',
provider: ModelProvider.OPENAI,
inputCostPer1kTokensInCents: 1.0,
outputCostPer1kTokensInCents: 3.0,
contextWindowTokens: 128000,
maxOutputTokens: 4096,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: false,
},
deprecated: true,
},
];
@@ -0,0 +1,70 @@
import { type AIModelConfig, ModelProvider } from './ai-models-types.const';
export const XAI_MODELS: AIModelConfig[] = [
// Active models
{
modelId: 'grok-4-1-fast-reasoning',
label: 'Grok 4.1 Fast',
description:
'Next-generation tool-calling agent with 2M context for advanced agentic workflows',
provider: ModelProvider.XAI,
inputCostPer1kTokensInCents: 0.02,
outputCostPer1kTokensInCents: 0.05,
contextWindowTokens: 2000000,
maxOutputTokens: 8192,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
doesSupportThinking: true,
nativeCapabilities: {
webSearch: true,
twitterSearch: true,
},
},
{
modelId: 'grok-4',
label: 'Grok-4',
description:
'Most capable Grok model with enhanced reasoning, web and Twitter search',
provider: ModelProvider.XAI,
inputCostPer1kTokensInCents: 0.3,
outputCostPer1kTokensInCents: 1.5,
contextWindowTokens: 256000,
maxOutputTokens: 8192,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
twitterSearch: true,
},
},
{
modelId: 'grok-3',
label: 'Grok-3',
description:
'Advanced model with web and Twitter search, optimized for real-time information',
provider: ModelProvider.XAI,
inputCostPer1kTokensInCents: 0.3,
outputCostPer1kTokensInCents: 1.5,
contextWindowTokens: 131072,
maxOutputTokens: 8192,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
twitterSearch: true,
},
},
{
modelId: 'grok-3-mini',
label: 'Grok-3 Mini',
description:
'Lightweight model with web and Twitter search for fast, cost-effective operations',
provider: ModelProvider.XAI,
inputCostPer1kTokensInCents: 0.03,
outputCostPer1kTokensInCents: 0.05,
contextWindowTokens: 131072,
maxOutputTokens: 8192,
supportedFileTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
nativeCapabilities: {
webSearch: true,
twitterSearch: true,
},
},
];
@@ -13,6 +13,9 @@ import {
ModelProvider,
type AIModelConfig,
} from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
import { ANTHROPIC_MODELS } from 'src/engine/metadata-modules/ai/ai-models/constants/anthropic-models.const';
import { OPENAI_MODELS } from 'src/engine/metadata-modules/ai/ai-models/constants/openai-models.const';
import { XAI_MODELS } from 'src/engine/metadata-modules/ai/ai-models/constants/xai-models.const';
export interface RegisteredAIModel {
modelId: string;
@@ -66,25 +69,18 @@ export class AiModelRegistryService {
}
private registerOpenAIModels(): void {
const openaiModels = AI_MODELS.filter(
(model) => model.provider === ModelProvider.OPENAI,
);
openaiModels.forEach((modelConfig) => {
OPENAI_MODELS.forEach((modelConfig) => {
this.modelRegistry.set(modelConfig.modelId, {
modelId: modelConfig.modelId,
provider: ModelProvider.OPENAI,
model: openai(modelConfig.modelId),
doesSupportThinking: modelConfig.doesSupportThinking,
});
});
}
private registerAnthropicModels(): void {
const anthropicModels = AI_MODELS.filter(
(model) => model.provider === ModelProvider.ANTHROPIC,
);
anthropicModels.forEach((modelConfig) => {
ANTHROPIC_MODELS.forEach((modelConfig) => {
this.modelRegistry.set(modelConfig.modelId, {
modelId: modelConfig.modelId,
provider: ModelProvider.ANTHROPIC,
@@ -95,15 +91,12 @@ export class AiModelRegistryService {
}
private registerXaiModels(): void {
const xaiModels = AI_MODELS.filter(
(model) => model.provider === ModelProvider.XAI,
);
xaiModels.forEach((modelConfig) => {
XAI_MODELS.forEach((modelConfig) => {
this.modelRegistry.set(modelConfig.modelId, {
modelId: modelConfig.modelId,
provider: ModelProvider.XAI,
model: xai(modelConfig.modelId),
doesSupportThinking: modelConfig.doesSupportThinking,
});
});
}
@@ -140,11 +133,30 @@ export class AiModelRegistryService {
return Array.from(this.modelRegistry.values());
}
private getFirstAvailableModelFromList(
modelIdList: string,
): RegisteredAIModel | undefined {
const modelIds = modelIdList
.split(',')
.map((id) => id.trim())
.filter((id) => id.length > 0);
for (const modelId of modelIds) {
const model = this.getModel(modelId);
if (model) {
return model;
}
}
return undefined;
}
getDefaultSpeedModel(): RegisteredAIModel {
const defaultModelId = this.twentyConfigService.get(
const defaultModelIds = this.twentyConfigService.get(
'DEFAULT_AI_SPEED_MODEL_ID',
);
let model = this.getModel(defaultModelId);
let model = this.getFirstAvailableModelFromList(defaultModelIds);
if (!model) {
const availableModels = this.getAvailableModels();
@@ -156,10 +168,10 @@ export class AiModelRegistryService {
}
getDefaultPerformanceModel(): RegisteredAIModel {
const defaultModelId = this.twentyConfigService.get(
const defaultModelIds = this.twentyConfigService.get(
'DEFAULT_AI_PERFORMANCE_MODEL_ID',
);
let model = this.getModel(defaultModelId);
let model = this.getFirstAvailableModelFromList(defaultModelIds);
if (!model) {
const availableModels = this.getAvailableModels();