Skip to main content

A semantic LLM router for OpenAI-compatible chat completions.

Project description

route67

route67 is a LLM router for OpenAI-compatible chat completions format. It uses a user-defined routing table for user defined question-model routing via semantic similarity, as a fallback a weak model answer or explicitly escalate to a strong model.

How it works

flowchart LR
    Q["User request"] --> R{"Semantic route match?"}
    R -- Yes --> M["Configured weak or strong model"]
    R -- No --> W["Weak model gate<br/>usage notes + strong-route examples"]
    W -- Answers --> O["Response"]
    W -- ESCALATE --> S["Strong model"]
    M --> O
    S --> O

Install

route67 requires Python 3.10 or newer. Choose either the standard Python workflow or the uv workflow.

Using python -m venv

Create and activate a virtual environment:

python -m venv .venv
# Windows PowerShell
.\.venv\Scripts\Activate.ps1
# macOS/Linux
source .venv/bin/activate

Then install route67 and its dependencies:

python -m pip install --upgrade pip
python -m pip install -e .

To also install the test dependencies, use python -m pip install -e ".[test]".

Using uv

With uv installed, create the environment and install the project from the lockfile:

uv sync

Run commands inside the environment with uv run, for example uv run python example.py. To include test dependencies, use uv sync --extra test.

Get started

Set an OpenAI API key in your environment:

# Windows PowerShell
$env:OPENAI_API_KEY = "your-api-key"
# macOS/Linux
export OPENAI_API_KEY="your-api-key"

Create example.py:

from llm_router import Controller, ModelSpec, RouterConfig, RoutingTableEntry

config = RouterConfig(
    routing_table=[
        RoutingTableEntry(
            "Prove this theorem",
            "strong_model",
            notes="Requires a rigorous multi-step proof.",
        ),
        RoutingTableEntry("Rewrite this paragraph", "weak_model"),
    ],
    weak_model=ModelSpec(
        "gpt-5-mini",
        usage_notes="Avoid difficult multi-step proofs.",
    ),
    strong_model=ModelSpec(
        "gpt-5",
        usage_notes="Use for rigorous proofs and difficult reasoning.",
    ),
    embedding_cache_path=".cache/routes",
    log_path=".cache/routing.jsonl",
)

client = Controller(config)
response = client.chat.completions.create(
    messages=[{"role": "user", "content": "Prove that sqrt(2) is irrational."}]
)
print(response.choices[0].message.content)

Run it with the activated standard virtual environment:

python example.py

Or with uv:

uv run python example.py

OpenAI-compatible providers

route67 can use any provider exposed through an OpenAI-compatible client. Create the provider's client normally and inject it into the controller. Model names in the routing configuration are passed to that provider unchanged.

For example, with OpenRouter:

import os

from openai import OpenAI
from llm_router import Controller, ModelSpec, RouterConfig, RoutingTableEntry

openrouter = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=os.environ["OPENROUTER_API_KEY"],
)

config = RouterConfig(
    routing_table=[
        RoutingTableEntry(
            "Answer questions about a country",
            "weak_model",
        ),
        RoutingTableEntry(
            "Solve a difficult reasoning or math problem",
            "strong_model",
            notes="Requires careful multi-step reasoning.",
        ),
    ],
    weak_model=ModelSpec(
        "openai/gpt-4.1-mini",
        usage_notes="Best for straightforward factual and writing questions.",
    ),
    strong_model=ModelSpec(
        "deepseek/deepseek-v4-flash",
        usage_notes="Use for difficult reasoning, mathematics, and verification.",
    ),
)

client = Controller(config, openai_client=openrouter)
response = client.chat.completions.create(
    messages=[
        {
            "role": "user",
            "content": "How many r's are in the word 'strawberry'?",
        }
    ],
    extra_body={"reasoning": {"enabled": True}},
)

Provider-specific request options such as extra_body and extra_headers are forwarded unchanged. Provider-specific response fields, including reasoning_details, are also preserved. To continue a provider's reasoning, pass its assistant message fields back unmodified in the next request.

Routing table entries target only "weak_model" or "strong_model". Provider model names live in ModelSpec, so switching models or providers does not require rewriting the routing table.

ModelSpec.usage_notes are added to the weak model's escalation system prompt. The prompt also includes up to five routing-table entries targeting "strong_model" as examples of requests that should be escalated. Add concise notes to those entries when the reason for escalation is useful context.

Your first request will download the minishlab/potion-base-8M from HuggingFace. The model is lazy-loaded, so constructing a controller with an empty routing table does not download it.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

route67-0.1.0.tar.gz (80.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

route67-0.1.0-py3-none-any.whl (12.6 kB view details)

Uploaded Python 3

File details

Details for the file route67-0.1.0.tar.gz.

File metadata

  • Download URL: route67-0.1.0.tar.gz
  • Upload date:
  • Size: 80.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for route67-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8669ee902558482764383af6f77dc0bbc5b1683f65dec013954c95b3da1f0ec5
MD5 3002c386197c87764ca2dee6947cbe9b
BLAKE2b-256 98e408fd2f32582efdeaf420963837a9b8858abbd8034e65a95b04355d8b92f4

See more details on using hashes here.

File details

Details for the file route67-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: route67-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 12.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for route67-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ccee9652df8170cd0e38fa0866bdf658a2075d5f2dd1e6f53913c0e002d58369
MD5 6b12a33bf1531e782f56390f1df7f537
BLAKE2b-256 6ad622923b41e2262fd09ef70db55418e1df4e20e972677c1b812d14b33ec6dc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page