Skip to content

Commit aad333b

Browse files
authored
Fix chats not being persisted (microsoft#194357)
* Fix chats not being persisted Fix #430 Still need to fully square the parsed chat representation with chat followups * Fix tests
1 parent 80e3d4e commit aad333b

15 files changed

+152
-49
lines changed

src/vs/editor/common/core/offsetRange.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@
55

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

8+
export interface IOffsetRange {
9+
readonly start: number;
10+
readonly endExclusive: number;
11+
}
12+
813
/**
914
* A range of offsets (0-based).
1015
*/
11-
export class OffsetRange {
16+
export class OffsetRange implements IOffsetRange {
1217
public static addRange(range: OffsetRange, sortedRanges: OffsetRange[]): void {
1318
let i = 0;
1419
while (i < sortedRanges.length && sortedRanges[i].endExclusive < range.start) {

src/vs/workbench/contrib/chat/common/chatModel.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import { IMarkdownString, MarkdownString, isMarkdownString } from 'vs/base/commo
99
import { Disposable } from 'vs/base/common/lifecycle';
1010
import { URI, UriComponents } from 'vs/base/common/uri';
1111
import { generateUuid } from 'vs/base/common/uuid';
12+
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
1213
import { ILogService } from 'vs/platform/log/common/log';
13-
import { IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents';
14-
import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
14+
import { IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
15+
import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
1516
import { IChat, IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IUsedContext, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
1617

1718
export interface IChatRequestModel {
@@ -347,7 +348,7 @@ export interface ISerializableChatAgentData {
347348

348349
export interface ISerializableChatRequestData {
349350
providerRequestId: string | undefined;
350-
message: string;
351+
message: string | IParsedChatRequest;
351352
response: (IMarkdownString | IChatResponseProgressFileTreeData)[] | undefined;
352353
agent?: ISerializableChatAgentData;
353354
responseErrorDetails: IChatResponseErrorDetails | undefined;
@@ -494,6 +495,7 @@ export class ChatModel extends Disposable implements IChatModel {
494495
public readonly providerId: string,
495496
private readonly initialData: ISerializableChatData | IExportableChatData | undefined,
496497
@ILogService private readonly logService: ILogService,
498+
@IChatAgentService private readonly chatAgentService: IChatAgentService,
497499
) {
498500
super();
499501

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

522-
return [];
523-
// return requests.map((raw: ISerializableChatRequestData) => {
524-
// const request = new ChatRequestModel(this, raw.message, raw.providerRequestId);
525-
// if (raw.response || raw.responseErrorDetails) {
526-
// 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
527-
// request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, true, raw.isCanceled, raw.vote, raw.providerRequestId, raw.responseErrorDetails, raw.followups);
528-
// }
529-
// return request;
530-
// });
524+
return requests.map((raw: ISerializableChatRequestData) => {
525+
const parsedRequest = typeof raw.message === 'string' ? this.getParsedRequestFromString(raw.message) :
526+
reviveParsedChatRequest(raw.message);
527+
const request = new ChatRequestModel(this, parsedRequest, raw.providerRequestId);
528+
if (raw.response || raw.responseErrorDetails) {
529+
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
530+
request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, true, raw.isCanceled, raw.vote, raw.providerRequestId, raw.responseErrorDetails, raw.followups);
531+
}
532+
return request;
533+
});
534+
}
535+
536+
private getParsedRequestFromString(message: string): IParsedChatRequest {
537+
// TODO These offsets won't be used, but chat replies need to go through the parser as well
538+
const parts = [new ChatRequestTextPart(new OffsetRange(1, message.length), { startColumn: 1, startLineNumber: 1, endColumn: 1, endLineNumber: 1 }, message)];
539+
return {
540+
text: message,
541+
parts
542+
};
531543
}
532544

533545
startReinitialize(): void {
@@ -679,7 +691,7 @@ export class ChatModel extends Disposable implements IChatModel {
679691
requests: this._requests.map((r): ISerializableChatRequestData => {
680692
return {
681693
providerRequestId: r.providerRequestId,
682-
message: typeof r.message === 'string' ? r.message : '',
694+
message: 'text' in r.message ? r.message : r.message.message,
683695
response: r.response ? r.response.response.value : undefined,
684696
responseErrorDetails: r.response?.errorDetails,
685697
followups: r.response?.followups,

src/vs/workbench/contrib/chat/common/chatParserTypes.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

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

1818
export interface IParsedChatRequestPart {
19-
readonly range: OffsetRange;
19+
readonly kind: string; // for serialization
20+
readonly range: IOffsetRange;
2021
readonly editorRange: IRange;
2122
readonly text: string;
2223
}
2324

2425
// TODO rename to tokens
2526

2627
export class ChatRequestTextPart implements IParsedChatRequestPart {
28+
static readonly Kind = 'text';
29+
readonly kind = ChatRequestTextPart.Kind;
2730
constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly text: string) { }
2831
}
2932

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

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

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

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

7283
get text(): string {
7384
return `/${this.slashCommand.command}`;
7485
}
7586
}
87+
88+
export function reviveParsedChatRequest(serialized: IParsedChatRequest): IParsedChatRequest {
89+
return {
90+
text: serialized.text,
91+
parts: serialized.parts.map(part => {
92+
if (part.kind === ChatRequestTextPart.Kind) {
93+
return new ChatRequestTextPart(
94+
new OffsetRange(part.range.start, part.range.endExclusive),
95+
part.editorRange,
96+
part.text
97+
);
98+
} else if (part.kind === ChatRequestVariablePart.Kind) {
99+
return new ChatRequestVariablePart(
100+
new OffsetRange(part.range.start, part.range.endExclusive),
101+
part.editorRange,
102+
(part as ChatRequestVariablePart).variableName,
103+
(part as ChatRequestVariablePart).variableArg
104+
);
105+
} else if (part.kind === ChatRequestAgentPart.Kind) {
106+
return new ChatRequestAgentPart(
107+
new OffsetRange(part.range.start, part.range.endExclusive),
108+
part.editorRange,
109+
(part as ChatRequestAgentPart).agent
110+
);
111+
} else if (part.kind === ChatRequestAgentSubcommandPart.Kind) {
112+
return new ChatRequestAgentSubcommandPart(
113+
new OffsetRange(part.range.start, part.range.endExclusive),
114+
part.editorRange,
115+
(part as ChatRequestAgentSubcommandPart).command
116+
);
117+
} else if (part.kind === ChatRequestSlashCommandPart.Kind) {
118+
return new ChatRequestSlashCommandPart(
119+
new OffsetRange(part.range.start, part.range.endExclusive),
120+
part.editorRange,
121+
(part as ChatRequestSlashCommandPart).slashCommand
122+
);
123+
} else {
124+
throw new Error(`Unknown chat request part: ${part.kind}`);
125+
}
126+
})
127+
};
128+
}

src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_not_first.0.snap

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
endLineNumber: 1,
1212
endColumn: 11
1313
},
14-
text: "Hello Mr. "
14+
text: "Hello Mr. ",
15+
kind: "text"
1516
},
1617
{
1718
range: {
@@ -30,7 +31,8 @@
3031
description: "",
3132
subCommands: [ { name: "subCommand" } ]
3233
}
33-
}
34+
},
35+
kind: "agent"
3436
},
3537
{
3638
range: {
@@ -43,7 +45,8 @@
4345
endLineNumber: 1,
4446
endColumn: 18
4547
},
46-
text: " "
48+
text: " ",
49+
kind: "text"
4750
},
4851
{
4952
range: {
@@ -56,7 +59,8 @@
5659
endLineNumber: 1,
5760
endColumn: 29
5861
},
59-
command: { name: "subCommand" }
62+
command: { name: "subCommand" },
63+
kind: "subcommand"
6064
},
6165
{
6266
range: {
@@ -69,7 +73,8 @@
6973
endLineNumber: 1,
7074
endColumn: 36
7175
},
72-
text: " thanks"
76+
text: " thanks",
77+
kind: "text"
7378
}
7479
],
7580
text: "Hello Mr. @agent /subCommand thanks"

src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_with_question_mark.0.snap

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
endLineNumber: 1,
1212
endColumn: 15
1313
},
14-
text: "Are you there "
14+
text: "Are you there ",
15+
kind: "text"
1516
},
1617
{
1718
range: {
@@ -30,7 +31,8 @@
3031
description: "",
3132
subCommands: [ { name: "subCommand" } ]
3233
}
33-
}
34+
},
35+
kind: "agent"
3436
},
3537
{
3638
range: {
@@ -43,7 +45,8 @@
4345
endLineNumber: 1,
4446
endColumn: 22
4547
},
46-
text: "?"
48+
text: "?",
49+
kind: "text"
4750
}
4851
],
4952
text: "Are you there @agent?"

src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents.0.snap

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
description: "",
1818
subCommands: [ { name: "subCommand" } ]
1919
}
20-
}
20+
},
21+
kind: "agent"
2122
},
2223
{
2324
range: {
@@ -30,7 +31,8 @@
3031
endLineNumber: 1,
3132
endColumn: 18
3233
},
33-
text: " Please do "
34+
text: " Please do ",
35+
kind: "text"
3436
},
3537
{
3638
range: {
@@ -43,7 +45,8 @@
4345
endLineNumber: 1,
4446
endColumn: 29
4547
},
46-
command: { name: "subCommand" }
48+
command: { name: "subCommand" },
49+
kind: "subcommand"
4750
},
4851
{
4952
range: {
@@ -56,7 +59,8 @@
5659
endLineNumber: 1,
5760
endColumn: 36
5861
},
59-
text: " thanks"
62+
text: " thanks",
63+
kind: "text"
6064
}
6165
],
6266
text: "@agent Please do /subCommand thanks"

