feat(codex-bridge): ship the local Codex bridge with the IDE
Adds the IDE's models and combos to Codex's model list and keeps conversation summaries readable across providers, so switching models in Codex no longer drops the conversation. Standard-library Python, run on the agent venv. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HRDGW1LiKcDUZYUdKBoWYW
This commit is contained in:
1 parent
df63432c92
commit
1ce5ab82cf
13 files changed
+2071
No files matched your search
@@ -0,0 +1,150 @@
|
||||
"""AITURK IDE "Codex Baglama": IDE'nin model ve combolarini Codex listesine ekler.
|
||||
|
||||
Kaynak, IDE'nin kullandigi gecittir (config.yaml -> providers.turkservis.base_url);
|
||||
anahtar IDE'nin kendi .env dosyasindan her yenilemede okunur, bridge.json'a yazilmaz.
|
||||
|
||||
Ac/kapat: %LOCALAPPDATA%\\TurkServis\\AITURK-IDE\\codex-baglama.json
|
||||
{"enabled": true, "kapsam": "ide"}
|
||||
Dosya yoksa ACIK sayilir (kullanici karari, 9 Ekim 2026: varsayilan acik).
|
||||
kapsam "ide" : IDE ayarinda tanimli modeller + adlandirilmis combolar (varsayilan)
|
||||
kapsam "hepsi" : gecitteki butun sohbet modelleri (1000+; Codex listesi cok buyur)
|
||||
"""
|
||||
import json, os, re, urllib.request
|
||||
|
||||
# Agent home: HERMES_HOME when the IDE passes it, else the default install location.
|
||||
AGENT = os.environ.get("HERMES_HOME") or os.path.join(os.environ.get("LOCALAPPDATA", ""), "TurkServis", "AITURK-IDE", "agent")
|
||||
IDE_DIR = os.path.dirname(os.path.normpath(AGENT))
|
||||
AYAR = os.path.join(IDE_DIR, "codex-baglama.json")
|
||||
KEY_NAME = "HERMES_CUSTOM_TURKSERVIS_API_KEY"
|
||||
DEFAULT_BASE = "https://ai.turkservis.online/v1"
|
||||
|
||||
|
||||
def ayar_oku(yol=AYAR):
|
||||
"""Dosya yoksa ya da bozuksa varsayilan: acik, kapsam 'ide'."""
|
||||
ayar = {"enabled": True, "kapsam": "ide"}
|
||||
try:
|
||||
with open(yol, encoding="utf-8") as f:
|
||||
v = json.load(f)
|
||||
if isinstance(v, dict):
|
||||
if isinstance(v.get("enabled"), bool):
|
||||
ayar["enabled"] = v["enabled"]
|
||||
if v.get("kapsam") in ("ide", "hepsi"):
|
||||
ayar["kapsam"] = v["kapsam"]
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
return ayar
|
||||
|
||||
|
||||
def anahtar_oku(agent=AGENT):
|
||||
try:
|
||||
with open(os.path.join(agent, ".env"), encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
if line.startswith(KEY_NAME + "="):
|
||||
return line.split("=", 1)[1].strip().strip('"').strip("'")
|
||||
except OSError:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def ide_yapilandirma(agent=AGENT):
|
||||
"""config.yaml'dan gecit adresi, varsayilan model ve tanimli model adlari.
|
||||
Kopru bagimliliksiz calistigi icin YAML'in yalniz bu bolumu satir satir okunur."""
|
||||
base, varsayilan, adlar = DEFAULT_BASE, "", []
|
||||
try:
|
||||
with open(os.path.join(agent, "config.yaml"), encoding="utf-8", errors="replace") as f:
|
||||
lines = f.read().splitlines()
|
||||
except OSError:
|
||||
return base, varsayilan, adlar
|
||||
bolum = saglayici = None
|
||||
modellerde = False
|
||||
for line in lines:
|
||||
if not line.strip() or line.lstrip().startswith("#"):
|
||||
continue
|
||||
girinti = len(line) - len(line.lstrip(" "))
|
||||
metin = line.strip()
|
||||
if girinti == 0:
|
||||
bolum = metin.rstrip(":"); saglayici = None; modellerde = False
|
||||
continue
|
||||
if bolum == "model" and girinti == 2:
|
||||
if metin.startswith("default:"):
|
||||
varsayilan = metin.split(":", 1)[1].strip().strip("'\"")
|
||||
continue
|
||||
if bolum != "providers":
|
||||
continue
|
||||
if girinti == 2:
|
||||
saglayici = metin.rstrip(":"); modellerde = False
|
||||
elif saglayici == "turkservis" and girinti == 4:
|
||||
modellerde = metin == "models:"
|
||||
if metin.startswith("base_url:"):
|
||||
base = metin.split(":", 1)[1].strip().strip("'\"") or base
|
||||
elif saglayici == "turkservis" and modellerde and girinti == 6:
|
||||
ad = re.split(r":\s*(\{|$)", metin)[0].strip().strip("'\"")
|
||||
if ad:
|
||||
adlar.append(ad)
|
||||
return base.rstrip("/"), varsayilan, adlar
|
||||
|
||||
|
||||
def kisa_ad(ad):
|
||||
"""Codex secicisinde gorunecek ad: yalniz kullanicinin ekledigi saglayici + model adi
|
||||
(kullanici istegi, 9 Ekim 2026). "OmniRoute · " / "9Router · " gibi gecit onekleri ve aradaki
|
||||
saglayici halkalari gosterilmez: "nano/qwen/qwen3.8-27b" -> "nano/qwen3.8-27b",
|
||||
"OmniRoute · ag/gemini-3.8-flash" -> "ag/gemini-3.8-flash", combo adi oldugu gibi kalir."""
|
||||
ad = (ad or "").split(" · ")[-1].strip()
|
||||
parcalar = [p for p in ad.split("/") if p]
|
||||
if len(parcalar) <= 2:
|
||||
return "/".join(parcalar) or ad
|
||||
return parcalar[0] + "/" + parcalar[-1]
|
||||
|
||||
|
||||
def adlandir(girdiler, dolu=()):
|
||||
"""[(kimlik, tam_ad)] -> {kimlik: gorunen_ad}. Kisa adlar cakisirsa aradaki halkalar geri konur;
|
||||
yine cakisirsa kimligin sonu eklenir. `dolu`: baska kaynaktan gelen, zaten kullanilan adlar."""
|
||||
kisa = {k: kisa_ad(a) for k, a in girdiler}
|
||||
tam = {k: (a or "").split(" · ")[-1].strip() for k, a in girdiler}
|
||||
sayac = {}
|
||||
for v in list(kisa.values()) + list(dolu):
|
||||
sayac[v.lower()] = sayac.get(v.lower(), 0) + 1
|
||||
sonuc, kullanilan = {}, {d.lower() for d in dolu}
|
||||
for k, _ in girdiler:
|
||||
ad = kisa[k] if sayac[kisa[k].lower()] == 1 else tam[k]
|
||||
if ad.lower() in kullanilan:
|
||||
ad = f"{ad} [{k[-8:]}]"
|
||||
kullanilan.add(ad.lower())
|
||||
sonuc[k] = ad
|
||||
return sonuc
|
||||
|
||||
|
||||
def sec(data, tanimli, kapsam="ide"):
|
||||
"""Gecit katalogundan Codex'e eklenecek girdiler: [{'id','ad'}], sirali ve tekrarsiz.
|
||||
Yalniz sohbet modelleri; resim/ses/video/embedding uretenler eklenmez."""
|
||||
tanimli = {a.lower() for a in tanimli if a}
|
||||
secilen, gorulen = [], set()
|
||||
for m in data:
|
||||
mid = m.get("id") or ""
|
||||
ts = m.get("turkservis") or {}
|
||||
if not mid or mid in gorulen or "chat" not in (ts.get("capabilities") or ["chat"]):
|
||||
continue
|
||||
gorunen = ts.get("display_name") or mid
|
||||
kisa = gorunen.split(" · ")[-1].strip()
|
||||
combo = "/" not in kisa # saglayici/model bicimi degil: adlandirilmis combo ya da takma ad
|
||||
if kapsam != "hepsi" and not combo and kisa.lower() not in tanimli and mid.lower() not in tanimli:
|
||||
continue
|
||||
gorulen.add(mid)
|
||||
secilen.append({"id": mid, "ad": gorunen, "sira": ts.get("order") or 0})
|
||||
secilen.sort(key=lambda s: (s["sira"], s["id"]))
|
||||
return [{"id": s["id"], "ad": s["ad"]} for s in secilen]
|
||||
|
||||
|
||||
def yukle(ua="AstraBridge/1.0 (codex)", timeout=30):
|
||||
"""Donus: (durum, base_url, anahtar, girdiler). durum: 'kapali' | 'anahtar-yok' | 'tamam'.
|
||||
Ag hatasinda istisna atar; cagiran son listeyi korur."""
|
||||
ayar = ayar_oku()
|
||||
if not ayar["enabled"]:
|
||||
return "kapali", "", "", []
|
||||
anahtar = anahtar_oku()
|
||||
if not anahtar:
|
||||
return "anahtar-yok", "", "", []
|
||||
base, varsayilan, adlar = ide_yapilandirma()
|
||||
req = urllib.request.Request(base + "/models", headers={"Authorization": f"Bearer {anahtar}", "User-Agent": ua})
|
||||
data = json.load(urllib.request.urlopen(req, timeout=timeout)).get("data", [])
|
||||
return "tamam", base, anahtar, sec(data, adlar + [varsayilan], ayar["kapsam"])
|
||||
Reference in new issue
Block a user