Skip to content
OpenSmartRoute
Documentation
Get started

Real-world use cases by industry

Nine complete, working catalogues - healthcare, banking, legal, retail and more - with the requests users send and where each one routes.

Get started 22 min read

Nine industry scenarios, each a complete routing catalogue you can copy into a file and run as it stands - no code to write: a hospital, a bank, a law firm, an online shop, an engineering team, an HR department, a marketing team, a hotel group and an analytics team. Every scenario shows the requests real users send, the target each one is routed to and the reason, and every one of those decisions is checked by the test suite on every commit.

Every decision on this page is verified, not illustrated

The test suite reads this very page: it writes each YAML block to a file, sends every request of every What happens table through the router (in process with the SDK's default objective and the command line's default, through osr route, and over HTTP through the server osr serve starts) and asserts the Routed to column; it also runs every osr command in every Try it block and checks its # -> target annotation. The same tables are repeated through the hosted platform's POST /api/v1/route (guard middleware, workspace policy and metering included) and, on a schedule, against the running production platform. If any surface ever decided differently, the change could not ship. Nothing here needs an API key or a network connection.

How to run a scenario

  1. Install the command line: pip install 'opensmartroute[yaml]'.
  2. Save a scenario's YAML block under the file name in its first line (for example healthcare.yaml). The one file carries both the targets: and the rules:.
  3. Route a request with the file:
osr -t healthcare.yaml -r healthcare.yaml route "What time does the clinic open on Saturdays?"   # -> front-desk-fast

The command prints the chosen target, its confidence and the whole trace - the signals it read, the targets policy refused and why, and every strategy's score. Add --json for the raw decision, --plan for a persona / skill / model plan, --event <name> to route a system event without any text and --constraint key=value for a hard constraint.

The same file serves the same decisions over HTTP:

osr -t healthcare.yaml -r healthcare.yaml serve            # http://127.0.0.1:8000
curl -s http://127.0.0.1:8000/route -H 'Content-Type: application/json' \
  -d '{"text": "What time does the clinic open on Saturdays?"}'
curl -s http://127.0.0.1:8000/route -H 'Content-Type: application/json' \
  -d '{"text": "", "context": {"event": "payment.failed"}}'

and the hosted platform takes the same JSON at POST /api/v1/route with your workspace key (platform guide):

curl -s https://api.opensmartroute.ai/api/v1/route -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' -d '{"text": "I want a refund for order #4821, it arrived damaged."}'

Each scenario follows the same shape:

PartWhat it tells you
SituationWho the users are, what they ask and what must never happen
CatalogueThe targets (models, agents, skills, tools, workflows, people) and the rules, as one YAML file
What happensReal requests, the target each one reaches, and why - the verified table
Try itThe commands to run, each annotated with the target it reaches - also verified

Three mechanisms recur in every industry:

  • Data that must stay inside stays inside. A target declares pii_allowed: false and the policy stage removes it the moment a request carries an e-mail address, phone number, IBAN, card or national id. This is a hard stop, never a score - a rule then prefers the on-premises target among what is left.
  • People are one target among the others. A human target with actions: [escalate] and a pinned rule means "I want to speak to a manager" or a payment.failed event reaches a person, not a model.
  • Events route without any text. when: { event: order.* } matches the event name, so webhooks and queue messages are routed the same way chat is (--event order.shipped on the command line, context.event over HTTP).

1. Healthcare - a hospital group

Situation. Front-desk staff and patients ask practical questions all day (opening hours, preparation for tests). Clinicians paste discharge notes and intake forms that name patients. Complex medication questions deserve the best model available. Anything with patient identifiers must never leave the hospital network, and an emergency has to reach the on-call clinician, not a chatbot.

Catalogue.

