Skip to content

Conversation

@anish-work
Copy link
Contributor

@anish-work anish-work commented Dec 9, 2025

  • make new method db_msgs_to_api_json
  • fetch attachments and append according to message role
  • send as conversationData in web widget config
  • redirect to base /chat url if no ceoonverstion found

Q/A checklist

  • I have tested my UI changes on mobile and they look acceptable
  • I have tested changes to the workflows in both the API and the UI
  • I have done a code review of my changes and looked at each line of the diff + the references of each function I have changed
  • My changes have not increased the import time of the server
How to check import time?

time python -c 'import server'

You can visualize this using tuna:

python3 -X importtime -c 'import server' 2> out.log && tuna out.log

To measure import time for a specific library:

$ time python -c 'import pandas'

________________________________________________________
Executed in    1.15 secs    fish           external
   usr time    2.22 secs   86.00 micros    2.22 secs
   sys time    0.72 secs  613.00 micros    0.72 secs

To reduce import times, import libraries that take a long time inside the functions that use them instead of at the top of the file:

def my_function():
    import pandas as pd
    ...

Legal Boilerplate

Look, I get it. The entity doing business as “Gooey.AI” and/or “Dara.network” was incorporated in the State of Delaware in 2020 as Dara Network Inc. and is gonna need some rights from me in order to utilize my contributions in this PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Dara Network Inc can use, modify, copy, and redistribute my contributions, under its choice of terms.


Note

Adds a shareable chat route that loads a conversation and prepopulates the web widget with serialized messages (including media, buttons, and references).

  • Routing/UI:
    • Add GET /chat/{integration_name}-{integration_id}/share/{conversation_id}/ to load a shared conversation.
    • Pass enableShareConversation and conversationData to web widget via get_web_widget_embed_code in routers/root.py.
  • Data serialization:
    • New db_msgs_to_api_json in bots/models/convo_msg.py to serialize Messages into API JSON with input_images/audio/documents for users and output_text/images/audio, buttons, references, IDs for assistants.
  • Integrations/Types:
    • Import Conversation and db_msgs_to_api_json in routers/root.py; optional typing for route params; add Q import for attachment filtering.

Written by Cursor Bugbot for commit 5e7fab8. This will update automatically on new commits. Configure here.


@anish-work anish-work requested a review from devxpy December 9, 2025 22:40
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 9, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds db_msgs_to_api_json(msgs: list["Message"]) -> typing.Iterator[dict] to bots/models/convo_msg.py (also imports Q). The function iterates messages in order and yields API-shaped dicts: user messages include input_images, input_audio, input_documents, input_prompt, and created_at; assistant messages parse display_content (via parse_bot_html) and yield raw_output_text, output_text, output_images, output_audio, web_url, user_message_id, bot_message_id (with MSG_ID_PREFIX stripped), references, status="completed", type="final_response", and created_at. routers/root.py adds route /chat/{integration_name}-{integration_id}/share/{conversation_id}/, extends chat_route with conversation_id, loads and serializes the Conversation into conversationData (404 if missing), and embeds it into the frontend widget payload with mode="fullscreen". daras_ai_v2/bot_integration_widgets.py adds enableShareConversation (default False) and a checkbox in the widget config.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review bots/models/convo_msg.py::db_msgs_to_api_json for correct ordering, attachment classification (images/audio/other), handling of multiple/absent attachments, and safe stripping of MSG_ID_PREFIX.
  • Validate parse_bot_html usage and mapping of parsed fields to the emitted payload schema.
  • Check routers/root.py for new route decorator, updated chat_route signature, conversation ID decoding/lookup and 404 behavior, and correct embedding of conversationData with mode="fullscreen".
  • Verify daras_ai_v2/bot_integration_widgets.py changes: flag default, config persistence, and UI checkbox binding.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely summarizes the main change: introducing a shareable conversation route for the web widget.
Description check ✅ Passed The pull request description covers the main objectives, includes all Q/A checklist items marked as complete, and contains required legal boilerplate.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/share-web-widget-conversations

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bff6628 and 489055e.

📒 Files selected for processing (1)
  • routers/root.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,js,ts,tsx,java,cs,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/devs-rules.mdc)

Format code in reverse topological order: place the main() function at the top and dependencies below it

Files:

  • routers/root.py
🧬 Code graph analysis (1)
routers/root.py (1)
bots/models/convo_msg.py (3)
  • db_msgs_to_api_json (600-650)
  • last_n_msgs (330-331)
  • last_n_msgs (456-462)
🪛 Ruff (0.14.8)
routers/root.py

548-548: Unused function argument: integration_name

(ARG001)


565-565: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ 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). (1)
  • GitHub Check: test (3.10.12, 1.8.3)
🔇 Additional comments (3)
routers/root.py (3)

28-31: LGTM!

The imports for Conversation and db_msgs_to_api_json are correctly added to support the new share functionality.


543-550: LGTM on route structure.

The integration_name parameter is intentionally captured from the URL for readable paths (e.g., /chat/my-bot-abc123/share/...), even though it's not used in the function body. This is an acceptable pattern.


577-596: LGTM!

The embed code generation correctly passes conversationData (or None when sharing is disabled) to the widget configuration. The template response structure is properly maintained.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 1

🧹 Nitpick comments (1)
routers/root.py (1)

533-585: Tighten chat share route typing and conversation lookup

The new /share/{conversation_id}/ variant and conversationData wiring look good overall, but a few small tweaks will improve robustness and keep linters happy:

  1. Use explicit optionals for type hints (RUF013)
    Align with the rest of the file (e.g., example_id: str | None) and Ruff’s guidance by making these parameters explicit optionals:
-def chat_route(
-    request: Request,
-    integration_id: str = None,
-    integration_name: str = None,
-    conversation_id: str = None,
-):
+def chat_route(
+    request: Request,
+    integration_id: str | None = None,
+    integration_name: str | None = None,
+    conversation_id: str | None = None,
+):

