<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Misbah Syed</title>
    <description>The latest articles on DEV Community by Misbah Syed (@misbahsy).</description>
    <link>https://dev.to/misbahsy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4106691%2F5e681711-311b-4aa2-a534-183eb6a41553.jpg</url>
      <title>DEV Community: Misbah Syed</title>
      <link>https://dev.to/misbahsy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/misbahsy"/>
    <language>en</language>
    <item>
      <title>Set up LiteLLM Auto Router: send each prompt to the right model</title>
      <dc:creator>Misbah Syed</dc:creator>
      <pubDate>Mon, 14 Sep 2026 21:41:20 +0000</pubDate>
      <link>https://dev.to/misbahsy/set-up-litellm-auto-router-send-each-prompt-to-the-right-model-4cnj</link>
      <guid>https://dev.to/misbahsy/set-up-litellm-auto-router-send-each-prompt-to-the-right-model-4cnj</guid>
      <description>

&lt;p&gt;Most AI apps let you pick one model, and then every query goes to it. "What's the capital of France?" goes to that model. "Find the race condition in our billing service" goes to the same one. Your simplest and hardest queries cost the same, so you pay frontier prices for the easy ones.&lt;/p&gt;

&lt;p&gt;Auto Router fixes that at the gateway. You add it to LiteLLM as a model, give it a name like &lt;code&gt;smart-router&lt;/code&gt;, and your app calls that name like any other model. For each request, the router reads the prompt, puts it in one of four tiers (SIMPLE, MEDIUM, COMPLEX, REASONING) and sends it to the model you mapped to that tier. Simple prompts go to a cheap model and complex ones go to a frontier model.&lt;/p&gt;

&lt;p&gt;Your app doesn't change. The routing lives in one block of &lt;code&gt;config.yaml&lt;/code&gt;, so when a newer model comes out, swapping it in is a one-line change.&lt;/p&gt;

&lt;p&gt;This guide builds that config step by step and tests it with four curl requests.&lt;/p&gt;

&lt;p&gt;If you would like to watch this tutorial as a video, please follow the &lt;a href="https://youtu.be/blSVb26UQYg" rel="noopener noreferrer"&gt;YouTube li&lt;/a&gt;nk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An OpenAI API key and an Anthropic API key&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;openssl&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Port 4000 free on your machine&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: install LiteLLM
&lt;/h2&gt;

&lt;p&gt;If you don't have &lt;code&gt;uv&lt;/code&gt; yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the gateway as a uv tool. This guide was tested on 1.100.1, so pin it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'litellm[proxy]==1.100.1'&lt;/span&gt;
litellm &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the shell can't find &lt;code&gt;litellm&lt;/code&gt;, run &lt;code&gt;uv tool update-shell&lt;/code&gt; and open a new terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: create the project and a strong master key
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;auto-router &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;auto-router

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The master key is what every client uses to call your gateway, so treat it like a password. The docs use &lt;code&gt;sk-1234&lt;/code&gt; as a placeholder. Don't use that, or anything a person or an agent could guess. Generate a random one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"sk-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;.env&lt;/code&gt; and paste your values in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env
&lt;/span&gt;&lt;span class="py"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"your-openai-key"&lt;/span&gt;
&lt;span class="py"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"your-anthropic-key"&lt;/span&gt;
&lt;span class="py"&gt;LITELLM_MASTER_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sk-&amp;lt;the 64 hex characters you generated&amp;gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lock it down, keep it out of git, and load it into your shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 .env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .env &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: add your models
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;config.yaml&lt;/code&gt; with a plain LiteLLM model list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.6-luna&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.6-terra&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-sonnet-5&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic/claude-sonnet-5&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/ANTHROPIC_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-opus-5&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic/claude-opus-5&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/ANTHROPIC_API_KEY&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;os.environ/NAME&lt;/code&gt; reads the key from the environment, so no secret lives in the YAML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: add the router and map the tiers
&lt;/h2&gt;

&lt;p&gt;Append a fifth entry to &lt;code&gt;model_list&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;smart-router&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto_router/complexity_router&lt;/span&gt;
      &lt;span class="na"&gt;complexity_router_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;tiers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SIMPLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;
          &lt;span class="na"&gt;MEDIUM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;
          &lt;span class="na"&gt;COMPLEX&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-sonnet-5&lt;/span&gt;
          &lt;span class="na"&gt;REASONING&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-opus-5&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This alone is a working router. It scores each prompt locally from signals like length, code and reasoning phrases, then picks a tier. No extra model call is made. Each tier value is a &lt;code&gt;model_name&lt;/code&gt; from step 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: add keyword rules
&lt;/h2&gt;

&lt;p&gt;Some requests need a strong model no matter how they score. Add this under &lt;code&gt;complexity_router_config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;        &lt;span class="na"&gt;keyword_tier_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;review"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
            &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;COMPLEX&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any prompt containing "security review" or "incident" now goes to COMPLEX without scoring. Set &lt;code&gt;tier: REASONING&lt;/code&gt; if you want the top tier instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: let an LLM classify the unclear prompts
&lt;/h2&gt;

