Skip to content

Commit 4affc19

Browse files
committed
feat(providers): add Fireworks BYOK support and official icon
1 parent 15e4553 commit 4affc19

File tree

9 files changed

+81
-18
lines changed

9 files changed

+81
-18
lines changed

apps/sim/.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ API_ENCRYPTION_KEY=your_api_encryption_key # Use `openssl rand -hex 32` to gener
2828
# OLLAMA_URL=http://localhost:11434 # URL for local Ollama server - uncomment if using local models
2929
# VLLM_BASE_URL=http://localhost:8000 # Base URL for your self-hosted vLLM (OpenAI-compatible)
3030
# VLLM_API_KEY= # Optional bearer token if your vLLM instance requires auth
31-
# FIREWORKS_API_KEY= # Optional Fireworks AI API key for dynamic model listing
31+
# FIREWORKS_API_KEY= # Optional Fireworks AI API key for model listing
3232

3333
# Admin API (Optional - for self-hosted GitOps)
3434
# ADMIN_API_KEY= # Use `openssl rand -hex 32` to generate. Enables admin API for workflow export/import.

apps/sim/app/api/providers/fireworks/models/route.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
3+
import { getBYOKKey } from '@/lib/api-key/byok'
4+
import { getSession } from '@/lib/auth'
35
import { env } from '@/lib/core/config/env'
46
import { filterBlacklistedModels, isProviderBlacklisted } from '@/providers/utils'
57

@@ -17,15 +19,31 @@ interface FireworksModelsResponse {
1719
object?: string
1820
}
1921

