mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
fix: certify and model-test send model.id like the proxy, not the client alias
This commit is contained in:
@@ -1733,8 +1733,9 @@ async def certify_upstream_provider(
|
||||
provider.provider_fee,
|
||||
sats_to_usd,
|
||||
)
|
||||
# The timeout applies per upstream call. The run makes up to five
|
||||
# calls, so the request can stay open for up to five times it.
|
||||
# The timeout applies per upstream call. The run makes up to six
|
||||
# calls (models, two short probes after a max_completion_tokens retry,
|
||||
# three cache probes), so the request can stay open for six times it.
|
||||
live_rows = await run_live_checks(
|
||||
provider.base_url,
|
||||
provider.api_key,
|
||||
|
||||
@@ -689,7 +689,8 @@ def _model_test_target(
|
||||
upstream.build_request_url(path, model_obj),
|
||||
upstream.prepare_headers({"content-type": "application/json"}),
|
||||
dict(upstream.prepare_params(path, None)),
|
||||
upstream.transform_model_name(model_id),
|
||||
# The proxy forwards ``model.id``, not the row's client alias.
|
||||
upstream.transform_model_name(model_obj.id),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -247,17 +247,14 @@ def shape_body(
|
||||
) -> Any:
|
||||
"""The JSON body the proxy would forward, model-name transforms included.
|
||||
|
||||
``prepare_request_body`` rewrites ``model`` from ``model.id``; the probe
|
||||
keeps the id it chose (``forwarded_model_id`` first) and only applies the
|
||||
provider's own name transform to it.
|
||||
``prepare_request_body`` sets ``model`` from ``model.id``, exactly as
|
||||
``forward_request`` does, so an alias row's ``forwarded_model_id`` never
|
||||
reaches the upstream here either.
|
||||
"""
|
||||
if upstream is None or model is None:
|
||||
return body
|
||||
shaped = upstream.prepare_request_body(json.dumps(body).encode(), model)
|
||||
data = json.loads(shaped) if shaped else dict(body)
|
||||
if isinstance(data, dict) and isinstance(body.get("model"), str):
|
||||
data["model"] = upstream.transform_model_name(body["model"])
|
||||
return data
|
||||
return json.loads(shaped) if shaped else body
|
||||
|
||||
|
||||
async def probe_upstream(
|
||||
@@ -876,7 +873,7 @@ async def run_live_checks(
|
||||
probe = await probe_upstream(
|
||||
base_url,
|
||||
api_key,
|
||||
model.forwarded_model_id or model.id,
|
||||
model.id,
|
||||
endpoint_tag=endpoint_tag,
|
||||
client=client,
|
||||
timeout=timeout,
|
||||
|
||||
@@ -604,7 +604,7 @@ async def run_cache_checks(
|
||||
probe = await probe_cache(
|
||||
base_url,
|
||||
api_key,
|
||||
model.forwarded_model_id or model.id,
|
||||
model.id,
|
||||
endpoint_tag=endpoint_tag,
|
||||
client=client,
|
||||
timeout=timeout,
|
||||
|
||||
@@ -106,7 +106,9 @@ async def test_certify_forwarded_alias_listed_path_succeeds(
|
||||
assert response.status_code == 200, response.text
|
||||
assert chat.call_count == 1
|
||||
body: dict[str, Any] = json.loads(chat.calls[0].request.content)
|
||||
assert body["model"] == forwarded
|
||||
# The path is keyed by the exposed id, but the upstream gets what the
|
||||
# proxy sends for this row: transform_model_name(model.id).
|
||||
assert body["model"] == "local-alias"
|
||||
assert body["provider"] == {"order": ["endpoint"], "allow_fallbacks": False}
|
||||
mismatch = await integration_client.post(
|
||||
f"/admin/api/upstream-providers/{provider.id}/certify",
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
"""Certification must send the upstream the model id the proxy sends.
|
||||
|
||||
An admin alias row has ``id`` and ``forwarded_model_id`` that differ. The
|
||||
proxy forwards ``transform_model_name(model.id)`` (``prepare_request_body``),
|
||||
so a probe that sends the forwarded id certifies a request no client can make.
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import AsyncClient, Response
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from routstr.core.db import ApiKey
|
||||
from routstr.proxy import reinitialize_upstreams
|
||||
|
||||
from .test_certify_endpoint import _admin_headers, _make_provider, _model_row
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_certify_and_proxy_send_the_same_model(
|
||||
integration_session: AsyncSession,
|
||||
integration_client: AsyncClient,
|
||||
) -> None:
|
||||
base_url = "https://certify-upstream.example/v1"
|
||||
respx.get(f"{base_url}/models").mock(return_value=Response(200, json={"data": []}))
|
||||
chat = respx.post(f"{base_url}/chat/completions").mock(
|
||||
return_value=Response(
|
||||
200,
|
||||
json={
|
||||
"id": "x",
|
||||
"object": "chat.completion",
|
||||
"model": "m",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": "ok"},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": {"prompt_tokens": 5, "completion_tokens": 1},
|
||||
},
|
||||
)
|
||||
)
|
||||
provider = await _make_provider(integration_session)
|
||||
model = _model_row(provider.id, model_id="row-id") # type: ignore[arg-type]
|
||||
model.forwarded_model_id = "client-alias"
|
||||
integration_session.add(model)
|
||||
integration_session.add(
|
||||
ApiKey(
|
||||
hashed_key="certify-contract", balance=10**9, created_at=int(time.time())
|
||||
)
|
||||
)
|
||||
await integration_session.commit()
|
||||
|
||||
with (
|
||||
patch("routstr.payment.models.sats_usd_price", return_value=0.0005),
|
||||
patch("routstr.payment.cost_calculation.sats_usd_price", return_value=0.0005),
|
||||
patch("routstr.payment.price.SATS_USD_PRICE", 0.0005),
|
||||
):
|
||||
await reinitialize_upstreams()
|
||||
proxied = await integration_client.post(
|
||||
"/v1/chat/completions",
|
||||
headers={"Authorization": "Bearer sk-certify-contract"},
|
||||
json={
|
||||
"model": "client-alias",
|
||||
"messages": [{"role": "user", "content": "hi"}],
|
||||
"max_tokens": 1,
|
||||
},
|
||||
)
|
||||
assert proxied.status_code == 200, proxied.text
|
||||
certified = await integration_client.post(
|
||||
f"/admin/api/upstream-providers/{provider.id}/certify",
|
||||
headers=_admin_headers(),
|
||||
json={"model_id": "row-id", "check_cache": False},
|
||||
)
|
||||
assert certified.status_code == 200, certified.text
|
||||
|
||||
bodies: list[dict[str, Any]] = [
|
||||
json.loads(call.request.content) for call in chat.calls
|
||||
]
|
||||
assert len(bodies) == 2
|
||||
proxy_model, certify_model = bodies[0]["model"], bodies[1]["model"]
|
||||
assert certify_model == proxy_model
|
||||
Reference in New Issue
Block a user