# healthcare.yaml
targets:
  - id: clinic-notes-onprem
    kind: llm
    name: Clinic model (inside the hospital network)
    capabilities:
      domains: [medical, general]
      actions: [summarize, extract, qa, classify, generation]
      max_complexity: 0.7
    constraints: { data_boundary: on_prem, pii_allowed: true, regions: [eu] }
    cost: { usd_per_1k_tokens: 0.001 }
    latency_ms: 1200
    quality_prior: 0.72
    examples:
      - "Summarize this discharge note."
      - "Extract the medications and dosages from this intake form."
  - id: front-desk-fast
    kind: llm
    name: Front-desk assistant (public cloud, cheap)
    capabilities:
      domains: [general, general_chat, customer_support]
      actions: [qa, classify, summarize, generation]
      max_complexity: 0.45
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.0002 }
    latency_ms: 300
    quality_prior: 0.55
    examples:
      - "What time does the clinic open on Saturdays?"
      - "Do I need to fast before a blood test?"
  - id: clinical-reasoner
    kind: llm
    name: Clinical reasoning model (public cloud, frontier)
    capabilities:
      domains: [medical, general]
      actions: [reasoning, qa, summarize, generation]
      min_complexity: 0.45
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.015 }
    latency_ms: 2500
    quality_prior: 0.93
    examples:
      - "Explain the interactions between these three medications and how to adjust the dosage."
  - id: on-call-clinician
    kind: human
    name: On-call clinician
    capabilities: { actions: [escalate], tags: [safety] }
    cost: { usd_per_1k_tokens: 0.5 }
    latency_ms: 300000
rules:
  - name: patient-data-stays-inside
    when: { contains_pii: true }
    prefer: [clinic-notes-onprem]
    weight: 1.0
  - name: clinical-emergency-to-human
    when: { actions: [escalate] }
    prefer: [on-call-clinician]
    pin: true
    weight: 1.0

What happens.

RequestRouted toWhy
Summarize this discharge note for patient John Doe, DOB 03/04/1961, phone +44 7700 900123: admitted with pneumonia, treated with IV antibiotics, discharged on oral amoxicillin.clinic-notes-onpremThe phone number is PII: both public-cloud models are rejected by policy before any scoring; the rule prefers the on-premises model
Explain step by step how metformin dosage should be adjusted for a patient with reduced kidney function and why.clinical-reasonerMedical domain, reasoning intent, complexity above the frontier model's floor; no identifiers, so the public model is allowed
What time does the clinic open on Saturdays?front-desk-fastA simple question: the cheapest capable model wins on utility
The patient reports acute chest pain right now - escalate to the on-call doctor.on-call-clinicianThe escalation intent triggers the pinned rule; every model is excluded

Try it. A department can insist on the boundary even when a note happens to contain no identifier; the trace names every target that was refused and why (data boundary public < required on_prem):

osr -t healthcare.yaml -r healthcare.yaml route "Summarize this discharge note for patient John Doe, phone +44 7700 900123."   # -> clinic-notes-onprem
osr -t healthcare.yaml -r healthcare.yaml route "Summarize this patient intake note." --constraint data_boundary=on_prem --constraint region=eu   # -> clinic-notes-onprem
osr -t healthcare.yaml -r healthcare.yaml route "The patient reports acute chest pain right now - escalate to the on-call doctor."   # -> on-call-clinician

2. Banking and financial services

Situation. Customers ask about products in the chat widget. Back-office teams extract fields from invoices and loan files that contain account numbers. Analysts want deep comparisons. A failed payment or a suspected fraud is an event from the core banking system, not a sentence, and it must land on the fraud desk.

Catalogue.

# banking.yaml
targets:
  - id: ledger-onprem
    kind: llm
    name: Back-office model (bank data centre)
    capabilities:
      domains: [finance, customer_support, general]
      actions: [extract, summarize, classify, qa]
      max_complexity: 0.7
    constraints: { data_boundary: on_prem, pii_allowed: true, regions: [eu] }
    cost: { usd_per_1k_tokens: 0.001 }
    latency_ms: 1200
    quality_prior: 0.72
    examples:
      - "Extract the IBAN, amount and due date from this invoice."
  - id: banker-fast
    kind: llm
    name: Customer chat model (cheap)
    capabilities:
      domains: [general, general_chat, customer_support, finance]
      actions: [qa, classify, generation, summarize]
      max_complexity: 0.45
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.0002 }
    latency_ms: 300
    quality_prior: 0.55
    examples:
      - "What is the interest rate on the savings account?"
  - id: analyst-frontier
    kind: llm
    name: Analyst model (frontier)
    capabilities:
      domains: [finance, math, data_analysis, general]
      actions: [reasoning, planning, generation, qa]
      min_complexity: 0.45
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.015 }
    latency_ms: 2500
    quality_prior: 0.93
    examples:
      - "Compare the risk-adjusted returns of two portfolios and explain the trade-offs."
  - id: fraud-desk
    kind: human
    name: Fraud desk
    capabilities: { actions: [escalate], tags: [safety] }
    cost: { usd_per_1k_tokens: 0.5 }
    latency_ms: 600000
