Skip to main content

ai-switch

The problem it solves

When you use multiple coding agents and multiple model providers, changing providers separately in Claude Code and Codex is repetitive and error-prone. ai-switch updates both agents in one command.

Choose a provider profile once, then activate it with ai-switch use NAME. The built-in GLM and DeepSeek presets already contain the provider-specific files, endpoints, protocols, model catalogs, and Claude Code mappings recommended by their official documentation. You only need to enter your API key. Custom OpenAI-compatible providers are supported too.

A profile can offer several models (for example GLM 5.3 and GLM 5.3 Flash, or DeepSeek Flash and DeepSeek V4 Pro). ai-switch use asks which one to activate and writes the answer to both agents, so the model pickers inside Claude Code and Codex keep working instead of being pinned to a single model.

Switching also leaves conversation history alone: sessions, rollouts, history.jsonl and the agents' runtime databases are never copied into a profile or overwritten by a switch, and ai-switch doctor reports the things that really do make old sessions disappear.

This is a secure, headless-friendly command-line tool for servers without a desktop environment or administrator privileges.

Install

Install from PyPI (recommended):

python3 -m pip install --user ai-switch-cli

For a system or virtual-environment install:

pip install ai-switch-cli

Install the latest development version directly from GitHub:

python3 -m pip install --user "git+https://github.com/ZidongS/ai-switch.git"

Or install a local checkout:

python3 -m pip install --user .

If ai-switch is not found afterwards, add the user script directory to PATH:

export PATH="$HOME/.local/bin:$PATH"

Quick start

Save the configuration currently in use as a fallback profile:

ai-switch init default --description "Default daily configuration"

Create a ready-to-use mainstream provider profile interactively:

ai-switch add

Select glm or deepseek, enter the API key, and activate the generated configuration:

ai-switch use glm

use activates the profile and does not ask anything: the profile's whole model list is published, so you pick the model inside the agent with /model (Codex) or its Opus/Sonnet/Haiku categories (Claude Code). The default for new sessions is the one you last used, or the profile default. Add --choose if you want to be asked, or -m/--model to set the default directly.

  ⠸ ▰▰▰▰▰▰▱▱▱▱▱▱▱▱▱▱  38% 2/5  writing ~/.codex/models.json
  ✓ ▰▰▰▰▰▰▰▰▰▰▰▰▱▱▱▱  75% 4/5  write ~/.codex/models.json  3 model(s)
  ✓ ▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰ 100% 5/5  save profile state  glm is now the active profile
╭──────────────────────────────────────────────────────────╮
│ ✦ glm is active                                 (0.73s)  │
├──────────────────────────────────────────────────────────┤
│ profile glm                                              │
│ default glm-5.3                                          │
│ models  3 published  ·  switch inside codex with /model  │
│ backup  ~/.config/ai-switch/backups/20260915-203657      │
│ next    restart claude/codex to reload                   │
╰──────────────────────────────────────────────────────────╯

The bar is a colour ramp (cyan → green) with a bright leading edge and it always finishes completely filled at 100%, followed by a short sweep as the closing flourish.

The progress animation needs a terminal — piped output, TERM=dumb, --plain and --no-color keep the plain one-line-per-action form (handy for scripts). NO_COLOR=1 mutes the colours but keeps the spinner.

With --choose you get the picker instead:

This profile provides 3 models:
  1) glm-5.3         GLM-5.3 flagship (1M context)  [default]
  2) glm-5.3-flash   GLM-5.3 Flash (fast, text+image, 1M context)
  3) glm-5-turbo     GLM-5 Turbo (agent-optimized, 200K context)
Select model [1-3, Enter=glm-5.3, q=cancel]:

For scripts and servers, choose without prompting (name, unique prefix, or index):

ai-switch use glm --model glm-5.3-flash
ai-switch use glm -m 2 -y
ai-switch use glm --dry-run          # show what would change
ai-switch use glm -m glm-5.3 --pin   # publish only this model (see below)

