18586361686
2025-04-30 8b6c4004486a43daf67757da074cd56581bf0500
feat: 完成Bot插件调用
1个文件已删除
17个文件已修改
4个文件已添加
880 ■■■■■ 已修改文件
aiflowy-commons/aiflowy-common-ai/pom.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-commons/aiflowy-common-ai/src/main/java/tech/aiflowy/common/ai/util/PluginHttpClient.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-commons/aiflowy-common-ai/src/main/java/tech/aiflowy/common/ai/util/PluginParam.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiBotController.java 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiPluginToolController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiBotPluginTool.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiBotPlugins.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiPlugin.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiPluginFunction.java 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiPluginTool.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/base/AiPluginBase.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/base/AiPluginToolBase.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/service/AiPluginToolService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/service/impl/AiPluginToolServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/hooks/useAxios.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/botDesign/BotDesign.tsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/plugin/ParameterConfig.tsx 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/plugin/Plugin.tsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/plugin/PluginTool.tsx 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/plugin/PluginToolEdit.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-commons/aiflowy-common-ai/pom.xml
@@ -89,6 +89,17 @@
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-http</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-json</artifactId>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>
aiflowy-commons/aiflowy-common-ai/src/main/java/tech/aiflowy/common/ai/util/PluginHttpClient.java
New file
@@ -0,0 +1,93 @@
package tech.aiflowy.common.ai.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.*;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.util.*;
public class PluginHttpClient {
    private static final int TIMEOUT = 10_000;
    /**
     * 发送插件请求,支持 path/query/body/header 四种参数类型
     *
     * @param url           API 地址
     * @param method        HTTP 方法(GET, POST, PUT, DELETE)
     * @param headers       默认请求头
     * @param pluginParams  插件参数定义列表
     * @return              JSON 格式的响应结果
     */
    public static JSONObject sendRequest(String url, String method, Map<String, Object> headers, List<PluginParam> pluginParams) {
        // 替换 URL 中的路径变量
        String processedUrl = replacePathVariables(url, pluginParams);
        // 设置请求方法
        Method httpMethod = Method.valueOf(method.toUpperCase());
        HttpRequest request = HttpRequest.of(processedUrl).method(httpMethod);
        // 处理请求头
        if (ObjectUtil.isNotEmpty(headers)) {
            for (Map.Entry<String, Object> entry : headers.entrySet()) {
                request.header(entry.getKey(), entry.getValue().toString());
            }
        }
        // 分类参数
        Map<String, Object> queryParams = new HashMap<>();
        Map<String, Object> bodyParams = new HashMap<>();
        for (PluginParam param : pluginParams) {
            if (!param.isEnabled()) continue;
            String paramName = param.getName();
            Object paramValue = param.getDefaultValue();
            if ("Query".equalsIgnoreCase(param.getMethod())) {
                queryParams.put(paramName, paramValue);
            } else if ("Body".equalsIgnoreCase(param.getMethod())) {
                bodyParams.put(paramName, paramValue);
            } else if ("Header".equalsIgnoreCase(param.getMethod())) {
                request.header(paramName, paramValue.toString());
            }
        }
        // 添加 Query 参数
        if (!queryParams.isEmpty()) {
            request.form(queryParams); // GET 请求会自动转为 query string
        }
        // 添加 Body 参数
        if (!bodyParams.isEmpty() && (httpMethod == Method.POST || httpMethod == Method.PUT)) {
            request.body(JSONUtil.toJsonStr(bodyParams));
            request.header(Header.CONTENT_TYPE, ContentType.JSON.getValue());
        }
        // 执行请求
        HttpResponse response = request.timeout(TIMEOUT).execute();
        return JSONUtil.parseObj(response.body());
    }
    /**
     * 替换 URL 中的路径变量 {xxx}
     */
    private static String replacePathVariables(String url, List<PluginParam> pluginParams) {
        String result = url;
        // 提取 path 类型的参数
        Map<String, Object> pathParams = new HashMap<>();
        for (PluginParam param : pluginParams) {
            if ("path".equalsIgnoreCase(param.getMethod())) {
                pathParams.put(param.getName(), param.getDefaultValue());
            }
        }
        // 替换 URL 中的路径变量
        for (Map.Entry<String, Object> entry : pathParams.entrySet()) {
            result = result.replaceAll("\\{" + entry.getKey() + "\\}", entry.getValue().toString());
        }
        return result;
    }
}
aiflowy-commons/aiflowy-common-ai/src/main/java/tech/aiflowy/common/ai/util/PluginParam.java
New file
@@ -0,0 +1,76 @@
package tech.aiflowy.common.ai.util;
public class PluginParam {
    private String name;
    private String description;
    private String type;
    private String method;       // Query / Body / Header / PathVariable 等
    private Object defaultValue;
    private boolean required;
    private boolean enabled;
    private String key;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }
    public Object getDefaultValue() {
        return defaultValue;
    }
    public void setDefaultValue(Object defaultValue) {
        this.defaultValue = defaultValue;
    }
    public boolean isRequired() {
        return required;
    }
    public void setRequired(boolean required) {
        this.required = required;
    }
    public boolean isEnabled() {
        return enabled;
    }
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
}
aiflowy-modules/aiflowy-module-ai/pom.xml
@@ -59,5 +59,9 @@
            <artifactId>aiflowy-common-file-storage</artifactId>
            <version>1.0.0-alpha.1</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
        </dependency>
    </dependencies>
