feat(codex-bridge): decision-model stages, Dify rules and beta.39

Owner request, 14 October 2026: use the decision model (Jev / Celeris) at
several stages, let Dify supply the rules, code it without new bugs.
Every new stage is switched on in Settings > Kademeli Gecis and starts
disabled, so the upgrade changes no behaviour by itself.

- Settings schema 2 (Python and TypeScript validate the same rules); a
  schema 1 file is filled from the defaults. Default chain is now
  Katman-1 -> Katman-2 -> Katman-3 with Katman-3, Chat-Model and
  bekci-sesli-bekci always passing (owner decision of the same day).
- watchModels: the Bekci correction channel follows a settings list
  instead of the hard-coded Sesli-Hizli-Zeki.
- prefilter: a predicted refusal starts the step at the chosen layer.
- autoRoute: virtual "Otomatik" model; one decision per user turn picks
  the combo, tool steps of the same turn keep it.
- finishCheck: an answer that stops early goes back to the same model once.
- A layer whose window cannot hold the request is skipped.
- policy: stage rules from a Dify workflow (karar_politikasi.py), fetched
  in the background, validated, last good copy kept; turns never wait.
- karar_sor.py: verifierCheck for the dynamic-workflow acceptance step.
- Bridge version 19, IDE 1.0.0-beta.39.

Tests: 299 bridge tests, 36 electron tests; live run on a side bridge
(port 8797) with Celeris and the AITURK gateway for every stage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
yilsemandClaude Opus 5.5 committed 2026-10-14 07:48:33 +03:00
1 parent 8f53b090f7
commit 7c4ef18df0
18 files changed
+1672 -119

No files matched your search

