109 lines
3.5 KiB
Python
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"},
|
|
]
|
|
}
|