POST /v1/lead-scoring/score now evaluates in the request, applies an overall AI deadline, and stores the completed run only after scoring. Idempotency is kept by (app_id, account_id, request_id) with a body fingerprint. Removes the webhook outbox, scheduler/recovery loop, app lead_scoring permission, credential audit events, seed_demo and generate_secret scripts, and the tests that covered them. Trims tests to behavior of the new flow.
76 lines
2.5 KiB
Python
76 lines
2.5 KiB
Python
"""Generate response examples from provider serializers."""
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
|
|
from ai_platform.lead_scoring.application.repository import ScoringRunRecord
|
|
from ai_platform.lead_scoring.application.run_use_cases import to_run_detail
|
|
from ai_platform.lead_scoring.application.scoring import calculate_run
|
|
from ai_platform.presentation.http.routes import to_detail_json, to_json
|
|
from ai_platform.presentation.http.run_responses import RunDetail, RunSummary
|
|
from ai_platform.presentation.http.schemas import RunRequest
|
|
|
|
FIXTURES = Path(__file__).resolve().parents[1] / "contracts/lead-scoring/v2/fixtures"
|
|
NOW = datetime(2026, 9, 28, tzinfo=UTC)
|
|
RUN_ID = "22222222-2222-4222-8222-222222222222"
|
|
|
|
|
|
def write_json(name: str, value: dict, *, check: bool) -> None:
|
|
path = FIXTURES / name
|
|
expected = json.dumps(value, ensure_ascii=False, indent=2) + "\n"
|
|
if check:
|
|
if not path.exists() or path.read_text() != expected:
|
|
raise SystemExit(f"Contract fixture drift: {name}; regenerate provider examples")
|
|
else:
|
|
path.write_text(expected)
|
|
|
|
|
|
async def main(*, check: bool = False) -> None:
|
|
request = RunRequest.model_validate_json(
|
|
(FIXTURES / "run-request.json").read_text()
|
|
).model_dump(mode="json")
|
|
rule_score, breakdown = calculate_run(request)
|
|
row = ScoringRunRecord(
|
|
id=RUN_ID,
|
|
request_id=request["request_id"],
|
|
app_id="omni-local",
|
|
account_id=request["account_id"],
|
|
status="completed",
|
|
fingerprint="0" * 64,
|
|
profile_version="profile-example",
|
|
request=request,
|
|
rule_score=rule_score,
|
|
ai_score=None,
|
|
final_score=rule_score,
|
|
degraded=False,
|
|
result_mode="rules_only",
|
|
error_code=None,
|
|
breakdown=breakdown,
|
|
created_at=NOW,
|
|
completed_at=NOW,
|
|
ai_confidence=None,
|
|
ai_reasoning=None,
|
|
ai_evidence=None,
|
|
)
|
|
write_json(
|
|
"run-summary.json",
|
|
RunSummary.model_validate(to_json(row)).model_dump(mode="json"),
|
|
check=check,
|
|
)
|
|
write_json(
|
|
"run-detail.json",
|
|
RunDetail.model_validate(to_detail_json(to_run_detail(row))).model_dump(mode="json"),
|
|
check=check,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--check", action="store_true", help="Reject drift without writing artifacts"
|
|
)
|
|
asyncio.run(main(check=parser.parse_args().check))
|