File size: 4,041 Bytes
db16a11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""metask-jev-4b HTTP server — TypeSafe /v1/systemone compatible.

POST /v1/systemone
  body: {"state": str|obj, "questions": {"decision": {type, instructions, criteria}}}
  resp: {"answers": {"decision": {
            "type": "choice"|"noul"|"score",
            "probabilities": {option: p},      # choice/score
            "noul": P(true),                   # noul
         }}}
GET  /health -> {"ok": true}
"""
import argparse
import json
import sys
from pathlib import Path

from flask import Flask, jsonify, request

sys.path.insert(0, str(Path(__file__).resolve().parent))
from jev_scorer import load_model, score  # noqa: E402

TEMPERATURE = {"choice": 1.7875, "noul": 2.25, "score": 2.05}

app = Flask(__name__)
_state = {}


def get_model(model_path):
    if "model" not in _state:
        _state["model"], _state["tok"], _state["dev"] = load_model(model_path)
        print(f"[serve] model loaded on {_state['dev']}", flush=True)
    return _state["model"], _state["tok"]


@app.route("/health")
def health():
    return jsonify(ok=True, model="metask-jev-4b")


@app.route("/v1/systemone", methods=["POST"])
def systemone():
    body = request.get_json(force=True)
    model, tok = get_model(_state.get("model_path", ""))
    questions = body.get("questions", {})
    state = body.get("state", "")
    if not isinstance(state, str):
        state = json.dumps(state, ensure_ascii=False)

    answers = {}
    for name, q in questions.items():
        qtype = q["type"]
        crit = q.get("criteria") or {}
        if qtype == "noul":
            schema = {"decision": {
                "description": q.get("instructions", ""),
                "type": "boolean", "choices": [False, True],
                "choice_descriptions": {"false": crit.get("false", "No"),
                                        "true": crit.get("true", "Yes")}}}
            T = TEMPERATURE["noul"]
        elif qtype == "score":
            labels = [str(i) for i in range(len(crit))]
            schema = {"decision": {
                "description": q.get("instructions", ""),
                "type": "enum", "choices": labels,
                "choice_descriptions": dict(zip(labels, crit))}}
            T = TEMPERATURE["score"]
        else:
            labels = list(crit.keys()) if isinstance(crit, dict) else list(crit)
            schema = {"decision": {
                "description": q.get("instructions", ""),
                "type": "enum", "choices": labels,
                "choice_descriptions": {k: (crit.get(k) or k) for k in labels}}}
            T = TEMPERATURE["choice"]

        try:
            r = score(model, tok, state, schema, temperature=T, max_input_tokens=4096)
        except ValueError as e:
            if "limit is" in str(e):
                return jsonify(error=f"422 over context limit: {e}"), 422
            return jsonify(error=str(e)), 500
        probs = r["probabilities"]
        if qtype == "noul":
            answers[name] = {"type": "noul", "noul": float(probs.get("true", 0.0)),
                             "probabilities": probs}
        else:
            answers[name] = {"type": qtype, "probabilities": probs}
    return jsonify(answers=answers)


def build_prompt(tok, state, schema, max_input_tokens):
    """Re-exported for card Quickstart: vendored official prepare_prompts."""
    from jev_schema import prepare_prompts
    return prepare_prompts(tok, state, schema, max_input_tokens)


if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("--port", type=int, default=8000)
    ap.add_argument("--model", default="wayfind/metask-jev-4b-policy-mix",
                    help="HF repo id or path to model_path.txt")
    args = ap.parse_args()
    mp = Path(args.model)
    if mp.exists() and mp.is_file():  # model_path.txt written by install.sh
        args.model = mp.read_text().strip()
    _state["model_path"] = args.model
    print(f"[serve] starting on :{args.port}, model={args.model}", flush=True)
    app.run(host="0.0.0.0", port=args.port, threaded=True)