Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/docs/content/docs/platform/costs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ The settings page groups keys into **Models**, **Search & web**, and **Enrichmen
| Together AI | Model calls |
| Baseten | Model calls |
| Nebius | Model calls |
| Runcrate | Model calls |
| Ollama Cloud | Model calls |
| Kie | Model calls |
| Thinking Machines | Model calls |
Expand Down
6 changes: 6 additions & 0 deletions apps/docs/content/docs/workflows/blocks/agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ Select a model beginning with `nebius/` to run an open model through Nebius Toke

Bring your own Nebius Token Factory API key. On Sim Cloud, a workspace admin can add it under **Workspace settings → BYOK → Nebius**. On self-hosted Sim, provide the key in the Agent block's **API Key** field, using a [secret reference](/platform/credentials). Model features such as image input and structured outputs depend on the selected model.

#### Runcrate

Select a model beginning with `runcrate/` to run an open model through the Runcrate inference API, an OpenAI-compatible endpoint. Sim includes DeepSeek, Gemma, Llama, MiniMax, Qwen, and GLM models in the catalog.

Bring your own Runcrate API key. On Sim Cloud, a workspace admin can add it under **Workspace settings → BYOK → Runcrate**. On self-hosted Sim, provide the key in the Agent block's **API Key** field, using a [secret reference](/platform/credentials). File attachments are not sent. A forced tool choice is only sent to models that honor it; other models use automatic tool choice.

#### Thinking Machines

Select a model beginning with `thinkingmachines/` to run Inkling, Inkling-Small, or Inkling 2 Small through Tinker serverless inference. To sample a checkpoint you trained on Tinker, enter `thinkingmachines/` followed by its `tinker://` path. Custom checkpoints have no catalog pricing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
ZeroBounceIcon,
} from '@/components/icons'
import { NebiusIcon } from '@/components/icons/nebius'
import { RuncrateIcon } from '@/components/icons/runcrate'
import { ThinkingMachinesIcon } from '@/components/icons/thinking-machines'
import { canMutateWorkspaceSettingsSection } from '@/components/settings/navigation'
import { type BYOKProviderId, MAX_BYOK_KEYS_PER_PROVIDER } from '@/lib/api/contracts/byok-keys'
Expand Down Expand Up @@ -183,6 +184,13 @@ const PROVIDERS: (BYOKManagerProvider & { id: BYOKProviderId })[] = [
description: 'LLM calls',
placeholder: 'Enter your Kie API key',
},
{
id: 'runcrate',
name: 'Runcrate',
icon: RuncrateIcon,
description: 'LLM calls',
placeholder: 'Enter your Runcrate API key',
},
{
id: 'thinkingmachines',
name: 'Thinking Machines',
Expand Down Expand Up @@ -391,6 +399,7 @@ const PROVIDER_SECTIONS: BYOKProviderSection[] = [
'nebius',
'ollama-cloud',
'kie',
'runcrate',
'thinkingmachines',
'falai',
],
Expand Down
20 changes: 20 additions & 0 deletions apps/sim/components/icons/runcrate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { SVGProps } from 'react'

interface RuncrateIconProps extends SVGProps<SVGSVGElement> {}

/** Runcrate logo mark; accepts standard SVG props. */
export function RuncrateIcon(props: RuncrateIconProps) {
return (
<svg {...props} viewBox='0 0 64 64' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect width='64' height='64' rx='14' fill='#1C2E7F' />
<path
d='M18 20l14 12-14 12'
stroke='#5B8CFF'
strokeWidth='5'
strokeLinecap='round'
strokeLinejoin='round'
/>
<path d='M36 46h12' stroke='#5B8CFF' strokeWidth='5' strokeLinecap='round' />
</svg>
)
}
2 changes: 2 additions & 0 deletions apps/sim/ee/organization-usage/components/usage-consumers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
ZaiIcon,
} from '@/components/icons'
import { NebiusIcon } from '@/components/icons/nebius'
import { RuncrateIcon } from '@/components/icons/runcrate'
import { ThinkingMachinesIcon } from '@/components/icons/thinking-machines'
import type {
OrganizationUsageBreakdown,
Expand Down Expand Up @@ -90,6 +91,7 @@ const PROVIDER_ICONS: Readonly<Record<string, ComponentType<{ className?: string
'ollama-cloud': OllamaIcon,
openai: OpenAIIcon,
openrouter: OpenRouterIcon,
runcrate: RuncrateIcon,
sakana: SakanaIcon,
thinkingmachines: ThinkingMachinesIcon,
together: TogetherIcon,
Expand Down
37 changes: 37 additions & 0 deletions apps/sim/lib/api-key/byok.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,43 @@ describe('getApiKeyWithBYOK for Nebius', () => {
})
})

