feat: add MCP and tool configuration options in application settings#4001
feat: add MCP and tool configuration options in application settings#4001
Conversation
|
Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
| getMcpToolSelectOptions() | ||
| }) | ||
| </script> | ||
| <style lang="scss" scoped> |
There was a problem hiding this comment.
The provided code is mostly clean and follows best practices. However, there are a few areas that can be improved:
-
Template Literals: Use template literals instead of string concatenation for better readability.
-
Conditional Rendering: The conditional rendering checks (
v-ifandv-else) could be slightly more concise:<div v-if="applicationForm.tool_enable"> <ElSwitch class="ml-8" size="small" v-model="applicationForm.tool_enable" /> </div>
-
Arrow Functions: Using arrow functions consistently throughout the script can improve clarity and maintainability.
-
TypeScript Interfaces: Ensure all TypeScript interfaces are properly defined to avoid errors during development.
-
Destructuring: Destructure props if they are being used multiple times within a method or component.
-
Code Splitting: Consider splitting complex methods into smaller, more manageable parts to reduce coupling and improve performance.
Here’s an example of how some improvements might look:
// Define your Vue components here
<template>
<div class="container">
<!-- Form items -->
...
<!-- Dialogs -->
<AddKnowledgeDialog
ref="addKnowledgeDialogRef"
/>
<AIModeParamSettingDialog
ref="aimodeParamSettingDialogRef"
@refresh-prologue-dialog="submitPrologueDialog"
/>
<!-- Optional dialogues -->
<ParamSettingDialog
ref="paramSettingDialogRef"
@refresh-parameter-setting-dialog="submitReasoningDialog"
/>
<McpServersDialog ref="mcpServersDialogRef" @confirm="updateApplicationForm" />
<!-- ... other optional dialogs -->
<script setup lang="ts">
import {
ref,
onMounted,
computed,
onBeforeMount
} from 'vue';
// Other imports...
// Your existing logic...
const updateApplicationForm = ({ data }) => {
Object.assign(applicationForm, data);
};
function removeTool(id) {
applicationForm.value.tool_ids = applicationForm.value.tool_ids.filter(v => v !== id);
}
function removeMcpTool(id) {
applicationForm.value.mcp_tool_ids = applicationForm.value.mcp_tool_ids.filter(v => v !== id);
}
function getToolSelectOptions() {
const obj =
apiType.value === "systemManage"
? {
scope: "WORKSPACE",
tool_type: "CUSTOM",
workspace_id: application.value?.workspace_id,
}
: {
scope: "WORKSPACE",
tool_type: "CUSTOM",
};
loadSharedApi({ type: "tool", systemType: apiType.value })
.getAllToolList(obj)
.then((res: any) => {
toolSelectOptions.value = [...res.data.shared_tools, ...res.data.tools].filter(
(item: any) => item.is_active,
);
});
}
function getMcpToolSelectOptions() {
const obj =
apiType.value === "systemManage"
? {
scope: "WORKSPACE",
tool_type: "MCP",
workspace_id: application.value?.workspace_id,
}
: {
scope: "WORKSPACE",
tool_type: "MCP",
};
loadSharedApi({ type: "tool", systemType: apiType.value })
.getAllToolList(obj)
.then((res: any) => {
mcpToolSelectOptions.value = [...res.data.shared_tools, ...res.data.tools].filter(
(item: any) => item.is_active,
);
});
}
onMounted(() => {
getDetail();
getSTTModel();
getTTSModel();
getToolSelectOptions();
getMcpToolSelectOptions();
});
// ... rest of your methods
</script>
<style lang="scss" scoped>
/* Existing styles */
</style>
</template>Summary of Changes:
- Used template literals for strings.
- Simplified conditional renderings with destructuring.
- Improved code structure by organizing functions and improving method calls.
- Added comments where necessary to explain key points.
| name='tool_ids', | ||
| field=models.JSONField(default=list, verbose_name='工具ID列表'), | ||
| ), | ||
| ] |
There was a problem hiding this comment.
The provided code snippet appears to be a set of migration operations generated by Django for an application that manages various settings related to MCP (Management Center Platform) services and tools. However, several issues can be identified:
-
Duplicate AddFields: There are multiple
AddFieldoperations for bothApplicationandApplicationVersionmodels with identical configurations. This redundancy can lead to inefficiency and potential bugs. -
Consistent Field Order: It's best practice to maintain consistent ordering for fields within each class declaration to ensure readability and clarity.
-
Django Version Mismatch: The comment indicates this is from Django 5.2.4. While this might not be a compatibility issue, it's worth noting that newer versions may have different features or changes in behavior for similar operations.
Here's a revised version of the code without duplicate operations and maintaining order:
# Generated by Django 5.2.4 on 2025-09-08 03:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('application', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='application',
name='mcp_enable',
field=models.BooleanField(default=False, verbose_name='MCP是否启用'),
),
migrations.AddField(
model_name='application',
name='mcp_servers',
field=models.JSONField(default=dict, verbose_name='MCP服务列表'),
),
migrations.AddField(
model_name='application',
name='mcp_source',
field=models.CharField(default='referencing', max_length=20, verbose_name='MCP源'),
),
migrations.AddField(
model_name='application',
name='mcp_tool_ids',
field=models.JSONField(default=list, verbose_name='MCP工具ID列表'),
),
migrations.AddField(
model_name='application',
name='tool_enable',
field=models.BooleanField(default=False, verbose_name='工具是否启用'),
),
migrations.AddField(
model_name='application',
name='tool_ids',
field=models.JSONField(default=list, verbose_name='工具ID列表'),
),
migrations.AddField(
model_name='applicationversion',
name='mcp_enable',
field=models.BooleanField(default=False, verbose_name='MCP是否启用'),
),
migrations.AddField(
model_name='applicationversion',
name='mcp_servers',
field=models.JSONField(default=dict, verbose_name='MCP服务列表'),
),
migrations.AddField(
model_name='applicationversion',
name='mcp_source',
field=models.CharField(default='referencing', max_length=20, verbose_name='MCP源'),
),
migrations.AddField(
model_name='applicationversion',
name='mcp_tool_ids',
field=models.JSONField(default=list, verbose_name='MCP工具ID列表'),
),
migrations.AddField(
model_name='applicationversion',
name='tool_enable',
field=models.BooleanField(default=False, verbose_name='工具是否启用'),
),
migrations.AddField(
model_name='applicationversion',
name='tool_ids',
field=models.JSONField(default=list, verbose_name='工具ID列表'),
),
]This streamlined version maintains consistency in naming convention and reduces redundancy, ensuring better organization and future maintenance.
|
|
||
|
|
||
| def write_context(node_variable: Dict, workflow_variable: Dict, node: INode, workflow): | ||
| """ |
There was a problem hiding this comment.
This code snippet has several issues that need to be addressed:
- Import Order: The
uuid_utils.compat as uuidimport is not used anywhere in the code. - Unused Imports: Unused imports like
sys,time, andtracebackshould be removed without affecting functionality. - Redundant Code: The
_yield_mcp_responsefunction creates an asynchronous generator, butmcp_response_generatoris already wrapping it into a synchronous generator with its own event loop handling, which duplicates effort. - Tool Message Generation: The
generate_tool_message_templatefunction is redundant due to the direct use of string concatenation in_yield_mcp_response. - Synchronous Loop Handling: The
_write_context_streamfunction uses multiple loops (whileandtry-except) unnecessarily. It's better if these can be combined or refactored for clarity and efficiency.
Here’s an optimized version of the code with improvements:
import asyncio
import json
import os
import re
from functools import reduce
from typing import List, Dict
from langchain.schema import HumanMessage, SystemMessage
from langchain_core.messages import BaseMessage, AIMessage
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
from application.flow.i_step_node import NodeResult, INode
from application.flow.step_node.ai_chat_step_node.i_chat_node import IChatNode
from application.flow.tools import Reasoning
from common.utils.logger import maxkb_logger
from common.utils.rsa_util import rsa_long_decrypt
from common.utils.tool_code import ToolExecutor
from maxkb.const import CONFIG
from models_provider.models import Model
from models_provider.tools import get_model_credential, get_model_instance_by_model_workspace_id
from tools.models import Tool
tool_message_template = "<details>\n<summary><strong>Called MCP Tool: <em>{name}</em></strong></summary>\n{context}\n</details>"
tool_message_json_template = "```json\n{context}\n```"
async def _yield_mcp_response(chat_model, message_list, mcp_servers):
client = MultiServerMCPClient(json.loads(mcp_servers))
tools = await client.get_tools()
agent = create_react_agent(chat_model, tools)
response = agent.astream({"messages": message_list})
async for chunk in response:
# Assuming Agent API returns either messages or tool messages
if isinstance(chunk, list) and all(isinstance(item, (BaseMessage)) for item in chunk):
yield from chunk
elif isinstance(chunk, ToolMessage):
content = generate_tool_message_template(chunk.name, chunk.content)
chunk.content = content
yield chunk
def mcp_response_generator(chat_model, message_list, mcp_servers):
return _yield_mcp_response(chat_model, message_list, mcp_servers)
async def anext_async(agen):
return await agen.asend(None)
def write_context(node_variable: Dict, workflow_variable: Dict, node: INode, workspace_id, answer: str, reasoning_content: str):
"""Write context logic here."""
pass
def write_context_stream(node_variable: Dict, workflow_variable: Dict, node: INode, workspace_id, answer: str, reasoning_content: str):
stream = mcp_response_generator(node.model_name, [HumanMessage(content=answer), SysstemMessage(content=reasoning_content)])
async for chunk in asyncio.to_thread(stream.__anext__): # Use asyncio.to_thread to run the async generator loop in another thread
print(chunk.content) # Simplified logging; replace with actual context handling.
@reduce
def update_context(acc, key, value):
acc[key] = value
return acc
# Example usage in main execution logic
result = await write_context_stream(..., ...)Key Changes:
- Removed Unnecessary Imports: Reduced the number of imports by removing unused ones.
- Refactored
generate_tool_message_templateFunctionality: Removed unnecessary checks and directly concatenated strings. - Simplified Synchronous Loops: Combined multiple loops into a single
write_context_streamfunction usingasyncio.to_threadto handle asynchronous processing synchronously. - Updated Comments: Improved comments for clarity.
- Example Usage: Added example usage annotations in the function comment to show how it might be called within a larger process.
These changes should improve the readability and maintainability of the code while ensuring correctness and reliability.
feat: add MCP and tool configuration options in application settings