Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/chatDebug/chatDebugModelTurnContentRenderer.ts
13406 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 * as DOM from '../../../../../base/browser/dom.js';
7
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
8
import { localize } from '../../../../../nls.js';
9
import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js';
10
import { ILanguageService } from '../../../../../editor/common/languages/language.js';
11
import { IChatDebugEventModelTurnContent } from '../../common/chatDebugService.js';
12
import { renderSection, tokenizeContent } from './chatDebugToolCallContentRenderer.js';
13
import { safeIntl } from '../../../../../base/common/date.js';
14
15
const $ = DOM.$;
16
const numberFormatter = safeIntl.NumberFormat();
17
18
/**
19
* Render a resolved model turn content with structured display of
20
* request metadata, token usage, and timing.
21
* When JSON is detected in section content, renders it with syntax highlighting.
22
*/
23
export async function renderModelTurnContent(content: IChatDebugEventModelTurnContent, languageService: ILanguageService, clipboardService?: IClipboardService, scrollable?: { scanDomNode(): void }): Promise<{ element: HTMLElement; disposables: DisposableStore }> {
24
const disposables = new DisposableStore();
25
const container = $('div.chat-debug-message-content');
26
container.tabIndex = 0;
27
28
// Header: Model Turn
29
DOM.append(container, $('div.chat-debug-message-content-title', undefined, localize('chatDebug.modelTurn.title', "Model Turn")));
30
31
// Status summary line
32
const statusParts: string[] = [];
33
if (content.requestName) {
34
statusParts.push(content.requestName);
35
}
36
if (content.model) {
37
statusParts.push(content.model);
38
}
39
if (content.status && content.status !== 'unknown') {
40
statusParts.push(content.status);
41
}
42
if (content.durationInMillis !== undefined) {
43
statusParts.push(localize('chatDebug.modelTurn.duration', "{0}ms", numberFormatter.value.format(content.durationInMillis)));
44
}
45
if (statusParts.length > 0) {
46
DOM.append(container, $('div.chat-debug-message-content-summary', undefined, statusParts.join(' \u00b7 ')));
47
}
48
49
// Token usage details
50
const detailsContainer = DOM.append(container, $('div.chat-debug-model-turn-details'));
51
52
if (content.inputTokens !== undefined) {
53
DOM.append(detailsContainer, $('div', undefined, localize('chatDebug.modelTurn.inputTokens', "Input tokens: {0}", numberFormatter.value.format(content.inputTokens))));
54
}
55
if (content.outputTokens !== undefined) {
56
DOM.append(detailsContainer, $('div', undefined, localize('chatDebug.modelTurn.outputTokens', "Output tokens: {0}", numberFormatter.value.format(content.outputTokens))));
57
}
58
if (content.cachedTokens !== undefined) {
59
DOM.append(detailsContainer, $('div', undefined, localize('chatDebug.modelTurn.cachedTokens', "Cached tokens: {0}", numberFormatter.value.format(content.cachedTokens))));
60
}
61
if (content.totalTokens !== undefined) {
62
DOM.append(detailsContainer, $('div', undefined, localize('chatDebug.modelTurn.totalTokens', "Total tokens: {0}", numberFormatter.value.format(content.totalTokens))));
63
}
64
if (content.timeToFirstTokenInMillis !== undefined) {
65
DOM.append(detailsContainer, $('div', undefined, localize('chatDebug.modelTurn.ttft', "Time to first token: {0}ms", numberFormatter.value.format(content.timeToFirstTokenInMillis))));
66
}
67
if (content.maxInputTokens !== undefined) {
68
DOM.append(detailsContainer, $('div', undefined, localize('chatDebug.modelTurn.maxInputTokens', "Max input tokens: {0}", numberFormatter.value.format(content.maxInputTokens))));
69
}
70
if (content.maxOutputTokens !== undefined) {
71
DOM.append(detailsContainer, $('div', undefined, localize('chatDebug.modelTurn.maxOutputTokens', "Max output tokens: {0}", numberFormatter.value.format(content.maxOutputTokens))));
72
}
73
if (content.errorMessage) {
74
DOM.append(detailsContainer, $('div.chat-debug-model-turn-error', undefined, localize('chatDebug.modelTurn.error', "Error: {0}", content.errorMessage)));
75
}
76
77
// Collapsible sections (e.g., system prompt, user prompt, tools, response)
78
if (content.sections && content.sections.length > 0) {
79
const sectionsContainer = DOM.append(container, $('div.chat-debug-message-sections'));
80
DOM.append(sectionsContainer, $('div.chat-debug-message-sections-label', undefined,
81
localize('chatDebug.modelTurn.sections', "Sections ({0})", content.sections.length)));
82
83
for (const section of content.sections) {
84
const { plainText, tokenizedHtml } = await tokenizeContent(section.content, languageService);
85
renderSection(sectionsContainer, section.name, plainText, tokenizedHtml, disposables, false, clipboardService, scrollable);
86
}
87
}
88
89
return { element: container, disposables };
90
}
91
92
/**
93
* Convert a resolved model turn content to plain text for clipboard / editor output.
94
*/
95
export function modelTurnContentToPlainText(content: IChatDebugEventModelTurnContent): string {
96
const lines: string[] = [];
97
lines.push(localize('chatDebug.modelTurn.requestLabel', "Request: {0}", content.requestName));
98
99
if (content.model) {
100
lines.push(localize('chatDebug.modelTurn.modelLabel', "Model: {0}", content.model));
101
}
102
if (content.status && content.status !== 'unknown') {
103
lines.push(localize('chatDebug.modelTurn.statusLabel', "Status: {0}", content.status));
104
}
105
if (content.durationInMillis !== undefined) {
106
lines.push(localize('chatDebug.modelTurn.durationLabel', "Duration: {0}ms", numberFormatter.value.format(content.durationInMillis)));
107
}
108
if (content.timeToFirstTokenInMillis !== undefined) {
109
lines.push(localize('chatDebug.modelTurn.ttftLabel', "Time to first token: {0}ms", numberFormatter.value.format(content.timeToFirstTokenInMillis)));
110
}
111
if (content.inputTokens !== undefined) {
112
lines.push(localize('chatDebug.modelTurn.inputTokensLabel', "Input tokens: {0}", numberFormatter.value.format(content.inputTokens)));
113
}
114
if (content.outputTokens !== undefined) {
115
lines.push(localize('chatDebug.modelTurn.outputTokensLabel', "Output tokens: {0}", numberFormatter.value.format(content.outputTokens)));
116
}
117
if (content.cachedTokens !== undefined) {
118
lines.push(localize('chatDebug.modelTurn.cachedTokensLabel', "Cached tokens: {0}", numberFormatter.value.format(content.cachedTokens)));
119
}
120
if (content.totalTokens !== undefined) {
121
lines.push(localize('chatDebug.modelTurn.totalTokensLabel', "Total tokens: {0}", numberFormatter.value.format(content.totalTokens)));
122
}
123
if (content.maxInputTokens !== undefined) {
124
lines.push(localize('chatDebug.modelTurn.maxInputTokensLabel', "Max input tokens: {0}", numberFormatter.value.format(content.maxInputTokens)));
125
}
126
if (content.maxOutputTokens !== undefined) {
127
lines.push(localize('chatDebug.modelTurn.maxOutputTokensLabel', "Max output tokens: {0}", numberFormatter.value.format(content.maxOutputTokens)));
128
}
129
if (content.errorMessage) {
130
lines.push(localize('chatDebug.modelTurn.errorLabel', "Error: {0}", content.errorMessage));
131
}
132
133
if (content.sections && content.sections.length > 0) {
134
lines.push('');
135
for (const section of content.sections) {
136
lines.push(`--- ${section.name} ---`);
137
lines.push(section.content);
138
lines.push('');
139
}
140
}
141
142
return lines.join('\n');
143
}
144
145