forked from cline/cline
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat: add dynamic model loading for Roo Code Cloud provider #8728
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 13 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
4444328
feat: add dynamic model loading for Roo Code Cloud provider
roomote 626d97b
Delete pr-body.md
mrubens 382764c
Make it work
mrubens eeabc50
Fix url
mrubens dbdc8e4
Fix tests
mrubens b2c02de
Update types
mrubens b3054a6
Reload model list on auth changes
mrubens 26978df
Merge remote-tracking branch 'origin/main' into feature/dynamic-roo-c…
mrubens dfecaa0
Update comment
mrubens 18aeb4f
PR feedback
mrubens d442484
PR feedback
mrubens 60e900c
PR feedback
mrubens b941b65
PR feedback
mrubens e78aace
Better model reloading on auth state change
mrubens 6a1c35e
PR feedback
mrubens File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,53 +1,49 @@ | ||
| import { z } from "zod" | ||
|
|
||
| import type { ModelInfo } from "../model.js" | ||
|
|
||
| export type RooModelId = | ||
| | "xai/grok-code-fast-1" | ||
| | "roo/code-supernova-1-million" | ||
| | "xai/grok-4-fast" | ||
| | "deepseek/deepseek-chat-v3.1" | ||
|
|
||
| export const rooDefaultModelId: RooModelId = "xai/grok-code-fast-1" | ||
|
|
||
| export const rooModels = { | ||
| "xai/grok-code-fast-1": { | ||
| maxTokens: 16_384, | ||
| contextWindow: 262_144, | ||
| supportsImages: false, | ||
| supportsPromptCache: true, | ||
| inputPrice: 0, | ||
| outputPrice: 0, | ||
| description: | ||
| "A reasoning model that is blazing fast and excels at agentic coding, accessible for free through Roo Code Cloud for a limited time. (Note: the free prompts and completions are logged by xAI and used to improve the model.)", | ||
| }, | ||
| "roo/code-supernova-1-million": { | ||
| maxTokens: 30_000, | ||
| contextWindow: 1_000_000, | ||
| supportsImages: true, | ||
| supportsPromptCache: true, | ||
| inputPrice: 0, | ||
| outputPrice: 0, | ||
| description: | ||
| "A versatile agentic coding stealth model with a 1M token context window that supports image inputs, accessible for free through Roo Code Cloud for a limited time. (Note: the free prompts and completions are logged by the model provider and used to improve the model.)", | ||
| }, | ||
| "xai/grok-4-fast": { | ||
| maxTokens: 30_000, | ||
| contextWindow: 2_000_000, | ||
| supportsImages: false, | ||
| supportsPromptCache: false, | ||
| inputPrice: 0, | ||
| outputPrice: 0, | ||
| description: | ||
| "Grok 4 Fast is xAI's latest multimodal model with SOTA cost-efficiency and a 2M token context window. (Note: prompts and completions are logged by xAI and used to improve the model.)", | ||
| deprecated: true, | ||
| }, | ||
| "deepseek/deepseek-chat-v3.1": { | ||
| maxTokens: 16_384, | ||
| contextWindow: 163_840, | ||
| supportsImages: false, | ||
| supportsPromptCache: false, | ||
| inputPrice: 0, | ||
| outputPrice: 0, | ||
| description: | ||
| "DeepSeek-V3.1 is a large hybrid reasoning model (671B parameters, 37B active). It extends the DeepSeek-V3 base with a two-phase long-context training process, reaching up to 128K tokens, and uses FP8 microscaling for efficient inference.", | ||
| }, | ||
| } as const satisfies Record<string, ModelInfo> | ||
| /** | ||
| * Roo Code Cloud is a dynamic provider - models are loaded from the /v1/models API endpoint. | ||
| * Default model ID used as fallback when no model is specified. | ||
| */ | ||
| export const rooDefaultModelId = "xai/grok-code-fast-1" | ||
|
|
||
| /** | ||
| * Empty models object maintained for type compatibility. | ||
| * All model data comes dynamically from the API. | ||
| */ | ||
| export const rooModels = {} as const satisfies Record<string, ModelInfo> | ||
|
|
||
| /** | ||
| * Roo Code Cloud API response schemas | ||
| */ | ||
|
|
||
| export const RooPricingSchema = z.object({ | ||
| input: z.string(), | ||
| output: z.string(), | ||
| input_cache_read: z.string().optional(), | ||
| input_cache_write: z.string().optional(), | ||
| }) | ||
|
|
||
| export const RooModelSchema = z.object({ | ||
| id: z.string(), | ||
| object: z.literal("model"), | ||
| created: z.number(), | ||
| owned_by: z.string(), | ||
| name: z.string(), | ||
| description: z.string(), | ||
| context_window: z.number(), | ||
| max_tokens: z.number(), | ||
| type: z.literal("language"), | ||
| tags: z.array(z.string()).optional(), | ||
| pricing: RooPricingSchema, | ||
| deprecated: z.boolean().optional(), | ||
| }) | ||
|
|
||
| export const RooModelsResponseSchema = z.object({ | ||
| object: z.literal("list"), | ||
| data: z.array(RooModelSchema), | ||
| }) | ||
|
|
||
| export type RooModel = z.infer<typeof RooModelSchema> | ||
| export type RooModelsResponse = z.infer<typeof RooModelsResponseSchema> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| import { RooModelsResponseSchema } from "@roo-code/types" | ||
|
|
||
| import type { ModelRecord } from "../../../shared/api" | ||
|
|
||
| import { DEFAULT_HEADERS } from "../constants" | ||
|
|
||
| /** | ||
| * Fetches available models from the Roo Code Cloud provider | ||
| * | ||
| * @param baseUrl The base URL of the Roo Code Cloud provider | ||
| * @param apiKey The API key (session token) for the Roo Code Cloud provider | ||
| * @returns A promise that resolves to a record of model IDs to model info | ||
| * @throws Will throw an error if the request fails or the response is not as expected. | ||
| */ | ||
| export async function getRooModels(baseUrl: string, apiKey?: string): Promise<ModelRecord> { | ||
| try { | ||
| const headers: Record<string, string> = { | ||
| "Content-Type": "application/json", | ||
| ...DEFAULT_HEADERS, | ||
| } | ||
|
|
||
| if (apiKey) { | ||
| headers["Authorization"] = `Bearer ${apiKey}` | ||
| } | ||
|
|
||
| // Construct the models endpoint URL | ||
| // Strip trailing /v1 or /v1/ to avoid /v1/v1/models | ||
| const normalizedBase = baseUrl.replace(/\/?v1\/?$/, "") | ||
| const url = `${normalizedBase}/v1/models` | ||
|
|
||
| // Use fetch with AbortController for better timeout handling | ||
| const controller = new AbortController() | ||
| const timeoutId = setTimeout(() => controller.abort(), 10000) | ||
|
|
||
| try { | ||
| const response = await fetch(url, { | ||
| headers, | ||
| signal: controller.signal, | ||
| }) | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`HTTP ${response.status}: ${response.statusText}`) | ||
| } | ||
|
|
||
| const data = await response.json() | ||
| const models: ModelRecord = {} | ||
|
|
||
| // Validate response against schema | ||
| const parsed = RooModelsResponseSchema.safeParse(data) | ||
|
|
||
| if (!parsed.success) { | ||
| console.error("Error fetching Roo Code Cloud models: Unexpected response format", data) | ||
| console.error("Validation errors:", parsed.error.format()) | ||
| throw new Error("Failed to fetch Roo Code Cloud models: Unexpected response format.") | ||
| } | ||
|
|
||
| // Process the validated model data | ||
| for (const model of parsed.data.data) { | ||
| const modelId = model.id | ||
|
|
||
| if (!modelId) continue | ||
|
|
||
| // Extract model data from the validated API response | ||
| // All required fields are guaranteed by the schema | ||
| const contextWindow = model.context_window | ||
| const maxTokens = model.max_tokens | ||
| const tags = model.tags || [] | ||
| const pricing = model.pricing | ||
|
|
||
| // Determine if the model supports images based on tags | ||
| const supportsImages = tags.includes("vision") | ||
|
|
||
| // Parse pricing (API returns strings, convert to numbers) | ||
| const inputPrice = parseFloat(pricing.input) | ||
| const outputPrice = parseFloat(pricing.output) | ||
| const cacheReadPrice = pricing.input_cache_read ? parseFloat(pricing.input_cache_read) : undefined | ||
| const cacheWritePrice = pricing.input_cache_write ? parseFloat(pricing.input_cache_write) : undefined | ||
|
|
||
| models[modelId] = { | ||
| maxTokens, | ||
| contextWindow, | ||
| supportsImages, | ||
| supportsPromptCache: Boolean(cacheReadPrice !== undefined), | ||
| inputPrice, | ||
| outputPrice, | ||
| cacheWritesPrice: cacheWritePrice, | ||
| cacheReadsPrice: cacheReadPrice, | ||
| description: model.description || model.name, | ||
| deprecated: model.deprecated || false, | ||
| } | ||
| } | ||
|
|
||
| return models | ||
| } finally { | ||
| clearTimeout(timeoutId) | ||
| } | ||
| } catch (error: any) { | ||
| console.error("Error fetching Roo Code Cloud models:", error.message ? error.message : error) | ||
|
|
||
| // Handle abort/timeout | ||
| if (error.name === "AbortError") { | ||
| throw new Error("Failed to fetch Roo Code Cloud models: Request timed out after 10 seconds.") | ||
| } | ||
|
|
||
| // Handle fetch errors | ||
| if (error.message?.includes("HTTP")) { | ||
| throw new Error(`Failed to fetch Roo Code Cloud models: ${error.message}. Check base URL and API key.`) | ||
| } | ||
|
|
||
| // Handle network errors | ||
| if (error instanceof TypeError) { | ||
| throw new Error( | ||
| "Failed to fetch Roo Code Cloud models: No response from server. Check Roo Code Cloud server status and base URL.", | ||
| ) | ||
| } | ||
|
|
||
| throw new Error(`Failed to fetch Roo Code Cloud models: ${error.message || "An unknown error occurred."}`) | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need some kind of tiered pricing support (or is that allowed with this pattern/standard)?