integration_name remains unused; if Ruff’s ARG001 is enforced, you may want to either prefix it with _ or add a short comment to clarify it’s only there for URL aesthetics.

  1. Make conversation/bot association explicit (hardening)
    To avoid accidentally showing a conversation under the wrong bot if a mismatched conversation_id is passed, you can assert the bot_integration relationship and reuse your existing redirect behavior:
-        try:
-            conversation: Conversation = Conversation.objects.get(
-                id=api_hashids.decode(conversation_id)[0],
-            )
-            mesasges = db_msgs_to_api_json(conversation.last_n_msgs())
+        try:
+            conversation: Conversation = Conversation.objects.get(
+                id=api_hashids.decode(conversation_id)[0],
+            )
+            if conversation.bot_integration_id != bi.id:
+                raise Conversation.DoesNotExist
+
+            messages = db_msgs_to_api_json(conversation.last_n_msgs())
             conversationData = dict(
                 id=conversation_id,
                 bot_id=integration_id,
                 timestamp=conversation.created_at.isoformat(),
                 user_id=conversation.web_user_id,
-                messages=mesasges,
+                messages=messages,
             )
         except (IndexError, Conversation.DoesNotExist):
-            # remove /share/conversation_id from the url and redirect to the root url
-            redirect_url = furl(
-                request.url.path.replace(f"/share/{conversation_id}", "/")
-            )
-            return RedirectResponse(str(redirect_url), status_code=302)
+            # remove /share/{conversation_id} from the URL and redirect to the root URL
+            base_path = request.url.path.split("/share/", 1)[0].rstrip("/") + "/"
+            redirect_url = furl(base_path)
+            return RedirectResponse(str(redirect_url), status_code=302)

This also:

  • Fixes the mesasges typo while keeping behavior the same.
  • Avoids potential // in the redirect path caused by the current replace.

Based on static analysis hints.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a96c0f8 and ff7bfe1.

📒 Files selected for processing (2)
  • bots/models/convo_msg.py (2 hunks)
  • routers/root.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,js,ts,tsx,java,cs,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/devs-rules.mdc)

Format code in reverse topological order: place the main() function at the top and dependencies below it

Files:

  • routers/root.py
  • bots/models/convo_msg.py
🧬 Code graph analysis (1)
routers/root.py (1)
bots/models/convo_msg.py (4)
  • Conversation (124-336)
  • db_msgs_to_api_json (598-685)
  • last_n_msgs (330-331)
  • last_n_msgs (456-462)
🪛 Ruff (0.14.8)
routers/root.py

537-537: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


538-538: Unused function argument: integration_name

(ARG001)


538-538: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


539-539: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

⏰ 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). (1)
  • GitHub Check: test (3.10.12, 1.8.3)
🔇 Additional comments (2)
bots/models/convo_msg.py (1)

12-12: Import of Q for attachment filtering looks correct

The added Q import is required for the input_documents exclusion logic in db_msgs_to_api_json and is consistent with the rest of the module.

routers/root.py (1)

10-13: Direct import of Conversation and db_msgs_to_api_json is appropriate

Pulling Conversation and the new helper directly from bots.models.convo_msg keeps this route’s dependencies explicit and avoids bloating the generic bots.models import.

Comment on lines 598 to 685
def db_msgs_to_api_json(msgs: list["Message"]) -> list[dict]:
from daras_ai_v2.bots import parse_bot_html
from routers.bots_api import MSG_ID_PREFIX

api_messages = [None] * len(msgs)
for i, msg in enumerate(msgs):
msg: Message
if msg.role == CHATML_ROLE_USER:
input_images = (
list(
MessageAttachment.objects.filter(
message=msg, metadata__mime_type__startswith="image/"
).values_list("url", flat=True)
)
or []
)

input_audio = (
list(
MessageAttachment.objects.filter(
message=msg, metadata__mime_type__startswith="audio/"
).values_list("url", flat=True)
)
or ""
)

# any document type other than audio/image
input_documents = (
list(
MessageAttachment.objects.filter(message=msg)
.exclude(
Q(metadata__mime_type__startswith="image/")
| Q(metadata__mime_type__startswith="audio/")
)
.values_list("url", flat=True)
)
or []
)
api_messages[i] = {
"role": msg.role,
"input_prompt": msg.display_content,
"input_images": input_images,
"input_audio": input_audio,
"input_documents": input_documents,
"created_at": msg.created_at.isoformat(),
}
elif msg.role == CHATML_ROLE_ASSISTANT:
output_images = (
list(
MessageAttachment.objects.filter(
message=msg, metadata__mime_type__startswith="image/"
).values_list("url", flat=True)
)
or []
)
output_audio = (
list(
MessageAttachment.objects.filter(
message=msg, metadata__mime_type__startswith="audio/"
).values_list("url", flat=True)
)
or []
)
output_documents = (
list(
MessageAttachment.objects.filter(
message=msg, metadata__mime_type__startswith="application/pdf"
).values_list("url", flat=True)
)
or []
)
buttons, text, _ = parse_bot_html(msg.display_content)
api_messages[i] = {
"role": msg.role,
"created_at": msg.created_at.isoformat(),
"status": "completed",
"type": "final_response",
"raw_output_text": [msg.content],
"output_text": [text],
"buttons": buttons,
"output_images": output_images,
"output_audio": output_audio,
"output_documents": output_documents,
"web_url": msg.saved_run.get_app_url(),
"user_message_id": msg.platform_msg_id,
"bot_message_id": msg.platform_msg_id.strip(MSG_ID_PREFIX),
}
return api_messages
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix None-handling and ID stripping in db_msgs_to_api_json

There are a few correctness issues here that can lead to crashes or inconsistent data:

  • msg.platform_msg_id is nullable, but msg.platform_msg_id.strip(MSG_ID_PREFIX) will raise if it is None, and .strip() removes characters, not a prefix substring, so IDs can be mangled.
  • msg.saved_run is nullable, but msg.saved_run.get_app_url() is called unconditionally and can raise.
  • input_audio falls back to "", while all other attachment fields are lists (and output_audio uses []), which makes the type of input_audio inconsistent.