&lt;p&gt;Add under &lt;code&gt;complexity_router_config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;        &lt;span class="na"&gt;classifier_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;heuristic_first&lt;/span&gt;
        &lt;span class="na"&gt;heuristic_first_max_tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SIMPLE&lt;/span&gt;
        &lt;span class="na"&gt;classifier_llm_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;
          &lt;span class="na"&gt;timeout_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
          &lt;span class="na"&gt;classification_rubric&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agentic&lt;/span&gt;
        &lt;span class="na"&gt;classifier_fallback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;heuristic&lt;/span&gt;
        &lt;span class="na"&gt;classifier_context_window_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;heuristic_first&lt;/code&gt; with &lt;code&gt;heuristic_first_max_tier: SIMPLE&lt;/code&gt;: if the local scorer is confident a prompt is SIMPLE, it routes right away. Anything else goes to the classifier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;classifier_llm_config.model&lt;/code&gt;: the small model that does the classifying.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;classifier_fallback: heuristic&lt;/code&gt;: if the classifier call fails or times out, the router uses the local score.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;classifier_context_window_size: 3&lt;/code&gt;: the classifier sees the last three turns, so a follow-up like "now do the same for refunds" is judged in context.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 7: default model, escalation and housekeeping
&lt;/h2&gt;

&lt;p&gt;Add under &lt;code&gt;complexity_router_config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;        &lt;span class="na"&gt;default_model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;
        &lt;span class="na"&gt;escalation_keywords&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LITELLM&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ESCALATE"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;route_housekeeping_to_cheapest_tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;default_model&lt;/code&gt;: used when there is nothing to classify.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;escalation_keywords&lt;/code&gt;: a user or agent can include this phrase to push a request up one tier. It is case-sensitive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;route_housekeeping_to_cheapest_tier&lt;/code&gt;: coding agents rename sessions by sending the whole conversation and asking for a title. That text looks like hard work to a scorer. This sends those calls to the cheapest tier.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 8: gateway settings
&lt;/h2&gt;

&lt;p&gt;At the top level of &lt;code&gt;config.yaml&lt;/code&gt;, below &lt;code&gt;model_list&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;litellm_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;drop_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;general_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;master_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/LITELLM_MASTER_KEY&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;drop_params: true&lt;/code&gt; drops request parameters a provider doesn't support instead of failing the call. Coding agents often send those. The master key is read from the environment, not written in the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full config.yaml
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.6-luna&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.6-terra&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-sonnet-5&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic/claude-sonnet-5&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/ANTHROPIC_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-opus-5&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic/claude-opus-5&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/ANTHROPIC_API_KEY&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;smart-router&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto_router/complexity_router&lt;/span&gt;
      &lt;span class="na"&gt;complexity_router_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;tiers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SIMPLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;
          &lt;span class="na"&gt;MEDIUM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;
          &lt;span class="na"&gt;COMPLEX&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-sonnet-5&lt;/span&gt;
          &lt;span class="na"&gt;REASONING&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-opus-5&lt;/span&gt;

        &lt;span class="na"&gt;keyword_tier_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;review"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
            &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;COMPLEX&lt;/span&gt;

        &lt;span class="na"&gt;classifier_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;heuristic_first&lt;/span&gt;
        &lt;span class="na"&gt;heuristic_first_max_tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SIMPLE&lt;/span&gt;
        &lt;span class="na"&gt;classifier_llm_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;
          &lt;span class="na"&gt;timeout_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
          &lt;span class="na"&gt;classification_rubric&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agentic&lt;/span&gt;
        &lt;span class="na"&gt;classifier_fallback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;heuristic&lt;/span&gt;
        &lt;span class="na"&gt;classifier_context_window_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

        &lt;span class="na"&gt;default_model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;
        &lt;span class="na"&gt;escalation_keywords&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LITELLM&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ESCALATE"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;route_housekeeping_to_cheapest_tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;litellm_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;drop_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;general_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;master_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/LITELLM_MASTER_KEY&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 9: start the gateway
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LITELLM_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;INFO litellm &lt;span class="nt"&gt;--config&lt;/span&gt; config.yaml &lt;span class="nt"&gt;--port&lt;/span&gt; 4000

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;LITELLM_LOG=INFO&lt;/code&gt; prints a &lt;code&gt;routing decision&lt;/code&gt; line for every request, so you can watch the router work in this terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 10: send four prompts
&lt;/h2&gt;

&lt;p&gt;Open a second terminal in the &lt;code&gt;auto-router&lt;/code&gt; folder and load the key there too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;auto-router
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .env &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response body still says &lt;code&gt;"model": "smart-router"&lt;/code&gt;. The model that answered comes back in the &lt;code&gt;x-litellm-model-name&lt;/code&gt; header, with its cost in &lt;code&gt;x-litellm-response-cost&lt;/code&gt;. Each command below prints only those headers. Remove the &lt;code&gt;| grep ...&lt;/code&gt; part to see the full response.&lt;/p&gt;

&lt;p&gt;The outputs are from one run. Your costs will differ.&lt;/p&gt;

