Repository navigation
Discover the whole OpenRouter catalog for BYO SaaS tenants #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| return { | ||
| providers = { | ||
| openrouter = {discovery='static',base_url='https://openrouter.ai/api/v1',api_kind='openai_compatible',auth_env='OPENROUTER_API_KEY',source='openrouter',tier='partner'}, | ||
| openrouter_market = {discovery='marketplace',discovery_id='openrouter_market',base_url='https://openrouter.ai/api/v1',api_kind='openai_compatible',auth_env='OPENROUTER_API_KEY',source='openrouter',tier='marketplace',market_price_cap={input=1000,output=1000}}, | ||
| }, | ||
| models = { | ||
| ['curated-model'] = {served_by={{provider='openrouter',provider_model_id='vendor/curated-model'}},capabilities={context=128000,supports_tools=true,supports_json_mode=true}}, | ||
| }, | ||
| profiles={default={scorer={'zero'}}}, | ||
| policy_envelope={'and',{'meets_req'},{'not',{'is','disabled'}}}, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| """BYO OpenRouter: the whole live catalog, discovered on demand without operator refreshers.""" | ||
| import asyncio | ||
| from pathlib import Path | ||
|
|
||
| import httpx | ||
| import pytest | ||
|
|
||
| from llm_router_host import LLMRouterHost | ||
| from saas_routes import choices | ||
| import tenant_providers as tp | ||
|
|
||
| ROOT = Path(__file__).resolve().parents[1] | ||
|
|
||
| MODELS = [ | ||
| {"id": "vendor/curated-model", "pricing": {"prompt": "0.000002", "completion": "0.000008"}, | ||
| "context_length": 200000, "supported_parameters": ["tools"], | ||
| "links": {"details": "/api/v1/models/vendor/curated-model/endpoints"}}, | ||
| {"id": "newlab/brand-new-model", "pricing": {"prompt": "0.0000001", "completion": "0.0000004"}, | ||
| "context_length": 131072, "supported_parameters": ["tools", "response_format"], | ||
| "architecture": {"input_modalities": ["text", "image"]}, | ||
| "links": {"details": "/api/v1/models/newlab/brand-new-model/endpoints"}}, | ||
| {"id": "other/variable-price", "pricing": {"prompt": "-1", "completion": "-1"}, "context_length": 8192}, | ||
| ] | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def base(host_store_clean): | ||
| tp._cache.clear() | ||
| tp._openrouter_public.update(at=0.0, value=None, failed_at=None) | ||
| host = LLMRouterHost(ROOT/'core/router.lua', ROOT/'tests/fixtures/openrouter_byo.lua', | ||
| discover=lambda _: {'ok': True, 'offers': [{'model_family': 'OPERATOR-ONLY'}]}) | ||
| host.init() | ||
| return host | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def openrouter(monkeypatch): | ||
| """Mock OpenRouter's public API; records every request path.""" | ||
| state = {"paths": [], "fail": False, "models": MODELS} | ||
| real = httpx.AsyncClient | ||
|
|
||
| def network(request): | ||
| state["paths"].append(request.url.path + ("?" + request.url.query.decode() if request.url.query else "")) | ||
| assert request.url.host == "openrouter.ai" | ||
| assert "authorization" not in request.headers # the listing needs no tenant credential | ||
| if state["fail"]: | ||
| return httpx.Response(503) | ||
| if request.url.query: | ||
| return httpx.Response(200, json={"data": []}) | ||
| return httpx.Response(200, json={"data": state["models"]}) | ||
|
|
||
| monkeypatch.setattr(httpx, "AsyncClient", lambda *a, **k: real(transport=httpx.MockTransport(network))) | ||
| return state | ||
|
|
||
|
|
||
| def tenant(base, tenant_id, key="sk-or-tenant"): | ||
| return base.for_tenant(tenant_id, {"OPENROUTER_API_KEY": key} if key else {}) | ||
|
|
||
|
|
||
| def test_byo_openrouter_lists_live_catalog_once_with_curated_prices(base, openrouter): | ||
| child = tenant(base, 1) | ||
| asyncio.run(tp.prepare(child)) | ||
| rows = {row["id"]: row for row in choices(child)} | ||
| assert set(rows) == {"openrouter|curated-model", "openrouter_market|brand-new-model"} | ||
| assert {row["label"].split(" · ")[1] for row in rows.values()} == {"OpenRouter"} | ||
| assert (rows["openrouter|curated-model"]["price_in"], rows["openrouter|curated-model"]["price_out"]) == pytest.approx((2.0, 8.0)) | ||
| new = rows["openrouter_market|brand-new-model"] | ||
| assert (new["price_in"], new["price_out"]) == pytest.approx((0.1, 0.4)) | ||
| assert new["tools"] is True | ||
| assert child._connection_errors == {} | ||
| # Only the listing (+ decision listing): no per-model endpoint detail requests. | ||
| assert not any("/endpoints" in path for path in openrouter["paths"]) | ||
|
|
||
|
|
||
| def test_new_models_appear_after_the_cache_expires(base, openrouter, monkeypatch): | ||
| asyncio.run(tp.prepare(tenant(base, 1))) | ||
| openrouter["models"] = MODELS + [{"id": "lab/released-today", "context_length": 32768, | ||
| "pricing": {"prompt": "0.0000003", "completion": "0.0000009"}}] | ||
| child = tenant(base, 2) | ||
| asyncio.run(tp.prepare(child)) | ||
| assert "openrouter_market|released-today" not in {r["id"] for r in choices(child)} # within TTL: shared snapshot | ||
| monkeypatch.setattr(tp, "OPENROUTER_CATALOG_TTL_S", 0) | ||
| child = tenant(base, 2) | ||
| asyncio.run(tp.prepare(child)) | ||
| assert "openrouter_market|released-today" in {r["id"] for r in choices(child)} | ||
|
|
||
|
|
||
| def test_listing_is_shared_across_tenants_and_fetched_once_per_ttl(base, openrouter): | ||
| async def run(): | ||
| await asyncio.gather(*(tp.prepare(tenant(base, tid)) for tid in (1, 2, 3))) | ||
| asyncio.run(run()) | ||
| assert [p for p in openrouter["paths"] if p.endswith("/models")] == ["/api/v1/models"] | ||
|
|
||
|
|
||
| def test_without_openrouter_key_nothing_is_fetched_and_operator_discovery_is_not_used(base, openrouter): | ||
| child = tenant(base, 1, key=None) | ||
| asyncio.run(tp.prepare(child)) | ||
| assert openrouter["paths"] == [] | ||
| assert choices(child) == [] | ||
|
|
||
|
|
||
| def test_failure_reports_error_and_never_falls_back_to_operator(base, openrouter): | ||
| openrouter["fail"] = True | ||
| child = tenant(base, 1) | ||
| asyncio.run(tp.prepare(child)) | ||
| ids = {r["id"] for r in choices(child)} | ||
| assert not any("OPERATOR-ONLY" in i for i in ids) | ||
| assert not any(i.startswith("openrouter_market|") for i in ids) | ||
| assert "sk-or" not in str(child._connection_errors) | ||
| assert child._connection_errors["openrouter"] | ||
|
|
||
|
|
||
| def test_short_outage_serves_the_last_public_listing(base, openrouter, monkeypatch): | ||
| asyncio.run(tp.prepare(tenant(base, 1))) | ||
| monkeypatch.setattr(tp, "OPENROUTER_CATALOG_TTL_S", 0) | ||
| openrouter["fail"] = True | ||
| child = tenant(base, 2) | ||
| asyncio.run(tp.prepare(child)) | ||
| assert "openrouter_market|brand-new-model" in {r["id"] for r in choices(child)} | ||
| assert child._connection_errors == {} | ||
|
|
||
|
|
||
|
|
||
| def test_preview_ranks_long_tail_models_and_names_exclusions_by_public_provider(base, openrouter): | ||
| from saas_routes import preview | ||
| child = tenant(base, 1) | ||
| asyncio.run(tp.prepare(child)) | ||
| result = preview(child, {"goal": "cost", "workload": "agent", "timeout_seconds": 8, | ||
| "targets": ["openrouter|curated-model", "openrouter_market|brand-new-model", | ||
| "openrouter_market|retired-model"]}) | ||
| assert [row["id"] for row in result["ranked"]] == ["openrouter_market|brand-new-model", "openrouter|curated-model"] | ||
| assert [row["label"] for row in result["excluded"]] == ["retired-model · OpenRouter"] | ||
|
|
||
|
|
||
| def test_failed_refresh_is_not_retried_by_every_caller(base, openrouter, monkeypatch): | ||
| asyncio.run(tp.prepare(tenant(base, 1))) | ||
| monkeypatch.setattr(tp, "OPENROUTER_CATALOG_TTL_S", 0) | ||
| openrouter["fail"] = True | ||
| for tid in (2, 3, 4): | ||
| child = tenant(base, tid) | ||
| asyncio.run(tp.prepare(child)) | ||
| assert "openrouter_market|brand-new-model" in {r["id"] for r in choices(child)} | ||
| # One listing, one failed refresh; later callers are served the last list without upstream calls. | ||
| assert [p for p in openrouter["paths"] if p.endswith("/models")] == ["/api/v1/models", "/api/v1/models"] | ||
| monkeypatch.setattr(tp, "OPENROUTER_CATALOG_RETRY_S", 0) | ||
| openrouter["fail"] = False | ||
| asyncio.run(tp.prepare(tenant(base, 5))) | ||
| assert tp._openrouter_public["failed_at"] is None | ||
|
|
||
|
|
||
| def test_first_failure_without_a_listing_fails_fast_for_the_next_caller(base, openrouter): | ||
| openrouter["fail"] = True | ||
| asyncio.run(tp.prepare(tenant(base, 1))) | ||
| child = tenant(base, 2) | ||
| asyncio.run(tp.prepare(child)) | ||
| assert child._connection_errors["openrouter"] | ||
| assert len([p for p in openrouter["paths"] if p.endswith("/models")]) == 1 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: genlayerlabs/unhardcoded
Length of output: 8786
🏁 Script executed:
Repository: genlayerlabs/unhardcoded
Length of output: 11929
Record stale-catalog refresh failures to avoid repeated upstream retries.
When the cached catalog is past its TTL but still within the stale period, a failed first
/modelsrequest returns the stale value without recording the failure. Each later caller retries OpenRouter.The queued-cancellation scenario does not occur: a first-request timeout reaches the stale fallback after 15 seconds. If only the decisions request times out,
pricing()catches that error and updates the shared cache before releasing the lock.🛠️ Suggested fix
Reset
failed_atin thebasefixture intests/test_saas_openrouter_discovery.py.🤖 Prompt for AI Agents