From 172cc12a827a2765fac2bdf498cc2c9f9ea0d1a5 Mon Sep 17 00:00:00 2001 From: Abdul Rahman <81605929+abdulrahmancodes@users.noreply.github.com> Date: Tue, 16 Sep 2025 21:25:11 +0530 Subject: [PATCH] feat: Add xAI models support (#14523) --- packages/twenty-cli/schemas/agent.schema.json | 13 ++++++- .../src/generated-metadata/graphql.ts | 3 +- .../twenty-front/src/generated/graphql.ts | 3 +- packages/twenty-server/package.json | 1 + .../ai/constants/ai-models.const.spec.ts | 5 ++- .../ai/constants/ai-models.const.ts | 25 ++++++++++++ .../ai/services/ai-model-registry.service.ts | 26 ++++++++++++- .../twenty-config/config-variables.ts | 9 +++++ yarn.lock | 39 +++++++++++++++++++ 9 files changed, 119 insertions(+), 5 deletions(-) diff --git a/packages/twenty-cli/schemas/agent.schema.json b/packages/twenty-cli/schemas/agent.schema.json index 1e931fca1c0..26f3fabf72e 100644 --- a/packages/twenty-cli/schemas/agent.schema.json +++ b/packages/twenty-cli/schemas/agent.schema.json @@ -48,7 +48,18 @@ "type": "string", "description": "AI model to use for this agent", "default": "auto", - "enum": ["auto", "gpt-4", "gpt-4-turbo", "gpt-3.5-turbo", "claude-3-opus", "claude-3-sonnet", "claude-3-haiku"] + "enum": [ + "auto", + "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" + ] }, "responseFormat": { "type": "object", diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 47348e64ee1..76b520c46ad 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1419,7 +1419,8 @@ export enum ModelProvider { ANTHROPIC = 'ANTHROPIC', NONE = 'NONE', OPENAI = 'OPENAI', - OPENAI_COMPATIBLE = 'OPENAI_COMPATIBLE' + OPENAI_COMPATIBLE = 'OPENAI_COMPATIBLE', + XAI = 'XAI' } export type Mutation = { diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 4c8cb20bf92..01c1825a886 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -1376,7 +1376,8 @@ export enum ModelProvider { ANTHROPIC = 'ANTHROPIC', NONE = 'NONE', OPENAI = 'OPENAI', - OPENAI_COMPATIBLE = 'OPENAI_COMPATIBLE' + OPENAI_COMPATIBLE = 'OPENAI_COMPATIBLE', + XAI = 'XAI' } export type Mutation = { diff --git a/packages/twenty-server/package.json b/packages/twenty-server/package.json index 47d71fbae84..b8d4cb244d0 100644 --- a/packages/twenty-server/package.json +++ b/packages/twenty-server/package.json @@ -17,6 +17,7 @@ "dependencies": { "@ai-sdk/anthropic": "^1.2.12", "@ai-sdk/openai": "^1.3.22", + "@ai-sdk/xai": "1.2.15", "@aws-sdk/client-lambda": "3.825.0", "@aws-sdk/client-s3": "3.825.0", "@aws-sdk/client-sts": "3.825.0", diff --git a/packages/twenty-server/src/engine/core-modules/ai/constants/ai-models.const.spec.ts b/packages/twenty-server/src/engine/core-modules/ai/constants/ai-models.const.spec.ts index ca4ba5dec97..1c73e2badc8 100644 --- a/packages/twenty-server/src/engine/core-modules/ai/constants/ai-models.const.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/ai/constants/ai-models.const.spec.ts @@ -7,7 +7,7 @@ import { AI_MODELS, ModelProvider } from './ai-models.const'; describe('AI_MODELS', () => { it('should contain all expected models', () => { - expect(AI_MODELS).toHaveLength(6); + expect(AI_MODELS).toHaveLength(9); expect(AI_MODELS.map((model) => model.modelId)).toEqual([ 'gpt-4o', 'gpt-4o-mini', @@ -15,6 +15,9 @@ describe('AI_MODELS', () => { 'claude-opus-4-20250514', 'claude-sonnet-4-20250514', 'claude-3-5-haiku-20241022', + 'grok-3', + 'grok-3-mini', + 'grok-4', ]); }); }); diff --git a/packages/twenty-server/src/engine/core-modules/ai/constants/ai-models.const.ts b/packages/twenty-server/src/engine/core-modules/ai/constants/ai-models.const.ts index 60d47ecd9c3..4d06d3401a9 100644 --- a/packages/twenty-server/src/engine/core-modules/ai/constants/ai-models.const.ts +++ b/packages/twenty-server/src/engine/core-modules/ai/constants/ai-models.const.ts @@ -3,6 +3,7 @@ export enum ModelProvider { OPENAI = 'openai', ANTHROPIC = 'anthropic', OPENAI_COMPATIBLE = 'open_ai_compatible', + XAI = 'xai', } export type ModelId = @@ -13,6 +14,9 @@ export type ModelId = | '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 interface AIModelConfig { @@ -66,4 +70,25 @@ export const AI_MODELS: AIModelConfig[] = [ inputCostPer1kTokensInCents: 0.08, outputCostPer1kTokensInCents: 0.4, }, + { + modelId: 'grok-3', + label: 'Grok-3', + provider: ModelProvider.XAI, + inputCostPer1kTokensInCents: 0.3, + outputCostPer1kTokensInCents: 1.5, + }, + { + modelId: 'grok-3-mini', + label: 'Grok-3 Mini', + provider: ModelProvider.XAI, + inputCostPer1kTokensInCents: 0.03, + outputCostPer1kTokensInCents: 0.05, + }, + { + modelId: 'grok-4', + label: 'Grok-4', + provider: ModelProvider.XAI, + inputCostPer1kTokensInCents: 0.5, + outputCostPer1kTokensInCents: 2.5, + }, ]; diff --git a/packages/twenty-server/src/engine/core-modules/ai/services/ai-model-registry.service.ts b/packages/twenty-server/src/engine/core-modules/ai/services/ai-model-registry.service.ts index 65f2ec04ca8..7f4b098fea6 100644 --- a/packages/twenty-server/src/engine/core-modules/ai/services/ai-model-registry.service.ts +++ b/packages/twenty-server/src/engine/core-modules/ai/services/ai-model-registry.service.ts @@ -2,12 +2,13 @@ import { Injectable } from '@nestjs/common'; import { anthropic } from '@ai-sdk/anthropic'; import { createOpenAI, openai } from '@ai-sdk/openai'; +import { xai } from '@ai-sdk/xai'; import { type LanguageModel } from 'ai'; import { AI_MODELS, - type AIModelConfig, ModelProvider, + type AIModelConfig, } from 'src/engine/core-modules/ai/constants/ai-models.const'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; @@ -40,6 +41,12 @@ export class AiModelRegistryService { this.registerAnthropicModels(); } + const xaiApiKey = this.twentyConfigService.get('XAI_API_KEY'); + + if (xaiApiKey) { + this.registerXaiModels(); + } + const openaiCompatibleBaseUrl = this.twentyConfigService.get( 'OPENAI_COMPATIBLE_BASE_URL', ); @@ -83,6 +90,20 @@ export class AiModelRegistryService { }); } + private registerXaiModels(): void { + const xaiModels = AI_MODELS.filter( + (model) => model.provider === ModelProvider.XAI, + ); + + xaiModels.forEach((modelConfig) => { + this.modelRegistry.set(modelConfig.modelId, { + modelId: modelConfig.modelId, + provider: ModelProvider.XAI, + model: xai(modelConfig.modelId), + }); + }); + } + private registerOpenAICompatibleModels( baseUrl: string, modelNamesString: string, @@ -206,6 +227,9 @@ export class AiModelRegistryService { case ModelProvider.ANTHROPIC: apiKey = this.twentyConfigService.get('ANTHROPIC_API_KEY'); break; + case ModelProvider.XAI: + apiKey = this.twentyConfigService.get('XAI_API_KEY'); + break; case ModelProvider.OPENAI_COMPATIBLE: apiKey = this.twentyConfigService.get('OPENAI_COMPATIBLE_API_KEY'); break; diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts index 58eb5bff61e..42383ba32c3 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts @@ -1053,6 +1053,15 @@ export class ConfigVariables { @IsOptional() OPENAI_COMPATIBLE_API_KEY: string; + @ConfigVariablesMetadata({ + group: ConfigVariablesGroup.LLM, + isSensitive: true, + description: 'API key for xAI integration', + type: ConfigVariableType.STRING, + }) + @IsOptional() + XAI_API_KEY: string; + @ConfigVariablesMetadata({ group: ConfigVariablesGroup.ServerConfig, description: 'Enable or disable multi-workspace support', diff --git a/yarn.lock b/yarn.lock index 5b30e4992c9..2210a50bbd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36,6 +36,18 @@ __metadata: languageName: node linkType: hard +"@ai-sdk/openai-compatible@npm:0.2.13": + version: 0.2.13 + resolution: "@ai-sdk/openai-compatible@npm:0.2.13" + dependencies: + "@ai-sdk/provider": "npm:1.1.3" + "@ai-sdk/provider-utils": "npm:2.2.7" + peerDependencies: + zod: ^3.0.0 + checksum: 10c0/d06d46c319f5b846339aa3ba6144e27791fba4eaa7ba47a758cec52b0c9e578fae61a2ab5cc1f52adaf010a36b3c9309ef46c8ba5de77bea01d15d2d4eb7c27a + languageName: node + linkType: hard + "@ai-sdk/openai@npm:^1.3.22": version: 1.3.22 resolution: "@ai-sdk/openai@npm:1.3.22" @@ -48,6 +60,19 @@ __metadata: languageName: node linkType: hard +"@ai-sdk/provider-utils@npm:2.2.7": + version: 2.2.7 + resolution: "@ai-sdk/provider-utils@npm:2.2.7" + dependencies: + "@ai-sdk/provider": "npm:1.1.3" + nanoid: "npm:^3.3.8" + secure-json-parse: "npm:^2.7.0" + peerDependencies: + zod: ^3.23.8 + checksum: 10c0/e89a4e03be59df56bfb15e25e761955ffabd39b350527dd3e27da89c35332d1db6eeffc596d2aa3e18a2f5535d79e8ddc4ad7066d6f05f490f7d10082f427f00 + languageName: node + linkType: hard + "@ai-sdk/provider-utils@npm:2.2.8": version: 2.2.8 resolution: "@ai-sdk/provider-utils@npm:2.2.8" @@ -101,6 +126,19 @@ __metadata: languageName: node linkType: hard +"@ai-sdk/xai@npm:1.2.15": + version: 1.2.15 + resolution: "@ai-sdk/xai@npm:1.2.15" + dependencies: + "@ai-sdk/openai-compatible": "npm:0.2.13" + "@ai-sdk/provider": "npm:1.1.3" + "@ai-sdk/provider-utils": "npm:2.2.7" + peerDependencies: + zod: ^3.0.0 + checksum: 10c0/60bd4af83dd90e4b6b3a4149633c77bcfb11e976013f86fd3383f8f23f5fb3afa1d790607fefc34d6a33cfd80fd9680b5b01366a6016070792ab8055b204a55c + languageName: node + linkType: hard + "@algolia/autocomplete-core@npm:1.9.3": version: 1.9.3 resolution: "@algolia/autocomplete-core@npm:1.9.3" @@ -51712,6 +51750,7 @@ __metadata: dependencies: "@ai-sdk/anthropic": "npm:^1.2.12" "@ai-sdk/openai": "npm:^1.3.22" + "@ai-sdk/xai": "npm:1.2.15" "@aws-sdk/client-lambda": "npm:3.825.0" "@aws-sdk/client-s3": "npm:3.825.0" "@aws-sdk/client-sts": "npm:3.825.0"