src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline.0.snap

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
description: "",
1818
subCommands: [ { name: "subCommand" } ]
1919
}
20-
}
20+
},
21+
kind: "agent"
2122
},
2223
{
2324
range: {
@@ -30,7 +31,8 @@
3031
endLineNumber: 2,
3132
endColumn: 4
3233
},
33-
text: " Please \ndo "
34+
text: " Please \ndo ",
35+
kind: "text"
3436
},
3537
{
3638
range: {
@@ -43,7 +45,8 @@
4345
endLineNumber: 2,
4446
endColumn: 15
4547
},
46-
command: { name: "subCommand" }
48+
command: { name: "subCommand" },
49+
kind: "subcommand"
4750
},
4851
{
4952
range: {
@@ -56,7 +59,8 @@
5659
endLineNumber: 2,
5760
endColumn: 21
5861
},
59-
text: " with "
62+
text: " with ",
63+
kind: "text"
6064
},
6165
{
6266
range: {
@@ -70,7 +74,8 @@
7074
endColumn: 31
7175
},
7276
variableName: "selection",
73-
variableArg: ""
77+
variableArg: "",
78+
kind: "var"
7479
},
7580
{
7681
range: {
@@ -83,7 +88,8 @@
8388
endLineNumber: 3,
8489
endColumn: 5
8590
},
86-
text: "\nand "
91+
text: "\nand ",
92+
kind: "text"
8793
},
8894
{
8995
range: {
@@ -97,7 +103,8 @@
97103
endColumn: 18
98104
},
99105
variableName: "debugConsole",
100-
variableArg: ""
106+
variableArg: "",
107+
kind: "var"
101108
}
102109
],
103110
text: "@agent Please \ndo /subCommand with #selection\nand #debugConsole"

src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_invalid_slash_command.0.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
endLineNumber: 1,
1212
endColumn: 14
1313
},
14-
text: "/explain this"
14+
text: "/explain this",
15+
kind: "text"
1516
}
1617
],
1718
text: "/explain this"

0 commit comments

Comments
 (0)