Skip to content

Commit 3fb8bc1

Browse files
taltaseasonliang28
authored andcommitted
feat(webview): add batchNearby utility for smarter tool ask batching
Replace batchConsecutive with batchNearby in ChatView.tsx to allow merging same-type tool asks even when separated by ignorable messages (api_req_started/finished, empty text rows, reasoning). Semantic boundaries (user_feedback, visible text, completion_result, checkpoint_saved, error) stop the merge, preserving correct ordering. This fixes UX where models like qwen insert API request rows between tool calls, causing UI to show multiple 'Zoo wants to read this file' instead of a single batched prompt.
1 parent 6fec173 commit 3fb8bc1

60 files changed

Lines changed: 2801 additions & 9 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {
2+
SECRET_STATE_KEYS,
3+
dynamicProviders,
4+
kimiCodeDefaultModelId,
5+
providerSettingsSchema,
6+
providerSettingsSchemaDiscriminated,
7+
} from "../index.js"
8+
9+
describe("Kimi Code provider types", () => {
10+
it("registers Kimi Code as a dynamic provider with a distinct secret", () => {
11+
expect(dynamicProviders).toContain("kimi-code")
12+
expect(SECRET_STATE_KEYS).toContain("kimiCodeApiKey")
13+
expect(SECRET_STATE_KEYS).toContain("moonshotApiKey")
14+
})
15+
16+
it("parses OAuth and API-key settings independently from Moonshot", () => {
17+
expect(
18+
providerSettingsSchemaDiscriminated.parse({
19+
apiProvider: "kimi-code",
20+
kimiCodeAuthMethod: "api-key",
21+
kimiCodeApiKey: "kimi-key",
22+
apiModelId: kimiCodeDefaultModelId,
23+
}),
24+
).toMatchObject({ kimiCodeApiKey: "kimi-key" })
25+
expect(providerSettingsSchema.parse({ apiProvider: "kimi-code", kimiCodeAuthMethod: "oauth" })).toMatchObject({
26+
kimiCodeAuthMethod: "oauth",
27+
})
28+
})
29+
})

