Skip to content

Commit

Permalink
Fix chats not being persisted (microsoft#194357)
Browse files Browse the repository at this point in the history
* Fix chats not being persisted
Fix microsoft#430
Still need to fully square the parsed chat representation with chat followups

* Fix tests
  • Loading branch information
roblourens committed Sep 28, 2023
1 parent 80e3d4e commit aad333b
Show file tree
Hide file tree
Showing 15 changed files with 152 additions and 49 deletions.
7 changes: 6 additions & 1 deletion src/vs/editor/common/core/offsetRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@

import { BugIndicatingError } from 'vs/base/common/errors';

export interface IOffsetRange {
readonly start: number;
readonly endExclusive: number;
}

/**
* A range of offsets (0-based).
*/
export class OffsetRange {
export class OffsetRange implements IOffsetRange {
public static addRange(range: OffsetRange, sortedRanges: OffsetRange[]): void {
let i = 0;
while (i < sortedRanges.length && sortedRanges[i].endExclusive < range.start) {
Expand Down
38 changes: 25 additions & 13 deletions src/vs/workbench/contrib/chat/common/chatModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { IMarkdownString, MarkdownString, isMarkdownString } from 'vs/base/commo
import { Disposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
import { ILogService } from 'vs/platform/log/common/log';
import { IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents';
import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { IChat, IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IUsedContext, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';

export interface IChatRequestModel {
Expand Down Expand Up @@ -347,7 +348,7 @@ export interface ISerializableChatAgentData {

export interface ISerializableChatRequestData {
providerRequestId: string | undefined;
message: string;
message: string | IParsedChatRequest;
response: (IMarkdownString | IChatResponseProgressFileTreeData)[] | undefined;
agent?: ISerializableChatAgentData;
responseErrorDetails: IChatResponseErrorDetails | undefined;
Expand Down Expand Up @@ -494,6 +495,7 @@ export class ChatModel extends Disposable implements IChatModel {
public readonly providerId: string,
private readonly initialData: ISerializableChatData | IExportableChatData | undefined,
@ILogService private readonly logService: ILogService,
@IChatAgentService private readonly chatAgentService: IChatAgentService,
) {
super();

Expand All @@ -519,15 +521,25 @@ export class ChatModel extends Disposable implements IChatModel {
this._welcomeMessage = new ChatWelcomeMessageModel(this, content);
}

return [];
// return requests.map((raw: ISerializableChatRequestData) => {
// const request = new ChatRequestModel(this, raw.message, raw.providerRequestId);
// if (raw.response || raw.responseErrorDetails) {
// const agent = raw.agent && this.chatAgentService.getAgents().find(a => a.id === raw.agent!.id); // TODO do something reasonable if this agent has disappeared since the last session
// request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, true, raw.isCanceled, raw.vote, raw.providerRequestId, raw.responseErrorDetails, raw.followups);
// }
// return request;
// });
return requests.map((raw: ISerializableChatRequestData) => {
const parsedRequest = typeof raw.message === 'string' ? this.getParsedRequestFromString(raw.message) :
reviveParsedChatRequest(raw.message);
const request = new ChatRequestModel(this, parsedRequest, raw.providerRequestId);
if (raw.response || raw.responseErrorDetails) {
const agent = raw.agent && this.chatAgentService.getAgents().find(a => a.id === raw.agent!.id); // TODO do something reasonable if this agent has disappeared since the last session
request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, true, raw.isCanceled, raw.vote, raw.providerRequestId, raw.responseErrorDetails, raw.followups);
}
return request;
});
}

private getParsedRequestFromString(message: string): IParsedChatRequest {
// TODO These offsets won't be used, but chat replies need to go through the parser as well
const parts = [new ChatRequestTextPart(new OffsetRange(1, message.length), { startColumn: 1, startLineNumber: 1, endColumn: 1, endLineNumber: 1 }, message)];
return {
text: message,
parts
};
}

startReinitialize(): void {
Expand Down Expand Up @@ -679,7 +691,7 @@ export class ChatModel extends Disposable implements IChatModel {
requests: this._requests.map((r): ISerializableChatRequestData => {
return {
providerRequestId: r.providerRequestId,
message: typeof r.message === 'string' ? r.message : '',
message: 'text' in r.message ? r.message : r.message.message,
response: r.response ? r.response.response.value : undefined,
responseErrorDetails: r.response?.errorDetails,
followups: r.response?.followups,
Expand Down
57 changes: 55 additions & 2 deletions src/vs/workbench/contrib/chat/common/chatParserTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { OffsetRange } from 'vs/editor/common/core/offsetRange';
import { IOffsetRange, OffsetRange } from 'vs/editor/common/core/offsetRange';
import { IRange } from 'vs/editor/common/core/range';
import { IChatAgentData, IChatAgentCommand } from 'vs/workbench/contrib/chat/common/chatAgents';
import { ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
Expand All @@ -16,14 +16,17 @@ export interface IParsedChatRequest {
}

export interface IParsedChatRequestPart {
readonly range: OffsetRange;
readonly kind: string; // for serialization
readonly range: IOffsetRange;
readonly editorRange: IRange;
readonly text: string;
}

// TODO rename to tokens

export class ChatRequestTextPart implements IParsedChatRequestPart {
static readonly Kind = 'text';
readonly kind = ChatRequestTextPart.Kind;
constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly text: string) { }
}

Expand All @@ -33,6 +36,8 @@ export const chatVariableLeader = '#'; // warning, this also shows up in a regex
* An invocation of a static variable that can be resolved by the variable service
*/
export class ChatRequestVariablePart implements IParsedChatRequestPart {
static readonly Kind = 'var';
readonly kind = ChatRequestVariablePart.Kind;
constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly variableName: string, readonly variableArg: string) { }

get text(): string {
Expand All @@ -45,6 +50,8 @@ export class ChatRequestVariablePart implements IParsedChatRequestPart {
* An invocation of an agent that can be resolved by the agent service
*/
export class ChatRequestAgentPart implements IParsedChatRequestPart {
static readonly Kind = 'agent';
readonly kind = ChatRequestAgentPart.Kind;
constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly agent: IChatAgentData) { }

get text(): string {
Expand All @@ -56,6 +63,8 @@ export class ChatRequestAgentPart implements IParsedChatRequestPart {
* An invocation of an agent's subcommand
*/
export class ChatRequestAgentSubcommandPart implements IParsedChatRequestPart {
static readonly Kind = 'subcommand';
readonly kind = ChatRequestAgentSubcommandPart.Kind;
constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly command: IChatAgentCommand) { }

get text(): string {
Expand All @@ -67,9 +76,53 @@ export class ChatRequestAgentSubcommandPart implements IParsedChatRequestPart {
* An invocation of a standalone slash command
*/
export class ChatRequestSlashCommandPart implements IParsedChatRequestPart {
static readonly Kind = 'slash';
readonly kind = ChatRequestSlashCommandPart.Kind;
constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly slashCommand: ISlashCommand) { }

get text(): string {
return `/${this.slashCommand.command}`;
}
}

export function reviveParsedChatRequest(serialized: IParsedChatRequest): IParsedChatRequest {
return {
text: serialized.text,
parts: serialized.parts.map(part => {
if (part.kind === ChatRequestTextPart.Kind) {
return new ChatRequestTextPart(
new OffsetRange(part.range.start, part.range.endExclusive),
part.editorRange,
part.text
);
} else if (part.kind === ChatRequestVariablePart.Kind) {
return new ChatRequestVariablePart(
new OffsetRange(part.range.start, part.range.endExclusive),
part.editorRange,
(part as ChatRequestVariablePart).variableName,
(part as ChatRequestVariablePart).variableArg
);
} else if (part.kind === ChatRequestAgentPart.Kind) {
return new ChatRequestAgentPart(
new OffsetRange(part.range.start, part.range.endExclusive),
part.editorRange,
(part as ChatRequestAgentPart).agent
);
} else if (part.kind === ChatRequestAgentSubcommandPart.Kind) {
return new ChatRequestAgentSubcommandPart(
new OffsetRange(part.range.start, part.range.endExclusive),
part.editorRange,
(part as ChatRequestAgentSubcommandPart).command
);
} else if (part.kind === ChatRequestSlashCommandPart.Kind) {
return new ChatRequestSlashCommandPart(
new OffsetRange(part.range.start, part.range.endExclusive),
part.editorRange,
(part as ChatRequestSlashCommandPart).slashCommand
);
} else {
throw new Error(`Unknown chat request part: ${part.kind}`);
}
})
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
endLineNumber: 1,
endColumn: 11
},
text: "Hello Mr. "
text: "Hello Mr. ",
kind: "text"
},
{
range: {
Expand All @@ -30,7 +31,8 @@
description: "",
subCommands: [ { name: "subCommand" } ]
}
}
},
kind: "agent"
},
{
range: {
Expand All @@ -43,7 +45,8 @@
endLineNumber: 1,
endColumn: 18
},
text: " "
text: " ",
kind: "text"
},
{
range: {
Expand All @@ -56,7 +59,8 @@
endLineNumber: 1,
endColumn: 29
},
command: { name: "subCommand" }
command: { name: "subCommand" },
kind: "subcommand"
},
{
range: {
Expand All @@ -69,7 +73,8 @@
endLineNumber: 1,
endColumn: 36
},
text: " thanks"
text: " thanks",
kind: "text"
}
],
text: "Hello Mr. @agent /subCommand thanks"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
endLineNumber: 1,
endColumn: 15
},
text: "Are you there "
text: "Are you there ",
kind: "text"
},
{
range: {
Expand All @@ -30,7 +31,8 @@
description: "",
subCommands: [ { name: "subCommand" } ]
}
}
},
kind: "agent"
},
{
range: {
Expand All @@ -43,7 +45,8 @@
endLineNumber: 1,
endColumn: 22
},
text: "?"
text: "?",
kind: "text"
}
],
text: "Are you there @agent?"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
description: "",
subCommands: [ { name: "subCommand" } ]
}
}
},
kind: "agent"
},
{
range: {
Expand All @@ -30,7 +31,8 @@
endLineNumber: 1,
endColumn: 18
},
text: " Please do "
text: " Please do ",
kind: "text"
},
{
range: {
Expand All @@ -43,7 +45,8 @@
endLineNumber: 1,
endColumn: 29
},
command: { name: "subCommand" }
command: { name: "subCommand" },
kind: "subcommand"
},
{
range: {
Expand All @@ -56,7 +59,8 @@
endLineNumber: 1,
endColumn: 36
},
text: " thanks"
text: " thanks",
kind: "text"
}
],
text: "@agent Please do /subCommand thanks"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
description: "",
subCommands: [ { name: "subCommand" } ]
}
}
},
kind: "agent"
},
{
range: {
Expand All @@ -30,7 +31,8 @@
endLineNumber: 2,
endColumn: 4
},
text: " Please \ndo "
text: " Please \ndo ",
kind: "text"
},
{
range: {
Expand All @@ -43,7 +45,8 @@
endLineNumber: 2,
endColumn: 15
},
command: { name: "subCommand" }
command: { name: "subCommand" },
kind: "subcommand"
},
{
range: {
Expand All @@ -56,7 +59,8 @@
endLineNumber: 2,
endColumn: 21
},
text: " with "
text: " with ",
kind: "text"
},
{
range: {
Expand All @@ -70,7 +74,8 @@
endColumn: 31
},
variableName: "selection",
variableArg: ""
variableArg: "",
kind: "var"
},
{
range: {
Expand All @@ -83,7 +88,8 @@
endLineNumber: 3,
endColumn: 5
},
text: "\nand "
text: "\nand ",
kind: "text"
},
{
range: {
Expand All @@ -97,7 +103,8 @@
endColumn: 18
},
variableName: "debugConsole",
variableArg: ""
variableArg: "",
kind: "var"
}
],
text: "@agent Please \ndo /subCommand with #selection\nand #debugConsole"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
endLineNumber: 1,
endColumn: 14
},
text: "/explain this"
text: "/explain this",
kind: "text"
}
],
text: "/explain this"
Expand Down
Loading

0 comments on commit aad333b

Please sign in to comment.