Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/intents/node/promptOverride.ts
13399 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Raw } from '@vscode/prompt-tsx';
7
import * as yaml from 'js-yaml';
8
import type { LanguageModelToolInformation, Uri } from 'vscode';
9
import { IFileSystemService } from '../../../platform/filesystem/common/fileSystemService';
10
import { ILogService } from '../../../platform/log/common/logService';
11
import { URI } from '../../../util/vs/base/common/uri';
12
13
interface PromptOverrideConfig {
14
readonly systemPrompt?: string;
15
readonly toolDescriptions?: Record<string, { readonly description: string }>;
16
}
17
18
interface PromptOverrideResult {
19
readonly messages: Raw.ChatMessage[];
20
readonly tools: LanguageModelToolInformation[];
21
}
22
23
const INLINE_PROMPT_OVERRIDE_SOURCE = 'inlinePromptOverrideString';
24
25
/** Tracks which override sources have already had a warning logged, to avoid spamming. */
26
const warnedSources = new Set<string>();
27
28
export async function applyConfiguredPromptOverrides(
29
inlinePromptOverride: string | null,
30
promptOverrideFile: string | null,
31
messages: readonly Raw.ChatMessage[],
32
tools: readonly LanguageModelToolInformation[],
33
fileSystemService: IFileSystemService,
34
logService: ILogService,
35
): Promise<PromptOverrideResult> {
36
const normalizedInlinePromptOverride = inlinePromptOverride?.trim();
37
const normalizedPromptOverrideFile = promptOverrideFile?.trim();
38
39
if (normalizedInlinePromptOverride) {
40
if (normalizedPromptOverrideFile) {
41
logService.trace('[PromptOverride] Both inline prompt override text and prompt override file are configured; using inline prompt override text');
42
}
43
44
return applyPromptOverridesFromString(normalizedInlinePromptOverride, messages, tools, logService);
45
}
46
47
if (normalizedPromptOverrideFile) {
48
return applyPromptOverrides(URI.file(normalizedPromptOverrideFile), messages, tools, fileSystemService, logService);
49
}
50
51
return clonePromptOverrideResult(messages, tools);
52
}
53
54
/**
55
* Applies debug prompt overrides from a YAML file.
56
* Reads the file via IFileSystemService, parses it, and applies system prompt and/or tool description overrides.
57
* Warnings for unreadable/unparseable files are logged once per file path.
58
*/
59
export async function applyPromptOverrides(
60
fileUri: Uri,
61
messages: readonly Raw.ChatMessage[],
62
tools: readonly LanguageModelToolInformation[],
63
fileSystemService: IFileSystemService,
64
logService: ILogService,
65
): Promise<PromptOverrideResult> {
66
const key = fileUri.toString();
67
let content: string;
68
try {
69
const buffer = await fileSystemService.readFile(fileUri);
70
content = new TextDecoder().decode(buffer);
71
} catch (err) {
72
logPromptOverrideFailure(logService, key, `Failed to read prompt override file "${key}"`, err);
73
return clonePromptOverrideResult(messages, tools);
74
}
75
76
const config = parsePromptOverrideConfig(content, key, `prompt override file "${key}"`, logService);
77
if (!config) {
78
return clonePromptOverrideResult(messages, tools);
79
}
80
81
return applyPromptOverrideConfig(config, messages, tools, logService);
82
}
83
84
85
export function applyPromptOverridesFromString(
86
content: string,
87
messages: readonly Raw.ChatMessage[],
88
tools: readonly LanguageModelToolInformation[],
89
logService: ILogService,
90
): PromptOverrideResult {
91
const config = parsePromptOverrideConfig(content, INLINE_PROMPT_OVERRIDE_SOURCE, `inline prompt override setting "${INLINE_PROMPT_OVERRIDE_SOURCE}"`, logService);
92
if (!config) {
93
return clonePromptOverrideResult(messages, tools);
94
}
95
96
return applyPromptOverrideConfig(config, messages, tools, logService);
97
}
98
99
function parsePromptOverrideConfig(
100
content: string,
101
sourceKey: string,
102
sourceDescription: string,
103
logService: ILogService,
104
): PromptOverrideConfig | undefined {
105
let config: PromptOverrideConfig;
106
try {
107
config = yaml.load(content) as PromptOverrideConfig;
108
} catch (err) {
109
logPromptOverrideFailure(logService, sourceKey, `Failed to parse prompt override from ${sourceDescription}`, err);
110
return undefined;
111
}
112
113
// On successful parsing, clear any previous warning so a new error is re-surfaced as warn.
114
warnedSources.delete(sourceKey);
115
116
if (!config || typeof config !== 'object') {
117
return undefined;
118
}
119
120
return config;
121
}
122
123
function applyPromptOverrideConfig(
124
config: PromptOverrideConfig,
125
messages: readonly Raw.ChatMessage[],
126
tools: readonly LanguageModelToolInformation[],
127
logService: ILogService,
128
): PromptOverrideResult {
129
let resultMessages = [...messages];
130
let resultTools = [...tools];
131
132
if (typeof config.systemPrompt === 'string') {
133
resultMessages = applySystemPromptOverride(resultMessages, config.systemPrompt);
134
logService.trace('[PromptOverride] Applied system prompt override');
135
}
136
137
if (config.toolDescriptions && typeof config.toolDescriptions === 'object') {
138
resultTools = applyToolDescriptionOverrides(resultTools, config.toolDescriptions);
139
logService.trace('[PromptOverride] Applied tool description overrides');
140
}
141
142
return { messages: resultMessages, tools: resultTools };
143
}
144
145
function clonePromptOverrideResult(
146
messages: readonly Raw.ChatMessage[],
147
tools: readonly LanguageModelToolInformation[],
148
): PromptOverrideResult {
149
return { messages: [...messages], tools: [...tools] };
150
}
151
152
function logPromptOverrideFailure(logService: ILogService, sourceKey: string, message: string, err: unknown): void {
153
if (!warnedSources.has(sourceKey)) {
154
warnedSources.add(sourceKey);
155
logService.warn(`[PromptOverride] ${message}: ${err}`);
156
} else {
157
logService.trace(`[PromptOverride] ${message}: ${err}`);
158
}
159
}
160
161
/**
162
* Resets the internal warning deduplication state.
163
* Exported for testing only.
164
*/
165
export function resetPromptOverrideWarnings(): void {
166
warnedSources.clear();
167
}
168
169
function applySystemPromptOverride(messages: Raw.ChatMessage[], systemPrompt: string): Raw.ChatMessage[] {
170
const nonSystemMessages = messages.filter(m => m.role !== Raw.ChatRole.System);
171
return [
172
{
173
role: Raw.ChatRole.System,
174
content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: systemPrompt }],
175
},
176
...nonSystemMessages,
177
];
178
}
179
180
function applyToolDescriptionOverrides(
181
tools: readonly LanguageModelToolInformation[],
182
overrides: Record<string, { readonly description: string }>,
183
): LanguageModelToolInformation[] {
184
return tools.map(tool => {
185
const override = overrides[tool.name];
186
if (override && typeof override.description === 'string') {
187
return { ...tool, description: override.description };
188
}
189
return tool;
190
});
191
}
192
193