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.
57 lines
2.3 KiB
Python
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)
|