feat(export): add metadata and statistics to export data

- Add ExportMetadata type with session info, token stats, file operation stats
- Track response_id from LLM API for telemetry correlation
- Collect usageMetadata from assistant messages
- Calculate file stats (files read/written, lines added/removed)
- Calculate token stats (total tokens, context usage percentage)
- Add metadata sidebar to HTML export template
- Support metadata in JSONL and Markdown formatters
- Update chatRecordingService to record response_id

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
mingholy.lmh 2026-03-12 21:37:05 +08:00
parent 27356c1bac
commit d59e668729
11 changed files with 776 additions and 31 deletions

View file

@ -28,6 +28,14 @@ export function normalizeSessionData(
}
});
// Build index of assistant messages by uuid for response_id mapping
const assistantMessageIndexByUuid = new Map<string, number>();
normalized.forEach((message, index) => {
if (message.type === 'assistant') {
assistantMessageIndexByUuid.set(message.uuid, index);
}
});
// Merge tool result information into tool call messages
for (const record of originalRecords) {
if (record.type !== 'tool_result') continue;
@ -58,6 +66,31 @@ export function normalizeSessionData(
mergeToolCallData(existingMessage.toolCall, toolCallMessage.toolCall);
}
// Merge response_id from assistant records
for (const record of originalRecords) {
if (record.type !== 'assistant') continue;
if (!record.response_id) continue;
const existingIndex = assistantMessageIndexByUuid.get(record.uuid);
if (existingIndex !== undefined) {
normalized[existingIndex].response_id = record.response_id;
}
}
// Merge usageMetadata from assistant records
for (const record of originalRecords) {
if (record.type !== 'assistant') continue;
if (!record.usageMetadata) continue;
const existingIndex = assistantMessageIndexByUuid.get(record.uuid);
if (existingIndex !== undefined) {
// Only set if not already present from collect phase
if (!normalized[existingIndex].usageMetadata) {
normalized[existingIndex].usageMetadata = record.usageMetadata;
}
}
}
return {
...sessionData,
messages: normalized,