Skip to content

Commit c20d51e

Browse files
committed
Merge remote-tracking branch 'origin/main' into provider_reasoning
2 parents 8d056f1 + 3cbdbc2 commit c20d51e

40 files changed

+592
-30
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Roo Code Changelog
22

3+
## [3.29.2] - 2025-10-27
4+
5+
- Add support for LongCat-Flash-Thinking-FP8 models in Chutes AI provider (#8425 by @leakless21, PR by @roomote)
6+
- Fix: Remove specific Claude model version from settings descriptions to avoid outdated references (#8435 by @rwydaegh, PR by @roomote)
7+
- Fix: Correct caching logic in Roo provider to improve performance (thanks @mrubens!)
8+
- Fix: Ensure free models don't display pricing information in the UI (thanks @mrubens!)
9+
310
## [3.29.1] - 2025-10-26
411

512
![3.29.1 Release - Window Cleaning](/releases/3.29.1-release.png)

packages/types/src/global-settings.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ export const globalSettingsSchema = z.object({
9393
autoCondenseContextPercent: z.number().optional(),
9494
maxConcurrentFileReads: z.number().optional(),
9595

96+
/**
97+
* Whether to include current time in the environment details
98+
* @default true
99+
*/
100+
includeCurrentTime: z.boolean().optional(),
101+
/**
102+
* Whether to include current cost in the environment details
103+
* @default true
104+
*/
105+
includeCurrentCost: z.boolean().optional(),
106+
96107
/**
97108
* Whether to include diagnostic messages (errors, warnings) in tool outputs
98109
* @default true

packages/types/src/model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ export const modelInfoSchema = z.object({
7878
cachableFields: z.array(z.string()).optional(),
7979
// Flag to indicate if the model is deprecated and should not be used
8080
deprecated: z.boolean().optional(),
81+
// Flag to indicate if the model is free (no cost)
82+
isFree: z.boolean().optional(),
8183
/**
8284
* Service tiers with pricing information.
8385
* Each tier can have a name (for OpenAI service tiers) and pricing overrides.

packages/types/src/providers/chutes.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ChutesModelId =
3535
| "zai-org/GLM-4.5-turbo"
3636
| "zai-org/GLM-4.6-FP8"
3737
| "zai-org/GLM-4.6-turbo"
38+
| "meituan-longcat/LongCat-Flash-Thinking-FP8"
3839
| "moonshotai/Kimi-K2-Instruct-75k"
3940
| "moonshotai/Kimi-K2-Instruct-0905"
4041
| "Qwen/Qwen3-235B-A22B-Thinking-2507"
@@ -339,6 +340,16 @@ export const chutesModels = {
339340
outputPrice: 3.25,
340341
description: "GLM-4.6-turbo model with 200K-token context window, optimized for fast inference.",
341342
},
343+
"meituan-longcat/LongCat-Flash-Thinking-FP8": {
344+
maxTokens: 32768,
345+
contextWindow: 128000,
346+
supportsImages: false,
347+
supportsPromptCache: false,
348+
inputPrice: 0,
349+
outputPrice: 0,
350+
description:
351+
"LongCat Flash Thinking FP8 model with 128K context window, optimized for complex reasoning and coding tasks.",
352+
},
342353
"Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": {
343354
maxTokens: 32768,
344355
contextWindow: 262144,

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,74 @@ describe("ChutesHandler", () => {
275275
)
276276
})
277277

278+
it("should return zai-org/GLM-4.6-FP8 model with correct configuration", () => {
279+
const testModelId: ChutesModelId = "zai-org/GLM-4.6-FP8"
280+
const handlerWithModel = new ChutesHandler({
281+
apiModelId: testModelId,
282+
chutesApiKey: "test-chutes-api-key",
283+
})
284+
const model = handlerWithModel.getModel()
285+
expect(model.id).toBe(testModelId)
286+
expect(model.info).toEqual(
287+
expect.objectContaining({
288+
maxTokens: 32768,
289+
contextWindow: 202752,
290+
supportsImages: false,
291+
supportsPromptCache: false,
292+
inputPrice: 0,
293+
outputPrice: 0,
294+
description:
295+
"GLM-4.6 introduces major upgrades over GLM-4.5, including a longer 200K-token context window for complex tasks, stronger coding performance in benchmarks and real-world tools (such as Claude Code, Cline, Roo Code, and Kilo Code), improved reasoning with tool use during inference, more capable and efficient agent integration, and refined writing that better matches human style, readability, and natural role-play scenarios.",
296+
temperature: 0.5, // Default temperature for non-DeepSeek models
297+
}),
298+
)
299+
})
300+
301+
it("should return zai-org/GLM-4.6-turbo model with correct configuration", () => {
302+
const testModelId: ChutesModelId = "zai-org/GLM-4.6-turbo"
303+
const handlerWithModel = new ChutesHandler({
304+
apiModelId: testModelId,
305+
chutesApiKey: "test-chutes-api-key",
306+
})
307+
const model = handlerWithModel.getModel()
308+
expect(model.id).toBe(testModelId)
309+
expect(model.info).toEqual(
310+
expect.objectContaining({
311+
maxTokens: 202752,
312+
contextWindow: 202752,
313+
supportsImages: false,
314+
supportsPromptCache: false,
315+
inputPrice: 1.15,
316+
outputPrice: 3.25,
317+
description: "GLM-4.6-turbo model with 200K-token context window, optimized for fast inference.",
318+
temperature: 0.5, // Default temperature for non-DeepSeek models
319+
}),
320+
)
321+
})
322+
323+
it("should return meituan-longcat/LongCat-Flash-Thinking-FP8 model with correct configuration", () => {
324+
const testModelId: ChutesModelId = "meituan-longcat/LongCat-Flash-Thinking-FP8"
325+
const handlerWithModel = new ChutesHandler({
326+
apiModelId: testModelId,
327+
chutesApiKey: "test-chutes-api-key",
328+
})
329+
const model = handlerWithModel.getModel()
330+
expect(model.id).toBe(testModelId)
331+
expect(model.info).toEqual(
332+
expect.objectContaining({
333+
maxTokens: 32768,
334+
contextWindow: 128000,
335+
supportsImages: false,
336+
supportsPromptCache: false,
337+
inputPrice: 0,
338+
outputPrice: 0,
339+
description:
340+
"LongCat Flash Thinking FP8 model with 128K context window, optimized for complex reasoning and coding tasks.",
341+
temperature: 0.5, // Default temperature for non-DeepSeek models
342+
}),
343+
)
344+
})
345+
278346
it("should return Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 model with correct configuration", () => {
279347
const testModelId: ChutesModelId = "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8"
280348
const handlerWithModel = new ChutesHandler({

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

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,4 +589,94 @@ describe("getLiteLLMModels", () => {
589589

590590
const result = await getLiteLLMModels("test-api-key", "http://localhost:4000")
591591
})
592+
593+
it("prefers max_output_tokens over max_tokens when both are present", async () => {
594+
const mockResponse = {
595+
data: {
596+
data: [
597+
{
598+
model_name: "claude-3-5-sonnet-4-5",
599+
model_info: {
600+
max_tokens: 200000, // This should be ignored
601+
max_output_tokens: 64000, // This should be used
602+
max_input_tokens: 200000,
603+
supports_vision: true,
604+
supports_prompt_caching: false,
605+
supports_computer_use: true,
606+
},
607+
litellm_params: {
608+
model: "anthropic/claude-3-5-sonnet-4-5",
609+
},
610+
},
611+
{
612+
model_name: "model-with-only-max-tokens",
613+
model_info: {
614+
max_tokens: 8192, // This should be used as fallback
615+
// No max_output_tokens
616+
max_input_tokens: 128000,
617+
supports_vision: false,
618+
},
619+
litellm_params: {
620+
model: "test/model-with-only-max-tokens",
621+
},
622+
},
623+
{
624+
model_name: "model-with-only-max-output-tokens",
625+
model_info: {
626+
// No max_tokens
627+
max_output_tokens: 16384, // This should be used
628+
max_input_tokens: 100000,
629+
supports_vision: false,
630+
},
631+
litellm_params: {
632+
model: "test/model-with-only-max-output-tokens",
633+
},
634+
},
635+
],
636+
},
637+
}
638+
639+
mockedAxios.get.mockResolvedValue(mockResponse)
640+
641+
const result = await getLiteLLMModels("test-api-key", "http://localhost:4000")
642+
643+
// Should use max_output_tokens (64000) instead of max_tokens (200000)
644+
expect(result["claude-3-5-sonnet-4-5"]).toEqual({
645+
maxTokens: 64000,
646+
contextWindow: 200000,
647+
supportsImages: true,
648+
supportsPromptCache: false,
649+
inputPrice: undefined,
650+
outputPrice: undefined,
651+
cacheWritesPrice: undefined,
652+
cacheReadsPrice: undefined,
653+
description: "claude-3-5-sonnet-4-5 via LiteLLM proxy",
654+
})
655+
656+
// Should fall back to max_tokens when max_output_tokens is not present
657+
expect(result["model-with-only-max-tokens"]).toEqual({
658+
maxTokens: 8192,
659+
contextWindow: 128000,
660+
supportsImages: false,
661+
supportsPromptCache: false,
662+
inputPrice: undefined,
663+
outputPrice: undefined,
664+
cacheWritesPrice: undefined,
665+
cacheReadsPrice: undefined,
666+
description: "model-with-only-max-tokens via LiteLLM proxy",
667+
})
668+
669+
// Should use max_output_tokens when max_tokens is not present
670+
expect(result["model-with-only-max-output-tokens"]).toEqual({
671+
maxTokens: 16384,
672+
contextWindow: 100000,
673+
supportsImages: false,
674+
supportsPromptCache: false,
675+
inputPrice: undefined,
676+
outputPrice: undefined,
677+
cacheWritesPrice: undefined,
678+
cacheReadsPrice: undefined,
679+
description: "model-with-only-max-output-tokens via LiteLLM proxy",
680+
})
681+
})
592682
})

src/api/providers/fetchers/litellm.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,9 @@ export async function getLiteLLMModels(apiKey: string, baseUrl: string): Promise
4141
if (!modelName || !modelInfo || !litellmModelName) continue
4242

4343
models[modelName] = {
44-
maxTokens: modelInfo.max_tokens || 8192,
44+
maxTokens: modelInfo.max_output_tokens || modelInfo.max_tokens || 8192,
4545
contextWindow: modelInfo.max_input_tokens || 200000,
4646
supportsImages: Boolean(modelInfo.supports_vision),
47-
// litellm_params.model may have a prefix like openrouter/
4847
supportsPromptCache: Boolean(modelInfo.supports_prompt_caching),
4948
inputPrice: modelInfo.input_cost_per_token ? modelInfo.input_cost_per_token * 1000000 : undefined,
5049
outputPrice: modelInfo.output_cost_per_token

src/api/providers/fetchers/roo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
9696
cacheReadsPrice: cacheReadPrice,
9797
description: model.description || model.name,
9898
deprecated: model.deprecated || false,
99+
isFree: tags.includes("free"),
99100
}
100101
}
101102

src/api/providers/roo.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,17 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<string> {
181181
}
182182

183183
if (lastUsage) {
184+
// Check if the current model is marked as free
185+
const model = this.getModel()
186+
const isFreeModel = model.info.isFree ?? false
187+
184188
yield {
185189
type: "usage",
186190
inputTokens: lastUsage.prompt_tokens || 0,
187191
outputTokens: lastUsage.completion_tokens || 0,
188192
cacheWriteTokens: lastUsage.cache_creation_input_tokens,
189193
cacheReadTokens: lastUsage.prompt_tokens_details?.cached_tokens,
190-
totalCost: lastUsage.cost ?? 0,
194+
totalCost: isFreeModel ? 0 : (lastUsage.cost ?? 0),
191195
}
192196
}
193197
}

src/core/environment/getEnvironmentDetails.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -190,21 +190,28 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
190190
details += terminalDetails
191191
}
192192

193-
// Add current time information with timezone.
194-
const now = new Date()
195-
196-
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
197-
const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation
198-
const timeZoneOffsetHours = Math.floor(Math.abs(timeZoneOffset))
199-
const timeZoneOffsetMinutes = Math.abs(Math.round((Math.abs(timeZoneOffset) - timeZoneOffsetHours) * 60))
200-
const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : "-"}${timeZoneOffsetHours}:${timeZoneOffsetMinutes.toString().padStart(2, "0")}`
201-
details += `\n\n# Current Time\nCurrent time in ISO 8601 UTC format: ${now.toISOString()}\nUser time zone: ${timeZone}, UTC${timeZoneOffsetStr}`
202-
203-
// Add context tokens information.
204-
const { contextTokens, totalCost } = getApiMetrics(cline.clineMessages)
205-
const { id: modelId } = cline.api.getModel()
193+
// Get settings for time and cost display
194+
const { includeCurrentTime = true, includeCurrentCost = true } = state ?? {}
195+
196+
// Add current time information with timezone (if enabled).
197+
if (includeCurrentTime) {
198+
const now = new Date()
199+
200+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
201+
const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation
202+
const timeZoneOffsetHours = Math.floor(Math.abs(timeZoneOffset))
203+
const timeZoneOffsetMinutes = Math.abs(Math.round((Math.abs(timeZoneOffset) - timeZoneOffsetHours) * 60))
204+
const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : "-"}${timeZoneOffsetHours}:${timeZoneOffsetMinutes.toString().padStart(2, "0")}`
205+
details += `\n\n# Current Time\nCurrent time in ISO 8601 UTC format: ${now.toISOString()}\nUser time zone: ${timeZone}, UTC${timeZoneOffsetStr}`
206+
}
206207

207-
details += `\n\n# Current Cost\n${totalCost !== null ? `$${totalCost.toFixed(2)}` : "(Not available)"}`
208+
// Add context tokens information (if enabled).
209+
if (includeCurrentCost) {
210+
const { totalCost } = getApiMetrics(cline.clineMessages)
211+
details += `\n\n# Current Cost\n${totalCost !== null ? `$${totalCost.toFixed(2)}` : "(Not available)"}`
212+
}
213+
214+
const { id: modelId } = cline.api.getModel()
208215

209216
// Add current mode and any mode-specific warnings.
210217
const {

0 commit comments

Comments
 (0)