Suggested minimal fix:

 def db_msgs_to_api_json(msgs: list["Message"]) -> list[dict]:
@@
-            input_audio = (
-                list(
-                    MessageAttachment.objects.filter(
-                        message=msg, metadata__mime_type__startswith="audio/"
-                    ).values_list("url", flat=True)
-                )
-                or ""
-            )
+            input_audio = (
+                list(
+                    MessageAttachment.objects.filter(
+                        message=msg, metadata__mime_type__startswith="audio/"
+                    ).values_list("url", flat=True)
+                )
+                or []
+            )
@@
-            api_messages[i] = {
+            api_messages[i] = {
                 "role": msg.role,
                 "created_at": msg.created_at.isoformat(),
                 "status": "completed",
                 "type": "final_response",
                 "raw_output_text": [msg.content],
                 "output_text": [text],
                 "buttons": buttons,
                 "output_images": output_images,
                 "output_audio": output_audio,
                 "output_documents": output_documents,
-                "web_url": msg.saved_run.get_app_url(),
-                "user_message_id": msg.platform_msg_id,
-                "bot_message_id": msg.platform_msg_id.strip(MSG_ID_PREFIX),
+                "web_url": msg.saved_run.get_app_url() if msg.saved_run else None,
+                "user_message_id": msg.platform_msg_id,
+                "bot_message_id": (
+                    msg.platform_msg_id
+                    and msg.platform_msg_id.removeprefix(MSG_ID_PREFIX)
+                ),
             }

Additionally (non-blocking but recommended), you could reuse msg.attachments instead of repeated MessageAttachment.objects.filter(...) calls to benefit from any existing prefetch_related("attachments") and cut down on per-message queries.

🤖 Prompt for AI Agents
In bots/models/convo_msg.py around lines 598-685, fix three issues: ensure
attachment fields are consistently lists by changing input_audio's fallback to
[] (matching other fields); guard against None for msg.platform_msg_id before
deriving bot_message_id (only strip the MSG_ID_PREFIX if platform_msg_id is
truthy and use a proper prefix-removal, e.g. if
platform_msg_id.startswith(MSG_ID_PREFIX) then remove that slice, otherwise
leave as-is); and guard msg.saved_run before calling get_app_url() (use a
conditional to call get_app_url() only when saved_run is not None and provide a
safe default like None or empty string otherwise). Optionally, replace repeated
MessageAttachment.objects.filter(...) calls with using msg.attachments
(prefetch_related) to reduce queries.

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: 1

🧹 Nitpick comments (2)
routers/root.py (2)

533-540: Tighten chat_route type hints and address Ruff warnings (RUF013, ARG001)

To align with existing style in this file and satisfy Ruff:

  • Avoid implicit Optional by using explicit unions.
  • Treat path parameters as required (no = None) except for conversation_id, which is only present on the /share/... variant.
  • integration_name is unused but required for the path parameter; if you want to silence ARG001 you can either (a) start using it (e.g., in SEO/meta), or (b) add a # noqa: ARG001 comment.

A concise refactor that should work with FastAPI and Ruff:

-@app.get("/chat/{integration_name}-{integration_id}/")
-@app.get("/chat/{integration_name}-{integration_id}/share/{conversation_id}/")
-def chat_route(
-    request: Request,
-    integration_id: str = None,
-    integration_name: str = None,
-    conversation_id: str = None,
-):
+@app.get("/chat/{integration_name}-{integration_id}/")
+@app.get("/chat/{integration_name}-{integration_id}/share/{conversation_id}/")
+def chat_route(
+    request: Request,
+    integration_id: str,
+    integration_name: str,
+    conversation_id: str | None = None,
+):

If you’d like to keep conversation_id strictly required on the /share/ route, you can also drop the | None and default, but the above matches your current logic (if conversation_id:).


574-581: Embedding conversationData into the web widget config looks consistent

Passing conversationData through get_web_widget_embed_code alongside enableShareConversation=True cleanly wires the new server-side behavior into the widget without affecting existing calls when conversation_id is absent (the value will just be None).

If you’d prefer to omit the key entirely when there’s no conversation, you could conditionally build the config dict, but that’s purely stylistic:

-            "embed_code": get_web_widget_embed_code(
-                bi,
-                config=dict(
-                    mode="fullscreen",
-                    enableShareConversation=True,
-                    conversationData=conversationData,
-                ),
-            ),
+            "embed_code": get_web_widget_embed_code(
+                bi,
+                config={
+                    "mode": "fullscreen",
+                    "enableShareConversation": True,
+                    **(
+                        {"conversationData": conversationData}
+                        if conversationData is not None
+                        else {}
+                    ),
+                },
+            ),

Not required, just an option if you want a slightly cleaner JSON shape.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff7bfe1 and 419b7bd.

📒 Files selected for processing (1)
  • routers/root.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,js,ts,tsx,java,cs,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/devs-rules.mdc)

Format code in reverse topological order: place the main() function at the top and dependencies below it

Files:

  • routers/root.py
🪛 Ruff (0.14.8)
routers/root.py

537-537: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


538-538: Unused function argument: integration_name

(ARG001)


538-538: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


539-539: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

⏰ 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: Analyze (python)
  • GitHub Check: test (3.10.12, 1.8.3)
🔇 Additional comments (1)
routers/root.py (1)

10-13: Importing Conversation and db_msgs_to_api_json looks correct

The new imports are minimal, scoped, and used below in chat_route without introducing any circular or unused-import issues.

