dotb_ai-platform/scripts/export_contract_fixtures.py
hungtran24 6577bb74be refactor: simplify scoring service, AI profile revisions and test suite
Phase 2 and 3 of the platform rework.

- Neutral request/response contract: external_id, metadata, X-AI-Key,
  run_id in the response; client-specific fields removed.
- AI profile is a revision table: saving creates an inactive revision,
  activation is separate, try-run does not store a run. One
  chat-completions gateway serves AIHub and OpenAI-compatible profiles.
- Add between and in rule operators.
- Contracts moved to contracts/lead-scoring/ with a score-response fixture.
- Admin Web updated for revisions, runs and the neutral fixture.
- Consolidate tests; remove rewrite_architect.md and the stale handoff doc;
  rewrite spec, CONTEXT and ARCHITECTURE for the current design.

Not verified: live AIHub/OpenAI behavior, Omni/EMS integration.
2026-10-09 08:23:21 +07:00

57 lines
2.3 KiB
Python

"""Generate the response example from the same mapping the API uses."""
import argparse
import json
from pathlib import Path
from ai_platform.lead_scoring.application.repository import ScoringResult
from ai_platform.lead_scoring.application.scoring import calculate_run
from ai_platform.presentation.http.run_mapping import score_json
from ai_platform.presentation.http.run_responses import ScoreResponse
from ai_platform.presentation.http.schemas import RunRequest
FIXTURES = Path(__file__).resolve().parents[1] / "contracts/lead-scoring/fixtures"
RUN_ID = "22222222-2222-4222-8222-222222222222"
def main(*, check: bool = False) -> None:
request = RunRequest.model_validate_json((FIXTURES / "run-request.json").read_text())
payload = request.model_dump(mode="json")
rule_score, breakdown = calculate_run(payload)
# Illustrative hybrid result: the AI block is hand-written, not produced by a model.
ai = {
"summary": "Khách hỏi lịch khai giảng và mục tiêu điểm, phản hồi nhanh.",
"suggested_action": "Gửi lịch khai giảng và hẹn gọi tư vấn trong ngày.",
"signals": [
{
"polarity": "positive",
"signal": "Hỏi lịch khai giảng",
"detail": "Khách chủ động hỏi tháng tới có lớp khai giảng không.",
"source": "message-789",
}
],
}
result = ScoringResult(
rule_score=rule_score,
breakdown=breakdown,
ai_score=80,
ai=ai,
final_score=round((rule_score + 80) / 2),
result_mode="hybrid",
error_code=None,
)
response = ScoreResponse.model_validate(score_json(RUN_ID, payload, result))
expected = json.dumps(response.model_dump(mode="json"), ensure_ascii=False, indent=2) + "\n"
path = FIXTURES / "score-response.json"
if check:
if not path.exists() or path.read_text() != expected:
raise SystemExit("Contract fixture drift: score-response.json; run `make contract`")
else:
path.write_text(expected)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check", action="store_true", help="Fail on drift without writing")
main(check=parser.parse_args().check)