divyanshudhruv commited on
Commit
c2e3388
·
verified ·
1 Parent(s): e473bb9

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. oev/__init__.py +1 -1
  2. oev/infer.py +33 -1
  3. oev/serve.py +25 -0
oev/__init__.py CHANGED
@@ -4,4 +4,4 @@ State + typed questions in, calibrated probability distributions out,
4
  one forward pass.
5
  """
6
 
7
- __version__ = "0.2.0"
 
4
  one forward pass.
5
  """
6
 
7
+ __version__ = "0.3.0"
oev/infer.py CHANGED
@@ -8,6 +8,36 @@ from oev.evaluate import load_model
8
  from oev.tokenizer_hf import HFTokenPacker
9
 
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  class OEV:
12
  def __init__(self, checkpoint="checkpoints/oev-tiny.pt", device="cpu", temperature=1.0):
13
  self.model = load_model(checkpoint, device)
@@ -21,6 +51,7 @@ class OEV:
21
  self.packer = HFTokenPacker(self.model.cfg["backbone"])
22
 
23
  def _question(self, name, q):
 
24
  pq = {"name": name, "type": q["type"], "instructions": q.get("instructions", q["type"])}
25
  if q["type"] == "choice":
26
  pq["options"] = list(q["options"])
@@ -48,7 +79,8 @@ class OEV:
48
 
49
  def decide(self, state, questions, temperature=None):
50
  out = {}
51
- for name, q in questions.items():
 
52
  pq = self._question(name, q)
53
  probs = self._probs(state, pq, temperature=temperature)
54
  best = max(range(len(probs)), key=probs.__getitem__)
 
8
  from oev.tokenizer_hf import HFTokenPacker
9
 
10
 
11
+ def normalize_question(name, q):
12
+ """Accept either question schema and return an OEV-schema copy.
13
+
14
+ Native schema: choice uses a bare "options" list, score uses "levels".
15
+ Jev / TypeSafe schema: choice and noul carry "criteria" as a map of
16
+ option name -> description, score carries "criteria" as a list of level
17
+ labels. Descriptions are ignored (OEV was trained on bare options); the
18
+ option names become the options. Native keys win when both are present.
19
+ """
20
+ q = dict(q)
21
+ criteria = q.get("criteria")
22
+ if q.get("type") == "choice" and "options" not in q:
23
+ if isinstance(criteria, dict) and criteria:
24
+ q["options"] = [str(option) for option in criteria]
25
+ else:
26
+ raise ValueError(
27
+ f"question {name!r} (choice) needs an 'options' list "
28
+ "or a 'criteria' map of option name -> description"
29
+ )
30
+ elif q.get("type") == "score" and "levels" not in q:
31
+ if isinstance(criteria, list) and criteria:
32
+ q["levels"] = list(criteria)
33
+ else:
34
+ raise ValueError(
35
+ f"question {name!r} (score) needs a 'levels' list "
36
+ "or a 'criteria' list of level labels"
37
+ )
38
+ return q
39
+
40
+
41
  class OEV:
42
  def __init__(self, checkpoint="checkpoints/oev-tiny.pt", device="cpu", temperature=1.0):
43
  self.model = load_model(checkpoint, device)
 
51
  self.packer = HFTokenPacker(self.model.cfg["backbone"])
52
 
53
  def _question(self, name, q):
54
+ q = normalize_question(name, q)
55
  pq = {"name": name, "type": q["type"], "instructions": q.get("instructions", q["type"])}
56
  if q["type"] == "choice":
57
  pq["options"] = list(q["options"])
 
79
 
80
  def decide(self, state, questions, temperature=None):
81
  out = {}
82
+ for name, raw_question in questions.items():
83
+ q = normalize_question(name, raw_question)
84
  pq = self._question(name, q)
85
  probs = self._probs(state, pq, temperature=temperature)
86
  best = max(range(len(probs)), key=probs.__getitem__)
oev/serve.py CHANGED
@@ -15,10 +15,13 @@ Usage:
15
  }'
16
 
17
  # Jev-compatible schema (drop-in for existing TypeSafe clients):
 
18
  curl -X POST localhost:8000/v1/systemone -H "Content-Type: application/json" -d '{
19
  "model": "oev",
20
  "state": "We were charged twice for the same order.",
21
  "questions": {
 
 
22
  "refund_requested": {"type": "noul", "instructions": "Does the user request a refund?"}
23
  }
24
  }'
@@ -91,11 +94,33 @@ def _jevify(answers: dict) -> dict:
91
  return out
92
 
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  @app.post("/v1/systemone")
95
  def systemone(req: SystemOneRequest):
96
  """Jev-compatible endpoint: existing TypeSafe clients work by changing baseUrl."""
97
  if agent is None:
98
  raise HTTPException(status_code=503, detail="no checkpoint loaded - run oev-serve --checkpoint ...")
 
 
 
99
  answers = agent.decide(req.state, req.questions)
100
  return {
101
  "model": req.model,
 
15
  }'
16
 
17
  # Jev-compatible schema (drop-in for existing TypeSafe clients):
18
+ # questions accept the same "criteria" shape laya and Kev serve.
19
  curl -X POST localhost:8000/v1/systemone -H "Content-Type: application/json" -d '{
20
  "model": "oev",
21
  "state": "We were charged twice for the same order.",
22
  "questions": {
23
+ "department": {"type": "choice", "instructions": "Which department?",
24
+ "criteria": {"billing": "payments and refunds", "technical": "bugs and outages"}},
25
  "refund_requested": {"type": "noul", "instructions": "Does the user request a refund?"}
26
  }
27
  }'
 
94
  return out
95
 
96
 
97
+ def _validate_jev_questions(questions: dict) -> str | None:
98
+ """Return an error message for questions that do not match the Jev schema."""
99
+ for name, question in questions.items():
100
+ if not isinstance(question, dict):
101
+ return f"question {name} must be an object"
102
+ qtype = question.get("type")
103
+ if qtype not in {"choice", "noul", "score"}:
104
+ return f"question {name} has unsupported type"
105
+ if qtype in {"choice", "noul"}:
106
+ criteria = question.get("criteria")
107
+ if criteria is not None and (not isinstance(criteria, dict) or not criteria):
108
+ return f"question {name} ({qtype}) criteria must be a non-empty object"
109
+ else:
110
+ criteria = question.get("criteria")
111
+ if not isinstance(criteria, list) or not criteria:
112
+ return f"question {name} (score) needs a non-empty criteria list of level labels"
113
+ return None
114
+
115
+
116
  @app.post("/v1/systemone")
117
  def systemone(req: SystemOneRequest):
118
  """Jev-compatible endpoint: existing TypeSafe clients work by changing baseUrl."""
119
  if agent is None:
120
  raise HTTPException(status_code=503, detail="no checkpoint loaded - run oev-serve --checkpoint ...")
121
+ error = _validate_jev_questions(req.questions)
122
+ if error:
123
+ raise HTTPException(status_code=422, detail=error)
124
  answers = agent.decide(req.state, req.questions)
125
  return {
126
  "model": req.model,