from __future__ import annotations from typing import Any, Literal from pydantic import BaseModel, ConfigDict, Field ExternalQueryKind = Literal["weather", "news", "company", "model", "market", "generic_web", "unknown"] ExternalAnswerContract = Literal["direct_fact", "summary_with_sources", "compare_with_sources", "clarify"] ExternalRetrievalStatus = Literal["evidence_found", "verifier_blocked", "no_relevant_results", "retrieval_failed"] ExternalImpactStatus = Literal["direct_match", "indirect_only", "no_direct_impact"] class ExternalQueryPlan(BaseModel): model_config = ConfigDict(extra="ignore") plan_version: str = "v1" query_kind: ExternalQueryKind = "unknown" answer_contract: ExternalAnswerContract = "summary_with_sources" freshness_required: bool = False topic: str = "" subject_entities: list[str] = Field(default_factory=list) location_hint: str | None = None time_range_hint: str | None = None preferred_domains: list[str] = Field(default_factory=list) search_queries: list[str] = Field(default_factory=list) result_limit: int = Field(default=3, ge=1, le=5) requires_multi_source: bool = False clarify_on_ambiguity: bool = False fit_brief_summary: str = "" project_scope_hint: str | None = None recommendation_goal: str | None = None project_object_anchors: list[str] = Field(default_factory=list) class ExternalEvidenceItem(BaseModel): model_config = ConfigDict(extra="ignore") source_type: str = "url" source_label: str = "" title: str = "" target_url: str = "" snippet: str = "" retrieved_at: str | None = None freshness_score: float = 0.0 authority_score: float = 0.0 grounded_fields: list[str] = Field(default_factory=list) raw_metadata: dict[str, Any] = Field(default_factory=dict) class ExternalAdapterResult(BaseModel): model_config = ConfigDict(extra="ignore") adapter_name: str status: str = "empty" evidence: list[ExternalEvidenceItem] = Field(default_factory=list) meta: dict[str, Any] = Field(default_factory=dict) class ExternalQueryExecution(BaseModel): model_config = ConfigDict(extra="ignore") plan: ExternalQueryPlan adapter_name: str evidence: list[ExternalEvidenceItem] = Field(default_factory=list) verifier: dict[str, Any] = Field(default_factory=dict) planner_meta: dict[str, Any] = Field(default_factory=dict) meta: dict[str, Any] = Field(default_factory=dict) def derive_retrieval_status(self) -> ExternalRetrievalStatus: verifier = dict(self.verifier or {}) attempts = list((self.meta or {}).get("adapter_attempts") or []) if self.evidence and bool(verifier.get("ok")): return "evidence_found" if self.evidence and not bool(verifier.get("ok")): return "verifier_blocked" if any( int(item.get("evidence_count") or 0) > 0 and not bool(item.get("verifier_ok")) for item in attempts if isinstance(item, dict) ): return "verifier_blocked" statuses = { str(item.get("status") or "").strip().lower() for item in attempts if isinstance(item, dict) and str(item.get("status") or "").strip() } if any(status in {"success", "empty"} for status in statuses): return "no_relevant_results" return "retrieval_failed" def to_context_packet(self, *, question: str) -> "ExternalContextPacket": verifier = dict(self.verifier or {}) uncertainties: list[str] = [] reason_code = str(verifier.get("reason_code") or "").strip() if reason_code: uncertainties.append(reason_code) for item in list(verifier.get("missing_fields") or []): normalized = str(item or "").strip() if normalized and normalized not in uncertainties: uncertainties.append(normalized) return ExternalContextPacket( question=str(question or "").strip(), plan=self.plan, adapter_attempts=list((self.meta or {}).get("adapter_attempts") or []), verified_evidence=list(self.evidence or []) if bool(verifier.get("ok")) else [], verifier=verifier, uncertainties=uncertainties, retrieval_status=self.derive_retrieval_status(), impact_status="no_direct_impact", affected_signal_sources=[], ) class ExternalContextPacket(BaseModel): model_config = ConfigDict(extra="ignore") question: str = "" plan: ExternalQueryPlan adapter_attempts: list[dict[str, Any]] = Field(default_factory=list) verified_evidence: list[ExternalEvidenceItem] = Field(default_factory=list) verifier: dict[str, Any] = Field(default_factory=dict) uncertainties: list[str] = Field(default_factory=list) retrieval_status: ExternalRetrievalStatus = "retrieval_failed" impact_status: ExternalImpactStatus = "no_direct_impact" affected_signal_sources: list[str] = Field(default_factory=list)