Skip to content

Commit 698dbf2

Browse files
committed
Standardize prompt construction across the extension by implementing a centralized builder utility
- current behavior: files are wrapped in <files></files>. Expected behavior: replace "<files>" with "# Files", remove "</files>". - current behavior: files are wrapped in <file path...></file>. Expected behavior: replace "<file>" with "### File: `[path]`", replace CDATA with markdown blocks "```". - current behavior: we're using <system></system> xml tags. expected behavior: we should use "# System" markdown heading - between "# System..." and user-typed instructions, place separator "---" - above "# System...", there is one line too much: [Pasted text] - when constructing file-containing messages, we see a lot of repetition, e.g. with "# Files" header or "---" separators. Should have appropriate util in `apps/editor/src/utils`
1 parent 4a4a43c commit 698dbf2

14 files changed

Lines changed: 226 additions & 120 deletions

File tree

README.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,20 @@ Here, context is managed by you, directly in the native file explorer so the mod
5050
> [!TIP]
5151
> Even if you're not sure what should be in context, the tool offers both 'intelligent' and static ways to find all the necessary files, including valuable examples.
5252
53-
**Generated prompts are XML-formatted:**
53+
**Generated prompts are structured as follows:**
5454

5555
<details>
5656
<summary>Edit context</summary>
5757

5858
```
59-
<files>[current file selection]</files>
60-
<system>[edit format instructions]</system>
59+
# Files
60+
[current file selection]
61+
62+
# System
63+
[edit format instructions]
64+
65+
---
66+
6167
[user-typed prompt]
6268
```
6369

@@ -67,7 +73,8 @@ Here, context is managed by you, directly in the native file explorer so the mod
6773
<summary>Ask about context</summary>
6874

6975
```
70-
<files>[current file selection]</files>
76+
# Files
77+
[current file selection]
7178
[user-typed prompt]
7279
```
7380

@@ -76,25 +83,32 @@ Here, context is managed by you, directly in the native file explorer so the mod
7683
<details>
7784
<summary>Code at cursor</summary>
7885

79-
```
80-
<files>
86+
````
87+
# Files
8188
[rough file selection]
82-
<file path="[active file]">
89+
### File: `[active file]`
90+
```
8391
[code before cursor]<missing_text>[user-typed prompt]</missing_text>[code after cursor]
84-
</file>
85-
</files>
86-
[instructions for the missing text]
8792
```
93+
[instructions for the missing text]
94+
````
8895

8996
</details>
9097

9198
<details>
9299
<summary>Find relevant files</summary>
93100

94101
```
95-
<files>[current file tree selection]</files>
96-
<system>[response format instructions]</system>
97-
Find all files building modules of the following task's scope:
102+
# Files
103+
[current file tree selection]
104+
105+
# System
106+
[response format instructions]
107+
108+
Find a complete set of relevant files according to the following query:
109+
110+
---
111+
98112
[user-typed prompt]
99113
```
100114

apps/editor/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "gemini-coder",
33
"displayName": "Code Web Chat",
44
"description": "Connect VS Code with chatbots (CWC)",
5-
"version": "1.959.0",
5+
"version": "1.960.0",
66
"scripts": {
77
"build": "npx vsce package --no-dependencies",
88
"vscode:prepublish": "rimraf out && npm run compile",

apps/editor/src/commands/code-at-cursor-command/utils/perform-code-at-cursor.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { build_user_content } from '../../../utils/build-user-content'
1414

1515
import { get_code_at_cursor_api_configuration } from './get-code-at-cursor-config'
1616
import { show_ghost_text } from './show-ghost-text'
17+
import { build_prompt } from '../../../utils/prompt-builder'
1718

1819
export const perform_code_at_cursor = async (params: {
1920
file_tree_provider: any
@@ -129,14 +130,16 @@ export const perform_code_at_cursor = async (params: {
129130

130131
const collected = await files_collector.collect_files()
131132

132-
const part1 = `<files>\n${collected.other_files}`
133-
const part2 = `${collected.recent_files}<file path="${vscode.workspace.asRelativePath(
134-
document.uri
135-
)}">\n<![CDATA[\n${text_before_cursor}${
136-
completion_instructions
137-
? `<missing_text>${completion_instructions}</missing_text>`
138-
: '<missing_text>'
139-
}${text_after_cursor}\n]]>\n</file>\n</files>\n${code_at_cursor_instructions}`
133+
const { part1, part2 } = build_prompt({
134+
other_files: collected.other_files,
135+
recent_files: collected.recent_files,
136+
active_file_context: `### File: \`${vscode.workspace.asRelativePath(document.uri)}\`\n\n\`\`\`\n${text_before_cursor}${
137+
completion_instructions
138+
? `<missing_text>${completion_instructions}</missing_text>`
139+
: '<missing_text>'
140+
}${text_after_cursor}\n\`\`\`\n\n`,
141+
system_instructions: code_at_cursor_instructions
142+
})
140143

