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

1.9 KiB

Lead Scoring: client contract

openapi.json is generated from the backend routes (make contract). fixtures/run-request.json and fixtures/score-response.json are generated examples; scripts/export_contract_fixtures.py --check rejects drift. The service knows no specific client: client-owned data travels in metadata.

Request

POST /v1/lead-scoring/score, synchronous.

Header Meaning
Authorization: Bearer <app_key> App credential (one per installation). 401 if invalid or revoked, 403 without the lead_scoring permission.
Idempotency-Key Required UUID chosen by the client. Same key with the same body replays the stored result; same key with a different body returns 422.
X-AI-Key Optional AI provider key for AIHub profiles. Held in memory for the call only, never stored or logged. Max 4096 chars, no CR/LF (else 422).

Body: snapshot (lead profile with external_id, conversations, custom fields), scoring_config (rule_weight, ai_weight, rules[]) and optional metadata (any JSON object up to 10KB, stored and returned unchanged). Rule path may be lead_profile.<name|phone|email|status|external_id>, custom_fields.<key> or metrics.<key>.

Response (200)

run_id, final_score, rule_score, ai_score, result_mode, error_code, breakdown[], ai {summary, suggested_action, signals[]}, metrics, metadata.

result_mode is rules_only, hybrid or rules_only_fallback. In the fallback case error_code says why (ai_timeout, ai_key_missing, ai_profile_missing, or a provider error code) and final_score equals rule_score. The AI model, prompt and timeout belong to the active AI profile, not the request.

Boundaries

No webhook: the caller waits for the response (the AI deadline is the profile timeout_seconds, default 50). Provider tests do not prove client persistence or writeback; verify both sides with a mock AI before enabling a real integration.