diff --git a/Dockerfile b/Dockerfile index 8142403b6e1b..747b0d25ec4a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -117,6 +117,8 @@ ENV \ BAICHUAN_API_KEY="" \ # DeepSeek DEEPSEEK_API_KEY="" \ + # Fireworks AI + FIREWORKSAI_API_KEY="" FIREWORKSAI_MODEL_LIST="" \ # Google GOOGLE_API_KEY="" GOOGLE_PROXY_URL="" \ # Groq diff --git a/Dockerfile.database b/Dockerfile.database index 33ee0ea08f3e..24af49ca37d9 100644 --- a/Dockerfile.database +++ b/Dockerfile.database @@ -149,6 +149,8 @@ ENV \ BAICHUAN_API_KEY="" \ # DeepSeek DEEPSEEK_API_KEY="" \ + # Fireworks AI + FIREWORKSAI_API_KEY="" FIREWORKSAI_MODEL_LIST="" \ # Google GOOGLE_API_KEY="" GOOGLE_PROXY_URL="" \ # Groq diff --git a/src/app/(main)/settings/llm/ProviderList/providers.tsx b/src/app/(main)/settings/llm/ProviderList/providers.tsx index 645c7d336796..232114be5715 100644 --- a/src/app/(main)/settings/llm/ProviderList/providers.tsx +++ b/src/app/(main)/settings/llm/ProviderList/providers.tsx @@ -5,6 +5,7 @@ import { AnthropicProviderCard, BaichuanProviderCard, DeepSeekProviderCard, + FireworksAIProviderCard, GoogleProviderCard, GroqProviderCard, MinimaxProviderCard, @@ -47,6 +48,7 @@ export const useProviderList = (): ProviderItem[] => { OpenRouterProviderCard, NovitaProviderCard, TogetherAIProviderCard, + FireworksAIProviderCard, QwenProviderCard, DeepSeekProviderCard, MinimaxProviderCard, diff --git a/src/app/api/chat/agentRuntime.ts b/src/app/api/chat/agentRuntime.ts index 05571c96ef0e..489d9dedc80d 100644 --- a/src/app/api/chat/agentRuntime.ts +++ b/src/app/api/chat/agentRuntime.ts @@ -151,6 +151,13 @@ const getLlmOptionsFromPayload = (provider: string, payload: JWTPayload) => { return { apiKey }; } + case ModelProvider.FireworksAI: { + const { FIREWORKSAI_API_KEY } = getLLMConfig(); + + const apiKey = apiKeyManager.pick(payload?.apiKey || FIREWORKSAI_API_KEY); + + return { apiKey }; + } case ModelProvider.ZeroOne: { const { ZEROONE_API_KEY } = getLLMConfig(); diff --git a/src/config/llm.ts b/src/config/llm.ts index 43f54ef2827a..b5d19d363dd3 100644 --- a/src/config/llm.ts +++ b/src/config/llm.ts @@ -64,6 +64,10 @@ export const getLLMConfig = () => { TOGETHERAI_API_KEY: z.string().optional(), TOGETHERAI_MODEL_LIST: z.string().optional(), + ENABLED_FIREWORKSAI: z.boolean(), + FIREWORKSAI_API_KEY: z.string().optional(), + FIREWORKSAI_MODEL_LIST: z.string().optional(), + ENABLED_AWS_BEDROCK: z.boolean(), AWS_BEDROCK_MODEL_LIST: z.string().optional(), AWS_REGION: z.string().optional(), @@ -149,6 +153,10 @@ export const getLLMConfig = () => { TOGETHERAI_API_KEY: process.env.TOGETHERAI_API_KEY, TOGETHERAI_MODEL_LIST: process.env.TOGETHERAI_MODEL_LIST, + ENABLED_FIREWORKSAI: !!process.env.FIREWORKSAI_API_KEY, + FIREWORKSAI_API_KEY: process.env.FIREWORKSAI_API_KEY, + FIREWORKSAI_MODEL_LIST: process.env.FIREWORKSAI_MODEL_LIST, + ENABLED_MOONSHOT: !!process.env.MOONSHOT_API_KEY, MOONSHOT_API_KEY: process.env.MOONSHOT_API_KEY, MOONSHOT_PROXY_URL: process.env.MOONSHOT_PROXY_URL, diff --git a/src/config/modelProviders/fireworksai.ts b/src/config/modelProviders/fireworksai.ts new file mode 100644 index 000000000000..d34cdff7177c --- /dev/null +++ b/src/config/modelProviders/fireworksai.ts @@ -0,0 +1,143 @@ +import { ModelProviderCard } from '@/types/llm'; + +// ref https://fireworks.ai/models?show=Serverless +// ref https://fireworks.ai/pricing +const FireworksAI: ModelProviderCard = { + chatModels: [ + { + description: 'Fireworks latest and most performant function-calling model. Firefunction-v2 is based on Llama-3 and trained to excel at function-calling as well as chat and instruction-following. See blog post for more details https://fireworks.ai/blog/firefunction-v2-launch-post', + displayName: 'Firefunction V2', + enabled: true, + functionCall: true, + id: 'accounts/fireworks/models/firefunction-v2', + tokens: 8192, + }, + { + description: 'Fireworks open-source function calling model.', + displayName: 'Firefunction V1', + functionCall: true, + id: 'accounts/fireworks/models/firefunction-v1', + tokens: 32_768, + }, + { + description: 'Vision-language model allowing both image and text as inputs (single image is recommended), trained on OSS model generated training data and open sourced on huggingface at fireworks-ai/FireLLaVA-13b', + displayName: 'FireLLaVA-13B', + enabled: true, + functionCall: false, + id: 'accounts/fireworks/models/firellava-13b', + tokens: 4096, + vision: true, + }, + { + displayName: 'Llama 3.1 8B Instruct', + enabled: true, + functionCall: false, + id: 'accounts/fireworks/models/llama-v3p1-8b-instruct', + tokens: 131_072, + }, + { + displayName: 'Llama 3.1 70B Instruct', + enabled: true, + functionCall: false, + id: 'accounts/fireworks/models/llama-v3p1-70b-instruct', + tokens: 131_072, + }, + { + displayName: 'Llama 3.1 405B Instruct', + enabled: true, + functionCall: false, + id: 'accounts/fireworks/models/llama-v3p1-405b-instruct', + tokens: 131_072, + }, + { + displayName: 'Llama 3 8B Instruct', + functionCall: false, + id: 'accounts/fireworks/models/llama-v3-8b-instruct', + tokens: 8192, + }, + { + displayName: 'Llama 3 70B Instruct', + functionCall: false, + id: 'accounts/fireworks/models/llama-v3-70b-instruct', + tokens: 8192, + }, + { + displayName: 'Llama 3 8B Instruct (HF version)', + functionCall: false, + id: 'accounts/fireworks/models/llama-v3-8b-instruct-hf', + tokens: 8192, + }, + { + displayName: 'Llama 3 70B Instruct (HF version)', + functionCall: false, + id: 'accounts/fireworks/models/llama-v3-70b-instruct-hf', + tokens: 8192, + }, + { + displayName: 'Gemma 2 9B Instruct', + enabled: true, + functionCall: false, + id: 'accounts/fireworks/models/gemma2-9b-it', + tokens: 8192, + }, + { + displayName: 'Mixtral MoE 8x7B Instruct', + enabled: true, + functionCall: false, + id: 'accounts/fireworks/models/mixtral-8x7b-instruct', + tokens: 32_768, + }, + { + displayName: 'Mixtral MoE 8x22B Instruct', + enabled: true, + functionCall: false, + id: 'accounts/fireworks/models/mixtral-8x22b-instruct', + tokens: 65_536, + }, + { + displayName: 'Mixtral MoE 8x7B Instruct (HF version)', + functionCall: false, + id: 'accounts/fireworks/models/mixtral-8x7b-instruct-hf', + tokens: 32_768, + }, + { + displayName: 'Phi 3 Vision Instruct', + enabled: true, + functionCall: false, + id: 'accounts/fireworks/models/phi-3-vision-128k-instruct', + tokens: 8192, + vision: true, + }, + { + displayName: 'Yi-Large', + enabled: true, + functionCall: false, + id: 'accounts/yi-01-ai/models/yi-large', + tokens: 32_768, + }, + { + displayName: 'StarCoder 7B', + functionCall: false, + id: 'accounts/fireworks/models/starcoder-7b', + tokens: 8192, + }, + { + displayName: 'StarCoder 15.5B', + functionCall: false, + id: 'accounts/fireworks/models/starcoder-16b', + tokens: 8192, + }, + { + displayName: 'MythoMax L2 13b', + functionCall: false, + id: 'accounts/fireworks/models/mythomax-l2-13b', + tokens: 4096, + }, + ], + checkModel: 'accounts/fireworks/models/firefunction-v2', + id: 'fireworksai', + modelList: { showModelFetcher: true }, + name: 'Fireworks AI', +}; + +export default FireworksAI; diff --git a/src/config/modelProviders/index.ts b/src/config/modelProviders/index.ts index f2f382f17b9b..44fc48a40c81 100644 --- a/src/config/modelProviders/index.ts +++ b/src/config/modelProviders/index.ts @@ -6,6 +6,7 @@ import AzureProvider from './azure'; import BaichuanProvider from './baichuan'; import BedrockProvider from './bedrock'; import DeepSeekProvider from './deepseek'; +import FireworksAIProvider from './fireworksai'; import GoogleProvider from './google'; import GroqProvider from './groq'; import MinimaxProvider from './minimax'; @@ -39,6 +40,7 @@ export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [ OllamaProvider.chatModels, OpenRouterProvider.chatModels, TogetherAIProvider.chatModels, + FireworksAIProvider.chatModels, PerplexityProvider.chatModels, AnthropicProvider.chatModels, ZeroOneProvider.chatModels, @@ -61,6 +63,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [ GoogleProvider, OpenRouterProvider, TogetherAIProvider, + FireworksAIProvider, BedrockProvider, PerplexityProvider, MinimaxProvider, @@ -93,6 +96,7 @@ export { default as AzureProviderCard } from './azure'; export { default as BaichuanProviderCard } from './baichuan'; export { default as BedrockProviderCard } from './bedrock'; export { default as DeepSeekProviderCard } from './deepseek'; +export { default as FireworksAIProviderCard } from './fireworksai'; export { default as GoogleProviderCard } from './google'; export { default as GroqProviderCard } from './groq'; export { default as MinimaxProviderCard } from './minimax'; diff --git a/src/const/settings/llm.ts b/src/const/settings/llm.ts index 6056265a074f..7d4513565f06 100644 --- a/src/const/settings/llm.ts +++ b/src/const/settings/llm.ts @@ -4,6 +4,7 @@ import { BaichuanProviderCard, BedrockProviderCard, DeepSeekProviderCard, + FireworksAIProviderCard, GoogleProviderCard, GroqProviderCard, MinimaxProviderCard, @@ -51,6 +52,10 @@ export const DEFAULT_LLM_CONFIG: UserModelProviderConfig = { enabled: false, enabledModels: filterEnabledModels(DeepSeekProviderCard), }, + fireworksai: { + enabled: false, + enabledModels: filterEnabledModels(FireworksAIProviderCard), + }, google: { enabled: false, enabledModels: filterEnabledModels(GoogleProviderCard), diff --git a/src/libs/agent-runtime/AgentRuntime.ts b/src/libs/agent-runtime/AgentRuntime.ts index 0454e3bbde0a..f6f15d42d8a0 100644 --- a/src/libs/agent-runtime/AgentRuntime.ts +++ b/src/libs/agent-runtime/AgentRuntime.ts @@ -9,6 +9,7 @@ import { LobeAzureOpenAI } from './azureOpenai'; import { LobeBaichuanAI } from './baichuan'; import { LobeBedrockAI, LobeBedrockAIParams } from './bedrock'; import { LobeDeepSeekAI } from './deepseek'; +import { LobeFireworksAI } from './fireworksai'; import { LobeGoogleAI } from './google'; import { LobeGroq } from './groq'; import { LobeMinimaxAI } from './minimax'; @@ -120,6 +121,7 @@ class AgentRuntime { baichuan: Partial; bedrock: Partial; deepseek: Partial; + fireworksai: Partial; google: { apiKey?: string; baseURL?: string }; groq: Partial; minimax: Partial; @@ -224,6 +226,11 @@ class AgentRuntime { break; } + case ModelProvider.FireworksAI: { + runtimeModel = new LobeFireworksAI(params.fireworksai); + break + } + case ModelProvider.ZeroOne: { runtimeModel = new LobeZeroOneAI(params.zeroone); break; diff --git a/src/libs/agent-runtime/fireworksai/index.test.ts b/src/libs/agent-runtime/fireworksai/index.test.ts new file mode 100644 index 000000000000..16c4c4e64065 --- /dev/null +++ b/src/libs/agent-runtime/fireworksai/index.test.ts @@ -0,0 +1,255 @@ +// @vitest-environment node +import OpenAI from 'openai'; +import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { + ChatStreamCallbacks, + LobeOpenAICompatibleRuntime, + ModelProvider, +} from '@/libs/agent-runtime'; + +import * as debugStreamModule from '../utils/debugStream'; +import { LobeFireworksAI } from './index'; + +const provider = ModelProvider.FireworksAI; +const defaultBaseURL = 'https://api.fireworks.ai/inference/v1'; + +const bizErrorType = 'ProviderBizError'; +const invalidErrorType = 'InvalidProviderAPIKey'; + +// Mock the console.error to avoid polluting test output +vi.spyOn(console, 'error').mockImplementation(() => {}); + +let instance: LobeOpenAICompatibleRuntime; + +beforeEach(() => { + instance = new LobeFireworksAI({ apiKey: 'test' }); + + // 使用 vi.spyOn 来模拟 chat.completions.create 方法 + vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue( + new ReadableStream() as any, + ); +}); + +afterEach(() => { + vi.clearAllMocks(); +}); + +describe('LobeFireworksAI', () => { + describe('init', () => { + it('should correctly initialize with an API key', async () => { + const instance = new LobeFireworksAI({ apiKey: 'test_api_key' }); + expect(instance).toBeInstanceOf(LobeFireworksAI); + expect(instance.baseURL).toEqual(defaultBaseURL); + }); + }); + + describe('chat', () => { + describe('Error', () => { + it('should return OpenAIBizError with an openai error response when OpenAI.APIError is thrown', async () => { + // Arrange + const apiError = new OpenAI.APIError( + 400, + { + status: 400, + error: { + message: 'Bad Request', + }, + }, + 'Error message', + {}, + ); + + vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError); + + // Act + try { + await instance.chat({ + messages: [{ content: 'Hello', role: 'user' }], + model: 'accounts/fireworks/models/firefunction-v2', + temperature: 0, + }); + } catch (e) { + expect(e).toEqual({ + endpoint: defaultBaseURL, + error: { + error: { message: 'Bad Request' }, + status: 400, + }, + errorType: bizErrorType, + provider, + }); + } + }); + + it('should throw AgentRuntimeError with NoOpenAIAPIKey if no apiKey is provided', async () => { + try { + new LobeFireworksAI({}); + } catch (e) { + expect(e).toEqual({ errorType: invalidErrorType }); + } + }); + + it('should return OpenAIBizError with the cause when OpenAI.APIError is thrown with cause', async () => { + // Arrange + const errorInfo = { + stack: 'abc', + cause: { + message: 'api is undefined', + }, + }; + const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {}); + + vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError); + + // Act + try { + await instance.chat({ + messages: [{ content: 'Hello', role: 'user' }], + model: 'accounts/fireworks/models/firefunction-v2', + temperature: 0, + }); + } catch (e) { + expect(e).toEqual({ + endpoint: defaultBaseURL, + error: { + cause: { message: 'api is undefined' }, + stack: 'abc', + }, + errorType: bizErrorType, + provider, + }); + } + }); + + it('should return OpenAIBizError with an cause response with desensitize Url', async () => { + // Arrange + const errorInfo = { + stack: 'abc', + cause: { message: 'api is undefined' }, + }; + const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {}); + + instance = new LobeFireworksAI({ + apiKey: 'test', + + baseURL: 'https://api.abc.com/v1', + }); + + vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError); + + // Act + try { + await instance.chat({ + messages: [{ content: 'Hello', role: 'user' }], + model: 'accounts/fireworks/models/firefunction-v2', + temperature: 0, + }); + } catch (e) { + expect(e).toEqual({ + endpoint: 'https://api.***.com/v1', + error: { + cause: { message: 'api is undefined' }, + stack: 'abc', + }, + errorType: bizErrorType, + provider, + }); + } + }); + + it('should throw an InvalidFireworksAIAPIKey error type on 401 status code', async () => { + // Mock the API call to simulate a 401 error + const error = new Error('Unauthorized') as any; + error.status = 401; + vi.mocked(instance['client'].chat.completions.create).mockRejectedValue(error); + + try { + await instance.chat({ + messages: [{ content: 'Hello', role: 'user' }], + model: 'accounts/fireworks/models/firefunction-v2', + temperature: 0, + }); + } catch (e) { + // Expect the chat method to throw an error with InvalidFireworksAIAPIKey + expect(e).toEqual({ + endpoint: defaultBaseURL, + error: new Error('Unauthorized'), + errorType: invalidErrorType, + provider, + }); + } + }); + + it('should return AgentRuntimeError for non-OpenAI errors', async () => { + // Arrange + const genericError = new Error('Generic Error'); + + vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(genericError); + + // Act + try { + await instance.chat({ + messages: [{ content: 'Hello', role: 'user' }], + model: 'accounts/fireworks/models/firefunction-v2', + temperature: 0, + }); + } catch (e) { + expect(e).toEqual({ + endpoint: defaultBaseURL, + errorType: 'AgentRuntimeError', + provider, + error: { + name: genericError.name, + cause: genericError.cause, + message: genericError.message, + stack: genericError.stack, + }, + }); + } + }); + }); + + describe('DEBUG', () => { + it('should call debugStream and return StreamingTextResponse when DEBUG_FIREWORKSAI_CHAT_COMPLETION is 1', async () => { + // Arrange + const mockProdStream = new ReadableStream() as any; // 模拟的 prod 流 + const mockDebugStream = new ReadableStream({ + start(controller) { + controller.enqueue('Debug stream content'); + controller.close(); + }, + }) as any; + mockDebugStream.toReadableStream = () => mockDebugStream; // 添加 toReadableStream 方法 + + // 模拟 chat.completions.create 返回值,包括模拟的 tee 方法 + (instance['client'].chat.completions.create as Mock).mockResolvedValue({ + tee: () => [mockProdStream, { toReadableStream: () => mockDebugStream }], + }); + + // 保存原始环境变量值 + const originalDebugValue = process.env.DEBUG_FIREWORKSAI_CHAT_COMPLETION; + + // 模拟环境变量 + process.env.DEBUG_FIREWORKSAI_CHAT_COMPLETION = '1'; + vi.spyOn(debugStreamModule, 'debugStream').mockImplementation(() => Promise.resolve()); + + // 执行测试 + // 运行你的测试函数,确保它会在条件满足时调用 debugStream + // 假设的测试函数调用,你可能需要根据实际情况调整 + await instance.chat({ + messages: [{ content: 'Hello', role: 'user' }], + model: 'accounts/fireworks/models/firefunction-v2', + stream: true, + temperature: 0, + }); + + // 验证 debugStream 被调用 + expect(debugStreamModule.debugStream).toHaveBeenCalled(); + + // 恢复原始环境变量值 + process.env.DEBUG_FIREWORKSAI_CHAT_COMPLETION = originalDebugValue; + }); + }); + }); +}); diff --git a/src/libs/agent-runtime/fireworksai/index.ts b/src/libs/agent-runtime/fireworksai/index.ts new file mode 100644 index 000000000000..3f3fe872d8fb --- /dev/null +++ b/src/libs/agent-runtime/fireworksai/index.ts @@ -0,0 +1,10 @@ +import { ModelProvider } from '../types'; +import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory'; + +export const LobeFireworksAI = LobeOpenAICompatibleFactory({ + baseURL: 'https://api.fireworks.ai/inference/v1', + debug: { + chatCompletion: () => process.env.DEBUG_FIREWORKSAI_CHAT_COMPLETION === '1', + }, + provider: ModelProvider.FireworksAI, +}); diff --git a/src/libs/agent-runtime/types/type.ts b/src/libs/agent-runtime/types/type.ts index 8c0999f9c120..11af2942570a 100644 --- a/src/libs/agent-runtime/types/type.ts +++ b/src/libs/agent-runtime/types/type.ts @@ -28,6 +28,7 @@ export enum ModelProvider { Baichuan = 'baichuan', Bedrock = 'bedrock', DeepSeek = 'deepseek', + FireworksAI = 'fireworksai', Google = 'google', Groq = 'groq', Minimax = 'minimax', diff --git a/src/libs/agent-runtime/utils/streams/openai.test.ts b/src/libs/agent-runtime/utils/streams/openai.test.ts index 84ef678d5133..4c0a70c6015f 100644 --- a/src/libs/agent-runtime/utils/streams/openai.test.ts +++ b/src/libs/agent-runtime/utils/streams/openai.test.ts @@ -287,7 +287,7 @@ describe('OpenAIStream', () => { expect(chunks).toEqual([ 'id: 2\n', 'event: tool_calls\n', - `data: [{"function":{"name":"tool1","arguments":"{}"},"id":"call_1","index":0,"type":"function"},{"function":{"name":"tool2","arguments":"{}"},"id":"call_2","index":1,"type":"function"}]\n\n`, + `data: [{"function":{"arguments":"{}","name":"tool1"},"id":"call_1","index":0,"type":"function"},{"function":{"arguments":"{}","name":"tool2"},"id":"call_2","index":1,"type":"function"}]\n\n`, ]); expect(onToolCallMock).toHaveBeenCalledTimes(1); @@ -334,7 +334,7 @@ describe('OpenAIStream', () => { expect(chunks).toEqual([ 'id: 5\n', 'event: tool_calls\n', - `data: [{"function":{"name":"tool1","arguments":"{}"},"id":"call_1","index":0,"type":"function"},{"function":{"name":"tool2","arguments":"{}"},"id":"call_2","index":1,"type":"function"}]\n\n`, + `data: [{"function":{"arguments":"{}","name":"tool1"},"id":"call_1","index":0,"type":"function"},{"function":{"arguments":"{}","name":"tool2"},"id":"call_2","index":1,"type":"function"}]\n\n`, ]); }); @@ -428,7 +428,7 @@ describe('OpenAIStream', () => { `data: [{"function":{"arguments":"","name":"realtime-weather____fetchCurrentWeather"},"id":"toolu_01VQtK4W9kqxGGLHgsPPxiBj","index":0,"type":"function"}]\n`, 'id: 1', 'event: tool_calls', - `data: [{"function":{"arguments":"{\\"city\\": \\"杭州\\"}"},"id":"toolu_01VQtK4W9kqxGGLHgsPPxiBj","index":0,"type":"function"}]\n`, + `data: [{"function":{"arguments":"{\\"city\\": \\"杭州\\"}","name":null},"id":"toolu_01VQtK4W9kqxGGLHgsPPxiBj","index":0,"type":"function"}]\n`, 'id: 1', 'event: stop', `data: "tool_calls"\n`, diff --git a/src/libs/agent-runtime/utils/streams/openai.ts b/src/libs/agent-runtime/utils/streams/openai.ts index b695a9ff2da5..84b68f43e96f 100644 --- a/src/libs/agent-runtime/utils/streams/openai.ts +++ b/src/libs/agent-runtime/utils/streams/openai.ts @@ -35,7 +35,10 @@ export const transformOpenAIStream = ( } return { - function: value.function, + function: { + arguments: value.function?.arguments ?? '{}', + name: value.function?.name ?? null, + }, id: value.id || stack?.tool?.id || generateToolCallId(index, value.function?.name), // mistral's tool calling don't have index and function field, it's data like: diff --git a/src/server/globalConfig/index.ts b/src/server/globalConfig/index.ts index baa5f9d04982..30d68fc25407 100644 --- a/src/server/globalConfig/index.ts +++ b/src/server/globalConfig/index.ts @@ -5,6 +5,7 @@ import { langfuseEnv } from '@/config/langfuse'; import { getLLMConfig } from '@/config/llm'; import { BedrockProviderCard, + FireworksAIProviderCard, GroqProviderCard, NovitaProviderCard, OllamaProviderCard, @@ -79,6 +80,9 @@ export const getServerGlobalConfig = () => { ENABLED_TOGETHERAI, TOGETHERAI_MODEL_LIST, + + ENABLED_FIREWORKSAI, + FIREWORKSAI_MODEL_LIST, } = getLLMConfig(); const config: GlobalServerConfig = { @@ -112,6 +116,16 @@ export const getServerGlobalConfig = () => { }), }, deepseek: { enabled: ENABLED_DEEPSEEK }, + + fireworksai: { + enabled: ENABLED_FIREWORKSAI, + enabledModels: extractEnabledModels(FIREWORKSAI_MODEL_LIST), + serverModelCards: transformToChatModelCards({ + defaultChatModels: FireworksAIProviderCard.chatModels, + modelString: FIREWORKSAI_MODEL_LIST, + }), + }, + google: { enabled: ENABLED_GOOGLE }, groq: { enabled: ENABLED_GROQ, diff --git a/src/types/user/settings/keyVaults.ts b/src/types/user/settings/keyVaults.ts index 523a1e8aa0c5..697380264fcb 100644 --- a/src/types/user/settings/keyVaults.ts +++ b/src/types/user/settings/keyVaults.ts @@ -22,6 +22,7 @@ export interface UserKeyVaults { baichuan?: OpenAICompatibleKeyVault; bedrock?: AWSBedrockKeyVault; deepseek?: OpenAICompatibleKeyVault; + fireworksai?: OpenAICompatibleKeyVault; google?: OpenAICompatibleKeyVault; groq?: OpenAICompatibleKeyVault; lobehub?: any;