diff --git a/README.md b/README.md index 78e47392ba27f7cd037e28f667b4d4b2efe43ec0..2fd14bffbaefc2559ab7c7f962ff835d26ec73b9 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 ef756c5c45d660063ac225e4d4f3905fdd9aacbc..90b4d49d95fa9156fa2084ee64f127a553d5992d 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 52a5f319558b223175bcd01d24c82b1b88f22ee9..324de05dd0b5c7d358cdcf71b4e3a6fcaaaf6e42 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 94b82b5237e36f0a7f75638e3f5f574ce9849c15..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..80b9664e02afbcdc8e0fce8c6ef14ea17a73ffe9 --- /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 0000000000000000000000000000000000000000..d981a8b921b7b6d41283f72521758d49b741d96f --- /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 0000000000000000000000000000000000000000..d549f6b54faf9f9e53066815db602a884ac0f85f --- /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 0000000000000000000000000000000000000000..aac8c607a7c74b0cc01165bde1994f8a70401a5c --- /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 0000000000000000000000000000000000000000..bf1d03be919e45742a3789481068239f451410a9 --- /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 0000000000000000000000000000000000000000..597fb9f32289756d0e7ec4cbd07a5c6b145d4113 --- /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 0000000000000000000000000000000000000000..18243057b74e0e253f098b341760efe189503aa6 --- /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 0000000000000000000000000000000000000000..ae247c6c49fb6f9cb48f28ccde79f04dced8bf2e --- /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 0000000000000000000000000000000000000000..3bc8f8c609334b15eb93df807e950d75e95980f1 --- /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 2134c976dc02809023f2bcd35b89f7776e906b92..0035421d1701804de4b35798af8befd94cb3abef 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 957df8822b34f8ceb8b23cbdd05c1c733234e7e0..be644afaf0b11e68f7abd3f1bb3a5bc28f932b3e 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 462e6c37d0802ba67318b8082876aa8bd67a71eb..b22ddc5d2788fd23ceba6b6b134a2d3b4b87de13 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 0000000000000000000000000000000000000000..71f023865007d1278272d00f706d9e22b734f98c --- /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 405185f95d62acd736011356e4ad8a8113ca11fe..87e8ab8f267d8e5036859f817a1a5f8936b44fd2 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 6879e7cfebbc2658f0abf36b158d7887a40dca93..eb5c1a4886c6e9290216fde7de54481ad5024656 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 94ae109570ab0f999e1a6674141bcdfdc0574039..26d07d3d8e619e02cff005aa12d28aa1f2496455 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 add729034bfe7fd511b5038a36daccbaaeb17d10..9b20e24128e2ce325e2512f4792d157482f0895f 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 0000000000000000000000000000000000000000..ca65f1d8894cdcc8fffb9f2d6b5aa9dfa0edec8e --- /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 4f06b0fc2ac9130a295e8e09ea4729439121007e..b556e910d482da3220f44a09a021b2f95cf0be2c 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 c88ec12e5c08c203c52017d259963193cad4cd2f..01fc442733d3f6307f53b506ebaa9eec99e928d9 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 a9a450f84b725e980b893824db6a8d5298d1d759..55347c389a56beda66037d4d51d2969260b71ba3 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 4af8a5fb27e27096e21d33ceb5cf457b07e57757..361b30181b302adfe6b0e7cd514b919f36feeed6 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 0000000000000000000000000000000000000000..08c3892e1cb987886784a88fd2ecf0cae5014084 --- /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 45c6fbc4da88310aafec3c8cc5ed045aa5ea6987..858d99ceeb84cf93e3bea9b91c3aff59f7c0a9a1 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 c1c5b9a4ef716f573c76f2deaea5d0447aaab6b2..ff0aca1bc9d9e8f0b4c8b8afa9ff80bd087f31e1 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 17518f5967f4f966e815eaf63e0c3264a0051f80..4163a742c1e2d7c9bb10b8c3b403ea7e1463f9f6 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 527a86be9073a3a4af70d5ba205370883588f4bc..7b263529a70322333cf0ef4876ff26d856cc3297 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 ae4e58a8af5679387c9ab84fb78fd3580da47f30..a9007e43cee3983195e01140c69559dfc1326dce 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 2732eb1ba7e4587ccc42fbefb0279e63f821d8f3..dbd6f2ec64e820ee458ae39ea10b5a155dfea154 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 0000000000000000000000000000000000000000..f2648abc6cfb8b0d66925b1ce797b9d8027de46a --- /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 9c2f37e90c85f10b8eadb826167851121864b32c..faed190dc760b045544f1d7af928416c95c04c2b 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 cc9053dffae1fb59752f888149e5284e2e177617..8c7a8cd0da6dd12d63bc9b9a1c76f09f2c328553 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 e9b9bef614ea4197de73f774b2cbabaf06d4ad67..3c7619e3a0e4c14ec576cf8cc2eb47936c8251d0 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 90091e0f089ed0b7eef79b97770213087f301439..4218321296264912acea1b6a9caf02f6d3d38d2c 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 eb5a5b98c18340c147b88e351fbbda795f23d64d..f80e0888caa4a6e7e08fda8f92e97bcaff5b645a 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 cb9e70f50039c6b8dad70f58020fbc3c29714ed1..f2011e3e96c38482ba209480a76a91a726ae30fc 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 6b9454dcc28bae44e742e65fdaecfb207e40b897..53967689b8d18ed077b68fce0c5b47497cb9e0a7 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 5f30f845fe84e79ae7d21e9bd0ab24299778839f..56c7ff6781009a89269f87c682c8e2ffa03aca8f 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 0651474950e30e426e72c586e5ca8ac3354f595e..6c47cb1526508003f5de935552323f3f712319e4 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 6666606acdbf8c03713f69a84b9716206cc68d31..1e33ea437c801c5aaba2d3a5ad7941ef61740b9e 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 494135689bcf641f582ae2310853705b919a3426..1caf563d38f81cbaa4f8685f19f9bc7b72f5ad4f 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 eb51b90521521bbfd769b3e39f4f385b78bdaf03..33d3e8e6df25e4f11052d5db58d3690bd1e311c8 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 6db637831800a353972ffb0ce3b60262a6c0f7ca..71cda17436ebd1c7c6b73b95c36257b02894c53d 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 2a04ad7ccca393c3e2dc9c58db2e67c285f6cefa..5b132ec542bb1ca016e79bef4b6ca6c9753592cd 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 4c9eaab114a35192a354ec8aff3897cf1f5bf1f8..f829f425a2a84a6334581597c9f81e10ed1a7fcc 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 518343ed263a990fc73b26e63c6c466333f2a7fe..f37a76f26be15b111723f4bb34a7728e3c62c71f 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 fc0664c989010a66e0a08d87e230f33443202001..af48236c9464de030fcf7588d9b02593deedbd0c 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 0000000000000000000000000000000000000000..9250aa9e8521b623b7d2ab849d952d1c8a884990 --- /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 68fc38a21ec590d1088c67f6069526fc1c5706b6..bbf16ee88988aab773104a956ec8d676ab864e46 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 90fe9bd884effeed739f4d3e1be2ee72900c841e..55a6f5a43f9294f676f2a5adf6290018fda8cb9e 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 e78aceca9a9af19642ac605c5f1dc817b2387a51..fb43667cd4a3c418d19654479b21f79ffcc997d1 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