Add "showRawRequestBody" command (#1121)

This commit is contained in:
Rob Lourens
2025-09-24 04:05:08 +00:00
committed by GitHub
parent 2420cc2081
commit eb5c529555
4 changed files with 64 additions and 5 deletions
+14 -1
View File
@@ -2160,6 +2160,10 @@
"command": "github.copilot.chat.debug.hideNesRequests",
"title": "Hide NES Requests"
},
{
"command": "github.copilot.chat.debug.showRawRequestBody",
"title": "Show Raw Request Body"
},
{
"command": "github.copilot.chat.debug.exportLogItem",
"title": "Export as...",
@@ -3347,6 +3351,10 @@
{
"command": "github.copilot.chat.mcp.setup.flow",
"when": "false"
},
{
"command": "github.copilot.chat.debug.showRawRequestBody",
"when": "false"
}
],
"view/title": [
@@ -3372,6 +3380,11 @@
}
],
"view/item/context": [
{
"command": "github.copilot.chat.debug.showRawRequestBody",
"when": "view == copilot-chat && viewItem == request",
"group": "export@0"
},
{
"command": "github.copilot.chat.debug.exportLogItem",
"when": "view == copilot-chat && (viewItem == toolcall || viewItem == request)",
@@ -4182,4 +4195,4 @@
"string_decoder": "npm:string_decoder@1.2.0",
"node-gyp": "npm:node-gyp@10.3.1"
}
}
}
@@ -26,6 +26,7 @@ const exportPromptArchiveCommand = 'github.copilot.chat.debug.exportPromptArchiv
const exportPromptLogsAsJsonCommand = 'github.copilot.chat.debug.exportPromptLogsAsJson';
const exportAllPromptLogsAsJsonCommand = 'github.copilot.chat.debug.exportAllPromptLogsAsJson';
const saveCurrentMarkdownCommand = 'github.copilot.chat.debug.saveCurrentMarkdown';
const showRawRequestBodyCommand = 'github.copilot.chat.debug.showRawRequestBody';
export class RequestLogTree extends Disposable implements IExtensionContribution {
readonly id = 'requestLogTree';
@@ -488,6 +489,15 @@ export class RequestLogTree extends Disposable implements IExtensionContribution
vscode.window.showErrorMessage(`Failed to export all prompt logs as JSON: ${error}`);
}
}));
this._register(vscode.commands.registerCommand(showRawRequestBodyCommand, async (arg?: ChatPromptItem) => {
const requestId = arg?.id;
if (!requestId) {
return;
}
await vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(ChatRequestScheme.buildUri({ kind: 'request', id: requestId }, 'rawrequest')));
}));
}
}
@@ -287,6 +287,8 @@ export class RequestLogger extends AbstractRequestLogger {
if (format === 'json') {
return this._renderToJson(entry);
} else if (format === 'rawrequest') {
return this._renderRawRequestToJson(entry);
} else {
// Existing markdown logic
switch (entry.kind) {
@@ -708,4 +710,21 @@ export class RequestLogger extends AbstractRequestLogger {
return result.join('\n');
}
private _renderRawRequestToJson(entry: LoggedInfo): string {
if (entry.kind !== LoggedInfoKind.Request) {
return 'Not available';
}
const req = entry.entry;
if (req.type === LoggedRequestKind.MarkdownContentRequest || !('body' in req.chatParams) || !req.chatParams.body) {
return 'Not available';
}
try {
return JSON.stringify(req.chatParams.body, null, 2);
} catch (e) {
return `Failed to render body: ${e}`;
}
}
}
@@ -29,8 +29,15 @@ export type UriData = { kind: 'request'; id: string } | { kind: 'latest' };
export class ChatRequestScheme {
public static readonly chatRequestScheme = 'ccreq';
public static buildUri(data: UriData, format: 'markdown' | 'json' = 'markdown'): string {
const extension = format === 'json' ? 'json' : 'copilotmd';
public static buildUri(data: UriData, format: 'markdown' | 'json' | 'rawrequest' = 'markdown'): string {
let extension: string;
if (format === 'markdown') {
extension = 'copilotmd';
} else if (format === 'json') {
extension = 'json';
} else { // rawrequest
extension = 'request.json';
}
if (data.kind === 'latest') {
return `${ChatRequestScheme.chatRequestScheme}:latest.${extension}`;
} else {
@@ -38,7 +45,7 @@ export class ChatRequestScheme {
}
}
public static parseUri(uri: string): { data: UriData; format: 'markdown' | 'json' } | undefined {
public static parseUri(uri: string): { data: UriData; format: 'markdown' | 'json' | 'rawrequest' } | undefined {
// Check for latest markdown
if (uri === this.buildUri({ kind: 'latest' }, 'markdown')) {
return { data: { kind: 'latest' }, format: 'markdown' };
@@ -47,6 +54,10 @@ export class ChatRequestScheme {
if (uri === this.buildUri({ kind: 'latest' }, 'json')) {
return { data: { kind: 'latest' }, format: 'json' };
}
// Check for latest rawrequest
if (uri === this.buildUri({ kind: 'latest' }, 'rawrequest')) {
return { data: { kind: 'latest' }, format: 'rawrequest' };
}
// Check for specific request markdown
const mdMatch = uri.match(/ccreq:([^\s]+)\.copilotmd/);
@@ -54,6 +65,12 @@ export class ChatRequestScheme {
return { data: { kind: 'request', id: mdMatch[1] }, format: 'markdown' };
}
// specific raw body json
const bodyJsonMatch = uri.match(/ccreq:([^\s]+)\.request\.json/);
if (bodyJsonMatch) {
return { data: { kind: 'request', id: bodyJsonMatch[1] }, format: 'rawrequest' };
}
// Check for specific request JSON
const jsonMatch = uri.match(/ccreq:([^\s]+)\.json/);
if (jsonMatch) {
@@ -64,7 +81,7 @@ export class ChatRequestScheme {
}
public static findAllUris(text: string): { uri: string; range: OffsetRange }[] {
const linkRE = /(ccreq:[^\s]+\.(copilotmd|json))/g;
const linkRE = /(ccreq:[^\s]+\.(copilotmd|json|request\.json))/g;
return [...text.matchAll(linkRE)].map(
(m) => {
const identifier = m[1];