routers/root.py Outdated
Comment on lines 549 to 568
conversationData = None
if conversation_id:
try:
conversation: Conversation = Conversation.objects.get(
id=api_hashids.decode(conversation_id)[0],
)
mesasges = db_msgs_to_api_json(conversation.last_n_msgs())
conversationData = dict(
id=conversation_id,
bot_id=integration_id,
timestamp=conversation.created_at.isoformat(),
user_id=conversation.web_user_id,
messages=mesasges,
)
except (IndexError, Conversation.DoesNotExist):
# remove /share/conversation_id from the url and redirect to the root url
redirect_url = furl(
request.url.path.replace(f"/share/{conversation_id}", "/")
)
return RedirectResponse(str(redirect_url), status_code=302)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find the routers/root.py file and check its content
fd -type f -name "root.py" | grep -E "routers/root\.py"

Repository: GooeyAI/gooey-server

Length of output: 234


🏁 Script executed:

# Get the file and check its size first
wc -l routers/root.py

Repository: GooeyAI/gooey-server

Length of output: 83


🏁 Script executed:

# Read lines 549-568 to see the actual code
sed -n '549,568p' routers/root.py

Repository: GooeyAI/gooey-server

Length of output: 949


🏁 Script executed:

# Also read some context around it (540-575) to understand the function better
sed -n '540,575p' routers/root.py

Repository: GooeyAI/gooey-server

Length of output: 1475


🏁 Script executed:

# Get the full function to understand context
sed -n '530,575p' routers/root.py

Repository: GooeyAI/gooey-server

Length of output: 1754


🏁 Script executed:

# Check the function signature and decorators
sed -n '520,575p' routers/root.py

Repository: GooeyAI/gooey-server

Length of output: 2028


🏁 Script executed:

# Look for how conversation_id is used elsewhere and what web_user_id represents
rg "web_user_id" routers/root.py -B 2 -A 2

Repository: GooeyAI/gooey-server

Length of output: 264


🏁 Script executed:

# Check what db_msgs_to_api_json returns to understand data structure
rg "def db_msgs_to_api_json" -A 10

Repository: GooeyAI/gooey-server

Length of output: 736


🏁 Script executed:

# Check the Conversation model to understand web_user_id
rg "class Conversation" -A 20 | head -40

Repository: GooeyAI/gooey-server

Length of output: 1985


🏁 Script executed:

# Check if web_user_id is mentioned in security/privacy contexts
rg "web_user_id" -B 3 -A 3

Repository: GooeyAI/gooey-server

Length of output: 5388


🏁 Script executed:

# Look at the structure of the route - check trailing slash handling
sed -n '532,535p' routers/root.py

Repository: GooeyAI/gooey-server

Length of output: 214


Fix redirect path, variable typo, and review privacy exposure in shared conversation endpoint

Three issues need addressing:

  1. Redirect path double slash: The path replacement request.url.path.replace(f"/share/{conversation_id}", "/") will produce a double slash. For a URL like /chat/foo-123/share/abc/, this creates /chat/foo-123// instead of the intended /chat/foo-123/. Update the replacement pattern to f"/share/{conversation_id}/" to account for the trailing slash.

  2. Typo in variable name: Line 554 has mesasges instead of messages, and line 560 references this typo. Rename to messages.

  3. Privacy exposure: The web_user_id (internal user tracking identifier) is exposed in the public share endpoint at /chat/{integration_name}-{integration_id}/share/{conversation_id}/. Anyone with the share link can see this identifier, which reveals who initiated the conversation. Consider whether this user identifier should be included or omitted from the shared conversation data.

Apply the suggested changes:

  • Fix redirect path: replace(f"/share/{conversation_id}/", "/")
  • Rename mesasgesmessages on lines 554 and 560
  • Evaluate whether user_id=conversation.web_user_id should remain in the public response
🤖 Prompt for AI Agents
In routers/root.py around lines 549-568, fix three issues: change the path
replacement to remove the extra slash by using
replace(f"/share/{conversation_id}/", "/") when building redirect_url; rename
the misspelled local variable `mesasges` to `messages` wherever used (assignment
and when building conversationData); and remove or omit the
`user_id=conversation.web_user_id` field from the public `conversationData`
payload (or replace it with a non-identifying placeholder) so the internal
web_user_id is not exposed in the shared response.

@anish-work anish-work force-pushed the feature/share-web-widget-conversations branch from 419b7bd to 7ccdfed Compare December 9, 2025 23:00
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: 0

♻️ Duplicate comments (1)
routers/root.py (1)

549-568: Fix redirect path, variable typo, and avoid exposing web_user_id in shared payload

Three issues here (same as the earlier review, still present in this revision):

  1. Redirect can generate a double slash
    request.url.path.replace(f"/share/{conversation_id}", "/") leaves the trailing /, so /chat/foo-123/share/abc/ becomes /chat/foo-123//. Replace the full "/share/{conversation_id}/" segment instead.

  2. Typo in mesasges variable
    Local name is misspelled, and the same typo is used in the conversationData dict. This is error‑prone and hurts readability.

  3. Privacy: web_user_id is exposed in a public share endpoint
    The share URL is intended to be public; including user_id=conversation.web_user_id leaks an internal tracking identifier about who initiated the conversation. Unless product explicitly requires this, it’s safer to omit it (or replace with a non-identifying label) from conversationData.

Suggested patch:

-    conversationData = None
-    if conversation_id:
-        try:
-            conversation: Conversation = Conversation.objects.get(
-                id=api_hashids.decode(conversation_id)[0],
-            )
-            mesasges = db_msgs_to_api_json(conversation.last_n_msgs())
-            conversationData = dict(
-                id=conversation_id,
-                bot_id=integration_id,
-                timestamp=conversation.created_at.isoformat(),
-                user_id=conversation.web_user_id,
-                messages=mesasges,
-            )
-        except (IndexError, Conversation.DoesNotExist):
-            # remove /share/conversation_id from the url and redirect to the root url
-            redirect_url = furl(
-                request.url.path.replace(f"/share/{conversation_id}", "/")
-            )
-            return RedirectResponse(str(redirect_url), status_code=302)
+    conversationData = None
+    if conversation_id:
+        try:
+            conversation: Conversation = Conversation.objects.get(
+                id=api_hashids.decode(conversation_id)[0],
+            )
+            messages = db_msgs_to_api_json(conversation.last_n_msgs())
+            conversationData = dict(
+                id=conversation_id,
+                bot_id=integration_id,
+                timestamp=conversation.created_at.isoformat(),
+                # user_id intentionally omitted to avoid exposing internal identifiers
+                messages=messages,
+            )
+        except (IndexError, Conversation.DoesNotExist):
+            # remove /share/conversation_id/ from the url and redirect to the root url
+            redirect_url = furl(
+                request.url.path.replace(f"/share/{conversation_id}/", "/")
+            )
+            return RedirectResponse(str(redirect_url), status_code=302)