&lt;h3&gt;
  
  
  A simple question
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-si&lt;/span&gt; http://localhost:4000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$LITELLM_MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model": "smart-router", "messages": [{"role": "user", "content": "What is the capital of Japan? Keep it brief."}]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'^x-litellm-(model-name|response-cost|classifier-cost):'&lt;/span&gt;


x-litellm-model-name: openai/gpt-5.6-luna
x-litellm-response-cost: 9.4e-06

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the gateway terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ComplexityRouter: routing decision cause=heuristic_first_short_circuit, tier=SIMPLE, score=-0.150, signals=('short (11 tokens)', 'simple (what is, brief)'), routed_model=gpt-5.6-luna

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scorer was confident, so it went to the cheap model with no classifier call.&lt;/p&gt;

&lt;h3&gt;
  
  
  A keyword match
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-si&lt;/span&gt; http://localhost:4000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$LITELLM_MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model": "smart-router", "messages": [{"role": "user", "content": "Do a security review of this login handler: it compares the password with == and logs the request body."}]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'^x-litellm-(model-name|response-cost|classifier-cost):'&lt;/span&gt;


x-litellm-model-name: anthropic/claude-sonnet-5
x-litellm-response-cost: 0.010412000000000001


ComplexityRouter: routing decision &lt;span class="nv"&gt;cause&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;literal_keyword_match, &lt;span class="nv"&gt;escalated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;False, &lt;span class="nv"&gt;tier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMPLEX, &lt;span class="nv"&gt;routed_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;claude-sonnet-5

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"security review" matched the rule from step 5.&lt;/p&gt;

&lt;h3&gt;
  
  
  A prompt the classifier decides
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-si&lt;/span&gt; http://localhost:4000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$LITELLM_MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model": "smart-router", "messages": [{"role": "user", "content": "Our billing service double charges about one customer a week. Figure out where retries and webhooks could race, and propose a fix we can ship without downtime."}]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'^x-litellm-(model-name|response-cost|classifier-cost):'&lt;/span&gt;


x-litellm-model-name: anthropic/claude-sonnet-5
x-litellm-response-cost: 0.019232000000000003
x-litellm-classifier-cost: 0.0002036


ComplexityRouter: routing decision &lt;span class="nv"&gt;cause&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;llm_classifier, &lt;span class="nv"&gt;tier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMPLEX, &lt;span class="nv"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;n/a, &lt;span class="nv"&gt;signals&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s1"&gt;'llm-classifier:COMPLEX'&lt;/span&gt;,&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;routed_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;claude-sonnet-5

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No keyword matched and the scorer wasn't sure, so &lt;code&gt;gpt-5.6-luna&lt;/code&gt; classified it as COMPLEX. That's the only request with a classifier cost. The classifier is a model call, so another run can pick a different tier.&lt;/p&gt;

&lt;h3&gt;
  
  
  A housekeeping call
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-si&lt;/span&gt; http://localhost:4000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$LITELLM_MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model": "smart-router", "messages": [{"role": "user", "content": "You are coming up with a succinct title for a coding session. Session: refactor the distributed payment reconciliation architecture, fix concurrency bugs in the async webhook handler, add benchmarks."}]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'^x-litellm-(model-name|response-cost|classifier-cost):'&lt;/span&gt;


x-litellm-model-name: openai/gpt-5.6-luna
x-litellm-response-cost: 2.38e-05


ComplexityRouter: routing decision &lt;span class="nv"&gt;cause&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;housekeeping, &lt;span class="nv"&gt;tier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SIMPLE, &lt;span class="nv"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;n/a, &lt;span class="nv"&gt;signals&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s1"&gt;'housekeeping'&lt;/span&gt;,&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;routed_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gpt-5.6-luna

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt is full of words that would score high, but it's a session title request, so it went to the cheapest tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start simple
&lt;/h2&gt;

&lt;p&gt;Try the router with the prompts you send from your AI tools and coding agents. Start with only the tiers from step 4. Add keyword rules for the requests you care about, then add the LLM classifier once you need better decisions on unclear prompts.&lt;/p&gt;

