feat: implement AI model API proxy service
This commit is contained in:
108
backend/app/routes/anthropic.py
Normal file
108
backend/app/routes/anthropic.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""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"},
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user