Files
chat-proxy/backend/main.py
T
Samuele E. Locatelli ba3280280d Add cache for streamlit
2025-08-21 17:07:29 +00:00

108 lines
3.8 KiB
Python

# main.py
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
import redis
import json
import httpx
r = redis.Redis(host='localhost', port=6379, db=1)
def save_chat(user_id, message):
r.rpush(f"chat:{user_id}", json.dumps(message))
def get_chat(user_id):
messages = r.lrange(f"chat:{user_id}", 0, -1)
return [json.loads(m.decode('utf-8')) for m in messages]
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
LM_STUDIO_URL = "http://10.74.83.100:1234/v1/chat/completions"
MODEL_NAME = "qwen/qwen3-4b-2507" # update as needed
@app.post("/chat")
async def chat(request: Request):
data = await request.json()
user_id = data.get("user_id", "default")
message = data["message"]
save_chat(user_id, {"role": "user", "content": message})
history = get_chat(user_id)
async with httpx.AsyncClient(timeout=None) as client:
resp = await client.post(LM_STUDIO_URL, json={
"model": MODEL_NAME,
"messages": history,
})
result = resp.json()
reply = result["choices"][0]["message"]["content"]
save_chat(user_id, {"role": "assistant", "content": reply})
return {"response": reply}
@app.post("/chat-stream")
async def chat_stream(request: Request):
data = await request.json()
user_id = data.get("user_id", "default")
message = data["message"]
# Save user message and build history
save_chat(user_id, {"role": "user", "content": message})
history = get_chat(user_id)
async def event_generator():
assistant_text = ""
try:
async with httpx.AsyncClient(timeout=None) as client:
async with client.stream("POST", LM_STUDIO_URL, json={
"model": MODEL_NAME,
"messages": history,
"stream": True
}) as r:
async for raw_line in r.aiter_lines():
if not raw_line:
continue
# Normalize to standard SSE "data: ..." form
line = raw_line if raw_line.startswith("data:") else f"data: {raw_line}"
payload = line[len("data: "):].strip()
if payload == "[DONE]":
# Finalize and flush
yield "data: [DONE]\n\n"
break
# Echo the SSE line to client
yield f"data: {payload}\n\n"
# Accumulate content for saving to Redis
try:
obj = json.loads(payload)
choice = obj.get("choices", [{}])[0]
# Handle OpenAI-style streaming objects
delta = choice.get("delta", {})
piece = delta.get("content")
if piece is None:
# Some servers send "text" instead
piece = choice.get("text")
if piece:
assistant_text += piece
except Exception:
# Ignore non-JSON control lines
pass
finally:
if assistant_text:
save_chat(user_id, {"role": "assistant", "content": assistant_text})
headers = {
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no" # helps if behind Nginx
}
return StreamingResponse(event_generator(), media_type="text/event-stream", headers=headers)