rules:
  - name: account-data-stays-inside
    when: { contains_pii: true }
    prefer: [ledger-onprem]
    weight: 1.0
  - name: failed-payments-to-fraud-desk
    when: { event: ["payment.failed", "fraud.*"] }
    prefer: [fraud-desk]
    pin: true
    weight: 1.0
  - name: escalation
    when: { actions: [escalate] }
    prefer: [fraud-desk]
    pin: true
    weight: 1.0

What happens.

RequestRouted toWhy
Extract the IBAN and amount from this invoice: IBAN DE89370400440532013000, EUR 4,200 due in 30 days.ledger-onpremAn IBAN is PII: the public models are rejected, the rule prefers the data-centre model
Compare the risk-adjusted return of a 60/40 portfolio against an all-equity portfolio over 30 years and explain the trade-offs step by step.analyst-frontierFinance and mathematics, reasoning intent, high complexity: the frontier model is worth its price
What is the interest rate on the savings account?banker-fastSimple product question: cheapest capable model
event payment.failedfraud-deskNo text at all: the event name carries the intent and the pinned rule sends it to people

Try it. A webhook handler passes the event name through without composing a prompt; a region-bound team adds the boundary as a constraint; estimate quotes a request before it is sent (tokens, cost, latency and quality for every candidate, the recommended pick and the cheapest, fastest and best-quality alternatives):

osr -t banking.yaml -r banking.yaml route --event payment.failed   # -> fraud-desk
osr -t banking.yaml -r banking.yaml route "Summarize this loan file." --constraint region=eu --constraint data_boundary=on_prem   # -> ledger-onprem
osr -t banking.yaml -r banking.yaml estimate "Compare the risk-adjusted return of a 60/40 portfolio against an all-equity portfolio over 30 years."

Situation. Associates summarise NDAs a dozen times a day - a cheap model does that well. Partners want a careful analysis of liability clauses from the strongest model, framed by the firm's own counsel persona. Any document naming a client stays on the firm's servers.

Catalogue.

# legal.yaml
targets:
  - id: counsel-frontier
    kind: llm
    name: Counsel model (frontier)
    capabilities:
      domains: [legal, general]
      actions: [reasoning, summarize, qa, generation]
      min_complexity: 0.4
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.015 }
    latency_ms: 2500
    quality_prior: 0.92
    examples:
      - "Review this indemnification clause and explain our liability exposure."
  - id: paralegal-fast
    kind: llm
    name: Paralegal model (cheap)
    capabilities:
      domains: [legal, general]
      actions: [summarize, qa, classify, extract]
      max_complexity: 0.5
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.0004 }
    latency_ms: 400
    quality_prior: 0.6
    examples:
      - "Summarize the key points of this NDA in three bullets."
  - id: matter-files-onprem
    kind: llm
    name: Matter-files model (firm servers)
    capabilities:
      domains: [legal, general]
      actions: [summarize, extract, qa, generation]
      max_complexity: 0.7
    constraints: { data_boundary: on_prem, pii_allowed: true }
    cost: { usd_per_1k_tokens: 0.001 }
    latency_ms: 1200
    quality_prior: 0.7
  - id: persona-inhouse-counsel
    kind: persona
    primary: false
    capabilities: { domains: [legal], actions: [reasoning, summarize, qa] }
    instructions: "You are cautious in-house counsel. Cite the clause, flag jurisdiction risk, never give a final legal opinion."
rules:
  - name: client-data-stays-inside
    when: { contains_pii: true }
    prefer: [matter-files-onprem]
    weight: 1.0

What happens.

RequestRouted toWhy
Review this indemnification clause and explain our liability exposure under GDPR if a sub-processor breaches - compare the two drafting options step by step.counsel-frontierLegal reasoning at high complexity: the frontier model, above the cheap model's ceiling
Summarize the key points of this NDA in three bullets.paralegal-fastA routine summary below the frontier model's floor: the cheap model wins
Draft a contract amendment for client Maria Rossi (maria.rossi@example.com) extending the term by 12 months.matter-files-onpremThe e-mail address is PII: only the firm's own model is admissible

