Provider: finalize dynamic Chutes models and TEE filtering

This commit is contained in:
Veightor 2026-01-26 16:08:02 -05:00
parent 2bb5e73d51
commit 053c160b6b
8 changed files with 106 additions and 91 deletions

View File

@ -6,7 +6,9 @@ read_when:
---
# Chutes AI
Chutes provides high-performance inference for open-weight models, including GLM 4.6 TEE. Clawdbot supports Chutes via both OAuth and API key authentication.
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
@ -47,13 +49,14 @@ If you wish to use your own OAuth application instead of the default, set these
```json5
{
env: { CHUTES_API_KEY: "sk-..." },
agents: { defaults: { model: { primary: "chutes/zai-org/GLM-4.6-TEE" } } },
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}"
apiKey: "${CHUTES_API_KEY}",
teeOnly: false // Set to true to filter models by Trusted Execution Environment
}
}
}
@ -63,11 +66,12 @@ If you wish to use your own OAuth application instead of the default, set these
## Notes
- Chutes models are available under the `chutes/` provider prefix.
- The default model is `chutes/zai-org/GLM-4.6-TEE`.
- 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`
- `deepseek-ai/DeepSeek-V3.2-TEE`
- `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:

View 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);
}

View File

@ -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,
@ -42,15 +43,6 @@ const MOONSHOT_DEFAULT_COST = {
};
const CHUTES_BASE_URL = "https://llm.chutes.ai/v1";
const CHUTES_DEFAULT_MODEL_ID = "zai-org/GLM-4.6-TEE";
const CHUTES_DEFAULT_CONTEXT_WINDOW = 128000;
const CHUTES_DEFAULT_MAX_TOKENS = 4096;
const CHUTES_DEFAULT_COST = {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
};
const KIMI_CODE_BASE_URL = "https://api.kimi.com/coding/v1";
const KIMI_CODE_MODEL_ID = "kimi-for-coding";
@ -298,57 +290,13 @@ function buildMoonshotProvider(): ProviderConfig {
};
}
function buildChutesProvider(): ProviderConfig {
async function buildChutesProvider(opts?: { teeOnly?: boolean }): Promise<ProviderConfig> {
const models = await discoverChutesModels(opts);
return {
baseUrl: CHUTES_BASE_URL,
api: "openai-completions",
models: [
{
id: CHUTES_DEFAULT_MODEL_ID,
name: "GLM 4.6 TEE",
reasoning: false,
input: ["text"],
cost: CHUTES_DEFAULT_COST,
contextWindow: CHUTES_DEFAULT_CONTEXT_WINDOW,
maxTokens: CHUTES_DEFAULT_MAX_TOKENS,
},
{
id: "Qwen/Qwen3-235B-A22B-Instruct-2507-TEE",
name: "Qwen 3 235B (Tools)",
reasoning: false,
input: ["text"],
cost: CHUTES_DEFAULT_COST,
contextWindow: 262144,
maxTokens: 4096,
},
{
id: "deepseek-ai/DeepSeek-V3.2-TEE",
name: "DeepSeek V3.2 (Tools)",
reasoning: false,
input: ["text"],
cost: CHUTES_DEFAULT_COST,
contextWindow: 202752,
maxTokens: 4096,
},
{
id: "chutesai/Mistral-Small-3.1-24B-Instruct-2503",
name: "Mistral Small 3.1 (Tools)",
reasoning: false,
input: ["text"],
cost: CHUTES_DEFAULT_COST,
contextWindow: 131072,
maxTokens: 4096,
},
{
id: "NousResearch/Hermes-4-14B",
name: "Hermes 4 14B (Tools)",
reasoning: false,
input: ["text"],
cost: CHUTES_DEFAULT_COST,
contextWindow: 40960,
maxTokens: 4096,
},
],
models,
teeOnly: opts?.teeOnly,
};
}
@ -451,7 +399,7 @@ export async function resolveImplicitProviders(params: {
resolveEnvApiKeyVarName("chutes") ??
resolveApiKeyFromProfiles({ provider: "chutes", store: authStore });
if (chutesKey) {
providers.chutes = { ...buildChutesProvider(), apiKey: chutesKey };
providers.chutes = { ...(await buildChutesProvider()), apiKey: chutesKey };
}
const kimiCodeKey =

View File

@ -214,6 +214,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 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");
@ -341,6 +342,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");

View File

@ -26,7 +26,6 @@ import {
MOONSHOT_DEFAULT_MODEL_ID,
MOONSHOT_DEFAULT_MODEL_REF,
CHUTES_BASE_URL,
CHUTES_DEFAULT_MODEL_ID,
CHUTES_DEFAULT_MODEL_REF,
buildChutesModelDefinition,
} from "./onboard-auth.models.js";
@ -210,36 +209,16 @@ 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.6",
alias: models[CHUTES_DEFAULT_MODEL_REF]?.alias ?? "GLM 4.7 Flash",
};
const providers = { ...cfg.models?.providers };
const existingProvider = providers.chutes;
const existingModels = Array.isArray(existingProvider?.models) ? existingProvider.models : [];
const toolCapableModelIds = [
"Qwen/Qwen3-235B-A22B-Instruct-2507-TEE",
"deepseek-ai/DeepSeek-V3.2-TEE",
"chutesai/Mistral-Small-3.1-24B-Instruct-2503",
"NousResearch/Hermes-4-14B",
];
const defaultModel = buildChutesModelDefinition();
const toolModels = toolCapableModelIds.map((id) => buildChutesModelDefinition(id));
const allChutesModels = [defaultModel, ...toolModels];
const mergedModels = [...existingModels];
for (const model of allChutesModels) {
if (!mergedModels.some((m) => m.id === model.id)) {
mergedModels.push(model);
}
}
const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record<
string,
unknown
> as { apiKey?: string };
> as { apiKey?: string; teeOnly?: boolean };
const resolvedApiKey = typeof existingApiKey === "string" ? existingApiKey : undefined;
const normalizedApiKey = resolvedApiKey?.trim();
providers.chutes = {
@ -247,7 +226,9 @@ export function applyChutesProviderConfig(cfg: ClawdbotConfig): ClawdbotConfig {
baseUrl: CHUTES_BASE_URL,
api: "openai-completions",
...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}),
models: mergedModels,
// Models will be refreshed dynamically at startup,
// but we can pre-populate the default one for onboarding.
models: existingProvider?.models || [buildChutesModelDefinition()],
};
return {

View File

@ -126,7 +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.6-TEE";
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";

View File

@ -14,7 +14,7 @@ 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.6-TEE";
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;
@ -165,7 +165,7 @@ export function buildChutesModelDefinition(
}
return {
id: CHUTES_DEFAULT_MODEL_ID,
name: "GLM 4.6 TEE",
name: "GLM 4.7 Flash",
reasoning: false,
input: ["text"],
cost: CHUTES_DEFAULT_COST,

View File

@ -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 = {