93 lines
2.8 KiB
Python
93 lines
2.8 KiB
Python
"""Model discovery for assistants exposed through the public API."""
|
|||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from db.models import Assistant
|
||
|
|
from db.session import SessionLocal
|
||
|
|
from fastapi import APIRouter, Depends, Request
|
||
|
|
from services.openai_api import (
|
||
|
|
PUBLIC_API_BEARER,
|
||
|
|
PUBLIC_ERROR_RESPONSES,
|
||
|
|
REQUEST_ID_RESPONSE_HEADERS,
|
||
|
|
openai_error_response,
|
||
|
|
)
|
||
|
|
from services.openai_realtime.auth import (
|
||
|
|
RealtimeAuthError,
|
||
|
|
authenticate_bearer,
|
||
|
|
bearer_from_authorization,
|
||
|
|
)
|
||
|
|
from services.openai_responses.schemas import ModelList
|
||
|
|
from sqlalchemy import select
|
||
|
|
|
||
|
|
|
||
|
|
PUBLIC_ASSISTANT_TYPES = {"prompt", "workflow", "dify", "fastgpt"}
|
||
|
|
|
||
|
|
router = APIRouter(
|
||
|
|
prefix="/v1/models",
|
||
|
|
tags=["openai-models"],
|
||
|
|
dependencies=[Depends(PUBLIC_API_BEARER)],
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
async def _authenticate(request: Request) -> None:
|
||
|
|
token = bearer_from_authorization(request.headers.get("authorization"))
|
||
|
|
async with SessionLocal() as db:
|
||
|
|
credential = await authenticate_bearer(db, token)
|
||
|
|
if credential.ephemeral:
|
||
|
|
raise RealtimeAuthError("A long-lived API key is required")
|
||
|
|
|
||
|
|
|
||
|
|
def _capabilities(assistant: Assistant) -> list[str]:
|
||
|
|
capabilities = ["realtime"]
|
||
|
|
if assistant.runtime_mode == "pipeline":
|
||
|
|
capabilities.insert(0, "responses")
|
||
|
|
return capabilities
|
||
|
|
|
||
|
|
|
||
|
|
@router.get(
|
||
|
|
"",
|
||
|
|
response_model=ModelList,
|
||
|
|
responses={
|
||
|
|
200: {"headers": REQUEST_ID_RESPONSE_HEADERS},
|
||
|
|
**PUBLIC_ERROR_RESPONSES,
|
||
|
|
},
|
||
|
|
summary="List assistants available through the public API",
|
||
|
|
)
|
||
|
|
async def list_models(request: Request):
|
||
|
|
try:
|
||
|
|
await _authenticate(request)
|
||
|
|
async with SessionLocal() as db:
|
||
|
|
assistants = (
|
||
|
|
await db.execute(
|
||
|
|
select(Assistant)
|
||
|
|
.where(
|
||
|
|
Assistant.type.in_(PUBLIC_ASSISTANT_TYPES),
|
||
|
|
Assistant.runtime_mode.in_(("pipeline", "realtime")),
|
||
|
|
)
|
||
|
|
.order_by(Assistant.created_at.asc(), Assistant.id.asc())
|
||
|
|
)
|
||
|
|
).scalars().all()
|
||
|
|
return {
|
||
|
|
"object": "list",
|
||
|
|
"data": [
|
||
|
|
{
|
||
|
|
"id": f"assistant:{assistant.id}",
|
||
|
|
"object": "model",
|
||
|
|
"created": int(assistant.created_at.timestamp()),
|
||
|
|
"owned_by": "interactive-media",
|
||
|
|
"name": assistant.name,
|
||
|
|
"capabilities": _capabilities(assistant),
|
||
|
|
"assistant_id": assistant.id,
|
||
|
|
"assistant_type": assistant.type,
|
||
|
|
"runtime_mode": assistant.runtime_mode,
|
||
|
|
}
|
||
|
|
for assistant in assistants
|
||
|
|
],
|
||
|
|
}
|
||
|
|
except RealtimeAuthError as exc:
|
||
|
|
return openai_error_response(
|
||
|
|
str(exc),
|
||
|
|
code="invalid_api_key",
|
||
|
|
status_code=401,
|
||
|
|
)
|