Try it. Ask for a plan and the persona is layered on whichever model wins (the output shows plan: persona:persona-inhouse-counsel -> llm:counsel-frontier); its instructions become the system prompt when the plan is executed:

osr -t legal.yaml -r legal.yaml route "Review this indemnification clause and explain our liability exposure under GDPR if a sub-processor breaches - compare the two drafting options step by step." --plan   # -> counsel-frontier
osr -t legal.yaml -r legal.yaml route "Draft a contract amendment for client Maria Rossi (maria.rossi@example.com)."   # -> matter-files-onprem

4. Retail and e-commerce customer support

Situation. Shoppers ask about shipping and sizes, report damaged orders and demand refunds. Order events from the fulfilment system trigger customer notifications. A customer who asks for a manager gets one. The support agent has tools (order lookup, refund) and runs inside the company, so it may see customer details.

Catalogue.

# retail.yaml
targets:
  - id: support-agent
    kind: agent
    name: Support agent (order lookup, refunds)
    capabilities:
      domains: [customer_support]
      actions: [qa, action, classify, summarize]
      supports_tools: true
      tags: [autonomous]
    constraints: { data_boundary: private, pii_allowed: true }
    cost: { usd_per_1k_tokens: 0.004 }
    latency_ms: 3000
    quality_prior: 0.8
    examples:
      - "I want a refund for order #4821, it arrived damaged."
      - "Where is my order? It was due yesterday."
  - id: shop-chat-fast
    kind: llm
    name: Shop assistant (cheap)
    capabilities:
      domains: [general, general_chat]
      actions: [qa, generation, classify]
      max_complexity: 0.45
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.0002 }
    latency_ms: 300
    quality_prior: 0.55
    examples:
      - "Do you ship to Canada?"
      - "What sizes does the jacket come in?"
  - id: workflow-notify-customer
    kind: workflow
    name: Customer notification workflow
    capabilities: { domains: [customer_support], actions: [action] }
    cost: { usd_per_1k_tokens: 0.0001 }
    latency_ms: 200
    quality_prior: 0.7
  - id: support-team
    kind: human
    name: Support team
    capabilities: { actions: [escalate], tags: [safety] }
    cost: { usd_per_1k_tokens: 0.5 }
    latency_ms: 900000
rules:
  - name: support-questions-to-agent
    when: { domains: [customer_support] }
    prefer: [support-agent]
    weight: 0.8
  - name: order-events-to-notification-workflow
    when: { event: ["order.*", "shipment.*"] }
    prefer: [workflow-notify-customer]
    pin: true
    weight: 1.0
  - name: angry-customers-to-people
    when: { actions: [escalate] }
    prefer: [support-team]
    pin: true
    weight: 1.0

What happens.

RequestRouted toWhy
I want a refund for order #4821, it arrived damaged.support-agentCustomer-support domain and an action to perform: the rule prefers the agent with tools
Do you ship to Canada?shop-chat-fastA general question: the cheap model answers
event order.shippedworkflow-notify-customerThe fulfilment event is pinned to the notification workflow - no prompt involved
This is the third time I am asking. I want to speak to a manager now.support-teamEscalation intent: pinned to people

Try it. The fulfilment system sends its events as they happen; --context carries any extra facts along:

osr -t retail.yaml -r retail.yaml route --event order.shipped --context order_id=4821   # -> workflow-notify-customer
osr -t retail.yaml -r retail.yaml route "I want a refund for order #4821, it arrived damaged."   # -> support-agent
osr -t retail.yaml -r retail.yaml route "This is the third time I am asking. I want to speak to a manager now."   # -> support-team

Close the loop without code as well: when the agent has resolved the refund, report the outcome to the served router (POST /feedback) or the platform (POST /api/v1/feedback) with the decision's request_id and target_id, and the learners shift future support traffic toward what actually works:

curl -s http://127.0.0.1:8000/feedback -H 'Content-Type: application/json' \
  -d '{"request_id": "<from the decision>", "target_id": "support-agent", "success": true, "quality": 0.9}'

5. Software engineering

Situation. Developers ask quick language questions, request SQL, and hand over multi-step repository work (refactor, run tests, open a pull request). CI and deployment failures arrive as events and should go straight to the coding agent.