&lt;p&gt;The full option reference is on the &lt;a href="https://docs.litellm.ai/docs/proxy/auto_routing" rel="noopener noreferrer"&gt;Auto Routing docs page&lt;/a&gt;, and the code is in &lt;a href="https://github.com/BerriAI/litellm" rel="noopener noreferrer"&gt;BerriAI/litellm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;LiteLLM docs: &lt;a href="https://docs.litellm.ai" rel="noopener noreferrer"&gt;&lt;strong&gt;https://docs.litellm.ai&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AutoRouter tutorial page: &lt;a href="https://docs.litellm.ai/docs/proxy/auto_routing" rel="noopener noreferrer"&gt;https://docs.litellm.ai/docs/proxy/auto_routing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LiteLLM on GitHub: &lt;a href="https://github.com/BerriAI/litellm" rel="noopener noreferrer"&gt;&lt;strong&gt;https://github.com/BerriAI/litellm&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Video walkthrough: &lt;a href="https://youtu.be/blSVb26UQYg" rel="noopener noreferrer"&gt;&lt;strong&gt;Watch it on YouTube&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>autorouter</category>
      <category>litellm</category>
      <category>gateway</category>
    </item>
    <item>
      <title>How to set up Cursor with LiteLLM</title>
      <dc:creator>Misbah Syed</dc:creator>
      <pubDate>Thu, 10 Sep 2026 18:36:50 +0000</pubDate>
      <link>https://dev.to/misbahsy/how-to-set-up-cursor-with-litellm-3i0l</link>
      <guid>https://dev.to/misbahsy/how-to-set-up-cursor-with-litellm-3i0l</guid>
      <description>&lt;p&gt;This guide connects Cursor to a LiteLLM proxy, one you run yourself or one your team already hosts. Cursor takes one base URL and one API key in its settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tldr version&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Cursor Settings, then Models, then expand API Keys&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Turn on Override OpenAI Base URL and set it to &lt;code&gt;https://your-gateway.example.com/cursor&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Turn on OpenAI API Key, paste a LiteLLM key, confirm the prompt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add Custom Model, and type the &lt;code&gt;model_name&lt;/code&gt; from your &lt;code&gt;config.yaml&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pick that model in the model picker and send a message&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you prefer to watch the tutorial instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/oNkMgelVsEA" rel="noopener noreferrer"&gt;Watch it on YouTube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The steps below are the ones in the video, so you can move between the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;You need Cursor recent enough to have a &lt;strong&gt;Models&lt;/strong&gt; page under Cursor Settings, and a LiteLLM gateway. Docker builds that gateway in section 6 if you do not have one; skip Docker entirely if your team already hosts one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The one that catches people out.&lt;/strong&gt; The gateway has to be reachable from the public internet. Cursor does not call your model from your laptop, its own backend makes the request, so &lt;code&gt;http://localhost:4000&lt;/code&gt; is not reachable. Section 7 provides tips on how to make a publicly available URL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Versions used: LiteLLM &lt;code&gt;main-stable&lt;/code&gt;, UI v1.99.0, DB Postgres 16-alpine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get started!
&lt;/h2&gt;

&lt;p&gt;If a gateway is already running somewhere your Cursor can reach, sections 1 to 5 are enough. If you don't have a gateway running, jump to section 6, build the gateway, then come back to section 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Open the Models page in Cursor
&lt;/h2&gt;

&lt;p&gt;The gear icon in the top right opens Cursor Settings. Everything in this guide lives under &lt;strong&gt;Models&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg3eawzzsqafci90k1768.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg3eawzzsqafci90k1768.png" alt="Cursor Settings open on the Models page, where every step in this guide happens" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Point Cursor at the gateway
&lt;/h2&gt;

&lt;p&gt;Scroll down to &lt;strong&gt;API Keys&lt;/strong&gt; and expand it. Then put your gateway URL in the base URL field with &lt;code&gt;/cursor&lt;/code&gt; on the end, and the per-user API key from the LiteLLM gateway(more on it below). Turn on: &lt;strong&gt;OpenAI API Key&lt;/strong&gt; , and &lt;strong&gt;Override OpenAI Base URL toggles&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://your-gateway.example.com/cursor

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwdw3rrlx9bdafcr090y4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwdw3rrlx9bdafcr090y4.png" width="799" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/cursor&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; Is the route LiteLLM exposes for Cursor specifically.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Add your key and enable it
&lt;/h2&gt;

&lt;p&gt;Paste a LiteLLM key into the &lt;strong&gt;OpenAI API Key&lt;/strong&gt; field and save it. Use a virtual key from the gateway's Virtual Keys page if you have one.&lt;/p&gt;

&lt;p&gt;Cursor then asks you to confirm. Say yes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yz63xkmwrrbkol8zf1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yz63xkmwrrbkol8zf1m.png" alt="Cursor's confirmation dialog after saving a LiteLLM key, warning that Tab, Apply from Chat and Agent stay on Cursor's own models" width="799" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dialog warns that Tab, Apply from Chat and Agent run on Cursor's own models and cannot be billed to your key. Whereas, Chat and the model picker route through your gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Add the model names your gateway serves
&lt;/h2&gt;

&lt;p&gt;Cursor's model list is its own. If a &lt;code&gt;model_name&lt;/code&gt; from your config happens to match something Cursor already lists, it shows up on its own. In some case, it might not allow to use the same model names as the built-in model names in Cursor. In that case, you will have to add a custom model name.&lt;/p&gt;

&lt;p&gt;Scroll to the bottom of the model list, click &lt;strong&gt;Add Custom Model&lt;/strong&gt; , and type the name exactly as it appears in &lt;code&gt;config.yaml&lt;/code&gt; or public model names from the created models in the gateway.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faqyfrqwwpqlfzq06auyd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faqyfrqwwpqlfzq06auyd.png" alt="The Add Custom Model box in Cursor with a gateway model name typed exactly as it appears in config.yaml" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Send a message and check the logs
&lt;/h2&gt;