Selecting a model sets the default for new sessions in both agents, and the profile's other models stay in the catalogue so you can still switch inside the agent. That means an in-app /model choice is a per-session override — and Codex writes its own choice back to ~/.codex/config.toml when it exits, so the two agents can drift apart without anything else noticing; ai-switch doctor reports exactly that. If you want an activation to be exclusive instead, add --pin: the published catalogue then holds only the selected model, so Codex's own picker has nothing else to switch to (the next ai-switch use without --pin restores the full menu).

Edit ~/.codex/config.toml and ~/.claude/settings.json for another provider, then save that configuration as a second profile:

ai-switch init glm --description "GLM Coding Plan"
ai-switch list
ai-switch use glm
ai-switch current

Create a profile through an interactive prompt (no editor required; API keys are hidden while typing). Choose the built-in glm preset to generate the complete ZAI Codex Responses configuration, Codex model catalog and Claude Code model/environment mappings for the three models the endpoint publishes (glm-5.3, glm-5.3-flash, glm-5-turbo) automatically. The deepseek preset creates the two model entries the DeepSeek endpoint currently accepts (deepseek-flash and deepseek-v4-pro, both with image input metadata) plus the recommended Claude Code mappings:

ai-switch add

Update an existing profile description:

ai-switch describe default "Default daily configuration"

list shows the active marker, profile name, configured clients, description, detected models, and endpoint hostnames. current prints a panel of every profile with its models, the default one (★), the endpoint and — for the active profile — whether the live agent configuration still matches:

ai-switch  ·  4 profile(s)  ·  ~/.config/ai-switch
────────────────────────────────────────────────────────────────────
  ○ glm   glm-coding-plan
      Codex   glm-5.3  · 3 models
      Claude  glm-5.3  · 2 Claude category mappings
      host    open.bigmodel.cn
      models  glm-5.3 ★, glm-5.3-flash, glm-5-turbo

  ● paratera   custom paratera   active
      Codex   DeepSeek-V4.1-Flash  · 31 models
      Claude  DeepSeek-V4.1-Flash  · opus/sonnet/haiku → DeepSeek-V4.1-Flash
      host    llmapi.paratera.com
      models  DeepSeek-V4.1-Flash ★, DeepSeek-V4-Pro, …, MiniMax-Text-01

Use current --plain to print just the active profile name. API keys are never printed.

Choosing a model

Each profile keeps its model list in models.json next to the client files:

ai-switch models                 # list the models of the active profile
ai-switch models glm --json      # machine readable
ai-switch upgrade glm            # derive models.json for a profile from an older release

For every model a profile records the Codex catalogue entry and the Claude Code mapping, so activating one model updates all of these consistently:

  • Codex: the top-level model (and model_reasoning_effort), plus a ~/.codex/models.json catalogue that lists every model of the profile, which is what makes Codex's own /model picker show them.
  • Claude Code: the default model in settings.json plus the Opus/Sonnet/Haiku mappings. The presets map the three categories to different provider models where they exist, and they never set ANTHROPIC_MODEL, because a pinned environment model overrides your selection and makes /model do nothing.

The glm and deepseek presets ship multi-model lists. A custom (OpenAI-compatible) profile is open-ended as well: when you create it you can name several models at once, separated by spaces or commas (for example DeepSeek-V4.1-Flash Kimi-K3 Qwen3.8-Max), and those become the profile's catalogue — both agents can then pick them. Any other name the endpoint serves still works with ai-switch use NAME --model <name>, and it is added to the catalogue when you use it.

When a provider adds or renames a model

Model names change, and an endpoint refuses a name it does not know. Ask the provider what your key may use:

curl -s https://api.deepseek.com/models      -H "Authorization: Bearer $KEY"   # OpenAI-style list
curl -s https://open.bigmodel.cn/api/v1/models -H "Authorization: Bearer $KEY"  # Codex-format catalogue