</project>
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiBotController.java
@@ -74,6 +74,8 @@
    private AiPluginsService aiPluginsService;
    @Resource
    private AiBotPluginsService aiBotPluginsService;
    @Resource
    private AiPluginToolService aiPluginToolService;
    @PostMapping("updateOptions")
    public Result updateOptions(@JsonBody("id") BigInteger id, @JsonBody("options") Map<String, Object> options) {
@@ -147,10 +149,10 @@
        HumanMessage humanMessage = new HumanMessage(prompt);
        // 添加插件相关的function calling
        appendPluginFunctions(botId, humanMessage);
        appendPluginToolFunction(botId, humanMessage);
        //添加工作流相关的 Function Calling
        appendWorkflowFunctions(botId, humanMessage);
//        appendWorkflowFunctions(botId, humanMessage);
        //添加知识库相关的 Function Calling
        appendKnowledgeFunctions(botId, humanMessage);
@@ -166,7 +168,7 @@
                function_call(aiMessageResponse, emitter, needClose, historiesPrompt, llm, prompt, false);
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
            }
            if (needClose[0]) {
                System.out.println("function chat complete");
@@ -265,7 +267,8 @@
        HumanMessage humanMessage = new HumanMessage();
        // 添加插件、工作流、知识库相关的 Function Calling
        appendPluginFunctions(botId, humanMessage);
        appendPluginToolFunction(botId, humanMessage);
//        appendPluginFunctions(botId, humanMessage);
        appendWorkflowFunctions(botId, humanMessage);
        appendKnowledgeFunctions(botId, humanMessage);
@@ -276,6 +279,7 @@
            MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2));
            final Boolean[] needClose = {true};
