/** * AgentGuard Spend: outcome model router. * * The router is local config-as-data. It never sends prompts, completions, * provider keys, policies, or customer data to AgentGuard infrastructure. * * Patent notice: Protected by U.S. patent-pending technology * (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626; * 64/071,781; 64/071,789). */ import { getCachedCatalog, type OpenRouterCatalog } from './openrouter-catalog'; // Spend-and-policy-aware model router (removes vendor risk). Re-exported here so // the `./router` subpath exposes both the config recommender and the executor. export { createRouter, RoutingExhaustedError, type ModelRouter, type CreateRouterConfig, type RouterCandidate, type RouterPolicy, type RouterProvider, type RouterRequest, type RouteResult, type RouteReason, type RouteFallback, } from './router-engine'; export interface RouterCatalogModel { id: string; in_per_mtok: number; out_per_mtok: number; context?: number; image_capable?: boolean; origin?: 'US' | 'EU' | 'CN'; } export interface RouterDefault { vertical: string; outcome: string; need: string; effort?: 'low' | 'medium' | 'high' | 'xhigh'; reviewer_effort?: 'low' | 'medium' | 'high' | 'xhigh'; drafter: string; reviewer: string; vision_model?: string; allowed_models: readonly string[]; blocked_origins: readonly string[]; guardrail_tier?: string; status?: string; } export interface RecommendOptions { vertical?: string; outcome?: string; posture?: string; budgetTier?: string; budget_tier?: string; imageCapable?: boolean; catalog?: RouterCatalogModel[]; } export interface ModelRouteRecommendation { vertical: string; outcome: string; need: string; posture: string; budgetTier: string; effort: 'low' | 'medium' | 'high' | 'xhigh'; drafterEffort: 'low' | 'medium' | 'high' | 'xhigh'; reviewerEffort: 'low' | 'medium' | 'high' | 'xhigh'; drafter: string; reviewer: string; visionModel: string | null; blockedOrigins: string[]; allowedModels: string[]; } const DEFAULTS = { "version": 2, "compliance_verticals": [ "law", "accounting", "insurance", "realestate" ], "blocked_origin_prefixes": [ "deepseek/", "qwen/", "moonshotai/", "01-ai/", "z-ai/", "baichuan/", "stepfun/", "bytedance/", "ernie/" ], "defaults": { "law_medical_records_chronology": { "vertical": "law", "outcome": "medical_records_chronology", "need": "long-context", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-4.1-nano", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "openai/gpt-4.1-nano", "openai/gpt-oss-120b", "openai/gpt-4.1", "anthropic/claude-sonnet-4.6", "meta-llama/llama-4-scout" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "law_deposition_transcript_summary": { "vertical": "law", "outcome": "deposition_transcript_summary", "need": "long-context", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-4.1-nano", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "openai/gpt-4.1-nano", "openai/gpt-oss-120b", "openai/gpt-4.1", "anthropic/claude-sonnet-4.6", "meta-llama/llama-4-scout" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "law_billing_time_entry_utbms": { "vertical": "law", "outcome": "billing_time_entry_utbms", "need": "structured-reasoning", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "law_contract_clause_extraction": { "vertical": "law", "outcome": "contract_clause_extraction", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "law_discovery_response_shell": { "vertical": "law", "outcome": "discovery_response_shell", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "law_privilege_log_drafting": { "vertical": "law", "outcome": "privilege_log_drafting", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "law_client_intake_conflict_data_assembly": { "vertical": "law", "outcome": "client_intake_conflict_data_assembly", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "law_pi_demand_letter_draft": { "vertical": "law", "outcome": "pi_demand_letter_draft", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "law_estate_planning_doc_assembly": { "vertical": "law", "outcome": "estate_planning_doc_assembly", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "law_immigration_form_population": { "vertical": "law", "outcome": "immigration_form_population", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "law_pleading_fact_section_synthesis": { "vertical": "law", "outcome": "pleading_fact_section_synthesis", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "accounting_transaction_categorization": { "vertical": "accounting", "outcome": "transaction_categorization", "need": "fast-classification", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/gpt-5-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "meta-llama/llama-3.1-8b-instruct" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "accounting_bank_reconciliation": { "vertical": "accounting", "outcome": "bank_reconciliation", "need": "structured-reasoning", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "accounting_ap_source_doc_extraction": { "vertical": "accounting", "outcome": "ap_source_doc_extraction", "need": "vision-document", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-4.1-nano", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "openai/gpt-4.1-nano", "openai/gpt-oss-120b", "openai/gpt-4.1", "anthropic/claude-sonnet-4.6", "meta-llama/llama-4-scout" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "accounting_1099_pre_filing_reconciliation": { "vertical": "accounting", "outcome": "1099_pre_filing_reconciliation", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "accounting_w9_tin_matching": { "vertical": "accounting", "outcome": "w9_tin_matching", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "accounting_payroll_941_reconciliation": { "vertical": "accounting", "outcome": "payroll_941_reconciliation", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "accounting_new_client_coa_mapping": { "vertical": "accounting", "outcome": "new_client_coa_mapping", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "accounting_trial_balance_comparison": { "vertical": "accounting", "outcome": "trial_balance_comparison", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "accounting_sales_tax_return_data_prep": { "vertical": "accounting", "outcome": "sales_tax_return_data_prep", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "accounting_month_end_close_package": { "vertical": "accounting", "outcome": "month_end_close_package", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "insurance_policy_checking_variance": { "vertical": "insurance", "outcome": "policy_checking_variance", "need": "long-context", "effort": "high", "reviewer_effort": "xhigh", "drafter": "openai/gpt-4.1-nano", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "openai/gpt-4.1-nano", "openai/gpt-oss-120b", "openai/gpt-4.1", "anthropic/claude-sonnet-4.6", "meta-llama/llama-4-scout" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "active" }, "insurance_submission_acord_extraction": { "vertical": "insurance", "outcome": "submission_acord_extraction", "need": "vision-document", "effort": "medium", "reviewer_effort": "high", "drafter": "openai/gpt-4.1-nano", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "openai/gpt-4.1-nano", "openai/gpt-oss-120b", "openai/gpt-4.1", "anthropic/claude-sonnet-4.6", "meta-llama/llama-4-scout" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "active" }, "insurance_commission_reconciliation": { "vertical": "insurance", "outcome": "commission_reconciliation", "need": "structured-reasoning", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "insurance_loss_run_extraction_summary": { "vertical": "insurance", "outcome": "loss_run_extraction_summary", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "insurance_dec_page_extraction_to_ams": { "vertical": "insurance", "outcome": "dec_page_extraction_to_ams", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "insurance_endorsement_change_request_confirmation": { "vertical": "insurance", "outcome": "endorsement_change_request_confirmation", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "insurance_renewal_dec_vs_dec_comparison": { "vertical": "insurance", "outcome": "renewal_dec_vs_dec_comparison", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "insurance_appetite_triage_uploaded_guides": { "vertical": "insurance", "outcome": "appetite_triage_uploaded_guides", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "insurance_third_party_coi_verification": { "vertical": "insurance", "outcome": "third_party_coi_verification", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "insurance_coi_drafting_acord25": { "vertical": "insurance", "outcome": "coi_drafting_acord25", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "insurance_fnol_intake": { "vertical": "insurance", "outcome": "fnol_intake", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "realestate_transaction_deadline_extraction": { "vertical": "realestate", "outcome": "transaction_deadline_extraction", "need": "long-context", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-4.1-nano", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "openai/gpt-4.1-nano", "openai/gpt-oss-120b", "openai/gpt-4.1", "anthropic/claude-sonnet-4.6", "meta-llama/llama-4-scout" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "realestate_lease_abstraction": { "vertical": "realestate", "outcome": "lease_abstraction", "need": "long-context", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-4.1-nano", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "openai/gpt-4.1-nano", "openai/gpt-oss-120b", "openai/gpt-4.1", "anthropic/claude-sonnet-4.6", "meta-llama/llama-4-scout" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "realestate_fair_housing_copy_scanner": { "vertical": "realestate", "outcome": "fair_housing_copy_scanner", "need": "fast-classification", "effort": "medium", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/gpt-5-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "meta-llama/llama-3.1-8b-instruct" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "active" }, "realestate_owner_monthly_statement_narrative": { "vertical": "realestate", "outcome": "owner_monthly_statement_narrative", "need": "structured-reasoning", "effort": "medium", "reviewer_effort": "medium", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "specced" }, "realestate_pm_ap_invoice_extraction": { "vertical": "realestate", "outcome": "pm_ap_invoice_extraction", "need": "structured-reasoning", "effort": "medium", "reviewer_effort": "medium", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "specced" }, "realestate_rent_roll_delinquency_summary": { "vertical": "realestate", "outcome": "rent_roll_delinquency_summary", "need": "structured-reasoning", "effort": "medium", "reviewer_effort": "medium", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "specced" }, "realestate_maintenance_request_triage": { "vertical": "realestate", "outcome": "maintenance_request_triage", "need": "structured-reasoning", "effort": "medium", "reviewer_effort": "medium", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [ "CN" ], "guardrail_tier": "grounded_cite_or_refuse", "status": "specced" }, "realestate_tenant_screening_scorecard": { "vertical": "realestate", "outcome": "tenant_screening_scorecard", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "realestate_adverse_action_fcra_notice": { "vertical": "realestate", "outcome": "adverse_action_fcra_notice", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "reviewer_required", "status": "specced" }, "realestate_cma_comps_assembly": { "vertical": "realestate", "outcome": "cma_comps_assembly", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "realestate_disclosure_packet_preparation": { "vertical": "realestate", "outcome": "disclosure_packet_preparation", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [ "CN" ], "guardrail_tier": "human_of_record", "status": "specced" }, "ecommerce_wismo_order_status": { "vertical": "ecommerce", "outcome": "wismo_order_status", "need": "fast-classification", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/gpt-5-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "meta-llama/llama-3.1-8b-instruct" ], "blocked_origins": [], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "ecommerce_return_refund_eligibility": { "vertical": "ecommerce", "outcome": "return_refund_eligibility", "need": "structured-reasoning", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [], "guardrail_tier": "human_of_record", "status": "active" }, "ecommerce_payout_settlement_reconciliation": { "vertical": "ecommerce", "outcome": "payout_settlement_reconciliation", "need": "structured-reasoning", "effort": "low", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [], "guardrail_tier": "grounded_cite_or_refuse", "status": "active" }, "ecommerce_fba_lost_damaged_reconciliation": { "vertical": "ecommerce", "outcome": "fba_lost_damaged_reconciliation", "need": "structured-reasoning", "effort": "medium", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [], "guardrail_tier": "reviewer_required", "status": "active" }, "ecommerce_fba_missing_return_audit": { "vertical": "ecommerce", "outcome": "fba_missing_return_audit", "need": "structured-reasoning", "effort": "medium", "reviewer_effort": "high", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [], "guardrail_tier": "reviewer_required", "status": "active" }, "ecommerce_support_reply_from_docs": { "vertical": "ecommerce", "outcome": "support_reply_from_docs", "need": "structured-reasoning", "effort": "medium", "reviewer_effort": "medium", "drafter": "openai/gpt-oss-120b", "reviewer": "openai/o4-mini", "allowed_models": [ "openai/gpt-oss-120b", "openai/gpt-4.1-nano", "openai/gpt-5-mini", "openai/o4-mini" ], "blocked_origins": [], "guardrail_tier": "grounded_cite_or_refuse", "status": "specced" }, "ecommerce_chargeback_representment_ce30": { "vertical": "ecommerce", "outcome": "chargeback_representment_ce30", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [], "guardrail_tier": "human_of_record", "status": "specced" }, "ecommerce_amazon_a_to_z_appeal": { "vertical": "ecommerce", "outcome": "amazon_a_to_z_appeal", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [], "guardrail_tier": "human_of_record", "status": "specced" }, "ecommerce_product_listing_from_spec": { "vertical": "ecommerce", "outcome": "product_listing_from_spec", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [], "guardrail_tier": "reviewer_required", "status": "specced" }, "ecommerce_negative_review_response": { "vertical": "ecommerce", "outcome": "negative_review_response", "need": "drafting", "effort": "medium", "reviewer_effort": "high", "drafter": "mistralai/mistral-small-3.2-24b-instruct", "reviewer": "anthropic/claude-sonnet-4.6", "allowed_models": [ "mistralai/mistral-small-3.2-24b-instruct", "openai/gpt-oss-120b", "openai/gpt-5-mini", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6" ], "blocked_origins": [], "guardrail_tier": "reviewer_required", "status": "specced" } } } as const; const NEED_FALLBACKS: Record = { 'fast-classification': ['meta-llama/llama-3.1-8b-instruct', 'openai/gpt-5-nano', 'mistralai/mistral-nemo', 'qwen/qwen3-30b-a3b'], drafting: ['mistralai/mistral-small-3.2-24b-instruct', 'meta-llama/llama-3.3-70b-instruct', 'openai/gpt-oss-120b', 'openai/gpt-5-mini', 'deepseek/deepseek-chat-v3.1', 'anthropic/claude-haiku-4.5'], 'structured-reasoning': ['openai/gpt-oss-120b', 'openai/gpt-4.1-nano', 'openai/gpt-5-mini', 'qwen/qwen3-235b-a22b-2507', 'openai/o4-mini'], 'long-context': ['meta-llama/llama-4-scout', 'openai/gpt-4.1-nano', 'openai/gpt-4.1', 'anthropic/claude-sonnet-4.6', 'deepseek/deepseek-v4-flash'], 'vision-document': ['mistralai/mistral-small-3.2-24b-instruct', 'openai/gpt-4.1-mini', 'qwen/qwen3-vl-8b-instruct', 'qwen/qwen3-vl-32b-instruct', 'openai/gpt-5.1'], photo: ['amazon/nova-lite-v1', 'qwen/qwen3-vl-8b-instruct', 'openai/gpt-5.1', 'anthropic/claude-sonnet-4.6'], }; const REVIEWER_FALLBACKS = ['openai/gpt-5-mini', 'openai/o4-mini', 'openai/gpt-5.1', 'anthropic/claude-sonnet-4.6', 'deepseek/deepseek-r1', 'qwen/qwen3-235b-a22b']; const TIER_CEILINGS: Record = { free: 0.5, solo: 0.5, solo_pro: 0.5, startup: 2, startup_pro: 2, growth: Number.POSITIVE_INFINITY, growth_pro: Number.POSITIVE_INFINITY }; const REVIEWER_CEILINGS: Record = { free: 2, solo: 2, solo_pro: 2, startup: 10, startup_pro: 10, growth: Number.POSITIVE_INFINITY, growth_pro: Number.POSITIVE_INFINITY }; export function recommend(options: RecommendOptions = {}): ModelRouteRecommendation { const catalog = options.catalog ?? catalogFromOpenRouter(getCachedCatalog()) ?? fallbackCatalogFromDefaults(); const catalogById = new Map(catalog.map((model) => [model.id, model])); const vertical = normalizeVertical(options.vertical); const def = defaultFor(vertical, options.outcome); const posture = (options.posture || (isComplianceVertical(vertical) ? 'compliance' : 'standard')).toLowerCase(); const budgetTier = (options.budgetTier || options.budget_tier || 'solo').toLowerCase(); const drafterEffort = defaultEffort(def, 'drafter'); const reviewerEffort = defaultEffort(def, 'reviewer'); const drafterCandidates = unique([...(def.allowed_models || []), ...(NEED_FALLBACKS[def.need] || []), def.drafter]); const reviewerCandidates = unique([def.reviewer, ...REVIEWER_FALLBACKS]); const drafter = pickModel(drafterCandidates, catalogById, def, posture, TIER_CEILINGS[budgetTier] ?? TIER_CEILINGS.solo) ?? def.drafter; const reviewer = pickModel(reviewerCandidates, catalogById, def, posture, REVIEWER_CEILINGS[budgetTier] ?? REVIEWER_CEILINGS.solo) ?? def.reviewer; const needsVision = Boolean(def.vision_model || options.imageCapable || def.need.startsWith('vision')); const visionCandidates = unique([def.vision_model, ...(NEED_FALLBACKS['vision-document'] || []), ...(NEED_FALLBACKS.photo || [])]); const visionModel = needsVision ? pickModel(visionCandidates, catalogById, def, posture, TIER_CEILINGS[budgetTier] ?? TIER_CEILINGS.solo, true) ?? def.vision_model ?? null : null; return { vertical, outcome: def.outcome, need: def.need, posture, budgetTier, effort: drafterEffort, drafterEffort, reviewerEffort, drafter, reviewer, visionModel, blockedOrigins: posture === 'compliance' ? ['CN'] : [...def.blocked_origins], allowedModels: drafterCandidates.filter((id) => { const model = catalogById.get(id); return Boolean(model && allowedByOrigin(model, def, posture)); }), }; } function defaultEffort(def: RouterDefault, role: 'drafter' | 'reviewer'): 'low' | 'medium' | 'high' | 'xhigh' { const value = role === 'reviewer' ? def.reviewer_effort : def.effort; return value === 'low' || value === 'medium' || value === 'high' || value === 'xhigh' ? value : role === 'reviewer' ? 'high' : 'medium'; } function defaultFor(vertical: string, outcome?: string): RouterDefault { const key = defaultKey(vertical, outcome); const rows = DEFAULTS.defaults as unknown as Record; return rows[key] ?? Object.values(rows).find((row) => row.vertical === vertical) ?? Object.values(rows)[0]; } export function defaultKey(vertical: string, outcome?: string): string { const clean = normalizeVertical(vertical); const raw = String(outcome || '').trim(); const normalized = raw.startsWith(clean + '_') ? raw.slice(clean.length + 1) : slug(raw); return clean + '_' + normalized; } function pickModel(ids: string[], catalog: Map, def: RouterDefault, posture: string, ceiling: number, requireImage = false): string | null { const rows = ids .map((id) => catalog.get(id)) .filter((model): model is RouterCatalogModel => Boolean(model)) .filter((model) => allowedByOrigin(model, def, posture)) .filter((model) => !requireImage || model.image_capable === true); const underCeiling = rows.filter((model) => blended(model) <= ceiling); const pool = underCeiling.length ? underCeiling : rows; pool.sort((a, b) => blended(a) - blended(b) || a.id.localeCompare(b.id)); return pool[0]?.id ?? null; } function allowedByOrigin(model: RouterCatalogModel, def: RouterDefault, posture: string): boolean { if (/^google\/gemini/i.test(model.id)) return false; const blocksCn = posture === 'compliance' || def.blocked_origins.includes('CN'); return !(blocksCn && isCnOrigin(model)); } function isCnOrigin(model: RouterCatalogModel): boolean { return model.origin === 'CN' || /^(deepseek|qwen|moonshotai|01-ai|z-ai|baichuan|stepfun|bytedance|ernie)\//.test(model.id); } function blended(model: RouterCatalogModel): number { return (model.in_per_mtok + model.out_per_mtok) / 2; } function normalizeVertical(value?: string): string { const v = String(value || '').toLowerCase().replace(/[-_ ]/g, ''); if (v === 'realestate') return 'realestate'; return ['law', 'accounting', 'insurance', 'ecommerce'].includes(v) ? v : 'law'; } function isComplianceVertical(vertical: string): boolean { return (DEFAULTS.compliance_verticals as readonly string[]).includes(vertical); } function slug(value: string): string { return value.toLowerCase().replace(/&/g, 'and').replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, ''); } function unique(values: Array): string[] { return [...new Set(values.filter((value): value is string => Boolean(value)))]; } function catalogFromOpenRouter(catalog: OpenRouterCatalog | null): RouterCatalogModel[] | null { if (!catalog?.data?.length) return null; return catalog.data.map((model) => ({ id: model.id, in_per_mtok: Number(model.pricing?.prompt || 0) * 1e6, out_per_mtok: Number(model.pricing?.completion || 0) * 1e6, context: model.context_length || model.top_provider?.context_length || 0, image_capable: Array.isArray((model.architecture as { input_modalities?: unknown } | undefined)?.input_modalities) ? ((model.architecture as { input_modalities?: string[] }).input_modalities || []).includes('image') : false, origin: (/^(deepseek|qwen|moonshotai|01-ai|z-ai|baichuan|stepfun|bytedance|ernie)\//.test(model.id) ? 'CN' : /^mistralai\//.test(model.id) ? 'EU' : 'US') as 'US' | 'EU' | 'CN', })).filter((model) => Number.isFinite(model.in_per_mtok) && Number.isFinite(model.out_per_mtok)); } function fallbackCatalogFromDefaults(): RouterCatalogModel[] { const ids = new Set(); for (const row of Object.values(DEFAULTS.defaults as unknown as Record)) { ids.add(row.drafter); ids.add(row.reviewer); if (row.vision_model) ids.add(row.vision_model); for (const id of row.allowed_models) ids.add(id); } return [...ids].map((id) => ({ id, in_per_mtok: fallbackPrice(id)[0], out_per_mtok: fallbackPrice(id)[1], image_capable: /nova|vl|vision|mistral-small|gpt-4\.1-mini|gpt-5\.1/.test(id), origin: /^(deepseek|qwen|moonshotai|01-ai|z-ai|baichuan|stepfun|bytedance|ernie)\//.test(id) ? 'CN' : /^mistralai\//.test(id) ? 'EU' : 'US' })); } function fallbackPrice(id: string): [number, number] { const table: Record = { 'openai/gpt-5-nano': [0.05, 0.4], 'openai/gpt-4.1-nano': [0.1, 0.4], 'openai/gpt-5-mini': [0.25, 2], 'openai/gpt-oss-120b': [0.039, 0.18], 'openai/o4-mini': [1.1, 4.4], 'openai/gpt-5.1': [1.25, 10], 'openai/gpt-4.1': [2, 8], 'openai/gpt-4.1-mini': [0.4, 1.6], 'anthropic/claude-haiku-4.5': [1, 5], 'anthropic/claude-sonnet-4.6': [3, 15], 'meta-llama/llama-3.1-8b-instruct': [0.02, 0.05], 'meta-llama/llama-3.3-70b-instruct': [0.1, 0.32], 'meta-llama/llama-4-scout': [0.08, 0.3], 'mistralai/mistral-small-3.2-24b-instruct': [0.075, 0.2], 'mistralai/mistral-nemo': [0.02, 0.03], 'amazon/nova-lite-v1': [0.06, 0.24], 'deepseek/deepseek-chat-v3.1': [0.21, 0.79], 'deepseek/deepseek-r1': [0.7, 2.5], 'qwen/qwen3-30b-a3b': [0.09, 0.45], 'qwen/qwen3-235b-a22b-2507': [0.071, 0.1], 'qwen/qwen3-235b-a22b': [0.455, 1.82], 'qwen/qwen3-vl-8b-instruct': [0.08, 0.5], 'qwen/qwen3-vl-32b-instruct': [0.104, 0.416], }; return table[id] || [1, 5]; }