Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/script/alternativeAction/index.ts
13389 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 csvParse from 'csv-parse';
7
import * as fs from 'fs/promises';
8
import minimist from 'minimist';
9
import { IAlternativeAction } from '../../src/extension/inlineEdits/node/nextEditProviderTelemetry';
10
import { coalesce } from '../../src/util/vs/base/common/arrays';
11
import { Processor } from '../../test/pipeline/alternativeAction/processor';
12
import { IData, Scoring } from '../../test/pipeline/alternativeAction/types';
13
import { Either, log } from '../../test/pipeline/alternativeAction/util';
14
15
async function extractFromCsv(csvContents: string): Promise<(Scoring.t | undefined)[]> {
16
const options = {
17
columns: true as const, // Use first row as column headers
18
delimiter: ',', // Comma delimiter
19
quote: '"', // Double quotes
20
escape: '"', // Standard CSV escape character
21
skip_empty_lines: true, // Skip any empty rows
22
trim: true, // Remove whitespace around fields
23
relax_quotes: true, // Handle quotes within fields more flexibly
24
bom: true, // Handle UTF-8 BOM
25
cast: false // Keep all values as strings initially
26
} as const;
27
28
type CsvRecord = { Data: string };
29
30
const objects = (await new Promise<CsvRecord[]>((resolve, reject) =>
31
csvParse.parse<CsvRecord>(csvContents, options, (err, result) => {
32
if (err) {
33
reject(err);
34
} else {
35
if (result.every((item: any) => typeof item === 'object' && 'Data' in item && typeof item['Data'] === 'string')) {
36
resolve(result);
37
} else {
38
reject(new Error('Invalid CSV format'));
39
}
40
}
41
})
42
)).map(record => JSON.parse(record.Data) as IData);
43
44
const scoredEdits = objects.map((obj: IData) => {
45
const altAction: IAlternativeAction = obj.altAction;
46
if (!altAction || !altAction.recording) {
47
return undefined;
48
}
49
return Processor.createScoringForAlternativeAction(altAction, coalesce([parseSuggestedEdit(obj.postProcessingOutcome.suggestedEdit)]), false);
50
});
51
52
return scoredEdits;
53
}
54
55
function writeFiles(basename: string, scoring: Scoring.t) {
56
return [
57
fs.writeFile(`${basename}.scoredEdits.w.json`, JSON.stringify(scoring, null, 2)),
58
fs.writeFile(`${basename}.recording.w.json`, JSON.stringify(scoring.scoringContext.recording, null, 2)),
59
];
60
}
61
62
async function handleCsv(inputFilePath: string) {
63
log('Handling CSV file:', inputFilePath);
64
const csvContents = await fs.readFile(inputFilePath, 'utf8');
65
log('CSV contents read, length:', csvContents.length);
66
const extracted = await extractFromCsv(csvContents);
67
log('Extraction complete, number of scored edits:', extracted.filter(e => e).length);
68
try {
69
await Promise.all(extracted.flatMap((obj: Scoring.t | undefined, idx: number) => {
70
if (!obj) {
71
return [];
72
}
73
return writeFiles(idx.toString(), obj);
74
}));
75
log('All files written successfully');
76
} catch (e) {
77
log('Error writing files:', e);
78
}
79
}
80
81
function parseFile(fileContents: string): Either<IData, IAlternativeAction> | undefined {
82
let parsedObj: unknown;
83
try {
84
parsedObj = JSON.parse(fileContents);
85
} catch (e) {
86
console.error('Failed to parse JSON:', e);
87
return undefined;
88
}
89
if (parsedObj && typeof parsedObj === 'object' && 'prompt' in parsedObj) {
90
return Either.left(parsedObj as IData);
91
}
92
93
return Either.right(parsedObj as IAlternativeAction);
94
}
95
96
async function handleAlternativeActionJson(inputFilePath: string) {
97
log('Handling alternative action JSON file:', inputFilePath);
98
const fileContents = await fs.readFile(inputFilePath, 'utf8');
99
log('File contents read, length:', fileContents.length);
100
const obj = parseFile(fileContents);
101
if (!obj) {
102
console.error('Failed to parse alternative action JSON file');
103
return;
104
}
105
const altAction = obj.isLeft() ? obj.value.altAction : obj.value;
106
const edits: [start: number, endEx: number, text: string][] = [];
107
let isAccepted = false;
108
if (obj.isLeft()) {
109
const data = obj.value;
110
const parsedEdit = parseSuggestedEdit(data.postProcessingOutcome.suggestedEdit);
111
if (parsedEdit) {
112
edits.push(parsedEdit);
113
}
114
isAccepted = data.suggestionStatus === 'accepted';
115
}
116
const scoring = Processor.createScoringForAlternativeAction(altAction, edits, isAccepted);
117
if (!scoring) {
118
console.error('Failed to create scoring from alternative action');
119
return;
120
}
121
const outputFilePath = inputFilePath.replace(/\.json$/, '.scoredEdits.json');
122
await Promise.all(writeFiles(outputFilePath.replace(/\.scoredEdits\.json$/, ''), scoring));
123
log('Scoring written to:', outputFilePath);
124
}
125
126
function parseSuggestedEdit(suggestedEditStr: string): [number, number, string] | null {
127
const [stringifiedRange, quotedText] = suggestedEditStr.split(' -> ');
128
const match = stringifiedRange.match(/^\[(\d+), (\d+)\)$/);
129
if (match) {
130
const start = parseInt(match[1], 10);
131
const endEx = parseInt(match[2], 10);
132
const text = quotedText.slice(1, -1); // Remove surrounding quotes
133
return [start, endEx, text];
134
}
135
return null;
136
}
137
138
async function main() {
139
const argv = minimist(process.argv.slice(2), {
140
alias: {
141
p: 'path',
142
s: 'single',
143
c: 'csv'
144
},
145
boolean: ['single', 'csv'],
146
string: ['path']
147
});
148
149
if (!argv.path) {
150
console.error('Please provide a path to an alternative action JSON file using --path or -p');
151
process.exit(1);
152
}
153
154
const inputFilePath = argv.path;
155
156
if (argv.csv) {
157
await handleCsv(inputFilePath);
158
return;
159
}
160
161
await handleAlternativeActionJson(inputFilePath);
162
return;
163
}
164
165
main();
166
167