PMFlow/backend/app/services/adaptive_rag_service/internal_query_context.py

190 lines
6.2 KiB
Python

from __future__ import annotations
from typing import Any
from pydantic import BaseModel, ConfigDict, Field
class InternalContextPacket(BaseModel):
model_config = ConfigDict(extra="ignore")
packet_version: str = "v1"
question: str = ""
scope: dict[str, Any] = Field(default_factory=dict)
route_decision: dict[str, Any] = Field(default_factory=dict)
operation_contract: dict[str, Any] = Field(default_factory=dict)
target_resolution: dict[str, Any] = Field(default_factory=dict)
query_plan: dict[str, Any] = Field(default_factory=dict)
executor_summary: dict[str, Any] = Field(default_factory=dict)
verifier_summary: dict[str, Any] = Field(default_factory=dict)
uncertainties: list[str] = Field(default_factory=list)
def coerce_internal_query_context_packet(value: Any) -> InternalContextPacket | None:
if isinstance(value, InternalContextPacket):
return value
if isinstance(value, dict):
try:
return InternalContextPacket.model_validate(value)
except Exception:
return None
return None
def _safe_mapping(value: Any) -> dict[str, Any]:
return dict(value) if isinstance(value, dict) else {}
def _normalized_internal_route_mode(route_decision: Any) -> str:
payload = _safe_mapping(route_decision)
return str(
payload.get("effective_source_policy")
or payload.get("route_mode")
or payload.get("source_policy")
or ""
).strip().lower()
def internal_context_route_decision(
*,
packet: Any = None,
fallback: Any = None,
) -> dict[str, Any]:
normalized = coerce_internal_query_context_packet(packet)
if normalized is not None and isinstance(normalized.route_decision, dict):
return dict(normalized.route_decision)
return _safe_mapping(fallback)
def internal_context_requires_packet(
*,
packet: Any = None,
route_decision: Any = None,
) -> bool:
normalized_route = internal_context_route_decision(packet=packet, fallback=route_decision)
return _normalized_internal_route_mode(normalized_route) == "internal"
def internal_context_semantic_mirror(
*,
packet: Any = None,
query_semantics: Any = None,
key: str,
) -> dict[str, Any] | None:
if internal_context_requires_packet(packet=packet, route_decision=query_semantics):
return None
semantics = _safe_mapping(query_semantics)
value = semantics.get(str(key or "").strip())
return dict(value) if isinstance(value, dict) else None
def internal_context_operation_contract(
*,
packet: Any = None,
fallback: Any = None,
) -> dict[str, Any]:
normalized = coerce_internal_query_context_packet(packet)
if normalized is not None and isinstance(normalized.operation_contract, dict):
return dict(normalized.operation_contract)
return _safe_mapping(fallback)
def internal_context_target_resolution(
*,
packet: Any = None,
fallback: Any = None,
) -> dict[str, Any]:
normalized = coerce_internal_query_context_packet(packet)
if normalized is not None and isinstance(normalized.target_resolution, dict):
return dict(normalized.target_resolution)
return _safe_mapping(fallback)
def internal_context_query_plan(
*,
packet: Any = None,
fallback: Any = None,
) -> dict[str, Any]:
normalized = coerce_internal_query_context_packet(packet)
if normalized is not None and isinstance(normalized.query_plan, dict):
return dict(normalized.query_plan)
return _safe_mapping(fallback)
def internal_context_block_reason(
packet: Any,
*,
route_decision: Any = None,
) -> tuple[str, list[str]] | None:
normalized = coerce_internal_query_context_packet(packet)
if internal_context_requires_packet(packet=normalized, route_decision=route_decision) and normalized is None:
return ("missing_internal_context_packet", ["internal_context_packet"])
if normalized is None:
return None
operation_contract = internal_context_operation_contract(packet=normalized)
if not str(operation_contract.get("query_type") or "").strip():
return ("missing_internal_operation_contract", ["query_type"])
target_resolution = internal_context_target_resolution(packet=normalized)
resolution_mode = str(target_resolution.get("resolution_mode") or "").strip().lower()
if resolution_mode == "clarify":
return ("target_resolution_clarify", ["target_resolution"])
query_plan = internal_context_query_plan(packet=normalized)
if not str(query_plan.get("answer_contract") or "").strip():
return ("missing_internal_query_plan_contract", ["answer_contract"])
return None
def update_internal_query_context_packet(
packet: Any,
*,
executor_summary: dict[str, Any] | None = None,
verifier_summary: dict[str, Any] | None = None,
uncertainties: list[str] | None = None,
) -> InternalContextPacket | None:
normalized = coerce_internal_query_context_packet(packet)
if normalized is None:
return None
next_uncertainties = [
str(item).strip()
for item in list(normalized.uncertainties or [])
if str(item).strip()
]
for item in uncertainties or []:
text = str(item).strip()
if text and text not in next_uncertainties:
next_uncertainties.append(text)
return normalized.model_copy(
update={
"executor_summary": (
dict(executor_summary)
if isinstance(executor_summary, dict)
else dict(normalized.executor_summary or {})
),
"verifier_summary": (
dict(verifier_summary)
if isinstance(verifier_summary, dict)
else dict(normalized.verifier_summary or {})
),
"uncertainties": next_uncertainties,
}
)
__all__ = [
"InternalContextPacket",
"coerce_internal_query_context_packet",
"internal_context_block_reason",
"internal_context_operation_contract",
"internal_context_query_plan",
"internal_context_route_decision",
"internal_context_requires_packet",
"internal_context_semantic_mirror",
"internal_context_target_resolution",
"update_internal_query_context_packet",
]