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.
44 lines
1.7 KiB
Python
44 lines
1.7 KiB
Python
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from ai_platform.bootstrap.app import create_app
|
|
from ai_platform.infrastructure.config.settings import Settings
|
|
from ai_platform.presentation.http.run_responses import ScoreResponse
|
|
from ai_platform.presentation.http.schemas import RunRequest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
CONTRACT = ROOT / "contracts/lead-scoring"
|
|
|
|
|
|
def test_committed_openapi_and_fixtures_match_the_code():
|
|
assert json.loads((CONTRACT / "openapi.json").read_text()) == create_app(Settings()).openapi()
|
|
ScoreResponse.model_validate_json((CONTRACT / "fixtures/score-response.json").read_text())
|
|
RunRequest.model_validate_json((CONTRACT / "fixtures/run-request.json").read_text())
|
|
subprocess.run(
|
|
[sys.executable, "scripts/export_contract_fixtures.py", "--check"], cwd=ROOT, check=True
|
|
)
|
|
|
|
|
|
def test_score_operation_is_documented_with_neutral_headers():
|
|
openapi = create_app(Settings()).openapi()
|
|
operation = openapi["paths"]["/v1/lead-scoring/score"]["post"]
|
|
headers = {p["name"] for p in operation["parameters"]}
|
|
assert operation["security"] == [{"HTTPBearer": []}]
|
|
assert {"Idempotency-Key", "X-AI-Key"} <= headers
|
|
assert {"200", "401", "413", "422", "503"} <= set(operation["responses"])
|
|
assert operation["responses"]["200"]["content"]["application/json"]["schema"] == {
|
|
"$ref": "#/components/schemas/ScoreResponse"
|
|
}
|
|
paths = set(openapi["paths"])
|
|
assert {
|
|
"/v1/admin/ai-profiles",
|
|
"/v1/admin/ai-profiles/try",
|
|
"/v1/admin/ai-profiles/{revision}/activate",
|
|
"/v1/admin/runs",
|
|
"/v1/admin/runs/{run_id}",
|
|
"/v1/admin/apps",
|
|
} <= paths
|
|
assert "/v1/lead-scoring/runs/{run_id}" not in paths
|