141144
const user_content = build_user_content({
142145
model_provider,

apps/editor/src/constants/instructions.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ export const code_at_cursor_instructions_for_panel = (params: {
66
row: number
77
column: number
88
}) => `Find correct replacement text for the <missing_text> symbol.
9-
<system>
9+
10+
# System
11+
1012
Your response must begin with a markdown heading identifying the file and the cursor position, followed by a markdown code block containing the replacement text, followed by a brief explanation. The heading must be: "### Code at cursor: \`${
1113
params.file_path
1214
}\` (${params.row + 1}:${
@@ -19,8 +21,7 @@ Your response must begin with a markdown heading identifying the file and the cu
1921
!== undefined
2022
\`\`\`
2123
22-
The variable is possibly not defined.
23-
</system>`
24+
The variable is possibly not defined.`
2425

2526
export const intelligent_update_instructions =
2627
"Refactor the file according to the attached changes without explanations or any other text. Print the file in full because I have a disability which means I can't type and need to be able to just copy and paste."
@@ -31,17 +32,18 @@ export const commit_message_instructions =
3132
export const find_relevant_files_instructions =
3233
'Find a complete set of relevant files according to the following query:'
3334

34-
export const find_relevant_files_format = `<system>
35+
export const find_relevant_files_format = `# System
36+
3537
Your response must contain paths of relevant files enclosed in "relevant-files" and "file-path" XML tags. Don't send anything else. Example:
3638
3739
<relevant-files>
3840
<file-path>src/index.ts</file-path>
3941
<file-path>src/hello.ts</file-path>
4042
<file-path>src/welcome.ts</file-path>
41-
</relevant-files>
42-
</system>`
43+
</relevant-files>`
44+
45+
export const find_relevant_files_format_for_panel = `# System
4346
44-
export const find_relevant_files_format_for_panel = `<system>
4547
Your response must begin with "**Relevant files:**", then list paths one under another, followed by a brief explanation. Example:
4648
4749
**Relevant files:**
@@ -50,8 +52,7 @@ Your response must begin with "**Relevant files:**", then list paths one under a
5052
- \`src/hello.ts\`
5153
- \`src/welcome.ts\`
5254
53-
These files contain the core greeting logic and module exports.
54-
</system>`
55+
These files contain the core greeting logic and module exports.`
5556

5657
export const voice_input_instructions =
5758
'Respond with a transcription of the following audio recording or text "INAUDIBLE", and nothing else.'

apps/editor/src/context/context-initialization.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
} from '../constants/state-keys'
1919
import { SelectedFilesProvider } from './providers/selected-files/selected-files-provider'
2020
import { is_binary_file } from '../utils/is-binary'
21+
import { build_prompt } from '../utils/prompt-builder'
2122

2223
export const token_count_emitter = new EventEmitter()
2324

@@ -229,7 +230,7 @@ export const context_initialization = async (
229230
return
230231
}
231232

232-
context_text = `<files>\n${context_text}</files>\n`
233+
context_text = build_prompt({ context_text }).full_prompt + '\n'
233234
await vscode.env.clipboard.writeText(context_text)
234235
vscode.window.showInformationMessage(
235236
dictionary.information_message.CONTEXT_COPIED_TO_CLIPBOARD
@@ -274,10 +275,10 @@ export const context_initialization = async (
274275
}
275276

276277
if (is_binary_file(file_path, content_uint8_array)) {
277-
context_text += `<file path="${display_path.replace(
278+
context_text += `### File: \`${display_path.replace(
278279
/\\/g,
279280
'/'
280-
)}">Binary file</file>\n`
281+
)}\`\n\nBinary file\n\n`
281282
continue
282283
}
283284

