approximateMessageBreakdown method

MessageBreakdown approximateMessageBreakdown(
  1. List<Map<String, dynamic>> messages
)

Approximate token usage by message type.

Implementation

MessageBreakdown approximateMessageBreakdown(
  List<Map<String, dynamic>> messages,
) {
  final breakdown = _MutableMessageBreakdown();
  final toolUseIdToName = <String, String>{};

  // Build tool_use_id to name map
  for (final msg in messages) {
    if (msg['type'] == 'assistant') {
      final content = msg['content'] as List<dynamic>? ?? [];
      for (final block in content) {
        if (block is Map && block['type'] == 'tool_use') {
          final id = block['id'] as String?;
          final name = block['name'] as String? ?? 'unknown';
          if (id != null) toolUseIdToName[id] = name;
        }
      }
    }
  }

  for (final msg in messages) {
    final type = msg['type'] as String?;

    if (type == 'assistant') {
      final content = msg['content'] as List<dynamic>? ?? [];
      for (final block in content) {
        if (block is Map) {
          final blockStr = block.toString();
          final blockTokens = _roughTokenCountEstimation(blockStr);
          if (block['type'] == 'tool_use') {
            breakdown.toolCallTokens += blockTokens;
            final toolName = block['name'] as String? ?? 'unknown';
            breakdown.toolCallsByType[toolName] =
                (breakdown.toolCallsByType[toolName] ?? 0) + blockTokens;
          } else {
            breakdown.assistantMessageTokens += blockTokens;
          }
        }
      }
    } else if (type == 'user') {
      final content = msg['content'];
      if (content is String) {
        breakdown.userMessageTokens += _roughTokenCountEstimation(content);
      } else if (content is List) {
        for (final block in content) {
          if (block is Map) {
            final blockStr = block.toString();
            final blockTokens = _roughTokenCountEstimation(blockStr);
            if (block['type'] == 'tool_result') {
              breakdown.toolResultTokens += blockTokens;
              final toolUseId = block['tool_use_id'] as String?;
              final toolName =
                  (toolUseId != null ? toolUseIdToName[toolUseId] : null) ??
                  'unknown';
              breakdown.toolResultsByType[toolName] =
                  (breakdown.toolResultsByType[toolName] ?? 0) + blockTokens;
            } else {
              breakdown.userMessageTokens += blockTokens;
            }
          }
        }
      }
    } else if (type == 'attachment') {
      final attachment = msg['attachment'] as Map<String, dynamic>?;
      if (attachment != null) {
        final tokens = _roughTokenCountEstimation(attachment.toString());
        breakdown.attachmentTokens += tokens;
        final attachType = attachment['type'] as String? ?? 'unknown';
        breakdown.attachmentsByType[attachType] =
            (breakdown.attachmentsByType[attachType] ?? 0) + tokens;
      }
    }
  }

  breakdown.totalTokens =
      breakdown.toolCallTokens +
      breakdown.toolResultTokens +
      breakdown.attachmentTokens +
      breakdown.assistantMessageTokens +
      breakdown.userMessageTokens;

  // Convert to sorted lists
  final toolCallsByType =
      breakdown.toolCallsByType.entries
          .map(
            (e) => ToolCallsByType(
              name: e.key,
              callTokens: e.value,
              resultTokens: breakdown.toolResultsByType[e.key] ?? 0,
            ),
          )
          .toList()
        ..sort(
          (a, b) =>
              (b.callTokens + b.resultTokens) -
              (a.callTokens + a.resultTokens),
        );

  final attachmentsByType =
      breakdown.attachmentsByType.entries
          .map((e) => AttachmentsByType(name: e.key, tokens: e.value))
          .toList()
        ..sort((a, b) => b.tokens - a.tokens);

  return MessageBreakdown(
    toolCallTokens: breakdown.toolCallTokens,
    toolResultTokens: breakdown.toolResultTokens,
    attachmentTokens: breakdown.attachmentTokens,
    assistantMessageTokens: breakdown.assistantMessageTokens,
    userMessageTokens: breakdown.userMessageTokens,
    toolCallsByType: toolCallsByType,
    attachmentsByType: attachmentsByType,
  );
}