Files
aiturk-hermes-ide/apps/desktop/assets/codex-bridge/test_member_catalog.py
T
yilsemandClaude Opus 5.5 adf73eeedd fix(codex-link): summarize with a subscription model, reject tiny summaries, describe added models
Model switch lost the conversation: on 10 Oct a 1 MB thread was compacted by the
newly selected small model into 2201 characters. The compaction summary for a bridge
model is now written by a subscription model (ozet_kopru.ozet_modeli, default
gpt-6.1-sol); if that request is refused before it starts, the target model is used
as before. A summary far shorter than the conversation warrants is not accepted:
Codex gets response.failed and keeps the history.

The line under each added model no longer repeats its name. Teams show their
members, fallback chains their order, single models their provider and the traits
readable from the name; the text fits one phone row (30 characters).

Also normalizes tool call ids in the streamed response so Codex persists ids the
subscription backend accepts (fc_ -> ctc_), and bumps the bridge protocol to 6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:31:53 +03:00

111 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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],
["gpt-z", "ide/A-Team", "ide/Z-Team", "ide/bekci-kurnaz", "ide/9router-cc-opus"])
self.assertEqual([m["priority"] for m in models], [1, 2, 3, 4, 5])
def test_subscription_models_keep_their_own_order_priority_and_name(self):
models = [{"slug": "ide/GPT-6-Astra", "display_name": "GPT-6-Astra", "priority": 3},
{"slug": "gpt-6-astra", "display_name": "GPT-6-Astra", "priority": 2},
{"slug": "ide/A-Team", "display_name": "A-Team", "priority": 0},
{"slug": "gpt-6.1-sol", "display_name": "GPT-6.1-Sol", "priority": 1},
{"slug": "codex-auto-review", "display_name": "Codex Auto Review", "priority": 43}]
models.append({"slug": "ide/GPT-6-1-Sol", "display_name": "GPT-6-1-Sol", "priority": 2})
extras = [{"slug": "ide/GPT-6-Astra", "kind": "combo"}, {"slug": "ide/A-Team", "kind": "combo"},
{"slug": "ide/GPT-6-1-Sol", "kind": "combo"}]
originals = [dict(m) for m in models if not m["slug"].startswith("ide/")]
c.sirala_codex(models, extras)
self.assertEqual([m for m in models if not m["slug"].startswith("ide/")], originals)
self.assertEqual([m["slug"] for m in models],
["gpt-6-astra", "gpt-6.1-sol", "codex-auto-review", "ide/A-Team", "ide/GPT-6-1-Sol",
"ide/GPT-6-Astra"])
self.assertEqual([m["priority"] for m in models[3:]], [44, 45, 46])
self.assertEqual([m["display_name"] for m in models[4:]], ["GPT-6-1-Sol (AITURK)", "GPT-6-Astra (AITURK)"])
def test_description_line_tells_what_the_entry_is_and_fits_a_phone_row(self):
yedek = {"kind": "combo", "model": "Yedek", "strategy": "priority", "members": [
{"kind": "model", "model": "nano/z-ai/glm-5.3-flash-uncensored"},
{"kind": "model", "model": "nano/abliteration-ai/abliterated-model-large-v2"}]}
takim = {"topology": {"kind": "combo", "strategy": "priority", "members": [
{"kind": "combo", "model": "Takim-Hizli", "strategy": "fusion", "members": [
{"kind": "model", "model": "ag/gemini-3.8-flash-high"}, {"kind": "model", "model": "cc/claude-sonnet-5-5"}]},
yedek]}}
self.assertEqual(c.aciklama("Takim-Hizli", "Takim-Hizli", "combo", takim), "Takım: gemini+sonnet")
sirali = {"topology": {"kind": "combo", "strategy": "priority", "members": [
{"kind": "model", "model": "cx/gpt-6-astra"}, {"kind": "model", "model": "cc/claude-opus-5-5"},
{"kind": "model", "model": "cc/claude-sonnet-5-5"}, {"kind": "model", "model": "ag/gemini-3.8-flash-high"}]}}
self.assertEqual(c.aciklama("Ajan-Arac", "Ajan-Arac", "combo", sirali), "Sıralı: astra>opus>sonnet +1")
# An unresolved first member stays visible instead of being hidden behind the fallback.
eksik = {"topology": {"kind": "combo", "strategy": "priority", "members": [{"kind": "combo", "model": None}, yedek]}}
self.assertEqual(c.aciklama("GPT-6-Sol", "GPT-6-Sol", "combo", eksik), "Sıralı: ?>glm>abliterated")
donen = {"topology": {"kind": "combo", "strategy": "round-robin", "members": [yedek, yedek, yedek]}}
self.assertEqual(c.aciklama("Takim-Sira", "Takim-Sira", "combo", donen), "Dönüşümlü: 3 üye")
self.assertEqual(c.aciklama("X", "X", "combo", None), "Takım")
self.assertEqual(c.aciklama("9router-cc-claude-opus-5-5", "9R · cc/claude-opus-5-5", "model"), "Tek model · Claude")
self.assertEqual(c.aciklama("9router-nano-qwen-qwen3.8-27b-uncensored-thinking",
"9R · nano/qwen3.8-27b-uncensored:thinking", "model"),
"NanoGPT · sansürsüz · düşünen")
self.assertEqual(c.aciklama("bekci-kurnaz", "kurnaz", "model"), "Tek model · Bekçi")
uzun = {"topology": {"kind": "combo", "strategy": "priority", "members": [
{"kind": "model", "model": f"x/model{chr(97 + i) * 9}"} for i in range(8)]}}
self.assertLessEqual(len(c.aciklama("U", "U", "combo", uzun)), c.ACIKLAMA_SINIRI)
for ad in ("Takim-Hizli", "9R · cc/claude-opus-5-5"):
self.assertNotIn(ad, c.aciklama(ad, ad, "combo" if "Takim" in ad else "model", takim))
if __name__ == "__main__":
unittest.main()