+94 -13
View File
@@ -4,11 +4,16 @@ Kullanici karari (11 Ekim 2026): "Jev kati olsun. Af yok istisna yok. Herhangi b
gecirmezse 1.Katmana yonlendirecek. 1.Katman combosu gecmezse 2.Katmana, 2.Katman gecmezse 3.Katmana, 3.Katman
gecmezse 4.Katmana. 4.Katman combosu gelirse Jev bunu sorgulamadan gecirecek."
12 Ekim 2026 istisnasi: çözülmüş hedefi tam Sesli-Hizli-Zeki olan cevap Jev'e sorulmaz.
12 Ekim 2026 istisnasi: çözülmüş hedefi muaf listesinde olan cevap Jev'e sorulmaz (ayar: exemptModels).
Taşıma/boş yanıt kurtarması ve yedek katmanların denetimi korunur.
Diğer hedeflerde denetim secili modelin katman olup olmamasina bakmaz.
Gecmezse adimi sirayla Katman-1 -> Katman-2 -> Katman-3 -> Katman-4 yapar; secili model zaten bir katmansa
yalniz ondan sonrakiler denenir. Katmanlar OmniRoute combolaridir (KATMAN); kopru onlari yalniz adiyla cagirir.
Gecmezse adimi ayardaki zincir yapar (14 Ekim 2026: Katman-1 -> Katman-2 -> Katman-3, Katman-3 muaf); secili model
zaten bir katmansa yalniz ondan sonrakiler denenir. Katmanlar OmniRoute combolaridir; kopru onlari yalniz adiyla cagirir.
14 Ekim 2026 karar asamalari (hepsi ayardan acilir, varsayilan kapali): tur basinda "Otomatik" modelin combo secimi
(autoRoute), normal modelin reddedecegi tahmin edilen isi dogrudan katmana verme (prefilter), isi bitirmeden "bitti"
diyen son cevabi ayni modele bir kez geri verme (finishCheck). Karar modeli metin yazmaz; hata ya da zaman asiminda
bugunku yol izlenir.
Jev (TypeSafe System One) metin uretmez, sorulan evet/hayir sorularina olasilik verir. Kural tektir ve istisnasi
yoktur: cevap istegi yapmiyorsa (ret) ya da sormadan kendi sartlariyla degistiriyorsa (sart) adim gecmez.
@@ -22,7 +27,7 @@ import jev_settings
ADRES = "https://api.typesafe.ai/v1/systemone"
MODEL = "jev-1.13.0"
KATMAN = ("Katman-1", "Katman-2", "Katman-3", "Katman-4")
KATMAN = ("Katman-1", "Katman-2", "Katman-3") # varsayilan zincir; asil zincir ayardan okunur
ESIK = {"ret": 0.5, "sart": 0.7}
SORU = {
"ret": ("The assistant did NOT do what the user asked or did NOT answer the user's question: it refused, declined, "
@@ -32,6 +37,14 @@ SORU = {
}
_ANAHTAR = {"okundu": False, "deger": ""}
def izleme_mi(hedef):
"""Bekci izleme kanali (cevaptan sonra duzeltme) bu hedefin mi? Ayar: watchModels, tam ad eslesmesi."""
try:
return hedef in jev_settings.read()[0]['watchModels']
except Exception:
return hedef == "Sesli-Hizli-Zeki"
def muaf_mi(hedef):
"""Settings' always-pass list matches exact resolved model IDs.
@@ -109,16 +122,23 @@ def _metin(oge):
def son_istek(req):
"""Kullanicinin bu turdaki istegi: son kullanici mesaji. Sesli aktarimda yalniz <input> icindeki soz;
baska sarmalli sistem mesajlari (kanca baglami) atlanir."""
for oge in reversed(req.get("input") or []):
return son_istek_sirasi(req)[0]
def son_istek_sirasi(req):
"""(istek, girdideki sirasi). Turun arac adimlarinda da ayni sira doner; yoksa ("", -1)."""
girdi = req.get("input") or []
for sira in range(len(girdi) - 1, -1, -1):
oge = girdi[sira]
if not (isinstance(oge, dict) and oge.get("type", "message") == "message" and oge.get("role") == "user"):
continue
metin = _metin(oge).strip()
m = re.search(r"<realtime_delegation>.*?<input>(.*?)</input>", metin, re.S)
if m:
return m.group(1).strip()
return m.group(1).strip(), sira
if metin and not metin.startswith("<"):
return metin
return ""
return metin, sira
return "", -1
def hazirlik_mi(govde):
@@ -166,24 +186,83 @@ def komutlar(govde):
return "\n".join(satirlar)
def _gonder(govde, key, settings=None):
def _gonder(govde, key, settings=None, timeout=None):
settings = settings or jev_settings.read()[0]
istek = urllib.request.Request(settings['endpoint'], data=json.dumps(govde).encode("utf-8"), method="POST",
headers={"Authorization": "Bearer " + key, "Content-Type": "application/json"})
with urllib.request.urlopen(istek, timeout=settings['timeoutSeconds']) as yanit:
with urllib.request.urlopen(istek, timeout=timeout or settings['timeoutSeconds']) as yanit:
return json.load(yanit)
def sorular(durum, soru, key, timeout, gonder=_gonder, settings=None):
"""Karar modeline bir cagrida birden cok evet/hayir sorusu. Donus {ad: 0-1 olasilik} | None (sorulamadi).
durum: {"user_request": ..., ["assistant_reply": ...]}; soru: {ad: talimat}."""
settings = settings or jev_settings.read()[0]
if not key or not soru or not durum.get("user_request"):
return None
govde = {"model": settings['model'], "state": durum,
"questions": {k: {"type": "noul", "instructions": v} for k, v in soru.items()}}
if not jev_settings.acquire(settings):
return None
try:
response = gonder(govde, key, settings, timeout) if gonder is _gonder else gonder(govde, key)
yanitlar = (response or {}).get("answers") or {}
result = {k: float(yanitlar[k]["noul"]) for k in soru}
return result if all(0 <= v <= 1 for v in result.values()) else None
except Exception:
return None
finally:
jev_settings.release()
def on_karar(istek, oto, ret, key, gonder=_gonder):
"""Tur basi karari: istenen sorular tek cagrida. oto: Otomatik modelin combo secimi; ret: normal model reddeder mi.
Donus: {"hedef": Otomatik icin secilen ad ya da None, "ret": bool ya da None, "puan": {...} ya da None}."""
settings = jev_settings.read()[0]
a, p = settings['autoRoute'], settings['prefilter']
soru = {}
if oto:
soru.update(a['questions'])
if ret and p['enabled']:
soru["ret"] = p['question']
if not soru:
return {"hedef": None, "ret": None, "puan": None}
sure = max(a['timeoutSeconds'] if oto else 0, p['timeoutSeconds'] if ret else 0) or 3
puan = sorular({"user_request": istek}, soru, key, sure, gonder=gonder, settings=settings)
hedef = None
if oto:
if puan is None:
hedef = a['targets']['zor'] # karar alinamazsa guclu yol
elif puan["arastirma"] >= a['thresholds']['arastirma']:
hedef = a['targets']['arastirma']
elif puan["zor"] >= a['thresholds']['zor']:
hedef = a['targets']['zor']
else:
hedef = a['targets']['basit']
tahmin = None if puan is None or "ret" not in puan else puan["ret"] >= p['threshold']
return {"hedef": hedef, "ret": tahmin, "puan": puan}
def bitmedi_mi(puan):
"""finishCheck: son cevap isi bitirmeden 'bitti' mi dedi? Soru sorulmadiysa False."""
if not puan or "bitmedi" not in puan:
return False
return puan["bitmedi"] >= jev_settings.read()[0]['finishCheck']['threshold']
class Puan(dict):
"""Carry the thresholds used for this evaluation through a settings change."""
pass
def sor(istek, cevap, key, gonder=_gonder):
"""Jev'e sorar. Donus: {"ret", "sart"} olasiliklari | None (sorulamadi)."""
def sor(istek, cevap, key, gonder=_gonder, bitis=False):
"""Jev'e sorar. Donus: {"ret", "sart"[, "bitmedi"]} olasiliklari | None (sorulamadi).
bitis=True ve finishCheck acikken ayni cagriya "is bitmeden bitti mi dedi" sorusu da eklenir."""
settings = jev_settings.read()[0]
govde = {"model": settings['model'], "state": {"user_request": istek, "assistant_reply": cevap},
"questions": {k: {"type": "noul", "instructions": v} for k, v in settings['questions'].items()}}
if bitis and settings['finishCheck']['enabled']:
govde['questions']['bitmedi'] = {"type": "noul", "instructions": settings['finishCheck']['question']}
for name, criteria in settings.get('criteria', {}).items():
values = {k: v for k, v in criteria.items() if v.strip()}
if values:
@@ -193,7 +272,9 @@ def sor(istek, cevap, key, gonder=_gonder):
try:
response = gonder(govde, key, settings) if gonder is _gonder else gonder(govde, key)
yanitlar = (response or {}).get("answers") or {}
result = Puan({k: float(yanitlar[k]["noul"]) for k in SORU})
result = Puan({k: float(yanitlar[k]["noul"]) for k in settings['questions']})
if 'bitmedi' in govde['questions'] and isinstance(yanitlar.get('bitmedi'), dict):
result['bitmedi'] = float(yanitlar['bitmedi']['noul']) # ek soru: cevapsizsa ana karar yine gecerli
if any(not 0 <= v <= 1 for v in result.values()):
return None
result.thresholds = settings['thresholds']