Skip to content

Commit 91f556f

Browse files
authored
Merge pull request #3400 from Kilo-Org/beatlevic/fim-prompt
FIM prompt without XML parsing
2 parents ea48de4 + a5a7d2d commit 91f556f

File tree

190 files changed

+2506
-2381
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

190 files changed

+2506
-2381
lines changed

src/services/ghost/__tests__/GhostRecentOperations.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ describe("GhostRecentOperations", () => {
124124
const { userPrompt } = autoTriggerStrategy.getPrompts(autocompleteInput, prefix, suffix, languageId)
125125

126126
// Verify that the prompt includes the recent operations section
127-
// The new strategy system uses "## Recent Typing" format
128-
expect(userPrompt).toContain("## Recent Typing")
127+
// The strategy system uses "<RECENT_EDITS>" XML format
128+
expect(userPrompt).toContain("<RECENT_EDITS>")
129129
})
130130

131131
it("should not include recent operations in the prompt when not available", async () => {

src/services/ghost/__tests__/GhostServiceManager.spec.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,14 @@ describe("GhostServiceManager", () => {
111111
expect(result.suggestions.hasSuggestions()).toBe(false)
112112
})
113113

114-
it("should handle invalid XML format", async () => {
114+
it("should handle invalid COMPLETION format", async () => {
115115
const initialContent = `console.log('test');`
116116
const { context } = await setupTestDocument("invalid.js", initialContent)
117117

118-
// Test invalid XML format
119-
const invalidXML = "This is not a valid XML format"
118+
const invalidCOMPLETION = "This is not a valid COMPLETION format"
120119
const position = context.range?.start ?? context.document.positionAt(0)
121120
const { prefix, suffix } = extractPrefixSuffix(context.document, position)
122-
const result = parseGhostResponse(invalidXML, prefix, suffix)
121+
const result = parseGhostResponse(invalidCOMPLETION, prefix, suffix)
123122
expect(result.suggestions.hasSuggestions()).toBe(false)
124123
})
125124

@@ -133,14 +132,12 @@ describe("GhostServiceManager", () => {
133132
openFiles: [],
134133
}
135134

136-
// Use XML format
137-
const xmlResponse = `<change><search><![CDATA[console.log('test');]]></search><replace><![CDATA[// Added comment
138-
console.log('test');]]></replace></change>`
135+
const completionResponse = `<COMPLETION>// Added comment
136+
console.log('test');</COMPLETION>`
139137

140138
const position = context.range?.start ?? context.document.positionAt(0)
141139
const { prefix, suffix } = extractPrefixSuffix(context.document, position)
142-
const result = parseGhostResponse(xmlResponse, prefix, suffix)
143-
// Should work with the XML format
140+
const result = parseGhostResponse(completionResponse, prefix, suffix)
144141
expect(result.suggestions.hasSuggestions()).toBe(true)
145142
})
146143
})
Lines changed: 99 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,94 @@
11
import { AutocompleteInput } from "../types"
22
import { CURSOR_MARKER } from "./ghostConstants"
3-
import { isCommentLine, cleanComment } from "./CommentHelpers"
43
import type { TextDocument, Range } from "vscode"
54

65
export function getBaseSystemInstructions(): string {
7-
return `CRITICAL OUTPUT FORMAT:
8-
You must respond with XML-formatted changes ONLY. No explanations or text outside XML tags.
9-
10-
Format: <change><search><![CDATA[exact_code]]></search><replace><![CDATA[new_code]]></replace></change>
11-
12-
MANDATORY XML STRUCTURE RULES:
13-
- Every <change> tag MUST have a closing </change> tag
14-
- Every <search> tag MUST have a closing </search> tag
15-
- Every <replace> tag MUST have a closing </replace> tag
16-
- Every <![CDATA[ MUST have a closing ]]>
17-
- XML tags should be properly formatted and nested
18-
- Multiple <change> blocks allowed for different modifications
19-
20-
CHANGE ORDERING PRIORITY:
21-
- CRITICAL: Order all <change> blocks by proximity to the cursor marker (<<<AUTOCOMPLETE_HERE>>>)
22-
- Put changes closest to the cursor marker FIRST in your response
23-
- This allows immediate display of the most relevant suggestions to the user
24-
- Changes further from the cursor should come later in the response
25-
- Measure proximity by line distance from the cursor marker position
26-
27-
CONTENT MATCHING RULES:
28-
- Search content must match EXACTLY (including whitespace, indentation, and line breaks)
29-
- Use CDATA wrappers for all code content
30-
- Preserve all line breaks and formatting within CDATA sections
31-
- Never generate overlapping changes
32-
- The <search> block must contain exact text that exists in the code
33-
- If you can't find exact match, don't generate that change
34-
35-
EXAMPLE:
36-
<change><search><![CDATA[function example() {
37-
// old code
38-
}]]></search><replace><![CDATA[function example() {
39-
// new code
40-
}]]></replace></change>
41-
42-
--
6+
return `You are a HOLE FILLER. You are provided with a file containing holes, formatted as '{{FILL_HERE}}'. Your TASK is to complete with a string to replace this hole with, inside a <COMPLETION/> XML tag, including context-aware indentation, if needed. All completions MUST be truthful, accurate, well-written and correct.
7+
8+
## Context Tags
9+
<LANGUAGE>: file language | <RECENT_EDITS>: recent changes | <QUERY>: code with {{FILL_HERE}}
10+
11+
## EXAMPLE QUERY:
12+
13+
<QUERY>
14+
function sum_evens(lim) {
15+
var sum = 0;
16+
for (var i = 0; i < lim; ++i) {
17+
{{FILL_HERE}}
18+
}
19+
return sum;
20+
}
21+
</QUERY>
22+
23+
TASK: Fill the {{FILL_HERE}} hole.
24+
25+
## CORRECT COMPLETION
26+
27+
<COMPLETION>if (i % 2 === 0) {
28+
sum += i;
29+
}</COMPLETION>
30+
31+
## EXAMPLE QUERY:
32+
33+
<QUERY>
34+
def sum_list(lst):
35+
total = 0
36+
for x in lst:
37+
{{FILL_HERE}}
38+
return total
39+
40+
print sum_list([1, 2, 3])
41+
</QUERY>
42+
43+
## CORRECT COMPLETION:
44+
45+
<COMPLETION> total += x</COMPLETION>
46+
47+
## EXAMPLE QUERY:
48+
49+
<QUERY>
50+
// data Tree a = Node (Tree a) (Tree a) | Leaf a
51+
52+
// sum :: Tree Int -> Int
53+
// sum (Node lft rgt) = sum lft + sum rgt
54+
// sum (Leaf val) = val
55+
56+
// convert to TypeScript:
57+
{{FILL_HERE}}
58+
</QUERY>
59+
60+
## CORRECT COMPLETION:
61+
62+
<COMPLETION>type Tree<T>
63+
= {$:"Node", lft: Tree<T>, rgt: Tree<T>}
64+
| {$:"Leaf", val: T};
65+
66+
function sum(tree: Tree<number>): number {
67+
switch (tree.$) {
68+
case "Node":
69+
return sum(tree.lft) + sum(tree.rgt);
70+
case "Leaf":
71+
return tree.val;
72+
}
73+
}</COMPLETION>
74+
75+
## EXAMPLE QUERY:
76+
77+
The 5th {{FILL_HERE}} is Jupiter.
78+
79+
## CORRECT COMPLETION:
80+
81+
<COMPLETION>planet from the Sun</COMPLETION>
82+
83+
## EXAMPLE QUERY:
84+
85+
function hypothenuse(a, b) {
86+
return Math.sqrt({{FILL_HERE}}b ** 2);
87+
}
88+
89+
## CORRECT COMPLETION:
90+
91+
<COMPLETION>a ** 2 + </COMPLETION>
4392
4493
`
4594
}
@@ -56,20 +105,6 @@ export function addCursorMarker(document: TextDocument, range?: Range): string {
56105
}
57106

58107
export class AutoTriggerStrategy {
59-
shouldTreatAsComment(prefix: string, languageId: string): boolean {
60-
const lines = prefix.split("\n")
61-
const currentLine = lines[lines.length - 1].trim() || ""
62-
const previousLine = lines.length > 1 ? lines[lines.length - 2].trim() : ""
63-
64-
if (isCommentLine(currentLine, languageId)) {
65-
return true
66-
} else if (currentLine === "" && previousLine) {
67-
return isCommentLine(previousLine, languageId)
68-
} else {
69-
return false
70-
}
71-
}
72-
73108
getPrompts(
74109
autocompleteInput: AutocompleteInput,
75110
prefix: string,
@@ -79,24 +114,17 @@ export class AutoTriggerStrategy {
79114
systemPrompt: string
80115
userPrompt: string
81116
} {
82-
if (this.shouldTreatAsComment(prefix, languageId)) {
83-
return {
84-
systemPrompt: this.getCommentsSystemInstructions(),
85-
userPrompt: this.getCommentsUserPrompt(prefix, suffix, languageId),
86-
}
87-
} else {
88-
return {
89-
systemPrompt: this.getSystemInstructions(),
90-
userPrompt: this.getUserPrompt(autocompleteInput, prefix, suffix, languageId),
91-
}
117+
return {
118+
systemPrompt: this.getSystemInstructions(),
119+
userPrompt: this.getUserPrompt(autocompleteInput, prefix, suffix, languageId),
92120
}
93121
}
94122

95123
getSystemInstructions(): string {
96124
return (
97125
getBaseSystemInstructions() +
98-
`Task: Subtle Auto-Completion
99-
Provide non-intrusive completions after a typing pause. Be conservative and helpful.
126+
`Task: Auto-Completion
127+
Provide a subtle, non-intrusive completion after a typing pause.
100128
101129
`
102130
)
@@ -106,108 +134,24 @@ Provide non-intrusive completions after a typing pause. Be conservative and help
106134
* Build minimal prompt for auto-trigger
107135
*/
108136
getUserPrompt(autocompleteInput: AutocompleteInput, prefix: string, suffix: string, languageId: string): string {
109-
let prompt = ""
137+
let prompt = `<LANGUAGE>${languageId}</LANGUAGE>\n\n`
110138

111-
// Start with recent typing context from autocompleteInput
112139
if (autocompleteInput.recentlyEditedRanges && autocompleteInput.recentlyEditedRanges.length > 0) {
113-
prompt += "## Recent Typing\n"
140+
prompt += "<RECENT_EDITS>\n"
114141
autocompleteInput.recentlyEditedRanges.forEach((range, index) => {
115142
const description = `Edited ${range.filepath} at line ${range.range.start.line}`
116143
prompt += `${index + 1}. ${description}\n`
117144
})
118-
prompt += "\n"
145+
prompt += "</RECENT_EDITS>\n\n"
119146
}
120147

121-
// Add current position from autocompleteInput
122-
const line = autocompleteInput.pos.line + 1
123-
const char = autocompleteInput.pos.character + 1
124-
prompt += `## Current Position\n`
125-
prompt += `Line ${line}, Character ${char}\n\n`
126-
127-
// Add the full document with cursor marker
128-
const codeWithCursor = `${prefix}${CURSOR_MARKER}${suffix}`
129-
prompt += "## Full Code\n"
130-
prompt += `\`\`\`${languageId}\n${codeWithCursor}\n\`\`\`\n\n`
131-
132-
// Add specific instructions
133-
prompt += "## Instructions\n"
134-
prompt += `Provide a minimal, obvious completion at the cursor position (${CURSOR_MARKER}).\n`
135-
prompt += `IMPORTANT: Your <search> block must include the cursor marker ${CURSOR_MARKER} to target the exact location.\n`
136-
prompt += `Include surrounding text with the cursor marker to avoid conflicts with similar code elsewhere.\n`
137-
prompt += "Complete only what the user appears to be typing.\n"
138-
prompt += "Single line preferred, no new features.\n"
139-
prompt += "If nothing obvious to complete, provide NO suggestion.\n"
148+
prompt += `<QUERY>
149+
${prefix}{{FILL_HERE}}${suffix}
150+
</QUERY>
140151
141-
return prompt
142-
}
152+
TASK: Fill the {{FILL_HERE}} hole. Answer only with the CORRECT completion, and NOTHING ELSE. Do it now.
153+
Return the COMPLETION tags`
143154

144-
getCommentsSystemInstructions(): string {
145-
return (
146-
getBaseSystemInstructions() +
147-
`You are an expert code generation assistant that implements code based on comments.
148-
149-
## Core Responsibilities:
150-
1. Read and understand the comment's intent
151-
2. Generate complete, working code that fulfills the comment's requirements
152-
3. Follow the existing code style and patterns
153-
4. Add appropriate error handling
154-
5. Include necessary imports or dependencies
155-
156-
## Code Generation Guidelines:
157-
- Generate only the code that directly implements the comment
158-
- Match the indentation level of the comment
159-
- Use descriptive variable and function names
160-
- Follow language-specific best practices
161-
- Add type annotations where appropriate
162-
- Consider edge cases mentioned in the comment
163-
- If the comment describes multiple steps, implement them all
164-
165-
## Comment Types to Handle:
166-
- TODO comments: Implement the described task
167-
- FIXME comments: Fix the described issue
168-
- Implementation comments: Generate the described functionality
169-
- Algorithm descriptions: Implement the described algorithm
170-
- API/Interface descriptions: Implement the described interface
171-
172-
## Output Requirements:
173-
- Generate ONLY executable code that implements the comment
174-
- PRESERVE all existing code and comments in the provided context
175-
- Do not repeat the comment you are implementing in your output
176-
- Do not add explanatory comments unless necessary for complex logic
177-
- Ensure the code is production-ready
178-
- When using search/replace format, include ALL existing code to preserve it`
179-
)
180-
}
181-
182-
getCommentsUserPrompt(prefix: string, suffix: string, languageId: string): string {
183-
// Extract the comment from the prefix
184-
const lines = prefix.split("\n")
185-
const lastLine = lines[lines.length - 1]
186-
const previousLine = lines.length > 1 ? lines[lines.length - 2] : ""
187-
188-
// Determine which line contains the comment
189-
const commentLine = isCommentLine(lastLine, languageId) ? lastLine : previousLine
190-
const comment = cleanComment(commentLine, languageId)
191-
192-
const codeWithCursor = `${prefix}${CURSOR_MARKER}${suffix}`
193-
194-
let prompt = `## Comment-Driven Development
195-
- Language: ${languageId}
196-
- Comment to Implement:
197-
\`\`\`
198-
${comment}
199-
\`\`\`
200-
201-
## Full Code
202-
\`\`\`${languageId}
203-
${codeWithCursor}
204-
\`\`\`
205-
206-
## Instructions
207-
Generate code that implements the functionality described in the comment.
208-
The code should be placed at the cursor position (${CURSOR_MARKER}).
209-
Focus on implementing exactly what the comment describes.
210-
`
211155
return prompt
212156
}
213157
}

0 commit comments

Comments
 (0)