Skip to content

Commit

Permalink
Simplify ChatModel more (#198344)
Browse files Browse the repository at this point in the history
* Simplify ChatModel further

* Simplify ChatModel more

* Remove helper

* Clean up

* Fix addCompleteRequest

* Fix updateRepr
  • Loading branch information
roblourens authored Nov 15, 2023
1 parent f26376f commit 23fc07c
Show file tree
Hide file tree
Showing 13 changed files with 269 additions and 134 deletions.
6 changes: 3 additions & 3 deletions src/vs/workbench/api/common/extHostTypeConverters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2297,17 +2297,17 @@ export namespace InteractiveEditorResponseFeedbackKind {
export namespace ChatResponseProgress {
export function from(extension: IExtensionDescription, progress: vscode.ChatAgentExtendedProgress): extHostProtocol.IChatProgressDto {
if ('placeholder' in progress && 'resolvedContent' in progress) {
return { placeholder: progress.placeholder, kind: 'asyncContent' } satisfies extHostProtocol.IChatAsyncContentDto;
return { content: progress.placeholder, kind: 'asyncContent' } satisfies extHostProtocol.IChatAsyncContentDto;
} else if ('markdownContent' in progress) {
checkProposedApiEnabled(extension, 'chatAgents2Additions');
return { content: MarkdownString.from(progress.markdownContent), kind: 'content' };
return { content: MarkdownString.from(progress.markdownContent), kind: 'markdownContent' };
} else if ('content' in progress) {
if (typeof progress.content === 'string') {
return { content: progress.content, kind: 'content' };
}

checkProposedApiEnabled(extension, 'chatAgents2Additions');
return { content: MarkdownString.from(progress.content), kind: 'content' };
return { content: MarkdownString.from(progress.content), kind: 'markdownContent' };
} else if ('documents' in progress) {
return {
documents: progress.documents.map(d => ({
Expand Down
16 changes: 12 additions & 4 deletions src/vs/workbench/contrib/chat/browser/chat.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import { ChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
import { ChatWelcomeMessageModel } from 'vs/workbench/contrib/chat/common/chatModel';
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
import { IMarkdownString, MarkdownString, isMarkdownString } from 'vs/base/common/htmlContent';
import { ChatProviderService, IChatProviderService } from 'vs/workbench/contrib/chat/common/chatProvider';
import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
import { alertFocusChange } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions';
Expand Down Expand Up @@ -243,7 +243,11 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable {
const defaultAgent = chatAgentService.getDefaultAgent();
const agents = chatAgentService.getAgents();
if (defaultAgent?.metadata.helpTextPrefix) {
progress.report({ content: defaultAgent.metadata.helpTextPrefix, kind: 'content' });
if (isMarkdownString(defaultAgent.metadata.helpTextPrefix)) {
progress.report({ content: defaultAgent.metadata.helpTextPrefix, kind: 'markdownContent' });
} else {
progress.report({ content: defaultAgent.metadata.helpTextPrefix, kind: 'content' });
}
progress.report({ content: '\n\n', kind: 'content' });
}

Expand All @@ -263,10 +267,14 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable {

return agentLine + '\n' + commandText;
}))).join('\n');
progress.report({ content: new MarkdownString(agentText, { isTrusted: { enabledCommands: [SubmitAction.ID] } }), kind: 'content' });
progress.report({ content: new MarkdownString(agentText, { isTrusted: { enabledCommands: [SubmitAction.ID] } }), kind: 'markdownContent' });
if (defaultAgent?.metadata.helpTextPostfix) {
progress.report({ content: '\n\n', kind: 'content' });
progress.report({ content: defaultAgent.metadata.helpTextPostfix, kind: 'content' });
if (isMarkdownString(defaultAgent.metadata.helpTextPostfix)) {
progress.report({ content: defaultAgent.metadata.helpTextPostfix, kind: 'markdownContent' });
} else {
progress.report({ content: defaultAgent.metadata.helpTextPostfix, kind: 'content' });
}
}
}));
}
Expand Down
37 changes: 19 additions & 18 deletions src/vs/workbench/contrib/chat/browser/chatListRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions
import { CodeBlockPart, ICodeBlockData, ICodeBlockPart } from 'vs/workbench/contrib/chat/browser/codeBlockPart';
import { IChatAgentMetadata } from 'vs/workbench/contrib/chat/common/chatAgents';
import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { IPlaceholderMarkdownString } from 'vs/workbench/contrib/chat/common/chatModel';
import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatContentInlineReference, IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatResponseMarkdownRenderData, IChatResponseRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/files/browser/views/explorerView';
Expand Down Expand Up @@ -319,7 +319,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
const markdown = 'kind' in element.message ?
element.message.message :
convertParsedRequestToMarkdown(element.message);
this.basicRenderElement([new MarkdownString(markdown)], element, index, templateData);
this.basicRenderElement([{ content: new MarkdownString(markdown), kind: 'markdownContent' }], element, index, templateData);
} else {
this.renderWelcomeMessage(element, templateData);
}
Expand Down Expand Up @@ -400,7 +400,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
}
}

private basicRenderElement(value: ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData>, element: ChatTreeItem, index: number, templateData: IChatListItemTemplate) {
private basicRenderElement(value: ReadonlyArray<Exclude<IChatProgressResponseContent, IChatContentInlineReference>>, element: ChatTreeItem, index: number, templateData: IChatListItemTemplate) {
const fillInIncompleteTokens = isResponseVM(element) && (!element.isComplete || element.isCanceled || element.errorDetails?.responseIsFiltered || element.errorDetails?.responseIsIncomplete);

dom.clearNode(templateData.value);
Expand All @@ -414,9 +414,11 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch

let fileTreeIndex = 0;
for (const data of value) {
const result = 'value' in data
? this.renderMarkdown(data, element, templateData, fillInIncompleteTokens)
: this.renderTreeData(data, element, templateData, fileTreeIndex++);
const result = data.kind === 'treeData'
? this.renderTreeData(data.treeData, element, templateData, fileTreeIndex++)
: data.kind === 'markdownContent'
? this.renderMarkdown(data.content, element, templateData, fillInIncompleteTokens)
: this.renderPlaceholder(new MarkdownString(data.content), templateData);
templateData.value.appendChild(result.element);
templateData.elementDisposables.add(result);
}
Expand Down Expand Up @@ -519,10 +521,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
const renderedPart = renderedParts[index];
// Is this part completely new?
if (!renderedPart) {
if (isInteractiveProgressTreeData(part)) {
partsToRender[index] = part;
if (part.kind === 'treeData') {
partsToRender[index] = part.treeData;
} else {
const wordCountResult = this.getDataForProgressiveRender(element, part, { renderedWordCount: 0, lastRenderTime: 0 });
const wordCountResult = this.getDataForProgressiveRender(element, contentToMarkdown(part.content), { renderedWordCount: 0, lastRenderTime: 0 });
if (wordCountResult !== undefined) {
partsToRender[index] = {
renderedWordCount: wordCountResult.actualWordCount,
Expand All @@ -535,13 +537,13 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
}

// Did this part go from being a placeholder string to resolved tree data?
else if (isInteractiveProgressTreeData(part) && !isInteractiveProgressTreeData(renderedPart)) {
partsToRender[index] = part;
else if (part.kind === 'treeData' && !isInteractiveProgressTreeData(renderedPart)) {
partsToRender[index] = part.treeData;
}

// Did this part's content change?
else if (!isInteractiveProgressTreeData(part) && !isInteractiveProgressTreeData(renderedPart)) {
const wordCountResult = this.getDataForProgressiveRender(element, part, renderedPart);
else if (part.kind !== 'treeData' && !isInteractiveProgressTreeData(renderedPart)) {
const wordCountResult = this.getDataForProgressiveRender(element, contentToMarkdown(part.content), renderedPart);
// Check if there are any new words to render
if (wordCountResult !== undefined && renderedPart.renderedWordCount !== wordCountResult?.actualWordCount) {
partsToRender[index] = {
Expand Down Expand Up @@ -578,8 +580,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
// Avoid doing progressive rendering for multiple markdown parts simultaneously
else if (!hasRenderedOneMarkdownBlock) {
const { value } = wordCountResults[index];
const isPlaceholder = isPlaceholderMarkdown(renderableResponse[index]);
result = isPlaceholder
result = renderableResponse[index].kind === 'asyncContent'
? this.renderPlaceholder(new MarkdownString(value), templateData)
: this.renderMarkdown(new MarkdownString(value), element, templateData, true);
hasRenderedOneMarkdownBlock = true;
Expand Down Expand Up @@ -1278,6 +1279,6 @@ function isInteractiveProgressTreeData(item: IChatResponseProgressFileTreeData |
return 'label' in item;
}

function isPlaceholderMarkdown(item: IPlaceholderMarkdownString | IMarkdownString | IChatResponseProgressFileTreeData): item is IPlaceholderMarkdownString {
return 'isPlaceholder' in item;
function contentToMarkdown(str: string | IMarkdownString): IMarkdownString {
return typeof str === 'string' ? { value: str } : str;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from 'vs/base/browser/dom';
import { IMarkdownString, MarkdownString, isMarkdownString } from 'vs/base/common/htmlContent';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { revive } from 'vs/base/common/marshalling';
import { basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { Location } from 'vs/editor/common/languages';
import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
import { ChatRequestTextPart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatContentInlineReference, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatContentInlineReference } from 'vs/workbench/contrib/chat/common/chatService';

const variableRefUrl = 'http://_vscodedecoration_';

Expand Down Expand Up @@ -59,21 +60,21 @@ function renderFileWidget(href: string, a: HTMLAnchorElement): void {

const contentRefUrl = 'http://_vscodecontentref_'; // must be lowercase for URI

export function reduceInlineContentReferences(response: ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData | IChatContentInlineReference>): ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData> {
const result: (IMarkdownString | IChatResponseProgressFileTreeData)[] = [];
export function reduceInlineContentReferences(response: ReadonlyArray<IChatProgressResponseContent>): ReadonlyArray<Exclude<IChatProgressResponseContent, IChatContentInlineReference>> {
const result: Exclude<IChatProgressResponseContent, IChatContentInlineReference>[] = [];
for (const item of response) {
const previousItem = result[result.length - 1];
if ('inlineReference' in item) {
if (item.kind === 'inlineReference') {
const location = 'uri' in item.inlineReference ? item.inlineReference : { uri: item.inlineReference };
const printUri = URI.parse(contentRefUrl).with({ fragment: JSON.stringify(location) });
const markdownText = `[${item.name || basename(location.uri)}](${printUri.toString()})`;
if (isMarkdownString(previousItem)) {
result[result.length - 1] = new MarkdownString(previousItem.value + markdownText, { isTrusted: previousItem.isTrusted });
if (previousItem?.kind === 'markdownContent') {
result[result.length - 1] = { content: new MarkdownString(previousItem.content.value + markdownText, { isTrusted: previousItem.content.isTrusted }), kind: 'markdownContent' };
} else {
result.push(new MarkdownString(markdownText));
result.push({ content: new MarkdownString(markdownText), kind: 'markdownContent' });
}
} else if (isMarkdownString(item) && isMarkdownString(previousItem)) {
result[result.length - 1] = new MarkdownString(previousItem.value + item.value, { isTrusted: previousItem.isTrusted });
} else if (item.kind === 'markdownContent' && previousItem?.kind === 'markdownContent') {
result[result.length - 1] = { content: new MarkdownString(previousItem.content.value + item.content.value, { isTrusted: previousItem.content.isTrusted }), kind: 'markdownContent' };
} else {
result.push(item);
}
Expand Down
Loading

0 comments on commit 23fc07c

Please sign in to comment.