fix(conversation): track token usage and message count in conversation entity

Problem:
- Token usage was recorded to token_usage table but not to conversation entity
- Message count was not being incremented
- Dashboard showed 0 tokens for all conversations

Solution:
- Add inputTokens/outputTokens fields to StreamChunk interface
- Return token usage in 'end' chunk from ClaudeAgentServiceV2
- Capture token usage in conversation.service.ts sendMessage
- Call conversation.addTokens() and incrementMessageCount() after each exchange
- Consolidate conversation updates into single repo.update() call

Files changed:
- claude-agent-v2.service.ts: Add token fields to StreamChunk, return in 'end'
- conversation.service.ts: Track tokens and message counts properly

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-25 17:08:23 -08:00
parent 931055b51f
commit ae99b78579
2 changed files with 21 additions and 5 deletions

View File

@ -158,6 +158,8 @@ export class ConversationService {
let fullResponse = '';
const toolCalls: Array<{ name: string; input: unknown; result: unknown }> = [];
let updatedState: ConversationContext['consultingState'] | undefined;
let inputTokens = 0;
let outputTokens = 0;
// Stream response from Claude (with attachments for multimodal support)
for await (const chunk of this.claudeAgentService.sendMessage(
@ -181,6 +183,10 @@ export class ConversationService {
} else if (chunk.type === 'state_update' && chunk.newState) {
// V2: Capture updated consulting state
updatedState = chunk.newState;
} else if (chunk.type === 'end') {
// Capture token usage from end chunk
inputTokens = chunk.inputTokens || 0;
outputTokens = chunk.outputTokens || 0;
}
yield chunk;
@ -191,7 +197,6 @@ export class ConversationService {
// Convert state to JSON-compatible format for database storage
const stateForDb = JSON.parse(JSON.stringify(updatedState));
conversation.updateConsultingState(stateForDb);
await this.conversationRepo.update(conversation);
}
// Save assistant response
@ -205,12 +210,20 @@ export class ConversationService {
});
await this.messageRepo.save(assistantMessage);
// Update conversation statistics
conversation.incrementMessageCount('user');
conversation.incrementMessageCount('assistant');
conversation.addTokens(inputTokens, outputTokens);
// Update conversation title if first message
if (conversation.messageCount === 0) {
if (conversation.messageCount === 2) {
// Now 2 after incrementing (user + assistant)
const title = await this.generateTitle(params.content);
conversation.title = title;
await this.conversationRepo.update(conversation);
}
// Save all updates to conversation
await this.conversationRepo.update(conversation);
}
/**

View File

@ -66,6 +66,9 @@ export interface StreamChunk {
// V2新增
stageName?: string;
newState?: ConsultingState;
// Token usage (returned with 'end' chunk)
inputTokens?: number;
outputTokens?: number;
}
@Injectable()
@ -342,7 +345,7 @@ export class ClaudeAgentServiceV2 implements OnModuleInit {
// 7.6 返回更新后的状态
yield { type: 'state_update', newState: state };
yield { type: 'end' };
yield { type: 'end', inputTokens: totalInputTokens, outputTokens: totalOutputTokens };
return;
}
@ -403,7 +406,7 @@ export class ClaudeAgentServiceV2 implements OnModuleInit {
}
console.error('[ClaudeAgentV2] Tool loop exceeded maximum iterations');
yield { type: 'end' };
yield { type: 'end', inputTokens: totalInputTokens, outputTokens: totalOutputTokens };
}
/**