FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
40 lines
1.4 KiB
Python
40 lines
1.4 KiB
Python
"""Shared helpers for building prompts and (for MockLLMProvider) recovering
|
|
the structured evidence a prompt was built from, without a real model call.
|
|
Every analysis task embeds its evidence as a fenced JSON block via
|
|
`build_user_prompt`, so this stays consistent across all six tasks and lets
|
|
the mock provider parse it back out deterministically.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
_EVIDENCE_FENCE_START = "```json evidence"
|
|
_EVIDENCE_FENCE_END = "```"
|
|
|
|
|
|
def build_user_prompt(instructions: str, evidence: dict[str, Any]) -> str:
|
|
evidence_json = json.dumps(evidence, indent=2, default=str)
|
|
return (
|
|
f"{instructions}\n\n"
|
|
"Evidence (only use what is provided here - never invent facts not present):\n"
|
|
f"{_EVIDENCE_FENCE_START}\n{evidence_json}\n{_EVIDENCE_FENCE_END}"
|
|
)
|
|
|
|
|
|
def extract_evidence_block(user_prompt: str) -> dict[str, Any]:
|
|
"""Recovers the evidence dict embedded by `build_user_prompt`. Used only
|
|
by MockLLMProvider, which has no model to actually read the prompt."""
|
|
start = user_prompt.find(_EVIDENCE_FENCE_START)
|
|
if start == -1:
|
|
return {}
|
|
start += len(_EVIDENCE_FENCE_START)
|
|
end = user_prompt.find(_EVIDENCE_FENCE_END, start)
|
|
if end == -1:
|
|
return {}
|
|
try:
|
|
return json.loads(user_prompt[start:end].strip())
|
|
except json.JSONDecodeError:
|
|
return {}
|