Skip to content

Conversation

@omertuc
Copy link
Contributor

@omertuc omertuc commented Sep 1, 2025

Description

We saw that when deleting a conversation, it only gets deleted from the llama-stack database, but not from the lightspeed-stack conversations table.

This commit makes it that the conversation is deleted from both places, ensuring it does not show up when the user lists conversations.

Also fix a bug where the conversation ownership validation was not

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement

Related Tickets & Documents

  • Related Issue #
  • Closes #

MGMT-21651

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  • Please provide detailed steps to perform tests related to this code change.
  • How were the fix/results from this change verified? Please provide relevant screenshots or results.

Unit tests have been adjusted. I also tested manually and saw the conversation no longer appearing after deletion.

Summary by CodeRabbit

  • Bug Fixes

    • Deleting a conversation now fully removes it from your account, including local records, preventing lingering entries or inconsistencies in conversation history.
    • Improved reliability of the deletion flow after remote cleanup.
  • Tests

    • Updated unit tests for conversation deletion to prevent side effects and ensure consistent behavior.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 1, 2025

Warning

Rate limit exceeded

@omertuc has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 14 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b91101d and 2733aac.

📒 Files selected for processing (3)
  • src/app/endpoints/conversations.py (4 hunks)
  • src/utils/endpoints.py (1 hunks)
  • tests/unit/app/endpoints/test_conversations.py (1 hunks)

Walkthrough

Adds a local conversation deletion step to the conversations DELETE endpoint. After remote deletion via Llama Stack, the endpoint now calls a new utility function to remove the conversation from local storage. Tests are updated to patch the new utility to avoid side effects.

Changes

Cohort / File(s) Summary
Endpoint deletion flow
src/app/endpoints/conversations.py
Imports delete_conversation and invokes it after successful remote delete in delete_conversation_endpoint_handler. No changes to error handling or responses.
Utils local cleanup
src/utils/endpoints.py
Adds delete_conversation(conversation_id: str) -> None to delete UserConversation by id within a DB session; logs outcome.
Tests for deletion endpoint
tests/unit/app/endpoints/test_conversations.py
Updates test to patch app.endpoints.conversations.delete_conversation to isolate endpoint behavior and prevent DB side effects.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant API as Conversations API
  participant Remote as Llama Stack
  participant Utils as utils.endpoints
  participant DB

  Client->>API: DELETE /conversations/{id}
  API->>Remote: Delete session {id}
  Remote-->>API: 200 OK
  Note over API: Remote deletion succeeded
  API->>Utils: delete_conversation(id)
  Utils->>DB: Find & delete UserConversation(id)
  DB-->>Utils: Commit result
  Utils-->>API: None
  API-->>Client: 200 OK
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • maorfr
  • tisnik

Poem

I nibble the threads where old chats lie,
Snip the cloud cord, then sweep nearby.
Hop-hop—remote gone, local tidy too,
Little paws commit what they must do.
A soft thump: “All clear!”—onward I fly. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

omertuc added a commit to omertuc/assisted-chat that referenced this pull request Sep 1, 2025
See lightspeed-core/lightspeed-stack#476

lightspeed-stack is now also more strict with not accepting non-existent
config, so we have to remove some old config we used to pass that
doesn't exist anymore, otherwise lightspeed-stack would fail to start.
Copy link
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we use user_id in a compound key?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/utils/endpoints.py (1)

21-36: Return a boolean for idempotent feedback

Returning True/False (deleted vs. not found) can improve call-site logging/metrics without extra queries.

-def delete_conversation(conversation_id: str) -> None:
+def delete_conversation(conversation_id: str) -> bool:
@@
-        if db_conversation:
+        if db_conversation:
             session.delete(db_conversation)
             session.commit()
             logger.info("Deleted conversation %s from local database", conversation_id)
-        else:
+            return True
+        else:
             logger.info(
                 "Conversation %s not found in local database, it may have already been deleted",
                 conversation_id,
             )
+            return False
tests/unit/app/endpoints/test_conversations.py (1)

566-568: Assert the local cleanup was invoked

You patch delete_conversation but don’t assert it was called. Add an assertion to ensure the endpoint performs local cleanup.

Apply within the selected lines:

-        mocker.patch("app.endpoints.conversations.delete_conversation")
+        mock_delete = mocker.patch("app.endpoints.conversations.delete_conversation")

Then add this assertion after the response is created:

# Assert local cleanup was called
mock_delete.assert_called_once_with(conversation_id=VALID_CONVERSATION_ID)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 18fdf3c and b91101d.

📒 Files selected for processing (3)
  • src/app/endpoints/conversations.py (2 hunks)
  • src/utils/endpoints.py (1 hunks)
  • tests/unit/app/endpoints/test_conversations.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/utils/endpoints.py (2)
src/app/database.py (1)
  • get_session (34-40)
src/models/database/conversations.py (1)
  • UserConversation (11-36)
