safraeli commited on
Commit
a1a9123
·
verified ·
1 Parent(s): 36b2036

Fix chatbot: session_id validation, init failure guard, blank message check

Browse files
Files changed (1) hide show
  1. backend/api/routes/chatbot.py +39 -18
backend/api/routes/chatbot.py CHANGED
@@ -5,9 +5,8 @@ from __future__ import annotations
5
  import logging
6
  import threading
7
 
8
- from pydantic import BaseModel, Field
9
- from fastapi import APIRouter, Depends, Request
10
- from fastapi.responses import JSONResponse
11
  from slowapi import Limiter
12
  from slowapi.util import get_remote_address
13
 
@@ -21,12 +20,19 @@ router = APIRouter()
21
 
22
  class ChatRequest(BaseModel):
23
  message: str = Field(..., min_length=1, max_length=4000)
24
- session_id: str = "default"
 
 
 
 
 
 
 
25
 
26
 
27
  class FeedbackRequest(BaseModel):
28
- session_id: str
29
- message_id: str
30
  rating: str = Field(..., pattern=r"^(up|down|flag)$")
31
  comment: str = Field("", max_length=2000)
32
 
@@ -34,16 +40,25 @@ class FeedbackRequest(BaseModel):
34
  # Thread-safe lazy chatbot init
35
  _chatbot = None
36
  _chatbot_lock = threading.Lock()
 
37
 
38
 
39
  def _get_chatbot(hub: DataHub):
40
- global _chatbot
41
  if _chatbot is not None:
42
  return _chatbot
 
 
43
  with _chatbot_lock:
44
- if _chatbot is None:
 
 
45
  from src.chatbot.vineyard_chatbot import VineyardChatbot
46
  _chatbot = VineyardChatbot(hub=hub)
 
 
 
 
47
  return _chatbot
48
 
49
 
@@ -51,15 +66,21 @@ def _get_chatbot(hub: DataHub):
51
  @limiter.limit("10/minute")
52
  async def chat_message(request: Request, req: ChatRequest, hub: DataHub = Depends(get_datahub)):
53
  bot = _get_chatbot(hub)
54
- response = bot.chat(req.message)
55
- return {
56
- "message": response.message,
57
- "confidence": getattr(response, "confidence", None),
58
- "sources": getattr(response, "sources", []),
59
- "caveats": getattr(response, "caveats", []),
60
- "rule_violations": getattr(response, "rule_violations", []),
61
- "response_mode": getattr(response, "response_mode", "info"),
62
- }
 
 
 
 
 
 
63
 
64
 
65
  @router.post("/feedback")
@@ -76,4 +97,4 @@ async def chat_feedback(request: Request, req: FeedbackRequest):
76
  return {"status": "ok"}
77
  except Exception as exc:
78
  log.error("Feedback logging failed: %s", exc)
79
- return JSONResponse(status_code=500, content={"error": "Feedback logging failed"})
 
5
  import logging
6
  import threading
7
 
8
+ from pydantic import BaseModel, Field, field_validator
9
+ from fastapi import APIRouter, Depends, HTTPException, Request
 
10
  from slowapi import Limiter
11
  from slowapi.util import get_remote_address
12
 
 
20
 
21
  class ChatRequest(BaseModel):
22
  message: str = Field(..., min_length=1, max_length=4000)
23
+ session_id: str = Field("default", min_length=1, max_length=100, pattern=r"^[a-zA-Z0-9_\-]+$")
24
+
25
+ @field_validator("message")
26
+ @classmethod
27
+ def message_not_blank(cls, v: str) -> str:
28
+ if not v.strip():
29
+ raise ValueError("Message must not be blank")
30
+ return v.strip()
31
 
32
 
33
  class FeedbackRequest(BaseModel):
34
+ session_id: str = Field(..., min_length=1, max_length=100)
35
+ message_id: str = Field(..., min_length=1, max_length=100)
36
  rating: str = Field(..., pattern=r"^(up|down|flag)$")
37
  comment: str = Field("", max_length=2000)
38
 
 
40
  # Thread-safe lazy chatbot init
41
  _chatbot = None
42
  _chatbot_lock = threading.Lock()
43
+ _chatbot_init_failed = False
44
 
45
 
46
  def _get_chatbot(hub: DataHub):
47
+ global _chatbot, _chatbot_init_failed
48
  if _chatbot is not None:
49
  return _chatbot
50
+ if _chatbot_init_failed:
51
+ return None
52
  with _chatbot_lock:
53
+ if _chatbot is not None:
54
+ return _chatbot
55
+ try:
56
  from src.chatbot.vineyard_chatbot import VineyardChatbot
57
  _chatbot = VineyardChatbot(hub=hub)
58
+ except Exception as exc:
59
+ _chatbot_init_failed = True
60
+ log.error("Chatbot init failed (will not retry): %s", exc)
61
+ return None
62
  return _chatbot
63
 
64
 
 
66
  @limiter.limit("10/minute")
67
  async def chat_message(request: Request, req: ChatRequest, hub: DataHub = Depends(get_datahub)):
68
  bot = _get_chatbot(hub)
69
+ if bot is None:
70
+ raise HTTPException(status_code=503, detail="Chatbot unavailable")
71
+ try:
72
+ response = bot.chat(req.message)
73
+ return {
74
+ "message": response.message,
75
+ "confidence": getattr(response, "confidence", None),
76
+ "sources": getattr(response, "sources", []),
77
+ "caveats": getattr(response, "caveats", []),
78
+ "rule_violations": getattr(response, "rule_violations", []),
79
+ "response_mode": getattr(response, "response_mode", "info"),
80
+ }
81
+ except Exception as exc:
82
+ log.error("Chatbot error: %s", exc, exc_info=True)
83
+ raise HTTPException(status_code=500, detail="Chatbot processing failed")
84
 
85
 
86
  @router.post("/feedback")
 
97
  return {"status": "ok"}
98
  except Exception as exc:
99
  log.error("Feedback logging failed: %s", exc)
100
+ raise HTTPException(status_code=500, detail="Feedback logging failed")