20-
export async function GET(_request: NextRequest) {
22+
export async function GET(request: NextRequest) {
2123
if (isProviderBlacklisted('fireworks')) {
2224
logger.info('Fireworks provider is blacklisted, returning empty models')
2325
return NextResponse.json({ models: [] })
2426
}
2527

26-
const apiKey = env.FIREWORKS_API_KEY
28+
let apiKey: string | undefined
29+
30+
const workspaceId = request.nextUrl.searchParams.get('workspaceId')
31+
if (workspaceId) {
32+
const session = await getSession()
33+
if (session?.user?.id) {
34+
const byokResult = await getBYOKKey(workspaceId, 'fireworks')
35+
if (byokResult) {
36+
apiKey = byokResult.apiKey
37+
}
38+
}
39+
}
40+
41+
if (!apiKey) {
42+
apiKey = env.FIREWORKS_API_KEY
43+
}
44+
2745
if (!apiKey) {
28-
logger.info('No FIREWORKS_API_KEY configured, returning empty models')
46+
logger.info('No Fireworks API key available, returning empty models')
2947
return NextResponse.json({ models: [] })
3048
}
3149

apps/sim/app/api/workspaces/[id]/byok-keys/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const VALID_PROVIDERS = [
1818
'anthropic',
1919
'google',
2020
'mistral',
21+
'fireworks',
2122
'firecrawl',
2223
'exa',
2324
'serper',

apps/sim/app/workspace/[workspaceId]/providers/provider-models-loader.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useEffect } from 'react'
44
import { createLogger } from '@sim/logger'
5+
import { useParams } from 'next/navigation'
56
import { useProviderModels } from '@/hooks/queries/providers'
67
import {
78
updateFireworksProviderModels,
@@ -13,11 +14,11 @@ import { type ProviderName, useProvidersStore } from '@/stores/providers'
1314

1415
const logger = createLogger('ProviderModelsLoader')
1516

16-
function useSyncProvider(provider: ProviderName) {
17+
function useSyncProvider(provider: ProviderName, workspaceId?: string) {
1718
const setProviderModels = useProvidersStore((state) => state.setProviderModels)
1819
const setProviderLoading = useProvidersStore((state) => state.setProviderLoading)
1920
const setOpenRouterModelInfo = useProvidersStore((state) => state.setOpenRouterModelInfo)
20-
const { data, isLoading, isFetching, error } = useProviderModels(provider)
21+
const { data, isLoading, isFetching, error } = useProviderModels(provider, workspaceId)
2122

2223
useEffect(() => {
2324
setProviderLoading(provider, isLoading || isFetching)
@@ -54,10 +55,13 @@ function useSyncProvider(provider: ProviderName) {
5455
}
5556

5657
export function ProviderModelsLoader() {
58+
const params = useParams()
59+
const workspaceId = params?.workspaceId as string | undefined
60+
5761
useSyncProvider('base')
5862
useSyncProvider('ollama')
5963
useSyncProvider('vllm')
6064
useSyncProvider('openrouter')
61-
useSyncProvider('fireworks')
65+
useSyncProvider('fireworks', workspaceId)
6266
return null
6367
}

apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
BrandfetchIcon,
1919
ExaAIIcon,
2020
FirecrawlIcon,
21+
FireworksIcon,
2122
GeminiIcon,
2223
GoogleIcon,
2324
JinaAIIcon,
@@ -75,6 +76,13 @@ const PROVIDERS: {
7576
description: 'LLM calls and Knowledge Base OCR',
7677
placeholder: 'Enter your API key',
7778
},
79+
{
80+
id: 'fireworks',
81+
name: 'Fireworks',
82+
icon: FireworksIcon,
83+
description: 'LLM calls',
84+
placeholder: 'Enter your Fireworks API key',
85+
},
7886
{
7987
id: 'firecrawl',
8088
name: 'Firecrawl',

apps/sim/components/icons.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3424,13 +3424,17 @@ export function FireworksIcon(props: SVGProps<SVGSVGElement>) {
34243424
return (
34253425
<svg
34263426
{...props}
3427-
fill='currentColor'
3428-
height='1em'
3429-
viewBox='0 0 24 24'
3430-
width='1em'
3427+
viewBox='0 0 512 512'
34313428
xmlns='http://www.w3.org/2000/svg'
3429+
fillRule='evenodd'
3430+
clipRule='evenodd'
3431+
strokeLinejoin='round'
3432+
strokeMiterlimit={2}
34323433
>
3433-
<path d='M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.894 7.37l-2.453 4.248a.5.5 0 01-.433.25h-2.516l-1.226 2.124h3.742a.5.5 0 01.433.75l-2.453 4.248a.5.5 0 01-.866 0L9.67 14.742a.5.5 0 01.433-.75h2.516l1.226-2.124H10.1a.5.5 0 01-.433-.75l2.453-4.248a.5.5 0 01.866 0l2.453 4.248a.5.5 0 01-.433.75h-.516l1.97-3.412a.5.5 0 01.866 0l.568.914z' />
3434+
<path
3435+
d='M314.333 110.167L255.98 251.729l-58.416-141.562h-37.459l64 154.75c5.23 12.854 17.771 21.312 31.646 21.312s26.417-8.437 31.646-21.27l64.396-154.792h-37.459zm24.917 215.666L446 216.583l-14.562-34.77-116.584 119.562c-9.708 9.958-12.541 24.833-7.146 37.646 5.292 12.73 17.792 21.083 31.584 21.083l.042.063L506 359.75l-14.562-34.77-152.146.853h-.042zM66 216.5l14.563-34.77 116.583 119.562a34.592 34.592 0 017.146 37.646C199 351.667 186.5 360.02 172.708 360.02l-166.666-.375-.042.042 14.563-34.771 152.145.875L66 216.5z'
3436+
fill='currentColor'
3437+
/>
34343438
</svg>
34353439
)
34363440
}

apps/sim/hooks/queries/providers.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,21 @@ interface ProviderModelsResponse {
1919

2020
export const providerKeys = {
2121
all: ['provider-models'] as const,
22-
models: (provider: string) => [...providerKeys.all, provider] as const,
22+
models: (provider: string, workspaceId?: string) =>
23+
[...providerKeys.all, provider, workspaceId ?? ''] as const,
2324
}
2425

2526
async function fetchProviderModels(
2627
provider: ProviderName,
27-
signal?: AbortSignal
28+
signal?: AbortSignal,
29+
workspaceId?: string
2830
): Promise<ProviderModelsResponse> {
29-
const response = await fetch(providerEndpoints[provider], { signal })
31+
let url = providerEndpoints[provider]
32+
if (provider === 'fireworks' && workspaceId) {
33+
url = `${url}?workspaceId=${encodeURIComponent(workspaceId)}`
34+
}
35+
36+
const response = await fetch(url, { signal })
3037

3138
if (!response.ok) {
3239
logger.warn(`Failed to fetch ${provider} models`, {
@@ -46,10 +53,10 @@ async function fetchProviderModels(
4653
}
4754
}
4855

49-
export function useProviderModels(provider: ProviderName) {
56+
export function useProviderModels(provider: ProviderName, workspaceId?: string) {
5057
return useQuery({
51-
queryKey: providerKeys.models(provider),
52-
queryFn: ({ signal }) => fetchProviderModels(provider, signal),
58+
queryKey: providerKeys.models(provider, workspaceId),
59+
queryFn: ({ signal }) => fetchProviderModels(provider, signal, workspaceId),
5360
staleTime: 5 * 60 * 1000,
5461
})
5562
}

apps/sim/lib/api-key/byok.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,26 @@ export async function getApiKeyWithBYOK(
7373
return { apiKey: userProvidedKey || env.VLLM_API_KEY || 'empty', isBYOK: false }
7474
}
7575

76+
const isFireworksModel =
77+
provider === 'fireworks' ||
78+
useProvidersStore.getState().providers.fireworks.models.includes(model)
79+
if (isFireworksModel) {
80+
if (workspaceId) {
81+
const byokResult = await getBYOKKey(workspaceId, 'fireworks')
82+
if (byokResult) {
83+
logger.info('Using BYOK key for Fireworks', { model, workspaceId })
84+
return byokResult
85+
}
86+
}
87+
if (userProvidedKey) {
88+
return { apiKey: userProvidedKey, isBYOK: false }
89+
}
90+
if (env.FIREWORKS_API_KEY) {
91+
return { apiKey: env.FIREWORKS_API_KEY, isBYOK: false }
92+
}
93+
throw new Error(`API key is required for Fireworks ${model}`)
94+
}
95+
7696
const isBedrockModel = provider === 'bedrock' || model.startsWith('bedrock/')
7797
if (isBedrockModel) {
7898
return { apiKey: 'bedrock-uses-own-credentials', isBYOK: false }

apps/sim/tools/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type BYOKProviderId =
66
| 'anthropic'
77
| 'google'
88
| 'mistral'
9+
| 'fireworks'
910
| 'firecrawl'
1011
| 'exa'
1112
| 'serper'

0 commit comments

Comments
 (0)