Catalogue.

# engineering.yaml
targets:
  - id: coding-agent
    kind: agent
    name: Coding agent (repository access, tests, pull requests)
    capabilities:
      domains: [coding]
      actions: [code_generation, code_review, action, reasoning]
      min_complexity: 0.4
      supports_tools: true
      tags: [autonomous]
    cost: { usd_per_1k_tokens: 0.008 }
    latency_ms: 6000
    quality_prior: 0.85
    examples:
      - "Refactor this module to remove the circular import and run the test suite."
      - "Investigate why the deploy failed and open a pull request with the fix."
  - id: code-fast
    kind: llm
    name: Code model (cheap)
    capabilities:
      domains: [coding, general]
      actions: [qa, code_generation, summarize, reasoning]
      max_complexity: 0.5
    cost: { usd_per_1k_tokens: 0.0004 }
    latency_ms: 400
    quality_prior: 0.6
    examples:
      - "What does the Python zip function do?"
      - "Write a one-line list comprehension that squares a list."
  - id: skill-sql
    kind: skill
    name: SQL skill
    capabilities: { domains: [data_analysis, coding], actions: [code_generation, extract] }
    cost: { usd_per_1k_tokens: 0.001 }
    latency_ms: 200
    quality_prior: 0.8
    instructions: "Always qualify table names with the schema; never SELECT *."
    examples:
      - "Write a SQL query that returns monthly active users per region."
rules:
  - name: sql-to-skill
    when: { domains: [data_analysis], actions: [code_generation] }
    prefer: [skill-sql]
    pin: true
    weight: 1.0
  - name: broken-builds-to-agent
    when: { event: ["deploy.failed", "build.failed", "test.failed"] }
    prefer: [coding-agent]
    pin: true
    weight: 1.0
  - name: repo-work-to-agent
    when: { domains: [coding], actions: [action, code_review] }
    prefer: [coding-agent]
    weight: 0.9
  - name: hard-coding-to-agent
    when: { domains: [coding], min_complexity: 0.4 }
    prefer: [coding-agent]
    weight: 0.7

What happens.

RequestRouted toWhy
Refactor the payment module to remove the circular import, run the test suite, fix the failures and open a pull request.coding-agentRepository work with actions to perform: the agent with tools
Review this pull request and find the bug in the retry loop.coding-agentCode review is agent work in this catalogue
What does the Python zip function do?code-fastA quick language question: the cheap model
Write a SQL query that returns monthly active users per region from the events table.skill-sqlData-analysis code generation is pinned to the SQL skill
event deploy.failedcoding-agentThe failure event is pinned to the agent

Try it.

osr -t engineering.yaml -r engineering.yaml route --event deploy.failed --context service=checkout   # -> coding-agent
osr -t engineering.yaml -r engineering.yaml route "What does the Python zip function do?"   # -> code-fast
osr -t engineering.yaml -r engineering.yaml route "Write a SQL query that returns monthly active users per region from the events table."   # -> skill-sql

6. Human resources

Situation. Employees ask about policies. Recruiters screen CVs full of personal data. Managers draft performance reviews. Sensitive cases go to a human business partner.

Catalogue.

# hr.yaml
targets:
  - id: people-onprem
    kind: llm
    name: People-data model (inside the company)
    capabilities:
      domains: [hr, general]
      actions: [extract, classify, summarize, qa, generation]
      max_complexity: 0.7
    constraints: { data_boundary: on_prem, pii_allowed: true }
    cost: { usd_per_1k_tokens: 0.001 }
    latency_ms: 1200
    quality_prior: 0.72
    examples:
      - "Screen this resume against the job description."
  - id: hr-helpdesk-fast
    kind: llm
    name: HR helpdesk model (cheap)
    capabilities:
      domains: [hr, general, general_chat]
      actions: [qa, classify, summarize]
      max_complexity: 0.45
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.0002 }
    latency_ms: 300
    quality_prior: 0.55
    examples:
      - "How many days of parental leave does the leave policy give?"
  - id: hr-writer
    kind: llm
    name: HR writing model
    capabilities:
      domains: [hr, general]
      actions: [generation, summarize, reasoning]
      min_complexity: 0.3
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.003 }
    latency_ms: 900
    quality_prior: 0.78
    examples:
      - "Draft a constructive performance review for an engineer."
  - id: hr-business-partner
    kind: human
    name: HR business partner
    capabilities: { actions: [escalate], tags: [safety] }
    cost: { usd_per_1k_tokens: 0.5 }
    latency_ms: 3600000
