Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: clean up chat context types #213264

Merged
merged 1 commit into from
May 22, 2024
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
93 changes: 80 additions & 13 deletions src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ChatContextAttachments } from 'vs/workbench/contrib/chat/browser/contri
import { SelectAndInsertFileAction } from 'vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables';
import { ChatAgentLocation, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
import { CONTEXT_CHAT_LOCATION, CONTEXT_IN_CHAT_INPUT } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { IChatRequestVariableEntry } from 'vs/workbench/contrib/chat/common/chatModel';
import { ChatRequestAgentPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
import { AnythingQuickAccessProvider } from 'vs/workbench/contrib/search/browser/anythingQuickAccess';
Expand All @@ -31,6 +32,36 @@ export function registerChatContextActions() {
registerAction2(AttachContextAction);
}

export type IChatContextQuickPickItem = IFileQuickPickItem | IDynamicVariableQuickPickItem | IStaticVariableQuickPickItem;

export interface IFileQuickPickItem extends IQuickPickItem {
id: string;
name: string;
value: URI;

resource: URI;
isDynamic: true;
}

export interface IDynamicVariableQuickPickItem extends IQuickPickItem {
id: string;
name?: string;
value: unknown;
icon?: ThemeIcon;

command?: Command;
isDynamic: true;
}

export interface IStaticVariableQuickPickItem extends IQuickPickItem {
id: string;
name: string;
value: unknown;
icon?: ThemeIcon;

isDynamic?: false;
}

class AttachContextAction extends Action2 {

static readonly ID = 'workbench.action.chat.attachContext';
Expand Down Expand Up @@ -61,20 +92,43 @@ class AttachContextAction extends Action2 {
});
}

private async _attachContext(widget: IChatWidget, commandService: ICommandService, ...picks: any[]) {
const toAttach = [];
private async _attachContext(widget: IChatWidget, commandService: ICommandService, ...picks: IChatContextQuickPickItem[]) {
const toAttach: IChatRequestVariableEntry[] = [];
for (const pick of picks) {
if (pick && typeof pick === 'object' && 'command' in pick) {
if (pick && typeof pick === 'object' && 'command' in pick && pick.command) {
// Dynamic variable with a followup command
const selection = await commandService.executeCommand(pick.command.id, ...(pick.command.arguments ?? []));
if (selection) {
const qualifiedName = `${typeof pick.value === 'string' && pick.value.startsWith('#') ? pick.value.slice(1) : ''}${selection}`;

toAttach.push({ ...pick, isDynamic: pick.isDynamic, value: pick.value, name: qualifiedName, fullName: `$(${pick.icon.id}) ${selection}` });
if (!selection) {
// User made no selection, skip this variable
continue;
}
toAttach.push({
...pick,
isDynamic: pick.isDynamic,
value: pick.value,
name: `${typeof pick.value === 'string' && pick.value.startsWith('#') ? pick.value.slice(1) : ''}${selection}`,
// Apply the original icon with the new name
fullName: `${pick.icon ? `$(${pick.icon.id}) ` : ''}${selection}`
});
} else if (pick && typeof pick === 'object' && 'resource' in pick) {
toAttach.push({ ...pick, value: pick.resource, name: pick.label, id: pick.resource.toString(), isDynamic: true });
// #file variable
toAttach.push({
...pick,
id: pick.resource.toString(),
value: pick.resource,
name: pick.label,
isDynamic: true
});
} else {
toAttach.push({ ...pick, fullName: pick.label, name: 'name' in pick && typeof pick.name === 'string' ? pick.name : pick.label, icon: 'icon' in pick && ThemeIcon.isThemeIcon(pick.icon) ? pick.icon : undefined });
// All other dynamic variables and static variables
toAttach.push({
...pick,
id: pick.id,
value: pick.value,
fullName: pick.label,
name: 'name' in pick && typeof pick.name === 'string' ? pick.name : pick.label,
icon: 'icon' in pick && ThemeIcon.isThemeIcon(pick.icon) ? pick.icon : undefined
});
}
}

Expand All @@ -95,10 +149,15 @@ class AttachContextAction extends Action2 {

const usedAgent = widget.parsedInput.parts.find(p => p instanceof ChatRequestAgentPart);
const slowSupported = usedAgent ? usedAgent.agent.metadata.supportsSlowVariables : true;
const quickPickItems: (QuickPickItem & { isDynamic?: boolean; name?: string; icon?: ThemeIcon; command?: Command; value?: unknown })[] = [];
const quickPickItems: (IChatContextQuickPickItem | QuickPickItem)[] = [];
for (const variable of chatVariablesService.getVariables()) {
if (variable.fullName && (!variable.isSlow || slowSupported)) {
quickPickItems.push({ label: `${variable.icon ? `$(${variable.icon.id}) ` : ''}${variable.fullName}`, name: variable.name, id: variable.id, icon: variable.icon });
quickPickItems.push({
label: `${variable.icon ? `$(${variable.icon.id}) ` : ''}${variable.fullName}`,
name: variable.name,
id: variable.id,
icon: variable.icon
});
}
}

Expand All @@ -108,7 +167,15 @@ class AttachContextAction extends Action2 {
const completions = await chatAgentService.getAgentCompletionItems(agentPart.agent.id, '', CancellationToken.None);
for (const variable of completions) {
if (variable.fullName) {
quickPickItems.push({ label: `${variable.icon ? `$(${variable.icon.id}) ` : ''}${variable.fullName}`, id: variable.id, command: variable.command, icon: variable.icon, value: variable.value, isDynamic: true });
quickPickItems.push({
label: `${variable.icon ? `$(${variable.icon.id}) ` : ''}${variable.fullName}`,
id: variable.id,
command: variable.command,
icon: variable.icon,
value: variable.value,
isDynamic: true,
name: variable.name
});
}
}
}
Expand All @@ -123,7 +190,7 @@ class AttachContextAction extends Action2 {
enabledProviderPrefixes: [AnythingQuickAccessProvider.PREFIX],
placeholder: localize('chatContext.attach.placeholder', 'Search attachments'),
providerOptions: <AnythingQuickAccessProviderRunOptions>{
handleAccept: (item: IQuickPickItem) => {
handleAccept: (item: IChatContextQuickPickItem) => {
this._attachContext(widget, commandService, item);
},
additionPicks: quickPickItems,
Expand Down
6 changes: 4 additions & 2 deletions src/vs/workbench/contrib/chat/browser/chatVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class ChatVariablesService implements IChatVariablesService {
}
});

const resolvedAttachedContext: IChatRequestVariableEntry[] = [];
attachedContextVariables
?.forEach((attachment, i) => {
const data = this._resolver.get(attachment.name?.toLowerCase());
Expand All @@ -77,11 +78,11 @@ export class ChatVariablesService implements IChatVariablesService {
};
jobs.push(data.resolver(prompt.text, '', model, variableProgressCallback, token).then(value => {
if (value) {
resolvedVariables[i] = { id: data.data.id, modelDescription: data.data.modelDescription, name: attachment.name, range: attachment.range, value, references };
resolvedAttachedContext[i] = { id: data.data.id, modelDescription: data.data.modelDescription, name: attachment.name, range: attachment.range, value, references };
}
}).catch(onUnexpectedExternalError));
} else if (attachment.isDynamic) {
resolvedVariables[i] = { id: attachment.id, name: attachment.name, value: attachment.value };
resolvedAttachedContext[i] = { id: attachment.id, name: attachment.name, value: attachment.value };
}
});

Expand All @@ -91,6 +92,7 @@ export class ChatVariablesService implements IChatVariablesService {

// "reverse", high index first so that replacement is simple
resolvedVariables.sort((a, b) => b.range!.start - a.range!.start);
resolvedVariables.push(...resolvedAttachedContext);

return {
variables: resolvedVariables,
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/contrib/chat/common/chatAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ interface IChatAgentEntry {

export interface IChatAgentCompletionItem {
id: string;
name?: string;
fullName?: string;
icon?: ThemeIcon;
value: unknown;
Expand Down
Loading