diff --git a/src/api/providers/fetchers/chutes.ts b/src/api/providers/fetchers/chutes.ts index f3e7f2dc6fc..89bb5130159 100644 --- a/src/api/providers/fetchers/chutes.ts +++ b/src/api/providers/fetchers/chutes.ts @@ -1,19 +1,19 @@ import axios from "axios" import { z } from "zod" -import { type ModelInfo, TOOL_PROTOCOL, chutesModels } from "@roo-code/types" +import { type ModelInfo, chutesModels } from "@roo-code/types" import { DEFAULT_HEADERS } from "../constants" -// Chutes models endpoint follows OpenAI /models shape with additional fields +// Chutes models endpoint follows OpenAI /models shape with additional fields. const ChutesModelSchema = z.object({ id: z.string(), object: z.literal("model").optional(), owned_by: z.string().optional(), created: z.number().optional(), - context_length: z.number(), + context_length: z.number().optional(), max_model_len: z.number(), - input_modalities: z.array(z.string()), + input_modalities: z.array(z.string()).optional(), supported_features: z.array(z.string()).optional(), }) @@ -21,42 +21,49 @@ const ChutesModelsResponseSchema = z.object({ data: z.array(ChutesModelSchema) } export async function getChutesModels(apiKey?: string): Promise> { const headers: Record = { ...DEFAULT_HEADERS } - if (apiKey) headers["Authorization"] = `Bearer ${apiKey}` + + if (apiKey) { + headers["Authorization"] = `Bearer ${apiKey}` + } const url = "https://llm.chutes.ai/v1/models" - // Start with hardcoded models as the base + // Start with hardcoded models as the base. const models: Record = { ...chutesModels } try { const response = await axios.get(url, { headers }) const parsed = ChutesModelsResponseSchema.safeParse(response.data) - const data = parsed.success ? parsed.data.data : response.data?.data || [] - - for (const m of data as Array>) { - // Extract from API response (all fields are required) - const contextWindow = m.context_length - const maxTokens = m.max_model_len - const supportsImages = m.input_modalities.includes("image") - const supportsNativeTools = m.supported_features?.includes("tools") ?? false - - const info: ModelInfo = { - maxTokens, - contextWindow, - supportsImages, - supportsPromptCache: false, - supportsNativeTools, - inputPrice: 0, - outputPrice: 0, - description: `Chutes AI model: ${m.id}`, - } - // Union: dynamic models override hardcoded ones if they have the same ID - models[m.id] = info + if (parsed.success) { + for (const m of parsed.data.data) { + const contextWindow = m.context_length + + if (!contextWindow) { + console.error(`Context length is required for Chutes model: ${m.id}`) + continue + } + + const info: ModelInfo = { + maxTokens: m.max_model_len, + contextWindow, + supportsImages: (m.input_modalities || []).includes("image"), + supportsPromptCache: false, + supportsNativeTools: (m.supported_features || []).includes("tools"), + inputPrice: 0, + outputPrice: 0, + description: `Chutes AI model: ${m.id}`, + } + + // Union: dynamic models override hardcoded ones if they have the same ID. + models[m.id] = info + } + } else { + console.error(`Error parsing Chutes models: ${JSON.stringify(parsed.error.format(), null, 2)}`) } } catch (error) { console.error(`Error fetching Chutes models: ${error instanceof Error ? error.message : String(error)}`) - // On error, still return hardcoded models + // On error, still return hardcoded models. } return models