(feat): add support for llm based inital turn#844
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughAdds a ChangesInitial-Turn SSE Streaming
Sequence Diagram(s)sequenceDiagram
participant Browser
participant WidgetRouter
participant send_widget_initial_turn_handler
participant send_chat_initial_turn_handler
participant ChatAgent
Browser->>WidgetRouter: POST /widget/session/{id}/initial-turn
WidgetRouter->>send_widget_initial_turn_handler: session_id, request, WidgetSessionContext
send_widget_initial_turn_handler->>send_widget_initial_turn_handler: widget active, IP limit, ownership, ended, CHAT channel checks
send_widget_initial_turn_handler->>send_chat_initial_turn_handler: session_id, access_check=None
send_chat_initial_turn_handler->>send_chat_initial_turn_handler: Redis lock acquire (409 on contention)
send_chat_initial_turn_handler->>send_chat_initial_turn_handler: load session, template, history, agent state, template_vars
send_chat_initial_turn_handler->>ChatAgent: run_initial_turn(current_node)
ChatAgent->>ChatAgent: allocate aiohttp_session, mcp_pool, prepare tools
ChatAgent->>ChatAgent: seed_resume_context, _cycle_loop
ChatAgent-->>Browser: SSE events streamed via StreamingResponse
ChatAgent->>ChatAgent: finally — close aiohttp_session, mcp_pool
send_chat_initial_turn_handler->>send_chat_initial_turn_handler: finally — release Redis lock (if not handed off)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
app/api/routers/breeze_buddy/widget/handlers.py (1)
301-305: ⚡ Quick winAdd a return type hint to the new handler signature.
send_widget_initial_turn_handleris missing an explicit return annotation.As per coding guidelines, “Add type hints on all function signatures.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/routers/breeze_buddy/widget/handlers.py` around lines 301 - 305, The function `send_widget_initial_turn_handler` is missing an explicit return type annotation in its signature. Add a return type hint after the closing parenthesis of the parameter list using the arrow notation (->). Determine the appropriate return type based on what the function actually returns and add it to comply with the coding guidelines requiring type hints on all function signatures.Source: Coding guidelines
app/api/routers/breeze_buddy/widget/__init__.py (1)
154-159: ⚡ Quick winAdd a return type hint to the route handler.
send_widget_initial_turnshould declare its return type (e.g.,Response/StreamingResponse) for consistency with the typed router surface.As per coding guidelines, “Add type hints on all function signatures.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/routers/breeze_buddy/widget/__init__.py` around lines 154 - 159, The async function `send_widget_initial_turn` is missing a return type hint in its function signature. Add an appropriate return type annotation (such as Response or StreamingResponse) between the closing parenthesis of the parameter list and the colon for consistency with the typed router surface and to comply with the coding guidelines requiring type hints on all function signatures.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/api/routers/breeze_buddy/chat/handlers.py`:
- Around line 703-717: The issue is that run_initial_turn is being called
unconditionally even when the session already has message history, which can
cause duplicate writes and side effects on session retries. After fetching
history_rows using list_chat_messages_for_session around line 703, add a
conditional check before the run_initial_turn call to only execute it when
history_rows is empty. Wrap the run_initial_turn invocation in an if statement
that verifies the session is fresh by checking if the history list is empty,
ensuring initial-turn logic only runs on new sessions.
In `@app/api/routers/breeze_buddy/widget/handlers.py`:
- Around line 327-351: The call to send_chat_initial_turn_handler with
access_check=None skips re-validation of widget ownership and channel status
under the chat lock, creating a race condition where a concurrent channel switch
to VOICE can bypass the pre-check. Instead of passing access_check=None, provide
a validation function that re-checks the widget session ownership and
current_channel status under the chat lock to ensure the session is still on the
CHAT channel before starting the SSE stream.
---
Nitpick comments:
In `@app/api/routers/breeze_buddy/widget/__init__.py`:
- Around line 154-159: The async function `send_widget_initial_turn` is missing
a return type hint in its function signature. Add an appropriate return type
annotation (such as Response or StreamingResponse) between the closing
parenthesis of the parameter list and the colon for consistency with the typed
router surface and to comply with the coding guidelines requiring type hints on
all function signatures.
In `@app/api/routers/breeze_buddy/widget/handlers.py`:
- Around line 301-305: The function `send_widget_initial_turn_handler` is
missing an explicit return type annotation in its signature. Add a return type
hint after the closing parenthesis of the parameter list using the arrow
notation (->). Determine the appropriate return type based on what the function
actually returns and add it to comply with the coding guidelines requiring type
hints on all function signatures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 98dfd63e-8bb1-4253-92c0-c4dc6590391a
📒 Files selected for processing (4)
app/ai/voice/agents/breeze_buddy/chat/agent.pyapp/api/routers/breeze_buddy/chat/handlers.pyapp/api/routers/breeze_buddy/widget/__init__.pyapp/api/routers/breeze_buddy/widget/handlers.py
| history_limit = await CHAT_HISTORY_REPLAY_LIMIT() | ||
| history_rows = await list_chat_messages_for_session( | ||
| session_id, limit=history_limit | ||
| ) | ||
| history: list = blocks_to_llm_context_messages( | ||
| [ | ||
| { | ||
| "role": row.role.value, | ||
| "content": row.content, | ||
| "content_blocks": row.content_blocks, | ||
| } | ||
| for row in history_rows | ||
| if row.role in (ChatMessageRole.USER, ChatMessageRole.ASSISTANT) | ||
| ] | ||
| ) |
There was a problem hiding this comment.
Block /initial-turn once the session already has messages.
Line 703 already fetches message history, but Line 755 still runs run_initial_turn(...) unconditionally. Since initial-turn runs a normal LLM/tool cycle, retries on non-fresh sessions can duplicate writes/side effects.
💡 Suggested fix
history_rows = await list_chat_messages_for_session(
session_id, limit=history_limit
)
- history: list = blocks_to_llm_context_messages(
- [
- {
- "role": row.role.value,
- "content": row.content,
- "content_blocks": row.content_blocks,
- }
- for row in history_rows
- if row.role in (ChatMessageRole.USER, ChatMessageRole.ASSISTANT)
- ]
- )
+ if any(
+ row.role in (ChatMessageRole.USER, ChatMessageRole.ASSISTANT)
+ for row in history_rows
+ ):
+ raise HTTPException(
+ status_code=status.HTTP_409_CONFLICT,
+ detail=(
+ "Initial turn already started for this session. "
+ "Use /message for subsequent turns."
+ ),
+ )Also applies to: 750-757
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/api/routers/breeze_buddy/chat/handlers.py` around lines 703 - 717, The
issue is that run_initial_turn is being called unconditionally even when the
session already has message history, which can cause duplicate writes and side
effects on session retries. After fetching history_rows using
list_chat_messages_for_session around line 703, add a conditional check before
the run_initial_turn call to only execute it when history_rows is empty. Wrap
the run_initial_turn invocation in an if statement that verifies the session is
fresh by checking if the history list is empty, ensuring initial-turn logic only
runs on new sessions.
| # Cheap pre-check before delegating — the chat handler will reload | ||
| # the session under its own lock, but we want a clean 409 for | ||
| # "voice is live" before the SSE stream opens. | ||
| session = await get_chat_session_by_id(session_id) | ||
| if session is None: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_404_NOT_FOUND, | ||
| detail=f"Widget session '{session_id}' not found", | ||
| ) | ||
| assert_widget_session_ownership(session, ctx) | ||
| if session.status == ChatSessionStatus.ENDED: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_410_GONE, | ||
| detail=f"Widget session '{session_id}' has ended", | ||
| ) | ||
| if session.current_channel != WidgetChannel.CHAT: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_409_CONFLICT, | ||
| detail=( | ||
| f"Widget session is on channel {session.current_channel.value}; " | ||
| "end the voice attachment first." | ||
| ), | ||
| ) | ||
|
|
||
| return await send_chat_initial_turn_handler(session_id, access_check=None) |
There was a problem hiding this comment.
Re-check widget ownership/channel under the chat lock, not only before delegation.
The pre-check is done before lock acquisition, but Line 351 passes access_check=None, so locked revalidation is skipped. A concurrent channel switch to VOICE can slip through and still start SSE.
💡 Suggested fix
+ def _locked_access_check(fresh: ChatSession) -> None:
+ assert_widget_session_ownership(fresh, ctx)
+ if fresh.current_channel != WidgetChannel.CHAT:
+ raise HTTPException(
+ status_code=status.HTTP_409_CONFLICT,
+ detail=(
+ f"Widget session is on channel {fresh.current_channel.value}; "
+ "end the voice attachment first."
+ ),
+ )
+
- return await send_chat_initial_turn_handler(session_id, access_check=None)
+ return await send_chat_initial_turn_handler(
+ session_id, access_check=_locked_access_check
+ )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/api/routers/breeze_buddy/widget/handlers.py` around lines 327 - 351, The
call to send_chat_initial_turn_handler with access_check=None skips
re-validation of widget ownership and channel status under the chat lock,
creating a race condition where a concurrent channel switch to VOICE can bypass
the pre-check. Instead of passing access_check=None, provide a validation
function that re-checks the widget session ownership and current_channel status
under the chat lock to ensure the session is still on the CHAT channel before
starting the SSE stream.
bc32c87 to
70d2205
Compare
Summary by CodeRabbit
Release Notes