Update app.py

#2
by deleted - opened
Files changed (1) hide show
  1. app.py +64 -9
app.py CHANGED
@@ -2,7 +2,11 @@ from fastapi import FastAPI
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
4
  from pydantic import BaseModel
 
 
5
  import uvicorn
 
 
6
 
7
  app = FastAPI()
8
 
@@ -31,20 +35,53 @@ SYSTEM_PROMPT = (
31
  "Keep your replies short, casual, and to the point."
32
  )
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  class ChatRequest(BaseModel):
36
  message: str
 
 
 
 
 
 
37
 
38
 
39
- @app.post("/chat")
40
  async def chat(request: ChatRequest):
41
- # Priming turn anchors the persona much more strongly for small models
42
- messages = [
43
- {"role": "system", "content": SYSTEM_PROMPT},
44
- {"role": "user", "content": "Who are you?"},
45
- {"role": "assistant", "content": "I'm Bella! What can I help you with?"},
46
- {"role": "user", "content": request.message},
47
- ]
 
 
 
 
 
 
 
48
 
49
  prompt = tokenizer.apply_chat_template(
50
  messages,
@@ -75,8 +112,26 @@ async def chat(request: ChatRequest):
75
  if not response:
76
  response = "Hmm, I didn't quite catch that — can you rephrase?"
77
 
78
- return {"response": response}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
 
81
  if __name__ == "__main__":
82
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
4
  from pydantic import BaseModel
5
+ from collections import defaultdict
6
+ from threading import Lock
7
  import uvicorn
8
+ import uuid
9
+ import time
10
 
11
  app = FastAPI()
12
 
 
35
  "Keep your replies short, casual, and to the point."
36
  )
37
 
38
+ # --- In-memory session history store ---------------------------------------
39
+ # NOTE: this resets whenever the process restarts. For real persistence across
40
+ # restarts/multiple server instances, swap this dict for Firestore/Supabase
41
+ # (you already use both elsewhere in Bella AI) — the shape below (a list of
42
+ # {"role", "content"} dicts per session_id) maps directly onto either.
43
+ MAX_TURNS = 12 # how many user+assistant turn-pairs to keep per session
44
+ SESSION_TTL_SECONDS = 60 * 60 * 6 # drop sessions idle for 6+ hours
45
+
46
+ _histories: dict[str, list[dict]] = defaultdict(list)
47
+ _last_seen: dict[str, float] = {}
48
+ _lock = Lock()
49
+
50
+
51
+ def _prune_stale_sessions():
52
+ now = time.time()
53
+ stale = [sid for sid, ts in _last_seen.items() if now - ts > SESSION_TTL_SECONDS]
54
+ for sid in stale:
55
+ _histories.pop(sid, None)
56
+ _last_seen.pop(sid, None)
57
+
58
 
59
  class ChatRequest(BaseModel):
60
  message: str
61
+ session_id: str | None = None # client can pass one to resume a conversation
62
+
63
+
64
+ class ChatResponse(BaseModel):
65
+ response: str
66
+ session_id: str
67
 
68
 
69
+ @app.post("/chat", response_model=ChatResponse)
70
  async def chat(request: ChatRequest):
71
+ with _lock:
72
+ _prune_stale_sessions()
73
+
74
+ session_id = request.session_id or str(uuid.uuid4())
75
+ history = _histories[session_id]
76
+
77
+ # Build the message list: system + priming turn + saved history + new message
78
+ messages = [
79
+ {"role": "system", "content": SYSTEM_PROMPT},
80
+ {"role": "user", "content": "Who are you?"},
81
+ {"role": "assistant", "content": "I'm Bella! What can I help you with?"},
82
+ *history,
83
+ {"role": "user", "content": request.message},
84
+ ]
85
 
86
  prompt = tokenizer.apply_chat_template(
87
  messages,
 
112
  if not response:
113
  response = "Hmm, I didn't quite catch that — can you rephrase?"
114
 
115
+ with _lock:
116
+ history = _histories[session_id]
117
+ history.append({"role": "user", "content": request.message})
118
+ history.append({"role": "assistant", "content": response})
119
+ # keep only the last MAX_TURNS turn-pairs
120
+ if len(history) > MAX_TURNS * 2:
121
+ _histories[session_id] = history[-MAX_TURNS * 2:]
122
+ _last_seen[session_id] = time.time()
123
+
124
+ return {"response": response, "session_id": session_id}
125
+
126
+
127
+ @app.post("/chat/reset")
128
+ async def reset(session_id: str):
129
+ with _lock:
130
+ _histories.pop(session_id, None)
131
+ _last_seen.pop(session_id, None)
132
+ return {"status": "ok"}
133
 
134
 
135
  if __name__ == "__main__":
136
  uvicorn.run(app, host="0.0.0.0", port=7860)
137
+