unhardcoded
open source · policy routing for AI models

Build AI systems without hardcoded model choices.

Send a policy with each request. unhardcoded filters the live model catalog, picks the cheapest model that satisfies your rules, runs it through your provider keys, and returns a trace of the decision.

request
Prompt + policy
tools · intel ge 0.5 · cheapest
candidateprice_outintelverdict
deepseek-v4-flash$0.400.465below floor
minimax-m2.7$0.500.496below floor
deepseek-v4-pro$1.500.515selected
glm-5.1$2.000.514passes
gpt-5.5$10.000.602passes
trace
deepseek-v4-pro
cheapest passing · 412 ms
fp 301140696-1054914287
the mental model

The model routing loop

One policy decides one call. Same inputs, same catalog, same decision. Every time, and written down.

Request
Your call hits the OpenAI-compatible endpoint, carrying a policy.
Policy sigma-pol/v2
A small term: filter, rank, select, mutate, fallback.
Catalog
The live set of (provider, model) candidates with prices, benchmarks, capabilities.
Filter
Drop anything that fails a rule. No silent downgrade.
Rank
Score the survivors: cheapest, strongest, fastest, your call.
Select
Take the top one, or a top_k failover cascade.
Run
Inference over your provider keys, with failover on error.
Trace
A receipt of every candidate, the winner, and the fingerprint.

How policies work, in depth →

compose routed calls

Workflow patterns

A workflow is a bounded, acyclic graph of routed steps. Each node carries its own policy and routes independently; the whole graph writes one stitched trace. This is where unhardcoded stops being "routing with rules" and becomes a system.

The full workflow guide: node kinds, graph limits, templates →

ship the first request

Quickstart

unhardcoded is OpenAI-compatible. Three changes from a normal call.

  1. Point your SDK at the host.
    Change the baseURL; everything else in the SDK stays the same.
    client.ts
    const client = new OpenAI({
      baseURL: "https://<your-host>/v1",
      apiKey: process.env.UNHARDCODED_KEY,
    });
  2. Attach a policy to the call.
    Build a policy_ir in your backend and send it alongside messages. Routing comes from the policy, so model is just a trace label.
    route.ts
    const res = await client.chat.completions.create({
      model: "policy:support",
      policy_ir: ["policy",
        ["and", ["meets_req"], ["not", ["is", "disabled"]],
               ["cmp", "bench_intelligence", "ge", 0.5]],   // filter
        ["neg", ["normalize", ["field", "price_out"]]],          // rank: cheapest
        ["argmax"], ["id"], ["always", {"action": "next_candidate"}]],
      messages,
    });
  3. Read the trace.
    The response carries the decision: the chosen model, the candidates it ranked and rejected, and the policy fingerprint.
    response · trace (illustrative)
    {
      "chosen": { "model_family": "deepseek-v4-pro", "price_out": 1.5 },
      "trace": {
        "policy_fingerprint": "301140696-1054914287",
        "rejected": [{ "model_family": "deepseek-v4-flash", "reason": "cmp bench_intelligence ge 0.5" }],
        "total_latency_ms": 425
      }
    }

Full quickstart: auth, dry-runs, a runnable example →

copy a starting point

Policy presets

Common routing patterns as cards. Read the rules, copy the policy, adjust the floor and ceiling.

Cheapest decent
Cut cost without dropping below a quality bar.
filtertools-met · bench_intelligence ge 0.5
rankcheapest price_out
fallbacknext passing model
View JSON
cheapest-decent.json
["policy", ["and", ["meets_req"], ["not", ["is", "disabled"]],
         ["cmp", "bench_intelligence", "ge", 0.5]],
  ["neg", ["normalize", ["field", "price_out"]]],
  ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Smart balance
No strong preference: weigh capability against price.
filtertools-met · not disabled
rank0.6 intelligence + 0.4 cheap
fallbacknext passing model
View JSON
smart-balance.json
["policy", ["and", ["meets_req"], ["not", ["is", "disabled"]]],
  ["add",
    ["scale", 0.6, ["normalize", ["field", "bench_intelligence"]]],
    ["scale", 0.4, ["neg", ["normalize", ["field", "price_out"]]]]],
  ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Best intelligence
Critical task, no cost ceiling: take the most capable.
filtertools-met · not disabled
rankhighest bench_intelligence
fallbacknext passing model
View JSON
best-intelligence.json
["policy", ["and", ["meets_req"], ["not", ["is", "disabled"]]],
  ["field", "bench_intelligence"],
  ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Reasoning only
The task needs a reasoning-capable model.
filter+ is cap_reasoning
rankhighest intelligence
fallbacknext passing model
View JSON
reasoning-only.json
["policy", ["and", ["meets_req"], ["not", ["is", "disabled"]], ["is", "cap_reasoning"]],
  ["field", "bench_intelligence"],
  ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Vision · cheapest
Image input: the cheapest model that can see.
filter+ is in_image
rankcheapest price_out
fallbacknext passing model
View JSON
vision-cheapest.json
["policy", ["and", ["meets_req"], ["not", ["is", "disabled"]], ["is", "in_image"]],
  ["neg", ["normalize", ["field", "price_out"]]],
  ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Long-context RAG
Needs a large context window, then the cheapest that fits.
filter+ context ge 200000
rankcheapest price_out
fallbacknext passing model
View JSON
long-context-rag.json
["policy", ["and", ["meets_req"], ["not", ["is", "disabled"]], ["cmp", "context", "ge", 200000]],
  ["neg", ["normalize", ["field", "price_out"]]],
  ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Agentic fleet
Best tool-users for an agent loop.
filter+ tools · bench_agentic_rank le 5
rankhighest bench_agentic
fallbacknext passing model
View JSON
agentic-fleet.json
["policy", ["and", ["meets_req"], ["not", ["is", "disabled"]],
         ["has_cap", "supports_tools"], ["cmp", "bench_agentic_rank", "le", 5]],
  ["field", "bench_agentic"],
  ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Private / compliant
TEE-only and no logging: capability first.
filter+ is has_tee · is no_log
rankhighest intelligence
fallbacknext passing model
View JSON
private-compliant.json
["policy", ["and", ["meets_req"], ["not", ["is", "disabled"]],
         ["is", "has_tee"], ["is", "no_log"]],
  ["field", "bench_intelligence"],
  ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]

All presets, plus practical recipes →

the trust object

Every decision leaves a receipt

A trace is a structured, replayable record of how the model was chosen: which models were considered, why each passed or failed, what ran, and how to reproduce it.

decision_trace
deepseek-v4-flashrejected · cmp bench_intelligence ge 0.5
minimax-m2.7rejected · cmp bench_intelligence ge 0.5
deepseek-v4-proranked #1 · chosen
glm-5.1ranked #2 · cascade
gpt-5.5ranked #3 · cascade
deepseek-v4-prodecision_path · attempted · 412 ms
policy_fingerprint 301140696-1054914287 · sigma-pol/v2
rejected[]
Every filtered-out candidate with the exact rule that dropped it.
ranked[]
The survivors in selection order; the first is the pick, the rest are the failover cascade.
decision_path[]
The attempt log; every failover hop is recorded here.
policy_fingerprint
A stable id for the normalized policy; identity is a host-side sha256. A workflow also carries a flow_fingerprint.

How replay and debugging work →

when you need the detail

Reference

The complete spec, kept out of the reading path.

Source on GitHub: unhardcoded (the host) · unhardcoded-engine (the policy engine)