Skip to content

feat(openrouter-execution): add OpenRouter execution layer with BYOK and tier-based routing (APR-206)#58

Open
JacobSampson wants to merge 2 commits into
mainfrom
APR-206/openrouter-execution-layer
Open

feat(openrouter-execution): add OpenRouter execution layer with BYOK and tier-based routing (APR-206)#58
JacobSampson wants to merge 2 commits into
mainfrom
APR-206/openrouter-execution-layer

Conversation

@JacobSampson

Copy link
Copy Markdown
Contributor

Summary

  • Adds @aprovan/openrouter-execution package — the execution layer between the model selection engine (APR-205) and the OpenRouter API
  • resolveTier(complexity, costQualityTradeoff) maps the two inputs from the model selection engine to a model tier (FREE → BUDGET → MID_TIER → FRONTIER)
  • execute(request, config?) calls OpenRouter with BYOK-first provider preferences, automatic fallback across models in a tier, per-request budget caps, and retry on 429/5xx errors
  • Free models (complexity ≤ 2, tradeoff < 7) use :free-suffixed model IDs via OpenRouter's free tier; BYOK subscription keys registered in the OpenRouter account are used automatically at no markup

Architecture

File Responsibility
src/types.ts Zod schemas and TypeScript types for all public contracts
src/model-catalog.ts Ordered model lists per tier with OpenRouter provider preferences
src/tier-router.ts Maps (complexity 1-5, tradeoff 0-10) → ModelTier
src/openrouter-client.ts Thin fetch wrapper for /chat/completions with retryable error classification
src/executor.ts execute() — orchestrates tier resolution, model selection, retries, and fallback

Required Environment Variable

Variable Required Default Purpose
OPENROUTER_API_KEY OpenRouter API key
OPENROUTER_MAX_BUDGET_USD 0.5 Per-request cost cap in USD
OPENROUTER_APP_NAME AprovanLabs X-Title header for OpenRouter analytics

Test Plan

  • pnpm --filter @aprovan/openrouter-execution test — 21 tests across 2 files, all green
  • pnpm --filter @aprovan/openrouter-execution typecheck — zero type errors
  • pnpm --filter @aprovan/openrouter-execution lint — zero lint errors
  • pnpm --filter @aprovan/openrouter-execution build — ESM + DTS output clean
  • CI green on push

Acceptance criteria coverage:

  • OpenRouter API integration: OpenRouterClient + execute()
  • BYOK routing: providers field in catalog + provider.order in each request
  • cost_quality_tradeoff mapping: resolveTier() — 13 unit tests
  • Fallback routing: retry loop in execute() with allow_fallbacks: true
  • Free models for low complexity: ModelTier.FREE catalog + tier routing

Closes #APR-206

🤖 Generated with Claude Code

JacobSampson and others added 2 commits June 12, 2026 19:18
…and tier-based routing (APR-206)

Implements @aprovan/openrouter-execution — the execution layer that sits between the
model selection engine (APR-205) and the OpenRouter API.

- Tier router maps (complexity 1-5, cost_quality_tradeoff 0-10) to FREE / BUDGET /
  MID_TIER / FRONTIER model tiers
- Model catalog defines ordered fallback lists per tier using OpenRouter model IDs;
  BYOK subscription keys registered in OpenRouter are used automatically
- OpenRouterClient wraps the /chat/completions endpoint with provider preferences,
  per-request budget caps (max_price), and retryable error handling
- execute() tries each model in the tier in order, retrying on 429/5xx before
  falling back to the next model; reports fallback usage in the result
- 21 unit tests covering tier routing and executor behavior (fetch is mocked)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onfig

- Add eslint.config.mjs (ESLint 9 flat config extending @aprovan/eslint-config/base)
- Fix lint script to use ESLint 9 glob pattern
- Fix import order in executor.ts (alphabetical per import/order rule)
- Fix tsconfig to extend @aprovan/tsconfig/node.json (not /package.json)
- Add lib: ["ES2022", "DOM"] for fetch/AbortController types
- Add @types/node devDependency for process.env access
- Add return type to test helper function

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant