Update
This commit is contained in:
@@ -107,6 +107,7 @@ def test_model_is_forced(local_db, monkeypatch):
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
assert rt._client.calls[-1][1]["model"] == "deepseek-chat"
|
||||
@@ -143,6 +144,7 @@ def test_model_route_overrides_upstream(local_db, monkeypatch):
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
url, body = rt._client.calls[-1]
|
||||
@@ -169,7 +171,7 @@ def test_vision_rewrites_image_to_text(local_db, monkeypatch):
|
||||
],
|
||||
}
|
||||
]
|
||||
run_async(rt.handle_chat({"messages": msgs}, cfg, ("guest", "test"), "test"))
|
||||
run_async(rt.handle_chat({"messages": msgs}, cfg, ("guest", "test"), "test", "default"))
|
||||
sent = rt._client.calls[-1][1]["messages"][0]["content"]
|
||||
assert isinstance(sent, str) and "vision" in sent.lower()
|
||||
|
||||
@@ -185,6 +187,7 @@ def test_streaming_emits_sse(local_db, monkeypatch):
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -282,6 +285,7 @@ def test_embeddings_remaps_alias_to_configured_model(local_db, monkeypatch):
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
assert rt._client.calls[-1][1]["model"] == "qwen/qwen3-embedding-8b"
|
||||
@@ -302,6 +306,7 @@ def test_embeddings_success_records_ledger(local_db, monkeypatch):
|
||||
cfg,
|
||||
("guest", "ledger_probe"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
@@ -325,7 +330,7 @@ def test_embeddings_disabled_returns_503(local_db, monkeypatch):
|
||||
rt = svc.runtime()
|
||||
resp = run_async(
|
||||
rt.handle_embeddings(
|
||||
{"input": "hello"}, cfg, ("guest", "test"), "test"
|
||||
{"input": "hello"}, cfg, ("guest", "test"), "test", "default"
|
||||
)
|
||||
)
|
||||
assert resp.status_code == 503
|
||||
@@ -372,6 +377,7 @@ def test_images_remaps_alias_to_configured_model(local_db, monkeypatch):
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
assert rt._client.calls[-1][1]["model"] == "black-forest-labs/flux.2-pro"
|
||||
@@ -411,6 +417,7 @@ def test_image_route_overrides_upstream(local_db, monkeypatch):
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
url, body = rt._client.calls[-1]
|
||||
@@ -435,6 +442,7 @@ def test_images_success_records_ledger(local_db, monkeypatch):
|
||||
cfg,
|
||||
("guest", "img_ledger"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
@@ -459,7 +467,7 @@ def test_images_disabled_returns_503(local_db, monkeypatch):
|
||||
rt = svc.runtime()
|
||||
resp = run_async(
|
||||
rt.handle_images(
|
||||
{"prompt": "badge"}, cfg, ("guest", "test"), "test"
|
||||
{"prompt": "badge"}, cfg, ("guest", "test"), "test", "default"
|
||||
)
|
||||
)
|
||||
assert resp.status_code == 503
|
||||
@@ -489,3 +497,271 @@ def test_compute_cost_embed_branch():
|
||||
assert output_cost == 0.0
|
||||
assert abs(total - 0.01) < 1e-9
|
||||
assert abs(input_cost - 0.01) < 1e-9
|
||||
|
||||
|
||||
def test_app_reference_stored_in_ledger_for_chat(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClient_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
cfg["gateway_force_model"] = True
|
||||
cfg["gateway_model"] = "deepseek-chat"
|
||||
rt = svc.runtime()
|
||||
run_async(
|
||||
rt.handle_chat(
|
||||
{"messages": [{"role": "user", "content": "ref_test"}]},
|
||||
cfg,
|
||||
("guest", "ref_probe"),
|
||||
"test-ua",
|
||||
"my-custom-app",
|
||||
)
|
||||
)
|
||||
row = get_table("gateway_usage_ledger").find_one(owner_id="ref_probe")
|
||||
assert row is not None
|
||||
assert row["app_reference"] == "my-custom-app"
|
||||
assert row["backend"] == "chat"
|
||||
|
||||
|
||||
def test_app_reference_defaults_when_not_passed(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClient_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
cfg["gateway_force_model"] = True
|
||||
cfg["gateway_model"] = "deepseek-chat"
|
||||
rt = svc.runtime()
|
||||
run_async(
|
||||
rt.handle_chat(
|
||||
{"messages": [{"role": "user", "content": "default_test"}]},
|
||||
cfg,
|
||||
("guest", "default_probe"),
|
||||
"test-ua",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
row = get_table("gateway_usage_ledger").find_one(owner_id="default_probe")
|
||||
assert row is not None
|
||||
assert row["app_reference"] == "default"
|
||||
|
||||
|
||||
def test_app_reference_stored_in_ledger_for_embeddings(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeEmbedClient_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
cfg["gateway_embed_enabled"] = True
|
||||
cfg["gateway_embed_model"] = "qwen/qwen3-embedding-8b"
|
||||
rt = svc.runtime()
|
||||
run_async(
|
||||
rt.handle_embeddings(
|
||||
{"input": "hello"},
|
||||
cfg,
|
||||
("guest", "embed_ref"),
|
||||
"test-ua",
|
||||
"devplace-embed-test-v-1-0-0",
|
||||
)
|
||||
)
|
||||
row = get_table("gateway_usage_ledger").find_one(owner_id="embed_ref")
|
||||
assert row is not None
|
||||
assert row["app_reference"] == "devplace-embed-test-v-1-0-0"
|
||||
assert row["backend"] == "embed"
|
||||
|
||||
|
||||
def test_app_reference_stored_in_ledger_for_images(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeImageClient_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
cfg["gateway_image_enabled"] = True
|
||||
cfg["gateway_image_model"] = "black-forest-labs/flux.2-pro"
|
||||
rt = svc.runtime()
|
||||
run_async(
|
||||
rt.handle_images(
|
||||
{"prompt": "ref badge"},
|
||||
cfg,
|
||||
("guest", "img_ref"),
|
||||
"test-ua",
|
||||
"devplace-image-test-v-1-0-0",
|
||||
)
|
||||
)
|
||||
row = get_table("gateway_usage_ledger").find_one(owner_id="img_ref")
|
||||
assert row is not None
|
||||
assert row["app_reference"] == "devplace-image-test-v-1-0-0"
|
||||
assert row["backend"] == "image"
|
||||
|
||||
|
||||
def test_app_reference_column_exists(local_db):
|
||||
db = get_table("gateway_usage_ledger").db
|
||||
if "gateway_usage_ledger" not in db.tables:
|
||||
return
|
||||
rows = list(db.query("PRAGMA table_info('gateway_usage_ledger')"))
|
||||
column_names = [r["name"] for r in rows]
|
||||
assert "app_reference" in column_names
|
||||
|
||||
|
||||
def test_chat_unknown_model_falls_back_to_default(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClient_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
cfg["gateway_force_model"] = False
|
||||
cfg["gateway_model"] = "deepseek-v4-flash"
|
||||
rt = svc.runtime()
|
||||
run_async(
|
||||
rt.handle_chat(
|
||||
{"model": "nonexistent-model-v99", "messages": [{"role": "user", "content": "hi"}]},
|
||||
cfg,
|
||||
("guest", "fallback_test_chat"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
assert rt._client.calls[-1][1]["model"] == "deepseek-v4-flash"
|
||||
|
||||
|
||||
def test_embeddings_unknown_model_falls_back_to_default(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeEmbedClient_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
cfg["gateway_force_model"] = False
|
||||
cfg["gateway_embed_enabled"] = True
|
||||
cfg["gateway_embed_model"] = "qwen/qwen3-embedding-8b"
|
||||
rt = svc.runtime()
|
||||
run_async(
|
||||
rt.handle_embeddings(
|
||||
{"model": "nonexistent-embed-model", "input": "hello"},
|
||||
cfg,
|
||||
("guest", "fallback_test_embed"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
assert rt._client.calls[-1][1]["model"] == "qwen/qwen3-embedding-8b"
|
||||
|
||||
|
||||
def test_images_unknown_model_falls_back_to_default(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeImageClient_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
cfg["gateway_force_model"] = False
|
||||
cfg["gateway_image_enabled"] = True
|
||||
cfg["gateway_image_model"] = "black-forest-labs/flux.2-pro"
|
||||
rt = svc.runtime()
|
||||
run_async(
|
||||
rt.handle_images(
|
||||
{"model": "nonexistent-image-model", "prompt": "test badge"},
|
||||
cfg,
|
||||
("guest", "fallback_test_img"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
assert rt._client.calls[-1][1]["model"] == "black-forest-labs/flux.2-pro"
|
||||
|
||||
|
||||
class FakeClientWithUsage_openai_gateway(FakeClient_openai_gateway):
|
||||
async def send(self, request):
|
||||
self.calls.append((request.url, request.json_body))
|
||||
body = request.json_body or {}
|
||||
return FakeResp_openai_gateway(
|
||||
payload={
|
||||
"id": "x",
|
||||
"model": body.get("model"),
|
||||
"choices": [{"message": {"content": "hi there"}}],
|
||||
"usage": {"prompt_tokens": 7, "completion_tokens": 3, "total_tokens": 10},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_stream_options_stripped_from_upstream(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClient_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
rt = svc.runtime()
|
||||
run_async(
|
||||
rt.handle_chat(
|
||||
{
|
||||
"messages": [{"role": "user", "content": "hi"}],
|
||||
"stream": True,
|
||||
"stream_options": {"include_usage": True},
|
||||
},
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
upstream_payload = rt._client.calls[-1][1]
|
||||
assert "stream_options" not in upstream_payload
|
||||
assert upstream_payload["stream"] is False
|
||||
|
||||
|
||||
def test_include_usage_emits_usage_chunk(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClientWithUsage_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
rt = svc.runtime()
|
||||
resp = run_async(
|
||||
rt.handle_chat(
|
||||
{
|
||||
"messages": [{"role": "user", "content": "hi"}],
|
||||
"stream": True,
|
||||
"stream_options": {"include_usage": True},
|
||||
},
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
|
||||
async def drain():
|
||||
out = []
|
||||
async for chunk in resp.body_iterator:
|
||||
out.append(chunk if isinstance(chunk, str) else chunk.decode())
|
||||
return "".join(out)
|
||||
|
||||
body = run_async(drain())
|
||||
assert '"usage"' in body
|
||||
assert '"total_tokens": 10' in body
|
||||
assert "[DONE]" in body
|
||||
|
||||
|
||||
def test_no_usage_chunk_without_include_usage(local_db, monkeypatch):
|
||||
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClientWithUsage_openai_gateway)
|
||||
svc = GatewayService()
|
||||
cfg = svc.effective_config()
|
||||
rt = svc.runtime()
|
||||
resp = run_async(
|
||||
rt.handle_chat(
|
||||
{"messages": [{"role": "user", "content": "hi"}], "stream": True},
|
||||
cfg,
|
||||
("guest", "test"),
|
||||
"test",
|
||||
"default",
|
||||
)
|
||||
)
|
||||
|
||||
async def drain():
|
||||
out = []
|
||||
async for chunk in resp.body_iterator:
|
||||
out.append(chunk if isinstance(chunk, str) else chunk.decode())
|
||||
return "".join(out)
|
||||
|
||||
body = run_async(drain())
|
||||
assert '"usage"' not in body
|
||||
assert "[DONE]" in body
|
||||
|
||||
|
||||
def test_models_endpoint_publishes_molodetz(local_db):
|
||||
from devplacepy.services.openai_gateway import routing
|
||||
|
||||
routing.seed_default_deepseek_routes()
|
||||
try:
|
||||
svc = GatewayService()
|
||||
resp = svc._models_response()
|
||||
payload = json.loads(resp.body.decode())
|
||||
ids = {m["id"] for m in payload["data"]}
|
||||
assert payload["object"] == "list"
|
||||
assert {"molodetz", "molodetz-pro"} <= ids
|
||||
for model in payload["data"]:
|
||||
assert model["object"] == "model"
|
||||
assert model["owned_by"] == "molodetz"
|
||||
finally:
|
||||
routing.model_store.remove("molodetz")
|
||||
routing.model_store.remove("molodetz-pro")
|
||||
|
||||
@@ -280,3 +280,19 @@ def test_seed_default_deepseek_routes_is_idempotent_and_preserves_customization(
|
||||
context_window=1_048_576,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_seed_publishes_molodetz_aliases(local_db):
|
||||
r.seed_default_deepseek_routes()
|
||||
try:
|
||||
molodetz = r.model_store.resolve("molodetz", "chat")
|
||||
molodetz_pro = r.model_store.resolve("molodetz-pro", "chat")
|
||||
assert molodetz is not None
|
||||
assert molodetz.target_model == "deepseek-v4-flash"
|
||||
assert molodetz_pro is not None
|
||||
assert molodetz_pro.target_model == "deepseek-v4-pro"
|
||||
sources = {row["source_model"] for row in r.model_store.list()}
|
||||
assert {"molodetz", "molodetz-pro"} <= sources
|
||||
finally:
|
||||
r.model_store.remove("molodetz")
|
||||
r.model_store.remove("molodetz-pro")
|
||||
|
||||
@@ -219,3 +219,32 @@ def test_compute_cost_native_upstream_cost_unaffected_by_tiering():
|
||||
total, _, _, native = compute_cost({"cost": 0.05}, norm, tiered, "chat")
|
||||
assert native is True
|
||||
assert total == 0.05
|
||||
|
||||
|
||||
def test_validate_app_reference_accepts_valid_slugs():
|
||||
from devplacepy.services.openai_gateway.service import _validate_app_reference
|
||||
|
||||
assert _validate_app_reference("devplace-devii-v-1-0-0") == "devplace-devii-v-1-0-0"
|
||||
assert _validate_app_reference("my-app") == "my-app"
|
||||
assert _validate_app_reference("a") == "a"
|
||||
assert _validate_app_reference("abcdefghijklmnopqrstuvwxyz0123") == "abcdefghijklmnopqrstuvwxyz0123"
|
||||
assert _validate_app_reference("test_app.release-2") == "test_app.release-2"
|
||||
|
||||
|
||||
def test_validate_app_reference_rejects_invalid():
|
||||
from devplacepy.services.openai_gateway.service import _validate_app_reference
|
||||
|
||||
assert _validate_app_reference("") == "default"
|
||||
assert _validate_app_reference(" ") == "default"
|
||||
assert _validate_app_reference("hello world") == "default"
|
||||
assert _validate_app_reference("name@domain") == "default"
|
||||
assert _validate_app_reference("name/domain") == "default"
|
||||
assert _validate_app_reference(None) == "default"
|
||||
assert _validate_app_reference("abcdefghijklmnopqrstuvwxyz01234") == "default"
|
||||
|
||||
|
||||
def test_validate_app_reference_strips_whitespace():
|
||||
from devplacepy.services.openai_gateway.service import _validate_app_reference
|
||||
|
||||
assert _validate_app_reference(" my-app ") == "my-app"
|
||||
assert _validate_app_reference("\tdevplace\t") == "devplace"
|
||||
|
||||
Reference in New Issue
Block a user