<?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: Alexey Sazhin</title>
    <description>The latest articles on DEV Community by Alexey Sazhin (@palermo-777).</description>
    <link>https://dev.to/palermo-777</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%2F3852318%2Fe6eb3d7a-e288-474d-9b8d-293088019798.png</url>
      <title>DEV Community: Alexey Sazhin</title>
      <link>https://dev.to/palermo-777</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/palermo-777"/>
    <language>en</language>
    <item>
      <title>Stop Paying Claude Prices for "Hi" — Route LLM Prompts to the Right Model in 50 Lines</title>
      <dc:creator>Alexey Sazhin</dc:creator>
      <pubDate>Wed, 15 Jul 2026 13:13:37 +0000</pubDate>
      <link>https://dev.to/palermo-777/stop-paying-claude-prices-for-hi-route-llm-prompts-to-the-right-model-in-50-lines-3el0</link>
      <guid>https://dev.to/palermo-777/stop-paying-claude-prices-for-hi-route-llm-prompts-to-the-right-model-in-50-lines-3el0</guid>
      <description>&lt;p&gt;Every AI app hits the same fork in the road: pick an expensive frontier model and burn budget on trivial prompts, or pick a cheap open model and ship mediocre answers on hard ones.&lt;/p&gt;

&lt;p&gt;You shouldn't have to pick. Not every prompt deserves Claude Sonnet 4.6. "What's the capital of Portugal?" and "Refactor this 400-line React component to use Suspense" are not the same job — and they shouldn't cost the same.&lt;/p&gt;