src/app/endpoints/conversations.py (1)
src/utils/endpoints.py (1)
  • delete_conversation (21-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-pr
  • GitHub Check: e2e_tests
🔇 Additional comments (1)
src/app/endpoints/conversations.py (1)

23-27: LGTM: import of delete_conversation

Import grouping looks correct and localized to endpoint usage.

See bug MGMT-21651

We saw that when deleting a conversation, it only gets deleted from the
llama-stack database, but not from the lightspeed-stack conversations
table.

This commit makes it that the conversation is deleted from both places,
ensuring it does not show up when the user lists conversations.

Also fix a bug where the conversation ownership validation was not
effective because its return value was ignored.
@omertuc
Copy link
Contributor Author

omertuc commented Sep 2, 2025

don't we use user_id in a compound key?

What do you mean?

@tisnik
Copy link
Contributor

tisnik commented Sep 2, 2025

don't we use user_id in a compound key?

What do you mean?

the user_id and conversation_id used to be a compound key in database. And because user_id can't be mocked (it's taken from the token) it means, that nobody else will be able to delete (or even read) user conversation, even if anybody tries to send bunch of random conversation_ids into the REST API.

@omertuc
Copy link
Contributor Author

omertuc commented Sep 2, 2025

don't we use user_id in a compound key?

What do you mean?

the user_id and conversation_id used to be a compound key in database. And because user_id can't be mocked (it's taken from the token) it means, that nobody else will be able to delete (or even read) user conversation, even if anybody tries to send bunch of random conversation_ids into the REST API.

They're not a compound primary key

@tisnik
Copy link
Contributor

tisnik commented Sep 2, 2025

don't we use user_id in a compound key?

What do you mean?

the user_id and conversation_id used to be a compound key in database. And because user_id can't be mocked (it's taken from the token) it means, that nobody else will be able to delete (or even read) user conversation, even if anybody tries to send bunch of random conversation_ids into the REST API.

They're not a compound primary key

does not it mean than anybody can access any conversation then?

@omertuc
Copy link
Contributor Author

omertuc commented Sep 2, 2025

don't we use user_id in a compound key?

What do you mean?

the user_id and conversation_id used to be a compound key in database. And because user_id can't be mocked (it's taken from the token) it means, that nobody else will be able to delete (or even read) user conversation, even if anybody tries to send bunch of random conversation_ids into the REST API.

They're not a compound primary key

does not it mean than anybody can access any conversation then?

No, we use validate_conversation_ownership

@tisnik
Copy link
Contributor

tisnik commented Sep 2, 2025

don't we use user_id in a compound key?

What do you mean?

the user_id and conversation_id used to be a compound key in database. And because user_id can't be mocked (it's taken from the token) it means, that nobody else will be able to delete (or even read) user conversation, even if anybody tries to send bunch of random conversation_ids into the REST API.

They're not a compound primary key

does not it mean than anybody can access any conversation then?

No, we use validate_conversation_ownership

yup that's good. Also user_id is stored in db and conversation retrieved by:

        existing_conversation = (
            session.query(UserConversation)
            .filter_by(id=conversation_id, user_id=user_id)
            .first()
        )

so it looked like user_id+conversation_id are compound key. Whatever it look ok then

@omertuc
Copy link
Contributor Author

omertuc commented Sep 2, 2025

don't we use user_id in a compound key?

What do you mean?

the user_id and conversation_id used to be a compound key in database. And because user_id can't be mocked (it's taken from the token) it means, that nobody else will be able to delete (or even read) user conversation, even if anybody tries to send bunch of random conversation_ids into the REST API.

They're not a compound primary key

does not it mean than anybody can access any conversation then?

No, we use validate_conversation_ownership

yup that's good. Also user_id is stored in db and conversation retrieved by:

        existing_conversation = (
            session.query(UserConversation)
            .filter_by(id=conversation_id, user_id=user_id)
            .first()
        )

so it looked like user_id+conversation_id are compound key. Whatever it look ok then

Ah I see. Yeah that's a bug, IMO this should only query by conversation_id. I'll do that in a separate PR

Copy link
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tisnik tisnik merged commit c017e78 into lightspeed-core:main Sep 2, 2025
19 checks passed
omertuc added a commit to omertuc/assisted-chat that referenced this pull request Sep 2, 2025
See lightspeed-core/lightspeed-stack#476

lightspeed-stack is now also more strict with not accepting non-existent
config, so we have to remove some old config we used to pass that
doesn't exist anymore, otherwise lightspeed-stack would fail to start.
@omertuc omertuc mentioned this pull request Sep 2, 2025
18 tasks
@omertuc
Copy link
Contributor Author

omertuc commented Sep 2, 2025

don't we use user_id in a compound key?

What do you mean?

the user_id and conversation_id used to be a compound key in database. And because user_id can't be mocked (it's taken from the token) it means, that nobody else will be able to delete (or even read) user conversation, even if anybody tries to send bunch of random conversation_ids into the REST API.

They're not a compound primary key

does not it mean than anybody can access any conversation then?

No, we use validate_conversation_ownership

yup that's good. Also user_id is stored in db and conversation retrieved by:

        existing_conversation = (
            session.query(UserConversation)
            .filter_by(id=conversation_id, user_id=user_id)
            .first()
        )

so it looked like user_id+conversation_id are compound key. Whatever it look ok then

Ah I see. Yeah that's a bug, IMO this should only query by conversation_id. I'll do that in a separate PR

Followup #484

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants