Path: blob/main/extensions/copilot/src/platform/endpoint/node/responsesApiDebugDump.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as fs from 'fs';6import OpenAI from 'openai';7import * as path from 'path';8import { ILogService } from '../../log/common/logService';910/**11* Set to `true` to dump every SSE event from the Responses API to a12* timestamped log file under `<repo-root>/.responses-stream-dumps/`.13* Useful for debugging phased output, commentary/final_answer14* concatenation, and stream ordering.15*16* **Do not commit with this set to `true`.**17*/18const ENABLE_RESPONSES_STREAM_DUMP = false19// || Boolean("true") // this's done this way to easily uncomment but also to not let you commit it due to internationalized string doublequote use20;2122export interface IResponsesStreamDumper {23/** Append a single SSE event to the dump file. */24logEvent(responseStreamEvent: OpenAI.Responses.ResponseStreamEvent): void;25}2627const noopDumper: IResponsesStreamDumper = {28logEvent() { /* noop */ }29};3031class ResponsesStreamDumper implements IResponsesStreamDumper {32constructor(private readonly filePath: string) { }3334logEvent(responseStreamEvent: OpenAI.Responses.ResponseStreamEvent): void {35const timestamp = new Date();36try {37const prettyData = JSON.stringify({ ...responseStreamEvent, type: undefined }, null, 2);38fs.appendFileSync(this.filePath, `${timestamp.toISOString()} ${responseStreamEvent.type}\n${prettyData}\n\n`);39} catch {40// Swallow write errors so debugging never breaks real functionality.41}42}43}4445/**46* Creates a dumper for the given request. When {@link ENABLE_RESPONSES_STREAM_DUMP}47* is `false` this returns a no-op implementation with zero overhead.48*/49export function createResponsesStreamDumper(requestId: string, logService: ILogService): IResponsesStreamDumper {50if (!ENABLE_RESPONSES_STREAM_DUMP) {51return noopDumper;52}5354try {55// At runtime this file lives in `extensions/copilot/dist/`; go up56// three levels to land at the repo root, then write into a57// dedicated (gitignored) subfolder so dumps don't pollute `dist/`.58const repoRoot = path.resolve(__dirname, '..', '..', '..');59const dumpDir = path.join(repoRoot, '.responses-stream-dumps');60fs.mkdirSync(dumpDir, { recursive: true });61const ts = new Date().toISOString().replace(/[:.]/g, '-');62const filePath = path.join(dumpDir, `responses-stream-${ts}-${requestId.slice(0, 4)}.log`);63fs.writeFileSync(filePath, `# Responses API SSE stream dump\n# requestId=${requestId}\n# started=${new Date().toISOString()}\n\n`);64logService.info(`[responsesAPI] Dumping SSE stream to ${filePath}`);65return new ResponsesStreamDumper(filePath);66} catch {67return noopDumper;68}69}707172