86 lines
3.6 KiB
Python
86 lines
3.6 KiB
Python
import json
|
|
import httpx
|
|
from fastapi import APIRouter, Request
|
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
|
|
from config import LLAMA_SWAP_URL
|
|
from services.router_logic import FAST, FAST_NO_THINK, choose_model
|
|
from services.token_stats import increment_tokens
|
|
|
|
router = APIRouter(prefix="/v1")
|
|
|
|
|
|
@router.get("/models")
|
|
async def models():
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
r = await c.get(f"{LLAMA_SWAP_URL}/v1/models")
|
|
return JSONResponse(r.json(), status_code=r.status_code)
|
|
|
|
|
|
async def _proxy(path: str, request: Request):
|
|
body = await request.json()
|
|
requested = str(body.get("model") or "auto")
|
|
if requested == "auto":
|
|
alias, reason = choose_model(body)
|
|
body["model"] = alias
|
|
routed = {"x-mc-routed-to": alias, "x-mc-route-reason": reason}
|
|
else:
|
|
alias = requested
|
|
routed = {"x-mc-routed-to": requested}
|
|
# fast-Spur: Thinking aus für flotte Antworten (sofern Client es nicht selbst setzt).
|
|
if FAST_NO_THINK and alias == FAST and "chat_template_kwargs" not in body:
|
|
body["chat_template_kwargs"] = {"enable_thinking": False}
|
|
url = f"{LLAMA_SWAP_URL}{path}"
|
|
|
|
if body.get("stream"):
|
|
async def gen():
|
|
async with httpx.AsyncClient(timeout=None) as c:
|
|
async with c.stream("POST", url, json=body) as r:
|
|
async for chunk in r.aiter_raw():
|
|
try:
|
|
chunk_str = chunk.decode("utf-8", errors="ignore")
|
|
if '"usage":' in chunk_str:
|
|
for line in chunk_str.splitlines():
|
|
if line.startswith("data:"):
|
|
data_str = line[5:].strip()
|
|
if data_str == "[DONE]":
|
|
continue
|
|
try:
|
|
data_json = json.loads(data_str)
|
|
usage = data_json.get("usage")
|
|
if usage:
|
|
prompt = usage.get("prompt_tokens", 0)
|
|
completion = usage.get("completion_tokens", 0)
|
|
if prompt or completion:
|
|
increment_tokens(prompt, completion, model=alias)
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
yield chunk
|
|
return StreamingResponse(gen(), media_type="text/event-stream", headers=routed)
|
|
|
|
async with httpx.AsyncClient(timeout=600) as c:
|
|
r = await c.post(url, json=body)
|
|
resp_json = r.json()
|
|
try:
|
|
usage = resp_json.get("usage")
|
|
if usage:
|
|
prompt = usage.get("prompt_tokens", 0)
|
|
completion = usage.get("completion_tokens", 0)
|
|
if prompt or completion:
|
|
increment_tokens(prompt, completion, model=alias)
|
|
except Exception:
|
|
pass
|
|
return JSONResponse(resp_json, status_code=r.status_code, headers=routed)
|
|
|
|
|
|
@router.post("/chat/completions")
|
|
async def chat_completions(request: Request):
|
|
return await _proxy("/v1/chat/completions", request)
|
|
|
|
|
|
@router.post("/completions")
|
|
async def completions(request: Request):
|
|
return await _proxy("/v1/completions", request)
|