Support rendering function calls and responses when sharing chat to markdown (#8693)

Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
This commit is contained in:
Rohit Ramkumar
2025-09-19 17:47:20 -04:00
committed by GitHub
parent f028913b0e
commit 5be2a9d5e2
2 changed files with 150 additions and 17 deletions

View File

@@ -465,11 +465,27 @@ describe('chatCommand', () => {
const expectedPath = path.join(process.cwd(), 'my-chat.md');
const [actualPath, actualContent] = mockFs.writeFile.mock.calls[0];
expect(actualPath).toEqual(expectedPath);
const expectedContent =
'**user**:\n\ncontext\n\n---\n\n' +
'**model**:\n\ncontext response\n\n---\n\n' +
'**user**:\n\nHello\n\n---\n\n' +
'**model**:\n\nHi there!';
const expectedContent = `🧑‍💻 ## USER
context
---
✨ ## MODEL
context response
---
🧑‍💻 ## USER
Hello
---
✨ ## MODEL
Hi there!`;
expect(actualContent).toEqual(expectedContent);
expect(result).toEqual({
type: 'message',
@@ -540,13 +556,14 @@ describe('chatCommand', () => {
entries.forEach((entry, index) => {
const { role, parts } = mockHistory[index];
const text = parts.map((p) => p.text).join('');
expect(entry).toBe(`**${role}**:\n\n${text}`);
const roleIcon = role === 'user' ? '🧑‍💻' : '✨';
expect(entry).toBe(`${roleIcon} ## ${role.toUpperCase()}\n\n${text}`);
});
});
});
describe('serializeHistoryToMarkdown', () => {
it('should correctly serialize chat history to Markdown', () => {
it('should correctly serialize chat history to Markdown with icons', () => {
const history: Content[] = [
{ role: 'user', parts: [{ text: 'Hello' }] },
{ role: 'model', parts: [{ text: 'Hi there!' }] },
@@ -554,9 +571,9 @@ describe('chatCommand', () => {
];
const expectedMarkdown =
'**user**:\n\nHello\n\n---\n\n' +
'**model**:\n\nHi there!\n\n---\n\n' +
'**user**:\n\nHow are you?';
'🧑‍💻 ## USER\n\nHello\n\n---\n\n' +
'✨ ## MODEL\n\nHi there!\n\n---\n\n' +
'🧑‍💻 ## USER\n\nHow are you?';
const result = serializeHistoryToMarkdown(history);
expect(result).toBe(expectedMarkdown);
@@ -575,13 +592,110 @@ describe('chatCommand', () => {
{ role: 'user', parts: [{ text: 'How are you?' }] },
];
const expectedMarkdown =
'**user**:\n\nHello\n\n---\n\n' +
'**model**:\n\n\n\n---\n\n' +
'**user**:\n\nHow are you?';
const expectedMarkdown = `🧑‍💻 ## USER
Hello
---
✨ ## MODEL
---
🧑‍💻 ## USER
How are you?`;
const result = serializeHistoryToMarkdown(history);
expect(result).toBe(expectedMarkdown);
});
it('should correctly serialize function calls and responses', () => {
const history: Content[] = [
{
role: 'user',
parts: [{ text: 'Please call a function.' }],
},
{
role: 'model',
parts: [
{
functionCall: {
name: 'my-function',
args: { arg1: 'value1' },
},
},
],
},
{
role: 'user',
parts: [
{
functionResponse: {
name: 'my-function',
response: { result: 'success' },
},
},
],
},
];
const expectedMarkdown = `🧑‍💻 ## USER
Please call a function.
---
✨ ## MODEL
**Tool Command**:
\`\`\`json
{
"name": "my-function",
"args": {
"arg1": "value1"
}
}
\`\`\`
---
🧑‍💻 ## USER
**Tool Response**:
\`\`\`json
{
"name": "my-function",
"response": {
"result": "success"
}
}
\`\`\``;
const result = serializeHistoryToMarkdown(history);
expect(result).toBe(expectedMarkdown);
});
it('should handle items with undefined role', () => {
const history: Array<Partial<Content>> = [
{ role: 'user', parts: [{ text: 'Hello' }] },
{ parts: [{ text: 'Hi there!' }] },
];
const expectedMarkdown = `🧑‍💻 ## USER
Hello
---
✨ ## MODEL
Hi there!`;
const result = serializeHistoryToMarkdown(history as Content[]);
expect(result).toBe(expectedMarkdown);
});
});
});

View File

@@ -280,10 +280,29 @@ export function serializeHistoryToMarkdown(history: Content[]): string {
.map((item) => {
const text =
item.parts
?.filter((m) => !!m.text)
.map((m) => m.text)
?.map((part) => {
if (part.text) {
return part.text;
}
if (part.functionCall) {
return `**Tool Command**:\n\`\`\`json\n${JSON.stringify(
part.functionCall,
null,
2,
)}\n\`\`\``;
}
if (part.functionResponse) {
return `**Tool Response**:\n\`\`\`json\n${JSON.stringify(
part.functionResponse,
null,
2,
)}\n\`\`\``;
}
return '';
})
.join('') || '';
return `**${item.role}**:\n\n${text}`;
const roleIcon = item.role === 'user' ? '🧑‍💻' : '✨';
return `${roleIcon} ## ${(item.role || 'model').toUpperCase()}\n\n${text}`;
})
.join('\n\n---\n\n');
}