describe('getApiKeyWithBYOK for Runcrate', () => {
beforeEach(() => {
resetDbChainMock()
mockDecryptSecret.mockImplementation(async (encrypted: string) => ({
decrypted: encrypted.replace('encrypted-', 'decrypted-'),
}))
})

it('uses the configured workspace key instead of a caller key', async () => {
dbChainMockFns.orderBy.mockResolvedValueOnce([storedKey('runcrate-workspace')])
await expect(
getApiKeyWithBYOK(
'runcrate',
'runcrate/Qwen/Qwen3-235B-A22B-Instruct-2507',
uniqueWorkspaceId(),
'caller-key'
)
).resolves.toEqual({
apiKey: 'decrypted-runcrate-workspace',
isBYOK: true,
scope: 'workspace',
})
})

it('falls back to the caller key when no workspace key is configured', async () => {
dbChainMockFns.orderBy.mockResolvedValueOnce([])
await expect(
getApiKeyWithBYOK(
'runcrate',
'runcrate/Qwen/Qwen3-235B-A22B-Instruct-2507',
uniqueWorkspaceId(),
'caller-key'
)
).resolves.toEqual({ apiKey: 'caller-key', isBYOK: false })
})
})

describe('BYOK lookup retry lifetime', () => {
beforeEach(() => {
vi.useFakeTimers()
Expand Down
7 changes: 7 additions & 0 deletions apps/sim/lib/api-key/byok.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,13 @@ export async function getApiKeyWithBYOK(
throw new Error(`API key is required for Nebius ${model}`)
}

if (provider === 'runcrate') {
const byokResult = await getBYOKKey(workspaceId, 'runcrate', { signal })
if (byokResult) return byokResult
if (userProvidedKey) return { apiKey: userProvidedKey, isBYOK: false }
throw new Error(`API key is required for Runcrate ${model}`)
}

if (provider === 'thinkingmachines') {
const byokResult = await getBYOKKey(workspaceId, 'thinkingmachines', { signal })
if (byokResult) {
Expand Down
1 change: 1 addition & 0 deletions apps/sim/lib/api/contracts/byok-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const byokProviderIdSchema = z.enum([
'nebius',
'ollama-cloud',
'kie',
'runcrate',
'thinkingmachines',
'falai',
'firecrawl',
Expand Down
5 changes: 5 additions & 0 deletions apps/sim/providers/attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type AttachmentProvider =
| 'zai'
| 'kimi'
| 'thinkingmachines'
| 'runcrate'

export interface PreparedProviderAttachment {
file: UserFile
Expand Down Expand Up @@ -163,6 +164,7 @@ const UNSUPPORTED_FILE_PROVIDERS = new Set<AttachmentProvider>([
'meta',
'zai',
'thinkingmachines',
'runcrate',
])

const PROVIDER_SUPPORTED_LABELS: Record<AttachmentProvider, string> = {
Expand All @@ -189,6 +191,7 @@ const PROVIDER_SUPPORTED_LABELS: Record<AttachmentProvider, string> = {
zai: 'no file attachments in the current API adapter',
kimi: 'images through image_url message parts on multimodal models',
thinkingmachines: 'no file attachments in the current API adapter',
runcrate: 'no file attachments in the current API adapter',
}

export function getAttachmentProvider(providerId: ProviderId | string): AttachmentProvider | null {
Expand All @@ -215,6 +218,7 @@ export function getAttachmentProvider(providerId: ProviderId | string): Attachme
if (providerId === 'zai') return 'zai'
if (providerId === 'kimi') return 'kimi'
if (providerId === 'thinkingmachines') return 'thinkingmachines'
if (providerId === 'runcrate') return 'runcrate'
return null
}

Expand Down Expand Up @@ -432,6 +436,7 @@ function isMimeTypeSupportedByProvider(
case 'meta':
case 'zai':
case 'thinkingmachines':
case 'runcrate':
return false
default: {
const _exhaustive: never = provider
Expand Down
1 change: 1 addition & 0 deletions apps/sim/providers/history-adapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export const providerHistoryProtocols: Record<
baseten: 'chat-completions',
nebius: 'chat-completions',
thinkingmachines: 'chat-completions',
runcrate: 'chat-completions',
vllm: 'chat-completions',
litellm: 'chat-completions',
}
Expand Down
75 changes: 75 additions & 0 deletions apps/sim/providers/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
ZaiIcon,
} from '@/components/icons'
import { NebiusIcon } from '@/components/icons/nebius'
import { RuncrateIcon } from '@/components/icons/runcrate'
import { ThinkingMachinesIcon } from '@/components/icons/thinking-machines'
import { LARGE_VALUE_THRESHOLD_BYTES } from '@/lib/execution/payloads/large-value-ref'
import type { ModelPricing, ProviderId } from '@/providers/types'
Expand Down Expand Up @@ -442,6 +443,80 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
},
],
},
runcrate: {
id: 'runcrate',
name: 'Runcrate',
description: 'Open models via the Runcrate inference API',
defaultModel: 'runcrate/Qwen/Qwen3-235B-A22B-Instruct-2507',
modelPatterns: [/^runcrate\//],
icon: RuncrateIcon,
color: '#1C2E7F',
isReseller: true,
capabilities: {
temperature: { min: 0, max: 2 },
toolUsageControl: true,
},
models: [
{
id: 'runcrate/deepseek-ai/DeepSeek-V3.2',
pricing: { input: 0.273, cachedInput: 0.1365, output: 0.399, updatedAt: '2026-11-08' },
capabilities: { toolUsageControl: true, nativeStructuredOutputs: true },
contextWindow: 163840,
},
{
id: 'runcrate/google/gemma-3-27b-it',
pricing: { input: 0.084, cachedInput: 0.084, output: 0.168, updatedAt: '2026-11-08' },
capabilities: { toolUsageControl: true, nativeStructuredOutputs: true },
contextWindow: 131072,
},
{
id: 'runcrate/meta-llama/Llama-3.3-70B-Instruct-Turbo',
pricing: { input: 0.105, cachedInput: 0.105, output: 0.336, updatedAt: '2026-11-08' },
capabilities: {
toolUsageControl: true,
forcedToolUse: false,
nativeStructuredOutputs: true,
},
contextWindow: 131072,
},
{
id: 'runcrate/MiniMaxAI/MiniMax-M3',
pricing: { input: 0.294, cachedInput: 0.0588, output: 1.155, updatedAt: '2026-11-08' },
capabilities: {
toolUsageControl: true,
forcedToolUse: false,
nativeStructuredOutputs: true,
},
contextWindow: 524288,
},
{
id: 'runcrate/Qwen/Qwen3-235B-A22B-Instruct-2507',
pricing: { input: 0.0945, cachedInput: 0.0945, output: 0.5775, updatedAt: '2026-11-08' },
capabilities: {
toolUsageControl: true,
forcedToolUse: false,
nativeStructuredOutputs: true,
},
contextWindow: 262144,
},
{
id: 'runcrate/Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo',
pricing: { input: 0.315, cachedInput: 0.105, output: 1.05, updatedAt: '2026-11-08' },
capabilities: {
toolUsageControl: true,
forcedToolUse: false,
nativeStructuredOutputs: true,
},
contextWindow: 262144,
},
{
id: 'runcrate/zai-org/GLM-5.3',
pricing: { input: 0.590625, cachedInput: 0.13125, output: 2.625, updatedAt: '2026-11-08' },
capabilities: { toolUsageControl: true, nativeStructuredOutputs: true },
contextWindow: 1048576,
},
],
},
thinkingmachines: {
id: 'thinkingmachines',
name: 'Thinking Machines',
Expand Down
9 changes: 8 additions & 1 deletion apps/sim/providers/openai-compat/chat-completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,14 @@ export type ChatCompletionPayload = ChatCompletionCreateParamsNonStreaming & {
}

interface ChatCompletionExecutionConfig {
providerId: 'baseten' | 'together' | 'fireworks' | 'openrouter' | 'nebius' | 'thinkingmachines'
providerId:
| 'baseten'
| 'together'
| 'fireworks'
| 'openrouter'
| 'nebius'
| 'thinkingmachines'
| 'runcrate'
providerName: string
client: OpenAI
requestedModel: string
Expand Down
2 changes: 2 additions & 0 deletions apps/sim/providers/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ollamaProvider } from '@/providers/ollama'
import { ollamaCloudProvider } from '@/providers/ollama-cloud'
import { openaiProvider } from '@/providers/openai'
import { openRouterProvider } from '@/providers/openrouter'
import { runcrateProvider } from '@/providers/runcrate'
import { sakanaProvider } from '@/providers/sakana'
import { thinkingMachinesProvider } from '@/providers/thinkingmachines'
import { togetherProvider } from '@/providers/together'
Expand Down Expand Up @@ -58,6 +59,7 @@ const providerRegistry: Record<ProviderId, ProviderConfig> = {
together: togetherProvider,
baseten: basetenProvider,
nebius: nebiusProvider,
runcrate: runcrateProvider,
thinkingmachines: thinkingMachinesProvider,
ollama: ollamaProvider,
'ollama-cloud': ollamaCloudProvider,
Expand Down
Loading