iurbinah commited on
Commit
9a43741
·
verified ·
1 Parent(s): 2a8a6f9

Create server.pt

Browse files
Files changed (1) hide show
  1. src/server.pt +84 -0
src/server.pt ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # server.py
2
+ import os
3
+ import json
4
+ from typing import List, Optional, AsyncGenerator
5
+ from fastapi import FastAPI, HTTPException, Header
6
+ from fastapi.responses import StreamingResponse
7
+ from pydantic import BaseModel
8
+ import httpx
9
+
10
+ SERVER_API_KEY = os.getenv("OPENROUTER_API_KEY") # optional fallback
11
+ OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1/chat/completions"
12
+ DEFAULT_MODEL = "openai/gpt-4o-mini" # change as desired
13
+
14
+ app = FastAPI(title="OpenRouter Chat Backend")
15
+
16
+
17
+ class Message(BaseModel):
18
+ role: str # "system" | "user" | "assistant"
19
+ content: str
20
+
21
+
22
+ class ChatRequest(BaseModel):
23
+ messages: List[Message]
24
+ model: Optional[str] = None
25
+ temperature: Optional[float] = 0.7
26
+ max_tokens: Optional[int] = None
27
+
28
+
29
+ @app.post("/chat")
30
+ async def chat(
31
+ req: ChatRequest,
32
+ x_openrouter_api_key: Optional[str] = Header(default=None, alias="X-OpenRouter-Api-Key"),
33
+ http_referer: Optional[str] = Header(default=None, alias="HTTP-Referer"),
34
+ ):
35
+ model = req.model or DEFAULT_MODEL
36
+ # Prefer per-request key from client; fallback to server key if available
37
+ api_key = x_openrouter_api_key or SERVER_API_KEY
38
+ if not api_key:
39
+ raise HTTPException(status_code=401, detail="Missing OpenRouter API key")
40
+
41
+ headers = {
42
+ "Authorization": f"Bearer {api_key}",
43
+ "Content-Type": "application/json",
44
+ # Attribution headers (optional)
45
+ "HTTP-Referer": http_referer or "http://localhost:8501",
46
+ "X-Title": "OpenRouter Streamlit Chat",
47
+ }
48
+ payload = {
49
+ "model": model,
50
+ "messages": [m.dict() for m in req.messages],
51
+ "temperature": req.temperature,
52
+ "max_tokens": req.max_tokens,
53
+ "stream": True,
54
+ }
55
+
56
+ async def stream() -> AsyncGenerator[bytes, None]:
57
+ try:
58
+ async with httpx.AsyncClient(timeout=None) as client:
59
+ async with client.stream("POST", OPENROUTER_BASE_URL, headers=headers, json=payload) as r:
60
+ if r.status_code >= 400:
61
+ text = await r.aread()
62
+ raise HTTPException(status_code=r.status_code, detail=text.decode("utf-8"))
63
+ async for line in r.aiter_lines():
64
+ if not line:
65
+ continue
66
+ if line.startswith("data: "):
67
+ data = line[len("data: "):]
68
+ if data.strip() == "[DONE]":
69
+ yield b"event: done\ndata: done\n\n"
70
+ break
71
+ try:
72
+ obj = json.loads(data)
73
+ delta = obj.get("choices", [{}])[0].get("delta", {})
74
+ content = delta.get("content")
75
+ if content:
76
+ yield f"data: {json.dumps({'content': content})}\n\n".encode("utf-8")
77
+ except Exception:
78
+ yield f"data: {json.dumps({'raw': data})}\n\n".encode("utf-8")
79
+ except HTTPException:
80
+ raise
81
+ except Exception as e:
82
+ raise HTTPException(status_code=500, detail=str(e))
83
+
84
+ return StreamingResponse(stream(), media_type="text/event-stream")