A rejected name is also reported in the API error itself ("The supported API model names are ...", or 模型不存在,请检查模型代码。). Some providers publish their Codex catalogue directly (/api/v1/models on ZAI returns exactly the entries a profile's codex-models.json wants), so it can be copied verbatim. Historically DeepSeek answered to deepseek-v4-flash and deepseek-v4-flash-vision-exp; today those are aliases that the endpoint resolves to deepseek-flash.

To add a model, append it to the profile's models.json — the claude mapping decides what Opus/Sonnet/Haiku resolve to, the codex entry controls whether Codex's own picker offers it. Keep the entries of names that older sessions were recorded with in the Codex catalogue so those sessions stay resumable, then activate:

ai-switch use deepseek -m deepseek-flash

Model suffixes are proxy-specific: DeepSeek's Claude endpoint accepts and strips a [1m] marker (deepseek-v4-pro[1m]), while ZAI rejects it as an unknown model — glm-5.3 is already 1M context and must be used without a suffix. Worth a one-token request before trusting a name in a profile.

Gateways that break Codex's tool calls

Codex 0.154 records an assistant message item between a turn's tool calls and their results — the text it said before calling the tools, or an empty string when it said nothing. That is valid in the Responses API, but a gateway that translates Responses into Chat completions emits it as its own chat message, which leaves the tool_calls message without the tool messages that must follow it directly. The next request is then rejected and the session dies:

OpenAIException - The request is invalid: An assistant message with 'tool_calls' must be
followed by tool messages responding to each 'tool_call_id'. (insufficient tool messages
following tool_calls message)

Paratera (llmapi.paratera.com) does this. Claude Code is unaffected, because it speaks the Anthropic API rather than the Responses API — only Codex hits the translation.

For such a profile, ai-switch use starts a small local proxy that moves that message back in front of the calls, points Codex at it, and stops it again when you activate a profile that does not need it:

Note: Codex will use the llmapi.paratera.com patch proxy on 127.0.0.1:8791
      (Paratera translates Responses into Chat completions and splits a turn's tool calls from their results).
  • The profile keeps the provider's real endpoint. Only the live ~/.codex/config.toml is repointed, and only Codex's own configuration — Claude Code still talks to the provider directly.
  • The proxy listens on 127.0.0.1 only, forwards your key and your request body untouched apart from the item order, and logs counts and status codes (never keys, prompts or answers) to ~/.config/ai-switch/patch.log.
  • If the process dies (a reboot, a kill), Codex cannot reach its endpoint. ai-switch doctor reports it and ai-switch use NAME starts it again. ai-switch current shows whether it is up.
  • Nothing here is needed once the gateway's translation is fixed: ai-switch use NAME --no-patch activates the profile without the proxy and stops it if it is running.

This is a workaround for the gateway, not a fix — the rewriting is deliberately narrow (only a completed calls + message + results turn, only on /responses), and anything else is forwarded byte for byte.

Session history

Conversation history belongs to the agents, not to a provider, so ai-switch never manages it. These files are deliberately excluded from profiles and switches:

~/.codex/history.jsonl, ~/.codex/session_index.jsonl, ~/.codex/sessions/,
~/.codex/*_N.sqlite (runtime state, logs, goals, memories)
~/.claude.json, ~/.claude/history.jsonl, ~/.claude/projects/, ~/.claude/sessions/

If sessions do seem to disappear after a switch, run:

ai-switch doctor          # full report; --json for tooling
ai-switch doctor --fix    # quarantine damaged Codex runtime databases

doctor checks, among other things:

  • Damaged SQLite runtime databases. Codex keeps thread metadata in ~/.codex/state_*.sqlite and thread_history_1.sqlite. When one is damaged (typical messages: database disk image is malformed, file is not a database) Codex silently stops listing recent sessions in codex resume. doctor detects this — reading a snapshot copy when the live file cannot be opened — and --fix moves the damaged files into ~/.config/ai-switch/quarantine/<timestamp>/. Codex rebuilds the database from the rollout files on the next start, which brings resumable sessions back. Rollouts and history are not touched.

  • Network filesystems. If CODEX_HOME lives on NFS/CIFS/SMB, SQLite databases are corrupted sooner or later, which is the usual root cause of "history lost after switching". Keep the runtime databases on a local disk instead:

    export CODEX_SQLITE_HOME=/var/tmp/codex-sqlite-$USER   # a local, persistent directory
    

    Codex only honours the environment variable: a sqlite_home key in config.toml is parsed but ignored (checked with codex doctor). ai-switch add asks for a local directory when it detects a network filesystem, creates it and prints the export line to put in your shell profile; ai-switch doctor reports the variable's status.

  • A stale model catalogue. A catalogue left behind by the previous provider hides the new provider's models from Codex and makes sessions that used them unresumable. ai-switch removes that leftover file (only if it wrote it itself, and it backs it up first), and doctor verifies the configured model exists in the catalogue.

  • Disabled history persistence ([history] persistence = "none"), a Claude Code model pinned by ANTHROPIC_MODEL, and claude/codex processes that are running while you switch and will rewrite their configuration on exit.

codex resume only lists sessions of the current directory by default; codex resume --all shows every directory. That cwd filter, not a lost session, is a common false alarm.

Safety and storage

Before activation, the current Codex and Claude files are backed up under ~/.config/ai-switch/backups/<timestamp>/ (files that a switch removes are kept under removed/). Profiles are stored under ~/.config/ai-switch/profiles/; directories use mode 700 and files use mode 600. The active profile, the last used model and the running gateway patch proxy (pid, port, endpoint) are recorded in ~/.config/ai-switch/state.json; that proxy's log is ~/.config/ai-switch/patch.log. Restart claude or codex after switching so the process reloads its configuration.

Set AI_SWITCH_HOME to use a different profile directory, CODEX_HOME/CLAUDE_CONFIG_DIR for relocated agent configurations, and AI_SWITCH_API_KEY to fill the API key prompt non-interactively.

Commands

ai-switch init NAME [-d DESCRIPTION]  Save current files as a new profile
ai-switch list                        List profiles and configuration summaries
ai-switch use NAME [-m MODEL] [--choose] [--pin] [--plain] [--no-patch]
                                      Back up and activate a profile (animated, -m sets the default)
ai-switch models [NAME] [--json]      Show the models a profile offers
ai-switch current [-p]                Show every profile, its models and the live state (-p: name only)
ai-switch describe NAME TEXT          Set a profile description
ai-switch upgrade NAME                Derive models.json for a profile from an older release
ai-switch add                         Create a profile interactively (GLM, DeepSeek, or custom)
ai-switch doctor [--fix] [--json]     Check configuration and session-history health
ai-switch --help                      Show full usage and examples

Upgrading from 0.1

Existing profiles keep working. Profiles that only have codex-models.json are read as before and get a model picker automatically; run ai-switch upgrade NAME (or re-create the profile with ai-switch add) to materialise an editable models.json with explicit per-model Claude Code mappings.

Development

python3 -m unittest -v

The project uses only the Python standard library.

Release files for ai-switch-cli 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ai-switch-cli 0.5.0
File Size Uploaded
ai_switch_cli-0.5.0.tar.gz 43.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ai-switch-cli 0.5.0
File Interpreter ABI Platform
ai_switch_cli-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 80.9 kB

Release files / ai_switch_cli-0.5.0.tar.gz

Download URL ai_switch_cli-0.5.0.tar.gz
Size 43.2 kB
Tags Source
SHA-256 checksum
How to use checksums
406b62babe83fbd15728aac5cca0be09e5f6bd78832aa9b8b8ed61a7faa5cd2d
BLAKE2b-256 checksum
How to use checksums
6fb34f49cda0e122cee69607beca1a12c7f9fb141eb9b46976a1a8eb838e1c1c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.9

Release files / ai_switch_cli-0.5.0-py3-none-any.whl

Download URL ai_switch_cli-0.5.0-py3-none-any.whl
Size 37.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
160fe03f6bc5e78b25c9da320ce8c12138736b34f8a7d74b545e05bd1801bcd6
BLAKE2b-256 checksum
How to use checksums
75e1fd8c0404d103bfec00c5fd28586004355830427bcec80d4bd102ac4b8bad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.9

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.3.2

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page