rules:
  - name: candidate-and-employee-data-stays-inside
    when: { contains_pii: true }
    prefer: [people-onprem]
    weight: 1.0
  - name: sensitive-cases-to-people
    when: { actions: [escalate] }
    prefer: [hr-business-partner]
    pin: true
    weight: 1.0

What happens.

RequestRouted toWhy
Screen this resume for the data engineer role: Priya Natarajan, priya.n@example.com, +91 98765 43210, 6 years of Spark and Airflow.people-onpremE-mail and phone number: only the in-house model may see the CV
How many days of parental leave does the leave policy give?hr-helpdesk-fastPolicy question: the cheap model
Draft a constructive performance review for an engineer who ships fast but skips code reviews, and explain how to phrase the feedback.hr-writerDrafting with reasoning at moderate complexity: the writing model
An employee reported harassment on the team - escalate this to the HR business partner.hr-business-partnerEscalation intent: pinned to a person

Try it.

osr -t hr.yaml -r hr.yaml route "Screen this resume for the data engineer role: Priya Natarajan, priya.n@example.com, 6 years of Spark."   # -> people-onprem
osr -t hr.yaml -r hr.yaml route "How many days of parental leave does the leave policy give?"   # -> hr-helpdesk-fast
osr -t hr.yaml -r hr.yaml route "An employee reported harassment on the team - escalate this to the HR business partner."   # -> hr-business-partner

7. Marketing

Situation. Copy is written all day and should cost next to nothing - a small self-hosted model does it. Campaign planning needs a stronger model. Translations go to a dedicated translation skill so terminology stays consistent.

Catalogue.

# marketing.yaml
targets:
  - id: copywriter-small
    kind: llm
    name: Copywriter (small, self-hosted)
    capabilities:
      domains: [marketing, creative, general]
      actions: [generation, summarize, translate, qa]
      max_complexity: 0.5
      languages: ["*"]
    constraints: { data_boundary: on_prem, pii_allowed: true }
    cost: { usd_per_1k_tokens: 0.00005 }
    latency_ms: 700
    quality_prior: 0.6
    examples:
      - "Write three subject lines for our spring newsletter."
  - id: strategist-mid
    kind: llm
    name: Campaign strategist model
    capabilities:
      domains: [marketing, data_analysis, general]
      actions: [planning, reasoning, generation, summarize]
      min_complexity: 0.3
    cost: { usd_per_1k_tokens: 0.003 }
    latency_ms: 900
    quality_prior: 0.78
    examples:
      - "Plan a multi-channel campaign with budget allocation and KPIs per channel."
  - id: skill-translate
    kind: skill
    name: Translation skill
    capabilities: { actions: [translate], languages: ["*"] }
    cost: { usd_per_1k_tokens: 0.0005 }
    latency_ms: 300
    quality_prior: 0.85
    examples:
      - "Translate this headline into Spanish."
rules:
  - name: translations-to-skill
    when: { actions: [translate] }
    prefer: [skill-translate]
    pin: true
    weight: 1.0

What happens.

RequestRouted toWhy
Write three subject lines for our spring newsletter.copywriter-smallSimple copy: the near-free self-hosted model
Plan a 12-week multi-channel campaign for a B2B SaaS launch with budget allocation and KPIs per channel, and explain the trade-offs.strategist-midPlanning and reasoning above the copywriter's ceiling
Translate this landing page headline into Spanish: 'Route every request to the right model.'skill-translateTranslation intent: pinned to the skill

Try it.

osr -t marketing.yaml -r marketing.yaml route "Write three subject lines for our spring newsletter."   # -> copywriter-small
osr -t marketing.yaml -r marketing.yaml route "Translate this landing page headline into Spanish: Route every request to the right model."   # -> skill-translate

8. Travel and hospitality

Situation. Guests ask quick questions at any hour. Trip planning with bookings is agent work with tools. Anything naming a guest stays on the group's own systems.

Catalogue.