&lt;p&gt;In this tutorial we'll build a &lt;strong&gt;prompt classifier that routes each incoming request to the right model automatically&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Coding questions&lt;/strong&gt; → Claude Sonnet 4.6 (best-in-class for code)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything else&lt;/strong&gt; → a cheap, fast open model on Workers AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing is ~50 lines of TypeScript running on Cloudflare Workers, and the routing logic lives in AI Gateway — no vendor lock-in, unified logs, one endpoint for your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&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%2F4efxhdum7tmsr5ez3mdq.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%2F4efxhdum7tmsr5ez3mdq.png" alt=" " width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three moving parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A Worker&lt;/strong&gt; intercepts each OpenAI-compatible chat request&lt;/li&gt;
&lt;li&gt;It uses &lt;strong&gt;Workers AI&lt;/strong&gt; (Llama 4 Scout, free tier) to classify the prompt as &lt;code&gt;coding&lt;/code&gt; or &lt;code&gt;simple&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It attaches that tag as a &lt;code&gt;cf-aig-metadata&lt;/code&gt; header, then forwards to &lt;strong&gt;AI Gateway&lt;/strong&gt;, which reads the tag and routes to the correct upstream model&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your client only ever sees one endpoint. AI Gateway gives you unified logs, caching, and rate-limiting across both providers.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;A Cloudflare account (free tier is fine)&lt;/li&gt;
&lt;li&gt;An Anthropic API key (&lt;a href="https://console.anthropic.com/" rel="noopener noreferrer"&gt;sign up here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Node.js 20+ and &lt;a href="https://developers.cloudflare.com/workers/wrangler/" rel="noopener noreferrer"&gt;Wrangler&lt;/a&gt; installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create an AI Gateway
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In the Cloudflare Dashboard → &lt;strong&gt;AI&lt;/strong&gt; → &lt;strong&gt;AI Gateway&lt;/strong&gt; → &lt;strong&gt;Create Gateway&lt;/strong&gt;. Give it a name (e.g. &lt;code&gt;smart-router&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Make sure Authenticated Gateway option is enabled;&lt;/li&gt;
&lt;li&gt;AI Gateway created with Authenticated Gateway enabled;&lt;/li&gt;
&lt;li&gt;Copy the API token shown on the next screen — Cloudflare won't show it again. You will need this token to set the secret &lt;strong&gt;AI_GATEWAY_TOKEN&lt;/strong&gt; on a later step.&lt;/li&gt;
&lt;li&gt;Then add your Anthropic API key:&lt;/li&gt;
&lt;li&gt;Open your gateway → &lt;strong&gt;Provider Keys&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add&lt;/strong&gt; → select Anthropic → paste your key&lt;/li&gt;
&lt;/ol&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%2Fse6z2bvgy19imbea9c9b.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%2Fse6z2bvgy19imbea9c9b.png" alt=" " width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI Gateway will now handle authentication to Anthropic on your behalf — you won't need to expose that key from your Worker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Configure the Dynamic Route
&lt;/h2&gt;

&lt;p&gt;Still in your gateway, go to &lt;strong&gt;Dynamic Routes&lt;/strong&gt; → &lt;strong&gt;Add Route&lt;/strong&gt; → &lt;strong&gt;Start from scratch&lt;/strong&gt;. Name it &lt;code&gt;route1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Build this logic in the visual editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If&lt;/strong&gt; &lt;code&gt;metadata.task == "coding"&lt;/code&gt; → route to &lt;strong&gt;Anthropic&lt;/strong&gt; → &lt;code&gt;claude-sonnet-4-6&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Else&lt;/strong&gt; → route to &lt;strong&gt;Workers AI&lt;/strong&gt; → &lt;code&gt;@cf/moonshotai/kimi-k2.7-code&lt;/code&gt; (or any other cheap Workers AI model)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save the route. AI Gateway now knows how to fork traffic based on a &lt;code&gt;metadata.task&lt;/code&gt; field.&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%2Fezikvak26hqx8vz7gnml.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%2Fezikvak26hqx8vz7gnml.png" alt=" " width="799" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Build the classifier Worker
&lt;/h2&gt;

&lt;p&gt;Scaffold a new Worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create cloudflare@latest &lt;span class="nt"&gt;--&lt;/span&gt; prompt-router &lt;span class="nt"&gt;--type&lt;/span&gt; hello-world &lt;span class="nt"&gt;--ts&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;prompt-router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate into the new &lt;strong&gt;prompt-router/src&lt;/strong&gt; folder and replace &lt;strong&gt;src/index.ts&lt;/strong&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;AI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ai&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;GATEWAY_ACCOUNT_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;GATEWAY_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gatewayUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://gateway.ai.cloudflare.com/v1/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GATEWAY_ACCOUNT_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GATEWAY_NAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/compat/chat/completions`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prompt-router is running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prompt-router is running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Extract the latest user message&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;lastMessage&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;lastMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lastMessage&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Classify with a small, cheap model&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cf/meta/llama-4-scout-17b-16e-instruct&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Classify the user prompt into exactly one word: 'coding' or 'simple'. Reply with only that single word, nothing else.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;classification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;coding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;coding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;simple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Forward to AI Gateway with the classification as metadata&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cf-aig-metadata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gatewayUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update wrangler.jsonc.&lt;br&gt;
1) Replace &lt;strong&gt;{GATEWAY_ACCOUNT_ID}&lt;/strong&gt; by your Cloudflare Account ID: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the Cloudflare dashboard, go to the &lt;strong&gt;Account&lt;/strong&gt; list.&lt;/li&gt;
&lt;li&gt;Select the menu button at the end of the account row.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Copy account ID&lt;/strong&gt;.
&lt;img width="354" 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%2Fep7keijvzwzs1c0sohr0.png" alt="Image description" height="476"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Update &lt;strong&gt;{GATEWAY_NAME}&lt;/strong&gt; with your values (find them in &lt;strong&gt;AI &amp;gt; AI Gateway &amp;gt; Settings&lt;/strong&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prompt-router"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/index.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compatibility_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-01-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compatibility_flags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"nodejs_compat"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ai"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"binding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AI"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vars"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"GATEWAY_ACCOUNT_ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-account-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"GATEWAY_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"smart-router"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store the AI Gateway API token (saved on the creation of AI Gateway) as a Worker secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wrangler secret put AI_GATEWAY_TOKEN
&lt;span class="c"&gt;# paste the token when prompted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Test it&lt;/p&gt;

&lt;p&gt;Fire two prompts at your Worker with curl:&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="c"&gt;# Simple prompt — should route to Workers AI&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://prompt-router.&amp;lt;your-subdomain&amp;gt;.workers.dev &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;your-ai-gateway-token&amp;gt;"&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": "dynamic/route1",
    "messages": [{"role": "user", "content": "What is the capital of Portugal?"}]
  }'&lt;/span&gt;

