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>
93 lines
4.1 KiB
Python
93 lines
4.1 KiB
Python
"""Decision rules from Dify (14 Oct 2026).
|
|
|
|
Owner request, 14 Oct 2026: "Dify'de hazirlansa oradan gelse". The model a Codex step talks to stays an
|
|
OmniRoute combo, because a Dify app would not pass Codex's own tools through. What Dify supplies is the rules:
|
|
the decision questions, thresholds and which combo each kind of request goes to. The bridge fetches them in
|
|
the background (policy.refreshMinutes), validates them against the local settings and stores the last good
|
|
copy in jev-policy.json. A turn never waits for Dify; a failed fetch keeps the last good rules.
|
|
|
|
The Dify workflow returns one output named ``kurallar``: a JSON object (or JSON text) whose keys are any of
|
|
jev_settings.POLICY_KEYS, each holding the same fields as the local settings section, for example
|
|
{"autoRoute": {"enabled": true, "targets": {"zor": "Ajan"}}}.
|
|
"""
|
|
import copy
|
|
import json
|
|
import time
|
|
import urllib.request
|
|
|
|
import jev_settings
|
|
|
|
UA = "AITURK-IDE-codex-bridge/karar-politikasi"
|
|
MAX_BYTES = 64 * 1024
|
|
_son = {"zaman": 0.0}
|
|
|
|
|
|
def ayikla(cikti, yerel):
|
|
"""Dify output -> validated overrides. Raises ValueError with a short reason when unusable."""
|
|
if isinstance(cikti, str):
|
|
if len(cikti.encode("utf-8")) > MAX_BYTES:
|
|
raise ValueError("kurallar too large")
|
|
cikti = json.loads(cikti)
|
|
if not isinstance(cikti, dict):
|
|
raise ValueError("kurallar is not an object")
|
|
bilinmeyen = sorted(set(cikti) - set(jev_settings.POLICY_KEYS))
|
|
if bilinmeyen:
|
|
raise ValueError("unknown sections: " + ", ".join(bilinmeyen))
|
|
overrides = {k: v for k, v in cikti.items() if isinstance(v, dict)}
|
|
if len(overrides) != len(cikti):
|
|
raise ValueError("every section must be an object")
|
|
birlesik = copy.deepcopy(yerel)
|
|
for k, v in overrides.items():
|
|
birlesik[k] = jev_settings._fill(v, yerel[k])
|
|
jev_settings.validate(birlesik)
|
|
return overrides
|
|
|
|
|
|
def cek(settings, key, urlopen=urllib.request.urlopen):
|
|
"""One fetch. Returns {"ok": bool, "overrides": {...}, "error": str}; never raises."""
|
|
if not key:
|
|
return {"ok": False, "error": "Dify key not configured"}
|
|
url = settings['policy']['endpoint'].rstrip('/') + '/workflows/run'
|
|
govde = json.dumps({"inputs": {}, "response_mode": "blocking", "user": "aiturk-codex-bridge"}).encode("utf-8")
|
|
istek = urllib.request.Request(url, data=govde, method="POST", headers={
|
|
"Authorization": "Bearer " + key, "Content-Type": "application/json", "User-Agent": UA})
|
|
try:
|
|
with urlopen(istek, timeout=30) as yanit:
|
|
ham = yanit.read(MAX_BYTES * 4 + 1)
|
|
if len(ham) > MAX_BYTES * 4:
|
|
return {"ok": False, "error": "Dify response too large"}
|
|
veri = json.loads(ham)
|
|
cikti = ((veri.get("data") or {}).get("outputs") or {}).get("kurallar")
|
|
if cikti is None:
|
|
return {"ok": False, "error": "Dify output 'kurallar' missing"}
|
|
return {"ok": True, "overrides": ayikla(cikti, settings)}
|
|
except Exception as e:
|
|
return {"ok": False, "error": f"{type(e).__name__}: {e}"[:300]}
|
|
|
|
|
|
def zamani_mi(settings, simdi=None):
|
|
simdi = time.time() if simdi is None else simdi
|
|
return settings['policy']['source'] == 'dify' and simdi - _son["zaman"] >= settings['policy']['refreshMinutes'] * 60
|
|
|
|
|
|
def yenile(log=lambda _m: None, urlopen=urllib.request.urlopen, zorla=False):
|
|
"""Fetch when due; store the outcome. Local settings are read without the Dify overlay."""
|
|
try:
|
|
yerel, _ = _yerel_ayar()
|
|
except Exception as e:
|
|
log(f"karar kurallari: ayar okunamadi ({type(e).__name__})")
|
|
return None
|
|
if not zorla and not zamani_mi(yerel):
|
|
return None
|
|
_son["zaman"] = time.time()
|
|
sonuc = cek(yerel, jev_settings.policy_key(), urlopen=urlopen)
|
|
kayit = jev_settings.write_policy(sonuc)
|
|
log("karar kurallari Dify'dan alindi" if sonuc.get("ok") else f"karar kurallari alinamadi: {sonuc.get('error')}"
|
|
+ (" (son gecerli kurallar kullaniliyor)" if kayit.get("ok") else ""))
|
|
return kayit
|
|
|
|
|
|
def _yerel_ayar():
|
|
"""Settings as written locally (no Dify overlay), so a bad overlay can never validate itself."""
|
|
return jev_settings.read(overlay=False)
|