claude_aliases
def claude_aliases():Each Claude Code tier alias and the concrete id it tracks here: the family’s last, so newest, in CATALOG.
available_modelsCATALOG maps each provider to the model ids a picker offers before anything is asked over the network, in display order.
claude is what Claude Code’s --model accepts: the tier names fable, opus, sonnet and haiku, which follow the newest model of each family, then the full ids.anthropic is the same models through the Anthropic API.openai and gemini are the models offered through those APIs.codex is what Codex’s own picker lists.claude_ids returns the full Claude ids without the tier names. claude_aliases maps each tier name to the id it means here, which is the last id of that family in CATALOG. Adding a newer Opus to the end of the list moves opus to it.
Each Claude Code tier alias and the concrete id it tracks here: the family’s last, so newest, in CATALOG.
The concrete Claude Code ids in CATALOG, without the tier aliases.
test_eq(claude_aliases()['fable'], 'claude-fable-5-1')
test_eq(claude_aliases()['opus'], 'claude-opus-5-5')
assert 'claude-haiku-5-5' in claude_ids() and 'claude-haiku-5-5' in CATALOG['anthropic']
test_eq(claude_aliases()['haiku'], 'claude-haiku-5-5')
assert 'claude-sonnet-5-5' in claude_ids() and claude_aliases()['sonnet'] == 'claude-sonnet-5-5'
assert 'gpt-6.1-sol' in CATALOG['openai'] and CATALOG['openai'][0] == 'gpt-6-astra'
test_eq(set(claude_aliases()), {'fable', 'opus', 'sonnet', 'haiku'})
for p, ids in CATALOG.items(): test_eq(len(ids), len(set(ids))), pEach provider that can be asked for its models has one function in LIVE. The function returns the ids the provider lists, or [] when it cannot ask: no key, no file, no network, or an answer it cannot read. It never raises.
openai_ids asks OpenAI’s /v1/models with $OPENAI_API_KEY.anthropic_ids asks Anthropic’s /v1/models with $ANTHROPIC_API_KEY. It serves both anthropic and claude, since Claude Code runs the same models.codex_ids asks no server. Codex keeps its picker list in models_cache.json under $CODEX_HOME, which defaults to ~/.codex, and the entries marked visibility: 'list' are the ones it offers. The test below writes a small cache file and reads it back.Every id the current Anthropic key can list, or [] when it cannot be asked.
Every id the current OpenAI key can list, or [] when it cannot be asked.
import json, os, tempfile
with tempfile.TemporaryDirectory() as d:
cache = {'models': [{'slug': 'gpt-6-astra', 'visibility': 'list'}, {'slug': 'gpt-reserve', 'visibility': 'hide'}, {'slug': 'codex-auto-review', 'visibility': 'hide'}]}
(Path(d)/'models_cache.json').write_text(json.dumps(cache))
_old = os.environ.get('CODEX_HOME')
os.environ['CODEX_HOME'] = d
try: test_eq(codex_ids(), ['gpt-6-astra'])
finally:
if _old is None: del os.environ['CODEX_HOME']
else: os.environ['CODEX_HOME'] = _old
os.environ['CODEX_HOME'] = str(Path(d)/'nowhere')
try: test_eq(codex_ids(), [])
finally:
if _old is None: del os.environ['CODEX_HOME']
else: os.environ['CODEX_HOME'] = _oldA provider’s listing holds everything it serves: dated snapshots, fine-tunes, realtime and audio models, embeddings, moderation, image and search models. A picker wants none of these. _current decides which listed ids a picker may show. It hides an id by what kind of model it is, not by which generation it belongs to, so a new generation shows up the day the provider lists it. Older chat generations (gpt-4*, gpt-3.5*, claude-2-* and claude-3-*) appear only with legacy=True. The cell below lists ids it keeps, ids it always hides, and ids it shows only with legacy.
for ok in ('gpt-6-astra', 'gpt-7', 'o5-mini', 'claude-fable-5-1', 'claude-haiku-4-5-20251001', 'gemini-3.5-flash'): assert _current('openai', ok), ok
for hidden in ('gpt-6-astra-2026-09-01', 'gpt-3.5-turbo-0125', 'ft:gpt-3.5-turbo-0125:org::abc', 'ada:ft-org-2023-04-06-20-44-19', 'gpt-realtime', 'gpt-6-audio', 'gpt-image-2', 'gpt-6-transcribe', 'tts-1', 'gpt-live-1', 'gpt-3.5-turbo-instruct', 'text-embedding-4', 'omni-moderation-latest', 'dall-e-3', 'whisper-1', 'davinci-002', 'babbage-002', 'sora-2', 'computer-use-preview', 'gpt-5-search-api', 'gpt-4o-search-preview', 'o3-deep-research', 'chat-latest', ''):
assert not _current('openai', hidden), hidden
assert not _current('openai', hidden, legacy=True), hidden
for old in ('gpt-4.1', 'gpt-4o', 'gpt-3.5-turbo', 'claude-3-5-haiku-latest'):
assert not _current('openai', old) and _current('openai', old, legacy=True), oldprovider_models(p) is the list a picker shows for provider p. It starts with the CATALOG ids in their order, then adds the ids from the live listing that _current admits and that are not dated snapshots, newest first by version number, with nothing repeated. The live answer is remembered for five minutes with ramabana.core.probed. If the listing fails, the result is the catalog alone. A provider that is neither in CATALOG nor in LIVE gives [].
The first cell below stands in a fake OpenAI listing. The second makes every listing fail. The third fakes Anthropic’s endpoint and shows that claude and anthropic share one request.
Ids a picker offers for provider p: the curated ones in their order, then what live discovery adds.
from ramabana.core import forget_probes
_orig = dict(LIVE)
forget_probes()
LIVE['openai'] = lambda: ['gpt-6-astra', 'gpt-6-nova', 'gpt-6-nova-2026-09-01', 'gpt-6-audio', 'gpt-4.1']
try:
got = provider_models('openai')
test_eq(got[:len(CATALOG['openai'])], list(CATALOG['openai']))
test_eq(got[len(CATALOG['openai']):], ['gpt-6-nova'])
test_eq(got.count('gpt-6-astra'), 1)
assert 'gpt-4.1' in provider_models('openai', legacy=True)
# newest first, by the numbers: alphabetical put gpt-6.1 after every gpt-5
forget_probes(); LIVE['openai'] = lambda: ['gpt-5.1', 'gpt-6.1-nova', 'gpt-5.10']
test_eq(provider_models('openai')[len(CATALOG['openai']):], ['gpt-6.1-nova', 'gpt-5.10', 'gpt-5.1'])
finally: LIVE.clear(); LIVE.update(_orig); forget_probes()_n, _key, _lst = [], os.environ.get('ANTHROPIC_API_KEY'), _listed
def _fake(*a, **kw): _n.append(1); return ['claude-sonnet-6', 'claude-sonnet-6-20270101', 'claude-sonnet-5-5']
forget_probes(); os.environ['ANTHROPIC_API_KEY'] = 'k'; _listed = _fake
try:
got = provider_models('claude')
test_eq(got[:len(CATALOG['claude'])], list(CATALOG['claude']))
test_eq(got[len(CATALOG['claude']):], ['claude-sonnet-6'])
assert 'claude-sonnet-6' in provider_models('anthropic') and len(_n) == 1
forget_probes(); del os.environ['ANTHROPIC_API_KEY']
test_eq(provider_models('claude'), list(CATALOG['claude']))
finally:
_listed = _lst; forget_probes()
if _key is None: os.environ.pop('ANTHROPIC_API_KEY', None)
else: os.environ['ANTHROPIC_API_KEY'] = _keyavailable_modelsramabana.core.available_models builds its hosted rows from provider_models, and includes a provider only when runtime_available and auth_status say it can be used here. Each row has a value, label, provider and source. With a fake OpenAI key and listing, the OpenAI rows are the catalog followed by the one new model:
import ramabana.models as _m
from ramabana.core import available_models, CLAUDE, CLAUDE_ALIASES, CLAUDE_MODELS
_orig_live, _key = dict(_m.LIVE), os.environ.get('OPENAI_API_KEY')
forget_probes()
_m.LIVE.update({'openai': lambda: ['gpt-6-astra', 'gpt-6-nova', 'gpt-6-nova-2026-09-01', 'gpt-6-audio'], 'anthropic': lambda: [], 'codex': lambda: []})
os.environ['OPENAI_API_KEY'] = 'sk-not-a-real-key'
try:
rows = available_models()
test_eq({tuple(sorted(r)) for r in rows}, {('label', 'provider', 'source', 'value')})
openai = [r['label'] for r in rows if r['provider'] == 'openai']
test_eq(openai, [*CATALOG['openai'], 'gpt-6-nova'])
test_eq([r['value'] for r in rows if r['provider'] == 'openai'][0], 'openai/gpt-6-astra')
finally:
_m.LIVE.clear(); _m.LIVE.update(_orig_live); forget_probes()
if _key is None: del os.environ['OPENAI_API_KEY']
else: os.environ['OPENAI_API_KEY'] = _key
test_eq(CLAUDE_MODELS, claude_ids())
assert {'claude-fable-5-1', 'claude-opus-5-5'} <= set(CLAUDE_MODELS)
test_eq(CLAUDE_ALIASES['fable'], 'claude-fable-5-1')
test_eq(CLAUDE['claude/opus'], 'claude-opus-5-5')