|
1 | 1 | import * as vscode from 'vscode'; |
2 | | -import type { ChatResult, ParticipantResponseType } from '../constants'; |
| 2 | +import type { ChatResult } from '../constants'; |
3 | 3 | import type { |
4 | 4 | InternalPromptPurpose, |
5 | 5 | ParticipantPromptProperties, |
6 | 6 | } from '../../telemetry/telemetryService'; |
7 | | -import { ParticipantErrorTypes } from '../participantErrorTypes'; |
| 7 | +import { PromptHistory } from './promptHistory'; |
8 | 8 |
|
9 | 9 | export interface PromptArgsBase { |
10 | 10 | request: { |
@@ -53,6 +53,26 @@ export function getContentLength( |
53 | 53 | return 0; |
54 | 54 | } |
55 | 55 |
|
| 56 | +export function getContent(message: vscode.LanguageModelChatMessage): string { |
| 57 | + const content = message.content as any; |
| 58 | + if (typeof content === 'string') { |
| 59 | + return content; |
| 60 | + } |
| 61 | + |
| 62 | + if (Array.isArray(content)) { |
| 63 | + return content.reduce((agg: string, element) => { |
| 64 | + const value = element?.value ?? element?.content?.value; |
| 65 | + if (typeof value === 'string') { |
| 66 | + return agg + value; |
| 67 | + } |
| 68 | + |
| 69 | + return agg; |
| 70 | + }, ''); |
| 71 | + } |
| 72 | + |
| 73 | + return ''; |
| 74 | +} |
| 75 | + |
56 | 76 | export function isContentEmpty( |
57 | 77 | message: vscode.LanguageModelChatMessage |
58 | 78 | ): boolean { |
@@ -88,7 +108,10 @@ export abstract class PromptBase<TArgs extends PromptArgsBase> { |
88 | 108 | } |
89 | 109 |
|
90 | 110 | async buildMessages(args: TArgs): Promise<ModelInput> { |
91 | | - let historyMessages = this.getHistoryMessages(args); |
| 111 | + let historyMessages = PromptHistory.getFilteredHistory({ |
| 112 | + history: args.context?.history, |
| 113 | + ...args, |
| 114 | + }); |
92 | 115 | // If the current user's prompt is a connection name, and the last |
93 | 116 | // message was to connect. We want to use the last |
94 | 117 | // message they sent before the connection name as their prompt. |
@@ -157,115 +180,4 @@ export abstract class PromptBase<TArgs extends PromptArgsBase> { |
157 | 180 | internal_purpose: this.internalPurposeForTelemetry, |
158 | 181 | }; |
159 | 182 | } |
160 | | - |
161 | | - // When passing the history to the model we only want contextual messages |
162 | | - // to be passed. This function parses through the history and returns |
163 | | - // the messages that are valuable to keep. |
164 | | - // eslint-disable-next-line complexity |
165 | | - protected getHistoryMessages({ |
166 | | - connectionNames, |
167 | | - context, |
168 | | - databaseName, |
169 | | - collectionName, |
170 | | - }: { |
171 | | - connectionNames?: string[]; // Used to scrape the connecting messages from the history. |
172 | | - context?: vscode.ChatContext; |
173 | | - databaseName?: string; |
174 | | - collectionName?: string; |
175 | | - }): vscode.LanguageModelChatMessage[] { |
176 | | - const messages: vscode.LanguageModelChatMessage[] = []; |
177 | | - |
178 | | - if (!context) { |
179 | | - return []; |
180 | | - } |
181 | | - |
182 | | - let previousItem: |
183 | | - | vscode.ChatRequestTurn |
184 | | - | vscode.ChatResponseTurn |
185 | | - | undefined = undefined; |
186 | | - |
187 | | - const namespaceIsKnown = |
188 | | - databaseName !== undefined && collectionName !== undefined; |
189 | | - for (const historyItem of context.history) { |
190 | | - if (historyItem instanceof vscode.ChatRequestTurn) { |
191 | | - if ( |
192 | | - historyItem.prompt?.trim().length === 0 || |
193 | | - connectionNames?.includes(historyItem.prompt) |
194 | | - ) { |
195 | | - // When the message is empty or a connection name then we skip it. |
196 | | - // It's probably going to be the response to the connect step. |
197 | | - previousItem = historyItem; |
198 | | - continue; |
199 | | - } |
200 | | - |
201 | | - if (previousItem instanceof vscode.ChatResponseTurn) { |
202 | | - const responseIntent = (previousItem.result as ChatResult).metadata |
203 | | - ?.intent; |
204 | | - |
205 | | - // If the namespace is already known, skip responses to prompts asking for it. |
206 | | - if (responseIntent === 'askForNamespace' && namespaceIsKnown) { |
207 | | - previousItem = historyItem; |
208 | | - continue; |
209 | | - } |
210 | | - } |
211 | | - |
212 | | - // eslint-disable-next-line new-cap |
213 | | - messages.push(vscode.LanguageModelChatMessage.User(historyItem.prompt)); |
214 | | - } |
215 | | - |
216 | | - if (historyItem instanceof vscode.ChatResponseTurn) { |
217 | | - if ( |
218 | | - historyItem.result.errorDetails?.message === |
219 | | - ParticipantErrorTypes.FILTERED |
220 | | - ) { |
221 | | - // If the response led to a filtered error, we do not want the |
222 | | - // error-causing message to be sent again so we remove it. |
223 | | - messages.pop(); |
224 | | - continue; |
225 | | - } |
226 | | - |
227 | | - let message = ''; |
228 | | - |
229 | | - // Skip a response to an empty user prompt message or connect message. |
230 | | - const responseTypesToSkip: ParticipantResponseType[] = [ |
231 | | - 'emptyRequest', |
232 | | - 'askToConnect', |
233 | | - ]; |
234 | | - |
235 | | - const responseType = (historyItem.result as ChatResult)?.metadata |
236 | | - ?.intent; |
237 | | - if (responseTypesToSkip.includes(responseType)) { |
238 | | - previousItem = historyItem; |
239 | | - continue; |
240 | | - } |
241 | | - |
242 | | - // If the namespace is already known, skip including prompts asking for it. |
243 | | - if (responseType === 'askForNamespace' && namespaceIsKnown) { |
244 | | - previousItem = historyItem; |
245 | | - continue; |
246 | | - } |
247 | | - |
248 | | - for (const fragment of historyItem.response) { |
249 | | - if (fragment instanceof vscode.ChatResponseMarkdownPart) { |
250 | | - message += fragment.value.value; |
251 | | - |
252 | | - if ( |
253 | | - (historyItem.result as ChatResult)?.metadata?.intent === |
254 | | - 'askForNamespace' |
255 | | - ) { |
256 | | - // When the message is the assistant asking for part of a namespace, |
257 | | - // we only want to include the question asked, not the user's |
258 | | - // database and collection names in the history item. |
259 | | - break; |
260 | | - } |
261 | | - } |
262 | | - } |
263 | | - // eslint-disable-next-line new-cap |
264 | | - messages.push(vscode.LanguageModelChatMessage.Assistant(message)); |
265 | | - } |
266 | | - previousItem = historyItem; |
267 | | - } |
268 | | - |
269 | | - return messages; |
270 | | - } |
271 | 183 | } |
0 commit comments