Skip to content

Commit ce6e3c3

Browse files
committed
Merge branch 'main' into cte/roo-types-package
2 parents 16b7ac9 + 1366ba0 commit ce6e3c3

File tree

17 files changed

+527
-45
lines changed

17 files changed

+527
-45
lines changed

.changeset/slow-spies-walk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
Fix o1-pro on OpenRouter

.changeset/twelve-pigs-reply.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
Preserve model settings when selecting a specific OpenRouter provider

src/api/providers/__tests__/ollama.test.ts renamed to src/api/providers/__tests__/ollama.spec.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1+
// npx vitest run api/providers/__tests__/ollama.spec.ts
2+
3+
import { vitest } from "vitest"
14
import { Anthropic } from "@anthropic-ai/sdk"
25

36
import { OllamaHandler } from "../ollama"
47
import { ApiHandlerOptions } from "../../../shared/api"
58

6-
// Mock OpenAI client
7-
const mockCreate = jest.fn()
8-
jest.mock("openai", () => {
9+
const mockCreate = vitest.fn()
10+
11+
vitest.mock("openai", () => {
912
return {
1013
__esModule: true,
11-
default: jest.fn().mockImplementation(() => ({
14+
default: vitest.fn().mockImplementation(() => ({
1215
chat: {
1316
completions: {
1417
create: mockCreate.mockImplementation(async (options) => {

src/api/providers/__tests__/openai-native.test.ts renamed to src/api/providers/__tests__/openai-native.spec.ts

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
// npx jest src/api/providers/__tests__/openai-native.test.ts
1+
// npx vitest run api/providers/__tests__/openai-native.spec.ts
22

3+
import { vitest } from "vitest"
34
import { Anthropic } from "@anthropic-ai/sdk"
45

56
import { OpenAiNativeHandler } from "../openai-native"
67
import { ApiHandlerOptions } from "../../../shared/api"
78

89
// Mock OpenAI client
9-
const mockCreate = jest.fn()
10+
const mockCreate = vitest.fn()
1011

11-
jest.mock("openai", () => {
12+
vitest.mock("openai", () => {
1213
return {
1314
__esModule: true,
14-
default: jest.fn().mockImplementation(() => ({
15+
default: vitest.fn().mockImplementation(() => ({
1516
chat: {
1617
completions: {
1718
create: mockCreate.mockImplementation(async (options) => {
@@ -372,6 +373,75 @@ describe("OpenAiNativeHandler", () => {
372373
})
373374
})
374375

376+
describe("temperature parameter handling", () => {
377+
it("should include temperature for models that support it", async () => {
378+
// Test with gpt-4.1 which supports temperature
379+
handler = new OpenAiNativeHandler({
380+
apiModelId: "gpt-4.1",
381+
openAiNativeApiKey: "test-api-key",
382+
})
383+
384+
await handler.completePrompt("Test prompt")
385+
expect(mockCreate).toHaveBeenCalledWith({
386+
model: "gpt-4.1",
387+
messages: [{ role: "user", content: "Test prompt" }],
388+
temperature: 0,
389+
})
390+
})
391+
392+
it("should strip temperature for o1 family models", async () => {
393+
const o1Models = ["o1", "o1-preview", "o1-mini"]
394+
395+
for (const modelId of o1Models) {
396+
handler = new OpenAiNativeHandler({
397+
apiModelId: modelId,
398+
openAiNativeApiKey: "test-api-key",
399+
})
400+
401+
mockCreate.mockClear()
402+
await handler.completePrompt("Test prompt")
403+
404+
const callArgs = mockCreate.mock.calls[0][0]
405+
// Temperature should be undefined for o1 models
406+
expect(callArgs.temperature).toBeUndefined()
407+
expect(callArgs.model).toBe(modelId)
408+
}
409+
})
410+
411+
it("should strip temperature for o3-mini model", async () => {
412+
handler = new OpenAiNativeHandler({
413+
apiModelId: "o3-mini",
414+
openAiNativeApiKey: "test-api-key",
415+
})
416+
417+
await handler.completePrompt("Test prompt")
418+
419+
const callArgs = mockCreate.mock.calls[0][0]
420+
// Temperature should be undefined for o3-mini models
421+
expect(callArgs.temperature).toBeUndefined()
422+
expect(callArgs.model).toBe("o3-mini")
423+
expect(callArgs.reasoning_effort).toBe("medium")
424+
})
425+
426+
it("should strip temperature in streaming mode for unsupported models", async () => {
427+
handler = new OpenAiNativeHandler({
428+
apiModelId: "o1",
429+
openAiNativeApiKey: "test-api-key",
430+
})
431+
432+
const stream = handler.createMessage(systemPrompt, messages)
433+
// Consume the stream
434+
for await (const _chunk of stream) {
435+
// Just consume the stream
436+
}
437+
438+
const callArgs = mockCreate.mock.calls[0][0]
439+
expect(callArgs).not.toHaveProperty("temperature")
440+
expect(callArgs.model).toBe("o1")
441+
expect(callArgs.stream).toBe(true)
442+
})
443+
})
444+
375445
describe("getModel", () => {
376446
it("should return model info", () => {
377447
const modelInfo = handler.getModel()

src/api/providers/__tests__/openai-usage-tracking.test.ts renamed to src/api/providers/__tests__/openai-usage-tracking.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
import { OpenAiHandler } from "../openai"
2-
import { ApiHandlerOptions } from "../../../shared/api"
1+
// npx vitest run api/providers/__tests__/openai-usage-tracking.spec.ts
2+
3+
import { vitest } from "vitest"
34
import { Anthropic } from "@anthropic-ai/sdk"
45

5-
// Mock OpenAI client with multiple chunks that contain usage data
6-
const mockCreate = jest.fn()
7-
jest.mock("openai", () => {
6+
import { ApiHandlerOptions } from "../../../shared/api"
7+
import { OpenAiHandler } from "../openai"
8+
9+
const mockCreate = vitest.fn()
10+
11+
vitest.mock("openai", () => {
812
return {
913
__esModule: true,
10-
default: jest.fn().mockImplementation(() => ({
14+
default: vitest.fn().mockImplementation(() => ({
1115
chat: {
1216
completions: {
1317
create: mockCreate.mockImplementation(async (options) => {

src/api/providers/__tests__/openai.test.ts renamed to src/api/providers/__tests__/openai.spec.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
// npx jest src/api/providers/__tests__/openai.test.ts
1+
// npx vitest run api/providers/__tests__/openai.spec.ts
22

3+
import { vitest, vi } from "vitest"
34
import { OpenAiHandler } from "../openai"
45
import { ApiHandlerOptions } from "../../../shared/api"
56
import { Anthropic } from "@anthropic-ai/sdk"
7+
import OpenAI from "openai"
68

7-
// Mock OpenAI client
8-
const mockCreate = jest.fn()
9-
jest.mock("openai", () => {
9+
const mockCreate = vitest.fn()
10+
11+
vitest.mock("openai", () => {
12+
const mockConstructor = vitest.fn()
1013
return {
1114
__esModule: true,
12-
default: jest.fn().mockImplementation(() => ({
15+
default: mockConstructor.mockImplementation(() => ({
1316
chat: {
1417
completions: {
1518
create: mockCreate.mockImplementation(async (options) => {
@@ -94,10 +97,8 @@ describe("OpenAiHandler", () => {
9497
})
9598

9699
it("should set default headers correctly", () => {
97-
// Get the mock constructor from the jest mock system
98-
const openAiMock = jest.requireMock("openai").default
99-
100-
expect(openAiMock).toHaveBeenCalledWith({
100+
// Check that the OpenAI constructor was called with correct parameters
101+
expect(vi.mocked(OpenAI)).toHaveBeenCalledWith({
101102
baseURL: expect.any(String),
102103
apiKey: expect.any(String),
103104
defaultHeaders: {

src/api/providers/openai-native.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
166166

167167
const info: ModelInfo = openAiNativeModels[id]
168168

169-
const { temperature, ...params } = getModelParams({
169+
const params = getModelParams({
170170
format: "openai",
171171
modelId: id,
172172
model: info,
@@ -176,13 +176,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
176176

177177
// The o3 models are named like "o3-mini-[reasoning-effort]", which are
178178
// not valid model ids, so we need to strip the suffix.
179-
// Also note that temperature is not supported for o1 and o3-mini.
180-
return {
181-
id: id.startsWith("o3-mini") ? "o3-mini" : id,
182-
info,
183-
...params,
184-
temperature: id.startsWith("o1") || id.startsWith("o3-mini") ? undefined : temperature,
185-
}
179+
return { id: id.startsWith("o3-mini") ? "o3-mini" : id, info, ...params }
186180
}
187181

188182
async completePrompt(prompt: string): Promise<string> {

src/api/transform/model-params.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type GetModelParamsOptions<T extends "openai" | "anthropic" | "openrouter"> = {
2222

2323
type BaseModelParams = {
2424
maxTokens: number | undefined
25-
temperature: number
25+
temperature: number | undefined
2626
reasoningEffort: "low" | "medium" | "high" | undefined
2727
reasoningBudget: number | undefined
2828
}
@@ -111,12 +111,27 @@ export function getModelParams({
111111
reasoning: getAnthropicReasoning({ model, reasoningBudget, reasoningEffort, settings }),
112112
}
113113
} else if (format === "openai") {
114+
// Special case for o1 and o3-mini, which don't support temperature.
115+
// TODO: Add a `supportsTemperature` field to the model info.
116+
if (modelId.startsWith("o1") || modelId.startsWith("o3-mini")) {
117+
params.temperature = undefined
118+
}
119+
114120
return {
115121
format,
116122
...params,
117123
reasoning: getOpenAiReasoning({ model, reasoningBudget, reasoningEffort, settings }),
118124
}
119125
} else {
126+
// Special case for o1-pro, which doesn't support temperature.
127+
// Note that OpenRouter's `supported_parameters` field includes
128+
// `temperature`, which is probably a bug.
129+
// TODO: Add a `supportsTemperature` field to the model info and populate
130+
// it appropriately in the OpenRouter fetcher.
131+
if (modelId === "openai/o1-pro") {
132+
params.temperature = undefined
133+
}
134+
120135
return {
121136
format,
122137
...params,

src/extension.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,11 @@ export async function activate(context: vscode.ExtensionContext) {
146146
const enableLogging = typeof socketPath === "string"
147147

148148
// Watch the core files and automatically reload the extension host.
149-
const enableCoreAutoReload = process.env?.NODE_ENV === "development"
150-
151-
if (enableCoreAutoReload) {
152-
console.log(`♻️♻️♻️ Core auto-reloading is ENABLED!`)
149+
if (process.env.NODE_ENV === "development") {
150+
console.log(`♻️♻️♻️ Core auto-reloading is ENABLED! Watching for changes in ${context.extensionPath}/**/*.ts`)
153151

154152
const watcher = vscode.workspace.createFileSystemWatcher(
155-
new vscode.RelativePattern(context.extensionPath, "src/**/*.ts"),
153+
new vscode.RelativePattern(context.extensionPath, "**/*.ts"),
156154
)
157155

158156
watcher.onDidChange((uri) => {

webview-ui/eslint.config.mjs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@ export default [
55
...reactConfig,
66
{
77
rules: {
8-
"@typescript-eslint/no-unused-vars": "off",
8+
"@typescript-eslint/no-unused-vars": [
9+
"error",
10+
{
11+
args: "all",
12+
ignoreRestSiblings: true,
13+
varsIgnorePattern: "^_",
14+
argsIgnorePattern: "^_",
15+
caughtErrorsIgnorePattern: "^_",
16+
},
17+
],
918
"@typescript-eslint/no-explicit-any": "off",
1019
"react/prop-types": "off",
1120
"react/display-name": "off",

0 commit comments

Comments
 (0)