Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/simulation/workbench/components/request.tsx
13399 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
6
import { Badge, Text, Tooltip } from '@fluentui/react-components';
7
import * as mobxlite from 'mobx-react-lite';
8
import * as React from 'react';
9
import { coalesce } from '../../../../src/util/vs/base/common/arrays';
10
import { assertType } from '../../../../src/util/vs/base/common/types';
11
import { ISerialisedChatMessage, ISerialisedChatResponse, InterceptedRequest } from '../../shared/sharedTypes';
12
import { DiffEditor } from './diffEditor';
13
import { Editor } from './editor';
14
import { isToolCall } from '../utils/utils';
15
import { Raw } from '@vscode/prompt-tsx';
16
17
type Props = {
18
readonly request: InterceptedRequest;
19
readonly title?: string;
20
readonly baselineRequest: InterceptedRequest | undefined;
21
readonly idx: number;
22
readonly expand: boolean;
23
};
24
25
export const RequestView = mobxlite.observer(({ request, title, baselineRequest, idx, expand }: Props) => {
26
27
const [expanded, setExpanded] = React.useState(expand);
28
29
function renderResponse(response: ISerialisedChatResponse): string {
30
if (response.type === 'success') {
31
const parts = [response.value?.join('\n-----\n')];
32
parts.push(request.response.copilotFunctionCalls?.map(c => {
33
let argsStr = c.arguments;
34
try {
35
const parsedArgs = JSON.parse(c.arguments);
36
argsStr = JSON.stringify(parsedArgs, undefined, 2);
37
} catch (e) { }
38
return `🛠️ ${c.name} (${c.id}) ${argsStr}`;
39
}).join('\n'));
40
return coalesce(parts).join('\n');
41
} else if (response.type === 'length' && response.truncatedValue) {
42
return response.truncatedValue;
43
} else {
44
return '<NO REPLY>\n' + JSON.stringify(response);
45
}
46
}
47
48
const currentRunRenderedMessages = renderRequestMessages(request.requestMessages);
49
const baselineRunRenderedMessages = baselineRequest ? renderRequestMessages(baselineRequest.requestMessages) : undefined;
50
51
const requestChangeInfo = baselineRequest && (
52
<Tooltip content={'Below compares two requests - one from "Compare against" run and one from "Current run"'} relationship={'label'}>
53
<Text weight='bold'>{currentRunRenderedMessages === baselineRunRenderedMessages
54
? '(not changed)'
55
: '(changed)'}</Text>
56
</Tooltip>
57
);
58
59
return (
60
<div className='request-container'>
61
<div className='title' onClick={() => setExpanded(!expanded)}>
62
{expanded ? '▼' : '▶'} {isToolCall(request) ? 'Tool Call' : 'Chat Request'} #{idx + 1} {title ? `- ${title}` : ``} - {getRequestStats(request)} {requestChangeInfo}
63
{request.model && <Badge title='Chat model used for request' color='informative' size='small'>{request.model}</Badge>}
64
</div>
65
{
66
!expanded
67
? null
68
: (
69
<div className='request-details' style={{ borderLeft: '1px solid #ccc', marginLeft: '7px', paddingLeft: '5px' }}>
70
<h3>Request to Model</h3>
71
{baselineRequest && <Text size={300}>Left editor - request from "Compare against" run, Right editor - request from "Current run"</Text>}
72
<div style={{ marginLeft: '-10px' }}>
73
{
74
baselineRequest
75
? (
76
assertType(baselineRunRenderedMessages, 'must be non-undefined as long as `baselineRequest` is defined'),
77
<DiffEditor original={baselineRunRenderedMessages} modified={currentRunRenderedMessages} languageId='markdown' />
78
)
79
: <Editor lineNumbers={false} languageId='markdown' contents={currentRunRenderedMessages} />
80
}
81
</div>
82
<h3>Response from Model</h3>
83
{baselineRequest && <Text size={300}>Left editor - response from "Compare against" run, Right editor - response from "Current run"</Text>}
84
<div style={{ marginLeft: '-10px' }}>
85
<div className='reply'>
86
{
87
baselineRequest
88
? <DiffEditor languageId='markdown' original={renderResponse(baselineRequest.response)} modified={renderResponse(request.response)} />
89
: ((request.response.type === 'success' && request.response.value !== undefined) || (request.response.type === 'length' && request.response.truncatedValue !== undefined)
90
? <Editor lineNumbers={false} languageId='markdown' contents={renderResponse(request.response)} />
91
: <div >{renderResponse(request.response)}</div>)
92
}
93
</div>
94
</div>
95
</div>
96
)
97
}
98
</div>
99
);
100
});
101
102
function getRequestStats({ response }: InterceptedRequest) {
103
const result = [];
104
result.push(`(${response.type}`);
105
if (response.type === 'success') {
106
if (response.isCacheHit === true) {
107
result.push('cache hit');
108
if (response.cacheMetadata) {
109
result.push(`original fetch: ${response.cacheMetadata.requestTime.replace('T', ' ').substring(0, 19)}`);
110
result.push(`duration: ${response.cacheMetadata.requestDuration} ms`);
111
}
112
} else {
113
if (response.isCacheHit === false) {
114
result.push('cache miss');
115
}
116
result.push('server fetch');
117
if (response.cacheMetadata) {
118
result.push(`duration: ${response.cacheMetadata.requestDuration} ms`);
119
}
120
}
121
}
122
return result.join(', ') + ')';
123
}
124
125
function renderRequestMessages(messages: string | ISerialisedChatMessage[]): string {
126
return Array.isArray(messages) ? renderChatMessages(messages) : messages;
127
}
128
129
function renderChatMessages(messages: ISerialisedChatMessage[]): string {
130
const messageSeparator = '\n---------------------------------------------------------\n';
131
const roleSeparator = '\n------\n';
132
return (
133
messageSeparator +
134
messages
135
.map(m => {
136
const parts: string[] = [];
137
if (m.tool_call_id) {
138
parts.push(`🛠️ ${m.tool_call_id}`);
139
}
140
if (Array.isArray(m.content)) {
141
m.content.forEach(c => {
142
if (c.type === Raw.ChatCompletionContentPartKind.Text) {
143
parts.push(c.text);
144
} else {
145
parts.push(`${JSON.stringify(c)}`);
146
}
147
});
148
} else {
149
parts.push(m.content);
150
}
151
152
if (m.tool_calls) {
153
parts.push(m.tool_calls.map(c => {
154
let argsStr = c.function.arguments;
155
try {
156
const parsedArgs = JSON.parse(c.function.arguments);
157
argsStr = JSON.stringify(parsedArgs, undefined, 2);
158
} catch (e) { }
159
return `🛠️ ${c.function.name} (${c.id}) ${argsStr}`;
160
}).join('\n'));
161
}
162
const content = coalesce(parts).join('\n');
163
return `${m.role.toUpperCase()}:${roleSeparator}${content}`;
164
})
165
.join(messageSeparator)
166
).trim();
167
}
168
169