Path: blob/main/extensions/copilot/src/platform/log/common/messageStringify.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 { Raw } from '@vscode/prompt-tsx';6import { mapFindFirst } from '../../../util/vs/base/common/arraysFind';7import { roleToString } from '../../chat/common/globalStringUtils';8import { rawPartAsStatefulMarker } from '../../endpoint/common/statefulMarkerContainer';9import { rawPartAsThinkingData } from '../../endpoint/common/thinkingDataContainer';1011export function messageToMarkdown(message: Raw.ChatMessage, ignoreStatefulMarker?: boolean, skipFencing?: boolean): string {12const role = roleToString(message.role);13const capitalizedRole = role.charAt(0).toUpperCase() + role.slice(1);14let str = skipFencing ? `### ${capitalizedRole}\n` : `### ${capitalizedRole}\n~~~md\n`;15if (message.role === Raw.ChatRole.Tool) {16str += `🛠️ ${message.toolCallId}`;17if (message.content) {18str += '\n';19}20}2122if (Array.isArray(message.content)) {23str += message.content.map(item => {24if (item.type === Raw.ChatCompletionContentPartKind.Text) {25return item.text;26} else if (item.type === Raw.ChatCompletionContentPartKind.Image) {27return JSON.stringify(item);28} else if (item.type === Raw.ChatCompletionContentPartKind.Opaque) {29const asThinking = rawPartAsThinkingData(item);30if (asThinking) {31const parts: string[] = [];32if (asThinking.text) {33const thinkingText = Array.isArray(asThinking.text) ? asThinking.text.join('\n') : asThinking.text;34parts.push(`reasoning: ${thinkingText}`);35}36if (asThinking.encrypted?.length) {37parts.push(`encrypted_content=${asThinking.encrypted.length} chars`);38}3940if (parts.length) {41return parts.join('\n');42}43}44}45}).join('\n');46} else {47str += message.content;48}4950if (message.role === Raw.ChatRole.Assistant && message.toolCalls?.length) {51if (message.content) {52str += '\n';53}54str += message.toolCalls.map(c => {55let argsStr = c.function.arguments;56try {57const parsedArgs = JSON.parse(c.function.arguments);58argsStr = JSON.stringify(parsedArgs, undefined, 2)59.replace(/(?<!\\)\\n/g, '\n')60.replace(/(?<!\\)\\t/g, '\t');61} catch (e) { }62return `🛠️ ${c.function.name} (${c.id}) ${argsStr}`;63}).join('\n');64}6566if (message.content.some(part => part.type === Raw.ChatCompletionContentPartKind.CacheBreakpoint)) {67str += `\n[copilot_cache_control: { type: 'ephemeral' }]`;68}6970const statefulMarker = mapFindFirst(message.content, c => c.type === Raw.ChatCompletionContentPartKind.Opaque ? rawPartAsStatefulMarker(c) : undefined);71if (statefulMarker && !ignoreStatefulMarker) {72str += `\n[response_id: ${statefulMarker.marker} with ${statefulMarker.modelId}]`;73}7475if (!skipFencing) {76str += '\n~~~\n';77} else {78str += '\n';79}8081return str;82}838485