fix: resolve MissingGreenlet crash and add MCP fallback in AI Assistant
Capture user_id/account_id before try block so error handler survives db.rollback() without triggering lazy loads in async context. Add retry-without-MCP fallback when Anthropic MCP server returns rate limit or connection errors. Fixes PYTHON-FASTAPI-3, PYTHON-FASTAPI-4 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -145,11 +145,17 @@ async def post_message(
|
|||||||
|
|
||||||
plan = await get_user_plan(current_user.account_id, db)
|
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:
|
try:
|
||||||
ai_content, suggested_flows, chat = await assistant_chat_service.send_message(
|
ai_content, suggested_flows, chat = await assistant_chat_service.send_message(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
user_id=current_user.id,
|
user_id=user_id,
|
||||||
account_id=current_user.account_id,
|
account_id=account_id,
|
||||||
message=data.message,
|
message=data.message,
|
||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
@@ -159,8 +165,8 @@ async def post_message(
|
|||||||
logger.exception("Assistant chat message failed: %s", e)
|
logger.exception("Assistant chat message failed: %s", e)
|
||||||
await db.rollback()
|
await db.rollback()
|
||||||
await record_ai_usage(
|
await record_ai_usage(
|
||||||
user_id=current_user.id,
|
user_id=user_id,
|
||||||
account_id=current_user.account_id,
|
account_id=account_id,
|
||||||
conversation_id=None,
|
conversation_id=None,
|
||||||
generation_type="assistant_message",
|
generation_type="assistant_message",
|
||||||
tier=plan,
|
tier=plan,
|
||||||
@@ -180,8 +186,8 @@ async def post_message(
|
|||||||
)
|
)
|
||||||
|
|
||||||
await record_ai_usage(
|
await record_ai_usage(
|
||||||
user_id=current_user.id,
|
user_id=user_id,
|
||||||
account_id=current_user.account_id,
|
account_id=account_id,
|
||||||
conversation_id=None,
|
conversation_id=None,
|
||||||
generation_type="assistant_message",
|
generation_type="assistant_message",
|
||||||
tier=plan,
|
tier=plan,
|
||||||
|
|||||||
@@ -189,15 +189,29 @@ async def _call_anthropic_cached(
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
response = await client.beta.messages.create(
|
try:
|
||||||
model=settings.AI_MODEL_ANTHROPIC,
|
response = await client.beta.messages.create(
|
||||||
max_tokens=max_tokens,
|
model=settings.AI_MODEL_ANTHROPIC,
|
||||||
system=system_blocks,
|
max_tokens=max_tokens,
|
||||||
messages=messages,
|
system=system_blocks,
|
||||||
mcp_servers=mcp_servers,
|
messages=messages,
|
||||||
tools=tools,
|
mcp_servers=mcp_servers,
|
||||||
betas=["mcp-client-2025-11-20"],
|
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
|
# Extract text from response — MCP responses can have multiple block
|
||||||
# types (text, mcp_tool_use, mcp_tool_result). We join all text blocks.
|
# types (text, mcp_tool_use, mcp_tool_result). We join all text blocks.
|
||||||
|
|||||||
Reference in New Issue
Block a user