Skip to content

Commit f056b5b

Browse files
committed
added feedback functionalities
1 parent b8ea175 commit f056b5b

File tree

5 files changed

+468
-105
lines changed

5 files changed

+468
-105
lines changed

backend/onyx/server/query_and_chat/chat_backend.py

Lines changed: 178 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from datetime import timedelta
99
from uuid import UUID
1010

11-
from fastapi import APIRouter
11+
from fastapi import APIRouter,Path
1212
from fastapi import Depends
1313
from fastapi import HTTPException
1414
from fastapi import Query
@@ -100,7 +100,14 @@
100100
from shared_configs.contextvars import get_current_tenant_id
101101
from sqlalchemy.orm import Session, joinedload
102102
from sqlalchemy import desc
103-
103+
from typing import Optional
104+
from datetime import datetime
105+
from fastapi import Query
106+
from sqlalchemy.exc import SQLAlchemyError
107+
from io import StringIO
108+
import csv
109+
from sqlalchemy import cast, Date, or_
110+
from fastapi import Query
104111
RECENT_DOCS_FOLDER_ID = -1
105112

106113
logger = setup_logger()
@@ -196,52 +203,191 @@ def update_chat_session_model(
196203
db_session.add(chat_session)
197204
db_session.commit()
198205

206+
# @router.get("/get-user-feedback")
207+
# def get_user_feedback(
208+
# skip: int = Query(0, ge=0, description="Number of records to skip for pagination"),
209+
# limit: int = Query(50, ge=1, le=100, description="Number of records to return"),
210+
# user: User = Depends(current_user),
211+
# db_session: Session = Depends(get_session),
212+
# ):
213+
# """
214+
# Get paginated chat feedbacks for the logged-in user.
215+
# Works by joining ChatMessageFeedback -> ChatMessage -> ChatSession
216+
# and filtering by ChatSession.user_id = current user.
217+
# """
218+
219+
# # Build query using relationships instead of raw join conditions
220+
# base_query = (
221+
# db_session.query(ChatMessageFeedback)
222+
# .join(ChatMessage, ChatMessage.id == ChatMessageFeedback.chat_message_id)
223+
# .join(ChatSession, ChatSession.id == ChatMessage.chat_session_id)
224+
# .filter(ChatSession.user_id == user.id) # strictly this user's sessions
225+
# .order_by(ChatMessageFeedback.feedback_timestamp.desc())
226+
# )
227+
228+
# total_count = base_query.count()
229+
# feedback_rows = base_query.offset(skip).limit(limit).all()
230+
231+
# return {
232+
# "total": total_count,
233+
# "skip": skip,
234+
# "limit": limit,
235+
# "feedbacks": [
236+
# {
237+
# "id": fb.id,
238+
# "chat_message_id": fb.chat_message_id,
239+
# "is_positive": fb.is_positive,
240+
# "feedback_text": fb.feedback_text,
241+
# "required_followup": fb.required_followup,
242+
# "predefined_feedback": fb.predefined_feedback,
243+
# "feedback_timestamp": fb.feedback_timestamp,
244+
# }
245+
# for fb in feedback_rows
246+
# ],
247+
# }
248+
199249
@router.get("/get-user-feedback")
200250
def get_user_feedback(
201-
skip: int = Query(0, ge=0, description="Number of records to skip for pagination"),
202-
limit: int = Query(50, ge=1, le=100, description="Number of records to return"),
251+
skip: int = Query(0, ge=0, description="Number of records to skip for pagination. Must be >= 0."),
252+
limit: int = Query(50, ge=1, le=100, description="Number of records to return. Must be between 1 and 100."),
253+
start_date: Optional[datetime] = Query(None, description="Start date filter (YYYY-MM-DD), time ignored."),
254+
end_date: Optional[datetime] = Query(None, description="End date filter (YYYY-MM-DD), time ignored."),
255+
search: Optional[str] = Query(None, description="Search feedback by ID or feedback text."),
256+
download: bool = Query(False, description="Download the current page's feedback as CSV if true."),
203257
user: User = Depends(current_user),
204258
db_session: Session = Depends(get_session),
205259
):
206-
"""
207-
Get paginated chat feedbacks for the logged-in user.
208-
Works by joining ChatMessageFeedback -> ChatMessage -> ChatSession
209-
and filtering by ChatSession.user_id = current user.
210-
"""
260+
if start_date and end_date and start_date.date() > end_date.date():
261+
raise HTTPException(
262+
status_code=400,
263+
detail="Invalid date range: start_date must be less than or equal to end_date."
264+
)
265+
266+
try:
267+
base_query = (
268+
db_session.query(ChatMessageFeedback, ChatSession)
269+
.join(ChatMessage, ChatMessage.id == ChatMessageFeedback.chat_message_id)
270+
.join(ChatSession, ChatSession.id == ChatMessage.chat_session_id)
271+
.filter(ChatSession.user_id == user.id)
272+
)
273+
274+
if start_date:
275+
base_query = base_query.filter(
276+
cast(ChatMessageFeedback.feedback_timestamp, Date) >= start_date.date()
277+
)
278+
if end_date:
279+
base_query = base_query.filter(
280+
cast(ChatMessageFeedback.feedback_timestamp, Date) <= end_date.date()
281+
)
282+
283+
if search:
284+
if search.isdigit():
285+
base_query = base_query.filter(
286+
or_(
287+
ChatMessageFeedback.id == int(search),
288+
ChatMessageFeedback.feedback_text.ilike(f"%{search}%")
289+
)
290+
)
291+
else:
292+
base_query = base_query.filter(ChatMessageFeedback.feedback_text.ilike(f"%{search}%"))
293+
294+
base_query = base_query.order_by(ChatMessageFeedback.feedback_timestamp.desc())
295+
296+
total_count = base_query.count()
297+
298+
if download:
299+
feedback_rows = base_query.all()
300+
else:
301+
feedback_rows = base_query.offset(skip).limit(limit).all()
302+
303+
if download:
304+
from io import StringIO
305+
import csv
306+
from fastapi.responses import StreamingResponse
307+
308+
output = StringIO()
309+
writer = csv.writer(output)
310+
writer.writerow([
311+
"feedback_text", "is_positive", "feedback_timestamp",
312+
"chat_session_id", "chat_session_name"
313+
])
314+
for fb, chat in feedback_rows:
315+
writer.writerow([
316+
fb.feedback_text, fb.is_positive, fb.feedback_timestamp,
317+
chat.id, chat.description
318+
])
319+
output.seek(0)
320+
return StreamingResponse(
321+
output,
322+
media_type="text/csv",
323+
headers={"Content-Disposition": f"attachment; filename=chat_feedback_{user.id}_all.csv"}
324+
)
325+
326+
return {
327+
"total": total_count,
328+
"skip": skip,
329+
"limit": limit,
330+
"feedbacks": [
331+
{
332+
"id": fb.id,
333+
"chat_message_id": fb.chat_message_id,
334+
"is_positive": fb.is_positive,
335+
"feedback_text": fb.feedback_text,
336+
"required_followup": fb.required_followup,
337+
"predefined_feedback": fb.predefined_feedback,
338+
"feedback_timestamp": fb.feedback_timestamp,
339+
"chat_session": {
340+
"id": chat.id,
341+
"name": chat.description,
342+
"persona_id": chat.persona_id,
343+
"time_created": chat.time_created.isoformat(),
344+
"time_updated": chat.time_updated.isoformat(),
345+
"shared_status": chat.shared_status,
346+
"folder_id": chat.folder_id,
347+
"current_alternate_model": chat.current_alternate_model,
348+
"current_temperature_override": chat.temperature_override,
349+
}
350+
}
351+
for fb, chat in feedback_rows
352+
],
353+
}
354+
355+
except ValueError as ve:
356+
raise HTTPException(status_code=422, detail=f"Value error: {str(ve)}")
357+
except SQLAlchemyError:
358+
raise HTTPException(status_code=500, detail="Database error occurred. Please try again later.")
359+
except Exception as e:
360+
raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
211361

212-
# Build query using relationships instead of raw join conditions
213-
base_query = (
362+
@router.delete("/delete-feedback/{feedback_id}")
363+
def delete_feedback(
364+
feedback_id: int = Path(..., description="ID of the feedback to delete"),
365+
user: User = Depends(current_user),
366+
db_session: Session = Depends(get_session),
367+
):
368+
# Find feedback by ID and ensure it belongs to current user
369+
feedback = (
214370
db_session.query(ChatMessageFeedback)
215371
.join(ChatMessage, ChatMessage.id == ChatMessageFeedback.chat_message_id)
216372
.join(ChatSession, ChatSession.id == ChatMessage.chat_session_id)
217-
.filter(ChatSession.user_id == user.id) # strictly this user's sessions
218-
.order_by(ChatMessageFeedback.feedback_timestamp.desc())
373+
.filter(ChatMessageFeedback.id == feedback_id)
374+
.filter(ChatSession.user_id == user.id)
375+
.first()
219376
)
220377

221-
total_count = base_query.count()
222-
feedback_rows = base_query.offset(skip).limit(limit).all()
378+
if not feedback:
379+
raise HTTPException(status_code=404, detail="Feedback not found or you do not have permission to delete it.")
380+
381+
# Delete feedback
382+
db_session.delete(feedback)
383+
db_session.commit()
223384

224385
return {
225-
"total": total_count,
226-
"skip": skip,
227-
"limit": limit,
228-
"feedbacks": [
229-
{
230-
"id": fb.id,
231-
"chat_message_id": fb.chat_message_id,
232-
"is_positive": fb.is_positive,
233-
"feedback_text": fb.feedback_text,
234-
"required_followup": fb.required_followup,
235-
"predefined_feedback": fb.predefined_feedback,
236-
"feedback_timestamp": fb.feedback_timestamp,
237-
}
238-
for fb in feedback_rows
239-
],
386+
"message": "Successfully deleted",
387+
"status": "Success"
240388
}
241389

242390

243-
244-
245391
@router.get("/get-chat-session/{session_id}")
246392
def get_chat_session(
247393
session_id: UUID,

0 commit comments

Comments
 (0)