packages/types/src/__tests__/provider-identifiers.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const expectedProviderIdentifiers = [
4545
"gemini-cli",
4646
"mistral",
4747
"moonshot",
48+
"kimi-code",
4849
"minimax",
4950
"mimo",
5051
"openai-codex",
@@ -104,6 +105,7 @@ describe("provider identifiers", () => {
104105
providerIdentifiers.moonshot,
105106
providerIdentifiers.opencodeGo,
106107
providerIdentifiers.kenari,
108+
providerIdentifiers.kimiCode,
107109
])
108110
expect(localProviders).toEqual([providerIdentifiers.ollama, providerIdentifiers.lmstudio])
109111
expect(internalProviders).toEqual([providerIdentifiers.vscodeLm])

packages/types/src/global-settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ export const SECRET_STATE_KEYS = [
300300
"openAiNativeApiKey",
301301
"deepSeekApiKey",
302302
"moonshotApiKey",
303+
"kimiCodeApiKey",
303304
"mistralApiKey",
304305
"minimaxApiKey",
305306
"requestyApiKey",

packages/types/src/model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export const modelInfoSchema = z.object({
122122
})
123123
.optional(),
124124
description: z.string().optional(),
125+
displayName: z.string().optional(),
125126
// Default effort value for models that support reasoning effort
126127
reasoningEffort: reasoningEffortExtendedSchema.optional(),
127128
minTokensPerCachePoint: z.number().optional(),

packages/types/src/provider-identifiers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const providerIdentifiers = {
2828
geminiCli: "gemini-cli",
2929
mistral: "mistral",
3030
moonshot: "moonshot",
31+
kimiCode: "kimi-code",
3132
minimax: "minimax",
3233
mimo: "mimo",
3334
openaiCodex: "openai-codex",

packages/types/src/provider-settings.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export const dynamicProviders = [
5555
providerIdentifiers.moonshot,
5656
providerIdentifiers.opencodeGo,
5757
providerIdentifiers.kenari,
58+
providerIdentifiers.kimiCode,
5859
] as const
5960

6061
export type DynamicProvider = (typeof dynamicProviders)[number]
@@ -309,6 +310,14 @@ const moonshotSchema = apiModelIdProviderModelSchema.extend({
309310
moonshotApiKey: z.string().optional(),
310311
})
311312

313+
export const kimiCodeAuthMethodSchema = z.enum(["oauth", "api-key"])
314+
export type KimiCodeAuthMethod = z.infer<typeof kimiCodeAuthMethodSchema>
315+
316+
const kimiCodeSchema = apiModelIdProviderModelSchema.extend({
317+
kimiCodeAuthMethod: kimiCodeAuthMethodSchema.optional(),
318+
kimiCodeApiKey: z.string().optional(),
319+
})
320+
312321
const minimaxSchema = apiModelIdProviderModelSchema.extend({
313322
minimaxBaseUrl: z
314323
.union([z.literal("https://api.minimax.io/v1"), z.literal("https://api.minimaxi.com/v1")])
@@ -425,6 +434,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
425434
deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })),
426435
poeSchema.merge(z.object({ apiProvider: z.literal("poe") })),
427436
moonshotSchema.merge(z.object({ apiProvider: z.literal("moonshot") })),
437+
kimiCodeSchema.merge(z.object({ apiProvider: z.literal("kimi-code") })),
428438
minimaxSchema.merge(z.object({ apiProvider: z.literal("minimax") })),
429439
mimoSchema.merge(z.object({ apiProvider: z.literal("mimo") })),
430440
requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })),
@@ -463,6 +473,7 @@ export const providerSettingsSchema = z.object({
463473
...deepSeekSchema.shape,
464474
...poeSchema.shape,
465475
...moonshotSchema.shape,
476+
...kimiCodeSchema.shape,
466477
...minimaxSchema.shape,
467478
...mimoSchema.shape,
468479
...requestySchema.shape,
@@ -544,6 +555,7 @@ export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = {
544555
"gemini-cli": "apiModelId",
545556
mistral: "apiModelId",
546557
moonshot: "apiModelId",
558+
"kimi-code": "apiModelId",
547559
minimax: "apiModelId",
548560
mimo: "apiModelId",
549561
deepseek: "apiModelId",
@@ -652,6 +664,11 @@ export const MODELS_BY_PROVIDER: Record<
652664
label: "Moonshot",
653665
models: Object.keys(moonshotModels),
654666
},
667+
"kimi-code": {
668+
id: "kimi-code",
669+
label: "Kimi Code",
670+
models: [],
671+
},
655672
minimax: {
656673
id: "minimax",
657674
label: "MiniMax",

packages/types/src/providers/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export * from "./xai.js"
2525
export * from "./vercel-ai-gateway.js"
2626
export * from "./opencode-go.js"
2727
export * from "./kenari.js"
28+
export * from "./kimi-code.js"
2829
export * from "./zai.js"
2930
export * from "./minimax.js"
3031
export * from "./mimo.js"
@@ -53,6 +54,7 @@ import { xaiDefaultModelId } from "./xai.js"
5354
import { vercelAiGatewayDefaultModelId } from "./vercel-ai-gateway.js"
5455
import { opencodeGoDefaultModelId } from "./opencode-go.js"
5556
import { kenariDefaultModelId } from "./kenari.js"
57+
import { kimiCodeDefaultModelId } from "./kimi-code.js"
5658
import { internationalZAiDefaultModelId, mainlandZAiDefaultModelId } from "./zai.js"
5759
import { minimaxDefaultModelId } from "./minimax.js"
5860
import { mimoDefaultModelId } from "./mimo.js"
@@ -129,6 +131,8 @@ export function getProviderDefaultModelId(
129131
return opencodeGoDefaultModelId
130132
case "kenari":
131133
return kenariDefaultModelId
134+
case "kimi-code":
135+
return kimiCodeDefaultModelId
132136
case "zoo-gateway":
133137
return zooGatewayDefaultModelId
134138
case "anthropic":
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { ModelInfo } from "../model.js"
2+
3+
export const KIMI_CODE_BASE_URL = "https://api.kimi.com/coding/v1"
4+
export const kimiCodeDefaultModelId = "kimi-for-coding"
5+
6+
export const kimiCodeReasoningEfforts = ["low", "high", "max"] as const
7+
8+
export const kimiCodeDefaultModelInfo: ModelInfo = {
9+
contextWindow: 262_144,
10+
maxTokens: 32_768,
11+
supportsImages: false,
12+
supportsPromptCache: false,
13+
supportsReasoningEffort: [...kimiCodeReasoningEfforts],
14+
requiredReasoningEffort: true,
15+
reasoningEffort: "max",
16+
description: "Kimi Code's coding model for subscription and API-key access.",
17+
}
18+
19+
export const kimiCodeModels = {
20+
[kimiCodeDefaultModelId]: kimiCodeDefaultModelInfo,
21+
} as const satisfies Record<string, ModelInfo>
22+
23+
export type KimiCodeModelId = keyof typeof kimiCodeModels

packages/types/src/vscode-extension-host.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,14 @@ export type ExtensionState = Pick<
389389
mdmCompliant?: boolean
390390
taskSyncEnabled: boolean
391391
openAiCodexIsAuthenticated?: boolean
392+
kimiCodeIsAuthenticated?: boolean
393+
kimiCodeOAuthState?: {
394+
status: "idle" | "authorizing" | "polling" | "authenticated" | "error"
395+
userCode?: string
396+
verificationUri?: string
397+
expiresAt?: number
398+
error?: string
399+
}
392400
zooCodeIsAuthenticated?: boolean
393401
zooCodeUserName?: string
394402
zooCodeUserEmail?: string
@@ -538,6 +546,8 @@ export interface WebviewMessage {
538546
| "rooCloudManualUrl"
539547
| "openAiCodexSignIn"
540548
| "openAiCodexSignOut"
549+
| "kimiCodeSignIn"
550+
| "kimiCodeSignOut"
541551
| "zooCodeSignOut"
542552
| "switchOrganization"
543553
| "condenseTaskContextRequest"

src/api/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
OpenAiNativeHandler,
2121
DeepSeekHandler,
2222
MoonshotHandler,
23+
KimiCodeHandler,
2324
MistralHandler,
2425
VsCodeLmHandler,
2526
RequestyHandler,
@@ -178,6 +179,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
178179
return new QwenCodeHandler(options)
179180
case "moonshot":
180181
return new MoonshotHandler(options)
182+
case "kimi-code":
183+
return new KimiCodeHandler(options)
181184
case "vscode-lm":
182185
return new VsCodeLmHandler(options)
183186
case "mistral":

0 commit comments

Comments
 (0)