mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: add dynamic settings support for Roo models from API (#9852)
This commit is contained in:
parent
247bf017e0
commit
a3de2935b3
5 changed files with 96 additions and 39 deletions
|
|
@ -39,6 +39,9 @@ export const RooModelSchema = z.object({
|
|||
pricing: RooPricingSchema,
|
||||
deprecated: z.boolean().optional(),
|
||||
default_temperature: z.number().optional(),
|
||||
// Dynamic settings that map directly to ModelInfo properties
|
||||
// Allows the API to configure model-specific defaults like includedTools, excludedTools, reasoningEffort, etc.
|
||||
settings: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
|
||||
export const RooModelsResponseSchema = z.object({
|
||||
|
|
|
|||
|
|
@ -439,12 +439,12 @@ describe("RooHandler", () => {
|
|||
}
|
||||
})
|
||||
|
||||
it("should not override existing properties when applying MODEL_DEFAULTS", () => {
|
||||
it("should return cached model info with settings applied from API", () => {
|
||||
const handlerWithMinimax = new RooHandler({
|
||||
apiModelId: "minimax/minimax-m2:free",
|
||||
})
|
||||
const modelInfo = handlerWithMinimax.getModel()
|
||||
// The defaults should be merged, but not overwrite existing cached values
|
||||
// The settings from API should already be applied in the cached model info
|
||||
expect(modelInfo.info.supportsNativeTools).toBe(true)
|
||||
expect(modelInfo.info.inputPrice).toBe(0.15)
|
||||
expect(modelInfo.info.outputPrice).toBe(0.6)
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ describe("getRooModels", () => {
|
|||
description: "Fast coding model",
|
||||
deprecated: false,
|
||||
isFree: false,
|
||||
defaultToolProtocol: "native", // Applied from MODEL_DEFAULTS
|
||||
defaultToolProtocol: "native",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
|
@ -719,4 +719,86 @@ describe("getRooModels", () => {
|
|||
|
||||
expect(models["test/non-stealth-model"].isStealthModel).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should apply API-provided settings to model info", async () => {
|
||||
const mockResponse = {
|
||||
object: "list",
|
||||
data: [
|
||||
{
|
||||
id: "test/model-with-settings",
|
||||
object: "model",
|
||||
created: 1234567890,
|
||||
owned_by: "test",
|
||||
name: "Model with Settings",
|
||||
description: "Model with API-provided settings",
|
||||
context_window: 128000,
|
||||
max_tokens: 8192,
|
||||
type: "language",
|
||||
tags: ["tool-use"],
|
||||
pricing: {
|
||||
input: "0.0001",
|
||||
output: "0.0002",
|
||||
},
|
||||
settings: {
|
||||
includedTools: ["apply_patch"],
|
||||
excludedTools: ["apply_diff", "write_to_file"],
|
||||
reasoningEffort: "high",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockResponse,
|
||||
})
|
||||
|
||||
const models = await getRooModels(baseUrl, apiKey)
|
||||
|
||||
expect(models["test/model-with-settings"].includedTools).toEqual(["apply_patch"])
|
||||
expect(models["test/model-with-settings"].excludedTools).toEqual(["apply_diff", "write_to_file"])
|
||||
expect(models["test/model-with-settings"].reasoningEffort).toBe("high")
|
||||
})
|
||||
|
||||
it("should handle arbitrary settings properties dynamically", async () => {
|
||||
const mockResponse = {
|
||||
object: "list",
|
||||
data: [
|
||||
{
|
||||
id: "test/dynamic-settings-model",
|
||||
object: "model",
|
||||
created: 1234567890,
|
||||
owned_by: "test",
|
||||
name: "Dynamic Settings Model",
|
||||
description: "Model with arbitrary settings",
|
||||
context_window: 128000,
|
||||
max_tokens: 8192,
|
||||
type: "language",
|
||||
tags: [],
|
||||
pricing: {
|
||||
input: "0.0001",
|
||||
output: "0.0002",
|
||||
},
|
||||
settings: {
|
||||
customProperty: "custom-value",
|
||||
anotherSetting: 42,
|
||||
nestedConfig: { key: "value" },
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockResponse,
|
||||
})
|
||||
|
||||
const models = await getRooModels(baseUrl, apiKey)
|
||||
const model = models["test/dynamic-settings-model"] as any
|
||||
|
||||
// Arbitrary settings should be passed through
|
||||
expect(model.customProperty).toBe("custom-value")
|
||||
expect(model.anotherSetting).toBe(42)
|
||||
expect(model.nestedConfig).toEqual({ key: "value" })
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,31 +5,6 @@ import { parseApiPrice } from "../../../shared/cost"
|
|||
|
||||
import { DEFAULT_HEADERS } from "../constants"
|
||||
|
||||
// Model-specific defaults that should be applied even when models come from API cache
|
||||
// These override API-provided values for specific models
|
||||
// Exported so RooHandler.getModel() can also apply these for fallback cases
|
||||
export const MODEL_DEFAULTS: Record<string, Partial<ModelInfo>> = {
|
||||
"minimax/minimax-m2:free": {
|
||||
includedTools: ["search_and_replace"],
|
||||
excludedTools: ["apply_diff"],
|
||||
},
|
||||
"openai/gpt-5.1": {
|
||||
includedTools: ["apply_patch"],
|
||||
excludedTools: ["apply_diff", "write_to_file"],
|
||||
reasoningEffort: "medium",
|
||||
},
|
||||
"openai/gpt-5": {
|
||||
includedTools: ["apply_patch"],
|
||||
excludedTools: ["apply_diff", "write_to_file"],
|
||||
reasoningEffort: "medium",
|
||||
},
|
||||
"openai/gpt-5-mini": {
|
||||
includedTools: ["apply_patch"],
|
||||
excludedTools: ["apply_diff", "write_to_file"],
|
||||
reasoningEffort: "medium",
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches available models from the Roo Code Cloud provider
|
||||
*
|
||||
|
|
@ -150,9 +125,12 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
|
|||
isStealthModel: isStealthModel || undefined,
|
||||
}
|
||||
|
||||
// Apply model-specific defaults (e.g., defaultToolProtocol)
|
||||
const modelDefaults = MODEL_DEFAULTS[modelId]
|
||||
models[modelId] = modelDefaults ? { ...baseModelInfo, ...modelDefaults } : baseModelInfo
|
||||
// Apply API-provided settings on top of base model info
|
||||
// Settings allow the proxy to dynamically configure model-specific options
|
||||
// like includedTools, excludedTools, reasoningEffort, etc.
|
||||
const apiSettings = model.settings as Partial<ModelInfo> | undefined
|
||||
|
||||
models[modelId] = apiSettings ? { ...baseModelInfo, ...apiSettings } : baseModelInfo
|
||||
}
|
||||
|
||||
return models
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { getRooReasoning } from "../transform/reasoning"
|
|||
import type { ApiHandlerCreateMessageMetadata } from "../index"
|
||||
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
|
||||
import { getModels, getModelsFromCache } from "../providers/fetchers/modelCache"
|
||||
import { MODEL_DEFAULTS } from "../providers/fetchers/roo"
|
||||
import { handleOpenAIError } from "./utils/openai-error-handler"
|
||||
import { generateImageWithProvider, generateImageWithImagesApi, ImageGenerationResult } from "./utils/image-generation"
|
||||
import { t } from "../../i18n"
|
||||
|
|
@ -335,17 +334,12 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<string> {
|
|||
override getModel() {
|
||||
const modelId = this.options.apiModelId || rooDefaultModelId
|
||||
|
||||
// Get models from shared cache
|
||||
// Get models from shared cache (settings are already applied by the fetcher)
|
||||
const models = getModelsFromCache("roo") || {}
|
||||
const modelInfo = models[modelId]
|
||||
|
||||
// Get model-specific defaults if they exist
|
||||
const modelDefaults = MODEL_DEFAULTS[modelId]
|
||||
|
||||
if (modelInfo) {
|
||||
// Merge model-specific defaults with cached model info
|
||||
const mergedInfo = modelDefaults ? { ...modelInfo, ...modelDefaults } : modelInfo
|
||||
return { id: modelId, info: mergedInfo }
|
||||
return { id: modelId, info: modelInfo }
|
||||
}
|
||||
|
||||
// Return the requested model ID even if not found, with fallback info.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue