Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,8 @@ for human review.
| `api` | medium | Calls each provider's OpenAI-compatible `/v1/models` endpoint when `<PROVIDER>_API_KEY` is set in your environment. Used to detect *removals* (a believed-free model that's no longer listed). |
| `litellm_cost_map` | medium | Reads the public [litellm](https://github.com/BerriAI/litellm) pricing map: flags zero-priced models as free **and** snapshots per-token prices for paid ones into the sidecar `pricing` block (used by the proxy to cost tokens offline — see [Token + cost accounting](#usage-accounting)). |
| `together` | high | When `TOGETHER_API_KEY` is set, reads Together's `/v1/models` pricing — zero-priced models are free; paid models contribute per-token prices to the `pricing` block. |
| `fireworks` | high | When `FIREWORKS_API_KEY` is set, reads Fireworks' `/inference/v1/models` and flags models marked `is_free`/`serverless_billing: free` or zero-priced as free. |
| `requesty` | high | When `REQUESTY_API_KEY` is set, reads Requesty's `/v1/models` pricing — zero-priced models are free; paid models contribute per-token prices to the `pricing` block. |
| `community` | low | Pulls the [tashfeenahmed/freellmapi](https://github.com/tashfeenahmed/freellmapi) community list as a sanity signal. |
| `probe` | high · **opt-in** | Sends a tiny real chat request to each `believed_free` model and flags any that report a cost. Off by default; enable with `probe_cost: true` in `config.json` or the `--probe` flag. Spends a little quota. |

Expand Down
5 changes: 5 additions & 0 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
"api_key": "sk-or-...",
"model_filter": null
},
"requesty": {
"base_url": "https://router.requesty.ai/v1",
"api_key": "...",
"model_filter": null
},
"ollama-cloud": {
"base_url": "https://ollama.com/v1",
"api_key": "...",
Expand Down
12 changes: 12 additions & 0 deletions llmproxy/providers.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"deepseek",
"cohere",
"openrouter",
"requesty",
"ollama-cloud",
"moonshot",
"minimax",
Expand Down Expand Up @@ -1391,6 +1392,17 @@
"free_limits": {},
"fallback_models": []
},
"requesty": {
"display": "Requesty (LLM router)",
"base_url": "https://router.requesty.ai/v1",
"key_required": true,
"key_hint": "Get your API key at app.requesty.ai",
"believed_free": [],
"model_reasoning": {},
"model_capabilities": {},
"free_limits": {},
"fallback_models": []
},
"opencode-zen": {
"display": "OpenCode Zen (free gateway)",
"base_url": "https://opencode.ai/zen/v1",
Expand Down
2 changes: 2 additions & 0 deletions scripts/sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .fireworks import FireworksSource
from .litellm_cost_map import LiteLLMCostMapSource
from .openrouter import OpenRouterSource
from .requesty import RequestySource
from .together import TogetherSource

ALL_SOURCES: dict[str, type[Source]] = {
Expand All @@ -21,6 +22,7 @@
"litellm_cost_map": LiteLLMCostMapSource,
"together": TogetherSource,
"fireworks": FireworksSource,
"requesty": RequestySource,
# Active cost probe. Excluded from the default source set because it sends
# real requests; opt in via free_tier.cost_probe.enabled or --cost-probe.
"cost_probe": CostProbeSource,
Expand Down
95 changes: 95 additions & 0 deletions scripts/sources/requesty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""Requesty source — pricing-aware /v1/models detection.

Requesty's `/v1/models` endpoint (OpenAI-compatible) returns pricing
metadata per model as flat top-level fields — `input_price`, `output_price`,
`cached_price` — not nested under a `pricing` sub-object. Models with zero
input/output cost are genuinely free (Requesty publishes a small set of
$0-cost models from upstream providers that host them for free, e.g. some
Nemotron and Poolside variants). Requires ``REQUESTY_API_KEY`` in the
environment; without it, the source yields nothing.

Model ids are already namespaced by upstream provider (e.g.
"anthropic/claude-opus-4-7"); we prefix with our own provider key for the
fully-qualified id, consistent with every other source.

Prices are plain per-token dollar amounts (confirmed against a live
response), consistent with OpenRouter's convention rather than Together's
per-million-token convention.
"""

from __future__ import annotations

import os

import requests

from .base import Evidence, Source

REQUESTY_URL = "https://router.requesty.ai/v1/models"
TIMEOUT = (5, 15)


class RequestySource(Source):
name = "requesty"

def __init__(self, url: str = REQUESTY_URL):
self.url = url

def fetch(self) -> list[Evidence]:
api_key = os.environ.get("REQUESTY_API_KEY")
if not api_key:
return []
resp = requests.get(
self.url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=TIMEOUT,
)
resp.raise_for_status()
data = resp.json()
models = data.get("data", data) if isinstance(data, dict) else data
if not isinstance(models, list):
return []

out: list[Evidence] = []
for model in models:
if not isinstance(model, dict):
continue
mid = model.get("id")
if not mid:
continue
in_cost = _to_float(model.get("input_price"))
out_cost = _to_float(model.get("output_price"))
is_free = (in_cost == 0.0 and out_cost == 0.0)
out.append(Evidence(
provider="requesty",
model_id=f"requesty/{mid}",
is_free=is_free,
source=self.name,
confidence="high",
url=self.url,
pricing=_pricing(in_cost, out_cost),
notes=f"input_price={model.get('input_price')!r} output_price={model.get('output_price')!r}",
))
return out


def _pricing(in_cost: float, out_cost: float) -> dict | None:
"""Per-token pricing for a PAID model, or None when free/unknown."""
in_ok = in_cost != float("inf")
out_ok = out_cost != float("inf")
if not in_ok and not out_ok:
return None
in_cost = in_cost if in_ok else 0.0
out_cost = out_cost if out_ok else 0.0
if in_cost == 0.0 and out_cost == 0.0:
return None
return {"input_cost_per_token": in_cost, "output_cost_per_token": out_cost}


def _to_float(v) -> float:
if v is None:
return float("inf")
try:
return float(v)
except (TypeError, ValueError):
return float("inf")
41 changes: 41 additions & 0 deletions tests/fixtures/requesty_models.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[
{
"id": "nvidia/nemotron-3-super-120b-a12b",
"object": "model",
"owned_by": "nvidia",
"input_price": 0,
"output_price": 0,
"cached_price": 0
},
{
"id": "nvidia/nemotron-3-nano-30b-a3b",
"object": "model",
"owned_by": "nvidia",
"input_price": 0,
"output_price": 0,
"cached_price": 0
},
{
"id": "google/gemma-4-31b-it",
"object": "model",
"owned_by": "google",
"input_price": 0,
"output_price": 0,
"cached_price": 0
},
{
"id": "anthropic/claude-opus-4-7",
"object": "model",
"owned_by": "anthropic",
"input_price": 0.000015,
"output_price": 0.000075,
"cached_price": 0.0000015
},
{
"id": "openai/gpt-5.2",
"object": "model",
"owned_by": "openai",
"input_price": 0.000002,
"output_price": 0.000008
}
]
73 changes: 73 additions & 0 deletions tests/test_scraper/test_requesty_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Requesty source: $0 models → is_free=True, paid → is_free=False."""

from __future__ import annotations

from pathlib import Path

import responses

from scripts.sources.requesty import REQUESTY_URL, RequestySource


def _set_key(monkeypatch):
monkeypatch.setenv("REQUESTY_API_KEY", "req-test")


@responses.activate
def test_free_models_emitted(fixtures_dir: Path, monkeypatch):
_set_key(monkeypatch)
body = (fixtures_dir / "requesty_models.json").read_text()
responses.add(responses.GET, REQUESTY_URL, body=body, status=200,
content_type="application/json")
evs = RequestySource().fetch()
by_id = {e.model_id: e for e in evs}
assert by_id["requesty/nvidia/nemotron-3-super-120b-a12b"].is_free is True
assert by_id["requesty/nvidia/nemotron-3-nano-30b-a3b"].is_free is True
assert by_id["requesty/google/gemma-4-31b-it"].is_free is True
assert by_id["requesty/anthropic/claude-opus-4-7"].is_free is False
assert by_id["requesty/openai/gpt-5.2"].is_free is False


@responses.activate
def test_paid_models_carry_per_token_pricing(fixtures_dir: Path, monkeypatch):
_set_key(monkeypatch)
body = (fixtures_dir / "requesty_models.json").read_text()
responses.add(responses.GET, REQUESTY_URL, body=body, status=200,
content_type="application/json")
by_id = {e.model_id: e for e in RequestySource().fetch()}
assert by_id["requesty/anthropic/claude-opus-4-7"].pricing == {
"input_cost_per_token": 0.000015,
"output_cost_per_token": 0.000075,
}
# Free models carry no pricing opinion.
assert by_id["requesty/nvidia/nemotron-3-super-120b-a12b"].pricing is None


def test_no_api_key_returns_empty(monkeypatch):
"""Without REQUESTY_API_KEY, the source yields nothing (no HTTP call)."""
monkeypatch.delenv("REQUESTY_API_KEY", raising=False)
assert RequestySource().fetch() == []


@responses.activate
def test_all_evidence_high_confidence(fixtures_dir: Path, monkeypatch):
_set_key(monkeypatch)
body = (fixtures_dir / "requesty_models.json").read_text()
responses.add(responses.GET, REQUESTY_URL, body=body, status=200)
evs = RequestySource().fetch()
assert evs
for e in evs:
assert e.confidence == "high"
assert e.source == "requesty"


@responses.activate
def test_auth_header_present(fixtures_dir: Path, monkeypatch):
"""Verify the source sends the Bearer auth header."""
_set_key(monkeypatch)
body = (fixtures_dir / "requesty_models.json").read_text()
rsp = responses.add(responses.GET, REQUESTY_URL, body=body, status=200)
RequestySource().fetch()
assert rsp.call_count == 1
auth = responses.calls[0].request.headers.get("Authorization", "")
assert auth.startswith("Bearer ")
Loading