Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompt/test/node/conversation.spec.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
/* eslint-disable local/code-no-unused-expressions */
6
import { describe, expect, it } from 'vitest';
7
import type { ChatResult } from 'vscode';
8
import { ChatVariablesCollection } from '../../common/chatVariablesCollection';
9
import { IResultMetadata, normalizeSummariesOnRounds, Turn, TurnMessage, TurnStatus } from '../../common/conversation';
10
import { ToolCallRound } from '../../common/toolCallRound';
11
12
describe('Turn', () => {
13
describe('setResponse', () => {
14
it('should set the response message and status correctly', () => {
15
const request: TurnMessage = { type: 'user', message: 'Hello' };
16
const turn = new Turn('1', request, new ChatVariablesCollection([]));
17
18
const result: ChatResult = { metadata: {} };
19
const response: TurnMessage = { type: 'model', message: 'Hi there!' };
20
turn.setResponse(TurnStatus.Success, response, undefined, result);
21
22
expect(turn.responseMessage).to.equal(response);
23
expect(turn.responseStatus).to.equal(TurnStatus.Success);
24
expect(turn.responseChatResult === result);
25
});
26
27
it('should throw an error if setResponse is called more than once', () => {
28
const request: TurnMessage = { type: 'user', message: 'Hello' };
29
const turn = new Turn('1', request, new ChatVariablesCollection([]));
30
31
const response: TurnMessage = { type: 'model', message: 'Hi there!' };
32
turn.setResponse(TurnStatus.Success, response, undefined, undefined);
33
34
expect(() => turn.setResponse(TurnStatus.Success, response, undefined, undefined)).to.throw();
35
});
36
37
it('should default status to InProgress if not set', () => {
38
const request: TurnMessage = { type: 'user', message: 'Hello' };
39
const turn = new Turn('1', request, new ChatVariablesCollection([]));
40
41
expect(turn.responseStatus).to.equal(TurnStatus.InProgress);
42
});
43
44
const genericToolCall = { id: 'id', name: 'name', arguments: '{}' };
45
it('should restore summaries from metadata to current turns', () => {
46
const turn1 = new Turn('1', { type: 'user', message: 'Hello' });
47
const turn1Meta: Partial<IResultMetadata> = {
48
summary: {
49
text: 'summary 1',
50
toolCallRoundId: 'round1'
51
},
52
toolCallRounds: [
53
new ToolCallRound('Hello', [genericToolCall], undefined, 'round1'),
54
new ToolCallRound('Hello', [], undefined, 'round2'),
55
]
56
};
57
turn1.setResponse(TurnStatus.Success, { type: 'model', message: 'Hi there!' }, undefined, { metadata: turn1Meta });
58
normalizeSummariesOnRounds([turn1]);
59
expect(turn1.rounds[0].summary).to.equal('summary 1');
60
});
61
62
it('should restore only the last summary from summaries array', () => {
63
const turn1 = new Turn('1', { type: 'user', message: 'Hello' });
64
const turn1Meta: Partial<IResultMetadata> = {
65
summaries: [
66
{ text: 'summary 1', toolCallRoundId: 'round1' },
67
{ text: 'summary 2', toolCallRoundId: 'round2' },
68
],
69
toolCallRounds: [
70
new ToolCallRound('Hello', [genericToolCall], undefined, 'round1'),
71
new ToolCallRound('Hello', [genericToolCall], undefined, 'round2'),
72
new ToolCallRound('Hello', [], undefined, 'round3'),
73
]
74
};
75
turn1.setResponse(TurnStatus.Success, { type: 'model', message: 'Hi there!' }, undefined, { metadata: turn1Meta });
76
normalizeSummariesOnRounds([turn1]);
77
expect(turn1.rounds[0].summary).to.be.undefined;
78
expect(turn1.rounds[1].summary).to.equal('summary 2');
79
});
80
81
it('should restore only the last summary across turns', () => {
82
const turn1 = new Turn('1', { type: 'user', message: 'Hello' });
83
const turn1Meta: Partial<IResultMetadata> = {
84
toolCallRounds: [
85
new ToolCallRound('Hello', [genericToolCall], undefined, 'round1'),
86
new ToolCallRound('Hello', [genericToolCall], undefined, 'round2'),
87
]
88
};
89
turn1.setResponse(TurnStatus.Success, { type: 'model', message: 'Hi there!' }, undefined, { metadata: turn1Meta });
90
91
const turn2 = new Turn('2', { type: 'user', message: 'Hello' });
92
const turn2Meta: Partial<IResultMetadata> = {
93
summaries: [
94
{ text: 'summary for round1', toolCallRoundId: 'round1' },
95
{ text: 'summary for round3', toolCallRoundId: 'round3' },
96
],
97
toolCallRounds: [
98
new ToolCallRound('Hello', [genericToolCall], undefined, 'round3'),
99
new ToolCallRound('Hello', [], undefined, 'round4'),
100
]
101
};
102
turn2.setResponse(TurnStatus.Success, { type: 'model', message: 'Hi there!' }, undefined, { metadata: turn2Meta });
103
104
normalizeSummariesOnRounds([turn1, turn2]);
105
expect(turn1.rounds[0].summary).to.be.undefined;
106
expect(turn2.rounds[0].summary).to.equal('summary for round3');
107
});
108
109
it('should restore summaries from pendingSummaries when resultMetadata is absent', () => {
110
// Simulates the mid-tool-call-loop case: setResponse hasn't been called yet,
111
// so resultMetadata is empty, but addPendingSummary stored the summary.
112
const turn1 = new Turn('1', { type: 'user', message: 'Hello' });
113
turn1.addPendingSummary('round1', 'pending summary text');
114
// No setResponse call — this is the key: resultMetadata doesn't exist
115
// Manually set rounds so normalizeSummariesOnRounds can find the target
116
const turn1Meta: Partial<IResultMetadata> = {
117
toolCallRounds: [
118
new ToolCallRound('Hello', [genericToolCall], undefined, 'round1'),
119
new ToolCallRound('Hello', [], undefined, 'round2'),
120
]
121
};
122
turn1.setResponse(TurnStatus.Success, { type: 'model', message: 'Hi there!' }, undefined, { metadata: turn1Meta });
123
// Clear resultMetadata summaries to simulate mid-loop state
124
(turn1.responseChatResult!.metadata as Record<string, unknown>)['summaries'] = undefined;
125
(turn1.responseChatResult!.metadata as Record<string, unknown>)['summary'] = undefined;
126
127
normalizeSummariesOnRounds([turn1]);
128
expect(turn1.rounds[0].summary).to.equal('pending summary text');
129
});
130
131
it('should restore summaries from metadata to previous turns', () => {
132
const turn1 = new Turn('1', { type: 'user', message: 'Hello' });
133
const turn1Meta: Partial<IResultMetadata> = {
134
toolCallRounds: [
135
new ToolCallRound('Hello', [genericToolCall], undefined, 'round1'),
136
new ToolCallRound('Hello', [], undefined, 'round2'),
137
]
138
};
139
turn1.setResponse(TurnStatus.Success, { type: 'model', message: 'Hi there!' }, undefined, { metadata: turn1Meta });
140
141
const turn2 = new Turn('2', { type: 'user', message: 'Hello' });
142
const turn2Meta: Partial<IResultMetadata> = {
143
summary: {
144
text: 'summary',
145
toolCallRoundId: 'round1'
146
},
147
toolCallRounds: [
148
new ToolCallRound('Hello', [genericToolCall], undefined, 'round3'),
149
new ToolCallRound('Hello', [], undefined, 'round4'),
150
]
151
};
152
turn2.setResponse(TurnStatus.Success, { type: 'model', message: 'Hi there!' }, undefined, { metadata: turn2Meta });
153
154
normalizeSummariesOnRounds([turn1, turn2]);
155
expect(turn1.rounds[0].summary).to.equal('summary');
156
expect(turn2.rounds[0].summary).to.equal(undefined);
157
});
158
});
159
});
160
161