Skip to content

Commit efadbea

Browse files
committed
feat(providers): add Prism BYOK support
1 parent 3bdc279 commit efadbea

22 files changed

Lines changed: 813 additions & 3 deletions

File tree

apps/docs/components/icons.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5392,6 +5392,22 @@ export function BasetenIcon(props: SVGProps<SVGSVGElement>) {
53925392
)
53935393
}
53945394

5395+
export function PrismIcon(props: SVGProps<SVGSVGElement>) {
5396+
return (
5397+
<svg
5398+
{...props}
5399+
fill='currentColor'
5400+
height='1em'
5401+
width='1em'
5402+
viewBox='0 0 24 24'
5403+
xmlns='http://www.w3.org/2000/svg'
5404+
>
5405+
<title>Prism</title>
5406+
<path d='M0 23.61 11.96.17l3.85 8.18L5.3 23.61H0zm12.95 0 3.74-14.04 7.23 14.04H12.95z' />
5407+
</svg>
5408+
)
5409+
}
5410+
53955411
export function CohereIcon(props: SVGProps<SVGSVGElement>) {
53965412
return (
53975413
<svg {...props} height='1em' width='1em' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>

apps/docs/content/docs/platform/costs.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ The BYOK settings page groups providers the same way.
179179
| Fireworks | LLM calls |
180180
| Together AI | LLM calls |
181181
| Baseten | LLM calls |
182+
| Prism | Open-source model inference (BYOK only) |
182183
| Ollama Cloud | LLM calls |
183184
| Fal.ai | Image and video generation |
184185
</Tab>

apps/docs/content/docs/workflows/blocks/agent.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,11 @@ While an agent runs, Sim can stream its thinking and tool lifecycle live — the
104104

105105
{/* agent-stream-capabilities:begin — generated by `bun run agent-stream-docs:generate`; do not edit between markers */}
106106

107-
Live tool-call chips stream for **OpenAI, Anthropic, Azure Anthropic, Google, Vertex AI, DeepSeek, Groq, AWS Bedrock** models. Other providers run tools without live chips and project the settled final answer when the run completes; they do not ask the model to regenerate that answer just to create a stream.
107+
Live tool-call chips stream for **Prism, OpenAI, Anthropic, Azure Anthropic, Google, Vertex AI, DeepSeek, Groq, AWS Bedrock** models. Other providers run tools without live chips and project the settled final answer when the run completes; they do not ask the model to regenerate that answer just to create a stream.
108108

109109
| Provider | Streamed thinking | Models |
110110
|----------|-------------------|--------|
111+
| Prism | Full thinking deltas | `prism/deepseek-v4.1-flash`, `prism/deepseek-v4-flash` |
111112
| OpenAI | Summaries only — Requires OpenAI organization verification; falls back to no summaries. | `gpt-6-astra`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5-pro`, `gpt-5.5`, `gpt-5.4-pro`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, `gpt-5.2-pro`, `gpt-5.2`, `gpt-5.1`, `gpt-5-pro`, `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `o4-mini`, `o3`, `o3-mini`, `o1` |
112113
| Anthropic | Summaries only — These generations omit full thinking; Sim requests summarized thinking on streaming runs. | `claude-fable-5-1`, `claude-fable-5`, `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-opus-4-5`, `claude-sonnet-4-5`, `claude-haiku-4-5` |
113114
| Azure OpenAI | Summaries only — Requires OpenAI organization verification; falls back to no summaries. | `azure/gpt-5.4`, `azure/gpt-5.4-mini`, `azure/gpt-5.4-nano`, `azure/gpt-5.2`, `azure/gpt-5.1`, `azure/gpt-5.1-codex`, `azure/gpt-5`, `azure/gpt-5-mini`, `azure/gpt-5-nano`, `azure/o3`, `azure/o4-mini` |

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
ParallelIcon,
3636
PeopleDataLabsIcon,
3737
PerplexityIcon,
38+
PrismIcon,
3839
ProspeoIcon,
3940
SerperIcon,
4041
TinyFishIcon,
@@ -151,6 +152,13 @@ const PROVIDERS: (BYOKManagerProvider & { id: BYOKProviderId })[] = [
151152
description: 'LLM calls',
152153
placeholder: 'Enter your Baseten API key',
153154
},
155+
{
156+
id: 'prism',
157+
name: 'Prism',
158+
icon: PrismIcon,
159+
description: 'Open-source model inference',
160+
placeholder: 'prism_sk_...',
161+
},
154162
{
155163
id: 'ollama-cloud',
156164
name: 'Ollama Cloud',
@@ -355,6 +363,7 @@ const PROVIDER_SECTIONS: BYOKProviderSection[] = [
355363
'fireworks',
356364
'together',
357365
'baseten',
366+
'prism',
358367
'ollama-cloud',
359368
'falai',
360369
],

apps/sim/components/icons.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5392,6 +5392,22 @@ export function BasetenIcon(props: SVGProps<SVGSVGElement>) {
53925392
)
53935393
}
53945394

5395+
export function PrismIcon(props: SVGProps<SVGSVGElement>) {
5396+
return (
5397+
<svg
5398+
{...props}
5399+
fill='currentColor'
5400+
height='1em'
5401+
width='1em'
5402+
viewBox='0 0 24 24'
5403+
xmlns='http://www.w3.org/2000/svg'
5404+
>
5405+
<title>Prism</title>
5406+
<path d='M0 23.61 11.96.17l3.85 8.18L5.3 23.61H0zm12.95 0 3.74-14.04 7.23 14.04H12.95z' />
5407+
</svg>
5408+
)
5409+
}
5410+
53955411
export function CohereIcon(props: SVGProps<SVGSVGElement>) {
53965412
return (
53975413
<svg {...props} height='1em' width='1em' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>

apps/sim/ee/organization-usage/components/usage-consumers.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
OllamaIcon,
2323
OpenAIIcon,
2424
OpenRouterIcon,
25+
PrismIcon,
2526
SakanaIcon,
2627
TogetherIcon,
2728
VertexIcon,
@@ -73,6 +74,7 @@ const PROVIDER_ICONS: Readonly<Record<string, ComponentType<{ className?: string
7374
'ollama-cloud': OllamaIcon,
7475
openai: OpenAIIcon,
7576
openrouter: OpenRouterIcon,
77+
prism: PrismIcon,
7678
sakana: SakanaIcon,
7779
together: TogetherIcon,
7880
vertex: VertexIcon,

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,3 +520,52 @@ describe('getApiKeyWithBYOK for Fireworks', () => {
520520
expect(result).toEqual({ apiKey: 'platform-fireworks-key', isBYOK: false })
521521
})
522522
})
523+
524+
describe('getApiKeyWithBYOK for Prism', () => {
525+
const model = 'prism/deepseek-v4-flash'
526+
527+
beforeEach(() => {
528+
vi.clearAllMocks()
529+
resetDbChainMock()
530+
mockDecryptSecret.mockImplementation(async (encrypted: string) => ({
531+
decrypted: encrypted.replace('encrypted-', 'decrypted-'),
532+
}))
533+
mockIsOrganizationBYOKEntitled.mockResolvedValue(true)
534+
mockEnv.PRISM_API_KEY = 'must-not-be-used'
535+
;(useProvidersStore.getState as ReturnType<typeof vi.fn>).mockReturnValue({
536+
providers: {
537+
ollama: { models: [] },
538+
vllm: { models: [] },
539+
litellm: { models: [] },
540+
},
541+
})
542+
})
543+
544+
it('prefers a stored BYOK key over an inline key', async () => {
545+
dbChainMockFns.orderBy.mockResolvedValue([storedKey('prism-key')])
546+
547+
await expect(
548+
getApiKeyWithBYOK('prism', model, uniqueWorkspaceId(), 'inline-key')
549+
).resolves.toEqual({
550+
apiKey: 'decrypted-prism-key',
551+
isBYOK: true,
552+
scope: 'workspace',
553+
})
554+
})
555+
556+
it('uses an inline key only when no stored key exists', async () => {
557+
await expect(
558+
getApiKeyWithBYOK('prism', model, uniqueWorkspaceId(), 'inline-key')
559+
).resolves.toEqual({
560+
apiKey: 'inline-key',
561+
isBYOK: false,
562+
})
563+
})
564+
565+
it('never falls back to an environment or platform key', async () => {
566+
await expect(getApiKeyWithBYOK('prism', model, uniqueWorkspaceId())).rejects.toThrow(
567+
'API key is required for Prism'
568+
)
569+
expect(mockGetRotatingApiKey).not.toHaveBeenCalled()
570+
})
571+
})

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,20 @@ export async function getApiKeyWithBYOK(
211211
return { apiKey: userProvidedKey || env.LITELLM_API_KEY || 'empty', isBYOK: false }
212212
}
213213

214+
if (provider === 'prism') {
215+
if (workspaceId) {
216+
const byokResult = await getBYOKKey(workspaceId, 'prism')
217+
if (byokResult) {
218+
logger.info('Using BYOK key for Prism', { model, workspaceId, scope: byokResult.scope })
219+
return byokResult
220+
}
221+
}
222+
if (userProvidedKey) {
223+
return { apiKey: userProvidedKey, isBYOK: false }
224+
}
225+
throw new Error(`API key is required for Prism ${model}`)
226+
}
227+
214228
const isFireworksModel =
215229
provider === 'fireworks' ||
216230
useProvidersStore.getState().providers.fireworks.models.includes(model)

apps/sim/lib/api/contracts/byok-keys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const byokProviderIdSchema = z.enum([
1313
'fireworks',
1414
'together',
1515
'baseten',
16+
'prism',
1617
'ollama-cloud',
1718
'falai',
1819
'firecrawl',

apps/sim/providers/attachments.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,11 +355,11 @@ describe('provider attachments', () => {
355355
).toThrow('Supported attachments: images')
356356
})
357357

358-
it('rejects providers without file attachment support', () => {
358+
it.each(['deepseek', 'prism'])('rejects file attachments for %s', (provider) => {
359359
expect(() =>
360360
formatMessagesForProvider(
361361
[{ role: 'user', content: 'Analyze this file', files: [imageFile] }],
362-
'deepseek'
362+
provider
363363
)
364364
).toThrow('not supported')
365365
})

0 commit comments

Comments
 (0)