Skip to content

Commit 54b70a6

Browse files
committed
include tool calls
1 parent e1a4ecb commit 54b70a6

2 files changed

Lines changed: 121 additions & 12 deletions

File tree

packages/core/src/services/sessionSummaryService.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { SessionSummaryService } from './sessionSummaryService.js';
99
import type { BaseLlmClient } from '../core/baseLlmClient.js';
1010
import type { MessageRecord } from './chatRecordingService.js';
1111
import type { GenerateContentResponse } from '@google/genai';
12+
import { CoreToolCallStatus } from '../scheduler/types.js';
1213

1314
describe('SessionSummaryService', () => {
1415
let service: SessionSummaryService;
@@ -1215,4 +1216,58 @@ Debugged intermittent 401 errors caused by concurrent token refresh calls.
12151216
expect(result).not.toBeNull();
12161217
expect(result!.summary).toBe('Fix the bug');
12171218
});
1219+
1220+
it('should include recorded tool calls when gemini content is empty', async () => {
1221+
const messages: MessageRecord[] = [
1222+
{
1223+
id: '1',
1224+
timestamp: '2026-01-01T00:00:00Z',
1225+
type: 'user',
1226+
content: [{ text: 'Figure out why session memory is missing context' }],
1227+
},
1228+
{
1229+
id: '2',
1230+
timestamp: '2026-01-01T00:01:00Z',
1231+
type: 'gemini',
1232+
content: [{ text: '' }],
1233+
toolCalls: [
1234+
{
1235+
id: 'tool-1',
1236+
name: 'run_shell_command',
1237+
args: {
1238+
command:
1239+
'cat packages/core/src/services/sessionSummaryService.ts',
1240+
},
1241+
result: [
1242+
{
1243+
functionResponse: {
1244+
id: 'tool-1',
1245+
name: 'run_shell_command',
1246+
response: {
1247+
output:
1248+
'packages/core/src/services/sessionSummaryService.ts',
1249+
},
1250+
},
1251+
},
1252+
],
1253+
status: CoreToolCallStatus.Success,
1254+
timestamp: '2026-01-01T00:01:05Z',
1255+
},
1256+
],
1257+
},
1258+
];
1259+
1260+
await service.generateMemoryExtraction({ messages });
1261+
1262+
const callArgs = mockGenerateContent.mock.calls[0][0];
1263+
const promptText = callArgs.contents[0].parts[0].text as string;
1264+
1265+
expect(promptText).toContain('Tool: run_shell_command');
1266+
expect(promptText).toContain(
1267+
'"command":"cat packages/core/src/services/sessionSummaryService.ts"',
1268+
);
1269+
expect(promptText).toContain(
1270+
'packages/core/src/services/sessionSummaryService.ts',
1271+
);
1272+
});
12181273
});

packages/core/src/services/sessionSummaryService.ts

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import type { MessageRecord } from './chatRecordingService.js';
7+
import type { MessageRecord, ToolCallRecord } from './chatRecordingService.js';
88
import type { BaseLlmClient } from '../core/baseLlmClient.js';
99
import { partListUnionToString } from '../core/geminiRequest.js';
1010
import { debugLogger } from '../utils/debugLogger.js';
1111
import type { Content } from '@google/genai';
1212
import { getResponseText } from '../utils/partUtils.js';
1313
import { LlmRole } from '../telemetry/types.js';
14+
import { safeJsonStringify } from '../utils/safeJsonStringify.js';
1415

1516
const DEFAULT_MAX_MESSAGES = 20;
1617
const DEFAULT_TIMEOUT_MS = 5000;
@@ -280,15 +281,15 @@ export class SessionSummaryService {
280281
maxMessages: number,
281282
maxMessageLength: number,
282283
): string | null {
283-
const filteredMessages = messages.filter((msg) => {
284-
if (msg.type !== 'user' && msg.type !== 'gemini') {
285-
return false;
286-
}
287-
const content = partListUnionToString(msg.content);
288-
return content.trim().length > 0;
289-
});
290-
291-
let relevantMessages: MessageRecord[];
284+
const filteredMessages = messages
285+
.filter((msg) => msg.type === 'user' || msg.type === 'gemini')
286+
.map((msg) => ({
287+
msg,
288+
content: this.formatMessageForConversation(msg),
289+
}))
290+
.filter(({ content }) => content.length > 0);
291+
292+
let relevantMessages: typeof filteredMessages;
292293
if (filteredMessages.length <= maxMessages) {
293294
relevantMessages = filteredMessages;
294295
} else {
@@ -304,9 +305,8 @@ export class SessionSummaryService {
304305
}
305306

306307
return relevantMessages
307-
.map((msg) => {
308+
.map(({ msg, content }) => {
308309
const role = msg.type === 'user' ? 'User' : 'Assistant';
309-
const content = partListUnionToString(msg.content);
310310
const characters = Array.from(content);
311311
const truncated =
312312
characters.length > maxMessageLength
@@ -316,4 +316,58 @@ export class SessionSummaryService {
316316
})
317317
.join('\n\n');
318318
}
319+
320+
private formatMessageForConversation(msg: MessageRecord): string {
321+
const sections: string[] = [];
322+
const content = partListUnionToString(msg.content).trim();
323+
324+
if (content) {
325+
sections.push(content);
326+
}
327+
328+
if (msg.type === 'gemini' && msg.toolCalls?.length) {
329+
sections.push(
330+
...msg.toolCalls.map((toolCall) =>
331+
this.formatToolCallForConversation(toolCall),
332+
),
333+
);
334+
}
335+
336+
return sections.join('\n');
337+
}
338+
339+
private formatToolCallForConversation(toolCall: ToolCallRecord): string {
340+
const lines = [`Tool: ${toolCall.name}`, `Status: ${toolCall.status}`];
341+
342+
if (Object.keys(toolCall.args).length > 0) {
343+
lines.push(`Args: ${this.stringifyForConversation(toolCall.args)}`);
344+
}
345+
346+
const result = this.stringifyToolCallResult(toolCall);
347+
if (result) {
348+
lines.push(`Result: ${result}`);
349+
}
350+
351+
return lines.join('\n');
352+
}
353+
354+
private stringifyToolCallResult(toolCall: ToolCallRecord): string {
355+
if (!toolCall.result) {
356+
return '';
357+
}
358+
359+
return this.stringifyForConversation(toolCall.result);
360+
}
361+
362+
private stringifyForConversation(value: unknown): string {
363+
if (value === undefined || value === null) {
364+
return '';
365+
}
366+
367+
if (typeof value === 'string') {
368+
return value;
369+
}
370+
371+
return safeJsonStringify(value);
372+
}
319373
}

0 commit comments

Comments
 (0)