Additionally (optional but recommended), consider asserting that the loaded conversation actually belongs to bi before building conversationData, and treat mismatches as not found.

🧹 Nitpick comments (1)
routers/root.py (1)

533-539: Tighten type hints and handle Ruff’s ARG001 warning for integration_name

Functionally this signature works, but there are a couple of small cleanups you can make:

  • To satisfy RUF013, consider making the optional parameters explicit, e.g.:
    def chat_route(
        request: Request,
        integration_id: str | None = None,
        integration_name: str | None = None,  # required by the path, but may stay unused
        conversation_id: str | None = None,
    ):
  • integration_name is required for the FastAPI path, but unused in the body, which trips ARG001. Easiest fix is to keep the name (for routing) and add a # noqa: ARG001 comment, or explicitly reference it in logging/metrics if useful.

These are non-blocking, but will keep static analysis clean.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 419b7bd and 7ccdfed.

📒 Files selected for processing (2)
  • bots/models/convo_msg.py (2 hunks)
  • routers/root.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • bots/models/convo_msg.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,js,ts,tsx,java,cs,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/devs-rules.mdc)

Format code in reverse topological order: place the main() function at the top and dependencies below it

Files:

  • routers/root.py
🧬 Code graph analysis (1)
routers/root.py (2)
bots/models/convo_msg.py (4)
  • Conversation (124-336)
  • db_msgs_to_api_json (600-687)
  • last_n_msgs (330-331)
  • last_n_msgs (456-462)
daras_ai_v2/bot_integration_widgets.py (1)
  • get_web_widget_embed_code (364-385)
🪛 Ruff (0.14.8)
routers/root.py

537-537: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


538-538: Unused function argument: integration_name

(ARG001)


538-538: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


539-539: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

⏰ 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). (1)
  • GitHub Check: test (3.10.12, 1.8.3)
🔇 Additional comments (2)
routers/root.py (2)

10-13: Import of Conversation and db_msgs_to_api_json is appropriate and scoped correctly

Both symbols are used only inside chat_route for the share flow; importing them here keeps dependencies local and matches their use below. No issues from a correctness or style perspective.


574-581: Embedding conversationData into widget config looks correct

Passing enableShareConversation=True and the (possibly None) conversationData into get_web_widget_embed_code keeps the existing fullscreen behavior and cleanly extends it for shared conversations. The data remains JSON‑serializable and defaults to null when no conversation is provided, which the frontend can handle.

devxpy
devxpy previously approved these changes Dec 11, 2025
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

"created_at": msg.created_at.isoformat(),
}
elif msg.role == CHATML_ROLE_ASSISTANT:
references = msg.saved_run.state.get("references")
Copy link

Choose a reason for hiding this comment

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

Bug: Null pointer when saved_run is None

The code accesses msg.saved_run.state.get("references") without checking if saved_run is None. The Message.saved_run field is defined with null=True, default=None, so assistant messages may have no associated saved run, which would cause an AttributeError when accessing .state.

Fix in Cursor Fix in Web

"output_audio": audio,
"web_url": msg.saved_run.get_app_url(),
"user_message_id": msg.platform_msg_id,
"bot_message_id": msg.platform_msg_id.strip(MSG_ID_PREFIX),
Copy link

Choose a reason for hiding this comment

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

Bug: Null pointer when platform_msg_id is None

The code calls msg.platform_msg_id.strip(MSG_ID_PREFIX) without checking if platform_msg_id is None. The Message.platform_msg_id field is defined with null=True, default=None, so this will raise an AttributeError when the field is None.

Fix in Cursor Fix in Web

try:
conversation: Conversation = Conversation.objects.get(
id=api_hashids.decode(conversation_id)[0],
)
Copy link

Choose a reason for hiding this comment

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

Bug: Conversation access lacks bot integration authorization check

The conversation is fetched only by its ID without verifying it belongs to the bi (bot integration) specified in the URL. Since Conversation has a bot_integration foreign key, a malicious user could craft a share URL with one integration's ID but another conversation's ID, potentially exposing private conversation data from a different integration. The query needs to filter by bot_integration=bi as well.

Fix in Cursor Fix in Web

"output_audio": audio,
"web_url": msg.saved_run.get_app_url(),
"user_message_id": msg.platform_msg_id,
"bot_message_id": msg.platform_msg_id.strip(MSG_ID_PREFIX),
Copy link

Choose a reason for hiding this comment

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

Bug: Wrong string method strips characters instead of prefix

The code uses str.strip(MSG_ID_PREFIX) but strip() removes all characters in the argument from both ends of the string, not a prefix. With MSG_ID_PREFIX = "web-", this removes any 'w', 'e', 'b', or '-' characters from both ends rather than just the "web-" prefix. Other places in the codebase correctly use .removeprefix() or .lstrip() for this purpose.

Fix in Cursor Fix in Web

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: 0

♻️ Duplicate comments (2)
bots/models/convo_msg.py (1)

600-650: Critical issues from previous review remain unfixed.

The same critical None-handling and ID-stripping issues flagged in the previous review are still present:

  1. Line 634: msg.saved_run.state.get("references") will raise AttributeError if msg.saved_run is None (nullable field).
  2. Line 646: msg.saved_run.get_app_url() will raise AttributeError if msg.saved_run is None.
  3. Line 648: msg.platform_msg_id.strip(MSG_ID_PREFIX) will raise AttributeError if msg.platform_msg_id is None, and .strip() removes characters not prefixes, potentially mangling IDs.
  4. Line 616: audio = audios and audios[0] produces None or False when empty, inconsistent with other attachment fields that are lists.