&lt;p&gt;Open a chat, click the model picker, and pick one of your models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkl01lwvr6c68wwck7yl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkl01lwvr6c68wwck7yl9.png" alt="Cursor's chat model picker with a gateway-served model selected" width="800" height="631"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now go to the gateway UI, open &lt;strong&gt;Logs&lt;/strong&gt; under Observability, and narrow the time filter to the last fifteen minutes. Your request is there with a cost against it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwi8015wstxwvc86stifa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwi8015wstxwvc86stifa.png" alt="The Logs page in the LiteLLM UI, filtered to the last fifteen minutes, showing the request Cursor just sent" width="799" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click a row for the detail and now you can see which key sent it, how many tokens went out and came back, what it cost, how long it took, and how much of the prompt was served from cache.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F68tt0eznc0osetfib9qc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F68tt0eznc0osetfib9qc.png" alt="A single LiteLLM log entry expanded, showing the key, token counts, cost, latency and cache usage" width="800" height="906"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Tags row shows &lt;code&gt;User-Agent: Cursor&lt;/code&gt;, which indicates that it was Cursor traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. No gateway yet? Let's run one
&lt;/h2&gt;

&lt;p&gt;Start a new project directory, or create these three files in your existing project directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  config.yaml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm-gpt-5.6-terra&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.6-terra&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm-claude-sonnet-5&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic/claude-sonnet-5&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/ANTHROPIC_API_KEY&lt;/span&gt;

&lt;span class="na"&gt;litellm_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;drop_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;general_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;master_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/LITELLM_MASTER_KEY&lt;/span&gt;

  &lt;span class="c1"&gt;# Coding turns stream for a long time. Do not cut them off.&lt;/span&gt;
  &lt;span class="na"&gt;request_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;

&lt;span class="na"&gt;router_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;num_retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;cooldown_time&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;model_name&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; This is the name Cursor sends and the name you type into Add Custom Model. Prefixing them with &lt;code&gt;litellm-&lt;/code&gt; keeps your model names from colliding with Cursor's built-in list, and when you see one in a log you know where it came from.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;model&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; The provider's own name for the model, with the provider in front. LiteLLM uses this to decide which API to call and how to shape the request. You can find the full list of &lt;a href="https://models.litellm.ai" rel="noopener noreferrer"&gt;models supported here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drop_params: true&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; Coding agents send parameters that not every provider accepts. This line drops them instead of failing the request.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;request_timeout: 600&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; An agentic turn can stream for minutes. The default cuts it off well before that.&lt;/p&gt;

&lt;h3&gt;
  
  
  .env
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;LITELLM_MASTER_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sk-your-own-long-random-string&lt;/span&gt;
&lt;span class="py"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sk-...&lt;/span&gt;
&lt;span class="py"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sk-ant-...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The master key is the only required variable, along with any provider api key needed for the models listed in the config.yaml.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker-compose.yml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm-coding-agents&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;litellm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/berriai/litellm:main-stable&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--config"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/app/config.yaml"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--port"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4000"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4000:4000"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./config.yaml:/app/config.yaml:ro&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql://litellm:litellm@postgres:5432/litellm&lt;/span&gt;
      &lt;span class="na"&gt;STORE_MODEL_IN_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;True"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-c&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;import&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;urllib.request,sys;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sys.exit(0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;urllib.request.urlopen('http://localhost:4000/health/liveliness').status==200&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="c1"&gt;# Deliberately not published to the host. Nothing outside this network&lt;/span&gt;
    &lt;span class="c1"&gt;# needs it, and a 5432 collision is an annoying afternoon.&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;litellm-pgdata:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;litellm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-d&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;litellm"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;litellm-pgdata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postgres DB allows UI access to make virtual keys, budgets and the logs we saw in section 5.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;STORE_MODEL_IN_DB: "True"&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; Lets you add models from the UI. Models added that way live in the database.&lt;/p&gt;

&lt;p&gt;Bring it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then check it is actually serving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:4000/health/liveliness

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want &lt;code&gt;"I'm alive!"&lt;/code&gt; back.&lt;/p&gt;

&lt;p&gt;A quick tip: if something else already holds port 4000, find it with &lt;code&gt;lsof -nP -iTCP:4000 -sTCP:LISTEN&lt;/code&gt; and either stop it or change the published port in &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Give the local gateway a public URL
&lt;/h2&gt;

&lt;p&gt;Cursor's backend has to reach the gateway, so a local port needs a tunnel in front of it. You can assign a public URL to your locally deployed gateway using either ngrok or cloudflared.&lt;/p&gt;

