Skip to content

Commit 4f1d914

Browse files
committed
Fix the retrieval of previous conversation when using Conversations API
1 parent 6d772a6 commit 4f1d914

5 files changed

Lines changed: 261 additions & 113 deletions

File tree

src/app/endpoints/conversations_v3.py

Lines changed: 84 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@
3232
delete_conversation,
3333
retrieve_conversation,
3434
)
35-
from utils.suid import check_suid
35+
from utils.suid import (
36+
check_suid,
37+
normalize_conversation_id,
38+
to_llama_stack_conversation_id,
39+
)
3640

3741
logger = logging.getLogger("app.endpoints.handlers")
3842
router = APIRouter(tags=["conversations_v3"])
@@ -282,9 +286,17 @@ async def get_conversation_endpoint_handler(
282286
).dump_detail(),
283287
)
284288

289+
# Normalize the conversation ID for database operations (strip conv_ prefix if present)
290+
normalized_conv_id = normalize_conversation_id(conversation_id)
291+
logger.debug(
292+
"GET conversation - original ID: %s, normalized ID: %s",
293+
conversation_id,
294+
normalized_conv_id,
295+
)
296+
285297
user_id = auth[0]
286298
if not can_access_conversation(
287-
conversation_id,
299+
normalized_conv_id,
288300
user_id,
289301
others_allowed=(
290302
Action.READ_OTHERS_CONVERSATIONS in request.state.authorized_actions
@@ -293,36 +305,50 @@ async def get_conversation_endpoint_handler(
293305
logger.warning(
294306
"User %s attempted to read conversation %s they don't have access to",
295307
user_id,
296-
conversation_id,
308+
normalized_conv_id,
297309
)
298310
raise HTTPException(
299311
status_code=status.HTTP_403_FORBIDDEN,
300312
detail=AccessDeniedResponse(
301313
user_id=user_id,
302314
resource="conversation",
303-
resource_id=conversation_id,
315+
resource_id=normalized_conv_id,
304316
action="read",
305317
).dump_detail(),
306318
)
307319

308320
# If reached this, user is authorized to retrieve this conversation
309-
conversation = retrieve_conversation(conversation_id)
321+
# Note: We check if conversation exists in DB but don't fail if it doesn't,
322+
# as it might exist in llama-stack but not be persisted yet
323+
conversation = retrieve_conversation(normalized_conv_id)
310324
if conversation is None:
311-
raise HTTPException(
312-
status_code=status.HTTP_404_NOT_FOUND,
313-
detail=NotFoundResponse(
314-
resource="conversation", resource_id=conversation_id
315-
).dump_detail(),
325+
logger.warning(
326+
"Conversation %s not found in database, will try llama-stack",
327+
normalized_conv_id,
316328
)
317329

318-
logger.info("Retrieving conversation %s using Conversations API", conversation_id)
330+
logger.info(
331+
"Retrieving conversation %s using Conversations API", normalized_conv_id
332+
)
319333

320334
try:
321335
client = AsyncLlamaStackClientHolder().get_client()
322336

337+
# Convert to llama-stack format (add 'conv_' prefix if needed)
338+
llama_stack_conv_id = to_llama_stack_conversation_id(normalized_conv_id)
339+
logger.debug(
340+
"Calling llama-stack list_items with conversation_id: %s",
341+
llama_stack_conv_id,
342+
)
343+
323344
# Use Conversations API to retrieve conversation items
324-
conversation_items_response = await client.conversations.list_items(
325-
conversation_id=conversation_id,
345+
from llama_stack_client import NOT_GIVEN
346+
347+
conversation_items_response = await client.conversations.items.list(
348+
conversation_id=llama_stack_conv_id,
349+
after=NOT_GIVEN, # No pagination cursor
350+
include=NOT_GIVEN, # Include all available data
351+
limit=1000, # Max items to retrieve
326352
order="asc", # Get items in chronological order
327353
)
328354

@@ -348,7 +374,7 @@ async def get_conversation_endpoint_handler(
348374
chat_history = simplify_conversation_items(items_dicts)
349375

350376
return ConversationResponse(
351-
conversation_id=conversation_id,
377+
conversation_id=normalized_conv_id,
352378
chat_history=chat_history,
353379
)
354380

@@ -366,7 +392,7 @@ async def get_conversation_endpoint_handler(
366392
raise HTTPException(
367393
status_code=status.HTTP_404_NOT_FOUND,
368394
detail=NotFoundResponse(
369-
resource="conversation", resource_id=conversation_id
395+
resource="conversation", resource_id=normalized_conv_id
370396
).dump_detail(),
371397
) from e
372398

@@ -375,12 +401,12 @@ async def get_conversation_endpoint_handler(
375401

376402
except Exception as e:
377403
# Handle case where conversation doesn't exist or other errors
378-
logger.exception("Error retrieving conversation %s: %s", conversation_id, e)
404+
logger.exception("Error retrieving conversation %s: %s", normalized_conv_id, e)
379405
raise HTTPException(
380406
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
381407
detail={
382408
"response": "Unknown error",
383-
"cause": f"Unknown error while getting conversation {conversation_id} : {str(e)}",
409+
"cause": f"Unknown error while getting conversation {normalized_conv_id} : {str(e)}",
384410
},
385411
) from e
386412

@@ -421,9 +447,12 @@ async def delete_conversation_endpoint_handler(
421447
).dump_detail(),
422448
)
423449

450+
# Normalize the conversation ID for database operations (strip conv_ prefix if present)
451+
normalized_conv_id = normalize_conversation_id(conversation_id)
452+
424453
user_id = auth[0]
425454
if not can_access_conversation(
426-
conversation_id,
455+
normalized_conv_id,
427456
user_id,
428457
others_allowed=(
429458
Action.DELETE_OTHERS_CONVERSATIONS in request.state.authorized_actions
@@ -432,46 +461,47 @@ async def delete_conversation_endpoint_handler(
432461
logger.warning(
433462
"User %s attempted to delete conversation %s they don't have access to",
434463
user_id,
435-
conversation_id,
464+
normalized_conv_id,
436465
)
437466
raise HTTPException(
438467
status_code=status.HTTP_403_FORBIDDEN,
439468
detail=AccessDeniedResponse(
440469
user_id=user_id,
441470
resource="conversation",
442-
resource_id=conversation_id,
471+
resource_id=normalized_conv_id,
443472
action="delete",
444473
).dump_detail(),
445474
)
446475

447476
# If reached this, user is authorized to delete this conversation
448-
conversation = retrieve_conversation(conversation_id)
477+
conversation = retrieve_conversation(normalized_conv_id)
449478
if conversation is None:
450479
raise HTTPException(
451480
status_code=status.HTTP_404_NOT_FOUND,
452481
detail=NotFoundResponse(
453-
resource="conversation", resource_id=conversation_id
482+
resource="conversation", resource_id=normalized_conv_id
454483
).dump_detail(),
455484
)
456485

457-
logger.info("Deleting conversation %s using Conversations API", conversation_id)
486+
logger.info("Deleting conversation %s using Conversations API", normalized_conv_id)
458487

459488
try:
460489
# Get Llama Stack client
461490
client = AsyncLlamaStackClientHolder().get_client()
462491

492+
# Convert to llama-stack format (add 'conv_' prefix if needed)
493+
llama_stack_conv_id = to_llama_stack_conversation_id(normalized_conv_id)
494+
463495
# Use Conversations API to delete the conversation
464-
await client.conversations.openai_delete_conversation(
465-
conversation_id=conversation_id
466-
)
496+
await client.conversations.delete(conversation_id=llama_stack_conv_id)
467497

468-
logger.info("Successfully deleted conversation %s", conversation_id)
498+
logger.info("Successfully deleted conversation %s", normalized_conv_id)
469499

470500
# Also delete from local database
471-
delete_conversation(conversation_id=conversation_id)
501+
delete_conversation(conversation_id=normalized_conv_id)
472502

473503
return ConversationDeleteResponse(
474-
conversation_id=conversation_id,
504+
conversation_id=normalized_conv_id,
475505
success=True,
476506
response="Conversation deleted successfully",
477507
)
@@ -488,12 +518,12 @@ async def delete_conversation_endpoint_handler(
488518
# If not found in LlamaStack, still try to delete from local DB
489519
logger.warning(
490520
"Conversation %s not found in LlamaStack, cleaning up local DB",
491-
conversation_id,
521+
normalized_conv_id,
492522
)
493-
delete_conversation(conversation_id=conversation_id)
523+
delete_conversation(conversation_id=normalized_conv_id)
494524

495525
return ConversationDeleteResponse(
496-
conversation_id=conversation_id,
526+
conversation_id=normalized_conv_id,
497527
success=True,
498528
response="Conversation deleted successfully",
499529
)
@@ -503,12 +533,12 @@ async def delete_conversation_endpoint_handler(
503533

504534
except Exception as e:
505535
# Handle case where conversation doesn't exist or other errors
506-
logger.exception("Error deleting conversation %s: %s", conversation_id, e)
536+
logger.exception("Error deleting conversation %s: %s", normalized_conv_id, e)
507537
raise HTTPException(
508538
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
509539
detail={
510540
"response": "Unknown error",
511-
"cause": f"Unknown error while deleting conversation {conversation_id} : {str(e)}",
541+
"cause": f"Unknown error while deleting conversation {normalized_conv_id} : {str(e)}",
512542
},
513543
) from e
514544

@@ -547,9 +577,12 @@ async def update_conversation_endpoint_handler(
547577
).dump_detail(),
548578
)
549579

580+
# Normalize the conversation ID for database operations (strip conv_ prefix if present)
581+
normalized_conv_id = normalize_conversation_id(conversation_id)
582+
550583
user_id = auth[0]
551584
if not can_access_conversation(
552-
conversation_id,
585+
normalized_conv_id,
553586
user_id,
554587
others_allowed=(
555588
Action.QUERY_OTHERS_CONVERSATIONS in request.state.authorized_actions
@@ -558,66 +591,69 @@ async def update_conversation_endpoint_handler(
558591
logger.warning(
559592
"User %s attempted to update conversation %s they don't have access to",
560593
user_id,
561-
conversation_id,
594+
normalized_conv_id,
562595
)
563596
raise HTTPException(
564597
status_code=status.HTTP_403_FORBIDDEN,
565598
detail=AccessDeniedResponse(
566599
user_id=user_id,
567600
resource="conversation",
568-
resource_id=conversation_id,
601+
resource_id=normalized_conv_id,
569602
action="update",
570603
).dump_detail(),
571604
)
572605

573606
# If reached this, user is authorized to update this conversation
574-
conversation = retrieve_conversation(conversation_id)
607+
conversation = retrieve_conversation(normalized_conv_id)
575608
if conversation is None:
576609
raise HTTPException(
577610
status_code=status.HTTP_404_NOT_FOUND,
578611
detail=NotFoundResponse(
579-
resource="conversation", resource_id=conversation_id
612+
resource="conversation", resource_id=normalized_conv_id
580613
).dump_detail(),
581614
)
582615

583616
logger.info(
584617
"Updating metadata for conversation %s using Conversations API",
585-
conversation_id,
618+
normalized_conv_id,
586619
)
587620

588621
try:
589622
# Get Llama Stack client
590623
client = AsyncLlamaStackClientHolder().get_client()
591624

625+
# Convert to llama-stack format (add 'conv_' prefix if needed)
626+
llama_stack_conv_id = to_llama_stack_conversation_id(normalized_conv_id)
627+
592628
# Prepare metadata with topic summary
593629
metadata = {"topic_summary": update_request.topic_summary}
594630

595631
# Use Conversations API to update the conversation metadata
596632
await client.conversations.update_conversation(
597-
conversation_id=conversation_id,
633+
conversation_id=llama_stack_conv_id,
598634
metadata=metadata,
599635
)
600636

601637
logger.info(
602638
"Successfully updated metadata for conversation %s in LlamaStack",
603-
conversation_id,
639+
normalized_conv_id,
604640
)
605641

606642
# Also update in local database
607643
with get_session() as session:
608644
db_conversation = (
609-
session.query(UserConversation).filter_by(id=conversation_id).first()
645+
session.query(UserConversation).filter_by(id=normalized_conv_id).first()
610646
)
611647
if db_conversation:
612648
db_conversation.topic_summary = update_request.topic_summary
613649
session.commit()
614650
logger.info(
615651
"Successfully updated topic summary in local database for conversation %s",
616-
conversation_id,
652+
normalized_conv_id,
617653
)
618654

619655
return ConversationUpdateResponse(
620-
conversation_id=conversation_id,
656+
conversation_id=normalized_conv_id,
621657
success=True,
622658
message="Topic summary updated successfully",
623659
)
@@ -634,7 +670,7 @@ async def update_conversation_endpoint_handler(
634670
raise HTTPException(
635671
status_code=status.HTTP_404_NOT_FOUND,
636672
detail=NotFoundResponse(
637-
resource="conversation", resource_id=conversation_id
673+
resource="conversation", resource_id=normalized_conv_id
638674
).dump_detail(),
639675
) from e
640676

@@ -643,11 +679,11 @@ async def update_conversation_endpoint_handler(
643679

644680
except Exception as e:
645681
# Handle case where conversation doesn't exist or other errors
646-
logger.exception("Error updating conversation %s: %s", conversation_id, e)
682+
logger.exception("Error updating conversation %s: %s", normalized_conv_id, e)
647683
raise HTTPException(
648684
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
649685
detail={
650686
"response": "Unknown error",
651-
"cause": f"Unknown error while updating conversation {conversation_id} : {str(e)}",
687+
"cause": f"Unknown error while updating conversation {normalized_conv_id} : {str(e)}",
652688
},
653689
) from e

0 commit comments

Comments
 (0)