Apply this diff to fix the issues:

 def db_msgs_to_api_json(msgs: list["Message"]) -> typing.Iterator[dict]:
     from daras_ai_v2.bots import parse_bot_html
     from routers.bots_api import MSG_ID_PREFIX
 
     for msg in msgs:
         msg: Message
         images = list(
             msg.attachments.filter(
                 metadata__mime_type__startswith="image/"
             ).values_list("url", flat=True)
         )
         audios = list(
             msg.attachments.filter(
                 metadata__mime_type__startswith="audio/"
             ).values_list("url", flat=True)
         )
-        audio = audios and audios[0]
+        audio = audios[0] if audios else None
         if msg.role == CHATML_ROLE_USER:
             # any document type other than audio/image
             documents = list(
                 msg.attachments.exclude(
                     Q(metadata__mime_type__startswith="image/")
                     | Q(metadata__mime_type__startswith="audio/")
                 ).values_list("url", flat=True)
             )
             yield {
                 "role": msg.role,
                 "input_prompt": msg.display_content,
                 "input_images": images,
                 "input_audio": audio,
                 "input_documents": documents,
                 "created_at": msg.created_at.isoformat(),
             }
         elif msg.role == CHATML_ROLE_ASSISTANT:
-            references = msg.saved_run.state.get("references")
+            references = msg.saved_run.state.get("references") if msg.saved_run else None
             buttons, text, _ = parse_bot_html(msg.display_content)
             yield {
                 "role": msg.role,
                 "created_at": msg.created_at.isoformat(),
                 "status": "completed",
                 "type": "final_response",
                 "raw_output_text": [msg.content],
                 "output_text": [text],
                 "buttons": buttons,
                 "output_images": images,
                 "output_audio": audio,
-                "web_url": msg.saved_run.get_app_url(),
+                "web_url": msg.saved_run.get_app_url() if msg.saved_run else None,
                 "user_message_id": msg.platform_msg_id,
-                "bot_message_id": msg.platform_msg_id.strip(MSG_ID_PREFIX),
+                "bot_message_id": (
+                    msg.platform_msg_id.removeprefix(MSG_ID_PREFIX)
+                    if msg.platform_msg_id
+                    else None
+                ),
                 "references": references,
             }
routers/root.py (1)

549-565: Fix typo, add authorization check, and address privacy/exception handling.

Multiple issues need attention:

  1. Line 556 (typo from previous review): Variable misspelled as mesasges instead of messages (also line 562).

  2. Critical security issue: No validation that the conversation belongs to this bot integration. Anyone with a conversation_id can view conversations from ANY bot by accessing them through a different bot's share URL. Add authorization check: if conversation.bot_integration_id != bi.id: raise HTTPException(status_code=404).

  3. Line 561 (privacy from previous review): web_user_id is an internal tracking identifier exposed in public share URLs. Consider whether this should be included or replaced with a non-identifying placeholder.

  4. Line 555 (static analysis): Exception handling should use raise ... from err or raise ... from None for proper exception chaining.

Apply this diff:

     if conversation_id:
         try:
             conversation: Conversation = Conversation.objects.get(
                 id=api_hashids.decode(conversation_id)[0],
             )
-        except (IndexError, Conversation.DoesNotExist):
+        except (IndexError, Conversation.DoesNotExist) as e:
-            raise HTTPException(status_code=404)
+            raise HTTPException(status_code=404) from e
+        
+        # Validate conversation belongs to this bot integration
+        if conversation.bot_integration_id != bi.id:
+            raise HTTPException(status_code=404)
+        
-        mesasges = list(db_msgs_to_api_json(conversation.last_n_msgs()))
+        messages = list(db_msgs_to_api_json(conversation.last_n_msgs()))
         conversation_data = dict(
             id=conversation_id,
             bot_id=integration_id,
             timestamp=conversation.created_at.isoformat(),
-            user_id=conversation.web_user_id,
+            user_id=conversation.web_user_id,  # TODO: Consider privacy implications
-            messages=mesasges,
+            messages=messages,
         )
     else:
         conversation_data = None
🧹 Nitpick comments (1)
routers/root.py (1)

535-540: Unused parameter may be intentional for URL routing.

Static analysis flags integration_name as unused in the function body. While the parameter isn't used in the logic, it's likely required in the URL path for human-readable routes (e.g., /chat/my-bot-abc123/ vs /chat/abc123/). If this is intentional, consider suppressing the warning or adding a comment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e4a7e2 and 5e7fab8.

📒 Files selected for processing (2)
  • bots/models/convo_msg.py (2 hunks)
  • routers/root.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,js,ts,tsx,java,cs,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/devs-rules.mdc)

Format code in reverse topological order: place the main() function at the top and dependencies below it

Files:

  • bots/models/convo_msg.py
  • routers/root.py
🧬 Code graph analysis (2)
bots/models/convo_msg.py (2)
daras_ai_v2/bots.py (1)
  • parse_bot_html (339-368)
bots/models/saved_run.py (1)
  • get_app_url (172-178)
routers/root.py (1)
bots/models/convo_msg.py (4)
  • Conversation (124-336)
  • db_msgs_to_api_json (600-650)
  • last_n_msgs (330-331)
  • last_n_msgs (456-462)
🪛 Ruff (0.14.8)
routers/root.py

538-538: Unused function argument: integration_name

(ARG001)


555-555: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ 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). (3)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Analyze (python)
  • GitHub Check: test (3.10.12, 1.8.3)
🔇 Additional comments (4)
bots/models/convo_msg.py (1)

12-12: LGTM: Import addition supports new functionality.

The Q import is correctly used in the new db_msgs_to_api_json function for building complex query filters.