# travel.yaml
targets:
  - id: concierge-fast
    kind: llm
    name: Concierge model (cheap)
    capabilities:
      domains: [travel, general, general_chat, customer_support]
      actions: [qa, generation, summarize]
      max_complexity: 0.45
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.0002 }
    latency_ms: 300
    quality_prior: 0.55
    examples:
      - "What time is checkout?"
  - id: booking-agent
    kind: agent
    name: Booking agent (search and reservations)
    capabilities:
      domains: [travel]
      actions: [planning, action, reasoning, generation]
      supports_tools: true
      tags: [autonomous]
      min_complexity: 0.3
    constraints: { pii_allowed: false }
    cost: { usd_per_1k_tokens: 0.006 }
    latency_ms: 5000
    quality_prior: 0.82
    examples:
      - "Plan a five-day itinerary with train bookings."
  - id: guest-records-onprem
    kind: llm
    name: Guest-records model (own systems)
    capabilities:
      domains: [travel, customer_support, general]
      actions: [extract, summarize, qa, classify]
      max_complexity: 0.7
    constraints: { data_boundary: on_prem, pii_allowed: true }
    cost: { usd_per_1k_tokens: 0.001 }
    latency_ms: 1200
    quality_prior: 0.7
rules:
  - name: guest-data-stays-inside
    when: { contains_pii: true }
    prefer: [guest-records-onprem]
    weight: 1.0
  - name: trip-planning-to-agent
    when: { domains: [travel], actions: [planning, action] }
    prefer: [booking-agent]
    weight: 0.8

What happens.

RequestRouted toWhy
Plan a five-day itinerary in Kyoto for a family with two kids and book the train from Tokyo.booking-agentTravel planning with a booking to make: the agent with tools
What time is checkout?concierge-fastA trivial question: the cheap model
Change the booking for guest anna.k@example.com to a late arrival on Friday.guest-records-onpremThe guest's e-mail address is PII: only the group's own model is admissible

Try it.

osr -t travel.yaml -r travel.yaml route "Plan a five-day itinerary in Kyoto for a family with two kids and book the train from Tokyo."   # -> booking-agent
osr -t travel.yaml -r travel.yaml route "Change the booking for guest anna.k@example.com to a late arrival on Friday."   # -> guest-records-onprem

9. Data and analytics

Situation. Analysts ask for SQL, quick arithmetic and narrative analysis of datasets. SQL goes to a deterministic skill, arithmetic to a calculator tool that costs nothing and never hallucinates, analysis to a mid-tier model.

Catalogue.

# analytics.yaml
targets:
  - id: skill-sql
    kind: skill
    name: SQL skill
    capabilities: { domains: [data_analysis], actions: [code_generation, extract] }
    cost: { usd_per_1k_tokens: 0.001 }
    latency_ms: 200
    quality_prior: 0.8
    instructions: "Always qualify table names with the schema; never SELECT *."
    examples:
      - "Write a SQL query that returns revenue per month."
  - id: tool-calculator
    kind: tool
    name: Calculator
    capabilities: { domains: [math], actions: [qa], max_complexity: 0.3 }
    cost: { usd_per_1k_tokens: 0.0 }
    latency_ms: 5
    quality_prior: 0.99
    examples:
      - "What is 17 percent of 2340?"
  - id: analyst-mid
    kind: llm
    name: Analyst model
    capabilities:
      domains: [data_analysis, math, general]
      actions: [reasoning, summarize, qa, generation, planning]
      min_complexity: 0.25
    cost: { usd_per_1k_tokens: 0.003 }
    latency_ms: 900
    quality_prior: 0.78
    examples:
      - "Analyze this CSV of monthly sales and describe the trend."
rules:
  - name: sql-to-skill
    when: { domains: [data_analysis], actions: [code_generation] }
    prefer: [skill-sql]
    pin: true
    weight: 1.0
  - name: arithmetic-to-calculator
    when: { domains: [math], max_complexity: 0.4 }
    prefer: [tool-calculator]
    pin: true
    weight: 1.0

What happens.

RequestRouted toWhy
Write a SQL query that returns revenue per month and region from the orders table.skill-sqlData-analysis code generation: pinned to the SQL skill
What is 17% of 2340?tool-calculatorSimple arithmetic: pinned to the free, exact tool
Analyze this CSV of monthly sales and explain the trend and the likely drivers.analyst-midReasoning over data: the analyst model

Try it.

