Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/inlineChat/node/rendererVisualization.ts
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 { PromptRenderer } from '@vscode/prompt-tsx';
7
import { IDebugValueEditorGlobals } from '../../../util/common/debugValueEditorGlobals';
8
9
export class RendererVisualizations {
10
public static getIfVisualizationTestIsRunning(): RendererVisualizations | undefined {
11
if (VisualizationTestRun.instance) {
12
return new RendererVisualizations();
13
}
14
return undefined;
15
}
16
17
/**
18
* Exposes the rendering to the visualization extension.
19
* Also overrides the render method so that we can show the tree once rendering is done.
20
*/
21
public decorateAndRegister<T extends PromptRenderer<any, any>>(renderer: T, label: string): T {
22
let first = false;
23
renderer.render = async function (this: unknown, ...args: any[]) {
24
const result = await Object.getPrototypeOf(renderer).render.apply(this, ...args);
25
if (!first) {
26
first = true;
27
new RendererVisualization(renderer, label);
28
VisualizationTestRun.instance?.reload();
29
}
30
31
return result;
32
} as any;
33
return renderer;
34
}
35
}
36
37
/**
38
* Describes the visualization of a prompt renderer.
39
* Only used for debugging.
40
*/
41
class RendererVisualization {
42
constructor(
43
private readonly _renderer: PromptRenderer<any, any>,
44
label: string,
45
) {
46
VisualizationTestRun.instance?.addData(`Prompt ${label}`, () => this.getData());
47
}
48
49
getData() {
50
class RenderedNode {
51
constructor(
52
public readonly label: string,
53
private readonly children: RenderedNode[],
54
private readonly range: [number, number] | undefined,
55
) {
56
if (!range) {
57
const childrenRanges = children.map(c => c.range).filter(r => !!r);
58
if (childrenRanges.length > 0) {
59
range = [Number.MAX_SAFE_INTEGER, 0];
60
for (const crange of childrenRanges) {
61
range[0] = Math.min(range[0], crange[0]);
62
range[1] = Math.max(range[1], crange[1]);
63
}
64
this.range = range;
65
}
66
}
67
}
68
69
toObj(): unknown {
70
return {
71
label: this.label,
72
codicon: (this.label === 'Text' || this.label === 'LineBreak') ? 'text-size' : 'symbol-class',
73
range: this.range,
74
children: this.children.map(c => c.toObj()),
75
};
76
}
77
}
78
79
const data = this._renderer;
80
let promptResult = '';
81
82
function walk(item: /*PromptTreeElement |*/ any): RenderedNode {
83
if (item.kind === 0 /* Piece */) {
84
const messageClasses = [
85
'SystemMessage',
86
'UserMessage',
87
'AssistantMessage',
88
];
89
const ctorName = item['_obj'].constructor.name;
90
91
if (messageClasses.some(c => ctorName.indexOf(c) !== -1)) {
92
promptResult += `\n======== ${ctorName} ========\n`;
93
}
94
95
const children = (item['_children'] as any[]).map(c => walk(c)).filter(c => c.label !== 'LineBreak');
96
97
return new RenderedNode(ctorName, children, undefined);
98
99
} else if (item.kind === 1 /* Text */) {
100
const start = promptResult.length;
101
promptResult = promptResult + item.text;
102
return new RenderedNode('Text', [], [start, promptResult.length]);
103
} else if (item.kind === 2 /* LineBreak */) {
104
const start = promptResult.length;
105
promptResult = promptResult + '\n';
106
return new RenderedNode('LineBreak', [], [start, promptResult.length]);
107
}
108
throw new Error();
109
}
110
111
const n = walk(data['_root']);
112
113
const d = {
114
root: n.toObj(),
115
source: promptResult,
116
...{ $fileExtension: 'ast.w' },
117
};
118
119
return d;
120
}
121
}
122
123
export class VisualizationTestRun {
124
private static _instance: VisualizationTestRun | undefined = undefined;
125
public static get instance() { return this._instance; }
126
127
public static startRun() {
128
this._instance = new VisualizationTestRun();
129
}
130
131
private readonly g = globalThis as any as IDebugValueEditorGlobals;
132
133
private _data: readonly any[] = [];
134
private _knownLabels: Set<string> = new Set();
135
136
constructor() {
137
this.g.$$debugValueEditor_properties = [];
138
}
139
140
public addData(label: string, getData: () => unknown, suffix?: string, property?: string): void {
141
const propertyName = 'debugValueProperty###' + label;
142
(globalThis as any)[propertyName] = () => {
143
const data = getData();
144
if (suffix) {
145
return { [suffix]: data };
146
}
147
return data;
148
};
149
150
if (!this._knownLabels.has(propertyName)) {
151
this._knownLabels.add(propertyName);
152
const suffixStr = suffix ? `.${suffix}` : '';
153
this._data = [...this._data, { label, expression: `globalThis[${JSON.stringify(propertyName)}]()${suffixStr}${property ?? ''}` }];
154
this.g.$$debugValueEditor_properties = this._data;
155
} else {
156
this.g.$$debugValueEditor_refresh?.('{}');
157
}
158
}
159
160
public reload(): void {
161
this.g.$$debugValueEditor_refresh?.('{}');
162
}
163
}
164
165