&lt;span class="c"&gt;# Coding prompt — should route to Claude&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://prompt-router.&amp;lt;your-subdomain&amp;gt;.workers.dev &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;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;your-ai-gateway-token&amp;gt;"&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": "dynamic/route1",
    "messages": [{"role": "user", "content": "Write a Python function that reverses a linked list in place."}]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Cloudflare Dashboard &amp;gt; Compute &amp;gt; Workers &amp;amp; Pages prompt-router&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Check the &lt;strong&gt;Observability / Logs&lt;/strong&gt; tab. You should see it successfully extract the prompt and log: Task: coding.&lt;/li&gt;
&lt;li&gt;Go to AI &amp;gt; AI Gateway &amp;gt; Gateways &amp;gt; Your Gateway.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Logs&lt;/strong&gt; tab and make sure that &lt;strong&gt;claude-sonnet-4-6&lt;/strong&gt; model (slower, expensive, higher quality) is used for coding tasks and &lt;strong&gt;Kimi-K2&lt;/strong&gt; (fast, cheap) for the rest. Cost delta is visible in the logs.
&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%2F6drb18r5oacw4h5mur8v.png" alt=" " width="800" height="182"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 5: Point your existing app at it&lt;br&gt;
Anything that speaks the OpenAI Chat Completions API will work. For example, in Open WebUI or LibreChat:&lt;br&gt;
Base URL: &lt;a href="https://prompt-router" rel="noopener noreferrer"&gt;https://prompt-router&lt;/a&gt;..workers.dev&lt;br&gt;
Model: dynamic/route1&lt;br&gt;
API Key: your AI Gateway token&lt;/p&gt;

&lt;p&gt;In Continue.dev or Cursor, use the same base URL and model name. From the client's perspective it's a single OpenAI-compatible model — the routing is invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does this actually save money?
&lt;/h2&gt;

&lt;p&gt;Very rough back-of-envelope, based on current public prices:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Input $/1M tokens&lt;/th&gt;
&lt;th&gt;Output $/1M token&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 4.6&lt;/td&gt;
&lt;td&gt;$3.0&lt;/td&gt;
&lt;td&gt;$15.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workers AI (Kimi-K2)&lt;/td&gt;
&lt;td&gt;$0.95&lt;/td&gt;
&lt;td&gt;$4.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If 70% of a typical chat app's prompts are "simple" (conversation, factual questions, summaries) and 30% are "coding" or complex reasoning, routing them separately cuts total LLM cost by roughly 60-70% versus sending everything to Claude — while keeping Claude on the workloads where it earns its price.&lt;/p&gt;

&lt;p&gt;Your mileage will vary. Instrument the classifier's output and measure your own traffic mix before quoting numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;A few natural extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More categories. Add reasoning, creative, summarization, translation — each routed to a specialist model.&lt;/li&gt;
&lt;li&gt;Confidence-based fallback. Ask the classifier for a confidence score, and only route to the cheap model if confidence is high; otherwise default to the strong one.&lt;/li&gt;
&lt;li&gt;Per-user or per-tier routing. Free-tier users always hit the cheap model; paid users get the frontier one. Read a header or JWT claim and inject it into cf-aig-metadata alongside task.&lt;/li&gt;
&lt;li&gt;Evals-based routing. Score responses from both models offline, then bias the router toward whichever one wins for a given category.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The interesting bit isn't the classifier — it's the pattern. Once you have a Worker sitting between your app and AI Gateway, injecting metadata, you can express any routing policy you want in the Gateway's visual editor without touching code again.&lt;/li&gt;
&lt;li&gt;Prompt-based routing is the simplest useful policy. Once you've shipped it, cost-aware, latency-aware, and quality-aware routing are all one Dynamic Route edit away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full code and setup notes: &lt;a href="https://github.com/palermo-777/prompt-router" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you build something with this, drop a comment — especially if you find a smarter classifier prompt than mine.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cloudflare</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
