from __future__ import annotations import re from typing import Any from urllib.parse import urlparse from uuid import uuid4 from ...config.ai_prompts import ( PROMPT_EXTERNAL_COMPARE_WITH_SOURCES_QA, PROMPT_EXTERNAL_DIRECT_FACT_QA, PROMPT_EXTERNAL_SIGNAL_SOURCE_RECOMMENDATION_PACKET, PROMPT_EXTERNAL_SIGNAL_SOURCE_RECOMMENDATION_QA, PROMPT_EXTERNAL_SUMMARY_WITH_SOURCES_QA, ) from ...schemas.context_workspace import ResearchCandidateItem, ResearchWorkspace from ..prompt_runtime_service import PromptRuntimeRenderResult, PromptRuntimeService from .schemas import ( ExternalContextPacket, SignalSourceRecommendationCandidate, SignalSourceRecommendationPacket, ) class ExternalAnswerPresenter: _PROMPT_CONFIG = { "direct_fact": ( "PROMPT_EXTERNAL_DIRECT_FACT_QA", PROMPT_EXTERNAL_DIRECT_FACT_QA, "external_direct_fact", ), "summary_with_sources": ( "PROMPT_EXTERNAL_SUMMARY_WITH_SOURCES_QA", PROMPT_EXTERNAL_SUMMARY_WITH_SOURCES_QA, "external_summary_with_sources", ), "compare_with_sources": ( "PROMPT_EXTERNAL_COMPARE_WITH_SOURCES_QA", PROMPT_EXTERNAL_COMPARE_WITH_SOURCES_QA, "external_compare_with_sources", ), } def __init__( self, *, llm_service: Any, db: Any, tenant_id: int, ) -> None: self.llm_service = llm_service self.db = db self.tenant_id = int(tenant_id or 1) @classmethod def _normalize_contract(cls, value: Any) -> str: normalized = str(value or "").strip().lower() if normalized in cls._PROMPT_CONFIG: return normalized return "summary_with_sources" @staticmethod def _display_source_label(*, source_label: Any = None, title: Any = None, target_url: Any = None) -> str: raw_label = str(source_label or "").strip() raw_title = str(title or "").strip() raw_url = str(target_url or "").strip() url_wrapper = re.match(r"^url\[(?P