Skip to content

Commit a3de293

Browse files
feat: add dynamic settings support for Roo models from API (#9852)
1 parent 247bf01 commit a3de293

File tree

5 files changed

+96
-39
lines changed

5 files changed

+96
-39
lines changed

packages/types/src/providers/roo.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export const RooModelSchema = z.object({
3939
pricing: RooPricingSchema,
4040
deprecated: z.boolean().optional(),
4141
default_temperature: z.number().optional(),
42+
// Dynamic settings that map directly to ModelInfo properties
43+
// Allows the API to configure model-specific defaults like includedTools, excludedTools, reasoningEffort, etc.
44+
settings: z.record(z.string(), z.unknown()).optional(),
4245
})
4346

4447
export const RooModelsResponseSchema = z.object({

src/api/providers/__tests__/roo.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,12 +439,12 @@ describe("RooHandler", () => {
439439
}
440440
})
441441

442-
it("should not override existing properties when applying MODEL_DEFAULTS", () => {
442+
it("should return cached model info with settings applied from API", () => {
443443
const handlerWithMinimax = new RooHandler({
444444
apiModelId: "minimax/minimax-m2:free",
445445
})
446446
const modelInfo = handlerWithMinimax.getModel()
447-
// The defaults should be merged, but not overwrite existing cached values
447+
// The settings from API should already be applied in the cached model info
448448
expect(modelInfo.info.supportsNativeTools).toBe(true)
449449
expect(modelInfo.info.inputPrice).toBe(0.15)
450450
expect(modelInfo.info.outputPrice).toBe(0.6)

src/api/providers/fetchers/__tests__/roo.spec.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe("getRooModels", () => {
7777
description: "Fast coding model",
7878
deprecated: false,
7979
isFree: false,
80-
defaultToolProtocol: "native", // Applied from MODEL_DEFAULTS
80+
defaultToolProtocol: "native",
8181
},
8282
})
8383
})
@@ -719,4 +719,86 @@ describe("getRooModels", () => {
719719

720720
expect(models["test/non-stealth-model"].isStealthModel).toBeUndefined()
721721
})
722+
723+
it("should apply API-provided settings to model info", async () => {
724+
const mockResponse = {
725+
object: "list",
726+
data: [
727+
{
728+
id: "test/model-with-settings",
729+
object: "model",
730+
created: 1234567890,
731+
owned_by: "test",
732+
name: "Model with Settings",
733+
description: "Model with API-provided settings",
734+
context_window: 128000,
735+
max_tokens: 8192,
736+
type: "language",
737+
tags: ["tool-use"],
738+
pricing: {
739+
input: "0.0001",
740+
output: "0.0002",
741+
},
742+
settings: {
743+
includedTools: ["apply_patch"],
744+
excludedTools: ["apply_diff", "write_to_file"],
745+
reasoningEffort: "high",
746+
},
747+
},
748+
],
749+
}
750+
751+
mockFetch.mockResolvedValueOnce({
752+
ok: true,
753+
json: async () => mockResponse,
754+
})
755+
756+
const models = await getRooModels(baseUrl, apiKey)
757+
758+
expect(models["test/model-with-settings"].includedTools).toEqual(["apply_patch"])
759+
expect(models["test/model-with-settings"].excludedTools).toEqual(["apply_diff", "write_to_file"])
760+
expect(models["test/model-with-settings"].reasoningEffort).toBe("high")
761+
})
762+
763+
it("should handle arbitrary settings properties dynamically", async () => {
764+
const mockResponse = {
765+
object: "list",
766+
data: [
767+
{
768+
id: "test/dynamic-settings-model",
769+
object: "model",
770+
created: 1234567890,
771+
owned_by: "test",
772+
name: "Dynamic Settings Model",
773+
description: "Model with arbitrary settings",
774+
context_window: 128000,
775+
max_tokens: 8192,
776+
type: "language",
777+
tags: [],
778+
pricing: {
779+
input: "0.0001",
780+
output: "0.0002",
781+
},
782+
settings: {
783+
customProperty: "custom-value",
784+
anotherSetting: 42,
785+
nestedConfig: { key: "value" },
786+
},
787+
},
788+
],
789+
}
790+
791+
mockFetch.mockResolvedValueOnce({
792+
ok: true,
793+
json: async () => mockResponse,
794+
})
795+
796+
const models = await getRooModels(baseUrl, apiKey)
797+
const model = models["test/dynamic-settings-model"] as any
798+
799+
// Arbitrary settings should be passed through
800+
expect(model.customProperty).toBe("custom-value")
801+
expect(model.anotherSetting).toBe(42)
802+
expect(model.nestedConfig).toEqual({ key: "value" })
803+
})
722804
})

src/api/providers/fetchers/roo.ts

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,6 @@ import { parseApiPrice } from "../../../shared/cost"
55

66
import { DEFAULT_HEADERS } from "../constants"
77

8-
// Model-specific defaults that should be applied even when models come from API cache
9-
// These override API-provided values for specific models
10-
// Exported so RooHandler.getModel() can also apply these for fallback cases
11-
export const MODEL_DEFAULTS: Record<string, Partial<ModelInfo>> = {
12-
"minimax/minimax-m2:free": {
13-
includedTools: ["search_and_replace"],
14-
excludedTools: ["apply_diff"],
15-
},
16-
"openai/gpt-5.1": {
17-
includedTools: ["apply_patch"],
18-
excludedTools: ["apply_diff", "write_to_file"],
19-
reasoningEffort: "medium",
20-
},
21-
"openai/gpt-5": {
22-
includedTools: ["apply_patch"],
23-
excludedTools: ["apply_diff", "write_to_file"],
24-
reasoningEffort: "medium",
25-
},
26-
"openai/gpt-5-mini": {
27-
includedTools: ["apply_patch"],
28-
excludedTools: ["apply_diff", "write_to_file"],
29-
reasoningEffort: "medium",
30-
},
31-
}
32-
338
/**
349
* Fetches available models from the Roo Code Cloud provider
3510
*
@@ -150,9 +125,12 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
150125
isStealthModel: isStealthModel || undefined,
151126
}
152127

153-
// Apply model-specific defaults (e.g., defaultToolProtocol)
154-
const modelDefaults = MODEL_DEFAULTS[modelId]
155-
models[modelId] = modelDefaults ? { ...baseModelInfo, ...modelDefaults } : baseModelInfo
128+
// Apply API-provided settings on top of base model info
129+
// Settings allow the proxy to dynamically configure model-specific options
130+
// like includedTools, excludedTools, reasoningEffort, etc.
131+
const apiSettings = model.settings as Partial<ModelInfo> | undefined
132+
133+
models[modelId] = apiSettings ? { ...baseModelInfo, ...apiSettings } : baseModelInfo
156134
}
157135

158136
return models

src/api/providers/roo.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { getRooReasoning } from "../transform/reasoning"
1515
import type { ApiHandlerCreateMessageMetadata } from "../index"
1616
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
1717
import { getModels, getModelsFromCache } from "../providers/fetchers/modelCache"
18-
import { MODEL_DEFAULTS } from "../providers/fetchers/roo"
1918
import { handleOpenAIError } from "./utils/openai-error-handler"
2019
import { generateImageWithProvider, generateImageWithImagesApi, ImageGenerationResult } from "./utils/image-generation"
2120
import { t } from "../../i18n"
@@ -335,17 +334,12 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<string> {
335334
override getModel() {
336335
const modelId = this.options.apiModelId || rooDefaultModelId
337336

338-
// Get models from shared cache
337+
// Get models from shared cache (settings are already applied by the fetcher)
339338
const models = getModelsFromCache("roo") || {}
340339
const modelInfo = models[modelId]
341340

342-
// Get model-specific defaults if they exist
343-
const modelDefaults = MODEL_DEFAULTS[modelId]
344-
345341
if (modelInfo) {
346-
// Merge model-specific defaults with cached model info
347-
const mergedInfo = modelDefaults ? { ...modelInfo, ...modelDefaults } : modelInfo
348-
return { id: modelId, info: mergedInfo }
342+
return { id: modelId, info: modelInfo }
349343
}
350344

351345
// Return the requested model ID even if not found, with fallback info.

0 commit comments

Comments
 (0)