"""run_inference — core of the context-inference framework (ADR-0014 §3). Contract: run_inference(manifest, history) → dict[key, value] Semantics: - For each InferredParam in manifest.inferred_params: - If len(history.events) < param.min_history → emit cold_start_default. - Otherwise → call param.infer(history) and emit the result. - Returns {key: value} ready for the caller to persist to user_preferences with source='inferred'. - User overrides (source='user') are handled by the caller's upsert logic; this function has no DB access. """ from __future__ import annotations import logging import time from typing import Any from ..manifest import AgentManifest from .history import UserHistory log = logging.getLogger(__name__) def run_inference(manifest: AgentManifest, history: UserHistory) -> dict[str, Any]: """Evaluate all InferredParams for an agent and return {key: inferred_value}.""" result: dict[str, Any] = {} n = len(history.events) for param in manifest.inferred_params: t0 = time.monotonic() if param.infer is None: result[param.key] = param.cold_start_default continue if n < param.min_history: value = param.cold_start_default source = "cold_start" else: try: value = param.infer(history) source = "inferred" except Exception as exc: log.warning( "inference_error agent=%s param=%s error=%s — using cold_start_default", manifest.id, param.key, exc, ) value = param.cold_start_default source = "error_fallback" latency_ms = round((time.monotonic() - t0) * 1000, 1) log.info( "inference_param agent=%s param=%s source=%s value=%r history_len=%d latency_ms=%s", manifest.id, param.key, source, value, n, latency_ms, ) result[param.key] = value return result