Download runtime/jevlike/client.py from Cem13/kodama-core: direct link, hf CLI and curl.
- Browser
- Download file 4.3 kB
-
https://huggingface.co/Cem13/kodama-core/resolve/main/runtime/jevlike/client.py
- Command line
-
hf download hf://Cem13/kodama-core/runtime/jevlike/client.py
-
curl -L -o client.py https://huggingface.co/Cem13/kodama-core/resolve/main/runtime/jevlike/client.py
4.3 kB
| """Tiny client for `python -m jevlike.server` (stdlib only). | |
| from jevlike import Choice, Score, Noul | |
| from jevlike.client import SystemOneClient | |
| c = SystemOneClient("http://127.0.0.1:8077") | |
| res = c.predict("My invoice is wrong and I'm cancelling!", { | |
| "dept": Choice("Which team?", {"billing": "invoices, refunds", "technical": "bugs, outages"}), | |
| "urgency": Score("How urgent?", ["not urgent", "soon", "blocking"]), | |
| "churn": Noul("The user threatens to cancel"), | |
| }) | |
| res["answers"]["dept"]["choice"] | |
| Answers have exactly the shapes of `SystemOne.predict` (see SPEC.md). Questions may also be given | |
| as plain wire dicts ({"type": ..., "instructions": ..., "criteria": ...}). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import urllib.error | |
| import urllib.request | |
| from typing import Any, Optional, Union | |
| from jevlike.types import Choice, Noul, Score | |
| QuestionLike = Union[Choice, Score, Noul, dict] | |
| class SystemOneError(RuntimeError): | |
| def __init__(self, status: int, detail: Any): | |
| self.status, self.detail = status, detail | |
| super().__init__(f"HTTP {status}: {detail}") | |
| def question_to_wire(q: QuestionLike) -> dict: | |
| if isinstance(q, dict): | |
| return q | |
| if isinstance(q, Choice): | |
| crit = dict(q.criteria) if isinstance(q.criteria, dict) else list(q.criteria) | |
| return {"type": "choice", "instructions": q.instructions, "criteria": crit} | |
| if isinstance(q, Score): | |
| return {"type": "score", "instructions": q.instructions, "criteria": list(q.criteria)} | |
| if isinstance(q, Noul): | |
| return {"type": "noul", "instructions": q.instructions} | |
| raise TypeError(f"not a question: {type(q).__name__}") | |
| def questions_to_wire(questions: dict[str, QuestionLike]) -> dict[str, dict]: | |
| return {k: question_to_wire(q) for k, q in questions.items()} | |
| class _Response: | |
| def __init__(self, status_code: int, body: bytes): | |
| self.status_code, self._body = status_code, body | |
| def json(self): | |
| return json.loads(self._body.decode("utf-8")) if self._body else None | |
| class _UrllibHTTP: | |
| def __init__(self, timeout: float): | |
| self.timeout = timeout | |
| def _send(self, req: urllib.request.Request) -> _Response: | |
| try: | |
| with urllib.request.urlopen(req, timeout=self.timeout) as r: | |
| return _Response(r.status, r.read()) | |
| except urllib.error.HTTPError as e: | |
| return _Response(e.code, e.read()) | |
| def get(self, url: str) -> _Response: | |
| return self._send(urllib.request.Request(url, method="GET")) | |
| def post(self, url: str, json: Any) -> _Response: # noqa: A002 - mirrors httpx | |
| data = _json_dumps(json).encode("utf-8") | |
| return self._send(urllib.request.Request(url, data=data, method="POST", | |
| headers={"Content-Type": "application/json"})) | |
| def _json_dumps(x) -> str: | |
| return json.dumps(x, ensure_ascii=False) | |
| class SystemOneClient: | |
| """`http` may be any object with httpx-style `get(url)` / `post(url, json=...)` (e.g. an | |
| httpx.Client or FastAPI's TestClient); by default a urllib-based one is used.""" | |
| def __init__(self, base_url: str = "http://127.0.0.1:8077", timeout: float = 60.0, http: Optional[Any] = None): | |
| self.base_url = base_url.rstrip("/") | |
| self.http = http if http is not None else _UrllibHTTP(timeout) | |
| def _check(self, r) -> Any: | |
| body = r.json() | |
| if r.status_code >= 400: | |
| raise SystemOneError(r.status_code, body.get("detail", body) if isinstance(body, dict) else body) | |
| return body | |
| def health(self) -> dict: | |
| return self._check(self.http.get(f"{self.base_url}/health")) | |
| def predict(self, state: Union[str, dict, list], questions: dict[str, QuestionLike]) -> dict: | |
| body = {"state": state, "questions": questions_to_wire(questions)} | |
| return self._check(self.http.post(f"{self.base_url}/v1/systemone", json=body)) | |
| def predict_batch(self, items: list[tuple[Union[str, dict, list], dict[str, QuestionLike]]]) -> list[dict]: | |
| body = {"items": [{"state": s, "questions": questions_to_wire(q)} for s, q in items]} | |
| return self._check(self.http.post(f"{self.base_url}/v1/systemone/batch", json=body))["results"] | |