"""Abstract interface for run metadata storage. RunManager depends on this interface. Implementations: - MemoryRunStore: in-memory dict (development, tests) - Future: RunRepository backed by SQLAlchemy ORM All methods accept an optional user_id for user isolation. When user_id is None, no user filtering is applied (single-user mode). """ from __future__ import annotations import abc from typing import Any class RunStore(abc.ABC): @abc.abstractmethod async def put( self, run_id: str, *, thread_id: str, assistant_id: str | None = None, user_id: str | None = None, status: str = "pending", multitask_strategy: str = "reject", metadata: dict[str, Any] | None = None, kwargs: dict[str, Any] | None = None, error: str | None = None, created_at: str | None = None, follow_up_to_run_id: str | None = None, ) -> None: pass @abc.abstractmethod async def get(self, run_id: str) -> dict[str, Any] | None: pass @abc.abstractmethod async def list_by_thread( self, thread_id: str, *, user_id: str | None = None, limit: int = 100, ) -> list[dict[str, Any]]: pass @abc.abstractmethod async def update_status( self, run_id: str, status: str, *, error: str | None = None, ) -> None: pass @abc.abstractmethod async def delete(self, run_id: str) -> None: pass @abc.abstractmethod async def update_run_completion( self, run_id: str, *, status: str, total_input_tokens: int = 0, total_output_tokens: int = 0, total_tokens: int = 0, llm_call_count: int = 0, lead_agent_tokens: int = 0, subagent_tokens: int = 0, middleware_tokens: int = 0, message_count: int = 0, last_ai_message: str | None = None, first_human_message: str | None = None, error: str | None = None, ) -> None: pass @abc.abstractmethod async def list_pending(self, *, before: str | None = None) -> list[dict[str, Any]]: pass @abc.abstractmethod async def aggregate_tokens_by_thread(self, thread_id: str) -> dict[str, Any]: """Aggregate token usage for completed runs in a thread. Returns a dict with keys: total_tokens, total_input_tokens, total_output_tokens, total_runs, by_model (model_name → {tokens, runs}), by_caller ({lead_agent, subagent, middleware}). """ pass