routers/root.py (3)

27-30: LGTM: Imports support new conversation-sharing feature.

The Conversation model and db_msgs_to_api_json function are correctly imported for the new share route functionality.


534-534: LGTM: Share route pattern is consistent.

The new route path /chat/{integration_name}-{integration_id}/share/{conversation_id}/ follows existing URL conventions and enables conversation sharing.


572-579: LGTM: Widget configuration correctly enables sharing.

The get_web_widget_embed_code call properly passes conversationData when present, enabling the share conversation feature in fullscreen mode.

anish-work and others added 4 commits December 15, 2025 14:50
- make new method db_msgs_to_api_json
- fetch attachments and append according to message role
- send as conversationData in web widget config
- redirect to base /chat url if no ceoonverstion found
@anish-work anish-work force-pushed the feature/share-web-widget-conversations branch from 5e7fab8 to e2411ef Compare December 15, 2025 09:20
@devxpy devxpy dismissed their stale review December 15, 2025 09:21

re-review changes

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: 0

♻️ Duplicate comments (3)
bots/models/convo_msg.py (1)

12-12: Fix None-handling, ID prefix stripping, and audio typing in db_msgs_to_api_json

The new serializer is close, but there are some correctness and robustness issues:

  1. saved_run is nullable, but you access it unconditionally:
  • Line 634: references = msg.saved_run.state.get("references")
  • Line 646: web_url = msg.saved_run.get_app_url()

If an assistant Message has saved_run=None, both lines will raise AttributeError. Please guard:

@@
-        elif msg.role == CHATML_ROLE_ASSISTANT:
-            references = msg.saved_run.state.get("references")
+        elif msg.role == CHATML_ROLE_ASSISTANT:
+            saved_run = msg.saved_run
+            references = saved_run.state.get("references") if saved_run else None
@@
-                "output_audio": audio,
-                "web_url": msg.saved_run.get_app_url(),
-                "user_message_id": msg.platform_msg_id,
-                "bot_message_id": msg.platform_msg_id.strip(MSG_ID_PREFIX),
+                "output_audio": audio,
+                "web_url": saved_run.get_app_url() if saved_run else None,
+                "user_message_id": msg.platform_msg_id,
+                "bot_message_id": (
+                    msg.platform_msg_id
+                    and msg.platform_msg_id.removeprefix(MSG_ID_PREFIX)
+                ),
  1. platform_msg_id is nullable and .strip() is the wrong operation:
  • If msg.platform_msg_id is None, msg.platform_msg_id.strip(...) (line 648) raises.
  • strip(MSG_ID_PREFIX) removes any of the characters from both ends, not the literal "web-" prefix, so IDs can be silently mangled.

Switch to guarded removeprefix as in MessageQuerySet.to_json (see lines 433–436), as shown above.

  1. Audio handling is subtly type-unstable:
  • audio = audios and audios[0] (line 616) yields:
    • a URL string when there is audio,
    • but [] when there is none.
  • This means input_audio/output_audio can be either str or list, unlike the other attachment fields.

Consider making the fallback explicit and stable, e.g.:

-        audios = list(
+        audios = list(
             msg.attachments.filter(
                 metadata__mime_type__startswith="audio/"
             ).values_list("url", flat=True)
         )
-        audio = audios and audios[0]
+        audio = audios[0] if audios else None

or, if the API expects lists for audio as well, use audios consistently for both input and output.

  1. Optional perf improvement: last_n_msgs() already uses prefetch_related("attachments"); calling msg.attachments.filter(...) / .exclude(...) for each message will still hit the DB. For better performance, you could use the prefetched msg.attachments.all() and filter in Python, especially if many messages are serialized at once.

To confirm the strip vs removeprefix behavior and the audios and audios[0] edge case, you can run:

# strip vs removeprefix
s = "web-abc"
print(s.strip("web-"))        # removes any of 'w','e','b','-' from both ends
print(s.removeprefix("web-")) # removes literal prefix only

# audio expression
audios = []
print(audios and audios[0])   # []
audios = ["url"]
print(audios and audios[0])   # "url"

Also applies to: 600-651

routers/root.py (2)

559-565: Scope shared conversation lookup to the current bot integration

Right now the shared Conversation is fetched only by its ID:

conversation: Conversation = Conversation.objects.get(
    id=api_hashids.decode(conversation_id)[0],
)

This does not verify that the conversation belongs to the bi (bot integration) resolved earlier, so a crafted URL could mix an integration’s slug/ID with an unrelated conversation’s ID and potentially expose another integration’s data.

Filter by bot_integration as well:

-        try:
-            conversation: Conversation = Conversation.objects.get(
-                id=api_hashids.decode(conversation_id)[0],
-            )
+        try:
+            conversation: Conversation = Conversation.objects.get(
+                id=api_hashids.decode(conversation_id)[0],
+                bot_integration=bi,
+            )

This keeps shared conversations confined to their owning integration.

You can verify there are no other unconstrained Conversation.objects.get(id=...) lookups tied to integrations with:

#!/bin/bash
rg -n "Conversation\.objects\.get\(" -C2

567-572: Avoid exposing web_user_id in public conversationData

The share route is publicly accessible, but conversation_data includes:

conversation_data = dict(
    id=conversation_id,
    bot_id=integration_id,
    timestamp=conversation.created_at.isoformat(),
    user_id=conversation.web_user_id,
    messages=mesasges,
)

web_user_id is an internal, per-user identifier on Conversation and may tie the shared transcript back to a specific user account. For a public share link this is likely unnecessary and a privacy risk.

Consider either:

  • Omitting the field entirely, or
  • Replacing it with a non-identifying placeholder (e.g., a random or hashed share ID), only if the frontend truly needs a stable identifier.

Example minimal change:

-            timestamp=conversation.created_at.isoformat(),
-            user_id=conversation.web_user_id,
-            messages=mesasges,
+            timestamp=conversation.created_at.isoformat(),
+            messages=mesasges,

If you’re unsure whether user_id is required on the frontend, search for its usage:

#!/bin/bash
rg -n "\"user_id\"" -S
🧹 Nitpick comments (1)
routers/root.py (1)

545-549: Fix typo in mesasges and address unused parameter

Three smaller improvements worth making:

  1. Typo in variable name (line 566):
-        mesasges = list(db_msgs_to_api_json(conversation.last_n_msgs()))
+        messages = list(db_msgs_to_api_json(conversation.last_n_msgs()))

Also update the reference on line 571 from messages=mesasges, to messages=messages,.

  1. Unused integration_name parameter (line 545):

The integration_name parameter is accepted but never used. To satisfy linters, either use it or rename to _integration_name:

-    integration_name: str | None = None,
+    _integration_name: str | None = None,
  1. Optional: explicit exception chaining (lines 555 and 567):

Add from None to distinguish intentional exception suppression from implicit chaining:

-        except (IndexError, BotIntegration.DoesNotExist):
-            raise HTTPException(status_code=404)
+        except (IndexError, BotIntegration.DoesNotExist):
+            raise HTTPException(status_code=404) from None

Apply the same pattern to the Conversation.DoesNotExist handler.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e7fab8 and e2411ef.

📒 Files selected for processing (2)
  • bots/models/convo_msg.py (2 hunks)
  • routers/root.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,js,ts,tsx,java,cs,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/devs-rules.mdc)

