Download engine/test_serve.py from decisionmodels/REAX-1.3: direct link, hf CLI and curl.
- Browser
- Download file 5.93 kB
-
https://huggingface.co/decisionmodels/REAX-1.3/resolve/main/engine/test_serve.py
- Command line
-
hf download hf://decisionmodels/REAX-1.3/engine/test_serve.py
-
curl -L -o test_serve.py https://huggingface.co/decisionmodels/REAX-1.3/resolve/main/engine/test_serve.py
5.93 kB
| # SPDX-License-Identifier: MIT | |
| import json | |
| import threading | |
| import unittest | |
| from urllib.error import HTTPError | |
| from urllib.request import Request, urlopen | |
| from probabilities import mix | |
| from serve import EngineService, RequestError, ResponseError, build_server | |
| class MockBackend: | |
| """CPU-only stand-in for Reax13Engine's native G/Q backend responses.""" | |
| def __init__(self): | |
| self.calls = [] | |
| def __call__(self, state, questions): | |
| self.calls.append((state, questions)) | |
| g = {"answers": {}} | |
| q = {"answers": {}} | |
| for qid, spec in questions.items(): | |
| if spec["type"] == "choice": | |
| keys = list(spec["criteria"]) | |
| g["answers"][qid] = {"type": "choice", "probabilities": dict(zip(keys, [0.7, 0.3]))} | |
| q["answers"][qid] = {"type": "choice", "probabilities": dict(zip(keys, [0.3, 0.7]))} | |
| elif spec["type"] == "noul": | |
| g["answers"][qid] = {"type": "noul", "noul": 0.2} | |
| q["answers"][qid] = {"type": "noul", "noul": 0.8} | |
| else: | |
| keys = [str(i) for i in range(len(spec["criteria"]))] | |
| g["answers"][qid] = {"type": "score", "probabilities": dict(zip(keys, [0.2, 0.3, 0.5]))} | |
| q["answers"][qid] = {"type": "score", "probabilities": dict(zip(keys, [0.5, 0.25, 0.25]))} | |
| return mix({"questions": questions}, g, q), {"mock": True} | |
| class BadResponseEngine: | |
| def __call__(self, state, questions): | |
| return {"model": "reax13", "answers": {"q": { | |
| "type": "choice", "choice": "a", "probabilities": {"a": 0.2, "b": 0.2}, | |
| }}}, None | |
| class ServeContractTests(unittest.TestCase): | |
| def setUp(self): | |
| self.questions = { | |
| "pick": {"type": "choice", "instructions": "Pick one", "criteria": {"a": "A", "b": "B"}}, | |
| "certain": {"type": "noul", "instructions": "Is this true?"}, | |
| "rating": {"type": "score", "instructions": "Rate it", "criteria": ["low", "mid", "high"]}, | |
| } | |
| self.backend = MockBackend() | |
| self.service = EngineService(self.backend) | |
| def test_joint_request_all_question_types_and_normalized_answers(self): | |
| response = self.service.infer({"state": {"case": "example"}, "questions": self.questions}) | |
| answers = response["answers"] | |
| self.assertEqual(set(answers), set(self.questions)) | |
| self.assertEqual(answers["pick"]["choice"], "a") | |
| self.assertAlmostEqual(sum(answers["pick"]["probabilities"].values()), 1.0) | |
| self.assertEqual(answers["certain"]["noul"], 0.5) | |
| self.assertEqual(answers["certain"]["probabilities"], {"false": 0.5, "true": 0.5}) | |
| score = answers["rating"] | |
| self.assertAlmostEqual(sum(score["probabilities"].values()), 1.0) | |
| expected = sum(int(k) * p for k, p in score["probabilities"].items()) | |
| self.assertAlmostEqual(score["score"], expected) | |
| self.assertEqual(len(self.backend.calls), 1) | |
| def test_one_question_per_request(self): | |
| request = {"state": "short state", "questions": {"only": self.questions["pick"]}} | |
| answer = self.service.infer(request)["answers"]["only"] | |
| self.assertEqual(answer["type"], "choice") | |
| self.assertEqual(set(answer["probabilities"]), {"a", "b"}) | |
| def test_invalid_request_rejected_before_backend(self): | |
| for question_type in ("other", ["choice"]): | |
| with self.subTest(question_type=question_type): | |
| with self.assertRaises(RequestError): | |
| self.service.infer({"state": "x", "questions": {"q": {"type": question_type}}}) | |
| self.assertEqual(self.backend.calls, []) | |
| def test_incomplete_or_unnormalized_engine_response_rejected(self): | |
| service = EngineService(BadResponseEngine()) | |
| with self.assertRaisesRegex(ResponseError, "sum to 1"): | |
| service.infer({"state": "x", "questions": {"q": self.questions["pick"]}}) | |
| def test_http_health_and_post_contract(self): | |
| server = build_server(self.backend, host="127.0.0.1", port=0) | |
| thread = threading.Thread(target=server.serve_forever, daemon=True) | |
| thread.start() | |
| base = f"http://127.0.0.1:{server.server_address[1]}" | |
| try: | |
| with urlopen(base + "/healthz", timeout=2) as reply: | |
| self.assertEqual(json.load(reply), {"status": "ready", "model": "reax13", "mode": "reax13"}) | |
| body = json.dumps({"state": "test", "questions": self.questions}).encode() | |
| request = Request(base + "/v1/systemone", data=body, headers={"Content-Type": "application/json"}) | |
| with urlopen(request, timeout=2) as reply: | |
| self.assertEqual(reply.status, 200) | |
| response = json.load(reply) | |
| self.assertEqual(set(response["answers"]), set(self.questions)) | |
| self.assertEqual(response["answers"]["certain"]["probabilities"], {"false": 0.5, "true": 0.5}) | |
| finally: | |
| server.shutdown() | |
| server.server_close() | |
| thread.join(timeout=2) | |
| def test_http_rejects_invalid_schema(self): | |
| server = build_server(self.backend, host="127.0.0.1", port=0) | |
| thread = threading.Thread(target=server.serve_forever, daemon=True) | |
| thread.start() | |
| base = f"http://127.0.0.1:{server.server_address[1]}" | |
| try: | |
| request = Request(base + "/v1/systemone", data=b'{"questions":{}}', headers={"Content-Type": "application/json"}) | |
| with self.assertRaises(HTTPError) as caught: | |
| urlopen(request, timeout=2) | |
| self.assertEqual(caught.exception.code, 422) | |
| error = json.load(caught.exception) | |
| self.assertEqual(error["error"]["code"], "invalid_request") | |
| finally: | |
| server.shutdown() | |
| server.server_close() | |
| thread.join(timeout=2) | |
| if __name__ == "__main__": | |
| unittest.main() | |