Files
saksham 1a4c80958f Initial commit: CI Agent competitive-intelligence monitoring app
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).
2026-08-05 10:48:20 -04:00

328 lines
13 KiB
Python

"""Deterministic mock provider - the default (`LLM_PROVIDER=mock`) and what
every automated test runs against. Never calls a network or costs money.
Rather than a generic reflection-based filler, each of the six analysis
tasks gets a purpose-built, deterministic builder that reads the same
evidence block a real model would see (see app/prompts/base.py) and
produces genuinely useful output from it - real counts, real titles, real
severities - never fabricated facts. This is what makes the fixture demo
(Phase 9) work end-to-end without a paid API key.
"""
from __future__ import annotations
import re
from typing import Any
from pydantic import BaseModel
from app.prompts.alert_summarization import AlertSummary
from app.prompts.base import extract_evidence_block
from app.prompts.change_significance import ChangeSignificanceAssessment
from app.prompts.company_profile import CompanyProfileExtraction
from app.prompts.extraction import ExtractionResult
from app.prompts.relevance import RelevanceAssessment
from app.prompts.report_generation import ReportContent
from app.prompts.synthesis import SynthesisResult
def _confidence_label(score: float) -> str:
if score >= 0.85:
return "confirmed"
if score >= 0.65:
return "strongly_indicated"
if score >= 0.45:
return "likely"
if score >= 0.25:
return "possible"
if score > 0:
return "unconfirmed"
return "insufficient_evidence"
def _build_relevance(evidence: dict[str, Any]) -> dict[str, Any]:
text = (evidence.get("document_text") or "").lower()
focus = (evidence.get("monitoring_focus") or "").lower()
focus_words = [w for w in focus.split() if len(w) > 4]
matches_focus = any(w in text for w in focus_words) if focus_words else False
return {
"is_relevant": True,
"matches_focus": matches_focus,
"topic_categories": [],
"source_reliability": 0.7,
"reasoning": "Mock provider: keyword-based heuristic (no live LLM configured).",
}
def _build_extraction(evidence: dict[str, Any]) -> dict[str, Any]:
text = (evidence.get("document_text") or "").strip()
if not text:
return {"signals": []}
first_sentence = text.split(".")[0][:200].strip()
if not first_sentence:
return {"signals": []}
return {
"signals": [
{
"signal_type": "event",
"description": first_sentence,
"supporting_passage": first_sentence,
"date": None,
"entities": [],
}
]
}
def _build_synthesis(evidence: dict[str, Any]) -> dict[str, Any]:
signals = evidence.get("signals") or []
if len(signals) < 2:
return {"conclusions": []}
return {
"conclusions": [
{
"conclusion": (
"Multiple related signals were detected together; the mock provider "
"does not attempt fine-grained synthesis - configure a live LLM provider "
"for a specific conclusion."
),
"evidence_summary": [s.get("description", "") for s in signals[:5]],
"source_count": len(signals),
"confidence": 0.3,
"alternative_explanations": [
"A live LLM provider would assess this more precisely."
],
"missing_information": [],
}
]
}
def _build_report(evidence: dict[str, Any]) -> dict[str, Any]:
profile = evidence.get("company_profile") or {}
company_name = profile.get("name") or "The company"
documents = evidence.get("source_documents") or []
changes = evidence.get("detected_changes") or []
failed = evidence.get("sources_that_failed_to_collect") or []
# The discovered profile (real, from onboarding) is genuine evidence even
# when no monitoring run has collected source_documents/detected_changes
# yet - build company_overview/market_positioning from it honestly
# rather than defaulting straight to "insufficient evidence".
overview_parts = []
if profile.get("description"):
overview_parts.append(profile["description"])
facts = []
if profile.get("industry"):
facts.append(f"industry: {profile['industry']}")
if profile.get("headquarters"):
facts.append(f"headquartered in {profile['headquarters']}")
elif profile.get("country") or profile.get("region"):
facts.append(
f"based in {', '.join(f for f in (profile.get('country'), profile.get('region')) if f)}"
)
if profile.get("aliases"):
facts.append(f"also known as {', '.join(profile['aliases'])}")
if facts:
overview_parts.append(f"{company_name} ({'; '.join(facts)}).")
company_overview = " ".join(overview_parts) or f"No description on file for {company_name}."
if profile.get("competitors"):
market_positioning = (
f"{company_name} operates in a space that includes "
f"{', '.join(profile['competitors'])} as named competitors, per the discovered "
"company profile. No comparative data (pricing, features, market share) is "
"available to assess relative positioning."
)
else:
market_positioning = "Insufficient evidence to assess market positioning."
executive_summary = (
f"Mock analysis (LLM_PROVIDER=mock) based on {len(documents)} collected document(s) "
f"and {len(changes)} detected change(s) for {company_name}."
)
if failed:
executive_summary += (
f" {len(failed)} source(s) failed to collect this run and are excluded below."
)
recent_developments = [
{
"headline": change.get("summary") or "Change detected",
"summary": (
f"{(change.get('change_type') or 'change').replace('_', ' ')} detected "
f"with {change.get('severity') or 'unknown'} severity."
),
"evidence": [
{
"detected_change_id": change.get("id"),
"description": change.get("summary") or "",
}
],
"confidence": _confidence_label(change.get("confidence_score") or 0.5),
"category": change.get("change_type"),
"date": change.get("created_at"),
}
for change in changes[:10]
]
hiring_signals = [
{
"headline": doc.get("title") or doc.get("url") or "Job posting",
"summary": (doc.get("excerpt") or "")[:280],
"evidence": [
{
"source_document_id": doc.get("id"),
"url": doc.get("url"),
"description": "Collected source document",
}
],
"confidence": "confirmed",
"category": "job_posting",
"date": doc.get("retrieved_date"),
}
for doc in documents
if doc.get("source_type") == "job_posting"
]
unknowns = (
[f"{len(failed)} source(s) failed to collect this run: {', '.join(failed[:5])}"]
if failed
else []
)
return {
"executive_summary": executive_summary,
"company_overview": company_overview,
"products_and_services": [],
"market_positioning": market_positioning,
"recent_developments": recent_developments,
"strategic_initiatives": [],
"key_inferred_projects": [],
"leadership_changes": [
d for d in recent_developments if d["category"] == "leadership_change"
],
"hiring_signals": hiring_signals,
"technology_signals": [],
"patent_signals": [],
"manufacturing_and_expansion_signals": [],
"partnerships_and_acquisitions": [],
"financial_signals": [d for d in recent_developments if d["category"] == "filing_new"],
"regulatory_and_legal_signals": [],
"customer_sentiment": "Insufficient evidence to assess customer sentiment.",
"competitor_comparison": "Insufficient evidence to compare against competitors.",
"swot": {"strengths": [], "weaknesses": [], "opportunities": [], "threats": []},
"risks": [],
"opportunities": [],
"unknowns_and_missing_data": unknowns,
"monitoring_recommendations": ["Continue monitoring configured sources on schedule."],
"methodology": (
f"Generated by the mock LLM provider from {len(documents)} stored source "
f"document(s) and {len(changes)} deterministic change-detection result(s). No "
"external model was called."
),
"limitations": (
"Generated by the deterministic mock provider, not a live LLM. Set "
"LLM_PROVIDER=anthropic or LLM_PROVIDER=ollama for narrative synthesis."
),
}
def _build_change_significance(evidence: dict[str, Any]) -> dict[str, Any]:
severity = evidence.get("deterministic_severity") or "low"
confidence = evidence.get("deterministic_confidence") or 0.5
is_meaningful = severity in ("critical", "high", "medium")
change_type = (evidence.get("change_type") or "change").replace("_", " ")
return {
"is_real_change": True,
"is_meaningful": is_meaningful,
"why_it_matters": (
f"Deterministic scoring classified this {change_type} as {severity} severity "
f"with {confidence:.0%} confidence."
),
"confidence": confidence,
"should_notify": is_meaningful,
}
def _build_alert_summary(evidence: dict[str, Any]) -> dict[str, Any]:
company = evidence.get("company_name") or "The company"
change_type = (evidence.get("change_type") or "change").replace("_", " ")
severity = evidence.get("severity") or "medium"
confidence = evidence.get("confidence") or 0.5
return {
"title": f"{company}: {change_type} detected"[:100],
"summary": evidence.get("change_summary") or f"A {change_type} was detected for {company}.",
"why_it_matters": f"Classified as {severity} severity with {confidence:.0%} confidence.",
}
_HQ_RE = re.compile(r"(?:headquartered|based) in ([A-Z][\w\s,]{2,60}?)(?:[.\n]|$)", re.IGNORECASE)
_FORMERLY_RE = re.compile(
r"formerly (?:known as|named) ([A-Z][\w&\s]{2,60}?)(?:[.,\n]|$)", re.IGNORECASE
)
def _build_company_profile(evidence: dict[str, Any]) -> dict[str, Any]:
"""Mirrors app/change_detection/extractors.py's philosophy: cheap,
deterministic regex heuristics over real evidence text, never a
fabricated guess. Most fields (industry/country/region/public
identifiers) stay empty since a name-only mock search has no real
signal for them - see search/mock.py's docstring for why that's
intentional, not a gap."""
homepage_text = evidence.get("homepage_text") or ""
search_results = evidence.get("search_results") or []
combined_text = homepage_text + "\n" + "\n".join(r.get("snippet", "") for r in search_results)
hq_match = _HQ_RE.search(combined_text)
headquarters = hq_match.group(1).strip() if hq_match else None
alias_match = _FORMERLY_RE.search(combined_text)
aliases = [alias_match.group(1).strip()] if alias_match else []
# First real sentence of the fetched homepage, if any - an honest,
# evidence-derived summary rather than a fabricated one.
first_sentence = re.split(r"(?<=[.!?])\s", homepage_text.strip(), maxsplit=1)[0].strip()
description = first_sentence[:280] if first_sentence and len(first_sentence) > 15 else None
return {
"description": description,
"industry": None,
"country": None,
"region": None,
"headquarters": headquarters,
"aliases": aliases,
"competitors": [],
"public_identifiers": [],
}
_BUILDERS = {
RelevanceAssessment: _build_relevance,
ExtractionResult: _build_extraction,
SynthesisResult: _build_synthesis,
ReportContent: _build_report,
ChangeSignificanceAssessment: _build_change_significance,
AlertSummary: _build_alert_summary,
CompanyProfileExtraction: _build_company_profile,
}
class MockLLMProvider:
provider_name = "mock"
async def generate_structured[T: BaseModel](
self, system_prompt: str, user_prompt: str, response_model: type[T]
) -> T:
evidence = extract_evidence_block(user_prompt)
builder = _BUILDERS.get(response_model)
data = builder(evidence) if builder is not None else {}
return response_model.model_validate(data)
async def generate_text(self, system_prompt: str, user_prompt: str) -> str:
evidence = extract_evidence_block(user_prompt)
return (
"[mock provider] No live LLM configured. "
f"{len(evidence)} evidence field(s) were provided for this request."
)