diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-baidusearch/pom.xml b/community/plugins/spring-ai-alibaba-starter-plugin-baidusearch/pom.xml index e352df6d..9d4cda69 100644 --- a/community/plugins/spring-ai-alibaba-starter-plugin-baidusearch/pom.xml +++ b/community/plugins/spring-ai-alibaba-starter-plugin-baidusearch/pom.xml @@ -27,7 +27,7 @@ spring-ai-alibaba-starter-plugin-baidusearch spring-ai-alibaba-starter-plugin-baidusearch - Baidu seartch tool for Spring AI Alibaba + Baidu search tool for Spring AI Alibaba UTF-8 diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-bingsearch/pom.xml b/community/plugins/spring-ai-alibaba-starter-plugin-bingsearch/pom.xml index 07de51da..bbdcdbb7 100644 --- a/community/plugins/spring-ai-alibaba-starter-plugin-bingsearch/pom.xml +++ b/community/plugins/spring-ai-alibaba-starter-plugin-bingsearch/pom.xml @@ -27,7 +27,7 @@ spring-ai-alibaba-starter-plugin-bingsearch spring-ai-alibaba-starter-plugin-bingsearch - Bing seartch tool for Spring AI Alibaba + Bing search tool for Spring AI Alibaba UTF-8 diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-bingsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/community/plugins/spring-ai-alibaba-starter-plugin-bingsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index bd142889..95d09ebd 100644 --- a/community/plugins/spring-ai-alibaba-starter-plugin-bingsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/community/plugins/spring-ai-alibaba-starter-plugin-bingsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1 @@ -com.alibaba.cloud.ai.plugin.bing.BingSearchPluginConfiguration \ No newline at end of file +com.alibaba.cloud.ai.plugin.bing.BingSearchPluginConfiguration diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/pom.xml b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/pom.xml new file mode 100644 index 00000000..259e15b8 --- /dev/null +++ b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/pom.xml @@ -0,0 +1,96 @@ + + + + + + 4.0.0 + + com.alibaba.cloud.ai + spring-ai-alibaba + ${revision} + ../../../pom.xml + + spring-ai-alibaba-starter-plugin-dingtalk + spring-ai-alibaba-starter-plugin-dingtalk + Ding Talk tool for Spring AI Alibaba + + + 2.1.68 + 2.0.0 + + + + + com.fasterxml.jackson.core + jackson-annotations + + + org.springframework.ai + spring-ai-spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + true + + + commons-codec + commons-codec + + + + com.aliyun + dingtalk + ${dingtalk-sdk-version} + + + + com.aliyun + alibaba-dingtalk-service-sdk + ${old-dingtalk-sdk-version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + true + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkConfiguration.java b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkConfiguration.java new file mode 100644 index 00000000..abce715b --- /dev/null +++ b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkConfiguration.java @@ -0,0 +1,41 @@ +package com.alibaba.cloud.ai.plugin.dingtalk; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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. + */ +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Description; + +/** + * @author YunLong + */ +@Configuration +@ConditionalOnClass(DingTalkService.class) +@EnableConfigurationProperties(DingTalkProperties.class) +public class DingTalkConfiguration { + + @Bean + @ConditionalOnMissingBean + @Description("Send DingTalk group chat messages using a custom robot") + @ConditionalOnProperty(prefix = "spring.ai.alibaba.plugin.dingtalk", name = "enabled", havingValue = "true") + public DingTalkService dingTalkGroupSendMessageByCustomRobotFunction(DingTalkProperties dingTalkProperties) { + return new DingTalkService(dingTalkProperties); + } +} diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkProperties.java b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkProperties.java new file mode 100644 index 00000000..b457f1c7 --- /dev/null +++ b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkProperties.java @@ -0,0 +1,53 @@ +package com.alibaba.cloud.ai.plugin.dingtalk; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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. + */ +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author YunLong + */ +@ConfigurationProperties(prefix = "spring.ai.alibaba.plugin.dingtalk") +public class DingTalkProperties { + + // Official Document Address:https://open.dingtalk.com/document/orgapp/custom-robots-send-group-messages + private String customRobotAccessToken; + + private String customRobotSignature; + + public DingTalkProperties() {} + + public DingTalkProperties(String customRobotAccessToken, String customRobotSignature) { + this.customRobotAccessToken = customRobotAccessToken; + this.customRobotSignature = customRobotSignature; + } + + public String getCustomRobotAccessToken() { + return customRobotAccessToken; + } + + public void setCustomRobotAccessToken(String customRobotAccessToken) { + this.customRobotAccessToken = customRobotAccessToken; + } + + public String getCustomRobotSignature() { + return customRobotSignature; + } + + public void setCustomRobotSignature(String customRobotSignature) { + this.customRobotSignature = customRobotSignature; + } +} diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkService.java b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkService.java new file mode 100644 index 00000000..b6f6fbdc --- /dev/null +++ b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/DingTalkService.java @@ -0,0 +1,82 @@ +package com.alibaba.cloud.ai.plugin.dingtalk; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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. + */ +import com.alibaba.cloud.ai.plugin.dingtalk.utils.SignUtils; +import com.dingtalk.api.DefaultDingTalkClient; +import com.dingtalk.api.DingTalkClient; +import com.dingtalk.api.request.OapiRobotSendRequest; +import com.dingtalk.api.response.OapiRobotSendResponse; +import com.fasterxml.jackson.annotation.JsonClassDescription; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import org.springframework.util.ObjectUtils; + +import java.util.function.Function; + +/** + * @author YunLong + */ +public class DingTalkService implements Function { + + private final DingTalkProperties dingTalkProperties; + + public DingTalkService(DingTalkProperties dingTalkProperties) { + this.dingTalkProperties = dingTalkProperties; + } + + /** + * The old version of DingTalk SDK. Some interfaces have not been fully replaced yet. + * Official Document Address:https://open.dingtalk.com/document/orgapp/custom-robots-send-group-messages + */ + @Override + public Response apply(Request request) { + + if (ObjectUtils.isEmpty(dingTalkProperties.getCustomRobotAccessToken()) || ObjectUtils.isEmpty(dingTalkProperties.getCustomRobotSignature())) { + throw new IllegalArgumentException("spring.ai.alibaba.plugin.dingtalk.custom-robot must not be null."); + } + + String accessToken = dingTalkProperties.getCustomRobotAccessToken(); + String signature = dingTalkProperties.getCustomRobotSignature(); + + // Request Body, please see the official document for more parameters. + OapiRobotSendRequest req = new OapiRobotSendRequest(); + req.setMsgtype("text"); + req.setText(String.format("{\"content\":\"%s\"}", request.message())); + + try { + DingTalkClient client = new DefaultDingTalkClient(String.format("https://oapi.dingtalk.com/robot/send?%s", SignUtils.getSign(signature))); + OapiRobotSendResponse response = client.execute(req, accessToken); + + if (response.isSuccess()) { + return new Response("The custom robot message was sent successfully!"); + } + } catch (Exception e) { + throw new RuntimeException("Custom robot message sending failed!"); + } + + return null; + } + + @JsonClassDescription("Send group chat messages using a custom robot") + public record Request( + @JsonProperty(required = true, value = "message") + @JsonPropertyDescription("Customize what the robot needs to send") String message) { + } + + public record Response(String message) { + } +} diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/utils/SignUtils.java b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/utils/SignUtils.java new file mode 100644 index 00000000..15fabcbb --- /dev/null +++ b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/java/com/alibaba/cloud/ai/plugin/dingtalk/utils/SignUtils.java @@ -0,0 +1,44 @@ +package com.alibaba.cloud.ai.plugin.dingtalk.utils; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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. + */ +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * @author YunLong + */ +public class SignUtils { + + public static String getSign(String signature) { + Long timestamp = System.currentTimeMillis(); + String stringToSign = timestamp + "\n" + signature; + + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(signature.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); + byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)); + String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), StandardCharsets.UTF_8); + return "×tamp=" + timestamp + "&sign=" + sign; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..1b1e0434 --- /dev/null +++ b/community/plugins/spring-ai-alibaba-starter-plugin-dingtalk/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.alibaba.cloud.ai.plugin.dingtalk.DingTalkConfiguration diff --git a/pom.xml b/pom.xml index 5517cbf0..97316284 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ community/plugins/spring-ai-alibaba-starter-plugin-time community/plugins/spring-ai-alibaba-starter-plugin-baidusearch community/plugins/spring-ai-alibaba-starter-plugin-bingsearch + community/plugins/spring-ai-alibaba-starter-plugin-dingtalk diff --git a/spring-ai-alibaba-examples/function-calling-example/pom.xml b/spring-ai-alibaba-examples/function-calling-example/pom.xml index 27268a07..e5bc7542 100644 --- a/spring-ai-alibaba-examples/function-calling-example/pom.xml +++ b/spring-ai-alibaba-examples/function-calling-example/pom.xml @@ -53,21 +53,31 @@ org.springframework.boot spring-boot-starter-web + + + com.alibaba.cloud.ai + spring-ai-alibaba-starter-plugin-time + ${spring-ai-alibaba.version} + + com.alibaba.cloud.ai - spring-ai-alibaba-starter-plugin-time - 1.0.0-M3.2 + spring-ai-alibaba-starter-plugin-baidusearch + ${spring-ai-alibaba.version} + com.alibaba.cloud.ai - spring-ai-alibaba-starter-plugin-baidusearch - 1.0.0-M3.2 + spring-ai-alibaba-starter-plugin-bingsearch + ${spring-ai-alibaba.version} + com.alibaba.cloud.ai - spring-ai-alibaba-starter-plugin-bingsearch - 1.0.0-M3.2 + spring-ai-alibaba-starter-plugin-dingtalk + ${spring-ai-alibaba.version} + diff --git a/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingController.java b/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingController.java index afacf651..a66cee77 100644 --- a/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingController.java +++ b/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingController.java @@ -27,20 +27,20 @@ @RequestMapping("/ai/func") public class FunctionCallingController { - private final ChatClient chatClient; + private final ChatClient chatClient; - public FunctionCallingController(ChatClient.Builder chatClientBuilder) { - this.chatClient = chatClientBuilder.build(); - } + public FunctionCallingController(ChatClient.Builder chatClientBuilder) { + this.chatClient = chatClientBuilder.build(); + } - @GetMapping("/weather-service") - public String weatherService(String subject) { - return chatClient.prompt() - .function("getWeather", "根据城市查询天气", new MockWeatherService()) - .user(subject) - .call() - .content(); - } + @GetMapping("/weather-service") + public String weatherService(String subject) { + return chatClient.prompt() + .function("getWeather", "根据城市查询天气", new MockWeatherService()) + .user(subject) + .call() + .content(); + } @GetMapping("/order-detail") public String orderDetail() { @@ -50,7 +50,7 @@ public String orderDetail() { .call() .content(); } - + @GetMapping("/baidu-search") public String baiduSearch(@RequestParam String query) { return chatClient.prompt() @@ -69,13 +69,22 @@ public String bingSearch(@RequestParam String query) { .content(); } - @GetMapping("/getTime") - public String getTime(String text) { - return chatClient.prompt() - .functions("getCityTimeFunction") - .user(text) - .call() - .content(); - } + @GetMapping("/getTime") + public String getTime(String text) { + return chatClient.prompt() + .functions("getCityTimeFunction") + .user(text) + .call() + .content(); + } + + @GetMapping("/dingTalk-custom-robot-send") + public String dingTalkCustomRobotSend(String input) { + return chatClient.prompt() + .functions("dingTalkGroupSendMessageByCustomRobotFunction") + .user(String.format("帮我用自定义机器人发送'%s'", input)) + .call() + .content(); + } } diff --git a/spring-ai-alibaba-examples/function-calling-example/src/main/resources/application.yml b/spring-ai-alibaba-examples/function-calling-example/src/main/resources/application.yml index 973bf747..0ef70685 100644 --- a/spring-ai-alibaba-examples/function-calling-example/src/main/resources/application.yml +++ b/spring-ai-alibaba-examples/function-calling-example/src/main/resources/application.yml @@ -1,12 +1,16 @@ spring: application: - name: cfunction-calling-example + name: function-calling-example ai: dashscope: - api-key: ${AI_DASHSCOPE_API_KEY} + api-key: ${AI_DASHSCOPE_API_KEY} alibaba: plugin: + dingtalk: + enabled: true + custom-robot-access-token: ${spring.ai.alibaba.plugin.dingtalk.custom-robot-access-token} # accessToken of custom robot + custom-robot-signature: ${spring.ai.alibaba.plugin.dingtalk.custom-robot-signature} # sign of custom robot bing: enabled: true token: ${AI_BING_TOKEN} \ No newline at end of file