Files
zzrouter/backend/app/routes/anthropic.py

109 lines
3.5 KiB
Python

"""Anthropic native endpoint (passthrough)."""
import time
from typing import Optional, List
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from ..models import ClientKey
from ..middleware.auth import get_current_client
from ..services.router import ModelRouter
from ..services.key_selector import key_selector
from ..services.logger import async_logger, LogEntry
from ..providers.litellm_wrapper import LiteLLMWrapper
router = APIRouter(prefix="/v1/anthropic", tags=["anthropic"])
class Message(BaseModel):
role: str
content: str
class AnthropicRequest(BaseModel):
model: str
messages: List[Message]
max_tokens: int = 1024
stream: bool = False
temperature: Optional[float] = None
@router.post("/messages")
async def anthropic_messages(
request: AnthropicRequest,
client: ClientKey = Depends(get_current_client)
):
"""Anthropic native messages endpoint."""
start_time = time.time()
# Get Anthropic provider
provider = await ModelRouter.get_provider("anthropic")
if not provider:
raise HTTPException(status_code=503, detail={"error": "Anthropic provider not configured"})
# Get next API key
provider_key = await key_selector.get_next_key(provider.id)
if not provider_key:
raise HTTPException(status_code=503, detail={"error": "No available Anthropic API key"})
messages = [{"role": m.role, "content": m.content} for m in request.messages]
kwargs = {"max_tokens": request.max_tokens}
if request.temperature is not None:
kwargs["temperature"] = request.temperature
try:
response, prompt_tokens, completion_tokens = await LiteLLMWrapper.chat_completion(
provider=provider,
api_key=provider_key.key,
model=request.model,
messages=messages,
stream=request.stream,
**kwargs
)
if request.stream:
return StreamingResponse(
LiteLLMWrapper.stream_response(
response, client.id, provider.id, request.model, start_time
),
media_type="text/event-stream"
)
else:
latency_ms = int((time.time() - start_time) * 1000)
await async_logger.log(LogEntry(
client_key_id=client.id,
provider_id=provider.id,
model=request.model,
prompt_tokens=prompt_tokens,
completion_tokens=completion_tokens,
latency_ms=latency_ms,
success=True
))
return response
except Exception as e:
latency_ms = int((time.time() - start_time) * 1000)
await async_logger.log(LogEntry(
client_key_id=client.id,
provider_id=provider.id,
model=request.model,
latency_ms=latency_ms,
success=False,
error_message=str(e)
))
raise HTTPException(status_code=500, detail={"error": str(e)})
@router.get("/models")
async def anthropic_models(client: ClientKey = Depends(get_current_client)):
"""List Anthropic models."""
return {
"object": "list",
"data": [
{"id": "claude-3-opus-20240229", "object": "model", "owned_by": "anthropic"},
{"id": "claude-3-sonnet-20240229", "object": "model", "owned_by": "anthropic"},
{"id": "claude-3-haiku-20240307", "object": "model", "owned_by": "anthropic"},
]
}