osr -t analytics.yaml -r analytics.yaml route "What is 17% of 2340?"   # -> tool-calculator
osr -t analytics.yaml -r analytics.yaml route "Analyze this CSV of monthly sales and explain the trend and the likely drivers."   # -> analyst-mid

The patterns behind the scenarios

You needDeclareWhat the router does
Personal or regulated data never leaves your boundaryconstraints: { pii_allowed: false } on every external target; a rule when: { contains_pii: true } preferring the internal oneDetects e-mail, phone, IBAN, card, SSN and Aadhaar numbers in the request; policy removes every pii_allowed: false target before scoring
A whole department must stay on-premises regardless of content--constraint data_boundary=on_prem (HTTP: "constraints": {"data_boundary": "on_prem"})Rejects every target with a weaker boundary and records the reason in the trace
Only targets in a regionconstraints: { regions: [eu] } on the target and --constraint region=eu on the requestSame hard filter
A person handles emergencies, complaints and sensitive casesA human target with actions: [escalate] and a pinned rule when: { actions: [escalate] }Every model is excluded when the escalation intent is detected
System events are routed like chatwhen: { event: ["order.*", "payment.failed"] }; send --event order.shipped (HTTP: "context": {"event": "order.shipped"})The event name supplies domain and intent; pinned rules pick the workflow, agent or person
Cheap things stay cheap, hard things get the best modelmax_complexity on cheap targets, min_complexity on expensive onesThe utility trades quality against cost and latency inside the admissible set
Deterministic work never goes to a modelA skill or tool target and a pinned rule on its actionSQL, translation, arithmetic reach the skill or tool directly
A house style on top of any modelA persona with primary: false; ask with --planFills the persona slot of every plan whose domain matches; its instructions become the system prompt

From the page to production

  • Same file, served over HTTP. osr -t retail.yaml -r retail.yaml serve exposes /route, /feedback, the OpenAI-compatible /v1 endpoints and /mcp for the catalogue above; the deploy guide covers the container image, Helm chart and the hardening flags.
  • Real providers, still without code. Point the served router at any OpenAI-compatible endpoint with OSR_LLM_BASE_URL and OSR_LLM_API_KEY; every model target whose metadata.model names an upstream model then answers /v1/chat/completions for real (deploy guide). Attaching your own handlers in Python is the user guide's fourth chapter.
  • Hosted platform. The same decisions, traces and feedback loop behind an API key, with plans, tenants and a dashboard: platform guide. Mount a catalogue with OSR_TARGETS / OSR_RULES when you run the platform yourself (install guide); POST /api/v1/route takes the same text, context (including context.event) and constraints as the examples above.
  • Hardening for multi-tenant traffic. Health circuit breakers, budgets, input and output guards, audit and persistent learner state come from the enterprise builder or, on osr serve, the --guard --redact-pii --output-guard redact --health --audit audit.jsonl flags.
  • Measuring your own traffic. Put your requests and the target you expect into a JSONL file ({"text": "...", "expected": "support-agent"} per line) and run osr -t retail.yaml -r retail.yaml eval traffic.jsonl to gate routing accuracy in CI (evaluation).

How this page is verified

The test suite parses this Markdown file. For every scenario it writes the YAML block to the file named in its first line, builds the router the way osr does (load_targets + load_rules + the default strategies), routes every row of the What happens table (text rows as plain requests, event rows as an event without text) twice - with the SDK's default objective and with the command line's default --cost-weight 0.3 - and asserts the Routed to target. The same rows then go through osr -t <file> -r <file> route --json and through POST /route on the FastAPI app osr serve runs. Every osr command in every Try it block is executed as written, and its # -> target annotation is asserted. The tables are then repeated against the hosted platform application with the catalogue mounted as the deployment's OSR_TARGETS / OSR_RULES (POST /api/v1/route with a workspace key). Change a request, a target or a rule here and the tests tell you whether every surface still agrees.

Against a running deployment - production included - the same requests go to the default catalogue the platform mounts with a real workspace key, checking the guarantees that hold there (personal data never reaches a public-cloud model, escalations reach the human queue, payment.failed and order.shipped route without text, translations reach the translation skill, arithmetic reaches the calculator), recording feedback for every decision.

Something wrong or out of date on this page? Tell us - the page is named for you, so a one-line note is enough.