&lt;p&gt;ngrok:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 4000 &lt;span class="nt"&gt;--log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stdout

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgz5syzur4214owytbbrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgz5syzur4214owytbbrq.png" alt="An ngrok session in the terminal printing the public HTTPS forwarding URL for port 4000" width="800" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;cloudflared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloudflared tunnel &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:4000

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Either one prints an HTTPS URL. That URL, plus &lt;code&gt;/cursor&lt;/code&gt; goes into the base URL field from section 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qsr2u3mhmdbhpv3j6av.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qsr2u3mhmdbhpv3j6av.png" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A free tunnel URL changes every time you restart the tunnel, and Cursor will need the new one. For anything beyond trying this out, put the gateway on a host with a real domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Adding a model from the LiteLLM UI
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;STORE_MODEL_IN_DB&lt;/code&gt; on in docker-compose.yml, &lt;strong&gt;Models + Endpoints&lt;/strong&gt; has an &lt;strong&gt;Add Model&lt;/strong&gt; tab that writes to the database instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhddpfnznj6glujsah4jw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhddpfnznj6glujsah4jw.png" alt="The Add Model tab in LiteLLM's Models and Endpoints page, showing the LiteLLM Model Name and Public Model Name fields" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LiteLLM Model Name&lt;/strong&gt; is the provider's name for the model, the string LiteLLM will send upstream. &lt;strong&gt;Public Model Name&lt;/strong&gt; is what we can assing and what Cursor sends. Mapping &lt;code&gt;litellm-gpt-5.6-luna&lt;/code&gt; to &lt;code&gt;gpt-5.6-luna&lt;/code&gt; is the UI equivalent of the &lt;code&gt;model_name&lt;/code&gt; / &lt;code&gt;model&lt;/code&gt; pair in &lt;code&gt;config.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Scroll down for the credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxi7qq0tjsnzwsdz76elc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxi7qq0tjsnzwsdz76elc.png" alt="The credentials fields further down the LiteLLM Add Model form" width="799" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save it, add the public model name to Cursor with &lt;strong&gt;Add Custom Model&lt;/strong&gt; the way you did in section 4. Test a short prompt in the chat and the model sends the response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1cwxs12b4ioim7739zo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1cwxs12b4ioim7739zo.png" alt="A Cursor chat answering through the model that was just added from the LiteLLM UI" width="798" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manage usage with virtual keys
&lt;/h3&gt;

&lt;p&gt;Create a key per developer under &lt;strong&gt;Virtual Keys&lt;/strong&gt; , give each one a budget and a list of models it is allowed to use, and hand that to your teammates. The Key Alias column in the logs gives you details of the usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using different models
&lt;/h3&gt;

&lt;p&gt;Anything LiteLLM supports works here, not just OpenAI. Add a block to &lt;code&gt;model_list&lt;/code&gt;, give it a &lt;code&gt;model_name&lt;/code&gt; that starts with &lt;code&gt;litellm-&lt;/code&gt;, restart, and add that name to Cursor. Cursor is talking to an OpenAI-shaped endpoint, and it could Claude or Gemini or a local model.&lt;/p&gt;

&lt;h3&gt;
  
  
  No base URL field? Use the Azure OpenAI panel
&lt;/h3&gt;

&lt;p&gt;Some Cursor builds do not show &lt;strong&gt;Override OpenAI Base URL&lt;/strong&gt;. The &lt;strong&gt;Azure OpenAI&lt;/strong&gt; panel underneath it takes the same three pieces of information and gets you to the same place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2iguemd077cwcc6720v7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2iguemd077cwcc6720v7.png" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One difference, and it is easy to miss: &lt;strong&gt;do not&lt;/strong&gt; append &lt;code&gt;/cursor&lt;/code&gt; here. Base URL is the bare gateway URL. Deployment Name is your public model name, and API Key is the LiteLLM key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;LiteLLM docs: &lt;a href="https://docs.litellm.ai" rel="noopener noreferrer"&gt;https://docs.litellm.ai&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cursor tutorial page: &lt;a href="https://docs.litellm.ai/docs/tutorials/cursor%5C_integration" rel="noopener noreferrer"&gt;https://docs.litellm.ai/docs/tutorials/cursor_integration&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gateway quick start: &lt;a href="https://docs.litellm.ai/docs/proxy/quick%5C_start" rel="noopener noreferrer"&gt;https://docs.litellm.ai/docs/proxy/quick_start&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Docker quick start: &lt;a href="https://docs.litellm.ai/docs/proxy/docker%5C_quick%5C_start" rel="noopener noreferrer"&gt;https://docs.litellm.ai/docs/proxy/docker_quick_start&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LiteLLM on GitHub: &lt;a href="https://github.com/BerriAI/litellm" rel="noopener noreferrer"&gt;https://github.com/BerriAI/litellm&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Video walkthrough: &lt;a href="https://youtu.be/oNkMgelVsEA" rel="noopener noreferrer"&gt;Watch it on YouTube&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>litellm</category>
      <category>cursor</category>
    </item>
    <item>
      <title>How to set up OpenAI Codex with LiteLLM</title>
      <dc:creator>Misbah Syed</dc:creator>
      <pubDate>Wed, 09 Sep 2026 17:20:51 +0000</pubDate>
      <link>https://dev.to/misbahsy/how-to-set-up-openai-codex-with-litellm-485l</link>
      <guid>https://dev.to/misbahsy/how-to-set-up-openai-codex-with-litellm-485l</guid>
      <description>&lt;p&gt;This guide connects OpenAI Codex to a LiteLLM proxy, one you run yourself or one your team already hosts. Codex reads one base URL out of a config file, so pointing it at LiteLLM gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Any model LiteLLM can reach, from the same Codex CLI and desktop app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A log line and a cost for every request, in one place&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Per-developer keys with their own budgets and their own model access&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One provider key on the server instead of a copy on every machine&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The five-minute walkthrough this guide is written from:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/avKQz7VqJZE" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The steps below are the ones in the video, so you can move between the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Docker, for the setting up LiteLLM gateway. Not needed if you are pointing at a gateway already hosted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Node, for the Codex CLI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One provider key. One is enough to start&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything below runs on the open-source image, &lt;code&gt;ghcr.io/berriai/litellm:main-stable&lt;/code&gt;. The admin UI, virtual keys and spend tracking come from having a database attached, which the compose file does.&lt;/p&gt;

