feat(codex-bridge): web search for added models, forgiving spoken model names

Added models had no web search: Codex's built-in one runs on OpenAI's side and
the bridge drops it from their tool list. Measured in a voice session on
11 Oct 2026: a model looked for a search tool ten times and ended the turn
with no answer. The bridge now tells added models two ready commands (ara.py:
search and read a page, standard library only, no key). Codex's own models
never see the note: their requests do not reach the bridge.

Spoken model names: a half-spoken word matches the start of a name part
("think" -> thinking, "obli" -> obliterated), and when one candidate is the
others minus extra words, the one without the unsaid suffix is picked
("Qwen 3.8 Obliterated" -> not the ":thinking" one).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
yilsemandClaude Opus 5.5 committed 2026-10-11 07:25:30 +03:00
1 parent 23e8aa8191
commit c8993d58cc
6 files changed
+265 -2

No files matched your search

@@ -35,9 +35,25 @@ class Komut(unittest.TestCase):
("modeli glm flaş sansürsüz yap", "ide/9router-nano-z-ai-glm-5.3-flash-uncensored")]:
self.assertEqual((pick(text) or {}).get("slug"), slug, text)
def test_half_spoken_words_and_the_plain_variant(self):
"""Measured 11 Oct 2026 in a voice session: "obli think" was not found three times, and "Qwen 3.8
Obliterated" asked which one although the user had not said "thinking"."""
qwen = MODELS + [("ide/9router-nano-qwen-qwen3.8-27b-obliterated", "nano/qwen3.8-27b-obliterated"),
("ide/9router-nano-qwen-qwen3.8-27b-obliterated-thinking", "nano/qwen3.8-27b-obliterated:thinking"),
("ide/9router-nano-qwen-qwen3.8-27b-uncensored", "nano/qwen3.8-27b-uncensored")]
def sec(text):
return (g.eslestir(g.komut_coz(text)["ad"], qwen) or {}).get("slug")
thinking = "ide/9router-nano-qwen-qwen3.8-27b-obliterated-thinking"
self.assertEqual(sec("Modeli 3.8 Obliterated Think yap"), thinking)
self.assertEqual(sec("Modeli Qwen... modeli Qwen 3.8 .27 B... Obli... Think yap"), thinking)
self.assertEqual(sec("Modeli Qwen ... 3.8 Obliterated yap"), "ide/9router-nano-qwen-qwen3.8-27b-obliterated")
self.assertEqual(sec("modeli klod opus yap"), "ide/9router-cc-claude-opus-5-5") # not the "(no-think)" one
self.assertIn("adaylar", g.eslestir(g.komut_coz("modeli qwen 3.8 yap")["ad"], qwen)) # obliterated or uncensored: ask
def test_ambiguous_name_lists_candidates_and_changes_nothing(self):
self.assertEqual(pick("modeli agnes yap"), {"adaylar": ["agnes/agnes-2.0-flash", "agnes/agnes-2.5-flash"]})
self.assertIn("adaylar", pick("modeli klod opus yap")) # two Opus entries: ask
self.assertIn("adaylar", pick("modeli flash yap")) # several unrelated Flash models: ask
self.assertIn("adaylar", pick("modeli sol yap")) # GPT-6-Sol or GPT-6.1-Sol: ask
self.assertIn("adaylar", pick("modeli gpt 999 astra yap")) # words fit, version does not: ask