@@ -291,10 +292,10 @@ export const context_initialization = async (
291292
)
292293
}
293294

294-
context_text += `<file path="${display_path.replace(
295+
context_text += `### File: \`${display_path.replace(
295296
/\\/g,
296297
'/'
297-
)}">\n${content}\n</file>\n`
298+
)}\`\n\n\`\`\`\n${content}\n\`\`\`\n\n`
298299
} catch (error: any) {
299300
vscode.window.showErrorMessage(
300301
dictionary.error_message.ERROR_READING_FILE(
@@ -307,7 +308,7 @@ export const context_initialization = async (
307308

308309
if (context_text == '') return
309310

310-
context_text = `<files>\n${context_text}</files>\n`
311+
context_text = build_prompt({ context_text }).full_prompt + '\n'
311312
await vscode.env.clipboard.writeText(context_text)
312313
vscode.window.showInformationMessage(
313314
dictionary.information_message

apps/editor/src/context/providers/workspace/modules/token-calculator.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -316,23 +316,25 @@ export class TokenCalculator implements vscode.Disposable {
316316
}
317317

318318
const wrap_content = (text: string) => {
319-
if (!workspace_root) {
320-
return `<file path="${file_path.replace(
321-
/\\/g,
322-
'/'
323-
)}">\n<![CDATA[\n${text}\n]]>\n</file>\n`
324-
} else {
319+
let display_path = file_path.replace(/\\/g, '/')
320+
if (workspace_root) {
325321
const relative_path = path
326322
.relative(workspace_root, file_path)
327323
.replace(/\\/g, '/')
328324
if (this._provider.get_workspace_roots().length > 1) {
329325
const workspace_name =
330326
this._provider.get_workspace_name(workspace_root)
331-
return `<file path="${workspace_name}/${relative_path}">\n<![CDATA[\n${text}\n]]>\n</file>\n`
327+
display_path = `${workspace_name}/${relative_path}`
332328
} else {
333-
return `<file path="${relative_path}">\n<![CDATA[\n${text}\n]]>\n</file>\n`
329+
display_path = relative_path
334330
}
335331
}
332+
333+
if (is_binary) {
334+
return `### File: \`${display_path}\`\n\nBinary file\n\n`
335+
} else {
336+
return `### File: \`${display_path}\`\n\n\`\`\`\n${text}\n\`\`\`\n\n`
337+
}
336338
}
337339

338340
const token_count = Math.floor(wrap_content(content).length / 4)

apps/editor/src/utils/files-collector.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -86,27 +86,25 @@ export class FilesCollector {
8686

8787
const workspace_root = this._get_workspace_root_for_file(file_path)
8888

89-
if (!workspace_root) {
90-
collected_text += `<file path="${file_path.replace(
91-
/\\/g,
92-
'/'
93-
)}">\n<![CDATA[\n${content}\n]]>\n</file>\n`
94-
continue
89+
let display_path = file_path.replace(/\\/g, '/')
90+
if (workspace_root) {
91+
const relative_path = path
92+
.relative(workspace_root, file_path)
93+
.replace(/\\/g, '/')
94+
95+
display_path = relative_path
96+
if (this.workspace_roots.length > 1) {
97+
const workspace_name =
98+
this.workspace_provider.get_workspace_name(workspace_root)
99+
display_path = `${workspace_name}/${relative_path}`
100+
}
95101
}
96102

97-
const relative_path = path
98-
.relative(workspace_root, file_path)
99-
.replace(/\\/g, '/')
100-
101-
// Get the workspace name to prefix the path if there are multiple workspaces
102-
let display_path = relative_path
103-
if (this.workspace_roots.length > 1) {
104-
const workspace_name =
105-
this.workspace_provider.get_workspace_name(workspace_root)
106-
display_path = `${workspace_name}/${relative_path}`
103+
if (is_binary) {
104+
collected_text += `### File: \`${display_path}\`\n\nBinary file\n\n`
105+
} else {
106+
collected_text += `### File: \`${display_path}\`\n\n\`\`\`\n${content}\n\`\`\`\n\n`
107107
}
108-
109-
collected_text += `<file path="${display_path}">\n<![CDATA[\n${content}\n]]>\n</file>\n`
110108
} catch (error) {
111109
console.error(`Error reading file ${file_path}:`, error)
112110
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
export const build_prompt = (params: {
2+
other_files?: string
3+
recent_files?: string
4+
context_text?: string
5+
active_file_context?: string
6+
skill_definitions?: string
7+
system_instructions?: string
8+
user_instructions?: string
9+
separator?: boolean
10+
}): { part1: string; part2: string; full_prompt: string } => {
11+
let part1 = ''
12+
let part2 = ''
13+
let full_prompt = ''
14+
15+
if (params.context_text !== undefined) {
16+
if (params.context_text) {
17+
full_prompt += `# Files\n\n${params.context_text}`
18+
}
19+
if (params.active_file_context) {
20+
full_prompt += params.active_file_context
21+
}
22+
} else {
23+
part1 = `# Files\n\n${params.other_files || ''}`
24+
part2 += `${params.recent_files || ''}${params.active_file_context || ''}`
25+
full_prompt = part1 + part2
26+
}
27+
28+
if (params.skill_definitions) {
29+
part2 += params.skill_definitions
30+
full_prompt += params.skill_definitions
31+
}
32+
33+
const has_system = !!params.system_instructions
34+
const has_user = !!params.user_instructions
35+
const separator = params.separator !== false && has_system && has_user
36+
37+
if (params.system_instructions) {
38+
const sys = params.system_instructions.trimEnd()
39+
if (sys) {
40+
part2 += `${sys}\n`
41+
full_prompt += `${sys}\n`
42+
if (separator) {
43+
part2 += `\n---\n\n`
44+
full_prompt += `\n---\n\n`
45+
} else {
46+
part2 += `\n\n`
47+
full_prompt += `\n\n`
48+
}
49+
}
50+
}
51+
52+
if (params.user_instructions) {
53+
part2 += params.user_instructions
54+
full_prompt += params.user_instructions
55+
}
56+
57+
return { part1, part2, full_prompt }
58+
}

apps/editor/src/views/panel/backend/message-handlers/handle-code-at-cursor.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
show_configuration_quick_pick,
2222
map_api_configuration_to_item
2323
} from '@/utils/show-configuration-quick-pick'
24+
import { build_prompt } from '@/utils/prompt-builder'
2425

2526
const get_code_at_cursor_api_configuration = async (
2627
model_providers_manager: ModelProvidersManager,
@@ -227,12 +228,17 @@ export const handle_code_at_cursor = async (
227228

228229
const collected = await files_collector.collect_files()
229230

230-
const part1 = `<files>\n${collected.other_files}`
231-
const part2 = `${collected.recent_files}<file path="${relative_path}">\n<![CDATA[\n${text_before_cursor}${
232-
processed_completion_instructions
233-
? `<missing_text>${processed_completion_instructions}</missing_text>`
234-
: '<missing_text>'
235-
}${text_after_cursor}\n]]>\n</file>\n</files>\n${skill_definitions}${main_instructions}`
231+
const { part1, part2 } = build_prompt({
232+
other_files: collected.other_files,
233+
recent_files: collected.recent_files,
234+
active_file_context: `### File: \`${relative_path}\`\n\n\`\`\`\n${text_before_cursor}${
235+
processed_completion_instructions
236+
? `<missing_text>${processed_completion_instructions}</missing_text>`
237+
: '<missing_text>'
238+
}${text_after_cursor}\n\`\`\`\n\n`,
239+
skill_definitions,
240+
system_instructions: main_instructions
241+
})
236242

237243
const user_content = build_user_content({
238244
model_provider,

0 commit comments

Comments
 (0)