Skip to content

Commit 1257bb6

Browse files
Add exception handling for DB transactions
1 parent decfd26 commit 1257bb6

3 files changed

Lines changed: 210 additions & 70 deletions

File tree

backend/src/api/routers/conversations.py

Lines changed: 100 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from langchain_ollama import ChatOllama
1111
from langchain_core.messages import AIMessageChunk
1212
from starlette.responses import StreamingResponse
13+
from sqlalchemy.exc import SQLAlchemyError
1314
from sqlalchemy.orm import Session
1415

1516
from ...agents.retriever_graph import RetrieverGraph
@@ -222,7 +223,11 @@ def parse_agent_output(output: list) -> tuple[str, list[ContextSource], list[str
222223

223224
def get_history_str(db: Session | None, conversation_uuid: UUID | None) -> str:
224225
if use_db and db and conversation_uuid:
225-
history = crud.get_conversation_history(db, conversation_uuid)
226+
try:
227+
history = crud.get_conversation_history(db, conversation_uuid)
228+
except SQLAlchemyError:
229+
logging.error("Failed to retrieve conversation history", exc_info=True)
230+
return ""
226231
history_str = ""
227232
for i in history:
228233
user_msg = i.get("User", "")
@@ -254,21 +259,27 @@ async def get_agent_response(
254259

255260
conversation_uuid = user_input.conversation_uuid
256261

257-
if use_db and db:
258-
conversation = crud.get_or_create_conversation(
259-
db,
260-
conversation_uuid=conversation_uuid,
261-
title=user_question[:100] if user_question else None,
262-
)
263-
conversation_uuid = conversation.uuid
262+
db_persist = use_db and db is not None
263+
if db_persist:
264+
try:
265+
conversation = crud.get_or_create_conversation(
266+
db,
267+
conversation_uuid=conversation_uuid,
268+
title=user_question[:100] if user_question else None,
269+
)
270+
conversation_uuid = conversation.uuid
264271

265-
crud.create_message(
266-
db=db,
267-
conversation_uuid=conversation.uuid,
268-
role="user",
269-
content=user_question,
270-
)
271-
else:
272+
crud.create_message(
273+
db=db,
274+
conversation_uuid=conversation.uuid,
275+
role="user",
276+
content=user_question,
277+
)
278+
except SQLAlchemyError:
279+
logging.error("Failed to persist user message", exc_info=True)
280+
db_persist = False
281+
282+
if not db_persist:
272283
if conversation_uuid is None:
273284
from uuid import uuid4
274285

@@ -296,15 +307,18 @@ async def get_agent_response(
296307
]
297308
}
298309

299-
if use_db and db and conversation_uuid:
300-
crud.create_message(
301-
db=db,
302-
conversation_uuid=conversation_uuid,
303-
role="assistant",
304-
content=llm_response,
305-
context_sources=context_sources_dict,
306-
tools=tools,
307-
)
310+
if db_persist and conversation_uuid:
311+
try:
312+
crud.create_message(
313+
db=db,
314+
conversation_uuid=conversation_uuid,
315+
role="assistant",
316+
content=llm_response,
317+
context_sources=context_sources_dict,
318+
tools=tools,
319+
)
320+
except SQLAlchemyError:
321+
logging.error("Failed to persist assistant message", exc_info=True)
308322
else:
309323
if conversation_uuid:
310324
chat_history[conversation_uuid].append(
@@ -349,21 +363,27 @@ async def get_response_stream(user_input: UserInput, db: Session | None) -> Any:
349363

350364
conversation_uuid = user_input.conversation_uuid
351365

352-
if use_db and db:
353-
conversation = crud.get_or_create_conversation(
354-
db,
355-
conversation_uuid=conversation_uuid,
356-
title=user_question[:100] if user_question else None,
357-
)
358-
conversation_uuid = conversation.uuid
366+
db_persist = use_db and db is not None
367+
if db_persist:
368+
try:
369+
conversation = crud.get_or_create_conversation(
370+
db,
371+
conversation_uuid=conversation_uuid,
372+
title=user_question[:100] if user_question else None,
373+
)
374+
conversation_uuid = conversation.uuid
359375

360-
crud.create_message(
361-
db=db,
362-
conversation_uuid=conversation.uuid,
363-
role="user",
364-
content=user_question,
365-
)
366-
else:
376+
crud.create_message(
377+
db=db,
378+
conversation_uuid=conversation.uuid,
379+
role="user",
380+
content=user_question,
381+
)
382+
except SQLAlchemyError:
383+
logging.error("Failed to persist user message", exc_info=True)
384+
db_persist = False
385+
386+
if not db_persist:
367387
if conversation_uuid is None:
368388
from uuid import uuid4
369389

@@ -412,18 +432,21 @@ async def get_response_stream(user_input: UserInput, db: Session | None) -> Any:
412432

413433
full_response = "".join(chunks)
414434

415-
if use_db and db and conversation_uuid:
416-
context_sources_dict: dict[str, Any] = {
417-
"sources": [{"source": url, "context": ""} for url in urls]
418-
}
419-
crud.create_message(
420-
db=db,
421-
conversation_uuid=conversation_uuid,
422-
role="assistant",
423-
content=full_response,
424-
context_sources=context_sources_dict,
425-
tools=[],
426-
)
435+
if db_persist and conversation_uuid:
436+
try:
437+
context_sources_dict: dict[str, Any] = {
438+
"sources": [{"source": url, "context": ""} for url in urls]
439+
}
440+
crud.create_message(
441+
db=db,
442+
conversation_uuid=conversation_uuid,
443+
role="assistant",
444+
content=full_response,
445+
context_sources=context_sources_dict,
446+
tools=[],
447+
)
448+
except SQLAlchemyError:
449+
logging.error("Failed to persist assistant message", exc_info=True)
427450
else:
428451
if conversation_uuid:
429452
chat_history[conversation_uuid].append(
@@ -446,7 +469,13 @@ async def create_conversation(db: Session = Depends(get_db)) -> ConversationResp
446469
"""Creates a new conversation with an auto-generated UUID identifier."""
447470
if not use_db:
448471
raise HTTPException(status_code=503, detail=DB_DISABLED_MSG)
449-
new_conversation = crud.create_conversation(db, conversation_uuid=None, title=None)
472+
try:
473+
new_conversation = crud.create_conversation(
474+
db, conversation_uuid=None, title=None
475+
)
476+
except SQLAlchemyError:
477+
logging.error("Failed to create conversation", exc_info=True)
478+
raise HTTPException(status_code=500, detail="Failed to create conversation")
450479
return ConversationResponse.model_validate(new_conversation)
451480

452481

@@ -457,7 +486,11 @@ async def list_conversations(
457486
"""Retrieves a paginated list of all conversations without their messages."""
458487
if not use_db:
459488
raise HTTPException(status_code=503, detail=DB_DISABLED_MSG)
460-
conversations = crud.get_all_conversations(db, skip=skip, limit=limit)
489+
try:
490+
conversations = crud.get_all_conversations(db, skip=skip, limit=limit)
491+
except SQLAlchemyError:
492+
logging.error("Failed to list conversations", exc_info=True)
493+
raise HTTPException(status_code=500, detail="Failed to list conversations")
461494
return [ConversationListResponse.model_validate(conv) for conv in conversations]
462495

463496

@@ -468,7 +501,11 @@ async def get_conversation(
468501
"""Retrieves a complete conversation including all associated messages."""
469502
if not use_db:
470503
raise HTTPException(status_code=503, detail=DB_DISABLED_MSG)
471-
conversation = crud.get_conversation(db, id)
504+
try:
505+
conversation = crud.get_conversation(db, id)
506+
except SQLAlchemyError:
507+
logging.error("Failed to get conversation", exc_info=True)
508+
raise HTTPException(status_code=500, detail="Failed to get conversation")
472509
if not conversation:
473510
raise HTTPException(status_code=404, detail="Conversation not found")
474511
return ConversationResponse.model_validate(conversation)
@@ -479,7 +516,13 @@ async def delete_conversation(id: UUID, db: Session = Depends(get_db)) -> None:
479516
"""Permanently removes a conversation and all associated messages from the database."""
480517
if not use_db:
481518
raise HTTPException(status_code=503, detail=DB_DISABLED_MSG)
482-
conversation = crud.get_conversation(db, id)
483-
if not conversation:
484-
raise HTTPException(status_code=404, detail="Conversation not found")
485-
crud.delete_conversation(db, id)
519+
try:
520+
conversation = crud.get_conversation(db, id)
521+
if not conversation:
522+
raise HTTPException(status_code=404, detail="Conversation not found")
523+
crud.delete_conversation(db, id)
524+
except HTTPException:
525+
raise
526+
except SQLAlchemyError:
527+
logging.error("Failed to delete conversation", exc_info=True)
528+
raise HTTPException(status_code=500, detail="Failed to delete conversation")

backend/src/database/crud.py

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import logging
12
from typing import Optional
23
from sqlalchemy.orm import Session
34
from sqlalchemy import desc
5+
from sqlalchemy.exc import SQLAlchemyError
46
from .models import Conversation, Message
57
from uuid import UUID
68

9+
logger = logging.getLogger(__name__)
10+
711

812
def create_conversation(
913
db: Session, conversation_uuid: Optional[UUID] = None, title: Optional[str] = None
@@ -13,9 +17,14 @@ def create_conversation(
1317
if conversation_uuid
1418
else Conversation(title=title)
1519
)
16-
db.add(conversation)
17-
db.commit()
18-
db.refresh(conversation)
20+
try:
21+
db.add(conversation)
22+
db.commit()
23+
db.refresh(conversation)
24+
except SQLAlchemyError:
25+
db.rollback()
26+
logger.error("Failed to create conversation", exc_info=True)
27+
raise
1928
return conversation
2029

2130

@@ -50,17 +59,27 @@ def update_conversation_title(
5059
) -> Optional[Conversation]:
5160
conversation = get_conversation(db, conversation_uuid)
5261
if conversation:
53-
conversation.title = title
54-
db.commit()
55-
db.refresh(conversation)
62+
try:
63+
conversation.title = title
64+
db.commit()
65+
db.refresh(conversation)
66+
except SQLAlchemyError:
67+
db.rollback()
68+
logger.error("Failed to update conversation title", exc_info=True)
69+
raise
5670
return conversation
5771

5872

5973
def delete_conversation(db: Session, conversation_uuid: UUID) -> bool:
6074
conversation = get_conversation(db, conversation_uuid)
6175
if conversation:
62-
db.delete(conversation)
63-
db.commit()
76+
try:
77+
db.delete(conversation)
78+
db.commit()
79+
except SQLAlchemyError:
80+
db.rollback()
81+
logger.error("Failed to delete conversation", exc_info=True)
82+
raise
6483
return True
6584
return False
6685

@@ -80,9 +99,14 @@ def create_message(
8099
context_sources=context_sources,
81100
tools=tools,
82101
)
83-
db.add(message)
84-
db.commit()
85-
db.refresh(message)
102+
try:
103+
db.add(message)
104+
db.commit()
105+
db.refresh(message)
106+
except SQLAlchemyError:
107+
db.rollback()
108+
logger.error("Failed to create message", exc_info=True)
109+
raise
86110
return message
87111

88112

@@ -106,8 +130,13 @@ def get_conversation_messages(
106130
def delete_message(db: Session, message_id: UUID) -> bool:
107131
message = get_message(db, message_id)
108132
if message:
109-
db.delete(message)
110-
db.commit()
133+
try:
134+
db.delete(message)
135+
db.commit()
136+
except SQLAlchemyError:
137+
db.rollback()
138+
logger.error("Failed to delete message", exc_info=True)
139+
raise
111140
return True
112141
return False
113142

0 commit comments

Comments
 (0)