Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/endpoint/node/responsesApiDebugDump.ts
13401 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 fs from 'fs';
7
import OpenAI from 'openai';
8
import * as path from 'path';
9
import { ILogService } from '../../log/common/logService';
10
11
/**
12
* Set to `true` to dump every SSE event from the Responses API to a
13
* timestamped log file under `<repo-root>/.responses-stream-dumps/`.
14
* Useful for debugging phased output, commentary/final_answer
15
* concatenation, and stream ordering.
16
*
17
* **Do not commit with this set to `true`.**
18
*/
19
const ENABLE_RESPONSES_STREAM_DUMP = false
20
// || Boolean("true") // this's done this way to easily uncomment but also to not let you commit it due to internationalized string doublequote use
21
;
22
23
export interface IResponsesStreamDumper {
24
/** Append a single SSE event to the dump file. */
25
logEvent(responseStreamEvent: OpenAI.Responses.ResponseStreamEvent): void;
26
}
27
28
const noopDumper: IResponsesStreamDumper = {
29
logEvent() { /* noop */ }
30
};
31
32
class ResponsesStreamDumper implements IResponsesStreamDumper {
33
constructor(private readonly filePath: string) { }
34
35
logEvent(responseStreamEvent: OpenAI.Responses.ResponseStreamEvent): void {
36
const timestamp = new Date();
37
try {
38
const prettyData = JSON.stringify({ ...responseStreamEvent, type: undefined }, null, 2);
39
fs.appendFileSync(this.filePath, `${timestamp.toISOString()} ${responseStreamEvent.type}\n${prettyData}\n\n`);
40
} catch {
41
// Swallow write errors so debugging never breaks real functionality.
42
}
43
}
44
}
45
46
/**
47
* Creates a dumper for the given request. When {@link ENABLE_RESPONSES_STREAM_DUMP}
48
* is `false` this returns a no-op implementation with zero overhead.
49
*/
50
export function createResponsesStreamDumper(requestId: string, logService: ILogService): IResponsesStreamDumper {
51
if (!ENABLE_RESPONSES_STREAM_DUMP) {
52
return noopDumper;
53
}
54
55
try {
56
// At runtime this file lives in `extensions/copilot/dist/`; go up
57
// three levels to land at the repo root, then write into a
58
// dedicated (gitignored) subfolder so dumps don't pollute `dist/`.
59
const repoRoot = path.resolve(__dirname, '..', '..', '..');
60
const dumpDir = path.join(repoRoot, '.responses-stream-dumps');
61
fs.mkdirSync(dumpDir, { recursive: true });
62
const ts = new Date().toISOString().replace(/[:.]/g, '-');
63
const filePath = path.join(dumpDir, `responses-stream-${ts}-${requestId.slice(0, 4)}.log`);
64
fs.writeFileSync(filePath, `# Responses API SSE stream dump\n# requestId=${requestId}\n# started=${new Date().toISOString()}\n\n`);
65
logService.info(`[responsesAPI] Dumping SSE stream to ${filePath}`);
66
return new ResponsesStreamDumper(filePath);
67
} catch {
68
return noopDumper;
69
}
70
}
71
72