Format code in reverse topological order: place the main() function at the top and dependencies below it

Files:

  • bots/models/convo_msg.py
  • routers/root.py
🧬 Code graph analysis (2)
bots/models/convo_msg.py (2)
daras_ai_v2/bots.py (1)
  • parse_bot_html (339-368)
bots/models/saved_run.py (1)
  • get_app_url (172-178)
routers/root.py (2)
bots/models/convo_msg.py (4)
  • Conversation (124-336)
  • db_msgs_to_api_json (600-650)
  • last_n_msgs (330-331)
  • last_n_msgs (456-462)
daras_ai_v2/bot_integration_widgets.py (1)
  • get_web_widget_embed_code (364-385)
🪛 Ruff (0.14.8)
routers/root.py

548-548: Unused function argument: integration_name

(ARG001)


565-565: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ 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). (1)
  • GitHub Check: test (3.10.12, 1.8.3)
🔇 Additional comments (1)
routers/root.py (1)

28-31: New imports from bots.models.convo_msg look good

Importing Conversation and db_msgs_to_api_json here is appropriate and keeps the share-route logic local to root.py. No issues from a dependency or import-time perspective.

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: 0

♻️ Duplicate comments (1)
routers/root.py (1)

559-573: Critical: Add bot_integration filter to prevent IDOR vulnerability; fix typo.

The conversation lookup fetches by ID only without verifying it belongs to the specified bot integration. An attacker could access conversations from other integrations by using a valid conversation_id from a different integration_id.

Additionally, mesasges should be messages.

Apply this diff to fix both issues:

     if conversation_id:
         try:
             conversation: Conversation = Conversation.objects.get(
                 id=api_hashids.decode(conversation_id)[0],
+                bot_integration=bi,
             )
         except (IndexError, Conversation.DoesNotExist):
             raise HTTPException(status_code=404)
-        mesasges = list(db_msgs_to_api_json(conversation.last_n_msgs()))
+        messages = list(db_msgs_to_api_json(conversation.last_n_msgs()))
         conversation_data = dict(
             id=conversation_id,
             bot_id=integration_id,
             timestamp=conversation.created_at.isoformat(),
             user_id=conversation.web_user_id,
-            messages=mesasges,
+            messages=messages,
         )
🧹 Nitpick comments (1)
routers/root.py (1)

564-565: Use raise ... from None for proper exception chaining.

Per static analysis hint, distinguish the 404 from an error in exception handling.

         except (IndexError, Conversation.DoesNotExist):
-            raise HTTPException(status_code=404)
+            raise HTTPException(status_code=404) from None
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2411ef and bff6628.

📒 Files selected for processing (3)
  • bots/models/convo_msg.py (2 hunks)
  • daras_ai_v2/bot_integration_widgets.py (2 hunks)
  • routers/root.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • bots/models/convo_msg.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,js,ts,tsx,java,cs,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/devs-rules.mdc)

Format code in reverse topological order: place the main() function at the top and dependencies below it

Files:

  • daras_ai_v2/bot_integration_widgets.py
  • routers/root.py
🧬 Code graph analysis (1)
routers/root.py (2)
bots/models/convo_msg.py (3)
  • Conversation (124-336)
  • last_n_msgs (330-331)
  • last_n_msgs (456-462)
daras_ai_v2/bot_integration_widgets.py (1)
  • get_web_widget_embed_code (364-385)
🪛 Ruff (0.14.8)
routers/root.py

548-548: Unused function argument: integration_name

(ARG001)


565-565: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ 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: Analyze (python)
  • GitHub Check: test (3.10.12, 1.8.3)
🔇 Additional comments (5)
daras_ai_v2/bot_integration_widgets.py (1)

454-476: LGTM!

The enableShareConversation feature flag and UI checkbox follow the established pattern for other widget configuration options. The default of False is a safe choice for a new sharing feature.

routers/root.py (4)

28-31: LGTM!

The new imports are correctly scoped for the share route functionality.


543-550: LGTM!

The route pattern and signature updates are appropriate. The integration_name parameter, while flagged as unused by static analysis, serves a valid purpose in the URL structure for readability and is consistent with the existing pattern on line 543.


571-571: Verify: Should web_user_id be exposed in shared conversations?

The web_user_id is an internal user tracking identifier. Including it in the public share response reveals who initiated the conversation. Consider whether this aligns with privacy expectations for shared conversations, or if it should be omitted/anonymized.


582-588: LGTM!

The embed code generation correctly passes mode="fullscreen" and includes conversationData only when a conversation is being shared. The fallback to None preserves existing behavior.

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.

3 participants