Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/panel/safeElements.tsx
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
6
import { BasePromptElementProps, PromptElement, PromptElementProps, PromptReference, TextChunk } from '@vscode/prompt-tsx';
7
import type * as vscode from 'vscode';
8
import { isScenarioAutomation } from '../../../../platform/env/common/envService';
9
import { IVSCodeExtensionContext } from '../../../../platform/extContext/common/extensionContext';
10
import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';
11
import { ILogService } from '../../../../platform/log/common/logService';
12
import { IPromptPathRepresentationService } from '../../../../platform/prompts/common/promptPathRepresentationService';
13
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry';
14
import { createFencedCodeBlock } from '../../../../util/common/markdown';
15
import { basename } from '../../../../util/vs/base/common/resources';
16
import { ExtensionMode } from '../../../../vscodeTypes';
17
18
19
export abstract class SafePromptElement<P extends BasePromptElementProps, S = void> extends PromptElement<P, S> {
20
21
constructor(props: P,
22
@IVSCodeExtensionContext protected readonly _contextService: IVSCodeExtensionContext,
23
@ITelemetryService protected readonly _telemetryService: ITelemetryService,
24
@ILogService protected readonly _logService: ILogService,
25
@IIgnoreService protected readonly _ignoreService: IIgnoreService,
26
) {
27
super(props);
28
}
29
30
protected _handleFoulPrompt(): undefined {
31
// REPORT error telemetry
32
// FAIL when running tests
33
const err = new Error('BAD PROMPT');
34
this._logService.error(err);
35
36
if (this._contextService.extensionMode !== ExtensionMode.Production && !isScenarioAutomation) {
37
throw err;
38
}
39
40
/* __GDPR__
41
"prompt.invalidreference": {
42
"owner": "jrieken",
43
"comment": "Tracks bad prompt references",
44
"stack": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Error stack" }
45
}
46
*/
47
this._telemetryService.sendMSFTTelemetryErrorEvent('prompt.invalidreference', { stack: err.stack });
48
}
49
}
50
// --- SafeCodeBlock
51
52
export type CodeBlockProps = PromptElementProps<{
53
readonly includeFilepath?: boolean;
54
readonly uri: vscode.Uri;
55
readonly code: string;
56
readonly languageId?: string;
57
readonly references?: PromptReference[];
58
59
/**
60
* If set, each line of the prompt will be in its own text chunk with
61
* descending priority so that it may be trimmed if over the token budget.
62
*/
63
readonly lineBasedPriority?: boolean;
64
65
/**
66
* Invokes `code.trim()`
67
*
68
* @default true
69
*/
70
readonly shouldTrim?: boolean;
71
72
/**
73
* Fence style, defaults to '```'. An empty string omits the fence.
74
*/
75
readonly fence?: string;
76
}>;
77
78
export class CodeBlock extends SafePromptElement<CodeBlockProps> {
79
80
constructor(props: CodeBlockProps,
81
@IVSCodeExtensionContext _contextService: IVSCodeExtensionContext,
82
@ITelemetryService _telemetryService: ITelemetryService,
83
@ILogService _logService: ILogService,
84
@IIgnoreService _ignoreService: IIgnoreService,
85
@IPromptPathRepresentationService private readonly _promptPathRepresentationService: IPromptPathRepresentationService,
86
) {
87
super(props, _contextService, _telemetryService, _logService, _ignoreService);
88
}
89
90
async render() {
91
const isIgnored = this.props.uri ? await this._ignoreService.isCopilotIgnored(this.props.uri) : false;
92
if (isIgnored) {
93
return this._handleFoulPrompt();
94
}
95
const filePath = this.props.includeFilepath ? this._promptPathRepresentationService.getFilePath(this.props.uri) : undefined;
96
const code = createFencedCodeBlock(this.props.languageId ?? '', this.props.code, this.props.shouldTrim ?? true, filePath, this.props.fence);
97
const reference = this.props.references && <references value={this.props.references} />;
98
99
if (this.props.lineBasedPriority) {
100
const lines = code.split('\n');
101
// Ensure priority is highest for the last line too so that we don't
102
// have an incomplete code block during trimming:
103
return <>
104
{lines.map((line, i) => <TextChunk priority={i === lines.length - 1 ? lines.length : lines.length - i}>
105
{i === 0 && reference}{line}{i === lines.length - 1 ? '' : '\n'}
106
</TextChunk>)}
107
</>;
108
}
109
110
return <TextChunk>{reference}{code}</TextChunk>;
111
}
112
}
113
114
export type ExampleCodeBlockProps = PromptElementProps<{
115
readonly examplePath?: string;
116
readonly isSummarized?: boolean;
117
readonly includeFilepath?: boolean;
118
readonly range?: vscode.Range;
119
readonly code: string;
120
readonly languageId?: string;
121
readonly shouldTrim?: boolean;
122
readonly minNumberOfBackticks?: number;
123
}>;
124
125
126
export class ExampleCodeBlock extends SafePromptElement<ExampleCodeBlockProps> {
127
constructor(props: ExampleCodeBlockProps,
128
@IVSCodeExtensionContext _contextService: IVSCodeExtensionContext,
129
@ITelemetryService _telemetryService: ITelemetryService,
130
@ILogService _logService: ILogService,
131
@IIgnoreService _ignoreService: IIgnoreService,
132
@IPromptPathRepresentationService private readonly _promptPathRepresentationService: IPromptPathRepresentationService,
133
) {
134
super(props, _contextService, _telemetryService, _logService, _ignoreService);
135
}
136
137
async render() {
138
const filePath = this.props.includeFilepath ? this._promptPathRepresentationService.getExampleFilePath(this.props.examplePath ?? '/path/to/file') : undefined;
139
const code = createFencedCodeBlock(this.props.languageId ?? '', this.props.code, this.props.shouldTrim ?? true, filePath, this.props.minNumberOfBackticks);
140
return <TextChunk>{code}</TextChunk>;
141
}
142
}
143
144
145
// --- SafeUri
146
147
export const enum UriMode {
148
Basename,
149
Path
150
}
151
152
export type UriProps = PromptElementProps<{
153
value: vscode.Uri;
154
mode?: UriMode;
155
}>;
156
157
export class Uri extends SafePromptElement<UriProps> {
158
159
override get insertLineBreakBefore(): boolean {
160
return false;
161
}
162
163
async render() {
164
const isIgnored = await this._ignoreService.isCopilotIgnored(this.props.value);
165
if (isIgnored) {
166
return this._handleFoulPrompt();
167
}
168
169
let value: string;
170
if (this.props.mode === UriMode.Path) {
171
value = this.props.value.path;
172
} else {
173
value = basename(this.props.value);
174
}
175
return <>{value}</>;
176
}
177
}
178
179