diff --git a/backend/app/api/endpoints/assistant_chat.py b/backend/app/api/endpoints/assistant_chat.py index 83422367..3bcc1f5e 100644 --- a/backend/app/api/endpoints/assistant_chat.py +++ b/backend/app/api/endpoints/assistant_chat.py @@ -145,11 +145,17 @@ async def post_message( plan = await get_user_plan(current_user.account_id, db) + # Capture scalar fields before the try block — after db.rollback() + # the ORM objects are expired and accessing attributes triggers a + # lazy load, which crashes in async context (MissingGreenlet). + user_id = current_user.id + account_id = current_user.account_id + try: ai_content, suggested_flows, chat = await assistant_chat_service.send_message( chat_id=chat_id, - user_id=current_user.id, - account_id=current_user.account_id, + user_id=user_id, + account_id=account_id, message=data.message, db=db, ) @@ -159,8 +165,8 @@ async def post_message( logger.exception("Assistant chat message failed: %s", e) await db.rollback() await record_ai_usage( - user_id=current_user.id, - account_id=current_user.account_id, + user_id=user_id, + account_id=account_id, conversation_id=None, generation_type="assistant_message", tier=plan, @@ -180,8 +186,8 @@ async def post_message( ) await record_ai_usage( - user_id=current_user.id, - account_id=current_user.account_id, + user_id=user_id, + account_id=account_id, conversation_id=None, generation_type="assistant_message", tier=plan, diff --git a/backend/app/services/assistant_chat_service.py b/backend/app/services/assistant_chat_service.py index 86390608..9508fe56 100644 --- a/backend/app/services/assistant_chat_service.py +++ b/backend/app/services/assistant_chat_service.py @@ -189,15 +189,29 @@ async def _call_anthropic_cached( } ] - response = await client.beta.messages.create( - model=settings.AI_MODEL_ANTHROPIC, - max_tokens=max_tokens, - system=system_blocks, - messages=messages, - mcp_servers=mcp_servers, - tools=tools, - betas=["mcp-client-2025-11-20"], - ) + try: + response = await client.beta.messages.create( + model=settings.AI_MODEL_ANTHROPIC, + max_tokens=max_tokens, + system=system_blocks, + messages=messages, + mcp_servers=mcp_servers, + tools=tools, + betas=["mcp-client-2025-11-20"], + ) + except anthropic.BadRequestError as e: + # MCP server failures (rate limits, connection errors) should not + # block the assistant entirely — retry without MCP tools. + if "MCP server" in str(e) and mcp_servers is not anthropic.NOT_GIVEN: + logger.warning("MCP server error, retrying without MCP: %s", e) + response = await client.beta.messages.create( + model=settings.AI_MODEL_ANTHROPIC, + max_tokens=max_tokens, + system=system_blocks, + messages=messages, + ) + else: + raise # Extract text from response — MCP responses can have multiple block # types (text, mcp_tool_use, mcp_tool_result). We join all text blocks.