commit
5d05d3eb5f
83
docs/providers/chutes.md
Normal file
83
docs/providers/chutes.md
Normal file
@ -0,0 +1,83 @@
|
||||
---
|
||||
summary: "Use Chutes AI with Clawdbot"
|
||||
read_when:
|
||||
- You want to use Chutes AI models in Clawdbot
|
||||
- You need to configure Chutes via OAuth or API key
|
||||
---
|
||||
# Chutes AI
|
||||
|
||||
Chutes provides high-performance inference for open-weight models, including GLM 4.7 Flash. Clawdbot supports Chutes via both OAuth and API key authentication.
|
||||
|
||||
Models are fetched dynamically from the Chutes API, ensuring you always have access to the latest models, accurate pricing, and context window limits.
|
||||
|
||||
## CLI setup
|
||||
|
||||
To configure Chutes with an API key:
|
||||
|
||||
```bash
|
||||
clawdbot onboard --auth-choice chutes-api-key
|
||||
# or non-interactive
|
||||
clawdbot onboard --chutes-api-key "$CHUTES_API_KEY"
|
||||
```
|
||||
|
||||
To configure Chutes with OAuth (browser-based):
|
||||
|
||||
```bash
|
||||
clawdbot onboard --auth-choice chutes
|
||||
```
|
||||
|
||||
OAuth allows you to use your Chutes account without manually managing API keys. Clawdbot uses the standard [Sign in with Chutes](https://github.com/chutesai/Sign-in-with-Chutes) flow.
|
||||
|
||||
### OAuth Scopes
|
||||
|
||||
Clawdbot requests the following scopes by default:
|
||||
- `openid` (Required for authentication)
|
||||
- `profile` (Access to username)
|
||||
- `chutes:invoke` (Required to make AI API calls on your behalf)
|
||||
|
||||
### Custom OAuth App (Advanced)
|
||||
|
||||
If you wish to use your own OAuth application instead of the default, set these environment variables before running onboarding:
|
||||
|
||||
- `CHUTES_CLIENT_ID`: Your OAuth client ID
|
||||
- `CHUTES_CLIENT_SECRET`: Your OAuth client secret (if applicable)
|
||||
- `CHUTES_OAUTH_REDIRECT_URI`: Your redirect URI (default: `http://127.0.0.1:1456/oauth-callback`)
|
||||
|
||||
|
||||
## Config snippet
|
||||
|
||||
```json5
|
||||
{
|
||||
env: { CHUTES_API_KEY: "sk-..." },
|
||||
agents: { defaults: { model: { primary: "chutes/zai-org/GLM-4.7-Flash" } } },
|
||||
models: {
|
||||
providers: {
|
||||
chutes: {
|
||||
baseUrl: "https://llm.chutes.ai/v1",
|
||||
api: "openai-completions",
|
||||
apiKey: "${CHUTES_API_KEY}",
|
||||
teeOnly: false // Set to true to filter models by Trusted Execution Environment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Chutes models are available under the `chutes/` provider prefix.
|
||||
- The default model is `chutes/zai-org/GLM-4.7-Flash`.
|
||||
- Chutes uses OpenAI-compatible endpoints.
|
||||
- **Trusted Execution Environment (TEE)**: Models running in a TEE are marked with a "TEE" badge in the model picker. You can filter for these models by setting `teeOnly: true` in your provider config.
|
||||
- Many top models on Chutes support tool calling, including:
|
||||
- `Qwen/Qwen3-235B-A22B-Instruct-2507-TEE` (TEE)
|
||||
- `deepseek-ai/DeepSeek-V3.2-TEE` (TEE)
|
||||
- `chutesai/Mistral-Small-3.1-24B-Instruct-2503`
|
||||
- `NousResearch/Hermes-4-14B`
|
||||
- For a full list of available models, see the [Chutes Models API](https://llm.chutes.ai/v1/models). Popular models include:
|
||||
- `deepseek-ai/DeepSeek-V3.2-TEE`
|
||||
- `Qwen/Qwen3-235B-A22B-Instruct-2507-TEE`
|
||||
- `mistralai/Mistral-Small-24B-Instruct-2501-TEE`
|
||||
- `NousResearch/Hermes-4-14B`
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ See [Venice AI](/providers/venice).
|
||||
- [Vercel AI Gateway](/providers/vercel-ai-gateway)
|
||||
- [Moonshot AI (Kimi + Kimi Code)](/providers/moonshot)
|
||||
- [OpenCode Zen](/providers/opencode)
|
||||
- [Chutes AI](/providers/chutes)
|
||||
- [Amazon Bedrock](/bedrock)
|
||||
- [Z.AI](/providers/zai)
|
||||
- [GLM models](/providers/glm)
|
||||
|
||||
76
src/agents/chutes-models.ts
Normal file
76
src/agents/chutes-models.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import type { ModelDefinitionConfig } from "../config/types.models.js";
|
||||
|
||||
export const CHUTES_BASE_URL = "https://llm.chutes.ai/v1";
|
||||
export const CHUTES_DEFAULT_MODEL_ID = "zai-org/GLM-4.7-Flash";
|
||||
export const CHUTES_DEFAULT_MODEL_REF = `chutes/${CHUTES_DEFAULT_MODEL_ID}`;
|
||||
|
||||
export interface ChutesModelEntry {
|
||||
id: string;
|
||||
name?: string;
|
||||
context_length?: number;
|
||||
max_output_length?: number;
|
||||
confidential_compute?: boolean;
|
||||
pricing?: { prompt: number; completion: number };
|
||||
supported_features?: string[];
|
||||
}
|
||||
|
||||
export async function fetchChutesModels(): Promise<ChutesModelEntry[]> {
|
||||
try {
|
||||
const response = await fetch(`${CHUTES_BASE_URL}/models`, {
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch Chutes models: ${response.statusText}`);
|
||||
}
|
||||
const data = (await response.json()) as { data: ChutesModelEntry[] };
|
||||
return data.data || [];
|
||||
} catch (error) {
|
||||
console.warn(`[chutes-models] Failed to fetch models: ${String(error)}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function mapChutesModelToDefinition(entry: ChutesModelEntry): ModelDefinitionConfig {
|
||||
return {
|
||||
id: entry.id,
|
||||
name: entry.name || entry.id,
|
||||
reasoning: entry.supported_features?.includes("reasoning") ?? false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: entry.pricing?.prompt ?? 0,
|
||||
output: entry.pricing?.completion ?? 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: entry.context_length || 128000,
|
||||
maxTokens: entry.max_output_length || 4096,
|
||||
confidentialCompute: entry.confidential_compute,
|
||||
};
|
||||
}
|
||||
|
||||
export async function discoverChutesModels(opts?: {
|
||||
teeOnly?: boolean;
|
||||
}): Promise<ModelDefinitionConfig[]> {
|
||||
const models = await fetchChutesModels();
|
||||
if (models.length === 0) {
|
||||
// Fallback to minimal list
|
||||
return [
|
||||
{
|
||||
id: CHUTES_DEFAULT_MODEL_ID,
|
||||
name: "GLM 4.7 Flash",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 128000,
|
||||
maxTokens: 4096,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
let filtered = models;
|
||||
if (opts?.teeOnly) {
|
||||
filtered = models.filter((m) => m.confidential_compute === true);
|
||||
}
|
||||
|
||||
return filtered.map(mapChutesModelToDefinition);
|
||||
}
|
||||
@ -278,6 +278,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null {
|
||||
xai: "XAI_API_KEY",
|
||||
openrouter: "OPENROUTER_API_KEY",
|
||||
"vercel-ai-gateway": "AI_GATEWAY_API_KEY",
|
||||
chutes: "CHUTES_API_KEY",
|
||||
moonshot: "MOONSHOT_API_KEY",
|
||||
"kimi-code": "KIMICODE_API_KEY",
|
||||
minimax: "MINIMAX_API_KEY",
|
||||
|
||||
@ -8,6 +8,7 @@ export type ModelCatalogEntry = {
|
||||
provider: string;
|
||||
contextWindow?: number;
|
||||
reasoning?: boolean;
|
||||
confidentialCompute?: boolean;
|
||||
input?: Array<"text" | "image">;
|
||||
};
|
||||
|
||||
@ -17,6 +18,7 @@ type DiscoveredModel = {
|
||||
provider: string;
|
||||
contextWindow?: number;
|
||||
reasoning?: boolean;
|
||||
confidentialCompute?: boolean;
|
||||
input?: Array<"text" | "image">;
|
||||
};
|
||||
|
||||
@ -82,10 +84,12 @@ export async function loadModelCatalog(params?: {
|
||||
? entry.contextWindow
|
||||
: undefined;
|
||||
const reasoning = typeof entry?.reasoning === "boolean" ? entry.reasoning : undefined;
|
||||
const confidentialCompute =
|
||||
typeof entry?.confidentialCompute === "boolean" ? entry.confidentialCompute : undefined;
|
||||
const input = Array.isArray(entry?.input)
|
||||
? (entry.input as Array<"text" | "image">)
|
||||
: undefined;
|
||||
models.push({ id, name, provider, contextWindow, reasoning, input });
|
||||
models.push({ id, name, provider, contextWindow, reasoning, confidentialCompute, input });
|
||||
}
|
||||
|
||||
if (models.length === 0) {
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
import { ensureAuthProfileStore, listProfilesForProvider } from "./auth-profiles.js";
|
||||
import { resolveAwsSdkEnvVarName, resolveEnvApiKey } from "./model-auth.js";
|
||||
import { discoverBedrockModels } from "./bedrock-discovery.js";
|
||||
import { discoverChutesModels } from "./chutes-models.js";
|
||||
import {
|
||||
buildSyntheticModelDefinition,
|
||||
SYNTHETIC_BASE_URL,
|
||||
@ -40,6 +41,9 @@ const MOONSHOT_DEFAULT_COST = {
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
};
|
||||
|
||||
const CHUTES_BASE_URL = "https://llm.chutes.ai/v1";
|
||||
|
||||
const KIMI_CODE_BASE_URL = "https://api.kimi.com/coding/v1";
|
||||
const KIMI_CODE_MODEL_ID = "kimi-for-coding";
|
||||
const KIMI_CODE_CONTEXT_WINDOW = 262144;
|
||||
@ -286,6 +290,16 @@ function buildMoonshotProvider(): ProviderConfig {
|
||||
};
|
||||
}
|
||||
|
||||
async function buildChutesProvider(opts?: { teeOnly?: boolean }): Promise<ProviderConfig> {
|
||||
const models = await discoverChutesModels(opts);
|
||||
return {
|
||||
baseUrl: CHUTES_BASE_URL,
|
||||
api: "openai-completions",
|
||||
models,
|
||||
teeOnly: opts?.teeOnly,
|
||||
};
|
||||
}
|
||||
|
||||
function buildKimiCodeProvider(): ProviderConfig {
|
||||
return {
|
||||
baseUrl: KIMI_CODE_BASE_URL,
|
||||
@ -381,6 +395,13 @@ export async function resolveImplicitProviders(params: {
|
||||
providers.moonshot = { ...buildMoonshotProvider(), apiKey: moonshotKey };
|
||||
}
|
||||
|
||||
const chutesKey =
|
||||
resolveEnvApiKeyVarName("chutes") ??
|
||||
resolveApiKeyFromProfiles({ provider: "chutes", store: authStore });
|
||||
if (chutesKey) {
|
||||
providers.chutes = { ...(await buildChutesProvider()), apiKey: chutesKey };
|
||||
}
|
||||
|
||||
const kimiCodeKey =
|
||||
resolveEnvApiKeyVarName("kimi-code") ??
|
||||
resolveApiKeyFromProfiles({ provider: "kimi-code", store: authStore });
|
||||
|
||||
@ -52,7 +52,7 @@ export function registerOnboardCommand(program: Command) {
|
||||
.option("--mode <mode>", "Wizard mode: local|remote")
|
||||
.option(
|
||||
"--auth-choice <choice>",
|
||||
"Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip",
|
||||
"Auth: setup-token|claude-cli|token|chutes|chutes-api-key|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|codex-cli|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip",
|
||||
)
|
||||
.option(
|
||||
"--token-provider <id>",
|
||||
@ -69,12 +69,13 @@ export function registerOnboardCommand(program: Command) {
|
||||
.option("--openrouter-api-key <key>", "OpenRouter API key")
|
||||
.option("--ai-gateway-api-key <key>", "Vercel AI Gateway API key")
|
||||
.option("--moonshot-api-key <key>", "Moonshot API key")
|
||||
.option("--chutes-api-key <key>", "Chutes API key")
|
||||
.option("--kimi-code-api-key <key>", "Kimi Code API key")
|
||||
.option("--gemini-api-key <key>", "Gemini API key")
|
||||
.option("--zai-api-key <key>", "Z.AI API key")
|
||||
.option("--minimax-api-key <key>", "MiniMax API key")
|
||||
.option("--synthetic-api-key <key>", "Synthetic API key")
|
||||
.option("--venice-api-key <key>", "Venice API key")
|
||||
.option("--venice-api-key <key>", "Venice AI API key")
|
||||
.option("--opencode-zen-api-key <key>", "OpenCode Zen API key")
|
||||
.option("--gateway-port <port>", "Gateway port")
|
||||
.option("--gateway-bind <mode>", "Gateway bind: loopback|tailnet|lan|auto|custom")
|
||||
@ -119,6 +120,7 @@ export function registerOnboardCommand(program: Command) {
|
||||
openrouterApiKey: opts.openrouterApiKey as string | undefined,
|
||||
aiGatewayApiKey: opts.aiGatewayApiKey as string | undefined,
|
||||
moonshotApiKey: opts.moonshotApiKey as string | undefined,
|
||||
chutesApiKey: opts.chutesApiKey as string | undefined,
|
||||
kimiCodeApiKey: opts.kimiCodeApiKey as string | undefined,
|
||||
geminiApiKey: opts.geminiApiKey as string | undefined,
|
||||
zaiApiKey: opts.zaiApiKey as string | undefined,
|
||||
|
||||
@ -16,6 +16,7 @@ export type AuthChoiceGroupId =
|
||||
| "ai-gateway"
|
||||
| "moonshot"
|
||||
| "zai"
|
||||
| "chutes"
|
||||
| "opencode-zen"
|
||||
| "minimax"
|
||||
| "synthetic"
|
||||
@ -101,6 +102,12 @@ const AUTH_CHOICE_GROUP_DEFS: {
|
||||
hint: "Kimi K2 + Kimi Code",
|
||||
choices: ["moonshot-api-key", "kimi-code-api-key"],
|
||||
},
|
||||
{
|
||||
value: "chutes",
|
||||
label: "Chutes",
|
||||
hint: "OAuth + API key",
|
||||
choices: ["chutes", "chutes-api-key"],
|
||||
},
|
||||
{
|
||||
value: "zai",
|
||||
label: "Z.AI (GLM 4.7)",
|
||||
@ -133,6 +140,7 @@ export function buildAuthChoiceOptions(params: {
|
||||
label: "OpenAI Codex (ChatGPT OAuth)",
|
||||
});
|
||||
options.push({ value: "chutes", label: "Chutes (OAuth)" });
|
||||
options.push({ value: "chutes-api-key", label: "Chutes API key" });
|
||||
options.push({ value: "openai-api-key", label: "OpenAI API key" });
|
||||
options.push({ value: "openrouter-api-key", label: "OpenRouter API key" });
|
||||
options.push({
|
||||
|
||||
@ -13,6 +13,8 @@ import {
|
||||
} from "./google-gemini-model-default.js";
|
||||
import {
|
||||
applyAuthProfileConfig,
|
||||
applyChutesConfig,
|
||||
applyChutesProviderConfig,
|
||||
applyKimiCodeConfig,
|
||||
applyKimiCodeProviderConfig,
|
||||
applyMoonshotConfig,
|
||||
@ -34,6 +36,8 @@ import {
|
||||
SYNTHETIC_DEFAULT_MODEL_REF,
|
||||
VENICE_DEFAULT_MODEL_REF,
|
||||
VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF,
|
||||
CHUTES_DEFAULT_MODEL_REF,
|
||||
setChutesApiKey,
|
||||
setGeminiApiKey,
|
||||
setKimiCodeApiKey,
|
||||
setMoonshotApiKey,
|
||||
@ -265,6 +269,53 @@ export async function applyAuthChoiceApiProviders(
|
||||
return { config: nextConfig, agentModelOverride };
|
||||
}
|
||||
|
||||
if (authChoice === "chutes-api-key") {
|
||||
let hasCredential = false;
|
||||
|
||||
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "chutes") {
|
||||
await setChutesApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
|
||||
hasCredential = true;
|
||||
}
|
||||
|
||||
const envKey = resolveEnvApiKey("chutes");
|
||||
if (envKey) {
|
||||
const useExisting = await params.prompter.confirm({
|
||||
message: `Use existing CHUTES_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (useExisting) {
|
||||
await setChutesApiKey(envKey.apiKey, params.agentDir);
|
||||
hasCredential = true;
|
||||
}
|
||||
}
|
||||
if (!hasCredential) {
|
||||
const key = await params.prompter.text({
|
||||
message: "Enter Chutes API key",
|
||||
validate: validateApiKeyInput,
|
||||
});
|
||||
await setChutesApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
||||
}
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: "chutes:default",
|
||||
provider: "chutes",
|
||||
mode: "api_key",
|
||||
});
|
||||
{
|
||||
const applied = await applyDefaultModelChoice({
|
||||
config: nextConfig,
|
||||
setDefaultModel: params.setDefaultModel,
|
||||
defaultModel: CHUTES_DEFAULT_MODEL_REF,
|
||||
applyDefaultConfig: applyChutesConfig,
|
||||
applyProviderConfig: applyChutesProviderConfig,
|
||||
noteAgentModel,
|
||||
prompter: params.prompter,
|
||||
});
|
||||
nextConfig = applied.config;
|
||||
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
||||
}
|
||||
return { config: nextConfig, agentModelOverride };
|
||||
}
|
||||
|
||||
if (authChoice === "kimi-code-api-key") {
|
||||
let hasCredential = false;
|
||||
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "kimi-code") {
|
||||
|
||||
@ -9,6 +9,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
|
||||
"openai-codex": "openai-codex",
|
||||
"codex-cli": "openai-codex",
|
||||
chutes: "chutes",
|
||||
"chutes-api-key": "chutes",
|
||||
"openai-api-key": "openai",
|
||||
"openrouter-api-key": "openrouter",
|
||||
"ai-gateway-api-key": "vercel-ai-gateway",
|
||||
|
||||
@ -205,6 +205,7 @@ export async function promptDefaultModel(
|
||||
name?: string;
|
||||
contextWindow?: number;
|
||||
reasoning?: boolean;
|
||||
confidentialCompute?: boolean;
|
||||
}) => {
|
||||
const key = modelKey(entry.provider, entry.id);
|
||||
if (seen.has(key)) return;
|
||||
@ -214,6 +215,7 @@ export async function promptDefaultModel(
|
||||
if (entry.name && entry.name !== entry.id) hints.push(entry.name);
|
||||
if (entry.contextWindow) hints.push(`ctx ${formatTokenK(entry.contextWindow)}`);
|
||||
if (entry.reasoning) hints.push("reasoning");
|
||||
if (entry.confidentialCompute) hints.push("TEE");
|
||||
const aliases = aliasIndex.byKey.get(key);
|
||||
if (aliases?.length) hints.push(`alias: ${aliases.join(", ")}`);
|
||||
if (!hasAuth(entry.provider)) hints.push("auth missing");
|
||||
@ -341,6 +343,7 @@ export async function promptModelAllowlist(params: {
|
||||
if (entry.name && entry.name !== entry.id) hints.push(entry.name);
|
||||
if (entry.contextWindow) hints.push(`ctx ${formatTokenK(entry.contextWindow)}`);
|
||||
if (entry.reasoning) hints.push("reasoning");
|
||||
if ((entry as any).confidentialCompute) hints.push("TEE");
|
||||
const aliases = aliasIndex.byKey.get(key);
|
||||
if (aliases?.length) hints.push(`alias: ${aliases.join(", ")}`);
|
||||
if (!hasAuth(entry.provider)) hints.push("auth missing");
|
||||
|
||||
@ -80,6 +80,11 @@ export function toModelRow(params: {
|
||||
: (availableKeys?.has(modelKey(model.provider, model.id)) ?? false);
|
||||
const aliasTags = aliases.length > 0 ? [`alias:${aliases.join(",")}`] : [];
|
||||
const mergedTags = new Set(tags);
|
||||
|
||||
if ((model as any).confidentialCompute) {
|
||||
mergedTags.add("TEE");
|
||||
}
|
||||
|
||||
if (aliasTags.length > 0) {
|
||||
for (const tag of mergedTags) {
|
||||
if (tag === "alias" || tag.startsWith("alias:")) mergedTags.delete(tag);
|
||||
|
||||
@ -25,6 +25,9 @@ import {
|
||||
MOONSHOT_BASE_URL,
|
||||
MOONSHOT_DEFAULT_MODEL_ID,
|
||||
MOONSHOT_DEFAULT_MODEL_REF,
|
||||
CHUTES_BASE_URL,
|
||||
CHUTES_DEFAULT_MODEL_REF,
|
||||
buildChutesModelDefinition,
|
||||
} from "./onboard-auth.models.js";
|
||||
|
||||
export function applyZaiConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
@ -202,6 +205,70 @@ export function applyMoonshotConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
};
|
||||
}
|
||||
|
||||
export function applyChutesProviderConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[CHUTES_DEFAULT_MODEL_REF] = {
|
||||
...models[CHUTES_DEFAULT_MODEL_REF],
|
||||
alias: models[CHUTES_DEFAULT_MODEL_REF]?.alias ?? "GLM 4.7 Flash",
|
||||
};
|
||||
|
||||
const providers = { ...cfg.models?.providers };
|
||||
const existingProvider = providers.chutes;
|
||||
|
||||
const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record<
|
||||
string,
|
||||
unknown
|
||||
> as { apiKey?: string; teeOnly?: boolean };
|
||||
const resolvedApiKey = typeof existingApiKey === "string" ? existingApiKey : undefined;
|
||||
const normalizedApiKey = resolvedApiKey?.trim();
|
||||
providers.chutes = {
|
||||
...existingProviderRest,
|
||||
baseUrl: CHUTES_BASE_URL,
|
||||
api: "openai-completions",
|
||||
...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}),
|
||||
// Models will be refreshed dynamically at startup,
|
||||
// but we can pre-populate the default one for onboarding.
|
||||
models: existingProvider?.models || [buildChutesModelDefinition()],
|
||||
};
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
models,
|
||||
},
|
||||
},
|
||||
models: {
|
||||
mode: cfg.models?.mode ?? "merge",
|
||||
providers,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function applyChutesConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const next = applyChutesProviderConfig(cfg);
|
||||
const existingModel = next.agents?.defaults?.model;
|
||||
return {
|
||||
...next,
|
||||
agents: {
|
||||
...next.agents,
|
||||
defaults: {
|
||||
...next.agents?.defaults,
|
||||
model: {
|
||||
...(existingModel && "fallbacks" in (existingModel as Record<string, unknown>)
|
||||
? {
|
||||
fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks,
|
||||
}
|
||||
: undefined),
|
||||
primary: CHUTES_DEFAULT_MODEL_REF,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function applyKimiCodeProviderConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[KIMI_CODE_MODEL_REF] = {
|
||||
|
||||
@ -73,6 +73,19 @@ export async function setMoonshotApiKey(key: string, agentDir?: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function setChutesApiKey(key: string, agentDir?: string) {
|
||||
// Write to resolved agent dir so gateway finds credentials on startup.
|
||||
upsertAuthProfile({
|
||||
profileId: "chutes:default",
|
||||
credential: {
|
||||
type: "api_key",
|
||||
provider: "chutes",
|
||||
key,
|
||||
},
|
||||
agentDir: resolveAuthAgentDir(agentDir),
|
||||
});
|
||||
}
|
||||
|
||||
export async function setKimiCodeApiKey(key: string, agentDir?: string) {
|
||||
// Write to resolved agent dir so gateway finds credentials on startup.
|
||||
upsertAuthProfile({
|
||||
@ -113,6 +126,7 @@ export async function setVeniceApiKey(key: string, agentDir?: string) {
|
||||
}
|
||||
|
||||
export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7";
|
||||
export const CHUTES_DEFAULT_MODEL_REF = "chutes/zai-org/GLM-4.7-Flash";
|
||||
export const OPENROUTER_DEFAULT_MODEL_REF = "openrouter/auto";
|
||||
export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF = "vercel-ai-gateway/anthropic/claude-opus-4.5";
|
||||
|
||||
|
||||
@ -12,6 +12,13 @@ export const MOONSHOT_DEFAULT_MODEL_ID = "kimi-k2-0905-preview";
|
||||
export const MOONSHOT_DEFAULT_MODEL_REF = `moonshot/${MOONSHOT_DEFAULT_MODEL_ID}`;
|
||||
export const MOONSHOT_DEFAULT_CONTEXT_WINDOW = 256000;
|
||||
export const MOONSHOT_DEFAULT_MAX_TOKENS = 8192;
|
||||
|
||||
export const CHUTES_BASE_URL = "https://llm.chutes.ai/v1";
|
||||
export const CHUTES_DEFAULT_MODEL_ID = "zai-org/GLM-4.7-Flash";
|
||||
export const CHUTES_DEFAULT_MODEL_REF = `chutes/${CHUTES_DEFAULT_MODEL_ID}`;
|
||||
export const CHUTES_DEFAULT_CONTEXT_WINDOW = 128000;
|
||||
export const CHUTES_DEFAULT_MAX_TOKENS = 4096;
|
||||
|
||||
export const KIMI_CODE_BASE_URL = "https://api.kimi.com/coding/v1";
|
||||
export const KIMI_CODE_MODEL_ID = "kimi-for-coding";
|
||||
export const KIMI_CODE_MODEL_REF = `kimi-code/${KIMI_CODE_MODEL_ID}`;
|
||||
@ -45,6 +52,12 @@ export const MOONSHOT_DEFAULT_COST = {
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
};
|
||||
export const CHUTES_DEFAULT_COST = {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
};
|
||||
export const KIMI_CODE_DEFAULT_COST = {
|
||||
input: 0,
|
||||
output: 0,
|
||||
@ -103,6 +116,64 @@ export function buildMoonshotModelDefinition(): ModelDefinitionConfig {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildChutesModelDefinition(
|
||||
modelId: string = CHUTES_DEFAULT_MODEL_ID,
|
||||
): ModelDefinitionConfig {
|
||||
if (modelId === "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE") {
|
||||
return {
|
||||
id: modelId,
|
||||
name: "Qwen 3 235B (Tools)",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: CHUTES_DEFAULT_COST,
|
||||
contextWindow: 262144,
|
||||
maxTokens: 4096,
|
||||
};
|
||||
}
|
||||
if (modelId === "deepseek-ai/DeepSeek-V3.2-TEE") {
|
||||
return {
|
||||
id: modelId,
|
||||
name: "DeepSeek V3.2 (Tools)",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: CHUTES_DEFAULT_COST,
|
||||
contextWindow: 202752,
|
||||
maxTokens: 4096,
|
||||
};
|
||||
}
|
||||
if (modelId === "chutesai/Mistral-Small-3.1-24B-Instruct-2503") {
|
||||
return {
|
||||
id: modelId,
|
||||
name: "Mistral Small 3.1 (Tools)",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: CHUTES_DEFAULT_COST,
|
||||
contextWindow: 131072,
|
||||
maxTokens: 4096,
|
||||
};
|
||||
}
|
||||
if (modelId === "NousResearch/Hermes-4-14B") {
|
||||
return {
|
||||
id: modelId,
|
||||
name: "Hermes 4 14B (Tools)",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: CHUTES_DEFAULT_COST,
|
||||
contextWindow: 40960,
|
||||
maxTokens: 4096,
|
||||
};
|
||||
}
|
||||
return {
|
||||
id: CHUTES_DEFAULT_MODEL_ID,
|
||||
name: "GLM 4.7 Flash",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: CHUTES_DEFAULT_COST,
|
||||
contextWindow: CHUTES_DEFAULT_CONTEXT_WINDOW,
|
||||
maxTokens: CHUTES_DEFAULT_MAX_TOKENS,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKimiCodeModelDefinition(): ModelDefinitionConfig {
|
||||
return {
|
||||
id: KIMI_CODE_MODEL_ID,
|
||||
|
||||
@ -5,6 +5,8 @@ export {
|
||||
export { VENICE_DEFAULT_MODEL_ID, VENICE_DEFAULT_MODEL_REF } from "../agents/venice-models.js";
|
||||
export {
|
||||
applyAuthProfileConfig,
|
||||
applyChutesConfig,
|
||||
applyChutesProviderConfig,
|
||||
applyKimiCodeConfig,
|
||||
applyKimiCodeProviderConfig,
|
||||
applyMoonshotConfig,
|
||||
@ -35,6 +37,7 @@ export {
|
||||
export {
|
||||
OPENROUTER_DEFAULT_MODEL_REF,
|
||||
setAnthropicApiKey,
|
||||
setChutesApiKey,
|
||||
setGeminiApiKey,
|
||||
setKimiCodeApiKey,
|
||||
setMinimaxApiKey,
|
||||
@ -54,7 +57,11 @@ export {
|
||||
buildMinimaxApiModelDefinition,
|
||||
buildMinimaxModelDefinition,
|
||||
buildMoonshotModelDefinition,
|
||||
buildChutesModelDefinition,
|
||||
DEFAULT_MINIMAX_BASE_URL,
|
||||
CHUTES_BASE_URL,
|
||||
CHUTES_DEFAULT_MODEL_ID,
|
||||
CHUTES_DEFAULT_MODEL_REF,
|
||||
KIMI_CODE_BASE_URL,
|
||||
KIMI_CODE_MODEL_ID,
|
||||
KIMI_CODE_MODEL_REF,
|
||||
|
||||
@ -8,6 +8,7 @@ import { buildTokenProfileId, validateAnthropicSetupToken } from "../../auth-tok
|
||||
import { applyGoogleGeminiModelDefault } from "../../google-gemini-model-default.js";
|
||||
import {
|
||||
applyAuthProfileConfig,
|
||||
applyChutesConfig,
|
||||
applyKimiCodeConfig,
|
||||
applyMinimaxApiConfig,
|
||||
applyMinimaxConfig,
|
||||
@ -19,6 +20,7 @@ import {
|
||||
applyVercelAiGatewayConfig,
|
||||
applyZaiConfig,
|
||||
setAnthropicApiKey,
|
||||
setChutesApiKey,
|
||||
setGeminiApiKey,
|
||||
setKimiCodeApiKey,
|
||||
setMinimaxApiKey,
|
||||
@ -252,6 +254,25 @@ export async function applyNonInteractiveAuthChoice(params: {
|
||||
return applyMoonshotConfig(nextConfig);
|
||||
}
|
||||
|
||||
if (authChoice === "chutes-api-key") {
|
||||
const resolved = await resolveNonInteractiveApiKey({
|
||||
provider: "chutes",
|
||||
cfg: baseConfig,
|
||||
flagValue: opts.chutesApiKey,
|
||||
flagName: "--chutes-api-key",
|
||||
envVar: "CHUTES_API_KEY",
|
||||
runtime,
|
||||
});
|
||||
if (!resolved) return null;
|
||||
if (resolved.source !== "profile") await setChutesApiKey(resolved.key);
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: "chutes:default",
|
||||
provider: "chutes",
|
||||
mode: "api_key",
|
||||
});
|
||||
return applyChutesConfig(nextConfig);
|
||||
}
|
||||
|
||||
if (authChoice === "kimi-code-api-key") {
|
||||
const resolved = await resolveNonInteractiveApiKey({
|
||||
provider: "kimi-code",
|
||||
|
||||
@ -14,6 +14,7 @@ export type AuthChoice =
|
||||
| "openrouter-api-key"
|
||||
| "ai-gateway-api-key"
|
||||
| "moonshot-api-key"
|
||||
| "chutes-api-key"
|
||||
| "kimi-code-api-key"
|
||||
| "synthetic-api-key"
|
||||
| "venice-api-key"
|
||||
@ -64,6 +65,7 @@ export type OnboardOptions = {
|
||||
openrouterApiKey?: string;
|
||||
aiGatewayApiKey?: string;
|
||||
moonshotApiKey?: string;
|
||||
chutesApiKey?: string;
|
||||
kimiCodeApiKey?: string;
|
||||
geminiApiKey?: string;
|
||||
zaiApiKey?: string;
|
||||
|
||||
@ -31,6 +31,8 @@ export type ModelDefinitionConfig = {
|
||||
maxTokens: number;
|
||||
headers?: Record<string, string>;
|
||||
compat?: ModelCompatConfig;
|
||||
/** Chutes-only: indicates the model runs in a Trusted Execution Environment */
|
||||
confidentialCompute?: boolean;
|
||||
};
|
||||
|
||||
export type ModelProviderConfig = {
|
||||
@ -41,6 +43,8 @@ export type ModelProviderConfig = {
|
||||
headers?: Record<string, string>;
|
||||
authHeader?: boolean;
|
||||
models: ModelDefinitionConfig[];
|
||||
/** Chutes-only: filter models by confidential_compute: true */
|
||||
teeOnly?: boolean;
|
||||
};
|
||||
|
||||
export type BedrockDiscoveryConfig = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user