agy تنها ارائه‌دهندهٔ مدل؛ حذف بخش ارائه‌دهنده‌ها و افزودن زنجیرهٔ انتخاب مدل

- حذف کامل providerهای mock و openai_compatible: دامنه، آداپوترها، رجیستری، کانفیگ، seed و رابطٔ مدیریت
- بخش «ارائه‌دهنده‌ها» از کنسول حذف شد؛ ورود اکانت‌ها فقط از صفحهٔ اکانت‌های agy
- صفحهٔ مدل‌ها بازطراحی شد: ارائه‌دهنده → اکانت → مدل (کاتالوگ لحظه‌ای و زندهٔ همان اکانت از Google) → افورت استدلال؛ بدون هیچ ورود دستی نام مدل
- مدل‌ها ستون reasoning_effort گرفتند؛ آداپتور آن را به thinkingConfig (بودجهٔ ۱۰۲۴ تا ۳۲۷۶۸) ترجمه می‌کند و فقط به مدل‌های استدلالی می‌فرستد
- مهاجرت c2d3e4f5b6a7: افزودن reasoning_effort و حذف ردیف‌های providerهای قدیمی
- تست‌ها: ۲۴۶ تست بک‌اند و ۵۱ تست فرانت سبز؛ ruff و mypy و tsc و eslint تمیز
This commit is contained in:
mjalmoudy94
2026-10-13 10:01:11 +03:30
parent a2284db4f4
commit da5a2a5d1a
29 changed files with 906 additions and 995 deletions
+42 -94
View File
@@ -3,7 +3,9 @@
Two properties matter more than any feature here:
1. A normal customer can never reach these endpoints.
2. A provider API key can never come back out of the API.
2. Model identity and account material stay inside the console — a customer
never sees a raw model, and nothing that could act as a credential (a Google
refresh token, an API key) ever comes back out.
"""
from __future__ import annotations
@@ -12,146 +14,92 @@ from httpx import AsyncClient
from tests.conftest import auth_header, signup
SECRET_KEY = "sk-super-secret-value-do-not-leak-1234567890"
async def test_normal_user_cannot_list_providers(client: AsyncClient):
async def test_normal_user_cannot_list_models(client: AsyncClient):
token = (await signup(client, "peon@example.com"))["access_token"]
response = await client.get("/api/admin/providers", headers=auth_header(token))
response = await client.get("/api/admin/models", headers=auth_header(token))
assert response.status_code == 403
assert response.json()["error"]["code"] == "permission_denied"
async def test_normal_user_cannot_create_provider(client: AsyncClient):
async def test_normal_user_cannot_create_model(client: AsyncClient):
token = (await signup(client, "peon2@example.com"))["access_token"]
response = await client.post(
"/api/admin/providers",
json={"name": "evil", "type": "mock", "base_url": ""},
"/api/admin/models",
json={"internal_name": "evil-model"},
headers=auth_header(token),
)
assert response.status_code == 403
async def test_admin_endpoints_require_authentication(client: AsyncClient):
response = await client.get("/api/admin/providers")
response = await client.get("/api/admin/models")
assert response.status_code == 401
async def test_platform_owner_can_create_and_list_provider(
async def test_model_creation_resolves_the_agy_provider_itself(
client: AsyncClient, platform_owner_token: str
):
"""The provider is not a client choice: agy is the only one."""
created = await client.post(
"/api/admin/providers",
json={"name": "openrouter", "type": "openrouter", "base_url": "https://openrouter.ai/api/v1"},
"/api/admin/models",
json={
"internal_name": "gemini-3-pro-high",
"capabilities": ["text", "reasoning"],
"input_price_per_1m": 0.3,
"output_price_per_1m": 1.2,
"reasoning_effort": "high",
},
headers=auth_header(platform_owner_token),
)
assert created.status_code == 201, created.text
assert created.json()["name"] == "openrouter"
body = created.json()
assert body["internal_name"] == "gemini-3-pro-high"
# The display name falls back to the model id; nothing is typed twice.
assert body["display_name_for_admin"] == "gemini-3-pro-high"
assert body["reasoning_effort"] == "high"
assert body["provider_id"], "the agy provider must be resolved server-side"
listing = await client.get("/api/admin/providers", headers=auth_header(platform_owner_token))
listing = await client.get("/api/admin/models", headers=auth_header(platform_owner_token))
assert listing.status_code == 200
assert [p["name"] for p in listing.json()] == ["openrouter"]
assert [m["internal_name"] for m in listing.json()] == ["gemini-3-pro-high"]
async def test_duplicate_provider_name_is_rejected(client: AsyncClient, platform_owner_token: str):
payload = {"name": "dupe", "type": "mock", "base_url": ""}
async def test_duplicate_model_name_is_rejected(client: AsyncClient, platform_owner_token: str):
payload = {"internal_name": "claude-sonnet-4-5-thinking", "reasoning_effort": "high"}
first = await client.post(
"/api/admin/providers", json=payload, headers=auth_header(platform_owner_token)
"/api/admin/models", json=payload, headers=auth_header(platform_owner_token)
)
assert first.status_code == 201
second = await client.post(
"/api/admin/providers", json=payload, headers=auth_header(platform_owner_token)
"/api/admin/models", json=payload, headers=auth_header(platform_owner_token)
)
assert second.status_code == 409
async def test_credential_creation_returns_only_a_masked_key(
async def test_unknown_reasoning_effort_is_rejected(
client: AsyncClient, platform_owner_token: str
):
provider = (
await client.post(
"/api/admin/providers",
json={"name": "p1", "type": "openai_compatible", "base_url": "https://x.test/v1"},
headers=auth_header(platform_owner_token),
)
).json()
response = await client.post(
f"/api/admin/providers/{provider['id']}/credentials",
json={"name": "primary", "api_key": SECRET_KEY},
"/api/admin/models",
json={"internal_name": "gemini-3-flash", "reasoning_effort": "ultra"},
headers=auth_header(platform_owner_token),
)
assert response.status_code == 201, response.text
body = response.json()
assert SECRET_KEY not in str(body)
assert body["masked_key"].startswith("•")
assert body["masked_key"].endswith(SECRET_KEY[-4:])
assert response.status_code == 422
async def test_listing_credentials_never_exposes_the_key(
async def test_agy_account_and_model_endpoints_are_owner_only(
client: AsyncClient, platform_owner_token: str
):
provider = (
await client.post(
"/api/admin/providers",
json={"name": "p2", "type": "openai_compatible", "base_url": "https://y.test/v1"},
headers=auth_header(platform_owner_token),
)
).json()
await client.post(
f"/api/admin/providers/{provider['id']}/credentials",
json={"name": "primary", "api_key": SECRET_KEY},
headers=auth_header(platform_owner_token),
)
"""The account catalog is owner-only and never carries token material."""
token = (await signup(client, "peon3@example.com"))["access_token"]
response = await client.get(
f"/api/admin/providers/{provider['id']}/credentials",
headers=auth_header(platform_owner_token),
"/api/admin/agy/accounts/whatever/models", headers=auth_header(token)
)
assert response.status_code == 200
assert SECRET_KEY not in response.text
assert response.json()[0]["masked_key"] == "••••••"
assert response.status_code == 403
async def test_model_creation_and_listing(client: AsyncClient, platform_owner_token: str):
provider = (
await client.post(
"/api/admin/providers",
json={"name": "p3", "type": "mock", "base_url": ""},
headers=auth_header(platform_owner_token),
)
).json()
created = await client.post(
"/api/admin/models",
json={
"provider_id": provider["id"],
"internal_name": "internal-coder-v1",
"display_name_for_admin": "Coder V1",
"capabilities": ["text", "code"],
"input_price_per_1m": 0.3,
"output_price_per_1m": 1.2,
},
headers=auth_header(platform_owner_token),
)
assert created.status_code == 201, created.text
listing = await client.get("/api/admin/models", headers=auth_header(platform_owner_token))
assert listing.status_code == 200
assert listing.json()[0]["internal_name"] == "internal-coder-v1"
async def test_model_requires_existing_provider(client: AsyncClient, platform_owner_token: str):
response = await client.post(
"/api/admin/models",
json={
"provider_id": "does-not-exist",
"internal_name": "x",
"display_name_for_admin": "X",
},
headers=auth_header(platform_owner_token),
)
assert response.status_code == 404
unauthenticated = await client.get("/api/admin/agy/accounts/whatever/models")
assert unauthenticated.status_code == 401
async def test_routing_policy_creation_and_default_swap(