Skip to content

Commit aa5ebee

Browse files
committed
feat(providers): add Sakana AI provider with Fugu models
OpenAI-compatible provider at https://api.sakana.ai/v1 (bearer auth). Registers fugu (fast default) and fugu-ultra (reasoning flagship), both 1M context. BYOK-only, never hosted/auto-billed. Streaming, tool loop, and response_format supported; attachments mirror deepseek (unsupported in the current adapter).
1 parent d8da1e2 commit aa5ebee

10 files changed

Lines changed: 658 additions & 1 deletion

File tree

apps/sim/components/icons.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3439,6 +3439,16 @@ export const DeepseekIcon = (props: SVGProps<SVGSVGElement>) => (
34393439
</svg>
34403440
)
34413441

3442+
export const SakanaIcon = (props: SVGProps<SVGSVGElement>) => (
3443+
<svg {...props} height='1em' viewBox='152 5 38 30' width='1em' xmlns='http://www.w3.org/2000/svg'>
3444+
<title>Sakana AI</title>
3445+
<path
3446+
d='m187.2 7.8-2.5-0.7c-6.3-1.8-12.7-1.2-18 1.5l-10.2 5.7c-1.2 0.7-0.2 2.5 1 1.8l7.6-4.4c0.8 1.7 1.5 4 1.1 7.7-1.4-0.3-6-1.4-10.9 1.5-0.6 0.3-0.8 1.1-0.3 1.7 0.5 0.5 1.2 0.3 1.3 0.2 2.2-1.3 5.6-2.4 9.6-1.4-0.7 2.5-2.5 5.6-6 7.8-1.5 0.7-0.4 2.3 0.7 1.8 1.8-1 5.3-3.4 6.9-9 2.1 0.9 4.2 2.4 5.9 4.6l-7.2 4.1c-1.2 0.6-0.3 2.4 1.1 1.7l9-5c4.6-2.7 8.3-7.5 10.1-13.1l1.3-5.3c0.4-0.4 0-1.1-0.5-1.2zm-11.5 17.5-0.6 0.4c-2-2.6-4.5-4.7-7.5-5.7 0.5-3.8-0.3-6.8-1.2-9.1l1.1-0.6c4.8-2 9.8-2.7 16.2-0.9l1.6 0.4-0.8 2.7c-1.5 4.9-4.5 9.6-8.8 12.8z'
3447+
fill='#E60000'
3448+
/>
3449+
</svg>
3450+
)
3451+
34423452
export function GeminiIcon(props: SVGProps<SVGSVGElement>) {
34433453
const id = useId()
34443454
const gradientId = `gemini_gradient_${id}`

apps/sim/lib/tokenization/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export const TOKENIZATION_CONFIG = {
5656
confidence: 'medium',
5757
supportedMethods: ['heuristic', 'fallback'],
5858
},
59+
sakana: {
60+
avgCharsPerToken: 4,
61+
confidence: 'medium',
62+
supportedMethods: ['heuristic', 'fallback'],
63+
},
5964
ollama: {
6065
avgCharsPerToken: 4,
6166
confidence: 'low',

apps/sim/providers/attachments.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type AttachmentProvider =
3535
| 'xai'
3636
| 'deepseek'
3737
| 'cerebras'
38+
| 'sakana'
3839

3940
export interface PreparedProviderAttachment {
4041
file: UserFile
@@ -118,7 +119,7 @@ const BEDROCK_DOCUMENT_FORMATS = new Set([
118119
const BEDROCK_IMAGE_FORMATS = new Set(['png', 'jpeg', 'jpg', 'gif', 'webp'])
119120
const BEDROCK_VIDEO_FORMATS = new Set(['mp4', 'mov', 'mkv', 'webm'])
120121

121-
const UNSUPPORTED_FILE_PROVIDERS = new Set<AttachmentProvider>(['deepseek', 'cerebras'])
122+
const UNSUPPORTED_FILE_PROVIDERS = new Set<AttachmentProvider>(['deepseek', 'cerebras', 'sakana'])
122123

123124
const PROVIDER_SUPPORTED_LABELS: Record<AttachmentProvider, string> = {
124125
openai: 'images and documents through the Responses API input_image/input_file parts',
@@ -137,6 +138,7 @@ const PROVIDER_SUPPORTED_LABELS: Record<AttachmentProvider, string> = {
137138
xai: 'images through image_url message parts on Grok vision models',
138139
deepseek: 'no file attachments in the current API adapter',
139140
cerebras: 'no file attachments in the current API adapter',
141+
sakana: 'no file attachments in the current API adapter',
140142
}
141143

142144
export function getAttachmentProvider(providerId: ProviderId | string): AttachmentProvider | null {
@@ -156,6 +158,7 @@ export function getAttachmentProvider(providerId: ProviderId | string): Attachme
156158
if (providerId === 'xai') return 'xai'
157159
if (providerId === 'deepseek') return 'deepseek'
158160
if (providerId === 'cerebras') return 'cerebras'
161+
if (providerId === 'sakana') return 'sakana'
159162
return null
160163
}
161164

@@ -303,6 +306,7 @@ function isMimeTypeSupportedByProvider(
303306
return isImageMimeType(mimeType)
304307
case 'deepseek':
305308
case 'cerebras':
309+
case 'sakana':
306310
return false
307311
default: {
308312
const _exhaustive: never = provider

apps/sim/providers/models.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,35 @@ describe('orderModelIdsByReleaseDate', () => {
102102
expect([...ordered].sort()).toEqual([...input].sort())
103103
})
104104
})
105+
106+
describe('sakana provider definition', () => {
107+
const sakana = PROVIDER_DEFINITIONS.sakana
108+
109+
it('is registered with fugu as the default model', () => {
110+
expect(sakana).toBeDefined()
111+
expect(sakana.id).toBe('sakana')
112+
expect(sakana.defaultModel).toBe('fugu')
113+
expect(sakana.modelPatterns).toEqual([/^fugu/])
114+
})
115+
116+
it('exposes fugu and fugu-ultra with a 1M context window', () => {
117+
expect(sakana.models.map((m) => m.id)).toEqual(['fugu', 'fugu-ultra'])
118+
for (const model of sakana.models) {
119+
expect(model.contextWindow).toBe(1000000)
120+
}
121+
})
122+
123+
it('prices both models at the documented fugu-ultra ceiling', () => {
124+
for (const model of sakana.models) {
125+
expect(model.pricing.input).toBe(5)
126+
expect(model.pricing.output).toBe(30)
127+
expect(model.pricing.cachedInput).toBe(0.5)
128+
}
129+
})
130+
131+
it('routes bare fugu model IDs to the sakana provider', () => {
132+
const baseModels = getBaseModelProviders()
133+
expect(baseModels.fugu).toBe('sakana')
134+
expect(baseModels['fugu-ultra']).toBe('sakana')
135+
})
136+
})

apps/sim/providers/models.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
OllamaIcon,
2424
OpenAIIcon,
2525
OpenRouterIcon,
26+
SakanaIcon,
2627
TogetherIcon,
2728
VertexIcon,
2829
VllmIcon,
@@ -2197,6 +2198,47 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
21972198
},
21982199
],
21992200
},
2201+
sakana: {
2202+
id: 'sakana',
2203+
name: 'Sakana AI',
2204+
description: "Sakana AI's Fugu multi-agent models via an OpenAI-compatible API",
2205+
defaultModel: 'fugu',
2206+
modelPatterns: [/^fugu/],
2207+
icon: SakanaIcon,
2208+
color: '#E60000',
2209+
capabilities: {
2210+
temperature: { min: 0, max: 2 },
2211+
toolUsageControl: true,
2212+
},
2213+
models: [
2214+
{
2215+
id: 'fugu',
2216+
pricing: {
2217+
input: 5,
2218+
cachedInput: 0.5,
2219+
output: 30,
2220+
updatedAt: '2026-06-22',
2221+
},
2222+
capabilities: {},
2223+
contextWindow: 1000000,
2224+
releaseDate: '2026-06-15',
2225+
speedOptimized: true,
2226+
},
2227+
{
2228+
id: 'fugu-ultra',
2229+
pricing: {
2230+
input: 5,
2231+
cachedInput: 0.5,
2232+
output: 30,
2233+
updatedAt: '2026-06-22',
2234+
},
2235+
capabilities: {},
2236+
contextWindow: 1000000,
2237+
releaseDate: '2026-06-15',
2238+
recommended: true,
2239+
},
2240+
],
2241+
},
22002242
mistral: {
22012243
id: 'mistral',
22022244
name: 'Mistral AI',

apps/sim/providers/registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ollamaProvider } from '@/providers/ollama'
1616
import { ollamaCloudProvider } from '@/providers/ollama-cloud'
1717
import { openaiProvider } from '@/providers/openai'
1818
import { openRouterProvider } from '@/providers/openrouter'
19+
import { sakanaProvider } from '@/providers/sakana'
1920
import { togetherProvider } from '@/providers/together'
2021
import type { ProviderConfig, ProviderId } from '@/providers/types'
2122
import { vertexProvider } from '@/providers/vertex'
@@ -34,6 +35,7 @@ const providerRegistry: Record<ProviderId, ProviderConfig> = {
3435
xai: xAIProvider,
3536
cerebras: cerebrasProvider,
3637
groq: groqProvider,
38+
sakana: sakanaProvider,
3739
vllm: vllmProvider,
3840
litellm: litellmProvider,
3941
mistral: mistralProvider,

0 commit comments

Comments
 (0)