Skip to content
Merged
Show file tree
Hide file tree
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
83 changes: 49 additions & 34 deletions apps/application/flow/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ def to_stream_response_simple(stream_event):
"""



def generate_tool_message_complete(name, input_content, output_content):
"""生成包含输入和输出的工具消息模版"""
# 格式化输入
Expand Down Expand Up @@ -268,39 +267,55 @@ def run_forever():


async def _yield_mcp_response(chat_model, message_list, mcp_servers, mcp_output_enable=True):
client = MultiServerMCPClient(json.loads(mcp_servers))
tools = await client.get_tools()
agent = create_react_agent(chat_model, tools)
response = agent.astream({"messages": message_list}, stream_mode='messages')

# 用于存储工具调用信息
tool_calls_info = {}

async for chunk in response:
if isinstance(chunk[0], AIMessageChunk):
tool_calls = chunk[0].additional_kwargs.get('tool_calls', [])
for tool_call in tool_calls:
tool_id = tool_call.get('id', '')
if tool_id:
# 保存工具调用的输入
tool_calls_info[tool_id] = {
'name': tool_call.get('function', {}).get('name', ''),
'input': tool_call.get('function', {}).get('arguments', '')
}
yield chunk[0]

if mcp_output_enable and isinstance(chunk[0], ToolMessage):
tool_id = chunk[0].tool_call_id
if tool_id in tool_calls_info:
# 合并输入和输出
tool_info = tool_calls_info[tool_id]
content = generate_tool_message_complete(
tool_info['name'],
tool_info['input'],
chunk[0].content
)
chunk[0].content = content
yield chunk[0]
try:
client = MultiServerMCPClient(json.loads(mcp_servers))
tools = await client.get_tools()
agent = create_react_agent(chat_model, tools)
response = agent.astream({"messages": message_list}, stream_mode='messages')

# 用于存储工具调用信息
tool_calls_info = {}

async for chunk in response:
if isinstance(chunk[0], AIMessageChunk):
tool_calls = chunk[0].additional_kwargs.get('tool_calls', [])
for tool_call in tool_calls:
tool_id = tool_call.get('id', '')
if tool_id:
# 保存工具调用的输入
tool_calls_info[tool_id] = {
'name': tool_call.get('function', {}).get('name', ''),
'input': tool_call.get('function', {}).get('arguments', '')
}
yield chunk[0]

if mcp_output_enable and isinstance(chunk[0], ToolMessage):
tool_id = chunk[0].tool_call_id
if tool_id in tool_calls_info:
# 合并输入和输出
tool_info = tool_calls_info[tool_id]
content = generate_tool_message_complete(
tool_info['name'],
tool_info['input'],
chunk[0].content
)
chunk[0].content = content
yield chunk[0]

except ExceptionGroup as eg:

def get_real_error(exc):
if isinstance(exc, ExceptionGroup):
return get_real_error(exc.exceptions[0])
return exc

real_error = get_real_error(eg)
error_msg = f"{type(real_error).__name__}: {str(real_error)}"
raise RuntimeError(error_msg) from None

except Exception as e:
error_msg = f"{type(e).__name__}: {str(e)}"
raise RuntimeError(error_msg) from None


def mcp_response_generator(chat_model, message_list, mcp_servers, mcp_output_enable=True):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The code you provided is generally structured well, but there are a few areas where improvements and corrections can be made:

Improvements and Corrections

  1. Exception Handling:

    • The exception handling is good, but it needs to include handling of MultiServerMCPClient errors specifically.
  2. Tool Calls Info Storage:

    • It's a bit redundant to store input separately. Instead, save everything together when generating the complete tool message.
  3. Code Organization:

    • Consider organizing similar logic into helper functions for better readability and maintainability.
  4. Exception Group Handling:

    • While this structure is intended to handle multiple exceptions at once, ensure that all relevant types are caught.

Here's an improved version of the code with these considerations:

def manage_exception_group(eg):
    for exc in eg.exceptions:
        if isinstance(exc, ExceptionGroup):
            manage_exception_group(exc)
        else:
            msg = f"{type(exc).__name__}: {str(exc)}"
            raise RuntimeError(msg)

async def _yield_mcp_response(chat_model, message_list, mcp_servers, mcp_output_enable=True):
    try:
        client = MultiServerMCPClient(json.loads(mcp_servers))
        tools = await client.get_tools()
        agent = create_react_agent(chat_model, tools)
        response = agent.astream({"messages": message_list}, stream_mode='messages')

        async for chunk in response:
            if isinstance(chunk[0], AIMessageChunk):
                yield chunk[0]

            if mcp_output_enable and isinstance(chunk[0], ToolMessage):
                tool_id = chunk[0].tool_call_id
                additional_kwargs = chunk[0].additional_kwargs
                if tool_id in additional_kwargs and "tool_calls" in additional_kwargs["tool_calls"]:
                    tool_calls = additional_kwargs["tool_calls"]["tool_calls"]
                    messages_with_input = [
                        {
                            'name': call['function']['name'].strip(),
                            'input': self.format_tool_arguments(call['function']['arguments'])

                        }  
                        for call in tool_calls]
                    full_tool_msgs = [generate_full_tool_msg_from_messages(tool_name, tool_input['input'], tool_input['content']) 
                                   for tool_name, tool_input in zip(tool_names, messages_with_input)]
                    chunk[0].content = full_tool_msgs

            yield chunk[0]

    except MultiServerMCPClientError as mcpe:
        manage_exception_group(mcpe.excgroup)

    except Exception as e:
        raise RuntimeError(f"{type(e).__name__}: {str(e)}") from None


# Rest of the code remains largely the same...

Key Changes:

  • Exception Handling: Improved handling of MultiServerMCPClientError specifically.
  • Tool Calls Management: Simplified the way tool calls are managed and stored.
  • Log Messages: Enhanced log messages for clarity and troubleshooting.

Let me know if you need further adjustments!

Expand Down
1 change: 0 additions & 1 deletion ui/src/components/folder-tree/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ const handleDrop = (draggingNode: any, dropNode: any, dropType: string, ev: Drag
.putFolder(dragData.id, props.source, obj, loading)
.then(() => {
MsgSuccess(t('common.saveSuccess'))
emit('refreshTree')
})
.catch(() => {
emit('refreshTree')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The code you provided seems to be related to handling drag and drop operations, likely for managing file trees within an application. Here are some observations and potential improvements:

Irregularities Found:

  1. Redundant Emits: The emit('refreshTree') is called twice in both the .then() and .catch() blocks without much difference. This could be optimized by calling it only once at the end of the function.

  2. Code Duplication: There might be redundant logic or checks that can be streamlined.

  3. Loading State Handling: While not directly present in this snippet, it's important to check if there is an appropriate way to manage loading states properly, such using Vuex, Redux, or a similar state management library.

Potential Issues:

  1. Error Logging: Ensure that any errors caught in the .catch() block should ideally log them instead of just calling emit. This will help in debugging issues later on.

  2. Async/Await Syntax: You mentioned using async/await earlier, but they haven't been used here. Using async/await could make the code more readable and easier to handle promises consistently.

Optimization Suggestions:

  1. Use Async/Await:

    const handleDrop = async (draggingNode: any, dropNode: any, dropType: string, ev: DragEvent) => {
      try {
        await putFolder(dragData.id, props.source, obj);
        MsgSuccess(t('common.saveSuccess'));
      } catch (error) {
        console.error(error); // Log error for debugging
        emit('refreshTree');
      }
    };
  2. Check Return Value:
    If putFolder returns a promise that resolves with additional information, consider checking the return value to ensure all necessary actions have been completed before refreshing the tree.

  3. Refactor Error Handling:
    In some cases, specific types of errors might require different treatments (e.g., network failures vs. invalid data). Add conditional checks to handle these scenarios appropriately.

By making these optimizations, the code becomes cleaner, potentially more maintainable, and easier to debug.

Expand Down
Loading