Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions packages/dbgpt-serve/src/dbgpt_serve/agent/agents/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,36 @@ def get_or_build_agent_memory(self, conv_id: str, dbgpts_name: str) -> AgentMemo

return agent_memory

def _cleanup_failed_conversation(self, conv_id: str):
"""Clean up failed conversation and related data.

This method removes all messages and plans associated with a failed conversation,
then deletes the conversation record itself.

Args:
conv_id: The conversation ID to clean up
"""
try:
# Delete messages associated with this conversation
self.gpts_messages_dao.delete_chat_message(conv_id)
logger.info(f"Deleted messages for failed conversation: {conv_id}")
except Exception as e:
logger.warning(f"Failed to delete messages for {conv_id}: {e}")

try:
# Delete plans associated with this conversation
self.memory.plans_memory.remove_by_conv_id(conv_id)
logger.info(f"Deleted plans for failed conversation: {conv_id}")
except Exception as e:
logger.warning(f"Failed to delete plans for {conv_id}: {e}")

try:
# Delete the conversation record
self.gpts_conversations.delete_chat_message(conv_id)
logger.info(f"Deleted failed conversation record: {conv_id}")
Comment on lines +186 to +189
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

The method called to delete the conversation record is self.gpts_conversations.delete_chat_message(conv_id). This is the GptsConversationsDao.delete_chat_message() method, whose name strongly implies it deletes messages — but it actually deletes entries from the gpts_conversations table. The comment on line 187 correctly says "Delete the conversation record", but the method name creates confusion for readers and could lead to maintainability issues. A more appropriately named method (e.g., delete_by_conv_id) should be used or added to the DAO.

Copilot uses AI. Check for mistakes.
except Exception as e:
logger.warning(f"Failed to delete conversation record {conv_id}: {e}")
Comment on lines +172 to +191
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

The gpts_conversations.delete_chat_message(conv_id) call (and similarly gpts_messages_dao.delete_chat_message(conv_id)) uses a LIKE '%conv_id%' pattern inside the DAO, where conv_id is the failed conversation's ID in the format {base_conv_id}_{N} (e.g., "abc123_1").

This LIKE pattern (%abc123_1%) will match not only the target conversation abc123_1, but also any record whose conv_id contains abc123_1 as a substring — for example abc123_10, abc123_11, abc123_19, etc. If the user has had 10+ conversation turns under the same base_conv_id, those higher-numbered conversations (and their associated messages) will be silently deleted when only the failed _1 record should be cleaned up.

To fix this, instead of relying on the LIKE-based delete_chat_message method, a new or existing exact-match deletion method should be used, or the cleanup should use an equality filter (conv_id == failed_conv_id) rather than a substring match.

Copilot uses AI. Check for mistakes.

async def agent_chat_v2(
self,
conv_id: str,
Expand All @@ -184,19 +214,18 @@ async def agent_chat_v2(
f"gpts_conversations count:{conv_id}, "
f"{len(gpts_conversations) if gpts_conversations else 0}"
)
gpt_chat_order = (
"1" if not gpts_conversations else str(len(gpts_conversations) + 1)
)
agent_conv_id = conv_id + "_" + gpt_chat_order
message_round = 0
history_message_count = 0
is_retry_chat = False
last_speaker_name = None
history_messages = None
agent_conv_id = None

# 检查最后一个对话记录是否完成,如果是等待状态,则要继续进行当前对话
if gpts_conversations and len(gpts_conversations) > 0:
last_gpts_conversation: GptsConversationsEntity = gpts_conversations[-1]
logger.info(f"last conversation status:{last_gpts_conversation.__dict__}")
# 如果最后一条记录是WAITING状态,则重试当前对话
if last_gpts_conversation.state == Status.WAITING.value:
is_retry_chat = True
agent_conv_id = last_gpts_conversation.conv_id
Expand Down Expand Up @@ -229,6 +258,32 @@ async def agent_chat_v2(
if not gpt_app:
raise ValueError(f"Not found app {gpts_name}!")

# 如果最后一条记录是RUNNING或FAILED状态,说明上次对话异常中断,
# 需要清理残留数据后再创建新对话
if gpts_conversations and len(gpts_conversations) > 0:
last_gpts_conversation: GptsConversationsEntity = gpts_conversations[-1]
if last_gpts_conversation.state in [
Status.RUNNING.value,
Status.FAILED.value,
]:
logger.warning(
f"Last conversation {last_gpts_conversation.conv_id} is in "
f"{last_gpts_conversation.state} state, cleaning up before "
"starting new conversation"
)
# 清理失败的对话记录及相关数据
self._cleanup_failed_conversation(last_gpts_conversation.conv_id)
# 重新获取对话列表
gpts_conversations = self.gpts_conversations.get_like_conv_id_asc(
conv_id
)

# 生成新的对话ID
gpt_chat_order = (
"1" if not gpts_conversations else str(len(gpts_conversations) + 1)
)
agent_conv_id = conv_id + "_" + gpt_chat_order

## When creating a new gpts conversation record, determine whether to
# include the history of previous topics according to the application
# definition.
Expand Down
Loading