fix(codex-link): use member OmniRoute catalog and metadata ordering
This commit is contained in:
1 parent
88e660768c
commit
6e91154f15
8 files changed
+151
-45
No files matched your search
@@ -10,6 +10,13 @@ Codex depends on it while `openai_base_url` points here.
|
|||||||
key read from the agent's `.env` on every catalog refresh (never written elsewhere).
|
key read from the agent's `.env` on every catalog refresh (never written elsewhere).
|
||||||
- `ide_catalog.py`: which IDE models and combos are added to Codex's model list and
|
- `ide_catalog.py`: which IDE models and combos are added to Codex's model list and
|
||||||
how they are named (`provider/model`, combos by bare name).
|
how they are named (`provider/model`, combos by bare name).
|
||||||
|
The public `/models` catalog supplies every OmniRoute chat model; a member does
|
||||||
|
not need a private local model allowlist. `/router/catalog` supplies only the
|
||||||
|
`kind` metadata used for sorting, never additional model IDs. Direct 9Router
|
||||||
|
entries and internal 9Router combos are excluded. Combos sort A–Z first, then
|
||||||
|
individual models by provider and name, including OpenAI subscription models.
|
||||||
|
Codex labels the first visible row `isDefault`; a saved model selection remains
|
||||||
|
unchanged. A failed or empty refresh keeps the last successful catalog.
|
||||||
- `ozet_kopru.py`: conversation summaries are produced as plain text, so a summary made
|
- `ozet_kopru.py`: conversation summaries are produced as plain text, so a summary made
|
||||||
by one provider stays readable after the user switches to another.
|
by one provider stays readable after the user switches to another.
|
||||||
- `gemini_context.py`, `resim_context.py`: request fixes for routed models.
|
- `gemini_context.py`, `resim_context.py`: request fixes for routed models.
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from resim_context import resimleri_sadelestir
|
|||||||
import ide_catalog
|
import ide_catalog
|
||||||
import ozet_kopru
|
import ozet_kopru
|
||||||
|
|
||||||
SURUM = "4"
|
SURUM = "5"
|
||||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
# Codex sends request bodies zstd-compressed. Python gained zstd in 3.14; for older
|
# Codex sends request bodies zstd-compressed. Python gained zstd in 3.14; for older
|
||||||
@@ -183,14 +183,8 @@ def load_ide_catalog():
|
|||||||
ROUTES[IDE_ROUTE] = {"url": base + "/responses", "key": anahtar, "flatten_tools": True}
|
ROUTES[IDE_ROUTE] = {"url": base + "/responses", "key": anahtar, "flatten_tools": True}
|
||||||
clone = (CFG.get("nine_router_catalog") or {}).get("clone_from", "gpt-6-astra")
|
clone = (CFG.get("nine_router_catalog") or {}).get("clone_from", "gpt-6-astra")
|
||||||
levels = (CFG.get("nine_router_catalog") or {}).get("reasoning_levels")
|
levels = (CFG.get("nine_router_catalog") or {}).get("reasoning_levels")
|
||||||
# Secicide yalniz "saglayici/model" gorunur. 9Router listesinde ayni adla zaten duran model
|
# Gorunen adlari ayirt et; farkli kimlikteki modeller adlari benziyor diye silinmez.
|
||||||
# ikinci kez eklenmez (ayni modelin iki satir olmamasi icin).
|
|
||||||
var_olan = {m.get("display_name", "").lower() for m in extra_models}
|
var_olan = {m.get("display_name", "").lower() for m in extra_models}
|
||||||
for g in girdiler:
|
|
||||||
slug = IDE_PREFIX + g["id"]
|
|
||||||
g["kisa"] = ide_catalog.kisa_ad(g["ad"])
|
|
||||||
# "ad (2)": ayni modelin gecitteki ikinci yolu; taban ad listede varsa o da eklenmez.
|
|
||||||
girdiler = [g for g in girdiler if re.sub(r" \(\d+\)$", "", g["kisa"]).lower() not in var_olan]
|
|
||||||
adlar = ide_catalog.adlandir([(g["id"], g["ad"]) for g in girdiler], dolu=var_olan)
|
adlar = ide_catalog.adlandir([(g["id"], g["ad"]) for g in girdiler], dolu=var_olan)
|
||||||
for g in girdiler:
|
for g in girdiler:
|
||||||
slug = IDE_PREFIX + g["id"]
|
slug = IDE_PREFIX + g["id"]
|
||||||
@@ -198,7 +192,8 @@ def load_ide_catalog():
|
|||||||
continue
|
continue
|
||||||
extra_models.append({"slug": slug, "display_name": adlar[g["id"]],
|
extra_models.append({"slug": slug, "display_name": adlar[g["id"]],
|
||||||
"description": f"AITURK IDE: {g['ad']}", "route": IDE_ROUTE, "target": g["id"],
|
"description": f"AITURK IDE: {g['ad']}", "route": IDE_ROUTE, "target": g["id"],
|
||||||
"clone_from": clone, "priority": 300 + n, "reasoning_levels": levels})
|
"clone_from": clone, "priority": 300 + n, "reasoning_levels": levels,
|
||||||
|
"kind": g["kind"]})
|
||||||
model_map[slug] = (IDE_ROUTE, g["id"], None)
|
model_map[slug] = (IDE_ROUTE, g["id"], None)
|
||||||
n += 1
|
n += 1
|
||||||
else:
|
else:
|
||||||
@@ -724,6 +719,8 @@ class H(http.server.BaseHTTPRequestHandler):
|
|||||||
if not m.get("base_instructions") and not has_tmpl:
|
if not m.get("base_instructions") and not has_tmpl:
|
||||||
m.update(saved)
|
m.update(saved)
|
||||||
models.append(m); changed += 1
|
models.append(m); changed += 1
|
||||||
|
if EXTRA_MODELS:
|
||||||
|
ide_catalog.sirala_codex(models, EXTRA_MODELS)
|
||||||
out = json.dumps(data, ensure_ascii=False).encode("utf-8")
|
out = json.dumps(data, ensure_ascii=False).encode("utf-8")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"{tag} model listesi degistirilemedi: {e}")
|
log(f"{tag} model listesi degistirilemedi: {e}")
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ anahtar IDE'nin kendi .env dosyasindan her yenilemede okunur, bridge.json'a yazi
|
|||||||
Ac/kapat: %LOCALAPPDATA%\\TurkServis\\AITURK-IDE\\codex-baglama.json
|
Ac/kapat: %LOCALAPPDATA%\\TurkServis\\AITURK-IDE\\codex-baglama.json
|
||||||
{"enabled": true, "kapsam": "ide"}
|
{"enabled": true, "kapsam": "ide"}
|
||||||
Dosya yoksa ACIK sayilir (kullanici karari, 9 Ekim 2026: varsayilan acik).
|
Dosya yoksa ACIK sayilir (kullanici karari, 9 Ekim 2026: varsayilan acik).
|
||||||
kapsam "ide" : IDE ayarinda tanimli modeller + 9Router'a eklenmis saglayicilarin modelleri +
|
kapsam "ide" ve "hepsi": gecidin uye katalogundaki OmniRoute sohbet modelleri.
|
||||||
adlandirilmis combolar (varsayilan)
|
Yerel config'teki model adlari uye katalogunu sinirlamaz.
|
||||||
kapsam "hepsi" : gecitteki butun sohbet modelleri (1000+; Codex listesi cok buyur)
|
|
||||||
"""
|
"""
|
||||||
import json, os, re, urllib.request
|
import json, os, re, urllib.request
|
||||||
|
|
||||||
@@ -125,10 +124,10 @@ def adlandir(girdiler, dolu=()):
|
|||||||
return sonuc
|
return sonuc
|
||||||
|
|
||||||
|
|
||||||
def sec(data, tanimli, kapsam="ide"):
|
def sec(data, tanimli=(), kapsam="ide", turler=None):
|
||||||
"""Gecit katalogundan Codex'e eklenecek girdiler: [{'id','ad'}], sirali ve tekrarsiz.
|
"""Gecidin uye katalogundan girdiler: [{'id','ad','kind'}], sirali ve tekrarsiz.
|
||||||
Yalniz sohbet modelleri; resim/ses/video/embedding uretenler eklenmez."""
|
Yalniz sohbet modelleri; resim/ses/video/embedding uretenler eklenmez."""
|
||||||
tanimli = {a.lower() for a in tanimli if a}
|
turler = turler or {}
|
||||||
secilen, gorulen = [], set()
|
secilen, gorulen = [], set()
|
||||||
for m in data:
|
for m in data:
|
||||||
mid = m.get("id") or ""
|
mid = m.get("id") or ""
|
||||||
@@ -136,20 +135,16 @@ def sec(data, tanimli, kapsam="ide"):
|
|||||||
if not mid or mid in gorulen or "chat" not in (ts.get("capabilities") or ["chat"]):
|
if not mid or mid in gorulen or "chat" not in (ts.get("capabilities") or ["chat"]):
|
||||||
continue
|
continue
|
||||||
gorunen = ts.get("display_name") or mid
|
gorunen = ts.get("display_name") or mid
|
||||||
kisa = gorunen.split(" · ")[-1].strip()
|
kind = turler.get(mid) or ts.get("kind") or "model"
|
||||||
combo = "/" not in kisa # saglayici/model bicimi degil: adlandirilmis combo ya da takma ad
|
# Tek uye yolu: 9Router -> OmniRoute -> TurkServis -> Codex.
|
||||||
# 9Router'dan gelen combolar Codex'te gosterilmez (kullanici karari, 9 Ekim 2026): dogrudan
|
# Dogrudan 9Router satirlari ayni modelin ikinci kopyasi olarak eklenmez.
|
||||||
# 9Router kaynagi ve gecidin "9router" saglayicisi uzerinden gelen kopyalari.
|
if ts.get("source") != "omniroute":
|
||||||
if combo and (ts.get("source") == "9router" or mid.lower().startswith("9router-")):
|
|
||||||
continue
|
continue
|
||||||
# 9Router'a eklenmis saglayicilarin tekil modelleri her zaman eklenir (kullanici karari, 9 Ekim 2026:
|
# Combo adi biciminden tahmin edilmez; duz adli Agnes/Bekci modelleri de vardir.
|
||||||
# "9Router'in combosu olmasin dedim, 9Router'in modeli olsun"). Ayni modelin OmniRoute uzerinden
|
if kind == "combo" and mid.lower().startswith("9router-"):
|
||||||
# gelen kopyasi yalniz IDE ayarinda tanimliysa eklenir; boylece her model listede bir kez durur.
|
|
||||||
dokuz_model = not combo and ts.get("source") == "9router"
|
|
||||||
if kapsam != "hepsi" and not combo and not dokuz_model and kisa.lower() not in tanimli and mid.lower() not in tanimli:
|
|
||||||
continue
|
continue
|
||||||
gorulen.add(mid)
|
gorulen.add(mid)
|
||||||
secilen.append({"id": mid, "ad": gorunen, "sira": ts.get("order") or 0})
|
secilen.append({"id": mid, "ad": gorunen, "kind": kind, "sira": ts.get("order") or 0})
|
||||||
secilen.sort(key=lambda s: (s["sira"], s["id"]))
|
secilen.sort(key=lambda s: (s["sira"], s["id"]))
|
||||||
# Gecit ayni adin ikinci yoluna "ad (2)" der; birincisi listede yoksa sayac anlamsizdir.
|
# Gecit ayni adin ikinci yoluna "ad (2)" der; birincisi listede yoksa sayac anlamsizdir.
|
||||||
adlar = {s["ad"].split(" · ")[-1].strip().lower() for s in secilen}
|
adlar = {s["ad"].split(" · ")[-1].strip().lower() for s in secilen}
|
||||||
@@ -158,7 +153,38 @@ def sec(data, tanimli, kapsam="ide"):
|
|||||||
if taban != s["ad"] and taban.split(" · ")[-1].strip().lower() not in adlar:
|
if taban != s["ad"] and taban.split(" · ")[-1].strip().lower() not in adlar:
|
||||||
adlar.add(taban.split(" · ")[-1].strip().lower())
|
adlar.add(taban.split(" · ")[-1].strip().lower())
|
||||||
s["ad"] = taban
|
s["ad"] = taban
|
||||||
return [{"id": s["id"], "ad": s["ad"]} for s in secilen]
|
return [{"id": s["id"], "ad": s["ad"], "kind": s["kind"]} for s in secilen]
|
||||||
|
|
||||||
|
|
||||||
|
def sirala_codex(modeller, ekler):
|
||||||
|
"""Tum listeyi siralar: combo A-Z, sonra saglayici A-Z / model A-Z.
|
||||||
|
Kimlik ve yetenek alanlari korunur; kayitli model secimine dokunulmaz.
|
||||||
|
Codex'in isDefault etiketi kendi davranisi geregi ilk gorunur satira verilir."""
|
||||||
|
bilgiler = {m["slug"]: m for m in ekler}
|
||||||
|
|
||||||
|
def anahtar(m):
|
||||||
|
slug = m.get("slug", "")
|
||||||
|
bilgi = bilgiler.get(slug, {})
|
||||||
|
ad = kisa_ad(m.get("display_name") or slug)
|
||||||
|
if ad.startswith(DOKUZ_ISARET):
|
||||||
|
ad = ad[len(DOKUZ_ISARET):]
|
||||||
|
if bilgi.get("kind") == "combo":
|
||||||
|
return (0, ad.casefold(), "", slug)
|
||||||
|
if slug.startswith("ide/"):
|
||||||
|
mid = slug[len("ide/"):]
|
||||||
|
if mid.startswith("9router-"):
|
||||||
|
mid = mid[len("9router-"):]
|
||||||
|
saglayici, _, model = ad.partition("/")
|
||||||
|
if not model:
|
||||||
|
saglayici, _, _ = mid.partition("-")
|
||||||
|
model = ad
|
||||||
|
else:
|
||||||
|
saglayici, model = "OpenAI", ad
|
||||||
|
return (1, saglayici.casefold(), model.casefold(), slug)
|
||||||
|
|
||||||
|
modeller.sort(key=anahtar)
|
||||||
|
for n, model in enumerate(modeller):
|
||||||
|
model["priority"] = n
|
||||||
|
|
||||||
|
|
||||||
def yukle(ua="AstraBridge/1.0 (codex)", timeout=30):
|
def yukle(ua="AstraBridge/1.0 (codex)", timeout=30):
|
||||||
@@ -172,5 +198,19 @@ def yukle(ua="AstraBridge/1.0 (codex)", timeout=30):
|
|||||||
return "anahtar-yok", "", "", []
|
return "anahtar-yok", "", "", []
|
||||||
base, varsayilan, adlar = ide_yapilandirma()
|
base, varsayilan, adlar = ide_yapilandirma()
|
||||||
req = urllib.request.Request(base + "/models", headers={"Authorization": f"Bearer {anahtar}", "User-Agent": ua})
|
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", [])
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||||
return "tamam", base, anahtar, sec(data, adlar + [varsayilan], ayar["kapsam"])
|
data = json.load(resp).get("data")
|
||||||
|
if not isinstance(data, list) or not data:
|
||||||
|
raise ValueError("Gecit bos veya gecersiz model katalogu dondurdu")
|
||||||
|
req = urllib.request.Request(base + "/router/catalog", headers={"Authorization": f"Bearer {anahtar}", "User-Agent": ua})
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||||
|
metadata = json.load(resp).get("models")
|
||||||
|
if not isinstance(metadata, list) or not metadata:
|
||||||
|
raise ValueError("Gecit model turlerini dondurmedi")
|
||||||
|
# Bu ucta uye listesinde olmayan ic kayitlar da bulunur. Yalniz tur bilgisi alinir;
|
||||||
|
# modele erisim listesi her zaman /models yanitidir.
|
||||||
|
turler = {m["id"]: m["kind"] for m in metadata if m.get("id") and m.get("kind") in ("combo", "model")}
|
||||||
|
girdiler = sec(data, adlar + [varsayilan], ayar["kapsam"], turler)
|
||||||
|
if not girdiler:
|
||||||
|
raise ValueError("Gecitte kullanilabilir OmniRoute sohbet modeli yok")
|
||||||
|
return "tamam", base, anahtar, girdiler
|
||||||
@@ -2,8 +2,8 @@ import json, os, tempfile, unittest
|
|||||||
import ide_catalog as c
|
import ide_catalog as c
|
||||||
|
|
||||||
|
|
||||||
def girdi(mid, ad, yetenek=("chat",), sira=0, kaynak="omniroute"):
|
def girdi(mid, ad, yetenek=("chat",), sira=0, kaynak="omniroute", kind="model"):
|
||||||
return {"id": mid, "turkservis": {"display_name": ad, "capabilities": list(yetenek), "order": sira, "source": kaynak}}
|
return {"id": mid, "turkservis": {"display_name": ad, "capabilities": list(yetenek), "order": sira, "source": kaynak, "kind": kind}}
|
||||||
|
|
||||||
|
|
||||||
KATALOG = [
|
KATALOG = [
|
||||||
@@ -11,8 +11,8 @@ KATALOG = [
|
|||||||
girdi("9router-cc-opus", "OmniRoute · cc/claude-opus-5-5", sira=2),
|
girdi("9router-cc-opus", "OmniRoute · cc/claude-opus-5-5", sira=2),
|
||||||
girdi("9router-nano-x", "OmniRoute · nano/x", sira=1),
|
girdi("9router-nano-x", "OmniRoute · nano/x", sira=1),
|
||||||
girdi("gorsel", "OmniRoute · gpt-image-1", ("image",)),
|
girdi("gorsel", "OmniRoute · gpt-image-1", ("image",)),
|
||||||
girdi("9router-Takim-Agir", "OmniRoute · Takim-Agir", sira=9),
|
girdi("9router-Takim-Agir", "OmniRoute · Takim-Agir", sira=9, kind="combo"),
|
||||||
girdi("9router-Takim-Agir-6e65dba5", "OmniRoute · Takim-Agir", sira=8),
|
girdi("9router-Takim-Agir-6e65dba5", "OmniRoute · Takim-Agir", sira=8, kind="combo"),
|
||||||
girdi("Ajan-Arac", "OmniRoute · Ajan-Arac (tekrar)"),
|
girdi("Ajan-Arac", "OmniRoute · Ajan-Arac (tekrar)"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -52,22 +52,22 @@ class Yapilandirma(unittest.TestCase):
|
|||||||
class Secim(unittest.TestCase):
|
class Secim(unittest.TestCase):
|
||||||
def test_ide_kapsami(self):
|
def test_ide_kapsami(self):
|
||||||
s = c.sec(KATALOG, ["cc/claude-opus-5-5"], "ide")
|
s = c.sec(KATALOG, ["cc/claude-opus-5-5"], "ide")
|
||||||
# Tanimli model + adlandirilmis combolar; tanimsiz saglayici modeli ve gorsel modeli yok.
|
# Uye katalogu yerel config'teki adlarla sinirlanmaz.
|
||||||
self.assertEqual([x["id"] for x in s], ["9router-cc-opus", "Ajan-Arac"])
|
self.assertEqual([x["id"] for x in s], ["9router-nano-x", "9router-cc-opus", "Ajan-Arac"])
|
||||||
|
|
||||||
def test_9router_combolari_gosterilmez(self):
|
def test_9router_combolari_gosterilmez(self):
|
||||||
katalog = [girdi("9router-Takim-Agir-6e65dba5", "Takim-Agir", kaynak="9router", sira=1),
|
katalog = [girdi("9router-Takim-Agir-6e65dba5", "Takim-Agir", kaynak="9router", sira=1),
|
||||||
girdi("9router-Takim-Agir", "Takim-Agir (2)", sira=2),
|
girdi("9router-Takim-Agir", "Takim-Agir (2)", sira=2, kind="combo"),
|
||||||
girdi("Takim-Agir", "Takim-Agir (3)", sira=3),
|
girdi("Takim-Agir", "Takim-Agir (3)", sira=3),
|
||||||
girdi("Ajan-Arac", "Ajan-Arac", sira=4),
|
girdi("Ajan-Arac", "Ajan-Arac", sira=4),
|
||||||
girdi("9router-nano-x", "nano/x", kaynak="9router", sira=5)]
|
girdi("9router-nano-x", "nano/x", kaynak="9router", sira=5)]
|
||||||
s = c.sec(katalog, ["nano/x"], "ide")
|
s = c.sec(katalog, ["nano/x"], "ide")
|
||||||
# 9Router combosu ve gecidin 9router saglayicisindaki kopyasi yok; OmniRoute'un kendi combosu
|
# 9Router combosu ve gecidin 9router saglayicisindaki kopyasi yok; OmniRoute'un kendi combosu
|
||||||
# sayacsiz adiyla kalir. 9Router'in tekil modeli (combo degil) etkilenmez.
|
# sayacsiz adiyla kalir. Dogrudan 9Router tekilleri de eklenmez.
|
||||||
self.assertEqual([(x["id"], x["ad"]) for x in s],
|
self.assertEqual([(x["id"], x["ad"]) for x in s],
|
||||||
[("Takim-Agir", "Takim-Agir"), ("Ajan-Arac", "Ajan-Arac"), ("9router-nano-x", "nano/x")])
|
[("Takim-Agir", "Takim-Agir"), ("Ajan-Arac", "Ajan-Arac")])
|
||||||
|
|
||||||
def test_9router_saglayici_modelleri_tanimsiz_da_olsa_eklenir(self):
|
def test_9router_modelleri_yalniz_omniroute_uzerinden_eklenir(self):
|
||||||
katalog = [girdi("9router-nano-x-aaaa1111", "nano/x", kaynak="9router", sira=1),
|
katalog = [girdi("9router-nano-x-aaaa1111", "nano/x", kaynak="9router", sira=1),
|
||||||
girdi("9router-nano-x", "nano/x (2)", sira=2),
|
girdi("9router-nano-x", "nano/x (2)", sira=2),
|
||||||
girdi("9router-ocg-y-bbbb2222", "ocg/y", kaynak="9router", sira=3),
|
girdi("9router-ocg-y-bbbb2222", "ocg/y", kaynak="9router", sira=3),
|
||||||
@@ -75,10 +75,9 @@ class Secim(unittest.TestCase):
|
|||||||
girdi("9router-ses", "nano/ses", ("tts",), kaynak="9router", sira=5),
|
girdi("9router-ses", "nano/ses", ("tts",), kaynak="9router", sira=5),
|
||||||
girdi("9router-Takim-Agir-6e65dba5", "Takim-Agir", kaynak="9router", sira=6)]
|
girdi("9router-Takim-Agir-6e65dba5", "Takim-Agir", kaynak="9router", sira=6)]
|
||||||
s = c.sec(katalog, [], "ide")
|
s = c.sec(katalog, [], "ide")
|
||||||
# 9Router modelleri IDE ayarinda adi gecmese de gelir; OmniRoute kopyasi, OmniRoute'un kendi
|
# Yerel model tanimi gerekmez; iki kaynak yerine yalniz OmniRoute yolu gelir.
|
||||||
# saglayici modeli, sohbet olmayan model ve 9Router combosu gelmez.
|
|
||||||
self.assertEqual([(x["id"], x["ad"]) for x in s],
|
self.assertEqual([(x["id"], x["ad"]) for x in s],
|
||||||
[("9router-nano-x-aaaa1111", "nano/x"), ("9router-ocg-y-bbbb2222", "ocg/y")])
|
[("9router-nano-x", "nano/x"), ("vag-z", "vag/z")])
|
||||||
|
|
||||||
def test_gorunen_ad_yalniz_saglayici_ve_model(self):
|
def test_gorunen_ad_yalniz_saglayici_ve_model(self):
|
||||||
for tam, beklenen in [("OmniRoute · ag/gemini-3.8-flash", "ag/gemini-3.8-flash"),
|
for tam, beklenen in [("OmniRoute · ag/gemini-3.8-flash", "ag/gemini-3.8-flash"),
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
"""The shipped IDE must use the same public OmniRoute catalog as every member."""
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
import ide_catalog as c
|
||||||
|
|
||||||
|
|
||||||
|
def row(mid, name, source="omniroute", capabilities=("chat",)):
|
||||||
|
return {"id": mid, "turkservis": {"display_name": name, "source": source,
|
||||||
|
"capabilities": list(capabilities)}}
|
||||||
|
|
||||||
|
|
||||||
|
class MemberCatalog(unittest.TestCase):
|
||||||
|
def test_metadata_classifies_only_public_models(self):
|
||||||
|
public = [row("plain", "Plain"), row("team", "Team")]
|
||||||
|
metadata = [{"id": "plain", "kind": "model"}, {"id": "team", "kind": "combo"},
|
||||||
|
{"id": "internal", "kind": "combo"}]
|
||||||
|
replies = [io.BytesIO(json.dumps({"data": public}).encode()),
|
||||||
|
io.BytesIO(json.dumps({"models": metadata}).encode())]
|
||||||
|
with patch.object(c, "ayar_oku", return_value={"enabled": True, "kapsam": "ide"}), \
|
||||||
|
patch.object(c, "anahtar_oku", return_value="test-key"), \
|
||||||
|
patch.object(c, "ide_yapilandirma", return_value=("https://example.test/v1", "", [])), \
|
||||||
|
patch.object(c.urllib.request, "urlopen", side_effect=replies):
|
||||||
|
status, _, _, selected = c.yukle()
|
||||||
|
self.assertEqual(status, "tamam")
|
||||||
|
self.assertEqual({m["id"]: m["kind"] for m in selected}, {"plain": "model", "team": "combo"})
|
||||||
|
|
||||||
|
def test_empty_catalog_is_a_refresh_failure(self):
|
||||||
|
with patch.object(c, "ayar_oku", return_value={"enabled": True, "kapsam": "ide"}), \
|
||||||
|
patch.object(c, "anahtar_oku", return_value="test-key"), \
|
||||||
|
patch.object(c, "ide_yapilandirma", return_value=("https://example.test/v1", "", [])), \
|
||||||
|
patch.object(c.urllib.request, "urlopen", return_value=io.BytesIO(b'{"data": []}')):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
c.yukle()
|
||||||
|
|
||||||
|
def test_public_omniroute_models_do_not_need_private_local_configuration(self):
|
||||||
|
rows = [row("9router-nano-x", "9R · nano/x"), row("agnes-flat", "Agnes Flat"),
|
||||||
|
row("Ajan-Arac", "Ajan-Arac"), row("image", "image", capabilities=("image",))]
|
||||||
|
self.assertEqual({m["id"] for m in c.sec(rows, [])},
|
||||||
|
{"9router-nano-x", "agnes-flat", "Ajan-Arac"})
|
||||||
|
|
||||||
|
def test_direct_route_is_never_added_beside_omniroute(self):
|
||||||
|
rows = [row("9router-nano-x-direct", "9R · nano/x", "9router"),
|
||||||
|
row("9router-nano-x", "9R · nano/x")]
|
||||||
|
self.assertEqual([m["id"] for m in c.sec(rows, ["nano/x"], "hepsi")], ["9router-nano-x"])
|
||||||
|
|
||||||
|
def test_model_order_uses_combo_metadata_and_preserves_every_model(self):
|
||||||
|
models = [{"slug": "gpt-z", "display_name": "GPT Z", "priority": 1},
|
||||||
|
{"slug": "ide/bekci-kurnaz", "display_name": "kurnaz", "priority": 2},
|
||||||
|
{"slug": "ide/Z-Team", "display_name": "Z-Team", "priority": 5},
|
||||||
|
{"slug": "ide/9router-cc-opus", "display_name": "9R · cc/opus", "priority": 3},
|
||||||
|
{"slug": "ide/A-Team", "display_name": "A-Team", "priority": 4}]
|
||||||
|
extras = [{"slug": m["slug"], "kind": "combo" if m["slug"].endswith("Team") else "model"}
|
||||||
|
for m in models if m["slug"].startswith("ide/")]
|
||||||
|
c.sirala_codex(models, extras)
|
||||||
|
self.assertEqual([m["slug"] for m in models],
|
||||||
|
["ide/A-Team", "ide/Z-Team", "ide/bekci-kurnaz", "ide/9router-cc-opus", "gpt-z"])
|
||||||
|
self.assertEqual([m["priority"] for m in models], list(range(5)))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -27,7 +27,7 @@ export const CODEX_LINK_REFRESH_URL = `${CODEX_LINK_ORIGIN}/aiturk/codex-baglama
|
|||||||
export const CODEX_LINK_MARKER = '# AITURK IDE Codex Baglama'
|
export const CODEX_LINK_MARKER = '# AITURK IDE Codex Baglama'
|
||||||
// Must equal SURUM in assets/codex-bridge/bridge.py; a running bridge that reports
|
// Must equal SURUM in assets/codex-bridge/bridge.py; a running bridge that reports
|
||||||
// another version is restarted so an updated IDE never keeps an old bridge alive.
|
// another version is restarted so an updated IDE never keeps an old bridge alive.
|
||||||
export const CODEX_BRIDGE_VERSION = '4'
|
export const CODEX_BRIDGE_VERSION = '5'
|
||||||
export const CODEX_LINK_LINE = `openai_base_url = "${CODEX_LINK_ORIGIN}/backend-api/codex" ${CODEX_LINK_MARKER}`
|
export const CODEX_LINK_LINE = `openai_base_url = "${CODEX_LINK_ORIGIN}/backend-api/codex" ${CODEX_LINK_MARKER}`
|
||||||
|
|
||||||
export const CODEX_LINK_RUN_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
|
export const CODEX_LINK_RUN_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "aiturk-ide",
|
"name": "aiturk-ide",
|
||||||
"productName": "AITURK IDE",
|
"productName": "AITURK IDE",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0-beta.23",
|
"version": "1.0.0-beta.24",
|
||||||
"description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.",
|
"description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.",
|
||||||
"author": "AITURK / TurkServis",
|
"author": "AITURK / TurkServis",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
Generated
+1
-1
@@ -65,7 +65,7 @@
|
|||||||
},
|
},
|
||||||
"apps/desktop": {
|
"apps/desktop": {
|
||||||
"name": "aiturk-ide",
|
"name": "aiturk-ide",
|
||||||
"version": "1.0.0-beta.23",
|
"version": "1.0.0-beta.24",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@assistant-ui/core": "0.2.23",
|
"@assistant-ui/core": "0.2.23",
|
||||||
|
|||||||
Reference in new issue
Block a user