From 0e7fb4c80b86d44574687aebc0014d630b69f878 Mon Sep 17 00:00:00 2001 From: q1279335527 <1279335527@qq.com> Date: Sun, 19 Oct 2025 22:42:19 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=95=B4=E7=90=86=E6=96=87=E4=BB=B6=E7=BB=93?= =?UTF-8?q?=E6=9E=84=202.=E5=AF=B9=E4=BA=8Emcp=E6=9C=8D=E5=8A=A1=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E6=94=AF=E6=8C=81=203.=E6=B7=BB=E5=8A=A0mcp=E8=BF=90?= =?UTF-8?q?=E7=AE=97=E8=83=BD=E5=8A=9B=204.=E6=B7=BB=E5=8A=A0tools(?= =?UTF-8?q?=E5=8F=8Amcp+functioncall)=E8=BF=90=E7=AE=97=E8=83=BD=E5=8A=9B?= =?UTF-8?q?=205.=E6=B7=BB=E5=8A=A0=E4=BF=AE=E6=94=B9=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 + ai/pom.xml | 21 +- ai/viewer-ai-mcp-client/pom.xml | 10 + .../main/java/xyz/thoughtset/viewer/Main.java | 7 - .../client/MCPClientAutoConfiguration.java | 12 + .../ai/mcp/client/dao/McpBotInfoDao.java | 9 + .../ai/mcp/client/entity/McpBotInfo.java | 69 +++ .../ai/mcp/client/entity/McpClientLink.java | 81 ++++ .../ai/mcp/client/factory/McpBotFactory.java | 74 +++ .../mcp/client/factory/McpToolCallback.java | 91 ++++ .../mcp/client/service/McpBotInfoService.java | 12 + .../client/service/McpBotInfoServiceImpl.java | 83 ++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + ai/viewer-ai-mcp-server/pom.xml | 15 +- .../mcp/server/factory/McpServerFactory.java | 1 + .../controller/AiSupportController.java | 19 + .../static/html/pages/McpBotInfo.json | 442 ++++++++++++++++++ .../static/html/pages/McpServer.json | 2 +- .../resources/static/html/pages/funInfo.json | 127 ++++- .../resources/static/html/pages/index.json | 5 + executor/viewer-executor-blocks/pom.xml | 5 + .../blocks/executor/AIChatExecutor.java | 147 ++++++ .../AbstractAISupportBlockExecutor.java | 69 ++- .../executor/AbstractBlockExecutor.java | 11 +- .../blocks/executor/ExecAIBlockExecutor.java | 53 ++- .../blocks/executor/ExecutorRegistry.java | 2 +- .../executor/FunctionCallBlockExecutor.java | 42 ++ .../FunctionCallingBlockExecutor.java | 22 +- .../executor/IteratorBlockExecutor.java | 3 +- .../blocks/executor/LoopBlockExecutor.java | 3 +- .../blocks/executor/MCPBlockExecutor.java | 110 ++--- .../blocks/executor/SingleBlockExecutor.java | 4 +- .../blocks/executor/TaskBlockExecutor.java | 3 +- .../blocks/executor/ToolsBlockExecutor.java | 63 +++ .../executor/TransposeBlockExecutor.java | 8 +- .../blocks/executor/ValueBlockExecutor.java | 5 +- .../api/service/ApiInfoServiceImpl.java | 3 +- .../ModuleMCPServerAutoConfiguration.java | 1 - .../service/McpServerInfoServiceImpl.java | 3 +- .../modules/step}/entity/AISupportBody.java | 6 +- .../modules/step}/entity/BaseBlockBody.java | 2 +- .../modules/step}/entity/BlockTypeEnum.java | 3 +- .../viewer/modules/step}/entity/BodyEle.java | 2 +- .../modules/step}/entity/ChooseBody.java | 2 +- .../modules/step}/entity/ExecAIBody.java | 4 +- .../step}/entity/FunctionCallBody.java | 4 +- .../modules/step}/entity/IteratorBody.java | 2 +- .../viewer/modules/step}/entity/LoopBody.java | 2 +- .../viewer/modules/step}/entity/MCPBody.java | 13 +- .../modules/step}/entity/SingleBody.java | 4 +- .../viewer/modules/step}/entity/TaskBody.java | 4 +- .../viewer/modules/step/entity/ToolsBody.java | 15 + .../modules/step}/entity/TransposeBody.java | 2 +- .../modules/step}/entity/ValueBody.java | 4 +- .../step/service/BlockInfoServiceImpl.java | 8 +- pom.xml | 5 + 56 files changed, 1484 insertions(+), 239 deletions(-) delete mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/Main.java create mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/MCPClientAutoConfiguration.java create mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/dao/McpBotInfoDao.java create mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/entity/McpBotInfo.java create mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/entity/McpClientLink.java create mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/factory/McpBotFactory.java create mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/factory/McpToolCallback.java create mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/service/McpBotInfoService.java create mode 100644 ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/service/McpBotInfoServiceImpl.java create mode 100644 ai/viewer-ai-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 apis/viewer-apis-client/src/main/resources/static/html/pages/McpBotInfo.json create mode 100644 executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AIChatExecutor.java rename executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/{ai => }/AbstractAISupportBlockExecutor.java (50%) create mode 100644 executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/FunctionCallBlockExecutor.java create mode 100644 executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ToolsBlockExecutor.java rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/AISupportBody.java (61%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/BaseBlockBody.java (93%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/BlockTypeEnum.java (94%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/BodyEle.java (87%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/ChooseBody.java (87%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/ExecAIBody.java (70%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/FunctionCallBody.java (72%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/IteratorBody.java (87%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/LoopBody.java (88%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/MCPBody.java (49%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/SingleBody.java (78%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/TaskBody.java (70%) create mode 100644 modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ToolsBody.java rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/TransposeBody.java (87%) rename {executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks => modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step}/entity/ValueBody.java (70%) diff --git a/README.md b/README.md index 78e4739..2fd14bf 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,9 @@ java -jar viewer-apis-service.jar --spring.config.location=application.yml 13. 结合jdk25+springboot4优化 \ 14. spring native 15. AI Agent能力 +16. 响应式接口 +17. 并行查询 +18. 在管理端使用自然语言辅助生成查询 ## 🤝 参与贡献 diff --git a/ai/pom.xml b/ai/pom.xml index ef756c5..90b4d49 100644 --- a/ai/pom.xml +++ b/ai/pom.xml @@ -24,27 +24,24 @@ - - xyz.thoughtset.viewer - viewer-modules-step - + + + + xyz.thoughtset.viewer - viewer-executor-blocks + viewer-modules-export xyz.thoughtset.viewer - viewer-common-api + viewer-modules-datarel - xyz.thoughtset.viewer - viewer-modules-export - - - xyz.thoughtset.viewer - viewer-modules-datarel + io.modelcontextprotocol.sdk + mcp + ${mcp.sdk.version} diff --git a/ai/viewer-ai-mcp-client/pom.xml b/ai/viewer-ai-mcp-client/pom.xml index 52a5f31..324de05 100644 --- a/ai/viewer-ai-mcp-client/pom.xml +++ b/ai/viewer-ai-mcp-client/pom.xml @@ -17,4 +17,14 @@ UTF-8 + + + + org.springframework.ai + spring-ai-model + ${springai.version} + true + + + \ No newline at end of file diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/Main.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/Main.java deleted file mode 100644 index 94b82b5..0000000 --- a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package xyz.thoughtset.viewer; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello world!"); - } -} \ No newline at end of file diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/MCPClientAutoConfiguration.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/MCPClientAutoConfiguration.java new file mode 100644 index 0000000..80b9664 --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/MCPClientAutoConfiguration.java @@ -0,0 +1,12 @@ +package xyz.thoughtset.viewer.ai.mcp.client; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan +@MapperScan(basePackages = "xyz.thoughtset.viewer.ai.mcp.client.dao") +public class MCPClientAutoConfiguration { +} diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/dao/McpBotInfoDao.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/dao/McpBotInfoDao.java new file mode 100644 index 0000000..d981a8b --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/dao/McpBotInfoDao.java @@ -0,0 +1,9 @@ +package xyz.thoughtset.viewer.ai.mcp.client.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import xyz.thoughtset.viewer.ai.mcp.client.entity.McpBotInfo; + +@Mapper +public interface McpBotInfoDao extends BaseMapper { +} diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/entity/McpBotInfo.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/entity/McpBotInfo.java new file mode 100644 index 0000000..d549f6b --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/entity/McpBotInfo.java @@ -0,0 +1,69 @@ +package xyz.thoughtset.viewer.ai.mcp.client.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.modelcontextprotocol.client.McpClient; +import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport; +import io.modelcontextprotocol.spec.McpSchema; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.util.StringUtils; +import xyz.thoughtset.viewer.common.core.entity.BaseMeta; +import xyz.thoughtset.viewer.common.crud.core.annotation.ApiCRUDPower; + +import java.net.http.HttpClient; +import java.time.Duration; + +@TableName +@Data +@AllArgsConstructor +@ApiCRUDPower(insert = false,save = true,update = false,list = true) +public class McpBotInfo extends BaseMeta { + protected Integer typeCode = 11;//SSE + protected String version; + protected String serverTitle; + protected String baseUrl; + protected String sseEndpoint; + protected Integer requestTimeout; + protected Integer connectTimeout; + + protected String disableToolNames; + + protected String headers; + + + public McpBotInfo() { + this.statesCode = null; + } + + public McpBotInfo(String serverId, Integer runningCode) { + this(); + this.id = serverId; + this.statesCode = runningCode; + } + + public McpClientLink buildClientLink(ObjectMapper objectMapper){ + String sseEndpoint = StringUtils.hasText(this.sseEndpoint) ? this.sseEndpoint : "/sse"; + HttpClientSseClientTransport.Builder transportBuilder = HttpClientSseClientTransport.builder(baseUrl) + .sseEndpoint(sseEndpoint) + .connectTimeout(Duration.ofSeconds(this.connectTimeout!= null ? this.connectTimeout : 10)) + .clientBuilder(HttpClient.newBuilder()) + .objectMapper(objectMapper); + HttpClientSseClientTransport transport = transportBuilder.build(); + McpSchema.Implementation clientInfo = new McpSchema.Implementation( + this.title, + version); + McpClient.SyncSpec spec = McpClient.sync(transport) + .clientInfo(clientInfo) + .requestTimeout(Duration.ofSeconds(this.requestTimeout!= null ? this.requestTimeout : 20)); + //todo headers + //todo spec.toolsChangeConsumer() +// var client = spec.build(); +// McpSchema.InitializeResult result = client.initialize(); + return McpClientLink.builder() + .clientInfo(this) + .spec(spec) + .build(); + } + +} diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/entity/McpClientLink.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/entity/McpClientLink.java new file mode 100644 index 0000000..aac8c60 --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/entity/McpClientLink.java @@ -0,0 +1,81 @@ +package xyz.thoughtset.viewer.ai.mcp.client.entity; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.modelcontextprotocol.client.McpClient; +import io.modelcontextprotocol.client.McpSyncClient; +import io.modelcontextprotocol.spec.McpSchema; + +import java.io.Closeable; +import java.util.List; +import java.util.function.BiPredicate; + + +public record McpClientLink(McpBotInfo mcpBotInfo, + McpSyncClient mcpSyncClient, + McpSchema.InitializeResult initializeResult) implements Closeable { + + public static Builder builder() { + return new Builder(); + } + + public List mcpTools() { + return mcpSyncClient.listTools().tools(); + } + public String clientName(){ + return mcpSyncClient.getClientInfo().name(); + } + + public BiPredicate defaultToolFilter() { + return (client, tool) -> { + if (mcpBotInfo.getDisableToolNames() == null || mcpBotInfo.getDisableToolNames().isEmpty()) { + return true; + } + String[] disableToolArr = mcpBotInfo.getDisableToolNames().split(","); + for (String ele : disableToolArr) { + if (ele.trim().equalsIgnoreCase(tool.name())) { + return false; + } + } + return true; + }; + } + + @Override + public void close() { + mcpSyncClient.close(); + } + + public static final class Builder { + + + private McpBotInfo clientInfo; + private McpClient.SyncSpec spec; + + private Builder() { + } + + public McpClientLink.Builder spec(McpClient.SyncSpec spec) { + this.spec = spec; + return this; + } + + public McpClientLink.Builder clientInfo(McpBotInfo clientInfo) { + this.clientInfo = clientInfo; + return this; + } + + public McpClientLink build() { + if (this.spec == null) { + if (this.clientInfo == null){ + throw new IllegalStateException("McpBotInfo is required"); + } + } + McpSyncClient mcpSyncClient = this.spec.build(); + McpSchema.InitializeResult initializeResult = mcpSyncClient.initialize(); + return new McpClientLink(clientInfo,mcpSyncClient, initializeResult); + } + + } + + +} diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/factory/McpBotFactory.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/factory/McpBotFactory.java new file mode 100644 index 0000000..bf1d03b --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/factory/McpBotFactory.java @@ -0,0 +1,74 @@ +package xyz.thoughtset.viewer.ai.mcp.client.factory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.support.ToolUtils; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import xyz.thoughtset.viewer.ai.mcp.client.entity.McpBotInfo; +import xyz.thoughtset.viewer.ai.mcp.client.entity.McpClientLink; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +@Component +public class McpBotFactory implements DisposableBean { + + private final Map MCP_CLIENT_MAP = new ConcurrentHashMap<>(); + @Autowired + private ObjectMapper objectMapper; + + public void createMcpServer(List list) { + if (list == null || list.isEmpty()) return; + for (McpBotInfo ele : list) { + createMcpServer(ele); + } + } + + public void createMcpServer(McpBotInfo clientInfo) { + McpClientLink link = clientInfo.buildClientLink(objectMapper); + MCP_CLIENT_MAP.put(clientInfo.getId(), link); + } + + @Override + public void destroy() throws Exception { + MCP_CLIENT_MAP.values().parallelStream().forEach(McpClientLink::close); + } + + public ToolCallback[] clientsTools(List serverIds) { + var array = MCP_CLIENT_MAP.entrySet().stream() + .filter(entry -> serverIds == null || serverIds.contains(entry.getKey())) + .map(Map.Entry::getValue) + .flatMap(mcpLink -> mcpLink.mcpTools() + .stream() + .filter(tool -> mcpLink.defaultToolFilter().test(mcpLink.mcpBotInfo(), tool)) + .map(tool -> new McpToolCallback(mcpLink, tool)) + ) + .toArray(ToolCallback[]::new); + validateToolCallbacks(array); + return array; + } + + private void validateToolCallbacks(ToolCallback[] toolCallbacks) { + List duplicateToolNames = ToolUtils.getDuplicateToolNames(toolCallbacks); + if (!duplicateToolNames.isEmpty()) { + throw new IllegalStateException( + "Multiple tools with the same name (%s)".formatted(String.join(", ", duplicateToolNames))); + } + } + + public void removeClient(String serverId) { + if (!StringUtils.hasText(serverId) ||!MCP_CLIENT_MAP.containsKey(serverId)) { + return; + } + McpClientLink link = MCP_CLIENT_MAP.remove(serverId); + if (link != null) { + link.close(); + } + } +} diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/factory/McpToolCallback.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/factory/McpToolCallback.java new file mode 100644 index 0000000..597fb9f --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/factory/McpToolCallback.java @@ -0,0 +1,91 @@ +/* + * Copyright 2025-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.thoughtset.viewer.ai.mcp.client.factory; + +import io.modelcontextprotocol.client.McpSyncClient; +import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; +import io.modelcontextprotocol.spec.McpSchema.CallToolResult; +import io.modelcontextprotocol.spec.McpSchema.Tool; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ai.chat.model.ToolContext; +import org.springframework.ai.model.ModelOptionsUtils; +import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; +import org.springframework.ai.tool.definition.ToolDefinition; +import org.springframework.ai.tool.execution.ToolExecutionException; +import xyz.thoughtset.viewer.ai.mcp.client.entity.McpClientLink; + +import java.util.Map; + +@Slf4j +public class McpToolCallback implements ToolCallback { + + private final McpClientLink mcpClient; + private final String toolName; + private final Tool tool; + + + public McpToolCallback(McpClientLink mcpClient, Tool tool) { + this.mcpClient = mcpClient; + this.tool = tool; + this.toolName = this.mcpClient.clientName()+"#"+this.tool.name(); + } + + + @Override + public ToolDefinition getToolDefinition() { + return DefaultToolDefinition.builder() + .name(toolName) + .description(this.tool.description()) + .inputSchema(ModelOptionsUtils.toJsonString(this.tool.inputSchema())) + .build(); + } + + public String getOriginalToolName() { + return this.tool.name(); + } + + + @Override + public String call(String functionInput) { + Map arguments = ModelOptionsUtils.jsonToMap(functionInput); + log.debug("***** *****"+toolName+" call with arguments: {}", arguments); + CallToolResult response; + try { + response = this.mcpClient.mcpSyncClient().callTool(new CallToolRequest(this.tool.name(), arguments)); + } + catch (Exception ex) { + log.error("Exception while tool calling: ", ex); + throw new ToolExecutionException(this.getToolDefinition(), ex); + } + + if (response.isError() != null && response.isError()) { + log.error("Error calling tool: {}", response.content()); + throw new ToolExecutionException(this.getToolDefinition(), + new IllegalStateException("Error calling tool: " + response.content())); + } + return ModelOptionsUtils.toJsonString(response.content()); + } + + @Override + public String call(String toolArguments, ToolContext toolContext) { + return this.call(toolArguments); + } + +} diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/service/McpBotInfoService.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/service/McpBotInfoService.java new file mode 100644 index 0000000..1824305 --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/service/McpBotInfoService.java @@ -0,0 +1,12 @@ +package xyz.thoughtset.viewer.ai.mcp.client.service; + +import xyz.thoughtset.viewer.ai.mcp.client.entity.McpBotInfo; +import xyz.thoughtset.viewer.common.crud.core.service.BaseService; + +import java.util.List; + +public interface McpBotInfoService extends BaseService { + void unpublishedServer(String serverId); + List workingBots(); + void publishServer(String serverId); +} diff --git a/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/service/McpBotInfoServiceImpl.java b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/service/McpBotInfoServiceImpl.java new file mode 100644 index 0000000..ae247c6 --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/java/xyz/thoughtset/viewer/ai/mcp/client/service/McpBotInfoServiceImpl.java @@ -0,0 +1,83 @@ +package xyz.thoughtset.viewer.ai.mcp.client.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.SneakyThrows; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; +import xyz.thoughtset.viewer.ai.mcp.client.dao.McpBotInfoDao; +import xyz.thoughtset.viewer.ai.mcp.client.entity.McpBotInfo; +import xyz.thoughtset.viewer.ai.mcp.client.factory.McpBotFactory; +import xyz.thoughtset.viewer.common.core.constants.StatusCodeConstant; +import xyz.thoughtset.viewer.common.core.entity.BaseMeta; +import xyz.thoughtset.viewer.common.crud.core.service.BaseServiceImpl; +import xyz.thoughtset.viewer.modules.datarel.entity.DataRel; +import xyz.thoughtset.viewer.modules.datarel.service.DataRelService; + +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@DependsOn("mcpBotFactory") +//@Transactional +public class McpBotInfoServiceImpl extends BaseServiceImpl implements McpBotInfoService { + + @Autowired + protected McpBotFactory mcpBotFactory; + + + @EventListener(ApplicationReadyEvent.class) + public void initData() { + mcpBotFactory.createMcpServer(workingBots()); + } + + @Override + public McpBotInfo saveData(McpBotInfo data) { + mcpBotFactory.removeClient(data.getId()); + data.setStatesCode(StatusCodeConstant.EDITING); + return super.saveData(data); + } + + @Override + @Transactional + public boolean deleteById(String pkey) { + super.deleteById(pkey); + unpublishedServer(pkey); + return true; + } + + @Override + @Transactional + public void unpublishedServer(String serverId) { + baseMapper.updateById(new McpBotInfo(serverId, StatusCodeConstant.EDITING)); + mcpBotFactory.removeClient(serverId); + } + + @Override + public List workingBots() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.lambda() + .eq(McpBotInfo::getStatesCode, StatusCodeConstant.RUNNING) + .ne(McpBotInfo::getTypeCode,"") + .isNotNull(McpBotInfo::getTypeCode) + .orderByAsc(McpBotInfo::getOrderNum); + return baseMapper.selectList(queryWrapper); + } + + @SneakyThrows + @Override + @Transactional + public void publishServer(String serverId) { + McpBotInfo botInfo = baseMapper.selectById(serverId); + baseMapper.updateById(new McpBotInfo(serverId, StatusCodeConstant.RUNNING)); + mcpBotFactory.createMcpServer(botInfo); + } + + + +} diff --git a/ai/viewer-ai-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ai/viewer-ai-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..3bc8f8c --- /dev/null +++ b/ai/viewer-ai-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xyz.thoughtset.viewer.ai.mcp.client.MCPClientAutoConfiguration \ No newline at end of file diff --git a/ai/viewer-ai-mcp-server/pom.xml b/ai/viewer-ai-mcp-server/pom.xml index 2134c97..0035421 100644 --- a/ai/viewer-ai-mcp-server/pom.xml +++ b/ai/viewer-ai-mcp-server/pom.xml @@ -18,6 +18,16 @@ + + + xyz.thoughtset.viewer + viewer-modules-step + + + + xyz.thoughtset.viewer + viewer-executor-blocks + @@ -28,11 +38,6 @@ spring-ai-mcp ${springai.version} - - io.modelcontextprotocol.sdk - mcp - ${mcp.sdk.version} - io.modelcontextprotocol.sdk mcp-spring-webmvc diff --git a/ai/viewer-ai-mcp-server/src/main/java/xyz/thoughtset/viewer/ai/mcp/server/factory/McpServerFactory.java b/ai/viewer-ai-mcp-server/src/main/java/xyz/thoughtset/viewer/ai/mcp/server/factory/McpServerFactory.java index 957df88..be644af 100644 --- a/ai/viewer-ai-mcp-server/src/main/java/xyz/thoughtset/viewer/ai/mcp/server/factory/McpServerFactory.java +++ b/ai/viewer-ai-mcp-server/src/main/java/xyz/thoughtset/viewer/ai/mcp/server/factory/McpServerFactory.java @@ -89,6 +89,7 @@ public class McpServerFactory implements DisposableBean { .serverInfo(mcpServerInfo.getTitle(), mcpServerInfo.getVersion()); // .serverInfo("MCP Demo Weather Server", "1.0.0") mgtTools(capabilityBuilder,serverBuilder,key); + //promptly, we disable file and function support serverBuilder.instructions(mcpServerInfo.getInstructions()); Integer timeout = mcpServerInfo.getRequestTimeout(); if (timeout != null && timeout.longValue() > 0) { diff --git a/apis/viewer-apis-client/src/main/java/xyz/thoughtset/viewer/apis/client/controller/AiSupportController.java b/apis/viewer-apis-client/src/main/java/xyz/thoughtset/viewer/apis/client/controller/AiSupportController.java index 462e6c3..b22ddc5 100644 --- a/apis/viewer-apis-client/src/main/java/xyz/thoughtset/viewer/apis/client/controller/AiSupportController.java +++ b/apis/viewer-apis-client/src/main/java/xyz/thoughtset/viewer/apis/client/controller/AiSupportController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import xyz.thoughtset.viewer.ai.mcp.client.service.McpBotInfoService; import xyz.thoughtset.viewer.common.ai.model.factory.ModelsRegistry; import xyz.thoughtset.viewer.common.api.controller.BaseController; import xyz.thoughtset.viewer.common.core.util.ExtractConstantFieldUtil; @@ -18,6 +19,8 @@ import java.util.Collection; public class AiSupportController extends BaseController { @Autowired private McpServerInfoService mcpServerInfoService; + @Autowired + private McpBotInfoService mcpBotInfoService; @GetMapping(value = "/publishServer") @@ -36,5 +39,21 @@ public class AiSupportController extends BaseController { public Object modelPurposes(){ return ExtractConstantFieldUtil.extractEnumToList(xyz.thoughtset.viewer.common.ai.model.entity.purpose.ModelPurposeEnum.class); } + @GetMapping(value = "/publishBot") + public void publishBot(@RequestParam String apiId){ + mcpBotInfoService.publishServer(apiId); + } + @GetMapping(value = "/unpublishedBot") + public void unpublishedBot(@RequestParam String apiId){ + mcpBotInfoService.unpublishedServer(apiId); + } + @GetMapping(value = "/mcpTools") + public Object mcpTools(){ + return ExtractConstantFieldUtil.extractEnumToList(xyz.thoughtset.viewer.common.ai.model.entity.purpose.ModelPurposeEnum.class); + } + @GetMapping(value = "/workingBots") + public Object workingBots(){ + return mcpBotInfoService.workingBots(); + } } diff --git a/apis/viewer-apis-client/src/main/resources/static/html/pages/McpBotInfo.json b/apis/viewer-apis-client/src/main/resources/static/html/pages/McpBotInfo.json new file mode 100644 index 0000000..71f0238 --- /dev/null +++ b/apis/viewer-apis-client/src/main/resources/static/html/pages/McpBotInfo.json @@ -0,0 +1,442 @@ +{ + "type": "page", + "title": "McpBot", + "body": [ + { + "type": "crud2", + "id": "u:165b22238b91", + "showHeader": true, + "mode": "table2", + "dsType": "api", + "syncLocation": true, + "primaryField": "id", + "loadType": "", + "api": { + "url": "/q/findList/mcpBotInfo", + "method": "get" + }, + "filter": { + "type": "form", + "title": "条件查询", + "mode": "inline", + "columnCount": 3, + "clearValueOnHidden": true, + "behavior": [ + "SimpleQuery" + ], + "body": [ + { + "name": "title", + "label": "标题", + "type": "input-text", + "size": "full", + "required": false, + "behavior": "SimpleQuery", + "id": "u:921fabbdc69b" + }, + { + "name": "url", + "label": "url", + "type": "input-text", + "size": "full", + "required": false, + "behavior": "SimpleQuery", + "id": "u:f2a8558db402" + } + ], + "actions": [ + { + "type": "button", + "label": "新增", + "onEvent": { + "click": { + "actions": [ + { + "ignoreError": false, + "actionType": "drawer", + "drawer": { + "$ref": "modal-ref-1" + } + } + ] + } + }, + "id": "u:dac928e940bf", + "level": "light" + }, + { + "type": "reset", + "label": "重置", + "id": "u:bb2fce73c947" + }, + { + "type": "submit", + "label": "查询", + "level": "primary", + "id": "u:54860b14a5ae" + } + ], + "id": "u:2da7cfd2a1a4", + "feat": "Insert" + }, + "headerToolbar": [], + "footerToolbar": [], + "columns": [ + { + "type": "tpl", + "title": "id", + "name": "id", + "id": "u:80712901c67c", + "placeholder": "-", + "width": 1, + "visible": false, + "hidden": true + }, + { + "type": "tpl", + "title": "标题", + "name": "title", + "id": "u:6e73ce9732cd", + "placeholder": "-", + "width": "20%" + }, + { + "type": "tpl", + "title": "基础URL", + "name": "baseUrl", + "id": "u:cc7f5a4411b5", + "placeholder": "", + "copyable": true, + "wrapperComponent": "", + "width": "20%" + }, + { + "type": "tpl", + "title": "备注", + "name": "remark", + "id": "u:311857ef9045", + "placeholder": "-" + }, + { + "type": "operation", + "title": "操作", + "id": "u:de7a00d036bd", + "width": "12%", + "buttons": [ + { + "type": "button", + "label": "编辑", + "level": "link", + "behavior": "Edit", + "onEvent": { + "click": { + "actions": [ + { + "actionType": "drawer", + "drawer": { + "$ref": "modal-ref-1" + } + } + ] + } + }, + "id": "u:e75ec5a637dc", + "visible": true + }, + { + "label": "删除", + "level": "link", + "id": "u:fa836482bb1e", + "type": "button", + "behavior": "Delete", + "onEvent": { + "click": { + "actions": [ + { + "actionType": "ajax", + "outputVar": "responseResult", + "options": {}, + "api": { + "url": "/c/delete/mcpBotInfo", + "method": "post", + "requestAdaptor": "", + "adaptor": "", + "messages": {}, + "data": { + "pkey": "${id}" + }, + "dataType": "form-data" + } + }, + { + "actionType": "search", + "groupType": "component", + "componentId": "u:165b22238b91" + } + ] + } + }, + "className": "m-r-xs text-danger", + "confirmText": "确认要删除数据", + "visibleOn": "${statesCode != 200}" + }, + { + "type": "button", + "label": "启用", + "onEvent": { + "click": { + "actions": [ + { + "ignoreError": false, + "outputVar": "responseResult", + "actionType": "ajax", + "options": {}, + "api": { + "url": "publishBot", + "method": "get", + "requestAdaptor": "", + "adaptor": "", + "messages": {}, + "data": { + "apiId": "${id}" + } + } + }, + { + "componentId": "u:165b22238b91", + "ignoreError": false, + "actionType": "reload" + } + ] + } + }, + "id": "u:11024054f314", + "level": "success", + "block": false, + "size": "xs", + "hidden": false, + "visibleOn": "${statesCode != 200}" + }, + { + "type": "button", + "label": "停用", + "onEvent": { + "click": { + "actions": [ + { + "ignoreError": false, + "outputVar": "responseResult", + "actionType": "ajax", + "options": {}, + "api": { + "url": "unpublishedBot", + "method": "get", + "requestAdaptor": "", + "adaptor": "", + "messages": {}, + "data": { + "apiId": "${id}" + } + } + }, + { + "componentId": "u:165b22238b91", + "ignoreError": false, + "actionType": "reload" + } + ] + } + }, + "id": "u:8cf307205f6b", + "level": "warning", + "block": false, + "size": "xs", + "hidden": false, + "visibleOn": "${statesCode == 200}" + } + ] + } + ], + "quickSaveItemApi": { + "url": "/c/save/mcpServerInfo", + "method": "post", + "requestAdaptor": "", + "adaptor": "", + "messages": {}, + "dataType": "json" + }, + "editorSetting": { + "mock": { + "enable": true, + "maxDisplayRows": 5 + } + }, + "autoFillHeight": true + } + ], + "id": "u:eca6fca9f5c3", + "asideResizor": false, + "pullRefresh": { + "disabled": true + }, + "regions": [ + "body" + ], + "definitions": { + "modal-ref-1": { + "type": "drawer", + "body": [ + { + "id": "u:03d9f0c8baa0", + "type": "form", + "title": "表单", + "mode": "flex", + "labelAlign": "top", + "dsType": "api", + "feat": "Edit", + "body": [ + { + "name": "id", + "label": false, + "row": 0, + "type": "input-text", + "id": "u:609bc92154f1", + "colSize": "1", + "hidden": true + }, + { + "type": "input-number", + "label": false, + "name": "typeCode", + "keyboard": true, + "id": "u:14d8ecf50437", + "step": 1, + "visible": false + }, + { + "name": "title", + "label": "服务名", + "type": "input-text", + "id": "u:7735aab23360" + }, + { + "name": "baseUrl", + "label": "基础url", + "type": "input-text", + "id": "u:c3c349465b9f" + }, + { + "name": "sseEndpoint", + "label": "接口名", + "type": "input-text", + "id": "u:5fb3bdcc7485" + }, + { + "name": "connectTimeout", + "label": "连接超时", + "type": "input-number", + "id": "u:2bc7f8ad9cf3", + "keyboard": true, + "step": 1, + "labelAlign": "top", + "value": 60 + }, + { + "name": "requestTimeout", + "label": "超时时间", + "type": "input-number", + "id": "u:991c7df295fd", + "keyboard": false, + "step": 1, + "labelAlign": "top", + "suffix": "", + "value": 20 + }, + { + "type": "textarea", + "label": "描述", + "name": "remark", + "id": "u:21ca23f5bd5c", + "minRows": 3, + "maxRows": 20 + } + ], + "api": { + "url": "/c/save/mcpBotInfo", + "method": "post", + "requestAdaptor": "", + "adaptor": "", + "messages": {}, + "dataType": "json" + }, + "actions": [ + { + "type": "button", + "label": "提交", + "onEvent": { + "click": { + "actions": [ + { + "actionType": "submit", + "componentId": "u:03d9f0c8baa0" + } + ] + } + }, + "level": "primary" + } + ], + "resetAfterSubmit": true, + "initApi": { + "method": "get", + "url": "/q/findOne/mcpBotInfo", + "requestAdaptor": "", + "adaptor": "", + "messages": {}, + "sendOn": "${!ISEMPTY(id)}", + "data": { + "pkey": "${id}" + } + }, + "onEvent": { + "submitSucc": { + "weight": 0, + "actions": [ + { + "componentId": "u:165b22238b91", + "groupType": "component", + "actionType": "reload" + } + ] + } + } + } + ], + "title": "MCP信息", + "id": "u:3e949e0b822b", + "actions": [ + { + "type": "button", + "actionType": "cancel", + "label": "取消", + "id": "u:53f87f3f48f7" + }, + { + "type": "button", + "actionType": "confirm", + "label": "确定", + "primary": true, + "id": "u:617828ba92c3" + } + ], + "showCloseButton": true, + "closeOnOutside": false, + "closeOnEsc": false, + "showErrorMsg": true, + "showLoading": true, + "draggable": false, + "actionType": "drawer", + "resizable": false + } + } +} \ No newline at end of file diff --git a/apis/viewer-apis-client/src/main/resources/static/html/pages/McpServer.json b/apis/viewer-apis-client/src/main/resources/static/html/pages/McpServer.json index 405185f..87e8ab8 100644 --- a/apis/viewer-apis-client/src/main/resources/static/html/pages/McpServer.json +++ b/apis/viewer-apis-client/src/main/resources/static/html/pages/McpServer.json @@ -165,7 +165,7 @@ "outputVar": "responseResult", "options": {}, "api": { - "url": "/c/delete/apiInfo", + "url": "/c/delete/mcpServerInfo", "method": "post", "requestAdaptor": "", "adaptor": "", diff --git a/apis/viewer-apis-client/src/main/resources/static/html/pages/funInfo.json b/apis/viewer-apis-client/src/main/resources/static/html/pages/funInfo.json index 6879e7c..eb5c1a4 100644 --- a/apis/viewer-apis-client/src/main/resources/static/html/pages/funInfo.json +++ b/apis/viewer-apis-client/src/main/resources/static/html/pages/funInfo.json @@ -220,13 +220,6 @@ "pullRefresh": { "disabled": true }, - "dsType": "api", - "editorSetting": { - "mock": { - "enable": true, - "maxDisplayRows": 5 - } - }, "regions": [ "body" ], @@ -277,6 +270,8 @@ { "type": "input-table", "id": "u:904bffd1aa6c", + "name": "params", + "label": "参数设定", "columns": [ { "label": "id", @@ -399,8 +394,6 @@ } ], "draggable": true, - "name": "params", - "label": "参数设定", "addable": true, "footerAddBtn": { "label": "新增", @@ -839,18 +832,21 @@ }, { "label": "mcp", - "value": "mcp", - "hiddenOn": "${true}" + "value": "mcp" }, { - "label": "parallel", - "value": "parallel", - "hiddenOn": "${true}" + "label": "tools", + "value": "tools" }, { "label": "AI创作", "value": "execAI", "hiddenOn": "${true}" + }, + { + "label": "parallel", + "value": "parallel", + "hiddenOn": "${true}" } ], "id": "u:fd07835da4f1", @@ -1011,7 +1007,7 @@ "id": "u:8d2a2efc675c", "value": "", "visible": true, - "hiddenOn": "${!(bodyType==\"FunctionCall\" || bodyType==\"execAI\" || bodyType==\"mcp\")}", + "hiddenOn": "${!(bodyType==\"FunctionCall\" || bodyType==\"execAI\" || bodyType==\"mcp\" || bodyType==\"tools\")}", "clearValueOnHidden": true, "multiple": false, "source": { @@ -1042,7 +1038,7 @@ "optionAtLeft": true, "value": true, "visible": true, - "hiddenOn": "${!(bodyType==\"FunctionCall\" || bodyType==\"execAI\" || bodyType==\"mcp\")}", + "hiddenOn": "${!(bodyType==\"FunctionCall\" || bodyType==\"execAI\" || bodyType==\"mcp\" || bodyType==\"tools\")}", "clearValueOnHidden": true } ], @@ -1079,7 +1075,7 @@ "id": "u:018b6276b71b", "minRows": 3, "maxRows": 20, - "hiddenOn": "${!(bodyType==\"FunctionCall\" || bodyType==\"execAI\" || bodyType==\"mcp\")}", + "hiddenOn": "${!(bodyType==\"FunctionCall\" || bodyType==\"execAI\" || bodyType==\"mcp\" || bodyType==\"tools\")}", "clearValueOnHidden": true } ], @@ -1438,7 +1434,7 @@ "editBtnIcon": "", "editBtnLabel": "编辑", "clearValueOnHidden": true, - "hiddenOn": "${bodyType==\"transpose\" || bodyType==\"value\" || bodyType==\"execAI\"}" + "hiddenOn": "${bodyType==\"transpose\" || bodyType==\"value\" || bodyType==\"execAI\" || bodyType==\"mcp\"}" }, { "type": "input-table", @@ -1479,7 +1475,74 @@ "removable": true, "showIndex": true, "clearValueOnHidden": true, - "hiddenOn": "${!(bodyType==\"value\" || bodyType==\"execAI\")}" + "hiddenOn": "${!(bodyType==\"value\")}" + }, + { + "type": "select", + "label": "mcp服务", + "name": "data.mcpClientIds", + "id": "u:839c1d093062", + "multiple": true, + "hiddenOn": "${!( bodyType==\"mcp\" || bodyType==\"tools\")}", + "clearable": true, + "searchable": true, + "checkAll": true, + "joinValues": true, + "source": { + "method": "get", + "url": "workingBots", + "requestAdaptor": "", + "adaptor": "", + "messages": {} + }, + "labelField": "title", + "valueField": "id", + "required": true, + "defaultCheckAll": false, + "checkAllLabel": "全选" + }, + { + "type": "input-table", + "name": "data.dataParams", + "label": "提示预设词参数", + "columns": [ + { + "label": "属性名", + "name": "paramId", + "quickEdit": { + "type": "input-text", + "name": "paramId", + "id": "u:49ed032c7b26", + "mode": "popOver" + }, + "id": "u:5f0fb6b781f8", + "placeholder": "-" + }, + { + "label": "表达式", + "name": "dataExp", + "quickEdit": { + "type": "input-text", + "name": "dataExp", + "id": "u:2c957f94f388" + }, + "id": "u:6d5fd877e447" + } + ], + "addable": true, + "footerAddBtn": { + "label": "新增", + "icon": "fa fa-plus", + "id": "u:1a024cfcbda3" + }, + "strictMode": true, + "id": "u:4d4f94282765", + "needConfirm": false, + "copyable": true, + "removable": true, + "showIndex": false, + "clearValueOnHidden": true, + "hiddenOn": "${!(bodyType==\"FunctionCall\" || bodyType==\"execAI\" || bodyType==\"mcp\" || bodyType==\"tools\")}" } ], "id": "u:1bf35f729bae", @@ -1647,16 +1710,18 @@ }, { "type": "select", - "label": "选项", + "label": "查询类型", "name": "selectType", "options": [ { "label": "queryBody", - "value": "queryBody" + "value": "queryBody", + "hiddenOn": "${OR(selectTypeLimit==5,selectTypeLimit==3,selectTypeLimit==2)}" }, { "label": "funInfo", - "value": "funInfo" + "value": "funInfo", + "hiddenOn": "${OR(selectTypeLimit==4,selectTypeLimit==3,selectTypeLimit==1)}" } ], "id": "u:55c2ecbf4ba1", @@ -1722,6 +1787,10 @@ "properties": { "eleIndex": { "type": "number" + }, + "selectTypeLimit": { + "type": "number", + "default": "6" } }, "required": [] @@ -1740,7 +1809,17 @@ "label": "确定", "primary": true } - ] + ], + "size": "md" } - } + }, + "dsType": "api", + "editorSetting": { + "mock": { + "enable": true, + "maxDisplayRows": 5 + } + }, + "subTitle": "funInfo", + "title": "方法设定" } \ No newline at end of file diff --git a/apis/viewer-apis-client/src/main/resources/static/html/pages/index.json b/apis/viewer-apis-client/src/main/resources/static/html/pages/index.json index 94ae109..26d07d3 100644 --- a/apis/viewer-apis-client/src/main/resources/static/html/pages/index.json +++ b/apis/viewer-apis-client/src/main/resources/static/html/pages/index.json @@ -12,6 +12,11 @@ "url": "McpServer", "schemaApi": "/html/pages/McpServer.json" }, + { + "label": "McpBot", + "url": "McpBot", + "schemaApi": "/html/pages/McpBotInfo.json" + }, { "label": "定义AI源", "url": "AINode", diff --git a/executor/viewer-executor-blocks/pom.xml b/executor/viewer-executor-blocks/pom.xml index add7290..9b20e24 100644 --- a/executor/viewer-executor-blocks/pom.xml +++ b/executor/viewer-executor-blocks/pom.xml @@ -26,6 +26,11 @@ xyz.thoughtset.viewer viewer-modules-fun + + + xyz.thoughtset.viewer + viewer-ai-mcp-client + org.springframework.ai spring-ai-model diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AIChatExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AIChatExecutor.java new file mode 100644 index 0000000..ca65f1d --- /dev/null +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AIChatExecutor.java @@ -0,0 +1,147 @@ +package xyz.thoughtset.viewer.executor.blocks.executor; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; +import org.springframework.ai.chat.memory.MessageWindowChatMemory; +import org.springframework.ai.chat.messages.*; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.model.tool.ToolCallingChatOptions; +import org.springframework.ai.model.tool.ToolCallingManager; +import org.springframework.ai.model.tool.ToolExecutionResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.StringUtils; +import xyz.thoughtset.viewer.common.ai.model.entity.ModelParam; +import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; +import xyz.thoughtset.viewer.modules.step.entity.AISupportBody; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; +import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; + +import java.util.*; + + +@Slf4j +@SuppressWarnings({"unchecked","SpringJavaInjectionPointsAutowiringInspection"}) +public abstract class AIChatExecutor extends AbstractAISupportBlockExecutor { + @Autowired + protected ToolCallingManager toolCallingManager; +// @Autowired +// protected FunctionCallBlockToolCallbackProvider provider; + + + protected abstract void loadToolCallbacks(BlockInfo block, T body, ChatClient.Builder builder); + protected ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse){ + return toolCallingManager.executeToolCalls(prompt, chatResponse); + } + +//先选择函数,再加载参数执行函数 + @SneakyThrows + @Override + protected Object doQuery(BlockInfo block, T body, Map params, ExpressionParser parser, StandardEvaluationContext context) throws ExecException { + + HashMap filterMap = filterDataAsMapForPrompt(body, params, parser, context); + ModelParam modelParam = loadModelParam(body); + ChatClient.Builder builder = modelFactory.clientBuilder(modelParam); + MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder() + .maxMessages(modelParam.getMaxMemory()) + .build(); + + ToolCallingChatOptions.Builder toolCallingBuilder = ToolCallingChatOptions.builder() + .internalToolExecutionEnabled(false); + String limitSystemPrompt = + "选择适当的方法执行,由于部分参数存储在本地,对于非required参数,可以填入建议值或空值。预期值与方法执行结果不一致时,以方法结果为准。"; + String systemText = modelParam.chatSystemPrompt(); + if (body.getJsonType()!=null && body.getJsonType().booleanValue()){ + + systemText = limitSystemPrompt+""" + 最终结果必须按照JSON数组格式返回,不要有其余任何内容,不要有任何拼写错误,不要有任何语法错误,当后续要求与当前冲突时,以当前要求为准! + """ + + (StringUtils.hasText(systemText) ? systemText : ""); + } + String finalSystemText = systemText; + builder.defaultSystem(fillAndRenderPromptToStr(finalSystemText, parser, context, filterMap)); + loadToolCallbacks(block,body,builder); + ChatClient client = builder + .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) + .build(); + ChatOptions chatOptions = toolCallingBuilder.build(); + UserMessage message = UserMessage.builder() + .text(fillAndRenderPromptToStr(body.getUserMsg(), parser, context, filterMap)) + .build(); +// new UserMessage(body.getUserMsg(),params); + String chatId = block.getId()+ UUID.randomUUID(); + saveMegToChatMemory(chatId, Collections.singletonList(message), chatMemory); + Prompt chatPrompt = new Prompt(chatMemory.get(chatId),chatOptions); + ChatResponse chatResponse = client + .prompt(chatPrompt) + .toolContext(filterMap).call().chatResponse(); + List messageList = new ArrayList<>(); + while (chatResponse.hasToolCalls()) { + // 获取AI助手的响应消息 + AssistantMessage assistantMessage = chatResponse.getResult().getOutput(); + log.debug("***" + assistantMessage.getText()); + + // 获取AI要求调用的工具列表 + List toolCalls = assistantMessage.getToolCalls(); + toolCalls.forEach(toolCall -> { + log.debug("*** 准备调用工具{}:{},参数:({})", toolCall.type(), toolCall.name(), toolCall.arguments()); + }); + + // 执行工具调用 + ToolExecutionResult toolExecutionResult = null; + try { + toolExecutionResult = executeToolCalls(chatPrompt, chatResponse); + } catch (Exception e) { + log.error("调用工具失败", e); + messageList.add(new AssistantMessage("调用工具失败:" + e.getMessage())); + break; + } + + List toolResultMessages = toolExecutionResult.conversationHistory(); + log.debug("*** 执行结果" + toolResultMessages); + + Message lastMessage = toolResultMessages.get(toolResultMessages.size() - 1); + if (lastMessage.getMessageType() == MessageType.TOOL) { + ToolResponseMessage toolMessage = (ToolResponseMessage) lastMessage; + toolMessage.getMetadata().put("toolArguments", toolCalls); + messageList.add(toolMessage); + + // 记录每个工具调用的详细信息 + for (ToolResponseMessage.ToolResponse resp : toolMessage.getResponses()) { + log.debug("*** {}#{}#{}",resp.id(),resp.name(),resp.responseData()); + } + } + saveMegToChatMemory(chatId, toolResultMessages, chatMemory); + chatPrompt = new Prompt(chatMemory.get(chatId), chatOptions); + chatResponse = client + .prompt(chatPrompt) + .call() + .chatResponse(); + } + + String val = chatResponse.getResult().getOutput().getText(); + log.debug("===结果:{}", val); + Object result; + + try { + result = objectMapper.readValue(val, DATA_TYPE); + }catch (Exception e){ + if (!e.getClass().equals(com.fasterxml.jackson.core.JsonParseException.class)){ + log.error("错误结果:{}", val); + e.printStackTrace(); + } + result = val; + } + + return result; + } + + + + +} diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ai/AbstractAISupportBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AbstractAISupportBlockExecutor.java similarity index 50% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ai/AbstractAISupportBlockExecutor.java rename to executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AbstractAISupportBlockExecutor.java index 4f06b0f..b556e91 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ai/AbstractAISupportBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AbstractAISupportBlockExecutor.java @@ -1,28 +1,31 @@ -package xyz.thoughtset.viewer.executor.blocks.executor.ai; +package xyz.thoughtset.viewer.executor.blocks.executor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.MessageType; +import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.lang.NonNull; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import xyz.thoughtset.viewer.common.ai.model.dao.ModelParamDao; import xyz.thoughtset.viewer.common.ai.model.entity.ModelParam; import xyz.thoughtset.viewer.common.ai.model.factory.ModelFactory; import xyz.thoughtset.viewer.common.ai.model.service.ModelParamService; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.entity.AISupportBody; -import xyz.thoughtset.viewer.executor.blocks.entity.BaseBlockBody; -import xyz.thoughtset.viewer.executor.blocks.entity.FunctionCallBody; +import xyz.thoughtset.viewer.modules.step.entity.AISupportBody; import xyz.thoughtset.viewer.executor.blocks.executor.AbstractBlockExecutor; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; +import xyz.thoughtset.viewer.modules.step.entity.block.EleParam; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; @Slf4j @SuppressWarnings({"unchecked","SpringJavaInjectionPointsAutowiringInspection"}) @@ -32,6 +35,48 @@ public abstract class AbstractAISupportBlockExecutor ex @Autowired protected ModelFactory modelFactory; + + protected static String renderPrompt(String template, Map params){ + boolean paramsNotEmpty = !ObjectUtils.isEmpty(params); + if ( !paramsNotEmpty || !StringUtils.hasText(template)){ + return template; + } + PromptTemplate promptTemplate = PromptTemplate.builder() + .template(template) + .variables(params) + .build(); + return promptTemplate.render(); + } + + protected HashMap filterDataAsMapForPrompt( T body, Map params, ExpressionParser parser, StandardEvaluationContext context){ + HashMap resultMaps = new HashMap<>(params); + List eleParams = body.getDataParams(); + if (Objects.nonNull(eleParams)){ + for (EleParam blockParam : eleParams){ + Object value = ""; + if(StringUtils.hasLength(blockParam.getDataExp())){ + String exp =blockParam.getDataExp(); + exp = blockParam.getDataExp().startsWith("#")?exp:"#"+exp; + value = parser.parseExpression(exp).getValue(context); + } + String key = blockParam.getParamId(); + if (StringUtils.hasText(key) && !ObjectUtils.isEmpty(value)){ + if (!(value instanceof String)){ + try { + value = objectMapper.writeValueAsString(value); + } catch (Exception e) { + log.error("objectMapper error", value); + e.printStackTrace(); + value = ""; + } + } + resultMaps.put(key, value); + } + } + } + return resultMaps; + } + protected void saveMegToChatMemory(String chatId, List messages, ChatMemory chatMemory) { chatMemory.add(chatId, messages); @@ -63,13 +108,21 @@ public abstract class AbstractAISupportBlockExecutor ex return modelParam; } - @SneakyThrows protected static String fillPrompt(String template, ExpressionParser parser,StandardEvaluationContext context) { if (!StringUtils.hasText(template)){ return template; } - // 使用 SpEL 表达式解析模板并返回填充后的结果 - return parser.parseExpression(template).getValue(context, String.class); + try { + return parser.parseExpression(template).getValue(context, String.class); + }catch (Exception e){ + return template; + } + } + + protected static String fillAndRenderPromptToStr(String template, ExpressionParser parser,StandardEvaluationContext context,Map resultMaps) { + return renderPrompt( + fillPrompt(template, parser, context),resultMaps + ); } } diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AbstractBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AbstractBlockExecutor.java index c88ec12..01fc442 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AbstractBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/AbstractBlockExecutor.java @@ -3,6 +3,7 @@ package xyz.thoughtset.viewer.executor.blocks.executor; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; +import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.expression.ExpressionParser; @@ -10,18 +11,13 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.entity.BaseBlockBody; -import xyz.thoughtset.viewer.executor.blocks.entity.BlockTypeEnum; -import xyz.thoughtset.viewer.executor.blocks.entity.BodyEle; +import xyz.thoughtset.viewer.modules.step.entity.BaseBlockBody; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; import xyz.thoughtset.viewer.executor.blocks.utlis.BlockArgsUtils; import xyz.thoughtset.viewer.modules.ds.core.factory.ExecutorManager; import xyz.thoughtset.viewer.executor.blocks.constants.NodeConstant; -import xyz.thoughtset.viewer.modules.step.entity.BlockParam; -import xyz.thoughtset.viewer.modules.step.entity.QueryBlock; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; -import xyz.thoughtset.viewer.modules.step.entity.block.EleParam; -import xyz.thoughtset.viewer.modules.step.service.QueryBlockService; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -72,6 +68,7 @@ public abstract class AbstractBlockExecutor implements body.setBodyEles((List) block.getBodys()); if (ObjectUtils.isEmpty(body.getBodyEles()) && !BlockTypeEnum.EXECAI.equals(supportType) + && !BlockTypeEnum.MCP.equals(supportType) && !BlockTypeEnum.TRANSPOSE.equals(supportType)) { return null; } diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ExecAIBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ExecAIBlockExecutor.java index a9a450f..55347c3 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ExecAIBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ExecAIBlockExecutor.java @@ -1,17 +1,19 @@ package xyz.thoughtset.viewer.executor.blocks.executor; -import org.springframework.ai.chat.client.ChatClient; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.image.Image; +import org.springframework.ai.image.ImageModel; +import org.springframework.ai.image.ImagePrompt; +import org.springframework.ai.image.ImageResponse; import org.springframework.ai.model.Model; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import xyz.thoughtset.viewer.common.ai.model.entity.ModelParam; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.entity.BlockTypeEnum; -import xyz.thoughtset.viewer.executor.blocks.entity.ExecAIBody; -import xyz.thoughtset.viewer.executor.blocks.entity.ValueBody; -import xyz.thoughtset.viewer.executor.blocks.executor.ai.AbstractAISupportBlockExecutor; +import xyz.thoughtset.viewer.modules.step.entity.ExecAIBody; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; import xyz.thoughtset.viewer.modules.step.entity.block.EleParam; @@ -21,43 +23,54 @@ import java.util.List; import java.util.Map; import java.util.Objects; +@Slf4j @Component public class ExecAIBlockExecutor extends AbstractAISupportBlockExecutor { @Override protected Object doQuery(BlockInfo block, ExecAIBody body, Map params, ExpressionParser parser, StandardEvaluationContext context) throws ExecException { - HashMap resultMaps = new HashMap<>(params); -// BlockBodyEle bodyEle = block.firstBody(); -// List eleParams = bodyEle.getParams(); +// HashMap resultMaps = new HashMap<>(params); +// List eleParams = body.getDataParams(); // if (Objects.nonNull(eleParams)){ -// int size = eleParams.size(); // for (EleParam blockParam : eleParams){ -// Object value = null; +// Object value = ""; // if(StringUtils.hasLength(blockParam.getDataExp())){ // String exp =blockParam.getDataExp(); // exp = blockParam.getDataExp().startsWith("#")?exp:"#"+exp; // value = parser.parseExpression(exp).getValue(context); // } -// //qbid作为值对应名称进行存入 // String key = blockParam.getParamId(); -// if (StringUtils.hasText(key)){ -// context.setVariable(key, value); -// params.put(key, value); +// if (StringUtils.hasText(key) && !ObjectUtils.isEmpty(value)){ +// if (!(value instanceof String)){ +// try { +// value = objectMapper.writeValueAsString(value); +// } catch (Exception e) { +// log.error("objectMapper error", value); +// e.printStackTrace(); +// value = ""; +// } +// } // resultMaps.put(key, value); -// } else if (size == 1) { -// return value; // } // } // } -// ModelParam modelParam = loadModelParam(body); -// String userPrompt = fillPrompt(body.getUserMsg(), parser, context); + HashMap resultMaps = filterDataAsMapForPrompt(body, params, parser, context); + ModelParam modelParam = loadModelParam(body); + String userPrompt = fillAndRenderPromptToStr(body.getUserMsg(), parser, context, resultMaps); // String systemPrompt = fillPrompt(modelParam.getSystemPrompt(), parser, context); -// Model aiModel = modelFactory.buildModel(modelParam); + Model aiModel = modelFactory.buildModel(modelParam); + Object result = null; + if (aiModel instanceof ImageModel){ + result = ((ImageModel) aiModel).call( + new ImagePrompt(userPrompt) + ).getResult().getOutput(); + } +// aiModel. // if (StringUtils.hasText(systemPrompt)){ // builder.defaultSystem(systemPrompt); // } // ChatClient client = builder.build(); - return null; + return result; } } diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ExecutorRegistry.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ExecutorRegistry.java index 4af8a5f..361b301 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ExecutorRegistry.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ExecutorRegistry.java @@ -1,7 +1,7 @@ package xyz.thoughtset.viewer.executor.blocks.executor; -import xyz.thoughtset.viewer.executor.blocks.entity.BlockTypeEnum; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; import java.util.concurrent.ConcurrentHashMap; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/FunctionCallBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/FunctionCallBlockExecutor.java new file mode 100644 index 0000000..08c3892 --- /dev/null +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/FunctionCallBlockExecutor.java @@ -0,0 +1,42 @@ +package xyz.thoughtset.viewer.executor.blocks.executor; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import xyz.thoughtset.viewer.executor.blocks.tool.FunctionCallBlockToolCallbackProvider; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; +import xyz.thoughtset.viewer.modules.step.entity.FunctionCallBody; +import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; +import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + + +@Slf4j +@Component +public class FunctionCallBlockExecutor extends AIChatExecutor { + @Autowired + private FunctionCallBlockToolCallbackProvider provider; + + @Override + BlockTypeEnum getSupportType() { + return BlockTypeEnum.FUNCTION_CALL; + } + + @Override + protected void loadToolCallbacks(BlockInfo block,FunctionCallBody body, ChatClient.Builder builder) { + List funs = body.getBodyEles(); + if (!ObjectUtils.isEmpty(funs)){ + Map funParamMap = funs.parallelStream().collect(Collectors.toMap( + BlockBodyEle::getEleId, Function.identity() + )); + builder.defaultToolCallbacks(provider.getToolCallbacks(block.getId(),funParamMap)); + } + } + + +} diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/FunctionCallingBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/FunctionCallingBlockExecutor.java index 45c6fbc..858d99c 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/FunctionCallingBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/FunctionCallingBlockExecutor.java @@ -3,50 +3,34 @@ package xyz.thoughtset.viewer.executor.blocks.executor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; -import org.springframework.ai.chat.client.ChatClientResponse; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; -import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.ai.chat.messages.*; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.chat.prompt.DefaultChatOptions; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.converter.ListOutputConverter; -import org.springframework.ai.deepseek.DeepSeekChatOptions; import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.model.tool.ToolExecutionResult; -import org.springframework.ai.tool.ToolCallback; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.ParameterizedTypeReference; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import xyz.thoughtset.viewer.common.ai.model.dao.ModelParamDao; import xyz.thoughtset.viewer.common.ai.model.entity.ModelParam; -import xyz.thoughtset.viewer.common.ai.model.entity.purpose.ChatModelSetting; -import xyz.thoughtset.viewer.common.ai.model.factory.ModelFactory; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.entity.BlockTypeEnum; -import xyz.thoughtset.viewer.executor.blocks.entity.FunctionCallBody; -import xyz.thoughtset.viewer.executor.blocks.executor.ai.AbstractAISupportBlockExecutor; -import xyz.thoughtset.viewer.executor.blocks.tool.BlockToolCallbackProvider; +import xyz.thoughtset.viewer.modules.step.entity.FunctionCallBody; import xyz.thoughtset.viewer.executor.blocks.tool.FunctionCallBlockToolCallbackProvider; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; import java.util.*; -import java.util.concurrent.atomic.LongAdder; import java.util.function.Function; import java.util.stream.Collectors; @Slf4j -@Component +@Deprecated public class FunctionCallingBlockExecutor extends AbstractAISupportBlockExecutor { // @Autowired // protected ModelParamDao modelParamDao; @@ -101,7 +85,7 @@ public class FunctionCallingBlockExecutor extends AbstractAISupportBlockExecutor )); builder.defaultToolCallbacks(provider.getToolCallbacks(block.getId(),funParamMap)); - toolCallingBuilder = toolCallingBuilder.toolCallbacks(provider.getToolCallbacks(block.getId(),funParamMap)); +// toolCallingBuilder = toolCallingBuilder.toolCallbacks(provider.getToolCallbacks(block.getId(),funParamMap)); } ChatClient client = builder .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/IteratorBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/IteratorBlockExecutor.java index c1c5b9a..ff0aca1 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/IteratorBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/IteratorBlockExecutor.java @@ -6,8 +6,7 @@ import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; import xyz.thoughtset.viewer.executor.blocks.constants.NodeConstant; -import xyz.thoughtset.viewer.executor.blocks.entity.IteratorBody; -import xyz.thoughtset.viewer.executor.blocks.entity.LoopBody; +import xyz.thoughtset.viewer.modules.step.entity.IteratorBody; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/LoopBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/LoopBlockExecutor.java index 17518f5..4163a74 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/LoopBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/LoopBlockExecutor.java @@ -5,8 +5,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.entity.BodyEle; -import xyz.thoughtset.viewer.executor.blocks.entity.LoopBody; +import xyz.thoughtset.viewer.modules.step.entity.LoopBody; import xyz.thoughtset.viewer.executor.blocks.constants.NodeConstant; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/MCPBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/MCPBlockExecutor.java index 527a86b..7b26352 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/MCPBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/MCPBlockExecutor.java @@ -1,102 +1,44 @@ package xyz.thoughtset.viewer.executor.blocks.executor; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.model.tool.ToolExecutionResult; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; -import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.constants.NodeConstant; -import xyz.thoughtset.viewer.executor.blocks.entity.BlockTypeEnum; -import xyz.thoughtset.viewer.executor.blocks.entity.MCPBody; -import xyz.thoughtset.viewer.executor.blocks.entity.MCPBody; -import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; +import org.springframework.util.StringUtils; +import xyz.thoughtset.viewer.ai.mcp.client.factory.McpBotFactory; +import xyz.thoughtset.viewer.executor.blocks.tool.FunctionCallBlockToolCallbackProvider; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; +import xyz.thoughtset.viewer.modules.step.entity.MCPBody; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.LongAdder; -//todo -//@Component -public class MCPBlockExecutor extends AbstractBlockExecutor { + +@Slf4j +@Component +public class MCPBlockExecutor extends AIChatExecutor { + @Autowired + private McpBotFactory botFactory; + @Override BlockTypeEnum getSupportType() { return BlockTypeEnum.MCP; } @Override - protected Object doQuery(BlockInfo block, MCPBody body, Map params, ExpressionParser parser, StandardEvaluationContext context) throws ExecException { - List results = new ArrayList<>(); - StringBuilder sb = new StringBuilder("你是方法调度助手,任务是从用户问题中选择最合适的方法,并给出参数。\n可用方法:\n"); -// registry.getAll().forEach((name, regMethod) -> -// sb.append(name).append(" - ").append(regMethod.description()).append("\n")); - - sb.append("请输出JSON:{\n") - .append(" \"methodName\": \"方法名\",\n") - .append(" \"parameters\": { \"参数名\": 值 }\n") - .append("}\n只输出JSON,不要解释。"); - String systemPrompt = """ - 你是一个数据库查询助手。 - 可用数据库查询方法: - 1. findByName(name: String) - 按名字模糊查找用户 - 2. findByAgeGreaterThan(age: int) - 查询年龄大于指定值的用户 - 3. findByNameAndMinAge(name: String, minAge: int) - 按名字模糊+年龄条件查询 - - 当我给你一个自然语言查询时,请只返回 JSON,不要任何额外说明: - { - "method": "方法名", - "params": { "参数名": 值 } - } - """; -// LongAdder index = new LongAdder(); -// context.setVariable(NodeConstant.DATA_NODE, results); -// params.put(NodeConstant.DATA_NODE, results); -// Collection dataList = dataList(body, params, parser, context); -// if (!ObjectUtils.isEmpty(dataList)) { -// for (Object data : dataList) { -// context.setVariable(NodeConstant.CURRENT_DATA_NODE_INDEX, index.intValue()); -// params.put(NodeConstant.CURRENT_DATA_NODE_INDEX, index.intValue()); -// params.put(NodeConstant.CURRENT_DATA, data); -// context.setVariable(NodeConstant.CURRENT_DATA, data); -// doLoop(results, body, params, parser, context, index); -// } -// } -// params.remove(NodeConstant.DATA_NODE); -// context.setVariable(NodeConstant.DATA_NODE,null); -// context.setVariable(NodeConstant.CURRENT_DATA_NODE_INDEX, null); -// params.remove(NodeConstant.CURRENT_DATA_NODE_INDEX); -// context.setVariable(NodeConstant.CURRENT_DATA, null); -// params.remove(NodeConstant.CURRENT_DATA); - return results; - } - - protected void doLoop(List results, MCPBody body, Map params, ExpressionParser parser, StandardEvaluationContext context, LongAdder index) { - List eleResults = new ArrayList<>(body.getBodyEles().size()+1); - results.add(eleResults); - for (BlockBodyEle ele : body.getBodyEles()) { - eleResults.add(execNode(ele, params, parser, context)); + protected void loadToolCallbacks(BlockInfo block,MCPBody body, ChatClient.Builder builder) { + String clientIds = body.getMcpClientIds(); + if (StringUtils.hasText(clientIds)){ + builder.defaultToolCallbacks( + botFactory.clientsTools( + List.of(clientIds.split(",")) + ) + ); } - index.increment(); } -// protected Collection dataList(MCPBody body, Map params, ExpressionParser parser, StandardEvaluationContext context) { -// String condition = body.getIteratorRepx(); -// if (condition != null && !condition.isEmpty()) { -// Object result = parser.parseExpression(condition.startsWith("#")?condition:"#"+condition).getValue(context); -// if(!ObjectUtils.isEmpty(result)) { -// if (result instanceof Map) { -// return ((Map) result).entrySet(); -// } else if (result instanceof Collection) { -// return (Collection) result; -// } else { -// return List.of(result); -// } -// } -// } -// return null; -// } - - } diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/SingleBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/SingleBlockExecutor.java index ae4e58a..a9007e4 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/SingleBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/SingleBlockExecutor.java @@ -4,8 +4,8 @@ import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.entity.BlockTypeEnum; -import xyz.thoughtset.viewer.executor.blocks.entity.SingleBody; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; +import xyz.thoughtset.viewer.modules.step.entity.SingleBody; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; import java.util.Map; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/TaskBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/TaskBlockExecutor.java index 2732eb1..dbd6f2e 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/TaskBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/TaskBlockExecutor.java @@ -5,8 +5,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; import xyz.thoughtset.viewer.executor.blocks.constants.NodeConstant; -import xyz.thoughtset.viewer.executor.blocks.entity.BodyEle; -import xyz.thoughtset.viewer.executor.blocks.entity.TaskBody; +import xyz.thoughtset.viewer.modules.step.entity.TaskBody; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ToolsBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ToolsBlockExecutor.java new file mode 100644 index 0000000..f2648ab --- /dev/null +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ToolsBlockExecutor.java @@ -0,0 +1,63 @@ +package xyz.thoughtset.viewer.executor.blocks.executor; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.tool.ToolCallback; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; +import xyz.thoughtset.viewer.ai.mcp.client.factory.McpBotFactory; +import xyz.thoughtset.viewer.executor.blocks.tool.FunctionCallBlockToolCallbackProvider; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; +import xyz.thoughtset.viewer.modules.step.entity.FunctionCallBody; +import xyz.thoughtset.viewer.modules.step.entity.ToolsBody; +import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; +import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + + +@Slf4j +@Component +public class ToolsBlockExecutor extends AIChatExecutor { + @Autowired + private FunctionCallBlockToolCallbackProvider provider; + @Autowired + private McpBotFactory botFactory; + + @Override + BlockTypeEnum getSupportType() { + return BlockTypeEnum.TOOLS; + } + + @Override + protected void loadToolCallbacks(BlockInfo block,ToolsBody body, ChatClient.Builder builder) { + List funs = body.getBodyEles(); + List toolCallbackList = new ArrayList<>(); + if (!ObjectUtils.isEmpty(funs)){ + Map funParamMap = funs.parallelStream().collect(Collectors.toMap( + BlockBodyEle::getEleId, Function.identity() + )); + toolCallbackList.addAll( + Arrays.asList(provider.getToolCallbacks(block.getId(),funParamMap)) + ); + } + String clientIds = body.getMcpClientIds(); + if (StringUtils.hasText(clientIds)){ + toolCallbackList.addAll( + Arrays.asList(botFactory.clientsTools( + List.of(clientIds.split(",")) + )) + ); + } + builder.defaultToolCallbacks(toolCallbackList); + } + + +} diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/TransposeBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/TransposeBlockExecutor.java index 9c2f37e..faed190 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/TransposeBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/TransposeBlockExecutor.java @@ -4,14 +4,10 @@ import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.entity.BlockTypeEnum; -import xyz.thoughtset.viewer.executor.blocks.entity.TransposeBody; -import xyz.thoughtset.viewer.executor.blocks.entity.ValueBody; -import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; +import xyz.thoughtset.viewer.modules.step.entity.TransposeBody; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; -import xyz.thoughtset.viewer.modules.step.entity.block.EleParam; import java.util.*; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ValueBlockExecutor.java b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ValueBlockExecutor.java index cc9053d..8c7a8cd 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ValueBlockExecutor.java +++ b/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/executor/ValueBlockExecutor.java @@ -5,9 +5,8 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import xyz.thoughtset.viewer.common.exc.exceptions.ExecException; -import xyz.thoughtset.viewer.executor.blocks.entity.BlockTypeEnum; -import xyz.thoughtset.viewer.executor.blocks.entity.ValueBody; -import xyz.thoughtset.viewer.modules.step.entity.BlockParam; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; +import xyz.thoughtset.viewer.modules.step.entity.ValueBody; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; import xyz.thoughtset.viewer.modules.step.entity.block.EleParam; diff --git a/modules/viewer-modules-api/src/main/java/xyz/thoughtset/viewer/modules/api/service/ApiInfoServiceImpl.java b/modules/viewer-modules-api/src/main/java/xyz/thoughtset/viewer/modules/api/service/ApiInfoServiceImpl.java index e9b9bef..3c7619e 100644 --- a/modules/viewer-modules-api/src/main/java/xyz/thoughtset/viewer/modules/api/service/ApiInfoServiceImpl.java +++ b/modules/viewer-modules-api/src/main/java/xyz/thoughtset/viewer/modules/api/service/ApiInfoServiceImpl.java @@ -1,6 +1,7 @@ package xyz.thoughtset.viewer.modules.api.service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import jakarta.annotation.PostConstruct; import lombok.SneakyThrows; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; @@ -149,7 +150,7 @@ public class ApiInfoServiceImpl extends BaseServiceImpl imp dynamicApiService.unregisterMapping(id); } - @EventListener(ApplicationReadyEvent.class) + @PostConstruct public void initData() { // 初始化时加载所有API QueryWrapper queryWrapper = new QueryWrapper<>(); diff --git a/modules/viewer-modules-mcp-server/src/main/java/xyz/thoughtset/viewer/modules/mcp/server/ModuleMCPServerAutoConfiguration.java b/modules/viewer-modules-mcp-server/src/main/java/xyz/thoughtset/viewer/modules/mcp/server/ModuleMCPServerAutoConfiguration.java index 90091e0..4218321 100644 --- a/modules/viewer-modules-mcp-server/src/main/java/xyz/thoughtset/viewer/modules/mcp/server/ModuleMCPServerAutoConfiguration.java +++ b/modules/viewer-modules-mcp-server/src/main/java/xyz/thoughtset/viewer/modules/mcp/server/ModuleMCPServerAutoConfiguration.java @@ -7,7 +7,6 @@ import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan -//@EnableConfigurationProperties @MapperScan(basePackages = "xyz.thoughtset.viewer.modules.mcp.server.dao") public class ModuleMCPServerAutoConfiguration { } diff --git a/modules/viewer-modules-mcp-server/src/main/java/xyz/thoughtset/viewer/modules/mcp/server/service/McpServerInfoServiceImpl.java b/modules/viewer-modules-mcp-server/src/main/java/xyz/thoughtset/viewer/modules/mcp/server/service/McpServerInfoServiceImpl.java index eb5a5b9..f80e088 100644 --- a/modules/viewer-modules-mcp-server/src/main/java/xyz/thoughtset/viewer/modules/mcp/server/service/McpServerInfoServiceImpl.java +++ b/modules/viewer-modules-mcp-server/src/main/java/xyz/thoughtset/viewer/modules/mcp/server/service/McpServerInfoServiceImpl.java @@ -1,6 +1,7 @@ package xyz.thoughtset.viewer.modules.mcp.server.service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import jakarta.annotation.PostConstruct; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -30,7 +31,7 @@ public class McpServerInfoServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.lambda() diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/AISupportBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/AISupportBody.java similarity index 61% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/AISupportBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/AISupportBody.java index cb9e70f..f2011e3 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/AISupportBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/AISupportBody.java @@ -1,8 +1,11 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import xyz.thoughtset.viewer.modules.step.entity.block.EleParam; + +import java.util.List; @Data @NoArgsConstructor @@ -12,5 +15,6 @@ public abstract class AISupportBody extends BaseBlockBody { protected String modelId; protected String userMsg; protected Boolean jsonType; + protected List dataParams; } diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BaseBlockBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BaseBlockBody.java similarity index 93% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BaseBlockBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BaseBlockBody.java index 6b9454d..5396768 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BaseBlockBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BaseBlockBody.java @@ -1,4 +1,4 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BlockTypeEnum.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BlockTypeEnum.java similarity index 94% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BlockTypeEnum.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BlockTypeEnum.java index 5f30f84..56c7ff6 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BlockTypeEnum.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BlockTypeEnum.java @@ -1,4 +1,4 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.Getter; @@ -14,6 +14,7 @@ public enum BlockTypeEnum { ITERATOR("iterator", IteratorBody.class), // 并行块 // PARALLEL("parallel", ParallelBody.class), + TOOLS("tools", ToolsBody.class), // MCP MCP("mcp", MCPBody.class), // FunctionCall diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BodyEle.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BodyEle.java similarity index 87% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BodyEle.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BodyEle.java index 0651474..6c47cb1 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/BodyEle.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/BodyEle.java @@ -1,4 +1,4 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ChooseBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ChooseBody.java similarity index 87% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ChooseBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ChooseBody.java index 6666606..1e33ea4 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ChooseBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ChooseBody.java @@ -1,4 +1,4 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ExecAIBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ExecAIBody.java similarity index 70% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ExecAIBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ExecAIBody.java index 4941356..1caf563 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ExecAIBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ExecAIBody.java @@ -1,8 +1,6 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; @Data //@NoArgsConstructor diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/FunctionCallBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/FunctionCallBody.java similarity index 72% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/FunctionCallBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/FunctionCallBody.java index eb51b90..33d3e8e 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/FunctionCallBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/FunctionCallBody.java @@ -1,8 +1,6 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; @Data //@NoArgsConstructor diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/IteratorBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/IteratorBody.java similarity index 87% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/IteratorBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/IteratorBody.java index 6db6378..71cda17 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/IteratorBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/IteratorBody.java @@ -1,4 +1,4 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/LoopBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/LoopBody.java similarity index 88% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/LoopBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/LoopBody.java index 2a04ad7..5b132ec 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/LoopBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/LoopBody.java @@ -1,4 +1,4 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/MCPBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/MCPBody.java similarity index 49% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/MCPBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/MCPBody.java index 4c9eaab..f829f42 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/MCPBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/MCPBody.java @@ -1,15 +1,16 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -@Data -//@NoArgsConstructor -//@AllArgsConstructor -public class MCPBody extends BaseBlockBody { +import java.util.List; -// private String bodyType = BlockTypeEnum.MCP.getType(); +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MCPBody extends AISupportBody { + protected String mcpClientIds; @Override protected BlockTypeEnum supportType() { diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/SingleBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/SingleBody.java similarity index 78% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/SingleBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/SingleBody.java index 518343e..f37a76f 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/SingleBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/SingleBody.java @@ -1,8 +1,6 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; @Data diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/TaskBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/TaskBody.java similarity index 70% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/TaskBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/TaskBody.java index fc0664c..af48236 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/TaskBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/TaskBody.java @@ -1,8 +1,6 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; @Data //@NoArgsConstructor diff --git a/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ToolsBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ToolsBody.java new file mode 100644 index 0000000..9250aa9 --- /dev/null +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ToolsBody.java @@ -0,0 +1,15 @@ +package xyz.thoughtset.viewer.modules.step.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +public class ToolsBody extends MCPBody { + + @Override + protected BlockTypeEnum supportType() { + return BlockTypeEnum.TOOLS; + } + +} diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/TransposeBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/TransposeBody.java similarity index 87% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/TransposeBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/TransposeBody.java index 68fc38a..bbf16ee 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/TransposeBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/TransposeBody.java @@ -1,4 +1,4 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ValueBody.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ValueBody.java similarity index 70% rename from executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ValueBody.java rename to modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ValueBody.java index 90fe9bd..55a6f5a 100644 --- a/executor/viewer-executor-blocks/src/main/java/xyz/thoughtset/viewer/executor/blocks/entity/ValueBody.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/entity/ValueBody.java @@ -1,8 +1,6 @@ -package xyz.thoughtset.viewer.executor.blocks.entity; +package xyz.thoughtset.viewer.modules.step.entity; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; @Data //@NoArgsConstructor diff --git a/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/service/BlockInfoServiceImpl.java b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/service/BlockInfoServiceImpl.java index e78acec..fb43667 100644 --- a/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/service/BlockInfoServiceImpl.java +++ b/modules/viewer-modules-step/src/main/java/xyz/thoughtset/viewer/modules/step/service/BlockInfoServiceImpl.java @@ -14,6 +14,7 @@ import org.springframework.util.StringUtils; import xyz.thoughtset.viewer.common.crud.core.service.BaseServiceImpl; import xyz.thoughtset.viewer.modules.step.dao.BlockBodyEleDao; import xyz.thoughtset.viewer.modules.step.dao.BlockInfoDao; +import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum; import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle; import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo; import xyz.thoughtset.viewer.modules.step.entity.block.EleParam; @@ -44,8 +45,10 @@ public class BlockInfoServiceImpl extends BaseServiceImplviewer-ai-mcp-server ${revision} + + xyz.thoughtset.viewer + viewer-ai-mcp-client + ${revision} + xyz.thoughtset.viewer viewer-modules-mcp-server -- Gitee