mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
feat: identify generic upstream host in provider field and add masked provider_url
This commit is contained in:
@@ -63,6 +63,7 @@ from .cache_breakpoints import (
|
|||||||
)
|
)
|
||||||
from .count_tokens import MissingUsageEstimator, count_tokens_locally
|
from .count_tokens import MissingUsageEstimator, count_tokens_locally
|
||||||
from .litellm_routing import detect_litellm_prefix
|
from .litellm_routing import detect_litellm_prefix
|
||||||
|
from .model_paths import public_provider_url
|
||||||
from .rate_limit import UPSTREAM_RATE_LIMIT, classify_rate_limit
|
from .rate_limit import UPSTREAM_RATE_LIMIT, classify_rate_limit
|
||||||
from .reasoning_effort import apply_reasoning_effort
|
from .reasoning_effort import apply_reasoning_effort
|
||||||
|
|
||||||
@@ -476,9 +477,13 @@ class BaseUpstreamProvider:
|
|||||||
Idempotent: re-stamping an already-stamped payload must not nest the
|
Idempotent: re-stamping an already-stamped payload must not nest the
|
||||||
prefix repeatedly (e.g. never ``"anthropic:anthropic"``). This matters
|
prefix repeatedly (e.g. never ``"anthropic:anthropic"``). This matters
|
||||||
because streaming paths can apply the field more than once per chunk.
|
because streaming paths can apply the field more than once per chunk.
|
||||||
|
|
||||||
|
Also stamps ``provider_url`` with the upstream base URL that served
|
||||||
|
the request.
|
||||||
"""
|
"""
|
||||||
if not isinstance(response_json, dict):
|
if not isinstance(response_json, dict):
|
||||||
return
|
return
|
||||||
|
response_json["provider_url"] = public_provider_url(self.base_url)
|
||||||
provider_type = (self.provider_type or "").strip()
|
provider_type = (self.provider_type or "").strip()
|
||||||
existing = response_json.get("provider")
|
existing = response_json.get("provider")
|
||||||
existing_str = existing.strip() if isinstance(existing, str) else ""
|
existing_str = existing.strip() if isinstance(existing, str) else ""
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from .base import BaseUpstreamProvider
|
from .base import BaseUpstreamProvider
|
||||||
|
from .model_paths import public_provider_url
|
||||||
from .pricing_resolver import (
|
from .pricing_resolver import (
|
||||||
FallbackPricingResolver,
|
FallbackPricingResolver,
|
||||||
ResolvedPricing,
|
ResolvedPricing,
|
||||||
@@ -50,6 +52,22 @@ class GenericUpstreamProvider(BaseUpstreamProvider):
|
|||||||
provider_fee=provider_fee,
|
provider_fee=provider_fee,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _apply_provider_field(self, response_json: object) -> None:
|
||||||
|
"""Stamp ``"generic:<upstream host>"`` unless the upstream named itself.
|
||||||
|
|
||||||
|
A generic upstream is not a router, so nothing identifies the serving
|
||||||
|
endpoint in the payload; the base URL host fills that role.
|
||||||
|
"""
|
||||||
|
if not isinstance(response_json, dict):
|
||||||
|
return
|
||||||
|
existing = response_json.get("provider")
|
||||||
|
if not (isinstance(existing, str) and existing.strip()):
|
||||||
|
response_json["provider"] = (
|
||||||
|
urlparse(public_provider_url(self.base_url)).hostname
|
||||||
|
or self.upstream_name
|
||||||
|
)
|
||||||
|
super()._apply_provider_field(response_json)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _build_from_row(
|
def _build_from_row(
|
||||||
cls, provider_row: "UpstreamProviderRow"
|
cls, provider_row: "UpstreamProviderRow"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import httpx
|
|||||||
|
|
||||||
from ..payment.models import Model, async_fetch_openrouter_models
|
from ..payment.models import Model, async_fetch_openrouter_models
|
||||||
from .base import BaseUpstreamProvider
|
from .base import BaseUpstreamProvider
|
||||||
|
from .model_paths import public_provider_url
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..core.db import UpstreamProviderRow
|
from ..core.db import UpstreamProviderRow
|
||||||
@@ -32,6 +33,7 @@ class OpenRouterUpstreamProvider(BaseUpstreamProvider):
|
|||||||
"""
|
"""
|
||||||
if not isinstance(response_json, dict):
|
if not isinstance(response_json, dict):
|
||||||
return
|
return
|
||||||
|
response_json["provider_url"] = public_provider_url(self.base_url)
|
||||||
provider_type = (self.provider_type or "").strip()
|
provider_type = (self.provider_type or "").strip()
|
||||||
existing = response_json.get("provider")
|
existing = response_json.get("provider")
|
||||||
sub = existing.strip() if isinstance(existing, str) else ""
|
sub = existing.strip() if isinstance(existing, str) else ""
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from routstr.upstream.anthropic import AnthropicUpstreamProvider
|
from routstr.upstream.anthropic import AnthropicUpstreamProvider
|
||||||
from routstr.upstream.base import BaseUpstreamProvider
|
from routstr.upstream.base import BaseUpstreamProvider
|
||||||
|
from routstr.upstream.generic import GenericUpstreamProvider
|
||||||
from routstr.upstream.openrouter import OpenRouterUpstreamProvider
|
from routstr.upstream.openrouter import OpenRouterUpstreamProvider
|
||||||
|
|
||||||
|
|
||||||
@@ -127,3 +128,53 @@ def test_inject_cost_metadata_sets_provider() -> None:
|
|||||||
p.inject_cost_metadata(response_json, cost_data, key)
|
p.inject_cost_metadata(response_json, cost_data, key)
|
||||||
|
|
||||||
assert response_json["provider"] == "openrouter:Anthropic"
|
assert response_json["provider"] == "openrouter:Anthropic"
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_provider_field_generic_uses_upstream_host() -> None:
|
||||||
|
"""A generic upstream has no router-reported provider; the serving host
|
||||||
|
identifies it, mirroring ``openrouter:<sub-provider>``."""
|
||||||
|
p = GenericUpstreamProvider(base_url="https://api.deepseek.com/v1", api_key="k")
|
||||||
|
data: dict = {"id": "chatcmpl-1", "model": "deepseek-chat"}
|
||||||
|
p._apply_provider_field(data)
|
||||||
|
assert data["provider"] == "generic:api.deepseek.com"
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_provider_field_generic_keeps_upstream_reported_provider() -> None:
|
||||||
|
p = GenericUpstreamProvider(base_url="https://api.deepseek.com/v1", api_key="k")
|
||||||
|
data: dict = {"provider": "Fireworks"}
|
||||||
|
p._apply_provider_field(data)
|
||||||
|
assert data["provider"] == "generic:Fireworks"
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_provider_field_generic_idempotent() -> None:
|
||||||
|
p = GenericUpstreamProvider(base_url="https://api.deepseek.com/v1", api_key="k")
|
||||||
|
data: dict = {}
|
||||||
|
p._apply_provider_field(data)
|
||||||
|
p._apply_provider_field(data)
|
||||||
|
assert data["provider"] == "generic:api.deepseek.com"
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_provider_field_sets_provider_url() -> None:
|
||||||
|
"""Every provider exposes the upstream base URL it served from."""
|
||||||
|
generic = GenericUpstreamProvider(
|
||||||
|
base_url="https://api.deepseek.com/v1", api_key="k"
|
||||||
|
)
|
||||||
|
data: dict = {}
|
||||||
|
generic._apply_provider_field(data)
|
||||||
|
assert data["provider_url"] == "https://api.deepseek.com/v1"
|
||||||
|
|
||||||
|
openrouter = _make_provider(OpenRouterUpstreamProvider, "openrouter")
|
||||||
|
data = {"provider": "Anthropic"}
|
||||||
|
openrouter._apply_provider_field(data)
|
||||||
|
assert data["provider_url"] == "https://openrouter.ai/api/v1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_provider_field_masks_private_upstream() -> None:
|
||||||
|
"""Private or port-bearing upstream URLs are masked the same way model
|
||||||
|
paths mask them, so neither ``provider`` nor ``provider_url`` leaks a
|
||||||
|
local address."""
|
||||||
|
p = GenericUpstreamProvider(base_url="http://10.0.0.5:11434/v1", api_key="k")
|
||||||
|
data: dict = {}
|
||||||
|
p._apply_provider_field(data)
|
||||||
|
assert data["provider"] == "generic:localhost"
|
||||||
|
assert data["provider_url"] == "http://localhost"
|
||||||
|
|||||||
Reference in New Issue
Block a user