serializeConversation function
Serialize messages to plain text for summarization prompts.
Ported from pi's serializeConversation: [User]:, [Assistant]:,
[Assistant thinking]:, [Assistant tool calls]:, and truncated
[Tool result]: lines, joined by blank lines. Image blocks are skipped.
Implementation
String serializeConversation(List<Message> messages) {
final parts = <String>[];
for (final msg in messages) {
switch (msg) {
case UserMessage(:final content):
final text = content is String
? content
: (content as List<ContentBlock>)
.whereType<TextContent>()
.map((block) => block.text)
.join();
if (text.isNotEmpty) parts.add('[User]: $text');
case AssistantMessage(:final content):
final textParts = <String>[];
final thinkingParts = <String>[];
final toolCalls = <String>[];
for (final block in content) {
switch (block) {
case TextContent(:final text):
textParts.add(text);
case ThinkingContent(:final thinking):
thinkingParts.add(thinking);
case ToolCall(:final name, :final arguments):
final args = arguments.entries
.map(
(entry) => '${entry.key}=${_safeJsonEncode(entry.value)}',
)
.join(', ');
toolCalls.add('$name($args)');
default:
}
}
if (thinkingParts.isNotEmpty) {
parts.add('[Assistant thinking]: ${thinkingParts.join('\n')}');
}
if (textParts.isNotEmpty) {
parts.add('[Assistant]: ${textParts.join('\n')}');
}
if (toolCalls.isNotEmpty) {
parts.add('[Assistant tool calls]: ${toolCalls.join('; ')}');
}
case ToolResultMessage(:final content):
final text = content
.whereType<TextContent>()
.map((block) => block.text)
.join();
if (text.isNotEmpty) {
parts.add(
'[Tool result]: ${_truncateForSummary(text, _toolResultMaxChars)}',
);
}
default:
}
}
return parts.join('\n\n');
}