Fix: Add method to merge conversation stats (#10667)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Rohit Malhotra
2025-08-29 16:15:44 -04:00
committed by GitHub
parent 6a544d4274
commit 6eb32e9ae4
2 changed files with 168 additions and 0 deletions

View File

@@ -101,3 +101,60 @@ class ConversationStats:
del self.restored_metrics[service_id]
self.service_to_metrics[service_id] = llm.metrics
def merge_and_save(self, conversation_stats: 'ConversationStats'):
"""
Merge restored metrics from another ConversationStats into this one.
Important:
- This method is intended to be used immediately after restoring metrics from
storage, before any LLM services are registered. In that state, only
`restored_metrics` should contain entries and `service_to_metrics` should
be empty. If either side has entries in `service_to_metrics`, we log an
error but continue execution.
Behavior:
- Drop entries with zero accumulated_cost from both `restored_metrics` dicts
(self and incoming) before merging.
- Merge only `restored_metrics`. For duplicate keys, the incoming
`conversation_stats.restored_metrics` overwrites existing entries.
- Do NOT merge `service_to_metrics` here.
- Persist results by calling save_metrics().
"""
# If either side has active service metrics, log an error but proceed
if self.service_to_metrics or conversation_stats.service_to_metrics:
logger.error(
'merge_and_save should be used only when service_to_metrics are empty; '
'found active service metrics during merge. Proceeding anyway.',
extra={
'conversation_id': self.conversation_id,
'self_service_to_metrics_keys': list(
self.service_to_metrics.keys()
),
'incoming_service_to_metrics_keys': list(
conversation_stats.service_to_metrics.keys()
),
},
)
# Drop zero-cost entries from restored metrics only
def _drop_zero_cost(d: dict[str, Metrics]) -> None:
to_delete = [
k for k, v in d.items() if getattr(v, 'accumulated_cost', 0) == 0
]
for k in to_delete:
del d[k]
_drop_zero_cost(self.restored_metrics)
_drop_zero_cost(conversation_stats.restored_metrics)
# Merge restored metrics, allowing incoming to overwrite
self.restored_metrics.update(conversation_stats.restored_metrics)
# Save merged state
self.save_metrics()
logger.info(
'Merged conversation stats',
extra={'conversation_id': self.conversation_id},
)