Files
aiturk-hermes-ide/apps/desktop/assets/codex-bridge/karar_politikasi.py
T
yilsemandClaude Opus 5.5 7c4ef18df0 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>
2026-10-14 07:48:33 +03:00

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)