Spaces:
Runtime error
Runtime error
| """In-place fact update on REFERENCE-tier baseline text — no scratch generation.""" | |
| from __future__ import annotations | |
| import logging | |
| from backend.config import settings | |
| from backend.core.composition_output import ( | |
| accept_narrative_section_output, | |
| sanitize_section_prose, | |
| ) | |
| from backend.core.paragraph_merge import merge_observations_into_paragraph | |
| from backend.core.paragraph_retriever import InterferenceLevel | |
| from backend.llm import openai_client | |
| from backend.models.schema import TemplateSchema | |
| from backend.prompts.mapping_prompt import ( | |
| FACT_GROUNDING_RULES, | |
| MAPPING_SYSTEM_BASE_MAXIMUM, | |
| MAPPING_SYSTEM_BASE_MEDIUM, | |
| MAPPING_SYSTEM_BASE_MINIMUM, | |
| MAPPING_USER_TEMPLATE, | |
| RATING_HINT_TEMPLATE, | |
| RICS_DOMAIN_RULES, | |
| _observations_bulleted, | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # Relocated verbatim to backend/prompts/mapping_prompt.py (test-exempt). Aliased | |
| # here to preserve the existing internal/private reference and import surface. | |
| _RICS_DOMAIN_RULES = RICS_DOMAIN_RULES | |
| def select_mapping_prompt(interference_level: str) -> str: | |
| """Return the tier-specific mapping system prompt base for the given level.""" | |
| level = (interference_level or "maximum").strip().lower() | |
| if level == "minimum": | |
| return MAPPING_SYSTEM_BASE_MINIMUM | |
| if level == "medium": | |
| return MAPPING_SYSTEM_BASE_MEDIUM | |
| return MAPPING_SYSTEM_BASE_MAXIMUM | |
| def _normalize_interference_level( | |
| interference_level: str | InterferenceLevel | None, | |
| ) -> InterferenceLevel: | |
| """Map caller input to a supported composition tier; default maximum.""" | |
| raw = str(interference_level or "").strip().lower() | |
| if raw in ("minimum", "medium", "maximum"): | |
| return raw # type: ignore[return-value] | |
| return "maximum" | |
| def compose_mapping_system_prompt( | |
| interference_level: str | InterferenceLevel | None, | |
| ) -> str: | |
| """Full mapping system prompt: tier base + shared RICS domain rules.""" | |
| level = _normalize_interference_level(interference_level) | |
| return ( | |
| select_mapping_prompt(level).strip() | |
| + "\n\n" | |
| + FACT_GROUNDING_RULES.strip() | |
| + "\n\n" | |
| + _RICS_DOMAIN_RULES.strip() | |
| ) | |
| def build_interference_messages( | |
| interference_level: str | InterferenceLevel | None, | |
| *, | |
| observations: list[str], | |
| baseline: str, | |
| schema: TemplateSchema, | |
| section_id: str = "", | |
| section_title: str = "", | |
| rating_value: str | None = None, | |
| extra_references: list[str] | None = None, | |
| ) -> list[dict[str, str]]: | |
| """Select the prompt builder for minimum / medium / maximum AI involvement.""" | |
| level = _normalize_interference_level(interference_level) | |
| baseline_text = baseline.strip() | |
| if extra_references: | |
| extras = "\n\n".join(item.strip() for item in extra_references if item.strip()) | |
| if extras: | |
| baseline_text = f"{baseline_text}\n\n{extras}".strip() | |
| rating_line = "" | |
| if schema.rating_system.detected and rating_value: | |
| rating_line = RATING_HINT_TEMPLATE.format(rating_value=rating_value) | |
| system = compose_mapping_system_prompt(level) | |
| user = MAPPING_USER_TEMPLATE.format( | |
| section_id=section_id or "—", | |
| section_label=section_title or section_id or "—", | |
| rating_line=rating_line, | |
| first_reference_baseline_paragraph=baseline_text or "(none)", | |
| observations_bulleted=_observations_bulleted(observations), | |
| ) | |
| return [ | |
| {"role": "system", "content": system.strip()}, | |
| {"role": "user", "content": user.strip()}, | |
| ] | |
| def map_inplace_baseline( | |
| baseline_text: str, | |
| observations: list[str], | |
| schema: TemplateSchema, | |
| *, | |
| section_id: str = "", | |
| section_title: str = "", | |
| rating_value: str | None = None, | |
| messages: list[dict[str, str]] | None = None, | |
| ) -> str: | |
| """Apply in-place fact updates to the retrieved past-report baseline only.""" | |
| baseline = (baseline_text or "").strip() | |
| if not baseline: | |
| return "" | |
| if not observations: | |
| return baseline | |
| merged = merge_observations_into_paragraph(baseline, observations, schema) | |
| if settings.use_llm_paragraph_mapping and openai_client.is_available(): | |
| llm_messages = messages or build_interference_messages( | |
| "maximum", | |
| observations=observations, | |
| baseline=baseline, | |
| schema=schema, | |
| section_id=section_id, | |
| section_title=section_title, | |
| rating_value=rating_value, | |
| ) | |
| try: | |
| out = openai_client.chat_text( | |
| llm_messages, | |
| model=settings.mapping_model, | |
| max_tokens=settings.max_tokens_mapping, | |
| temperature=0.0, | |
| ) | |
| text = sanitize_section_prose((out or "").strip()) | |
| if accept_narrative_section_output(text, observations): | |
| return text | |
| except Exception as exc: # noqa: BLE001 | |
| logger.warning( | |
| "In-place LLM mapping failed (%s); using deterministic merge.", exc | |
| ) | |
| return merged | |
| def map_reference_paragraph( | |
| reference_paragraph: str, | |
| observations: list[str], | |
| schema: TemplateSchema, | |
| interference_level: InterferenceLevel, | |
| *, | |
| section_id: str = "", | |
| section_title: str = "", | |
| rating_value: str | None = None, | |
| extra_references: list[str] | None = None, | |
| ) -> str: | |
| """Map notes onto the assembled REFERENCE baseline using tier-specific prompts.""" | |
| level = _normalize_interference_level(interference_level) | |
| messages = build_interference_messages( | |
| level, | |
| observations=observations, | |
| baseline=reference_paragraph, | |
| schema=schema, | |
| section_id=section_id, | |
| section_title=section_title, | |
| rating_value=rating_value, | |
| extra_references=extra_references, | |
| ) | |
| return map_inplace_baseline( | |
| reference_paragraph, | |
| observations, | |
| schema, | |
| section_id=section_id, | |
| section_title=section_title, | |
| rating_value=rating_value, | |
| messages=messages, | |
| ) | |