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

767 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Adaptive RAG internal target resolution helpers."""
from __future__ import annotations
from .shared import *
class AdaptiveRAGTargetResolutionMixin:
_TARGET_ENTITY_TYPES = {"project", "feature", "config", "doc", "person", "signal", "unknown"}
_TARGET_RESOLUTION_MODES = {"deterministic", "llm_rerank", "semantic_filter", "clarify", "none"}
@classmethod
def _normalize_object_mentions(
cls,
raw: Any,
*,
question: str = "",
) -> list[str]:
_ = (cls, question)
mentions: list[str] = []
rows = raw if isinstance(raw, list) else []
for item in rows:
text = str(item or "").strip(" ,。,.!??::;;“”\"'()()")
text = QUESTION_NOISE_RE.sub("", text).strip()
if len(text) >= 2 and text not in mentions:
mentions.append(text[:32])
return mentions[:4]
def _build_operation_contract(
self,
*,
question: str,
query_semantics: Optional[dict[str, Any]],
) -> dict[str, Any]:
_ = question
semantics = query_semantics if isinstance(query_semantics, dict) else {}
query_type = str(semantics.get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower() or QUERY_TYPE_UNKNOWN
query_profile = dict(semantics.get("query_profile") or {}) if isinstance(semantics.get("query_profile"), dict) else {}
referenced_objects = {
str(item or "").strip().lower()
for item in list(query_profile.get("referenced_objects") or [])
if str(item or "").strip()
}
if query_type in {QUERY_TYPE_EXISTENCE, QUERY_TYPE_UNKNOWN} and referenced_objects.intersection(
{"model_routing", "system_config"}
):
query_type = QUERY_TYPE_STATUS
object_mentions = self._normalize_object_mentions(
semantics.get("object_mentions"),
question=question,
)
return {
"query_type": query_type,
"answer_contract": (
self._internal_answer_contract_for_query_type(query_type)
if hasattr(self, "_internal_answer_contract_for_query_type")
else "clarify_or_fail"
),
"query_profile": query_profile,
"object_mentions": object_mentions,
"clarify_policy": "ask_when_target_ambiguous",
}
@classmethod
def _normalize_target_candidate(cls, payload: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:
row = payload if isinstance(payload, dict) else {}
entity_type = str(row.get("target_entity_type") or row.get("entity_type") or "").strip().lower()
if entity_type not in cls._TARGET_ENTITY_TYPES:
return None
score = max(0.0, min(1.0, float(row.get("score") or 0.0)))
return {
"target_entity_type": entity_type,
"target_ref": str(row.get("target_ref") or "").strip() or None,
"resolved_label": str(row.get("resolved_label") or row.get("display_name") or "").strip() or None,
"score": round(score, 4),
"source_kind": str(row.get("source_kind") or "unknown").strip().lower() or "unknown",
"match_label": str(row.get("match_label") or "").strip() or None,
"source_confidence": round(max(0.0, min(1.0, float(row.get("source_confidence") or score or 0.0))), 4),
}
@staticmethod
def _target_candidate_identity(row: dict[str, Any]) -> str:
return "|".join(
[
str(row.get("target_entity_type") or ""),
str(row.get("target_ref") or ""),
str(row.get("resolved_label") or ""),
]
)
async def _collect_semantic_fact_target_candidates(
self,
*,
mentions: list[str],
project_id: Optional[int],
operation_contract: dict[str, Any],
) -> list[dict[str, Any]]:
if not mentions:
return []
candidates: list[dict[str, Any]] = []
fact_query = getattr(self, "_query_project_semantic_facts_by_lookup_terms", None)
if not callable(fact_query):
return []
for mention in mentions[:3]:
fact_rows, _used_keyword = await fact_query(
query_terms=[mention],
project_id=project_id,
limit=10,
)
project_ids: list[int] = []
fact_confidence_by_project: dict[int, float] = {}
for row in fact_rows:
try:
entity_id = int(row.entity_id)
except Exception:
continue
if entity_id not in project_ids:
project_ids.append(entity_id)
fact_confidence_by_project[entity_id] = max(
float(fact_confidence_by_project.get(entity_id) or 0.0),
float(getattr(row, "confidence", 0.0) or 0.0),
)
if not project_ids:
continue
project_rows = (
await self.db.execute(
select(Project).where(
and_(
Project.tenant_id == self.tenant_id,
Project.id.in_(project_ids),
)
)
)
).scalars().all()
for project in project_rows:
confidence = max(0.88, min(0.99, float(fact_confidence_by_project.get(int(project.id)) or 0.0)))
candidates.append(
{
"target_entity_type": "project",
"target_ref": f"project:{int(project.id)}",
"resolved_label": str(project.title or project.name or f"项目#{int(project.id)}"),
"score": round(min(0.99, confidence + 0.03), 4),
"source_kind": "semantic_facts",
"match_label": mention,
"source_confidence": round(confidence, 4),
}
)
return candidates
async def _collect_project_target_candidates(
self,
*,
mentions: list[str],
project_id: Optional[int],
) -> list[dict[str, Any]]:
if not mentions:
return []
candidates: list[dict[str, Any]] = []
similarity_scorer = getattr(self, "_lookup_similarity_score", None)
for mention in mentions[:3]:
matched_rows: list[Project] = []
searcher = getattr(self, "_search_projects_by_keyword", None)
if callable(searcher):
matched_rows = await searcher(keyword=mention, project_id=project_id, limit=6)
else:
stmt = select(Project).where(Project.tenant_id == self.tenant_id)
if project_id is not None:
stmt = stmt.where(Project.id == int(project_id))
like = f"%{mention}%"
stmt = stmt.where(
or_(
Project.title.ilike(like),
Project.name.ilike(like),
Project.description.ilike(like),
)
)
matched_rows = (await self.db.execute(stmt.order_by(Project.updated_at.desc()).limit(6))).scalars().all()
if not matched_rows:
stmt = select(Project).where(Project.tenant_id == self.tenant_id)
if project_id is not None:
stmt = stmt.where(Project.id == int(project_id))
project_rows = (
await self.db.execute(
stmt.order_by(Project.updated_at.desc(), Project.id.desc()).limit(120)
)
).scalars().all()
scored_rows: list[tuple[float, Project]] = []
for project in project_rows:
title = str(project.title or "").strip()
name = str(project.name or "").strip()
description = str(project.description or "").strip()
if callable(similarity_scorer):
score = max(
similarity_scorer(mention, title),
similarity_scorer(mention, name),
similarity_scorer(mention, description) * 0.8,
)
else:
lowered = mention.lower()
title_l = title.lower()
name_l = name.lower()
description_l = description.lower()
score = 0.0
if lowered and (lowered in title_l or lowered in name_l):
score = 0.9
elif lowered and lowered in description_l:
score = 0.76
if score >= 0.62:
scored_rows.append((score, project))
scored_rows.sort(key=lambda item: item[0], reverse=True)
matched_rows = [row for _, row in scored_rows[:6]]
for project in matched_rows:
title = str(project.title or "").strip().lower()
name = str(project.name or "").strip().lower()
description = str(project.description or "").strip().lower()
lowered = mention.lower()
score = 0.82
if callable(similarity_scorer):
score = max(
0.74,
similarity_scorer(mention, title) if title else 0.0,
similarity_scorer(mention, name) if name else 0.0,
(similarity_scorer(mention, description) * 0.8) if description else 0.0,
)
elif lowered and (lowered in title or lowered in name):
score = 0.91
elif lowered and lowered in description:
score = 0.86
candidates.append(
{
"target_entity_type": "project",
"target_ref": f"project:{int(project.id)}",
"resolved_label": str(project.title or project.name or f"项目#{int(project.id)}"),
"score": round(score, 4),
"source_kind": "project_db",
"match_label": mention,
"source_confidence": round(score, 4),
}
)
return candidates
def _collect_feature_target_candidates(
self,
*,
question: str,
mentions: list[str],
system_snapshot: Optional[dict[str, Any]],
) -> list[dict[str, Any]]:
matcher = getattr(self, "_match_feature_catalog_entries", None)
if not callable(matcher):
return []
search_text = " ".join(mentions[:2]) if mentions else question
rows = matcher(
question=search_text,
system_snapshot=system_snapshot,
limit=4,
)
candidates: list[dict[str, Any]] = []
for row in rows:
name = str(row.get("name") or "").strip()
route = str(row.get("route") or "").strip()
match_score = max(0.0, min(1.0, float(row.get("match_score") or 0.0)))
if not name and not route:
continue
score = min(0.92, max(0.72, 0.62 + match_score * 0.25))
target_key = route or name
candidates.append(
{
"target_entity_type": "feature",
"target_ref": f"feature:{target_key}",
"resolved_label": name or target_key,
"score": round(score, 4),
"source_kind": "feature_catalog",
"match_label": name or target_key,
"source_confidence": round(match_score or score, 4),
}
)
return candidates
def _collect_config_target_candidates(
self,
*,
query_semantics: Optional[dict[str, Any]],
operation_contract: dict[str, Any],
) -> list[dict[str, Any]]:
semantics = query_semantics if isinstance(query_semantics, dict) else {}
query_profile = semantics.get("query_profile") if isinstance(semantics.get("query_profile"), dict) else {}
referenced_objects = {
str(item or "").strip().lower()
for item in list(query_profile.get("referenced_objects") or [])
if str(item or "").strip()
}
if not {"model_routing", "system_config"}.intersection(referenced_objects):
return []
mentions = list(operation_contract.get("object_mentions") or [])
rows: list[dict[str, Any]] = []
if "model_routing" in referenced_objects:
rows.append(
{
"target_entity_type": "config",
"target_ref": "config:model_routing",
"resolved_label": mentions[0] if mentions else "模型配置",
"score": 0.9,
"source_kind": "system_config",
"match_label": mentions[0] if mentions else "模型配置",
"source_confidence": 0.9,
}
)
elif "system_config" in referenced_objects:
rows.append(
{
"target_entity_type": "config",
"target_ref": "config:system",
"resolved_label": mentions[0] if mentions else "系统配置",
"score": 0.86,
"source_kind": "system_config",
"match_label": mentions[0] if mentions else "系统配置",
"source_confidence": 0.86,
}
)
return rows
async def _collect_kg_target_candidates(
self,
*,
question: str,
mentions: list[str],
project_id: Optional[int],
) -> list[dict[str, Any]]:
resolver = getattr(self, "entity_resolution", None)
if not callable(resolver):
return []
resolution = await resolver(
question=" ".join(mentions[:2]) if mentions else question,
project_id=project_id,
limit=6,
)
matched_entities = resolution.get("matched_entities") if isinstance(resolution, dict) else []
if not isinstance(matched_entities, list) or not matched_entities:
return []
ids: list[int] = []
matched_score: dict[int, float] = {}
for row in matched_entities:
if not isinstance(row, dict):
continue
try:
entity_id = int(row.get("entity_id"))
except Exception:
continue
ids.append(entity_id)
matched_score[entity_id] = max(
float(matched_score.get(entity_id) or 0.0),
float(row.get("score") or 0.0),
)
if not ids:
return []
rows = (
await self.db.execute(
select(KGEntity).where(
and_(
KGEntity.tenant_id == self.tenant_id,
KGEntity.id.in_(ids),
)
)
)
).scalars().all()
candidates: list[dict[str, Any]] = []
for row in rows:
payload = self._safe_dict(row.payload) if hasattr(self, "_safe_dict") else (row.payload if isinstance(row.payload, dict) else {})
entity_score = max(0.0, min(1.0, float(matched_score.get(int(row.id)) or 0.0)))
project_ref = payload.get("project_id")
if project_ref is not None:
try:
project_ref = int(project_ref)
except Exception:
project_ref = None
if project_ref is not None:
candidates.append(
{
"target_entity_type": "project",
"target_ref": f"project:{int(project_ref)}",
"resolved_label": str(row.display_name or row.entity_key or f"项目#{int(project_ref)}"),
"score": round(min(0.89, max(0.68, 0.58 + entity_score * 0.25)), 4),
"source_kind": "knowledge_graph",
"match_label": str(row.display_name or row.entity_key or "").strip() or None,
"source_confidence": round(entity_score or 0.68, 4),
}
)
return candidates
async def _collect_internal_target_candidates(
self,
*,
question: str,
project_id: Optional[int],
query_semantics: Optional[dict[str, Any]],
system_snapshot: Optional[dict[str, Any]],
operation_contract: dict[str, Any],
) -> list[dict[str, Any]]:
_ = query_semantics
mentions = list(operation_contract.get("object_mentions") or [])
providers = await asyncio.gather(
self._collect_semantic_fact_target_candidates(
mentions=mentions,
project_id=project_id,
operation_contract=operation_contract,
),
self._collect_project_target_candidates(
mentions=mentions,
project_id=project_id,
),
self._collect_kg_target_candidates(
question=question,
mentions=mentions,
project_id=project_id,
),
)
rows: list[dict[str, Any]] = []
for bucket in providers:
for item in bucket:
normalized = self._normalize_target_candidate(item)
if normalized is not None:
rows.append(normalized)
for item in self._collect_feature_target_candidates(
question=question,
mentions=mentions,
system_snapshot=system_snapshot,
):
normalized = self._normalize_target_candidate(item)
if normalized is not None:
rows.append(normalized)
for item in self._collect_config_target_candidates(
query_semantics=query_semantics,
operation_contract=operation_contract,
):
normalized = self._normalize_target_candidate(item)
if normalized is not None:
rows.append(normalized)
deduped: dict[str, dict[str, Any]] = {}
for row in rows:
key = self._target_candidate_identity(row)
existing = deduped.get(key)
if existing is None or float(row.get("score") or 0.0) > float(existing.get("score") or 0.0):
deduped[key] = row
return sorted(deduped.values(), key=lambda item: float(item.get("score") or 0.0), reverse=True)[:8]
async def _rerank_internal_target_candidates(
self,
*,
question: str,
operation_contract: dict[str, Any],
candidates: list[dict[str, Any]],
) -> Optional[dict[str, Any]]:
if len(candidates) < 2 or not hasattr(self, "llm"):
return None
prompt = PROMPT_INTERNAL_TARGET_RESOLUTION.format(
question=str(question or "")[:600],
operation_contract=json.dumps(operation_contract, ensure_ascii=False)[:1000],
candidates=json.dumps(
[
{
"index": idx,
"target_entity_type": str(item.get("target_entity_type") or ""),
"target_ref": str(item.get("target_ref") or ""),
"resolved_label": str(item.get("resolved_label") or ""),
"score": float(item.get("score") or 0.0),
"source_kind": str(item.get("source_kind") or ""),
}
for idx, item in enumerate(candidates[:4])
],
ensure_ascii=False,
),
).strip()
try:
raw = await self.llm.generate(
prompt,
db=self.db,
tenant_id=self.tenant_id,
function_name="internal_target_resolution",
temperature=0.0,
max_tokens=220,
use_prompt_profile=False,
)
except Exception:
return None
parser = getattr(self, "_parse_json_object_safe", None)
parsed = parser(raw) if callable(parser) else {}
if not isinstance(parsed, dict):
return None
if bool(parsed.get("clarify")):
return {
"target_entity_type": "unknown",
"target_ref": None,
"resolved_label": None,
"resolution_mode": "clarify",
"confidence": round(max(0.0, min(1.0, float(parsed.get("confidence") or 0.0))), 4),
"candidate_summary": candidates[:4],
}
try:
selected_index = int(parsed.get("selected_index"))
except Exception:
return None
if selected_index < 0 or selected_index >= len(candidates[:4]):
return None
selected = dict(candidates[selected_index])
return {
"target_entity_type": str(selected.get("target_entity_type") or "unknown"),
"target_ref": selected.get("target_ref"),
"resolved_label": selected.get("resolved_label"),
"resolution_mode": "llm_rerank",
"confidence": round(max(0.0, min(1.0, float(parsed.get("confidence") or selected.get("score") or 0.0))), 4),
"candidate_summary": candidates[:4],
}
def _build_semantic_filter_target(
self,
*,
query_semantics: Optional[dict[str, Any]],
operation_contract: dict[str, Any],
) -> Optional[dict[str, Any]]:
semantics = query_semantics if isinstance(query_semantics, dict) else {}
subject_focus = str(semantics.get("subject_focus") or "unknown").strip().lower()
query_profile = semantics.get("query_profile") if isinstance(semantics.get("query_profile"), dict) else {}
mentions = list(operation_contract.get("object_mentions") or [])
resolved_label = mentions[0] if mentions else None
referenced_objects = {
str(item or "").strip().lower()
for item in list(query_profile.get("referenced_objects") or [])
if str(item or "").strip()
}
if {"model_routing", "system_config"}.intersection(referenced_objects):
target_ref = "config:model_routing" if "model_routing" in referenced_objects else "config:system"
return {
"target_entity_type": "config",
"target_ref": target_ref,
"resolved_label": resolved_label or ("模型配置" if target_ref.endswith("model_routing") else "系统配置"),
"resolution_mode": "deterministic",
"confidence": 0.84,
"candidate_summary": [],
}
if subject_focus == "project":
return {
"target_entity_type": "project",
"target_ref": None,
"resolved_label": resolved_label,
"resolution_mode": "semantic_filter",
"confidence": 0.78 if resolved_label else 0.64,
"candidate_summary": [],
}
if subject_focus == "feature" or "feature_catalog" in referenced_objects:
return {
"target_entity_type": "feature",
"target_ref": None,
"resolved_label": resolved_label,
"resolution_mode": "semantic_filter",
"confidence": 0.74 if resolved_label else 0.62,
"candidate_summary": [],
}
return None
async def _resolve_internal_target(
self,
*,
question: str,
project_id: Optional[int],
query_semantics: Optional[dict[str, Any]],
system_snapshot: Optional[dict[str, Any]],
operation_contract: Optional[dict[str, Any]],
) -> dict[str, Any]:
semantics = query_semantics if isinstance(query_semantics, dict) else {}
operation = operation_contract if isinstance(operation_contract, dict) else {}
source_policy = str(
semantics.get("effective_source_policy")
or semantics.get("source_policy")
or ""
).strip().lower()
if source_policy != "internal":
return {
"target_entity_type": "unknown",
"target_ref": None,
"resolved_label": None,
"resolution_mode": "none",
"confidence": 0.0,
"candidate_summary": [],
}
candidates = await self._collect_internal_target_candidates(
question=question,
project_id=project_id,
query_semantics=semantics,
system_snapshot=system_snapshot,
operation_contract=operation,
)
if not candidates:
fallback = self._build_semantic_filter_target(
query_semantics=semantics,
operation_contract=operation,
)
if fallback is not None:
return fallback
return {
"target_entity_type": "unknown",
"target_ref": None,
"resolved_label": None,
"resolution_mode": "clarify",
"confidence": 0.0,
"candidate_summary": [],
}
top = candidates[0]
second = candidates[1] if len(candidates) > 1 else None
margin = float(top.get("score") or 0.0) - float(second.get("score") or 0.0) if second else 1.0
if second and str(top.get("target_entity_type") or "") != str(second.get("target_entity_type") or "") and margin < 0.1:
reranked = await self._rerank_internal_target_candidates(
question=question,
operation_contract=operation,
candidates=candidates,
)
if reranked is not None:
return reranked
if float(top.get("score") or 0.0) >= 0.88 or margin >= 0.12 or second is None:
return {
"target_entity_type": str(top.get("target_entity_type") or "unknown"),
"target_ref": top.get("target_ref"),
"resolved_label": top.get("resolved_label"),
"resolution_mode": "deterministic",
"confidence": round(max(0.0, min(1.0, float(top.get("score") or 0.0))), 4),
"candidate_summary": candidates[:4],
}
if str(top.get("target_entity_type") or "") == str(second.get("target_entity_type") or ""):
return {
"target_entity_type": str(top.get("target_entity_type") or "unknown"),
"target_ref": top.get("target_ref"),
"resolved_label": top.get("resolved_label"),
"resolution_mode": "deterministic",
"confidence": round(max(0.0, min(1.0, float(top.get("score") or 0.0))), 4),
"candidate_summary": candidates[:4],
}
return {
"target_entity_type": "unknown",
"target_ref": None,
"resolved_label": None,
"resolution_mode": "clarify",
"confidence": round(max(0.0, min(1.0, float(top.get("score") or 0.0))), 4),
"candidate_summary": candidates[:4],
}
def _patch_scene_semantics_with_target_resolution(
self,
*,
scene_semantics: Optional[dict[str, Any]],
operation_contract: Optional[dict[str, Any]],
target_resolution: Optional[dict[str, Any]],
) -> dict[str, Any]:
normalized = dict(scene_semantics or {})
query_type = str((operation_contract or {}).get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower()
target_entity_type = str((target_resolution or {}).get("target_entity_type") or "unknown").strip().lower()
snapshot_focus = str(normalized.get("snapshot_focus") or "none").strip().lower()
if query_type == QUERY_TYPE_EXISTENCE and target_entity_type == "project":
normalized["snapshot_focus"] = "none"
return normalized
if target_entity_type not in {"feature", "config"} and snapshot_focus == "feature":
normalized["snapshot_focus"] = "none"
return normalized
return normalized
def _should_allow_snapshot_direct_answer(
self,
*,
scene_semantics: Optional[dict[str, Any]],
operation_contract: Optional[dict[str, Any]],
target_resolution: Optional[dict[str, Any]],
) -> bool:
snapshot_focus = str((scene_semantics or {}).get("snapshot_focus") or "none").strip().lower()
query_type = str((operation_contract or {}).get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower()
target_entity_type = str((target_resolution or {}).get("target_entity_type") or "unknown").strip().lower()
if snapshot_focus in {"identity", "architecture"}:
return query_type in {QUERY_TYPE_EXPLAIN, QUERY_TYPE_STATUS}
if snapshot_focus == "feature":
return query_type in {QUERY_TYPE_EXPLAIN, QUERY_TYPE_STATUS, QUERY_TYPE_LIST} and target_entity_type in {
"feature",
"config",
"unknown",
}
return False
@staticmethod
def _build_operation_contract_summary(operation_contract: Optional[dict[str, Any]]) -> dict[str, Any]:
payload = operation_contract if isinstance(operation_contract, dict) else {}
mentions = [
str(item or "").strip()
for item in list(payload.get("object_mentions") or [])
if str(item or "").strip()
]
return {
"query_type": str(payload.get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower() or QUERY_TYPE_UNKNOWN,
"answer_contract": str(payload.get("answer_contract") or "clarify_or_fail").strip() or "clarify_or_fail",
"object_mentions": mentions[:4],
"clarify_policy": str(payload.get("clarify_policy") or "").strip() or None,
}
@staticmethod
def _build_target_resolution_summary(target_resolution: Optional[dict[str, Any]]) -> dict[str, Any]:
payload = target_resolution if isinstance(target_resolution, dict) else {}
candidates: list[dict[str, Any]] = []
for item in list(payload.get("candidate_summary") or [])[:4]:
if not isinstance(item, dict):
continue
candidates.append(
{
"target_entity_type": str(item.get("target_entity_type") or "unknown").strip().lower() or "unknown",
"target_ref": str(item.get("target_ref") or "").strip() or None,
"resolved_label": str(item.get("resolved_label") or "").strip() or None,
"score": round(max(0.0, min(1.0, float(item.get("score") or 0.0))), 4),
"source_kind": str(item.get("source_kind") or "unknown").strip().lower() or "unknown",
}
)
return {
"target_entity_type": str(payload.get("target_entity_type") or "unknown").strip().lower() or "unknown",
"target_ref": str(payload.get("target_ref") or "").strip() or None,
"resolved_label": str(payload.get("resolved_label") or "").strip() or None,
"resolution_mode": str(payload.get("resolution_mode") or "none").strip().lower() or "none",
"confidence": round(max(0.0, min(1.0, float(payload.get("confidence") or 0.0))), 4),
"candidate_summary": candidates,
}
@staticmethod
def _build_query_plan_summary(query_plan: Optional[dict[str, Any]]) -> dict[str, Any]:
payload = query_plan if isinstance(query_plan, dict) else {}
filters: list[dict[str, Any]] = []
for item in list(payload.get("filters") or [])[:4]:
if not isinstance(item, dict):
continue
filters.append(
{
"field": str(item.get("field") or "").strip() or None,
"operator": str(item.get("operator") or "").strip() or None,
"value": item.get("value"),
"value_source": str(item.get("value_source") or "").strip() or None,
}
)
return {
"plan_version": str(payload.get("plan_version") or "unknown").strip() or "unknown",
"query_type": str(payload.get("query_type") or QUERY_TYPE_UNKNOWN).strip().lower() or QUERY_TYPE_UNKNOWN,
"entity_type": str(payload.get("entity_type") or "unknown").strip().lower() or "unknown",
"target_ref": str(payload.get("target_ref") or "").strip() or None,
"answer_contract": str(payload.get("answer_contract") or "").strip() or None,
"filters": filters,
}
def _inject_internal_query_runtime_summaries(
self,
*,
retrieval_metrics: Optional[dict[str, Any]],
query_semantics: Optional[dict[str, Any]],
) -> dict[str, Any]:
metrics = dict(retrieval_metrics or {})
semantics = query_semantics if isinstance(query_semantics, dict) else {}
metrics["operation_contract_summary"] = self._build_operation_contract_summary(semantics.get("operation_contract"))
metrics["target_resolution_summary"] = self._build_target_resolution_summary(semantics.get("target_resolution"))
metrics["query_plan_summary"] = self._build_query_plan_summary(semantics.get("query_plan"))
return metrics
__all__ = ["AdaptiveRAGTargetResolutionMixin"]