🧭 How toService ManagementIntermediate✨ AI-assisted

How to Route Requests Between GPT-6 Sol and Luna by Cost

WittyTech Daily··5 min read
#gpt-6#openai#model-routing#cost-optimization

OpenAI just made the cost-versus-quality tradeoff a lot more interesting. GPT-6 Sol runs $2 per million input tokens and $10 per million output. GPT-6 Luna runs $0.10 and $0.50. Both prices are permanent, not a launch promo, which means the twenty-fold gap between them is something you can actually plan around instead of a discount that evaporates in three months.

Most teams still send everything to Sol out of habit. That's the expensive default. This is how to route GPT-6 Sol and Luna traffic by cost, with GPT-6 Astra kept in reserve for the handful of requests that genuinely need a $10/$50 model instead of a $2/$10 one.

By the end you'll have a router that classifies a request before it spends a token, calls the cheapest tier that can plausibly handle it, escalates automatically when that tier comes up short, and logs what each decision actually cost.

Know the GPT-6 Sol and Luna pricing before you route anything

Three tiers, three different jobs:

| Model | Input / 1M | Output / 1M | Context | Best for | |---|---|---|---|---| | gpt-6-luna | $0.10 | $0.50 | 1.05M in, 128K out | summarization, extraction, classification, high-volume chat | | gpt-6-sol | $2 | $10 | large context, agentic-friendly | multi-step reasoning, coding, agentic workflows | | gpt-6-astra | $10 | $50 (cached in $1, batch/flex 0.5x, fast mode 2x) | 1.05M in, 128K out, 2x pricing past 272K input | requests Sol can't finish |

Luna is 20x cheaper than Sol on input alone. Get the routing wrong in one direction and you're overpaying for simple work; get it wrong the other way and Luna quietly gives you a worse answer on something that needed real reasoning.

Step 1: Classify the request before you call anything

Do the cheap part first: a rules-based classifier that looks at task type and rough complexity, no model call involved.

def classify(task_type: str, input_tokens: int, complexity: str) -> str:
    """Pick a tier before spending a token on the actual call."""
    if task_type == "agentic-coding" or complexity == "hard":
        return "sol"
    if task_type in ("summarization", "extraction", "classification") and input_tokens < 50_000:
        return "luna"
    if complexity == "reasoning-heavy":
        return "sol"
    return "luna"

This is the core of GPT-6 model routing: most production traffic is high-volume and low-complexity, so defaulting new request types to Luna and promoting only what proves it needs more is cheaper than the reverse.

Step 2: Call the cheapest tier that fits

import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

MODELS = {"luna": "gpt-6-luna", "sol": "gpt-6-sol", "astra": "gpt-6-astra"}

def call_with_routing(prompt: str, task_type: str, complexity: str, input_tokens: int):
    tier = classify(task_type, input_tokens, complexity)
    response = client.chat.completions.create(
        model=MODELS[tier],
        messages=[{"role": "user", "content": prompt}],
        max_completion_tokens=2000,
    )
    return tier, response

Note the max_completion_tokens param, not max_tokens. Both Luna and Sol will reject the older name.

Step 3: Escalate on real failure signals, not vibes

Don't guess whether Luna "felt" wrong. Check finish_reason and retry one tier up.

def call_with_escalation(prompt: str, task_type: str, complexity: str, input_tokens: int):
    tier, response = call_with_routing(prompt, task_type, complexity, input_tokens)
    finish_reason = response.choices[0].finish_reason

    if tier == "luna" and finish_reason in ("length", "content_filter"):
        tier = "sol"
        response = client.chat.completions.create(
            model=MODELS[tier],
            messages=[{"role": "user", "content": prompt}],
            max_completion_tokens=4000,
        )

    return tier, response

A truncated or filtered Luna response is a real signal that the request needed more model. A response that just "seems short" isn't, and hardcoding that kind of guess back into your classifier is how routers rot.

Step 4: Reserve Astra for genuine escalations

Astra is 5x Sol on input and 5x on output, so it should only fire when Sol itself fails or when the input genuinely exceeds what Sol can carry.

def escalate_to_astra(prompt: str, reason: str):
    return client.chat.completions.create(
        model=MODELS["astra"],
        messages=[{"role": "user", "content": prompt}],
    )

Wire this in only after a Sol call also truncates, refuses, or comes back empty. If you route straight to Astra whenever a task "sounds hard," you've rebuilt the expensive default you were trying to get away from.

Step 5: Track spend per tier so the savings are real

PRICES = {  # dollars per million tokens (input, output)
    "luna": (0.10, 0.50),
    "sol": (2.00, 10.00),
    "astra": (10.00, 50.00),
}

def cost(tier: str, usage) -> float:
    in_price, out_price = PRICES[tier]
    return (usage.prompt_tokens / 1_000_000) * in_price + (usage.completion_tokens / 1_000_000) * out_price

Log tier and cost() on every call. After a few thousand requests you'll know your actual Luna-to-Sol-to-Astra split, which is the only way to check whether the classifier's thresholds match reality instead of your guess at launch.

Gotchas

  • Luna and Astra cap output at 128K tokens each. Dropping a task to Luna to save money doesn't help if it still truncates and you end up paying for the retry anyway.
  • Astra's pricing doubles on input past 272,000 tokens for the whole request, so routing by "context is huge" alone can trigger that multiplier without you noticing.
  • Sol only supports function calling in Chat Completions with reasoning_effort: "none". If your router calls tools, account for that before assuming Sol is a drop-in upgrade from Luna for agentic tasks.
  • Rate limits are tier-specific. When you shift volume toward Luna or Sol, watch for 429s on whichever tier just inherited the traffic.

Get this right and you're not choosing between cheap and correct on every request, you're only paying premium prices for the requests that actually earn it.

← More in Service Management