Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/loud-lights-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---

Added gemini-3-flash-preview model
18 changes: 17 additions & 1 deletion packages/types/src/providers/gemini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { ModelInfo } from "../model.js"
// https://ai.google.dev/gemini-api/docs/models/gemini
export type GeminiModelId = keyof typeof geminiModels

export const geminiDefaultModelId: GeminiModelId = "gemini-3-pro-preview" // kilocode_change
export const geminiDefaultModelId: GeminiModelId = "gemini-3-pro-preview"

export const geminiModels = {
"gemini-3-pro-preview": {
Expand Down Expand Up @@ -31,6 +31,22 @@ export const geminiModels = {
},
],
},
"gemini-3-flash-preview": {
maxTokens: 65_536,
contextWindow: 1_048_576,
supportsImages: true,
supportsNativeTools: true,
defaultToolProtocol: "native",
supportsPromptCache: true,
supportsReasoningEffort: ["minimal", "low", "medium", "high"],
reasoningEffort: "medium",
supportsTemperature: true,
defaultTemperature: 1,
inputPrice: 0.3,
outputPrice: 2.5,
cacheReadsPrice: 0.075,
cacheWritesPrice: 1.0,
},
// 2.5 Pro models
"gemini-2.5-pro": {
maxTokens: 64_000,
Expand Down
29 changes: 16 additions & 13 deletions packages/types/src/providers/vertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,6 @@ export type VertexModelId = keyof typeof vertexModels
export const vertexDefaultModelId: VertexModelId = "claude-sonnet-4-5@20250929"

export const vertexModels = {
"gemini-3-flash-preview": {
maxTokens: 65_536,
contextWindow: 1_048_576,
supportsImages: true,
supportsNativeTools: true,
supportsPromptCache: true,
supportsReasoningEffort: ["minimal", "low", "medium", "high"],
reasoningEffort: "medium",
supportsTemperature: true,
defaultTemperature: 1,
inputPrice: 0.5,
outputPrice: 3.0,
},
"gemini-3-pro-preview": {
maxTokens: 65_536,
contextWindow: 1_048_576,
Expand All @@ -44,6 +31,22 @@ export const vertexModels = {
},
],
},
"gemini-3-flash-preview": {
maxTokens: 65_536,
contextWindow: 1_048_576,
supportsImages: true,
supportsNativeTools: true,
defaultToolProtocol: "native",
supportsPromptCache: true,
supportsReasoningEffort: ["minimal", "low", "medium", "high"],
reasoningEffort: "medium",
supportsTemperature: true,
defaultTemperature: 1,
inputPrice: 0.3,
outputPrice: 2.5,
cacheReadsPrice: 0.075,
cacheWritesPrice: 1.0,
},
"gemini-2.5-flash-preview-05-20:thinking": {
maxTokens: 65_535,
contextWindow: 1_048_576,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, it, expect } from "vitest"
import { removePrefixOverlap } from "../removePrefixOverlap.js"

describe("removePrefixOverlap", () => {
it("should remove full prefix match", () => {
expect(removePrefixOverlap("hello world", "hello ")).toBe("world")
})

it("should remove trimmed prefix when prefix has extra whitespace", () => {
expect(removePrefixOverlap("hello world", " hello ")).toBe(" world")
})

it("should remove partial word overlap", () => {
expect(removePrefixOverlap("hello world", "hel")).toBe("lo world")
})

it("should return completion as-is when no overlap", () => {
expect(removePrefixOverlap("world", "hello")).toBe("world")
})

it("should use last line of multi-line prefix", () => {
expect(removePrefixOverlap("hello world", "line 1\nline 2\nhello ")).toBe("world")
})

it("should handle empty strings", () => {
expect(removePrefixOverlap("hello world", "")).toBe("hello world")
expect(removePrefixOverlap("", "hello")).toBe("")
})

it("should be case-sensitive", () => {
expect(removePrefixOverlap("hello world", "HELLO ")).toBe("hello world")
})
})
17 changes: 2 additions & 15 deletions src/services/continuedev/core/autocomplete/postprocessing/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { longestCommonSubsequence } from "../../util/lcs.js"
import { lineIsRepeated } from "../util/textSimilarity.js"
import { removePrefixOverlap } from "./removePrefixOverlap.js"

function rewritesLineAbove(completion: string, prefix: string): boolean {
const lineAbove = prefix
Expand Down Expand Up @@ -143,21 +144,7 @@ export function postprocessCompletion({
}

if (llm.model.includes("mercury") || llm.model.includes("granite")) {
// Granite tends to repeat the start of the line in the completion output
const prefixEnd = prefix.split("\n").pop()
if (prefixEnd) {
if (completion.startsWith(prefixEnd)) {
completion = completion.slice(prefixEnd.length)
} else {
const trimmedPrefix = prefixEnd.trim()
const lastWord = trimmedPrefix.split(/\s+/).pop()
if (lastWord && completion.startsWith(lastWord)) {
completion = completion.slice(lastWord.length)
} else if (completion.startsWith(trimmedPrefix)) {
completion = completion.slice(trimmedPrefix.length)
}
}
}
completion = removePrefixOverlap(completion, prefix)
}

// // If completion starts with multiple whitespaces, but the cursor is at the end of the line
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function removePrefixOverlap(completion: string, prefix: string): string {
const prefixEnd = prefix.split("\n").pop()
if (prefixEnd) {
if (completion.startsWith(prefixEnd)) {
completion = completion.slice(prefixEnd.length)
} else {
const trimmedPrefix = prefixEnd.trim()
const lastWord = trimmedPrefix.split(/\s+/).pop()
if (lastWord && completion.startsWith(lastWord)) {
completion = completion.slice(lastWord.length)
} else if (completion.startsWith(trimmedPrefix)) {
completion = completion.slice(trimmedPrefix.length)
}
}
}
return completion
}
19 changes: 12 additions & 7 deletions src/services/ghost/chat-autocomplete/ChatTextAreaAutocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { GhostModel } from "../GhostModel"
import { ProviderSettingsManager } from "../../../core/config/ProviderSettingsManager"
import { VisibleCodeContext } from "../types"
import { ApiStreamChunk } from "../../../api/transform/stream"
import { removePrefixOverlap } from "../../continuedev/core/autocomplete/postprocessing/removePrefixOverlap.js"

/**
* Service for providing FIM-based autocomplete suggestions in ChatTextArea
Expand Down Expand Up @@ -64,6 +65,9 @@ export class ChatTextAreaAutocomplete {
}

const cleanedSuggestion = this.cleanSuggestion(response, userText)
console.log(
`[ChatAutocomplete] prefix: ${JSON.stringify(userText)} | response: ${JSON.stringify(response)} | cleanedSuggestion: ${JSON.stringify(cleanedSuggestion)}`,
)

return { suggestion: cleanedSuggestion }
}
Expand All @@ -81,6 +85,8 @@ export class ChatTextAreaAutocomplete {
- Use context from visible code if relevant
- NEVER repeat what the user already typed
- NEVER start with comments (//, /*, #)
- If the user is in the middle of typing a word (e.g., "hel"), include the COMPLETE word in your response (e.g., "hello world" not just "lo world")
- This allows proper prefix matching to remove the overlap correctly
- Return ONLY the completion text, no explanations or formatting`
}

Expand All @@ -90,7 +96,9 @@ export class ChatTextAreaAutocomplete {
private getChatUserPrompt(prefix: string): string {
return `${prefix}

TASK: Complete the user's message naturally. Return ONLY the completion text (what comes next), no explanations.`
TASK: Complete the user's message naturally.
- If the user is mid-word (e.g., typed "hel"), return the COMPLETE word (e.g., "hello world") so prefix matching can work correctly
- Return ONLY the completion text (what comes next), no explanations.`
}

/**
Expand Down Expand Up @@ -146,18 +154,15 @@ TASK: Complete the user's message naturally. Return ONLY the completion text (wh
* and filtering out unwanted patterns like comments
*/
private cleanSuggestion(suggestion: string, userText: string): string {
let cleaned = suggestion.trim()
let cleaned = suggestion

if (cleaned.startsWith(userText)) {
cleaned = cleaned.substring(userText.length)
}
cleaned = removePrefixOverlap(cleaned, userText)

const firstNewline = cleaned.indexOf("\n")
if (firstNewline !== -1) {
cleaned = cleaned.substring(0, firstNewline)
}

cleaned = cleaned.trimStart()
cleaned = cleaned.trimEnd() // Do NOT trim the end of the suggestion

// Filter out suggestions that start with comment patterns
// This happens because the context uses // prefixes for labels
Expand Down
Loading