//            if (humanMessage.getFunctions() != null && !humanMessage.getFunctions().isEmpty()) {
            if (humanMessage.getFunctions() != null && !humanMessage.getFunctions().isEmpty()) {
                try {
                    AiMessageResponse aiMessageResponse = llm.chat(historiesPrompt);
@@ -498,14 +502,32 @@
        }
    }
    private void appendPluginFunctions(BigInteger botId, HumanMessage humanMessage) {
//    private void appendPluginFunctions(BigInteger botId, HumanMessage humanMessage) {
//        QueryWrapper queryWrapper = QueryWrapper.create().eq(AiBotPlugins::getBotId, botId);
//        List<AiBotPlugins> aiBotPlugins = aiBotPluginsService.getMapper().selectListWithRelationsByQuery(queryWrapper);
//        if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(aiBotPlugins)) {
//            for (AiBotPlugins aiBotPlugin : aiBotPlugins) {
//                Function function = aiBotPlugin.getAiPlugins().toFunction();
//                humanMessage.addFunction(function);
//            }
//        }
//    }
    private void appendPluginToolFunction(BigInteger botId, HumanMessage humanMessage) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(AiBotPlugins::getBotId, botId);
        List<AiBotPlugins> aiBotPlugins = aiBotPluginsService.getMapper().selectListWithRelationsByQuery(queryWrapper);
        if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(aiBotPlugins)) {
            for (AiBotPlugins aiBotPlugin : aiBotPlugins) {
                Function function = aiBotPlugin.getAiPlugins().toFunction();
                humanMessage.addFunction(function);
        // 根据插件iD查询该插件下面有哪些插件工具,转换成Function
        for (AiBotPlugins aiBotPlugin: aiBotPlugins){
            BigInteger pluginId = aiBotPlugin.getPluginId();
            QueryWrapper queryTool = QueryWrapper.create()
                    .select("*")
                    .from("tb_ai_plugin_tool")
                    .where("plugin_id = ?", pluginId);
            List<AiPluginTool> aiPluginTools = aiPluginToolService.getMapper().selectListWithRelationsByQuery(queryTool);
            for (AiPluginTool item: aiPluginTools){
                humanMessage.addFunction(item.toFunction());
            }
        }
    }
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiPluginToolController.java
@@ -10,6 +10,7 @@
import tech.aiflowy.common.web.jsonbody.JsonBody;
import javax.annotation.Resource;
import java.math.BigInteger;
/**
 *  控制层。
@@ -35,7 +36,7 @@
    // 插件工具修改页面查询
    @PostMapping("/tool/search")
    public Result searchPlugin(@JsonBody(value = "aiPluginToolId", required = true) String aiPluginToolId){
    public Result searchPlugin(@JsonBody(value = "aiPluginToolId", required = true) BigInteger aiPluginToolId){
        return aiPluginToolService.searchPlugin(aiPluginToolId);
    }
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiBotPluginTool.java
New file
@@ -0,0 +1,20 @@
package tech.aiflowy.ai.entity;
import com.mybatisflex.annotation.RelationOneToOne;
import com.mybatisflex.annotation.Table;
@Table("tb_ai_bot_plugins")
public class AiBotPluginTool {
    @RelationOneToOne(selfField = "pluginId", targetField = "id")
    private AiPluginTool aiPluginTool;
    public AiPluginTool getAiPluginTool() {
        return aiPluginTool;
    }
    public void setAiPluginTool(AiPluginTool aiPluginTool) {
        this.aiPluginTool = aiPluginTool;
    }
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiBotPlugins.java
@@ -15,13 +15,13 @@
public class AiBotPlugins extends AiBotPluginsBase {
    @RelationOneToOne(selfField = "pluginId", targetField = "id")
    private AiPlugins aiPlugins;
    private AiPlugin aiPlugin;
    public AiPlugins getAiPlugins() {
        return aiPlugins;
    public AiPlugin getAiPlugin() {
        return aiPlugin;
    }
    public void setAiPlugins(AiPlugins aiPlugins) {
        this.aiPlugins = aiPlugins;
    public void setAiPlugin(AiPlugin aiPlugin) {
        this.aiPlugin = aiPlugin;
    }
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiPlugin.java
@@ -16,4 +16,9 @@
@Table("tb_ai_plugin")
public class AiPlugin extends AiPluginBase {
    public String getTitle() {
        return this.getName();
    }
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiPluginFunction.java
New file
@@ -0,0 +1,186 @@
package tech.aiflowy.ai.entity;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.agentsflex.core.llm.functions.BaseFunction;
import com.agentsflex.core.llm.functions.Function;
import com.agentsflex.core.llm.functions.Parameter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.core.query.QueryWrapper;
import tech.aiflowy.ai.mapper.AiPluginMapper;
import tech.aiflowy.ai.service.AiPluginToolService;
import tech.aiflowy.common.ai.util.PluginHttpClient;
import tech.aiflowy.common.ai.util.PluginParam;
import tech.aiflowy.common.domain.Result;
import tech.aiflowy.common.util.SpringContextUtil;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AiPluginFunction  implements Function {
    // 插件工具id
    private BigInteger pluginToolId;
    private String name;
    private String description;
    private Parameter[] parameters;
    public AiPluginFunction() {
    }
    public AiPluginFunction(AiPluginTool aiPluginTool) {
        this.name = aiPluginTool.getName();
        this.description = aiPluginTool.getDescription();
        this.pluginToolId = aiPluginTool.getId();
        this.parameters = getDefaultParameters();
    }
    public BigInteger getPluginToolId() {
        return pluginToolId;
    }
    public void setPluginToolId(BigInteger pluginToolId) {
        this.pluginToolId = pluginToolId;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public void setParameters(Parameter[] parameters) {
        this.parameters = parameters;
    }
    private AiPlugin getAiPlugin(BigInteger pluginId) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select("*")
                .from("tb_ai_plugin")
                .where("id = ?", pluginId);
        AiPluginMapper aiPluginMapper = SpringContextUtil.getBean(AiPluginMapper.class);
        AiPlugin aiPlugin1 = aiPluginMapper.selectOneByQuery(queryWrapper);
        return aiPlugin1;
    }
    private Parameter[] getDefaultParameters() {
        AiPluginToolService pluginToolService = SpringContextUtil.getBean(AiPluginToolService.class);
        QueryWrapper queryAiPluginToolWrapper = QueryWrapper.create()
                .select("*")
                .from("tb_ai_plugin_tool")
                .where("id = ? ", this.pluginToolId);
        AiPluginTool aiPluginTool = pluginToolService.getMapper().selectOneByQuery(queryAiPluginToolWrapper);
        List<Map<String, Object>> dataList = getDataList(aiPluginTool.getInputData());
        Parameter[] params = new Parameter[dataList.size()];
        for (int i = 0; i < dataList.size(); i++) {
            Map<String, Object> item = dataList.get(i);
            Parameter parameter = new Parameter();
            parameter.setName((String) item.get("name"));
            parameter.setDescription((String) item.get("description"));
            parameter.setRequired((boolean) item.get("required"));
            parameter.setType((String) item.get("type"));
            params[i] = parameter;
        }
        return params;
    }
    // 转换输入参数
    private List<Map<String, Object>> getDataList(String jsonArray){
        List<Map<String, Object>> dataList;
        try {
            dataList = new ObjectMapper().readValue(
                    jsonArray,
                    new TypeReference<List<Map<String, Object>>>(){}
            );
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        return dataList;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public String getDescription() {
        return description;
    }
    @Override
    public Parameter[] getParameters() {
        return parameters;
    }
    @Override
    public Object invoke(Map<String, Object> argsMap) {
        AiPluginToolService pluginToolService = SpringContextUtil.getBean(AiPluginToolService.class);
        QueryWrapper queryAiPluginToolWrapper = QueryWrapper.create()
                .select("*")
                .from("tb_ai_plugin_tool")
                .where("id = ? ", this.pluginToolId);
        AiPluginTool aiPluginTool = pluginToolService.getMapper().selectOneByQuery(queryAiPluginToolWrapper);
        String method = aiPluginTool.getRequestMethod().toUpperCase();
        AiPlugin aiPlugin = getAiPlugin(aiPluginTool.getPluginId());
        String url;
        if (!StrUtil.isEmpty(aiPluginTool.getBasePath())) {
            url = aiPlugin.getBaseUrl()+aiPluginTool.getBasePath();
        } else {
            url = aiPlugin.getBaseUrl()+"/"+aiPluginTool.getName();
        }
        List<Map<String, Object>> headers = getDataList(aiPlugin.getHeaders());
        Map<String, Object> headersMap = new HashMap<>();
        for (Map<String, Object> header : headers) {
            headersMap.put((String) header.get("label"), header.get("value"));
        }
        List<PluginParam> params = new ArrayList<>();
        List<Map<String, Object>> dataList = getDataList(aiPluginTool.getInputData());
        // 遍历插件工具定义的参数列表
        for (Map<String, Object> paramDef : dataList) {
            String paramName = (String) paramDef.get("name");
            // 检查大模型返回的参数中是否包含当前参数
            if (argsMap != null && argsMap.containsKey(paramName)) {
                PluginParam pluginParam = new PluginParam();
                pluginParam.setName(paramName);
                pluginParam.setDescription((String) paramDef.get("description"));
                pluginParam.setRequired((boolean) paramDef.get("required"));
                pluginParam.setType((String) paramDef.get("type"));
                pluginParam.setMethod((String) paramDef.get("method"));
                pluginParam.setEnabled((boolean) paramDef.get("enabled"));
                // 使用大模型返回的值,而不是默认值
                pluginParam.setDefaultValue(argsMap.get(paramName));
                params.add(pluginParam);
            } else if ((boolean) paramDef.get("required")) {
                // 如果是必填参数但大模型没有返回,使用默认值或抛出异常
                PluginParam pluginParam = new PluginParam();
                pluginParam.setName(paramName);
                pluginParam.setDescription((String) paramDef.get("description"));
                pluginParam.setRequired(true);
                pluginParam.setMethod((String) paramDef.get("method"));
                pluginParam.setEnabled((boolean) paramDef.get("enabled"));
                pluginParam.setType((String) paramDef.get("type"));
                pluginParam.setDefaultValue(paramDef.get("defaultValue"));
                params.add(pluginParam);
            }
        }
        JSONObject result = PluginHttpClient.sendRequest(url, method, headersMap, params);
        return result;
    }
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiPluginTool.java
@@ -1,5 +1,6 @@
package tech.aiflowy.ai.entity;
import com.agentsflex.core.llm.functions.Function;
import com.mybatisflex.annotation.Table;
import tech.aiflowy.ai.entity.base.AiPluginToolBase;
@@ -12,4 +13,10 @@
 */
@Table("tb_ai_plugin_tool")
public class AiPluginTool extends AiPluginToolBase {
    public  Function toFunction() {
        return new AiPluginFunction(this);
    }
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/base/AiPluginBase.java
@@ -4,6 +4,7 @@
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Date;
@@ -15,7 +16,7 @@
     * 插件id
     */
    @Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "插件id")
    private Long id;
    private BigInteger id;
    /**
     * 图标地址
@@ -84,11 +85,11 @@
    @Column(comment = "创建时间")
    private Date created;
    public Long getId() {
    public BigInteger getId() {
        return id;
    }
    public void setId(Long id) {
    public void setId(BigInteger id) {
        this.id = id;
    }
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/base/AiPluginToolBase.java
@@ -5,6 +5,7 @@
import com.mybatisflex.annotation.KeyType;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Date;
@@ -16,13 +17,13 @@
     * 插件工具id
     */
    @Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "插件工具id")
    private Long id;
    private BigInteger id;
    /**
     * 插件id
     */
    @Column(comment = "插件id")
    private Long pluginId;
    private BigInteger pluginId;
    /**
     * 名称
@@ -84,19 +85,19 @@
    @Column(comment = "调试状态【0失败 1成功】")
    private int debugStatus;
    public Long getId() {
    public BigInteger getId() {
        return id;
    }
    public void setId(Long id) {
    public void setId(BigInteger id) {
        this.id = id;
    }
    public Long getPluginId() {
    public BigInteger getPluginId() {
        return pluginId;
    }
    public void setPluginId(Long pluginId) {
    public void setPluginId(BigInteger pluginId) {
        this.pluginId = pluginId;
    }
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/service/AiPluginToolService.java
@@ -5,6 +5,8 @@
import tech.aiflowy.ai.entity.AiPluginTool;
import tech.aiflowy.common.domain.Result;
import java.math.BigInteger;
/**
 *  服务层。
 *
@@ -15,7 +17,7 @@
    Result savePluginTool(AiPluginTool aiPluginTool);
    Result searchPlugin(String aiPluginToolId);
    Result searchPlugin(BigInteger aiPluginToolId);
    Result updatePlugin(AiPluginTool aiPluginTool);
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/service/impl/AiPluginToolServiceImpl.java
@@ -16,6 +16,7 @@
import tech.aiflowy.common.domain.Result;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -49,7 +50,7 @@
    }
    @Override
    public Result searchPlugin(String aiPluginToolId) {
    public Result searchPlugin(BigInteger aiPluginToolId) {
        //查询当前插件工具
        QueryWrapper queryAiPluginToolWrapper = QueryWrapper.create()
                .select("*")
@@ -59,7 +60,8 @@
        // 查询当前的插件信息
        QueryWrapper queryAiPluginWrapper = QueryWrapper.create()
                .select("*")
                .from("tb_ai_plugin");
                .from("tb_ai_plugin")
                .where("id = ?", aiPluginTool.getPluginId());
        AiPlugin aiPlugin = aiPluginMapper.selectOneByQuery(queryAiPluginWrapper);
        Map<String, Object> result = new HashMap<>();
        result.put("data", aiPluginTool);
aiflowy-ui-react/src/hooks/useAxios.ts
@@ -53,6 +53,7 @@
})
export const useAxios =  makeUseAxios({
        // @ts-ignore
        cache: axiosCache,
        defaultOptions: {
            useCache: false,
aiflowy-ui-react/src/pages/ai/botDesign/BotDesign.tsx
@@ -170,6 +170,8 @@
        botId: params.id,
        sessionId: getSessionId()
    });
    console.log('pluginResult')
    console.log(pluginResult)
    useEffect(() => {
        setChats(messageResult?.data)
    }, [messageResult]);
@@ -340,8 +342,8 @@
                                children:
                                    <div>
                                        {pluginResult?.data?.map((item: any) => {
                                            return <ListItem key={item.id} title={item.aiPlugins.title}
                                                             description={item.aiPlugins.description}
                                            return <ListItem key={item.id} title={item.aiPlugin.name}
                                                             description={item.aiPlugin.description}
                                                             onButtonClick={() => {
                                                                 Modal.confirm({
                                                                     title: '确定要删除该插件吗?',
aiflowy-ui-react/src/pages/ai/plugin/ParameterConfig.tsx
File was deleted
aiflowy-ui-react/src/pages/ai/plugin/Plugin.tsx
@@ -21,7 +21,7 @@
    Radio,
    Row,
    Select,
    Space, Spin
    Space, Spin, Tooltip
} from 'antd';
import {usePage, usePostManual} from "../../../hooks/useApis.ts";
import SearchForm from "../../../components/AntdCrud/SearchForm.tsx";
@@ -295,7 +295,7 @@
                                        name: item.name,
                                        description: item.description,
                                        baseUrl: item.baseUrl,
                                        headers: JSON.parse(item.headers),
                                        headers: item.headers?JSON.parse(item.headers):[],
                                        authData: item.authData,
                                        authType: item.authType,
                                        position: item.position,
@@ -353,9 +353,17 @@
                                avatar={<Avatar src={item.icon || "/favicon.png"} />}
                                title={item.name}
                                description={
                                    <>
                                        <p>{item.description}</p>
                                    </>
                                    <Tooltip title={item.description}>
                                        <div style={{
                                            display: '-webkit-box',
                                            WebkitLineClamp: 1,      // 限制显示行数
                                            WebkitBoxOrient: 'vertical',
                                            overflow: 'hidden',
                                            textOverflow: 'ellipsis',
                                        }}>
                                            {item.description}
                                        </div>
                                    </Tooltip>
                                }
                            />
                        </Card>
@@ -450,15 +458,15 @@
                                    <Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
                                        <Form.Item
                                            {...restField}
                                            name={[name, 'first']}
                                            rules={[{ required: true, message: 'Missing first name' }]}
                                            name={[name, 'label']}
                                            rules={[{ required: true, message: 'Missing label ' }]}
                                        >
                                            <Input placeholder="Headers Name" />
                                        </Form.Item>
                                        <Form.Item
                                            {...restField}
                                            name={[name, 'last']}
                                            rules={[{ required: true, message: 'Missing last name' }]}
                                            name={[name, 'value']}
                                            rules={[{ required: true, message: 'Missing  value' }]}
                                        >
                                            <Input placeholder="Headers value" />
                                        </Form.Item>
aiflowy-ui-react/src/pages/ai/plugin/PluginTool.tsx
@@ -1,9 +1,9 @@
import React, {useEffect, useState} from 'react';
import {Button, Form, FormProps, Input, message, Modal, Space, Table, TableProps, Tag} from 'antd';
import {Button, Form, FormProps, Input, message, Modal, Space, Table, TableProps, Tooltip} from 'antd';
import {useLocation, useNavigate} from "react-router-dom";
import {useLayout} from "../../../hooks/useLayout.tsx";
import {useBreadcrumbRightEl} from "../../../hooks/useBreadcrumbRightEl.tsx";
import {EditOutlined, PlusOutlined, RestOutlined} from "@ant-design/icons";
import {ArrowLeftOutlined, EditOutlined, PlusOutlined, RestOutlined} from "@ant-design/icons";
import TextArea from "antd/es/input/TextArea";
import {usePage, usePostManual} from "../../../hooks/useApis.ts";
import {convertDatetimeUtil} from "../../../libs/changeDatetimeUtil.tsx";
@@ -39,10 +39,21 @@
    // 控制创建工具模态框的显示与隐藏
    const [isAddPluginToolModalOpen, setAddPluginToolIsOpen] = React.useState(false);
    useBreadcrumbRightEl(<Button type={"primary"} onClick={() => {
        setAddPluginToolIsOpen(true);
    }}>
        <PlusOutlined/>创建工具</Button>)
    useBreadcrumbRightEl(
        <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 16}}>
            <div>
                <Button onClick={() => navigate(-1)}  icon={<ArrowLeftOutlined />}>返回</Button>
                {/* 其他内容 */}
            </div>
            <div>
                <Button type={"primary"} onClick={() => {
                    setAddPluginToolIsOpen(true);
                }}>
                    <PlusOutlined/>创建工具</Button>
            </div>
        </div>
    )
    const {
        loading,
        result,
@@ -99,23 +110,34 @@
            key: 'name',
        },
        {
            title: '输入参数',
            dataIndex: 'inputParams',
            key: 'inputParams',
            title: '工具描述',
            dataIndex: 'description',
            key: 'description',
            ellipsis: true,  // 超出显示省略号
            render: (text: string) => (
                <Tooltip title={text}>
                    <span>{text}</span>
                </Tooltip>
            ),
        },
        {
            title: '调试状态',
            dataIndex: 'debugStatus',
            key: 'debugStatus',
            render: (item: number) =>{
                if (item === 0){
                    return  <Tag color="error">失败</Tag>
                }  else if(item === 1){
                    return  <Tag color="success">成功</Tag>
                }
            }
        },
        // {
        //     title: '输入参数',
        //     dataIndex: 'inputParams',
        //     key: 'inputParams',
        // },
        // {
        //     title: '调试状态',
        //     dataIndex: 'debugStatus',
        //     key: 'debugStatus',
        //     render: (item: number) =>{
        //         if (item === 0){
        //             return  <Tag color="error">失败</Tag>
        //         }  else if(item === 1){
        //             return  <Tag color="success">成功</Tag>
        //         }
        //
        //     }
        // },
        {
            title: '创建时间',
            dataIndex: 'created',
@@ -131,6 +153,7 @@
                        navigate('/ai/pluginToolEdit', {
                            state: {
                                id: record.id,
                                pluginId: id,
                                // 插件名称
                                pluginTitle:pluginTitle,
                                // 插件工具名称
aiflowy-ui-react/src/pages/ai/plugin/PluginToolEdit.tsx
@@ -1,11 +1,12 @@
import React, {useEffect, useState} from 'react';
import {useLayout} from "../../../hooks/useLayout.tsx";
import {useLocation} from "react-router-dom";
import {useLocation, useNavigate} from "react-router-dom";
import {Button, Collapse, Form, Input, message, Select, Space, Spin, Switch, Table} from "antd";
import {usePost, usePostManual} from "../../../hooks/useApis.ts";
import './less/pluginToolEdit.less'
import {DeleteOutlined, EditOutlined, PlusOutlined} from "@ant-design/icons";
import {ArrowLeftOutlined, DeleteOutlined, EditOutlined, PlusOutlined} from "@ant-design/icons";
import TextArea from "antd/es/input/TextArea";
import {useBreadcrumbRightEl} from "../../../hooks/useBreadcrumbRightEl.tsx";
interface inputDataParameter {
    key: string;
@@ -25,7 +26,19 @@
    required: boolean;
    enabled: boolean;
}
const PluginToolEdit: React.FC = () => {
    const navigate = useNavigate();
    useBreadcrumbRightEl(
        <div>
            <div>
                <button onClick={() => navigate(-1)}>返回</button>
                {/* 其他内容 */}
            </div>
        </div>
    )
    const { setOptions } = useLayout();
    const location = useLocation();
    const { id, pluginTitle, pluginToolTitle } = location.state || {};
@@ -36,7 +49,14 @@
    const [isEditOutput, setIsEditOutput] = useState(false);
    const [formInput] = Form.useForm();
    const [formOutput] = Form.useForm();
    useBreadcrumbRightEl(
        <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 16}}>
            <div>
                <Button onClick={() => navigate(-1)}  icon={<ArrowLeftOutlined />}>返回</Button>
                {/* 其他内容 */}
            </div>
        </div>
    )
    const [editStates, setEditStates] = useState({
        '1': false,
        '2': false,
@@ -50,8 +70,8 @@
            breadcrumbs: [
                { title: '首页' },
                { title: '插件', href: `/ai/plugin` },
                { title: pluginTitle, href: `/ai/plugin` },
                { title: pluginToolTitle, href: `/ai/pluginTool` },
                { title: pluginTitle},
                { title: pluginToolTitle},
                { title: '修改' },
            ],
        });
pom.xml
@@ -35,6 +35,8 @@
        <commonmark.version>0.18.0</commonmark.version>
        <jsoup.version>1.16.1</jsoup.version>
        <commons-io.version>2.18.0</commons-io.version>
        <hutool-http.version>5.8.28</hutool-http.version>
        <hutool-json.version>5.8.32</hutool-json.version>
    </properties>
    <dependencyManagement>
        <dependencies>
@@ -64,6 +66,18 @@
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-http</artifactId>
                <version>${hutool-http.version}</version>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-json</artifactId>
                <version>${hutool-json.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.zxing</groupId>
                <artifactId>core</artifactId>
                <version>3.5.3</version>