&lt;p&gt;Versions used here: litellm 1.99.0, current &lt;code&gt;@openai/codex&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's go!
&lt;/h2&gt;

&lt;p&gt;If your team already hosts a LiteLLM gateway, skip steps 2 and 3 below.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install OpenAI Codex
&lt;/h2&gt;

&lt;p&gt;Install the Codex CLI globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @openai/codex

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Configure LiteLLM for model routing
&lt;/h2&gt;

&lt;p&gt;Make a directory for the LiteLLM proxy and put &lt;code&gt;config.yaml&lt;/code&gt; in it. This file is the model list: the names your clients ask for, and where each one goes.&lt;/p&gt;

&lt;h3&gt;
  
  
  config.yaml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# `codex -m &amp;lt;id&amp;gt;` works with any of them.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.6-terra&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.6-luna&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-5.6-sol&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-5.6-sol&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;

  &lt;span class="c1"&gt;# Anything from another provider goes in the same list.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-sonnet-5&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic/claude-sonnet-5&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/ANTHROPIC_API_KEY&lt;/span&gt;

  &lt;span class="c1"&gt;# Catch-all, so `codex -m &amp;lt;anything&amp;gt;` reaches OpenAI without editing&lt;/span&gt;
  &lt;span class="c1"&gt;# this file. Delete this entry to restrict a fleet to the list above.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
    &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/*&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/OPENAI_API_KEY&lt;/span&gt;

&lt;span class="na"&gt;litellm_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;drop_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;general_settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;master_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;os.environ/LITELLM_MASTER_KEY&lt;/span&gt;
  &lt;span class="c1"&gt;# Coding turns stream for a long time. Do not cut them off.&lt;/span&gt;
  &lt;span class="na"&gt;request_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could as many model names to this list from the ones &lt;a href="https://models.litellm.ai/" rel="noopener noreferrer"&gt;supported by LiteLLM&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drop_params: true&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; Coding agents sometimes send parameters that not every provider accepts, which is dropped by this line.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;master_key&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; This is the key to access the proxy.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Start the LiteLLM proxy
&lt;/h2&gt;

&lt;p&gt;We'll start the LiteLLM gateway along with a postgres database, so that we can access the dashboard UI for the gateway..&lt;/p&gt;

&lt;h3&gt;
  
  
  docker-compose.yml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm-coding-agents&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;litellm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/berriai/litellm:main-stable&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--config"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/app/config.yaml"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--port"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4000"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4000:4000"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./config.yaml:/app/config.yaml:ro&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql://litellm:litellm@postgres:5432/litellm&lt;/span&gt;
      &lt;span class="na"&gt;STORE_MODEL_IN_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;True"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-c&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;import&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;urllib.request,sys;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sys.exit(0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;urllib.request.urlopen('http://localhost:4000/health/liveliness').status==200&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="c1"&gt;# Deliberately not published to the host. Nothing outside this&lt;/span&gt;
    &lt;span class="c1"&gt;# network needs it, and 5432 is usually already taken.&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;litellm-pgdata:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;litellm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-d&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;litellm"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;litellm-pgdata&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provider keys go in &lt;code&gt;.env&lt;/code&gt; next to the compose file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;
&lt;span class="py"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;
&lt;span class="py"&gt;LITELLM_MASTER_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sk-your-strong-string&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sk-your-strong-string&lt;/code&gt; is the placeholder master key used in the LiteLLM docs. Replace it with a real secret key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bring it up
&lt;/h3&gt;

&lt;p&gt;Start the proxy and its database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First boot runs the database migrations and takes about eighty seconds. Every boot after that is about fifteen (depending on the machine spec).&lt;/p&gt;

&lt;p&gt;Check that it is alive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:4000/health/liveliness

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that it is serving the model list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:4000/v1/models &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk-your-strong-string"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 400

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick tip: If something else is running on port 4000, you can check using this command before running docker compose. You could either stop that process or run LiteLLM gateway on a different port but will have to adjust rest of the tutorial accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsof &lt;span class="nt"&gt;-nP&lt;/span&gt; &lt;span class="nt"&gt;-iTCP&lt;/span&gt;:4000 &lt;span class="nt"&gt;-sTCP&lt;/span&gt;:LISTEN

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The proxy is up. Now we will look at the Codex side.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Configure Codex to use LiteLLM
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ~/.codex/config.toml
&lt;/h3&gt;

&lt;p&gt;It's a good idea to back up the existing codex config file first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; ~/.codex/config.toml ~/.codex/config.toml.bak 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then edit the codex config file as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gpt-5.6-terra"&lt;/span&gt;
&lt;span class="py"&gt;model_provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"litellm"&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_effort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"medium"&lt;/span&gt;

&lt;span class="nn"&gt;[model_providers.litellm]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"LiteLLM Gateway"&lt;/span&gt;
&lt;span class="py"&gt;base_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:4000/v1"&lt;/span&gt;
&lt;span class="c"&gt;# More on setting up LITELLM_API_KEY towards the end&lt;/span&gt;
&lt;span class="py"&gt;env_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"LITELLM_API_KEY"&lt;/span&gt;
&lt;span class="c"&gt;# Codex talks the Responses API. &lt;/span&gt;
&lt;span class="py"&gt;wire_api&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"responses"&lt;/span&gt;
&lt;span class="c"&gt;# A coding turn can stream for long time, hence the setting below&lt;/span&gt;
&lt;span class="py"&gt;stream_idle_timeout_ms&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7200000&lt;/span&gt;
&lt;span class="py"&gt;stream_max_retries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;request_max_retries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="c"&gt;# Codex cli complains if not in a trusted folder&lt;/span&gt;
&lt;span class="nn"&gt;[projects."/path/to/your/project"]&lt;/span&gt;
&lt;span class="py"&gt;trust_level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"trusted"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;model_provider = "litellm"&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; Has to match the &lt;code&gt;[model_providers.&amp;lt;name&amp;gt;]&lt;/code&gt; table below it. Any name works, as long as they both match.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wire_api = "responses"&lt;/code&gt; &lt;strong&gt;.&lt;/strong&gt; Codex uses the Responses API, whereas the default for a custom provider is chat completions.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Run Codex
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"reply with the word CONNECTED and nothing else"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Codex is now runs against your proxy, on your provider key, with your model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frii9ct2kod69dxc5v34j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frii9ct2kod69dxc5v34j.png" width="758" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or you can start a codex session in the terminal with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:4000/ui&lt;/code&gt;, sign in with the master key, and the request appears in the Logs tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Use the Codex desktop app
&lt;/h2&gt;

&lt;p&gt;The Codex desktop app reads the same &lt;code&gt;~/.codex/config.toml&lt;/code&gt;. Nothing new to write.&lt;/p&gt;

&lt;p&gt;But it will not see your environment variable. macOS hands apps launched from Finder or the Dock the GUI login session's environment, not your shell's. You can run this command to set env variable for the login session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;launchctl setenv LITELLM_API_KEY &lt;span class="s2"&gt;"sk-your-strong-string"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then fully quit and reopen the codex app. That holds until you log out. Make it a LaunchAgent if you want it permanent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fslogowm5arn6ejkhojro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fslogowm5arn6ejkhojro.png" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manage usage with virtual keys
&lt;/h3&gt;

&lt;p&gt;Clients get a virtual key, issued from the Virtual Keys page in the admin UI at &lt;a href="http://localhost:4000/ui" rel="noopener noreferrer"&gt;http://localhost:4000/ui&lt;/a&gt;, with its own budget and its own list of allowed models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmowu41ej6zmtot3rdxem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmowu41ej6zmtot3rdxem.png" width="798" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Issue one per person, per machine or per project, then set LITELLM_API_KEY to that key instead:&lt;/p&gt;

&lt;p&gt;export LITELLM_API_KEY=""&lt;/p&gt;

&lt;p&gt;Three things follow from that. Spend in the Logs tab is broken down by key, so you can see which project is costing what. A budget on the key stops it rather than surprising you at the end of the month. And if a key leaks, you can revoke that one key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using different models
&lt;/h3&gt;

&lt;p&gt;Any public name in the model list works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nt"&gt;-m&lt;/span&gt; gpt-5.6-luna

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Profiles are saved model and reasoning-effort pairs, that can get you started with quickly.&lt;/p&gt;

&lt;p&gt;In your ~/.codex/config.toml add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Optional profiles &lt;/span&gt;
&lt;span class="c"&gt;# Saved model plus reasoning-effort pairs. `codex --profile deep`.&lt;/span&gt;
&lt;span class="nn"&gt;[profiles.fast]&lt;/span&gt;
&lt;span class="py"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gpt-5.6-luna"&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_effort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"low"&lt;/span&gt;

&lt;span class="nn"&gt;[profiles.deep]&lt;/span&gt;
&lt;span class="py"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gpt-5.6-sol"&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_effort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"high"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run a codex session with a specific profile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nt"&gt;--profile&lt;/span&gt; deep

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Additional resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;LiteLLM docs: &lt;a href="https://docs.litellm.ai" rel="noopener noreferrer"&gt;https://docs.litellm.ai&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Codex tutorial page: &lt;a href="https://docs.litellm.ai/docs/tutorials/openai%5C_codex" rel="noopener noreferrer"&gt;https://docs.litellm.ai/docs/tutorials/openai_codex&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LiteLLM on GitHub: &lt;a href="https://github.com/BerriAI/litellm" rel="noopener noreferrer"&gt;https://github.com/BerriAI/litellm&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Video walkthrough: &lt;a href="https://youtu.be/avKQz7VqJZE" rel="noopener noreferrer"&gt;Watch it on YouTube&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>litellm</category>
      <category>codex</category>
      <category>openai</category>
    </item>
  </channel>
</rss>
