diff --git a/backend/api/server.py b/backend/api/server.py index b815ee0..67d2751 100644 --- a/backend/api/server.py +++ b/backend/api/server.py @@ -4903,7 +4903,7 @@ def _list_admin_accounts( child_session_count += 1 else: visible_session_count += 1 - row_tool_calls = int(data.get('tool_calls') or 0) + row_tool_calls = _admin_session_tool_call_count(data) tool_calls += row_tool_calls usage = data.get('usage') if isinstance(data.get('usage'), dict) else {} row_input_tokens = int(usage.get('input_tokens') or 0) @@ -5061,6 +5061,49 @@ def _admin_daily_series(buckets: dict[str, dict[str, Any]]) -> list[dict[str, An return [buckets[key] for key in sorted(buckets)] +def _admin_session_tool_call_count(data: dict[str, Any]) -> int: + raw_value = data.get('tool_calls') + raw_count = raw_value if isinstance(raw_value, int) else 0 + if raw_count < 0: + raw_count = 0 + file_history = data.get('file_history') + file_history_count = len(file_history) if isinstance(file_history, list) else 0 + message_count = max( + _admin_count_tool_calls_in_items(data.get('messages')), + _admin_count_tool_calls_in_items(data.get('turns')), + ) + derived_count = max(file_history_count, message_count) + if raw_count == 0 and derived_count: + return derived_count + # Some historical session files contain corrupted cumulative values here. + # Prefer a conservative derived count when the stored number is wildly + # larger than the persisted tool history/message structure can support. + if derived_count and raw_count > max(5000, derived_count * 20 + 100): + return derived_count + return raw_count + + +def _admin_count_tool_calls_in_items(value: Any) -> int: + if not isinstance(value, list): + return 0 + count = 0 + for item in value: + if not isinstance(item, dict): + continue + tool_calls = item.get('tool_calls') + if isinstance(tool_calls, list): + count += len(tool_calls) + content = item.get('content') + if isinstance(content, list): + for block in content: + if not isinstance(block, dict): + continue + block_type = str(block.get('type') or '') + if block_type in {'tool_use', 'tool-call', 'tool_call'}: + count += 1 + return count + + def _merge_admin_daily_series( series_items: Iterable[list[dict[str, Any]]], ) -> list[dict[str, Any]]: