107 lines
4.0 KiB
Python
107 lines
4.0 KiB
Python
"""Build a minimal unified project operating context packet."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any, Optional
|
|
|
|
from app.schemas.project_operating_context import ProjectOperatingContext
|
|
|
|
|
|
def _safe_int(value: Any) -> Optional[int]:
|
|
try:
|
|
if value is None:
|
|
return None
|
|
return int(value)
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
class ProjectOperatingContextBuilder:
|
|
def __init__(self, repo_scope: str | None = None) -> None:
|
|
self.repo_scope = str(repo_scope or "").strip() or self._default_repo_scope()
|
|
|
|
@staticmethod
|
|
def _default_repo_scope() -> str:
|
|
return Path(__file__).resolve().parents[4].name
|
|
|
|
@staticmethod
|
|
def _normalize_anchor(value: Any) -> dict[str, Any]:
|
|
if not isinstance(value, dict):
|
|
return {}
|
|
normalized: dict[str, Any] = {}
|
|
for key, raw in value.items():
|
|
if not str(key or "").strip():
|
|
continue
|
|
if raw in (None, "", [], {}):
|
|
continue
|
|
normalized[str(key).strip()] = raw
|
|
return normalized
|
|
|
|
def build(
|
|
self,
|
|
*,
|
|
current_project_id: int | None = None,
|
|
page_anchor: dict[str, Any] | None = None,
|
|
thread_anchor: dict[str, Any] | None = None,
|
|
) -> ProjectOperatingContext:
|
|
return ProjectOperatingContext(
|
|
current_project_id=_safe_int(current_project_id),
|
|
current_repo_scope=self.repo_scope,
|
|
page_anchor=self._normalize_anchor(page_anchor),
|
|
thread_anchor=self._normalize_anchor(thread_anchor),
|
|
)
|
|
|
|
def build_from_ingress(
|
|
self,
|
|
*,
|
|
project_hint_id: int | None = None,
|
|
rag_request_context: dict[str, Any] | None = None,
|
|
thread_context: dict[str, Any] | None = None,
|
|
world_model: dict[str, Any] | None = None,
|
|
session_project_id: int | None = None,
|
|
existing_packet: dict[str, Any] | None = None,
|
|
) -> ProjectOperatingContext:
|
|
context = rag_request_context if isinstance(rag_request_context, dict) else {}
|
|
world = world_model if isinstance(world_model, dict) else {}
|
|
existing = existing_packet if isinstance(existing_packet, dict) else {}
|
|
|
|
page_anchor = self._normalize_anchor(existing.get("page_anchor"))
|
|
if not page_anchor:
|
|
requested_project_id = _safe_int(context.get("project_id")) or _safe_int(project_hint_id)
|
|
source_chat_id = str(context.get("source_chat_id") or "").strip()
|
|
if requested_project_id is not None:
|
|
page_anchor["project_id"] = requested_project_id
|
|
if source_chat_id:
|
|
page_anchor["source_chat_id"] = source_chat_id
|
|
|
|
thread_anchor = self._normalize_anchor(existing.get("thread_anchor"))
|
|
if not thread_anchor:
|
|
raw_thread = thread_context if isinstance(thread_context, dict) else {}
|
|
reply_to_message_id = str(raw_thread.get("reply_to_message_id") or "").strip()
|
|
root_message_id = str(raw_thread.get("root_message_id") or "").strip()
|
|
if reply_to_message_id:
|
|
thread_anchor["reply_to_message_id"] = reply_to_message_id
|
|
if root_message_id:
|
|
thread_anchor["root_message_id"] = root_message_id
|
|
|
|
current_project_id = (
|
|
_safe_int(world.get("resolved_project_id"))
|
|
or _safe_int(existing.get("current_project_id"))
|
|
or _safe_int(session_project_id)
|
|
or _safe_int(context.get("project_id"))
|
|
or _safe_int(project_hint_id)
|
|
)
|
|
repo_scope = str(
|
|
world.get("repo_scope")
|
|
or existing.get("current_repo_scope")
|
|
or self.repo_scope
|
|
or ""
|
|
).strip() or self.repo_scope
|
|
|
|
return ProjectOperatingContextBuilder(repo_scope=repo_scope).build(
|
|
current_project_id=current_project_id,
|
|
page_anchor=page_anchor,
|
|
thread_anchor=thread_anchor,
|
|
)
|