File size: 4,194 Bytes
9b9c96c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""AUBIN Engine — katmanlı Sistem-1 / Sistem-2 karar motoru.

Laya tek katmanlıdır (her istek küçük bir kodlayıcıya). AUBIN Engine isteği EN UCUZ yeterli katmanda bitirir:

  K0  öğrenilmiş beceriler + bellek (AubinLearning: acquire_skill / learn) — milisaniye, yalnız kanıtlanmış soru tiplerinde
  K1  hızlı model (ör. AUBIN-E4B, ~0.25 s/karar)
  K2  güçlü model (ör. AUBIN-12B / 31B; isteğe bağlı akıl yürütme geçişi)

Her katmanın kalibre güveni o katmanın eşiğini geçerse karar orada verilir; geçmezse bir üst katmana çıkılır. Son katmanda
da eşik tutmazsa ve `abstain=True` ise cevap "çekimser" (null) döner — yanlış ama emin cevap yerine "emin değilim".
Yanıt biçimi Laya/Jev istemcileriyle uyumludur: {"answers": {qid: {"choice"|"score"|"noul", "confidence", ...}}, "routing": {...}}.

    eng = AubinEngine([("fast", Aubin("emrevrg/AUBIN-E4B-Control"), 0.85), ("strong", Aubin("emrevrg/AUBIN-12B"), 0.0)])
    eng.predict(state, questions)
"""
from __future__ import annotations

import time

from .core import options


def _answer_field(q, key, probs):
    """Laya/Jev alan adları: choice → "choice", score → "score" (seviye indeksi), noul → "noul" (evet olasılığı)."""
    t = q.get("type", "choice")
    if t == "noul":
        return {"noul": round(float(probs.get("true", 0.0)), 4)}
    if t == "score":
        keys = list(probs)
        return {"score": keys.index(key) if key in keys else None, "label": key}
    return {"choice": key}


class AubinEngine:
    def __init__(self, tiers, abstain=False, learner=None):
        """tiers: [(ad, model, eşik)] ucuzdan pahalıya; model .decide(state, questions) sunar (Aubin, AubinEnsemble,
        AubinLearning). eşik: o katmanda kararı kabul etmek için gereken kalibre güven (son katmanda genelde 0).
        learner: isteğe bağlı AubinLearning — K0 olarak en başta denenir (öğrenilmiş beceri/bellek)."""
        self.tiers = list(tiers)
        if learner is not None:
            self.tiers.insert(0, ("learned", learner, getattr(learner, "engine_threshold", 0.9)))
        self.abstain = abstain
        self.stats = {name: 0 for name, _, _ in self.tiers}
        self.stats["abstained"] = 0

    def predict(self, state, questions, min_confidence=None):
        t0 = time.perf_counter()
        pending, answers, route, tier_ms = dict(questions), {}, {}, {}
        for li, (name, model, thr) in enumerate(self.tiers):
            if not pending:
                break
            last = li == len(self.tiers) - 1
            thr = max(thr, min_confidence or 0.0) if not last or self.abstain else thr
            t1 = time.perf_counter()
            res = model.decide(state, pending)
            tier_ms[name] = round((time.perf_counter() - t1) * 1e3, 1)
            for qid, r in res.items():
                conf = float(r["confidence"])
                if conf >= thr or (last and not self.abstain):
                    answers[qid] = {**_answer_field(questions[qid], r["answer"], r["probabilities"]),
                                    "confidence": round(conf, 4), "probabilities": r["probabilities"], "tier": name}
                    route[qid] = name; self.stats[name] += 1
                    pending.pop(qid)
            if last:
                for qid in list(pending):            # son katmanda da eşik tutmadı → çekimser
                    keys, _ = options(questions[qid])
                    answers[qid] = {"choice": None, "abstained": True, "tier": name, "confidence": None}
                    route[qid] = "abstained"; self.stats["abstained"] += 1
                    pending.pop(qid)
        return {"answers": answers, "routing": {"per_question": route, "tier_ms": tier_ms,
                                                 "total_ms": round((time.perf_counter() - t0) * 1e3, 1)}}

    def learn(self, state, questions, answers):
        """Geri bildirim K0'a (varsa) gider: bir sonraki benzer istek pahalı katmana hiç çıkmadan çözülebilir."""
        for name, model, _ in self.tiers:
            if hasattr(model, "learn"):
                return model.learn(state, questions, answers)
        return None