diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index 32772456a..a3112be59 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -25,10 +25,6 @@ 5.3.1 - - 1.0.0-M1 - 2.15.1 - 4.23.0 1.11.4 @@ -50,19 +46,6 @@ ${nacos.client.version} - - - com.alibaba - dashscope-sdk-java - ${dashscope-sdk-java.version} - - - - org.springframework.ai - spring-ai-core - ${spring.ai.version} - - com.alibaba.csp @@ -235,11 +218,6 @@ ${revision} - - com.alibaba.cloud - spring-cloud-starter-alibaba-ai - ${revision} - com.alibaba.cloud spring-alibaba-nacos-config @@ -288,14 +266,6 @@ ${revision} - - - org.springframework.ai - spring-ai-test - ${spring.ai.version} - test - - diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/pom.xml b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/pom.xml deleted file mode 100644 index 5a3198ac9..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/pom.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - spring-cloud-alibaba-examples - com.alibaba.cloud - ${revision} - ../../pom.xml - - 4.0.0 - - spring-cloud-ai-chat-msg-context-example - Spring Cloud Starter Alibaba AI Chat Message Context Holder Example - Example for Chat Message Context Holder By Spring Cloud Alibaba AI - jar - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-ai - - - - - - - - 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 - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - false - - - - - diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/ChatMsgApplication.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/ChatMsgApplication.java deleted file mode 100644 index c727befca..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/ChatMsgApplication.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author yuluo - * @author yuluo - */ - -@SpringBootApplication -public class ChatMsgApplication { - - public static void main(String[] args) { - - SpringApplication.run(ChatMsgApplication.class, args); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/MessageContextHolder.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/MessageContextHolder.java deleted file mode 100644 index 347037a6b..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/MessageContextHolder.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.context; - -import java.util.List; - -import org.springframework.ai.chat.messages.Message; - -/** - * @author yuluo - * @author yuluo - */ - -public interface MessageContextHolder { - - /** - * Th default session id key. - * Can use session_id request_id &etc. - */ - String SCA_SESSION_ID = "SCA_SESSION_ID"; - - void addMsg(String sessionId, Message msg); - - void removeMsg(String sessionId); - - List getMsg(String sessionId); - - default String getSCASessionId() { - - return SCA_SESSION_ID; - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/defaults/MemoryMessageContextHolder.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/defaults/MemoryMessageContextHolder.java deleted file mode 100644 index f67226620..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/defaults/MemoryMessageContextHolder.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.context.defaults; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.StringJoiner; - -import com.alibaba.cloud.ai.example.tongyi.context.MessageContextHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.ai.chat.messages.Message; -import org.springframework.stereotype.Component; - -/** - * @author yuluo - * @author yuluo - */ - -@Component -public class MemoryMessageContextHolder implements MessageContextHolder { - - private static final Logger log = LoggerFactory.getLogger(MemoryMessageContextHolder.class); - - private final Map> msgContextHolderMap = new HashMap<>(); - - @Override - public void addMsg(String sessionId, Message msg) { - - msgContextHolderMap.computeIfAbsent(sessionId, k -> new ArrayList<>()); - log.info("addMsg: sessionId={}, msg={}", sessionId, msg); - } - - @Override - public void removeMsg(String sessionId) { - - msgContextHolderMap.remove(sessionId); - } - - @Override - public List getMsg(String sessionId) { - - return new ArrayList<>(msgContextHolderMap.getOrDefault(sessionId, new ArrayList<>())); - } - - @Override - public String toString() { - - StringBuilder sb = new StringBuilder(); - sb.append("MessageContextHolderImpl{"); - StringJoiner joiner = new StringJoiner(", ", "{", "}"); - for (Map.Entry> entry : msgContextHolderMap.entrySet()) { - joiner.add(entry.getKey() + "=" + entry.getValue().toString()); - } - sb.append("msgContextHolderMap=").append(joiner); - sb.append('}'); - return sb.toString(); - } -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/defaults/RedisMessageContextHolder.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/defaults/RedisMessageContextHolder.java deleted file mode 100644 index 9aaf2409b..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/context/defaults/RedisMessageContextHolder.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.context.defaults; - -import java.util.List; - -import com.alibaba.cloud.ai.example.tongyi.context.MessageContextHolder; - -import org.springframework.ai.chat.messages.Message; - -/** - * @author yuluo - * @author yuluo - */ - -//@Component -public class RedisMessageContextHolder implements MessageContextHolder { - - @Override - public void addMsg(String sessionId, Message msg) { - - System.out.println("RedisMessageContextHolder addMsg"); - } - - @Override - public void removeMsg(String sessionId) { - - System.out.println("RedisMessageContextHolder removeMsg"); - } - - @Override - public List getMsg(String sessionId) { - - System.out.println("RedisMessageContextHolder getMsg"); - return null; - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/controller/ChatMsgController.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/controller/ChatMsgController.java deleted file mode 100644 index 3f0244204..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/controller/ChatMsgController.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.controller; - -import com.alibaba.cloud.ai.example.tongyi.service.ChatMsgService; - -import org.springframework.beans.factory.annotation.Autowired; -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; - -/** - * @author yuluo - * @author yuluo - */ - -@RestController -@RequestMapping("/chat") -public class ChatMsgController { - - @Autowired - private ChatMsgService msgService; - - @GetMapping("/msg") - public String completion(@RequestParam String message) { - - return msgService.completion(message); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/ChatMsgService.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/ChatMsgService.java deleted file mode 100644 index 7453cf56b..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/ChatMsgService.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service; - -import com.alibaba.cloud.ai.example.tongyi.context.MessageContextHolder; - -import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** - * @author yuluo - * @author yuluo - */ - -@Service -public class ChatMsgService { - - private final ChatModel chatModel; - - private final MessageContextHolder messageContextHolder; - - @Autowired - public ChatMsgService(ChatModel chatModel, MessageContextHolder messageContextHolder) { - this.chatModel = chatModel; - this.messageContextHolder = messageContextHolder; - } - - public String completion(String message) { - - // create chat prompt - Prompt prompt = new Prompt(new UserMessage(message)); - - // collect user message - messageContextHolder.addMsg( - messageContextHolder.getSCASessionId(), - prompt.getInstructions().get(0) - ); - - ChatResponse resp = chatModel.call(prompt); - - // collect model response - messageContextHolder.addMsg( - messageContextHolder.getSCASessionId(), - resp.getResult().getOutput() - ); - - return resp.getResult().getOutput().getContent(); - - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/resources/application.yml deleted file mode 100644 index ae8fa1f42..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-chat-msg-context-example/src/main/resources/application.yml +++ /dev/null @@ -1,26 +0,0 @@ -# -# Copyright 2023-2024 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. -# - -server: - port: 8084 - -spring: - application: - name: tongyi-example - -# please setting api-key. suggestion by environment variable. -# Note: api-key is invalid, please apply for a new one. -# export SPRING_CLOUD_AI_TONGYI_API_KEY=sk-a3d73b1709bf4a178c28ed7c8b3b5a345 diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/README-en.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/README-en.md deleted file mode 100644 index fdce8e8ca..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/README-en.md +++ /dev/null @@ -1,118 +0,0 @@ -# Spring Cloud Alibaba AI Example - -## Project description - -The Spring Cloud Alibaba AI module is based on [Spring AI 0.8.1](https://docs.spring.io/spring-ai/reference/0.8-SNAPSHOT/index.html) the project API to complete the access of the general series of large models. This project demonstrates how to use `spring-cloud-starter-alibaba-ai` the Spring Cloud microservice application to integrate with the generic family model. - -[model service dashscope](https://help.aliyun.com/zh/dashscope/) It is a big model application service launched by Alibaba. Based on the concept of "Model-as-a-Service" (MaaS), Lingji Model Service provides a variety of model services including model reasoning and model fine-tuning training through standardized APIs around AI models in various fields. - -- Current completion of spring-ai chat api and image api. - -## Application access - -### Access `spring-cloud-starter-alibaba-ai` - -1. Add the following dependencies to the project POM. XML: - - ```xml - - com.alibaba.cloud - spring-cloud-starter-alibaba-ai - - ``` - -2. Add the following configuration to the application. Yml configuration file: - - > Note: It is recommended to set the api-key as an environment variable to avoid api-key leakage. - > - > ```shell - > export SPRING_CLOUD_AI_TONGYI_API_KEY=sk-a3d73b1709bf4a178c28ed7c8b3b5a45 - > ``` - - ```yml - spring: - cloud: - ai: - tongyi: - # apikey is invalid. - api-key: sk-a3d73b1709bf4a178c28ed7c8b3b5a45 - ``` - -3. Add the following code: - - ```yml - controller: - - @Autowired - @Qualifier("tongYiSimpleServiceImpl") - private TongYiService tongYiSimpleService; - - @GetMapping("/example") - public String completion( - @RequestParam(value = "message", defaultValue = "Tell me a joke") - String message - ) { - - return tongYiSimpleService.completion(message); - } - - service: - - private final ChatClient chatClient; - - - @Autowired - public TongYiSimpleServiceImpl(ChatClient chatClient, StreamingChatClient streamingChatClient) { - - this.chatClient = chatClient; - this.streamingChatClient = streamingChatClient; - } - - @Override - public String completion(String message) { - - Prompt prompt = new Prompt(new UserMessage(message)); - - return chatClient.call(prompt).getResult().getOutput().getContent(); - } - ``` - - At this point, the simplest model access is complete! It is slightly different from the code in this example, but the code in the example does not need to be modified. The corresponding function can be accomplished without modification. - -4. Start the application - - This Example project supports the following two startup methods: - - 1. IDE direct startup: find the main class `TongYiApplication` and execute the main method to start the application. - 2. Start after packaging and compiling: First `mvn clean package`, compile and package the project, and then enter the `target` folder to `java -jar spring-cloud-ai-example.jar` start the application. - -## Validate - -Browser address bar input: `http://localhost:8080/ai/example` - -The following response is returned: - - -```json -{ - "Tell me a joke": "Sure, here's a classic one for you:\n\nWhy was the math book sad?\n\nBecause it had too many problems.\n\nI hope that made you smile! If you're looking for more, just let me know." -} -``` - -## Simple front pages - -cd `resources/static`,open index.html file by local browser, input your question. then you get ai-models output(make api-keys effective): - -![ai-example](./images/sca-ai-example-front.gif) - -## Configuration item description - -More configuration refer: - -https://help.aliyun.com/zh/dashscope/developer-reference/api-details - -## More examples: - -This example consists of 6 samples, implemented by different serviceimpl, you can refer to the readme file in each serviceimpl, use @RestController as the entry point in the controller, you can use the browser or curl tool to request the interface. You can use a browser or curl tool to request the interface. - -> The example is already functional and does not require any code changes. Just replace the apikey in the corresponding example, the apikey provided in the project is invalid. diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/README.md deleted file mode 100644 index 3d45d78d6..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/README.md +++ /dev/null @@ -1,117 +0,0 @@ -# Spring Cloud Alibaba AI Example - -## 项目说明 - -Spring Cloud Alibaba AI 模块基于 [Spring AI 0.8.1](https://docs.spring.io/spring-ai/reference/0.8-SNAPSHOT/index.html) 项目 API 完成通义系列大模型的接入。本项目演示如何使用 `spring-cloud-starter-alibaba-ai` 完成 Spring Cloud 微服务应用与通义系列模型的整合。 - -[模型服务灵积](https://help.aliyun.com/zh/dashscope/) 是阿里巴巴推出的一个大模型应用服务。灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。 - -- 目前只完成 spring-ai chat completion api 和 image api 的接入。 - -## 应用接入 - -### 接入 `spring-cloud-starter-alibaba-ai` - -1. 在项目 pom.xml 中加入以下依赖: - - ```xml - - com.alibaba.cloud - spring-cloud-starter-alibaba-ai - - ``` - -2. 在 application.yml 配置文件中加入以下配置: - - > Note: 推荐使用环境变量的方式设置 api-key,避免 api-key 泄露。 - > - > ```shell - > export SPRING_CLOUD_AI_TONGYI_API_KEY=sk-a3d73b1709bf4a178c28ed7c8b3b5a45 - > ``` - - ```yaml - spring: - cloud: - ai: - tongyi: - # apiKey is invalid. - api-key: sk-a3d73b1709bf4a178c28ed7c8b3b5a45 - ``` - -3. 添加如下代码: - - ```java - controller: - - @Autowired - @Qualifier("tongYiSimpleServiceImpl") - private TongYiService tongYiSimpleService; - - @GetMapping("/example") - public String completion( - @RequestParam(value = "message", defaultValue = "Tell me a joke") - String message - ) { - - return tongYiSimpleService.completion(message); - } - - service: - - private final ChatClient chatClient; - - - @Autowired - public TongYiSimpleServiceImpl(ChatClient chatClient, StreamingChatClient streamingChatClient) { - - this.chatClient = chatClient; - this.streamingChatClient = streamingChatClient; - } - - @Override - public String completion(String message) { - - Prompt prompt = new Prompt(new UserMessage(message)); - - return chatClient.call(prompt).getResult().getOutput().getContent(); - } - ``` - - 至此,便完成了最简单的模型接入!和本 example 中的代码略有不同,但 example 中的代码无需修改。可完成对应功能。 - -4. 启动应用 - - 本 Example 项目支持如下两种启动方式: - - 1. IDE 直接启动:找到主类 `TongYiApplication`,执行 main 方法启动应用。 - 2. 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,进入 `target` 文件夹执行 `java -jar spring-cloud-ai-example.jar` 启动应用。 - -## 验证 - -浏览器地址栏输入:`http://localhost:8080/ai/example` - -返回如下响应: - -```json -{ - "Tell me a joke": "Sure, here's a classic one for you:\n\nWhy was the math book sad?\n\nBecause it had too many problems.\n\nI hope that made you smile! If you're looking for more, just let me know." -} -``` - -## 简易前端验证 - -进入 `resources/static` 目录下,使用浏览器打开 index.html 文件,输入问题,即可获得输出响应(确保 api-key 有效): - -![ai-example](./images/sca-ai-example-front.gif) - -## 配置项说明 - -更多配置项参考: - -https://help.aliyun.com/zh/dashscope/developer-reference/api-details - -## 更多 example 示例: - -本 example 由 6 个示例组成,由不同的 serviceimpl 实现,您可以参考每个 serviceimpl 中的 readme 文件,在 controller 中使用 @RestController 作为入口点,您可以使用浏览器或者 curl 工具对接口进行请求。完成对应功能接入的练习。 - -> example 已完成功能,无需修改代码。只在对应的 example 中替换 apikey 即可,项目中提供的 apikey 无效。 diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/images/sca-ai-example-front.gif b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/images/sca-ai-example-front.gif deleted file mode 100644 index bc84dd593..000000000 Binary files a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/images/sca-ai-example-front.gif and /dev/null differ diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/pom.xml b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/pom.xml deleted file mode 100644 index a490f5b3c..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/pom.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - spring-cloud-alibaba-examples - com.alibaba.cloud - ${revision} - ../../pom.xml - - 4.0.0 - - spring-cloud-ai-example - Spring Cloud Starter Alibaba AI Example - Example demonstrating how to use Spring Cloud Alibaba AI - jar - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-ai - - - - - - - - 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 - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - false - - - - - \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/TongYiApplication.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/TongYiApplication.java deleted file mode 100644 index 44f134c9e..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/TongYiApplication.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -@SpringBootApplication -public class TongYiApplication { - - public static void main(String[] args) { - - SpringApplication.run(TongYiApplication.class); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/controller/TongYiController.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/controller/TongYiController.java deleted file mode 100644 index 6430549fc..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/controller/TongYiController.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.controller; - -import java.util.List; -import java.util.Map; - -import com.alibaba.cloud.ai.example.tongyi.models.ActorsFilms; -import com.alibaba.cloud.ai.example.tongyi.models.Completion; -import com.alibaba.cloud.ai.example.tongyi.service.TongYiService; -import com.alibaba.dashscope.audio.asr.transcription.TranscriptionParam; - -import org.springframework.ai.chat.messages.AssistantMessage; -import org.springframework.ai.image.ImageResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.core.io.Resource; -import org.springframework.web.bind.annotation.CrossOrigin; -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; - -/** - * TongYi models Spring Cloud Alibaba Controller. - * - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -@RestController -@RequestMapping("/ai") -@CrossOrigin -public class TongYiController { - - @Autowired - @Qualifier("tongYiSimpleServiceImpl") - private TongYiService tongYiSimpleService; - - @GetMapping("/example") - public String completion( - @RequestParam(value = "message", defaultValue = "Tell me a joke") - String message - ) { - - return tongYiSimpleService.completion(message); - } - - @GetMapping("/stream") - public Map streamCompletion( - @RequestParam(value = "message", defaultValue = "请告诉我西红柿炖牛腩怎么做?") - String message - ) { - - return tongYiSimpleService.streamCompletion(message); - } - - @Autowired - @Qualifier("tongYiOutputParseServiceImpl") - private TongYiService tongYiOutputService; - - @GetMapping("/output") - public ActorsFilms generate( - @RequestParam(value = "actor", defaultValue = "Jeff Bridges") String actor - ) { - - return tongYiOutputService.genOutputParse(actor); - } - - @Autowired - @Qualifier("tongYiPromptTemplateServiceImpl") - private TongYiService tongYiPromptTemplateService; - - @GetMapping("/prompt-tmpl") - public AssistantMessage completion(@RequestParam(value = "adjective", defaultValue = "funny") String adjective, - @RequestParam(value = "topic", defaultValue = "cows") String topic) { - - return tongYiPromptTemplateService.genPromptTemplates(adjective, topic); - } - - @Autowired - @Qualifier("tongYiRolesServiceImpl") - private TongYiService tongYiRolesService; - - @GetMapping("/roles") - public AssistantMessage generate( - @RequestParam(value = "message", defaultValue = "Tell me about three famous pirates from the Golden Age of Piracy and why they did. Write at least a sentence for each pirate.") String message, - @RequestParam(value = "name", defaultValue = "bot") String name, - @RequestParam(value = "voice", defaultValue = "pirate") String voice) { - - return tongYiRolesService.genRole(message, name, voice); - } - - @Autowired - @Qualifier("tongYiStuffServiceImpl") - private TongYiService tongYiStuffService; - - @GetMapping("/stuff") - public Completion completion(@RequestParam(value = "message", - defaultValue = "Which athletes won the mixed doubles gold medal in curling at the 2022 Winter Olympics?") String message, - @RequestParam(value = "stuffit", defaultValue = "false") boolean stuffit) { - - return tongYiStuffService.stuffCompletion(message, stuffit); - } - - @Autowired - @Qualifier("tongYiImagesServiceImpl") - private TongYiService tongYiImgService; - - @GetMapping("/img") - public ImageResponse genImg(@RequestParam(value = "prompt", - defaultValue = "Painting a picture of blue water and blue sky.") String imgPrompt) { - - return tongYiImgService.genImg(imgPrompt); - } - - @Autowired - @Qualifier("tongYiAudioSimpleServiceImpl") - private TongYiService tongYiAudioService; - - @GetMapping("/audio/speech") - public String genAudio(@RequestParam(value = "prompt", - defaultValue = "你好,Spring Cloud Alibaba AI 框架!") String prompt) { - - return tongYiAudioService.genAudio(prompt); - } - - @Autowired - @Qualifier("tongYiAudioTranscriptionServiceImpl") - private TongYiService tongYiAudioTranscriptionService; - - /** - * audio transcription. Support urls audio resource. - * {@link Resource} - * {@link TranscriptionParam} - * @param url audio url. - * @return transcription result, is String type. - */ - @GetMapping("/audio/transcription") - public String audioTranscription(@RequestParam(value = "audioUrls", - defaultValue = "https://dashscope.oss-cn-beijing.aliyuncs.com/samples/audio/paraformer/realtime_asr_example.wav") String url) { - - return tongYiAudioTranscriptionService.audioTranscription(url); - } - - @Autowired - @Qualifier("tongYiTextEmbeddingServiceImpl") - private TongYiService tongYiTextEmbeddingService; - - @GetMapping("/textEmbedding") - public List textEmbedding(@RequestParam(value = "text", - defaultValue = "Spring Cloud Alibaba AI 框架!") String text) { - - return tongYiTextEmbeddingService.textEmbedding(text); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/models/ActorsFilms.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/models/ActorsFilms.java deleted file mode 100644 index 15ac124ed..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/models/ActorsFilms.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.models; - -import java.util.List; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -public class ActorsFilms { - - private String actor; - - private List movies; - - public ActorsFilms() { - } - - public String getActor() { - return actor; - } - - public void setActor(String actor) { - this.actor = actor; - } - - public List getMovies() { - return movies; - } - - public void setMovies(List movies) { - this.movies = movies; - } - - @Override - public String toString() { - return "ActorsFilms{" + "actor='" + actor + '\'' + ", movies=" + movies + '}'; - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/models/Completion.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/models/Completion.java deleted file mode 100644 index 43eedd419..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/models/Completion.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.models; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -public class Completion { - - private final String completion; - - public Completion(String completion) { - this.completion = completion; - } - - public String getCompletion() { - return completion; - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/AbstractTongYiServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/AbstractTongYiServiceImpl.java deleted file mode 100644 index 1a685d7e5..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/AbstractTongYiServiceImpl.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service; - -import java.util.List; -import java.util.Map; - -import com.alibaba.cloud.ai.example.tongyi.models.ActorsFilms; -import com.alibaba.cloud.ai.example.tongyi.models.Completion; - -import org.springframework.ai.chat.messages.AssistantMessage; -import org.springframework.ai.image.ImageResponse; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -public abstract class AbstractTongYiServiceImpl implements TongYiService { - - private static final String INFO_PREFIX = "please implement "; - private static final String INFO_SUFFIX = "() method."; - - @Override - public String completion(String message) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread().getStackTrace()[2].getMethodName()); - } - - @Override - public Map streamCompletion(String message) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - - @Override - public ActorsFilms genOutputParse(String actor) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - - @Override - public AssistantMessage genPromptTemplates(String adjective, String topic) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - - @Override - public AssistantMessage genRole(String message, String name, String voice) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - - @Override - public Completion stuffCompletion(String message, boolean stuffit) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - - @Override - public ImageResponse genImg(String imgPrompt) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - - @Override - public String genAudio(String text) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - - @Override - public List textEmbedding(String text) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - - @Override - public String audioTranscription(String url) { - - throw new RuntimeException(INFO_PREFIX + Thread.currentThread() - .getStackTrace()[2].getMethodName() + INFO_SUFFIX); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/TongYiService.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/TongYiService.java deleted file mode 100644 index 3f570f5cf..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/TongYiService.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service; - -import java.util.List; -import java.util.Map; - -import com.alibaba.cloud.ai.example.tongyi.models.ActorsFilms; -import com.alibaba.cloud.ai.example.tongyi.models.Completion; - -import org.springframework.ai.chat.messages.AssistantMessage; -import org.springframework.ai.embedding.EmbeddingResponse; -import org.springframework.ai.image.ImageResponse; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -public interface TongYiService { - - /** - * Hello World example. - * - * @param message conversation content question. - * @return AI answer. - */ - String completion(String message); - - /** - * Stream call. - * - * @param message conversation content question. - * @return AI answer. - */ - Map streamCompletion(String message); - - /** - * Output parse object. - * - * @param actor actor name. - * @return Object. - */ - ActorsFilms genOutputParse(String actor); - - /** - * Prompt template. - * - * @param adjective params1. - * @param topic params2. - * @return AI answer. - */ - AssistantMessage genPromptTemplates(String adjective, String topic); - - /** - * AI role example. - * - * @param message question content, - * @param name params1. - * @param voice params2. - * @return AI answer. - */ - AssistantMessage genRole(String message, String name, String voice); - - /** - * Stuff and answer. - * - * @param message question. - * @param stuffit is stuff. - * @return Completion object. - */ - Completion stuffCompletion(String message, boolean stuffit); - - /** - * Gen images. - * @param imgPrompt prompt info. - * @return {@link ImageResponse} - */ - ImageResponse genImg(String imgPrompt); - - /** - * Gen audio. - * @param text prompt info. - * @return ByteBuffer object. - */ - String genAudio(String text); - - /** - * Audio Transcription. - * @param audioUrls url of the audio file to be transcribed. - * @return the result file Path. - */ - String audioTranscription(String audioUrls); - - /** - * TongYI LLM Text embedding. - * @param text input text. - * @return {@link EmbeddingResponse} - */ - List textEmbedding(String text); - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/speech/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/speech/README.md deleted file mode 100644 index 3adb041f5..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/speech/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Spring Cloud Alibaba AI Audio - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/audio`。 -`controller` 将会调用 `TongYiService` 中的 `genAudio` 方法,完成服务请求得到响应。 - -有一个可选的 `ptompt` 参数,其默认值为“你好,Spring Cloud Alibaba AI 框架!”。 请求响应来自 Alibaba TongYi 语音合成服务。 - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具对接口发起请求: - -```shell -$ curl http://localhost:8080/ai/audio - -# Response: -D:\open_sources\sca-ai\spring-ai\04-11-18-21-59.wav -``` - -返回参数为保存到当前根路径下的音频文明路径。 diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/speech/TongYiAudioSimpleServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/speech/TongYiAudioSimpleServiceImpl.java deleted file mode 100644 index cdde2cc20..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/speech/TongYiAudioSimpleServiceImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.audio.speech; - -import java.io.File; -import java.io.FileOutputStream; -import java.nio.ByteBuffer; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; -import com.alibaba.cloud.ai.example.tongyi.service.TongYiService; -import com.alibaba.cloud.ai.tongyi.audio.speech.api.SpeechModel; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@Service -public class TongYiAudioSimpleServiceImpl extends AbstractTongYiServiceImpl { - - private static final Logger logger = LoggerFactory.getLogger(TongYiService.class); - - private final SpeechModel speechClient; - - @Autowired - public TongYiAudioSimpleServiceImpl(SpeechModel client) { - - this.speechClient = client; - } - - @Override - public String genAudio(String text) { - - logger.info("gen audio prompt is: {}", text); - - var resWAV = speechClient.call(text); - - return save(resWAV, SpeechSynthesisAudioFormat.WAV.getValue()); - } - - private String save(ByteBuffer audio, String type) { - - String currentPath = System.getProperty("user.dir"); - LocalDateTime now = LocalDateTime.now(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-HH-mm-ss"); - String fileName = currentPath + File.separator + now.format(formatter) + "." + type; - File file = new File(fileName); - - try (FileOutputStream fos = new FileOutputStream(file)) { - fos.write(audio.array()); - } - catch (Exception e) { - throw new RuntimeException(e); - } - - return fileName; - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/transcription/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/transcription/README.md deleted file mode 100644 index 6437811d5..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/transcription/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Spring Cloud Alibaba AI Audio Transcription - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/audio/transcription` -`controller` 将会调用 `TongYiService` 中的 `audioTranscription` 方法,完成服务请求得到响应。 - -可设置`file_urls`参数,提供一个或多个需要进行语音识别的音视频文件。 - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具对接口发起请求: - -```shell -$ curl -X GET "http://localhost:8080/ai/audio/transcription?audioUrls=url1&audioUrls=url2" - -# Response: -D:\Code\spring-cloud-alibaba\05-13-20-47-08.txt -D:\Code\spring-cloud-alibaba\05-13-20-47-09.txt -``` - -返回参数为保存到当前根路径下的音频转录文本文件的路径。 diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/transcription/TongYiAudioTranscriptionServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/transcription/TongYiAudioTranscriptionServiceImpl.java deleted file mode 100644 index d2275e927..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/audio/transcription/TongYiAudioTranscriptionServiceImpl.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.audio.transcription; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; - -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; -import com.alibaba.cloud.ai.example.tongyi.service.TongYiService; -import com.alibaba.cloud.ai.tongyi.audio.transcription.TongYiAudioTranscriptionModel; -import com.alibaba.cloud.ai.tongyi.audio.transcription.api.AudioTranscriptionPrompt; -import com.alibaba.cloud.ai.tongyi.audio.transcription.api.AudioTranscriptionResult; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; -import org.springframework.core.io.UrlResource; -import org.springframework.stereotype.Service; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -@Service -public class TongYiAudioTranscriptionServiceImpl extends AbstractTongYiServiceImpl { - - private static final Logger logger = LoggerFactory.getLogger(TongYiService.class); - - private final TongYiAudioTranscriptionModel audioTranscriptionModel; - - @Autowired - public TongYiAudioTranscriptionServiceImpl(final TongYiAudioTranscriptionModel transcriptionModel) { - - this.audioTranscriptionModel = transcriptionModel; - } - - @Override - public String audioTranscription(String audioUrls) { - - Resource resource; - - try { - resource = new UrlResource(audioUrls); - } - catch (IOException e) { - logger.error("Failed to create resource."); - throw new RuntimeException(e); - } - AudioTranscriptionPrompt audioTranscriptionPrompt = new AudioTranscriptionPrompt(resource); - - return save(audioTranscriptionModel.call(audioTranscriptionPrompt).getResults()); - } - - private String save(List resultList) { - String currentPath = System.getProperty("user.dir"); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-HH-mm-ss"); - StringBuilder retPaths = new StringBuilder(); - for (AudioTranscriptionResult audioTranscriptionResult : resultList) { - String tUrl = audioTranscriptionResult.getOutput(); - LocalDateTime now = LocalDateTime.now(); - String fileName = currentPath + File.separator + now.format(formatter) + ".txt"; - retPaths.append(fileName).append("\n"); - try { - URL url = new URL(tUrl); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - StringBuilder sb = new StringBuilder(); - int responseCode = connection.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_OK) { - try (BufferedInputStream in = new BufferedInputStream(connection.getInputStream()); FileOutputStream fileOutputStream = new FileOutputStream(fileName)) { - byte[] dataBuffer = new byte[1024]; - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - sb.append(new String(dataBuffer, 0, bytesRead)); - } - JsonObject rootObj = JsonParser.parseString(sb.toString()).getAsJsonObject(); - JsonArray transcriptsArray = rootObj.getAsJsonArray("transcripts"); - - for (var transcriptElement : transcriptsArray) { - JsonObject transcriptObj = transcriptElement.getAsJsonObject(); - String text = transcriptObj.get("text").getAsString(); - fileOutputStream.write(text.getBytes()); - } - logger.info("File downloaded successfully:{}\n", fileName); - } - } - else { - logger.error("The download failed, and the response code:{}", - responseCode); - } - connection.disconnect(); - } - catch (IOException e) { - logger.error("An error occurred during the file download process."); - } - } - return retPaths.toString(); - } -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/helloworld/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/helloworld/README.md deleted file mode 100644 index 956421a0e..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/helloworld/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Spring Cloud Alibaba AI Hello World - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/example`。 -`controller` 将会调用 `TongYiService` 中的 `completion` 方法,完成服务请求得到响应。 - -有一个可选的 `message` 参数,其默认值为“告诉我一个笑话”。 请求响应来自 Alibaba TongYi models 服务。 - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具对接口发起请求: - -```shell -$ curl http://localhost:8080/ai/example - -# Response: -Sure, here's one for you: - -Why don't scientists trust atoms? - -Because they make up everything! -``` - -现在使用 message 参数: - -```shell -$ curl --get --data-urlencode 'message=Tell me a joke about a cow.' http://localhost:8080/ai/example - -# Response: -Here's a classic cow joke for you: - -Why did the farmer separate the chicken and the sheep from the cows? - -Because he wanted to have eggs-clusive relationships with his hens! -``` diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/helloworld/TongYiSimpleServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/helloworld/TongYiSimpleServiceImpl.java deleted file mode 100644 index 214281b05..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/helloworld/TongYiSimpleServiceImpl.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.helloworld; - -import java.util.Map; - -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Flux; - -import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.StreamingChatModel; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** - * The Chat simple example service implementation. - * There is optional message parameter whose default value is "Tell me a joke". - * pl The response to the request is from the TongYi models Service. - * - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -@Service -public class TongYiSimpleServiceImpl extends AbstractTongYiServiceImpl { - - private static final Logger logger = LoggerFactory.getLogger(TongYiSimpleServiceImpl.class); - - private final ChatModel chatModel; - - private final StreamingChatModel streamingChatModel; - - @Autowired - public TongYiSimpleServiceImpl(ChatModel chatModel, StreamingChatModel streamingChatModel) { - - this.chatModel = chatModel; - this.streamingChatModel = streamingChatModel; - } - - @Override - public String completion(String message) { - - Prompt prompt = new Prompt(new UserMessage(message)); - - return chatModel.call(prompt).getResult().getOutput().getContent(); - } - - @Override - public Map streamCompletion(String message) { - - StringBuilder fullContent = new StringBuilder(); - - streamingChatModel.stream(new Prompt(message)) - .flatMap(chatResponse -> Flux.fromIterable(chatResponse.getResults())) - .map(content -> content.getOutput().getContent()) - .doOnNext(fullContent::append) - .last() - .map(lastContent -> Map.of(message, fullContent.toString())) - .block(); - - logger.info(fullContent.toString()); - - return Map.of(message, fullContent.toString()); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/images/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/images/README.md deleted file mode 100644 index 5a8a07c2d..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/images/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Spring Cloud Alibaba AI Image - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/img`。 -`controller` 将会调用 `TongYiService` 中的 `genImg` 方法,完成服务请求得到响应。 - -有一个可选的 `prompt` 参数为生成图片的提示信息,其默认值为“Painting a picture of blue water and blue sky.”。 - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具对接口发起请求: - -```shell -$ curl http://localhost:8080/ai/img -``` - -响应结果为:(base64 数据太多,使用 `xxx` 代替) - -```json -{ - "result": { - "output": { - "url": "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/1d/42/20240408/8d820c8d/e9913e23-24e9-4de7-8977-e4ccab33a231-1.png?Expires=1712670359&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=bMEaNS0RGTpD2yXO0lTMMY5AWxM%3D", - "b64Json": "xxxx" - }, - "metadata": null - }, - "metadata": { - "created": 1712583967354, - "taskId": "43d7e458-9f3e-4a51-9a39-17559d8f135d", - "metrics": { - "total": 4, - "succeeded": 4, - "failed": 0 - }, - "usage": { - "imageCount": 4 - } - }, - "results": [ - { - "output": { - "url": "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/1d/42/20240408/8d820c8d/e9913e23-24e9-4de7-8977-e4ccab33a231-1.png?Expires=1712670359&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=bMEaNS0RGTpD2yXO0lTMMY5AWxM%3D", - "b64Json": "xxxx" - }, - "metadata": null - }, - { - "output": { - "url": "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/1d/2b/20240408/8d820c8d/0bd0b40f-4e34-46da-8706-8f2ec86274d7-1.png?Expires=1712670359&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=dl3sMGQn8p7y%2FzKOmPR%2B64prQV4%3D", - "b64Json": "xxxx" - }, - "metadata": null - }, - { - "output": { - "url": "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/1d/62/20240408/c34adf05/ffb89a14-14c5-4740-ab55-37b59a69aaef-1.png?Expires=1712670359&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=vYd667hVPQUTt8aiJDBFxN%2B08jI%3D", - "b64Json": "xxxx" - }, - "metadata": null - }, - { - "output": { - "url": "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/1d/b5/20240408/8d820c8d/594b8672-c1ce-49b6-bab0-06e3616b4e0e-1.png?Expires=1712670359&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=ERyoV7pmmI8sZJwSFLpzknhfFEk%3D", - "b64Json": "xxxx" - }, - "metadata": null - } - ] -} -``` diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/images/TongYiImagesServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/images/TongYiImagesServiceImpl.java deleted file mode 100644 index 56475ac99..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/images/TongYiImagesServiceImpl.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.images; - -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; - -import org.springframework.ai.image.ImageModel; -import org.springframework.ai.image.ImagePrompt; -import org.springframework.ai.image.ImageResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** - * gen images example. - * - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -@Service -public class TongYiImagesServiceImpl extends AbstractTongYiServiceImpl { - - private final ImageModel imageModel; - - @Autowired - public TongYiImagesServiceImpl(ImageModel imageModel) { - - this.imageModel = imageModel; - } - - @Override - public ImageResponse genImg(String imgPrompt) { - - var prompt = new ImagePrompt(imgPrompt); - return imageModel.call(prompt); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/output/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/output/README.md deleted file mode 100644 index 9af498f96..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/output/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# Spring Cloud Alibaba AI Output - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/output`。 -`controller` 将会调用 `TongYiService` 中的 `genOutputParse` 方法,完成服务请求得到响应。 - -有一个可选的 `actor` 参数为演员的名字,其默认值为“Jeff Bridges”。 -演员姓名位于硬编码的文本中: - -```java -String userMessage = """ - Generate the filmography for the actor {actor}. - {format} - """; -``` - -`format` 变量来自 `OutputParser` 中。 - -## BeanOutputParser - -`BeanOutputParser` 当请求获得响应时,会将响应内容解析为一个 JavaBean 并返回给用户: - -```java -var outputParser = new BeanOutputParser<>(ActorsFilms.class); -String format = outputParser.getFormat(); -``` - -模型响应内容会被解析为类 `ActorsFils` 的类属性: - -```java -ActorsFilms actorsFilms = outputParser.parse(content); -``` - -> 返回的数据结构必须符合 `https://json-schema.org/draft/2020-12/schema` 规范,不然会解析失败,这里采用替换的方式先行处理: -> ```java -> // {@link BeanOutputParser#getFormat} -> // simple solve. -> String content = generation.getOutput().getContent() -> .replace("```json", "") -> .replace("```", ""); -> ``` - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具对接口发起请求: - -```shell -$ curl http://localhost:8080/ai/output -``` - -响应结果为: - -```json -{ - "actor": "Jeff Bridges", - "movies": [ - "Starman (1984)", - "The Natural (1984)", - "The Longest Week (1979)", - "Against the Wind (1977)", - "Fat City (1978)", - "American Heart (1981)", - "Iron Eagle (1986)", - "The China Syndrome (1979)", - "Blazing Saddles (1974)", - "Winter Kills (1979)", - "Theetreowners of Broadway (1973)", - "Trinity (1990)", - "Tokyo Story (1953) [English dub voice]", - "The Longest Ride (2015)", - "Another Day in the Valley (1991)", - "Stardust Memories (1976)", - "Jagged Edge (1985)", - "The Adventures of Ford Fairlane (1996)", - "The Vanishing (1988)", - "Dances with Wolves (1990)", - "The Mirror Has Two Faces (1996)", - "The Door in the Floor (2004)", - "The Way Back (2010)", - "The Secret Life of Walter Mitty (2013)", - "The Big Lebowski (1998)", - "The Iron Giant (1999) [voice]", - "The Man Who Wasn't There (2001)", - "True Grit (2010)", - "Crazy Heart (2009)", - "Iron Man 2 (2010) [voice]", - "TRON: Legacy (2010)", - "The Giver (2014)", - "Hell or High Water (2016)" - ] -} -``` - -现在使用 actor 参数: - -```shell -$ curl --get --data-urlencode 'actor=Bill Murray' http://localhost:8080/ai/output - -``` - -Response: - -```json -{ - "actor": "Bill Murray", - "movies": [ - "The<[email protected]>", - "Caddyshack", - "Ghostbusters", - "Stripes", - "Scrooged", - "Groundhog Day", - "What About Bob?", - " Coneheads ", - "Stuart Saves His Family", - " Rushmore", - "The Royal Tenenbaums", - "Lost in Translation", - "Garfield: The Movie", - "Osmosis Jones", - "The Life Aquatic with Steve Zissou", - "Hot Fuzz", - "Get Smart", - "The Grand Budapest Hotel", - "Trainwreck", - "Chef", - "Inside Out", - "Zootopia", - "On the Rocks", - "Spies in Disguise" - ] -} -``` diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/output/TongYiOutputParseServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/output/TongYiOutputParseServiceImpl.java deleted file mode 100644 index 230ea0feb..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/output/TongYiOutputParseServiceImpl.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.output; - -import java.util.Map; - -import com.alibaba.cloud.ai.example.tongyi.models.ActorsFilms; -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.Generation; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.PromptTemplate; -import org.springframework.ai.converter.BeanOutputConverter; -import org.springframework.stereotype.Service; - -/** - * The BeanOutputParser generates an TongYI JSON compliant schema for a - * JavaBean and provides instructions to use that schema when replying to a request. - * - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -@Service -public class TongYiOutputParseServiceImpl extends AbstractTongYiServiceImpl { - - private static final Logger logger = LoggerFactory.getLogger(TongYiOutputParseServiceImpl.class); - - private final ChatModel chatModel; - - public TongYiOutputParseServiceImpl(ChatModel chatModel) { - this.chatModel = chatModel; - } - - @Override - public ActorsFilms genOutputParse(String actor) { - - var outputParser = new BeanOutputConverter<>(ActorsFilms.class); - - String format = outputParser.getFormat(); - logger.info("format: " + format); - String userMessage = """ - Generate the filmography for the actor {actor}. - {format} - """; - PromptTemplate promptTemplate = new PromptTemplate(userMessage, Map.of("actor", actor, "format", format)); - Prompt prompt = promptTemplate.create(); - Generation generation = chatModel.call(prompt).getResult(); - - // {@link BeanOutputParser#getFormat} - // simple solve. - String content = generation.getOutput().getContent() - .replace("```json", "") - .replace("```", ""); - - return outputParser.parse(content); - } -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/prompttemplate/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/prompttemplate/README.md deleted file mode 100644 index f3e7641e6..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/prompttemplate/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Spring Cloud Alibaba AI Prompt Template - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/prompt-tmpl`。 -`controller` 将会调用 `TongYiService` 中的 `genPromptTemplates` 方法,完成服务请求得到响应。 - -本示例代码展示如何使用 `StringTemplate` 引擎和 `Spring AI PromptTemplate` 类。目录中 `resources\prompts` 文件为 `joke-prompt`。此文件由 Spring 加载: - -```java -@Value("classpath:/prompts/joke-prompt.st") -private Resource jokeResource; -``` - -文件内容为: - -```json -Tell me a {adjective} joke about {topic}. -``` - -接受两个可选参数: - -1. `adjective`,其默认值为 `funny` -2. `topic`,其默认值为 `cows` - -请求响应来自 Alibaba TongYi models. - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具对接口发起请求: - -```shell -$ curl http://localhost:8080/ai/prompt-tmpl -``` - -响应结果为: - -```json -{ - "messageType": "ASSISTANT", - "properties": {}, - "content": "Why did the cow go to art school? Because she wanted to learn how to draw moo-vements!", - "media": [] -} -``` - -现在使用 adjective 和 topic 参数: - -```shell -$ curl --get --data-urlencode message='Tell me about 3 famous physicists' --data-urlencode name='yuluo' --data-urlencode voice='Rick Sanchez' http://localhost:8080/ai/roles -``` - -Response: - -```json -{ - "messageType": "ASSISTANT", - "properties": {}, - "content": "Sure, here's another one:\n\nWhy did the farmer separate the chicken and the cow?\n\nBecause he wanted to have eggs-clusive relationships with his hens!", - "media": [] -} -``` diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/prompttemplate/TongYiPromptTemplateServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/prompttemplate/TongYiPromptTemplateServiceImpl.java deleted file mode 100644 index 8623e2118..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/prompttemplate/TongYiPromptTemplateServiceImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.prompttemplate; - -import java.util.Map; - -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; - -import org.springframework.ai.chat.messages.AssistantMessage; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.PromptTemplate; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Service; - -/** - * The TongYiPromptTemplateServiceImpl shows how to use the StringTemplate Engine and the Spring AI PromptTemplate class. - * In the resources\prompts directory is the file joke-prompt. - * - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -@Service -public class TongYiPromptTemplateServiceImpl extends AbstractTongYiServiceImpl { - - private final ChatModel chatModel; - - @Value("classpath:/prompts/joke-prompt.st") - private Resource jokeResource; - - public TongYiPromptTemplateServiceImpl(ChatModel chatModel) { - this.chatModel = chatModel; - } - - @Override - public AssistantMessage genPromptTemplates(String adjective, String topic) { - - PromptTemplate promptTemplate = new PromptTemplate(jokeResource); - - Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic)); - return chatModel.call(prompt).getResult().getOutput(); - } -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/roles/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/roles/README.md deleted file mode 100644 index 2778d480d..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/roles/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Spring Cloud Alibaba AI Roles - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/roles`。 -`controller` 将会调用 `TongYiService` 中的 `genRole` 方法,完成服务请求得到响应。 - -接受带有三个可选参数的PromptTemplateControllerHTTP GET 请求http://localhost:8080/ai/roles - -`message` 用户请求消息,默认值为 `Tell me about three famous pirates from the Golden Age of Piracy and why they did. Write at least a sentence for each pirate.`; -`name` AI助手的名字,默认值为 `Bob`; -`voice` AI 助手回复的语音风格。默认值为 `pirate`。 - -请求响应来自 Alibaba TongYi models 服务。 - -## Roles - -对于每个角色,都会创建一条消息,该消息将作为提示的一部分发送到 AI 模型。 - -> 用户消息是“消息”的内容。 -> -> 系统消息为 AI 模型设置响应的上下文。 - -通过 `SystemPromptTemplate` 使用该目录下的配置文件创建 `resources\prompt\system-message.st`。 - -该文件内容为: - -```json -You are a helpful AI assistant. -You are an AI assistant that helps people find information. -Your name is {name} -You should reply to the user's request with your name and also in the style of a {voice}. -``` - -用户消息和系统消息组合在一起创建 `Prompt`。 - -```java -Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); -``` - -> 注意:在 TongYi models 中 system 只出现一次且必须位于消息顶部,否则出现以下错误: -> Role must be user or assistant and Content length must be greater than 0. - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具对接口发起请求: - -```shell -$ curl http://localhost:8080/ai/roles -``` - -Response: - -```json -{ - "messageType": "ASSISTANT", - "properties": {}, - "content": "Ahoy matey! I be Bot, the swashbucklin' AI at yer service! Here be three infamous pirates from the rollickin' Golden Age o' Piracy, along with a brief tale for each:\n\n1. Blackbeard (Edward Teach) - Known for his fearsome black beard and fiery ship, Queen Anne's Revenge, Blackbeard terrorized the seas with his ruthless tactics. He sought treasure and power, often leaving a trail of dread behind.\n\n2. Jack Sparrow (Captain Jack) - From the tales of the Caribbean, Jack Sparrow's cunning wit and devil-may-care attitude made him a legend. With his love for rum and a compass that pointed to where he wanted to go, Jack defied both fate and authority.\n\n3. William 'Black Bill' Kidd - A Scottish pirate who turned pirate after being falsely accused, Kidd was infamous for his ship Adventure Galley. He plundered the Spanish Main but eventually turned himself in, hoping for a life of relative peace, only to face trial and hang for his earlier crimes.\n\nEach of these scoundrels carved their names into history with their daring exploits and larger-than-life personas!", - "media": [] -} -``` - -现在使用 message, name, voice 参数: - -```shell -$ curl --get --data-urlencode message=="Tell me about 3 famous physicists" name=="yuluo" voice=="Rick Sanchez" http://localhost:8080/ai/roles -``` - -Response: - -```json -{ - "messageType":"ASSISTANT", - "properties":{}, - "content":"Yo dawg, I'm Yuluo, the quantum physicist's best friend! Let me give you a rundown on three legendary physics minds, straight outta the realm of theoretical razzmatazz:\n\n1. Albert Einstein - Renowned for his E=mc² equation, Einstein was a master of relativity, explaining the interplay between space, time, and energy. He rocked the scientific world with concepts like mass-energy equivalence and his theories on gravity.\n\n2. Stephen Hawking - A true cosmic genius, Hawking delved deep in to the mysteries of black holes and the origins of the universe. Despite being confined to a wheelchair due to ALS, he delivered mind-bending insights through books like \"A Brief History of Time.\"\n\n3. Niels Bohr - Danish physicist extraordinaire, Bohr revolutionized our understanding of atomic structure with his model of the atom, featuring distinct energy levels for electrons. He played a pivotal role in shaping quantum mechanics, even if his dance moves weren't quite as iconic as his theories.\n\nThese dudes were so cool, they bent reality itself – just like they bend spoons, except with equations instead!", - "media":[]} -``` diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/roles/TongYiRolesServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/roles/TongYiRolesServiceImpl.java deleted file mode 100644 index a83217f2d..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/roles/TongYiRolesServiceImpl.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.roles; - -import java.util.List; -import java.util.Map; - -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; - -import org.springframework.ai.chat.messages.AssistantMessage; -import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.SystemPromptTemplate; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Service; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -@Service -public class TongYiRolesServiceImpl extends AbstractTongYiServiceImpl { - - private final ChatModel chatModel; - - public TongYiRolesServiceImpl(ChatModel chatModel) { - this.chatModel = chatModel; - } - - @Value("classpath:/prompts/assistant-message.st") - private Resource systemResource; - - @Override - public AssistantMessage genRole(String message, String name, String voice) { - - /** - TongYi model rules: Role must be user or assistant and Content length must be greater than 0. - SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); - org.springframework.ai.chat.messages.Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); - - In TongYi models, System role must appear at the top of the message and can only appear once. - https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.4dbcc11akAaRbs#b9ad0a10cfhpe - */ - - SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); - org.springframework.ai.chat.messages.Message systemPromptTemplateMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); - UserMessage userMessage = new UserMessage(message); - - Prompt prompt = new Prompt(List.of(systemPromptTemplateMessage, userMessage)); - - return chatModel.call(prompt).getResult().getOutput(); - } -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/stuff/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/stuff/README.md deleted file mode 100644 index 76227da55..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/stuff/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Spring Cloud Alibaba AI Stuff models - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/stuff`。 -`controller` 将会调用 `TongYiService` 中的 `stuffCompletion` 方法,完成服务请求得到响应。 - -模型训练数据仅仅到 2021 年,对之后的问题无法回答。此示例将演示如何使用一些数据完成对模型上下文的填充,使得模型能够回答更多的问题。 -将以 2022 年的冰壶比赛冠军是谁为例进行演示。 - -```shell -$ curl http://localhost:8080/ai/stuff -{"completion":"As previously mentioned, my knowledge is current until 2021 and I do not have information about the 2022 Winter Olympics results. Therefore, I can't provide the winner of the mixed doubles gold medal in curling from those games."} -``` - -## 预处理内容 - -### qa-prompt.st - -```json -Use the following pieces of context to answer the question at the end. -If you don't know the answer, just say that you don't know, don't try to make up an answer. - -{context} - -Question: {question} -Helpful Answer: -``` - -```md -Jack and arokg, slim won the gold medal in mixed doubles curling at the 2022 Winter Olympics. -``` - -以上内容将由 Spring 加载到 Resource 对象中: - -```java -@Value("classpath:/docs/wikipedia-curling.md") -private Resource docsToStuffResource; - -@Value("classpath:/prompts/qa-prompt.st") -private Resource qaPromptResource; -``` - -一并填充到 Prompt 中,由 Alibaba TongYi models 返回响应。 - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具对接口发起请求: - -```shell -$ curl http://localhost:8080/ai/stuff - -# Response: -{"completion":"I'm sorry, but I don't have information on the specific winners of the 2022 Winter Olympics events. My knowledge is current until 2021, and I cannot provide real-time or future sports results."} -``` - -现在使用 stuffit 参数: - -```shell -$ curl --get --data-urlencode 'stuffit=true' http://localhost:8080/ai/stuff - -# Response: -{"completion":"The athletes who won the mixed doubles gold medal in curling at the 2022 Winter Olympics, according to the provided context, were Jack and Arokg Slim."} -``` diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/stuff/TongYiStuffServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/stuff/TongYiStuffServiceImpl.java deleted file mode 100644 index f62d02cf7..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/stuff/TongYiStuffServiceImpl.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.stuff; - -import java.util.HashMap; -import java.util.Map; - -import com.alibaba.cloud.ai.example.tongyi.models.Completion; -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; - -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.Generation; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.PromptTemplate; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Service; - -/** - * Train the model using pre-found data to enhance the AI model to achieve the desired results. - * - * @author yuluo - * @author yuluo - * @since 2023.0.0.0 - */ - -@Service -public class TongYiStuffServiceImpl extends AbstractTongYiServiceImpl { - - private final ChatModel chatModel; - - public TongYiStuffServiceImpl(ChatModel chatModel) { - this.chatModel = chatModel; - } - - @Value("classpath:/docs/wikipedia-curling.md") - private Resource docsToStuffResource; - - @Value("classpath:/prompts/qa-prompt.st") - private Resource qaPromptResource; - - // TongYi model: Range of input length should be [1, 6000] - @Override - public Completion stuffCompletion(String message, boolean stuffit) { - - PromptTemplate promptTemplate = new PromptTemplate(qaPromptResource); - Map map = new HashMap<>(); - map.put("question", message); - - if (stuffit) { - map.put("context", docsToStuffResource); - } - else { - map.put("context", ""); - } - - Prompt prompt = promptTemplate.create(map); - Generation generation = chatModel.call(prompt).getResult(); - return new Completion(generation.getOutput().getContent()); - } -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/textembedding/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/textembedding/README.md deleted file mode 100644 index 8d5da8ee3..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/textembedding/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Spring Cloud Alibaba AI Text Embedding - -`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/textEmbedding`。 -`controller` 将会调用 `TongYiService` 中的 `genAudio` 方法,完成服务请求得到响应。 - -有一个可选的 `text` 参数,其默认值为“Spring Cloud Alibaba AI 框架!”。 请求响应来自 Alibaba TongYi Text Embedding 服务。 - -## 构建和运行 - -1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey; -2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。 - -## 访问接口 - -使用 curl 工具或者使用浏览器对接口发起请求: - -```shell -$ curl http://localhost:8080/ai/textEmbedding - -# Response: -为一组向量集合 -``` diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/textembedding/TongYiTextEmbeddingServiceImpl.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/textembedding/TongYiTextEmbeddingServiceImpl.java deleted file mode 100644 index 50958bbfe..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/textembedding/TongYiTextEmbeddingServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.example.tongyi.service.impl.textembedding; - -import java.util.List; - -import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl; - -import org.springframework.ai.embedding.EmbeddingModel; -import org.springframework.stereotype.Service; - -/** - * @author why_ohh - * @author why_ohh - */ - -@Service -public class TongYiTextEmbeddingServiceImpl extends AbstractTongYiServiceImpl { - - private final EmbeddingModel embeddingModel; - - public TongYiTextEmbeddingServiceImpl(EmbeddingModel embeddingModel) { - - this.embeddingModel = embeddingModel; - } - - @Override - public List textEmbedding(String text) { - - return embeddingModel.embed(text); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/application.yml deleted file mode 100644 index de666d396..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/application.yml +++ /dev/null @@ -1,26 +0,0 @@ -# -# Copyright 2023-2024 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. -# - -server: - port: 8080 - -spring: - application: - name: tongyi-example - -# please setting api-key. suggestion by environment variable. -# Note: api-key is invalid, please apply for a new one. -# export SPRING_CLOUD_ALIBABA_TONGYI_API_KEY=sk-a3d73b1709bf4a178c28ed7c8b3b5a345 diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/docs/wikipedia-curling.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/docs/wikipedia-curling.md deleted file mode 100644 index 619327080..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/docs/wikipedia-curling.md +++ /dev/null @@ -1 +0,0 @@ -Jack and arokg, slim won the gold medal in mixed doubles curling at the 2022 Winter Olympics. diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/assistant-message.st b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/assistant-message.st deleted file mode 100644 index 31a52e86a..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/assistant-message.st +++ /dev/null @@ -1,4 +0,0 @@ -You are a helpful AI assistant. -You are an AI assistant that helps people find information. -Your name is {name} -You should reply to the user's request with your name and also in the style of a {voice}. diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/joke-prompt.st b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/joke-prompt.st deleted file mode 100644 index 4e6aa0898..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/joke-prompt.st +++ /dev/null @@ -1 +0,0 @@ -Tell me a {adjective} joke about {topic}. diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/qa-prompt.st b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/qa-prompt.st deleted file mode 100644 index af5df383b..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/prompts/qa-prompt.st +++ /dev/null @@ -1,7 +0,0 @@ -Use the following pieces of context to answer the question at the end. -If you don't know the answer, just say that you don't know, don't try to make up an answer. - -{context} - -Question: {question} -Helpful Answer: diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/static/index.html b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/static/index.html deleted file mode 100644 index 409de69c7..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/static/index.html +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - SCA AI Example Front - - - -
-

Spring Cloud Alibaba AI Example

-
- - -
-
- -
-
- -
-
- - - - \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/static/js/marked.min.js b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/static/js/marked.min.js deleted file mode 100644 index 190a549a8..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example/src/main/resources/static/js/marked.min.js +++ /dev/null @@ -1,1663 +0,0 @@ -/** - * marked v12.0.1 - a markdown parser - * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed) - * https://github.com/markedjs/marked - */ -!function(e, t) { - "object" == typeof exports && "undefined" != typeof module ? t(exports) : "function" == typeof define && define.amd ? define(["exports"], t) : t((e = "undefined" != typeof globalThis ? globalThis : e || self).marked = {}) -}(this, (function(e) { - "use strict"; - function t() { - return { - async: !1, - breaks: !1, - extensions: null, - gfm: !0, - hooks: null, - pedantic: !1, - renderer: null, - silent: !1, - tokenizer: null, - walkTokens: null - } - } - function n(t) { - e.defaults = t - } - e.defaults = { - async: !1, - breaks: !1, - extensions: null, - gfm: !0, - hooks: null, - pedantic: !1, - renderer: null, - silent: !1, - tokenizer: null, - walkTokens: null - }; - const s = /[&<>"']/ - , r = new RegExp(s.source,"g") - , i = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/ - , l = new RegExp(i.source,"g") - , o = { - "&": "&", - "<": "<", - ">": ">", - '"': """, - "'": "'" - } - , a = e=>o[e]; - function c(e, t) { - if (t) { - if (s.test(e)) - return e.replace(r, a) - } else if (i.test(e)) - return e.replace(l, a); - return e - } - const h = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi; - function p(e) { - return e.replace(h, ((e,t)=>"colon" === (t = t.toLowerCase()) ? ":" : "#" === t.charAt(0) ? "x" === t.charAt(1) ? String.fromCharCode(parseInt(t.substring(2), 16)) : String.fromCharCode(+t.substring(1)) : "")) - } - const u = /(^|[^\[])\^/g; - function k(e, t) { - let n = "string" == typeof e ? e : e.source; - t = t || ""; - const s = { - replace: (e,t)=>{ - let r = "string" == typeof t ? t : t.source; - return r = r.replace(u, "$1"), - n = n.replace(e, r), - s - } - , - getRegex: ()=>new RegExp(n,t) - }; - return s - } - function g(e) { - try { - e = encodeURI(e).replace(/%25/g, "%") - } catch (e) { - return null - } - return e - } - const f = { - exec: ()=>null - }; - function d(e, t) { - const n = e.replace(/\|/g, ((e,t,n)=>{ - let s = !1 - , r = t; - for (; --r >= 0 && "\\" === n[r]; ) - s = !s; - return s ? "|" : " |" - } - )).split(/ \|/); - let s = 0; - if (n[0].trim() || n.shift(), - n.length > 0 && !n[n.length - 1].trim() && n.pop(), - t) - if (n.length > t) - n.splice(t); - else - for (; n.length < t; ) - n.push(""); - for (; s < n.length; s++) - n[s] = n[s].trim().replace(/\\\|/g, "|"); - return n - } - function x(e, t, n) { - const s = e.length; - if (0 === s) - return ""; - let r = 0; - for (; r < s; ) { - const i = e.charAt(s - r - 1); - if (i !== t || n) { - if (i === t || !n) - break; - r++ - } else - r++ - } - return e.slice(0, s - r) - } - function b(e, t, n, s) { - const r = t.href - , i = t.title ? c(t.title) : null - , l = e[1].replace(/\\([\[\]])/g, "$1"); - if ("!" !== e[0].charAt(0)) { - s.state.inLink = !0; - const e = { - type: "link", - raw: n, - href: r, - title: i, - text: l, - tokens: s.inlineTokens(l) - }; - return s.state.inLink = !1, - e - } - return { - type: "image", - raw: n, - href: r, - title: i, - text: c(l) - } - } - class w { - options; - rules; - lexer; - constructor(t) { - this.options = t || e.defaults - } - space(e) { - const t = this.rules.block.newline.exec(e); - if (t && t[0].length > 0) - return { - type: "space", - raw: t[0] - } - } - code(e) { - const t = this.rules.block.code.exec(e); - if (t) { - const e = t[0].replace(/^ {1,4}/gm, ""); - return { - type: "code", - raw: t[0], - codeBlockStyle: "indented", - text: this.options.pedantic ? e : x(e, "\n") - } - } - } - fences(e) { - const t = this.rules.block.fences.exec(e); - if (t) { - const e = t[0] - , n = function(e, t) { - const n = e.match(/^(\s+)(?:```)/); - if (null === n) - return t; - const s = n[1]; - return t.split("\n").map((e=>{ - const t = e.match(/^\s+/); - if (null === t) - return e; - const [n] = t; - return n.length >= s.length ? e.slice(s.length) : e - } - )).join("\n") - }(e, t[3] || ""); - return { - type: "code", - raw: e, - lang: t[2] ? t[2].trim().replace(this.rules.inline.anyPunctuation, "$1") : t[2], - text: n - } - } - } - heading(e) { - const t = this.rules.block.heading.exec(e); - if (t) { - let e = t[2].trim(); - if (/#$/.test(e)) { - const t = x(e, "#"); - this.options.pedantic ? e = t.trim() : t && !/ $/.test(t) || (e = t.trim()) - } - return { - type: "heading", - raw: t[0], - depth: t[1].length, - text: e, - tokens: this.lexer.inline(e) - } - } - } - hr(e) { - const t = this.rules.block.hr.exec(e); - if (t) - return { - type: "hr", - raw: t[0] - } - } - blockquote(e) { - const t = this.rules.block.blockquote.exec(e); - if (t) { - const e = x(t[0].replace(/^ *>[ \t]?/gm, ""), "\n") - , n = this.lexer.state.top; - this.lexer.state.top = !0; - const s = this.lexer.blockTokens(e); - return this.lexer.state.top = n, - { - type: "blockquote", - raw: t[0], - tokens: s, - text: e - } - } - } - list(e) { - let t = this.rules.block.list.exec(e); - if (t) { - let n = t[1].trim(); - const s = n.length > 1 - , r = { - type: "list", - raw: "", - ordered: s, - start: s ? +n.slice(0, -1) : "", - loose: !1, - items: [] - }; - n = s ? `\\d{1,9}\\${n.slice(-1)}` : `\\${n}`, - this.options.pedantic && (n = s ? n : "[*+-]"); - const i = new RegExp(`^( {0,3}${n})((?:[\t ][^\\n]*)?(?:\\n|$))`); - let l = "" - , o = "" - , a = !1; - for (; e; ) { - let n = !1; - if (!(t = i.exec(e))) - break; - if (this.rules.block.hr.test(e)) - break; - l = t[0], - e = e.substring(l.length); - let s = t[2].split("\n", 1)[0].replace(/^\t+/, (e=>" ".repeat(3 * e.length))) - , c = e.split("\n", 1)[0] - , h = 0; - this.options.pedantic ? (h = 2, - o = s.trimStart()) : (h = t[2].search(/[^ ]/), - h = h > 4 ? 1 : h, - o = s.slice(h), - h += t[1].length); - let p = !1; - if (!s && /^ *$/.test(c) && (l += c + "\n", - e = e.substring(c.length + 1), - n = !0), - !n) { - const t = new RegExp(`^ {0,${Math.min(3, h - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`) - , n = new RegExp(`^ {0,${Math.min(3, h - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`) - , r = new RegExp(`^ {0,${Math.min(3, h - 1)}}(?:\`\`\`|~~~)`) - , i = new RegExp(`^ {0,${Math.min(3, h - 1)}}#`); - for (; e; ) { - const a = e.split("\n", 1)[0]; - if (c = a, - this.options.pedantic && (c = c.replace(/^ {1,4}(?=( {4})*[^ ])/g, " ")), - r.test(c)) - break; - if (i.test(c)) - break; - if (t.test(c)) - break; - if (n.test(e)) - break; - if (c.search(/[^ ]/) >= h || !c.trim()) - o += "\n" + c.slice(h); - else { - if (p) - break; - if (s.search(/[^ ]/) >= 4) - break; - if (r.test(s)) - break; - if (i.test(s)) - break; - if (n.test(s)) - break; - o += "\n" + c - } - p || c.trim() || (p = !0), - l += a + "\n", - e = e.substring(a.length + 1), - s = c.slice(h) - } - } - r.loose || (a ? r.loose = !0 : /\n *\n *$/.test(l) && (a = !0)); - let u, k = null; - this.options.gfm && (k = /^\[[ xX]\] /.exec(o), - k && (u = "[ ] " !== k[0], - o = o.replace(/^\[[ xX]\] +/, ""))), - r.items.push({ - type: "list_item", - raw: l, - task: !!k, - checked: u, - loose: !1, - text: o, - tokens: [] - }), - r.raw += l - } - r.items[r.items.length - 1].raw = l.trimEnd(), - r.items[r.items.length - 1].text = o.trimEnd(), - r.raw = r.raw.trimEnd(); - for (let e = 0; e < r.items.length; e++) - if (this.lexer.state.top = !1, - r.items[e].tokens = this.lexer.blockTokens(r.items[e].text, []), - !r.loose) { - const t = r.items[e].tokens.filter((e=>"space" === e.type)) - , n = t.length > 0 && t.some((e=>/\n.*\n/.test(e.raw))); - r.loose = n - } - if (r.loose) - for (let e = 0; e < r.items.length; e++) - r.items[e].loose = !0; - return r - } - } - html(e) { - const t = this.rules.block.html.exec(e); - if (t) { - return { - type: "html", - block: !0, - raw: t[0], - pre: "pre" === t[1] || "script" === t[1] || "style" === t[1], - text: t[0] - } - } - } - def(e) { - const t = this.rules.block.def.exec(e); - if (t) { - const e = t[1].toLowerCase().replace(/\s+/g, " ") - , n = t[2] ? t[2].replace(/^<(.*)>$/, "$1").replace(this.rules.inline.anyPunctuation, "$1") : "" - , s = t[3] ? t[3].substring(1, t[3].length - 1).replace(this.rules.inline.anyPunctuation, "$1") : t[3]; - return { - type: "def", - tag: e, - raw: t[0], - href: n, - title: s - } - } - } - table(e) { - const t = this.rules.block.table.exec(e); - if (!t) - return; - if (!/[:|]/.test(t[2])) - return; - const n = d(t[1]) - , s = t[2].replace(/^\||\| *$/g, "").split("|") - , r = t[3] && t[3].trim() ? t[3].replace(/\n[ \t]*$/, "").split("\n") : [] - , i = { - type: "table", - raw: t[0], - header: [], - align: [], - rows: [] - }; - if (n.length === s.length) { - for (const e of s) - /^ *-+: *$/.test(e) ? i.align.push("right") : /^ *:-+: *$/.test(e) ? i.align.push("center") : /^ *:-+ *$/.test(e) ? i.align.push("left") : i.align.push(null); - for (const e of n) - i.header.push({ - text: e, - tokens: this.lexer.inline(e) - }); - for (const e of r) - i.rows.push(d(e, i.header.length).map((e=>({ - text: e, - tokens: this.lexer.inline(e) - })))); - return i - } - } - lheading(e) { - const t = this.rules.block.lheading.exec(e); - if (t) - return { - type: "heading", - raw: t[0], - depth: "=" === t[2].charAt(0) ? 1 : 2, - text: t[1], - tokens: this.lexer.inline(t[1]) - } - } - paragraph(e) { - const t = this.rules.block.paragraph.exec(e); - if (t) { - const e = "\n" === t[1].charAt(t[1].length - 1) ? t[1].slice(0, -1) : t[1]; - return { - type: "paragraph", - raw: t[0], - text: e, - tokens: this.lexer.inline(e) - } - } - } - text(e) { - const t = this.rules.block.text.exec(e); - if (t) - return { - type: "text", - raw: t[0], - text: t[0], - tokens: this.lexer.inline(t[0]) - } - } - escape(e) { - const t = this.rules.inline.escape.exec(e); - if (t) - return { - type: "escape", - raw: t[0], - text: c(t[1]) - } - } - tag(e) { - const t = this.rules.inline.tag.exec(e); - if (t) - return !this.lexer.state.inLink && /^/i.test(t[0]) && (this.lexer.state.inLink = !1), - !this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(t[0]) ? this.lexer.state.inRawBlock = !0 : this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(t[0]) && (this.lexer.state.inRawBlock = !1), - { - type: "html", - raw: t[0], - inLink: this.lexer.state.inLink, - inRawBlock: this.lexer.state.inRawBlock, - block: !1, - text: t[0] - } - } - link(e) { - const t = this.rules.inline.link.exec(e); - if (t) { - const e = t[2].trim(); - if (!this.options.pedantic && /^$/.test(e)) - return; - const t = x(e.slice(0, -1), "\\"); - if ((e.length - t.length) % 2 == 0) - return - } else { - const e = function(e, t) { - if (-1 === e.indexOf(t[1])) - return -1; - let n = 0; - for (let s = 0; s < e.length; s++) - if ("\\" === e[s]) - s++; - else if (e[s] === t[0]) - n++; - else if (e[s] === t[1] && (n--, - n < 0)) - return s; - return -1 - }(t[2], "()"); - if (e > -1) { - const n = (0 === t[0].indexOf("!") ? 5 : 4) + t[1].length + e; - t[2] = t[2].substring(0, e), - t[0] = t[0].substring(0, n).trim(), - t[3] = "" - } - } - let n = t[2] - , s = ""; - if (this.options.pedantic) { - const e = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(n); - e && (n = e[1], - s = e[3]) - } else - s = t[3] ? t[3].slice(1, -1) : ""; - return n = n.trim(), - /^$/.test(e) ? n.slice(1) : n.slice(1, -1)), - b(t, { - href: n ? n.replace(this.rules.inline.anyPunctuation, "$1") : n, - title: s ? s.replace(this.rules.inline.anyPunctuation, "$1") : s - }, t[0], this.lexer) - } - } - reflink(e, t) { - let n; - if ((n = this.rules.inline.reflink.exec(e)) || (n = this.rules.inline.nolink.exec(e))) { - const e = t[(n[2] || n[1]).replace(/\s+/g, " ").toLowerCase()]; - if (!e) { - const e = n[0].charAt(0); - return { - type: "text", - raw: e, - text: e - } - } - return b(n, e, n[0], this.lexer) - } - } - emStrong(e, t, n="") { - let s = this.rules.inline.emStrongLDelim.exec(e); - if (!s) - return; - if (s[3] && n.match(/[\p{L}\p{N}]/u)) - return; - if (!(s[1] || s[2] || "") || !n || this.rules.inline.punctuation.exec(n)) { - const n = [...s[0]].length - 1; - let r, i, l = n, o = 0; - const a = "*" === s[0][0] ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd; - for (a.lastIndex = 0, - t = t.slice(-1 * e.length + n); null != (s = a.exec(t)); ) { - if (r = s[1] || s[2] || s[3] || s[4] || s[5] || s[6], - !r) - continue; - if (i = [...r].length, - s[3] || s[4]) { - l += i; - continue - } - if ((s[5] || s[6]) && n % 3 && !((n + i) % 3)) { - o += i; - continue - } - if (l -= i, - l > 0) - continue; - i = Math.min(i, i + l + o); - const t = [...s[0]][0].length - , a = e.slice(0, n + s.index + t + i); - if (Math.min(n, i) % 2) { - const e = a.slice(1, -1); - return { - type: "em", - raw: a, - text: e, - tokens: this.lexer.inlineTokens(e) - } - } - const c = a.slice(2, -2); - return { - type: "strong", - raw: a, - text: c, - tokens: this.lexer.inlineTokens(c) - } - } - } - } - codespan(e) { - const t = this.rules.inline.code.exec(e); - if (t) { - let e = t[2].replace(/\n/g, " "); - const n = /[^ ]/.test(e) - , s = /^ /.test(e) && / $/.test(e); - return n && s && (e = e.substring(1, e.length - 1)), - e = c(e, !0), - { - type: "codespan", - raw: t[0], - text: e - } - } - } - br(e) { - const t = this.rules.inline.br.exec(e); - if (t) - return { - type: "br", - raw: t[0] - } - } - del(e) { - const t = this.rules.inline.del.exec(e); - if (t) - return { - type: "del", - raw: t[0], - text: t[2], - tokens: this.lexer.inlineTokens(t[2]) - } - } - autolink(e) { - const t = this.rules.inline.autolink.exec(e); - if (t) { - let e, n; - return "@" === t[2] ? (e = c(t[1]), - n = "mailto:" + e) : (e = c(t[1]), - n = e), - { - type: "link", - raw: t[0], - text: e, - href: n, - tokens: [{ - type: "text", - raw: e, - text: e - }] - } - } - } - url(e) { - let t; - if (t = this.rules.inline.url.exec(e)) { - let e, n; - if ("@" === t[2]) - e = c(t[0]), - n = "mailto:" + e; - else { - let s; - do { - s = t[0], - t[0] = this.rules.inline._backpedal.exec(t[0])?.[0] ?? "" - } while (s !== t[0]); - e = c(t[0]), - n = "www." === t[1] ? "http://" + t[0] : t[0] - } - return { - type: "link", - raw: t[0], - text: e, - href: n, - tokens: [{ - type: "text", - raw: e, - text: e - }] - } - } - } - inlineText(e) { - const t = this.rules.inline.text.exec(e); - if (t) { - let e; - return e = this.lexer.state.inRawBlock ? t[0] : c(t[0]), - { - type: "text", - raw: t[0], - text: e - } - } - } - } - const m = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/ - , y = /(?:[*+-]|\d{1,9}[.)])/ - , $ = k(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g, y).replace(/blockCode/g, / {4}/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}/).replace(/html/g, / {0,3}<[^\n>]+>\n/).getRegex() - , z = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/ - , T = /(?!\s*\])(?:\\.|[^\[\]\\])+/ - , R = k(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/).replace("label", T).replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex() - , _ = k(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g, y).getRegex() - , A = "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul" - , S = /|$))/ - , I = k("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))", "i").replace("comment", S).replace("tag", A).replace("attribute", / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex() - , E = k(z).replace("hr", m).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("|table", "").replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", ")|<(?:script|pre|style|textarea|!--)").replace("tag", A).getRegex() - , q = { - blockquote: k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph", E).getRegex(), - code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, - def: R, - fences: /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/, - heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, - hr: m, - html: I, - lheading: $, - list: _, - newline: /^(?: *(?:\n|$))+/, - paragraph: E, - table: f, - text: /^[^\n]+/ - } - , Z = k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr", m).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("blockquote", " {0,3}>").replace("code", " {4}[^\\n]").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", ")|<(?:script|pre|style|textarea|!--)").replace("tag", A).getRegex() - , L = { - ...q, - table: Z, - paragraph: k(z).replace("hr", m).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("table", Z).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", ")|<(?:script|pre|style|textarea|!--)").replace("tag", A).getRegex() - } - , P = { - ...q, - html: k("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment", S).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(), - def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, - heading: /^(#{1,6})(.*)(?:\n+|$)/, - fences: f, - lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/, - paragraph: k(z).replace("hr", m).replace("heading", " *#{1,6} *[^\n]").replace("lheading", $).replace("|table", "").replace("blockquote", " {0,3}>").replace("|fences", "").replace("|list", "").replace("|html", "").replace("|tag", "").getRegex() - } - , Q = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/ - , v = /^( {2,}|\\)\n(?!\s*$)/ - , B = "\\p{P}\\p{S}" - , C = k(/^((?![*_])[\spunctuation])/, "u").replace(/punctuation/g, B).getRegex() - , M = k(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/, "u").replace(/punct/g, B).getRegex() - , O = k("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])", "gu").replace(/punct/g, B).getRegex() - , D = k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])", "gu").replace(/punct/g, B).getRegex() - , j = k(/\\([punct])/, "gu").replace(/punct/g, B).getRegex() - , H = k(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme", /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email", /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex() - , U = k(S).replace("(?:--\x3e|$)", "--\x3e").getRegex() - , X = k("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment", U).replace("attribute", /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex() - , F = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/ - , N = k(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label", F).replace("href", /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title", /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex() - , G = k(/^!?\[(label)\]\[(ref)\]/).replace("label", F).replace("ref", T).getRegex() - , J = k(/^!?\[(ref)\](?:\[\])?/).replace("ref", T).getRegex() - , K = { - _backpedal: f, - anyPunctuation: j, - autolink: H, - blockSkip: /\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g, - br: v, - code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, - del: f, - emStrongLDelim: M, - emStrongRDelimAst: O, - emStrongRDelimUnd: D, - escape: Q, - link: N, - nolink: J, - punctuation: C, - reflink: G, - reflinkSearch: k("reflink|nolink(?!\\()", "g").replace("reflink", G).replace("nolink", J).getRegex(), - tag: X, - text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\t + " ".repeat(n.length))); e; ) - if (!(this.options.extensions && this.options.extensions.block && this.options.extensions.block.some((s=>!!(n = s.call({ - lexer: this - }, e, t)) && (e = e.substring(n.raw.length), - t.push(n), - !0))))) - if (n = this.tokenizer.space(e)) - e = e.substring(n.raw.length), - 1 === n.raw.length && t.length > 0 ? t[t.length - 1].raw += "\n" : t.push(n); - else if (n = this.tokenizer.code(e)) - e = e.substring(n.raw.length), - s = t[t.length - 1], - !s || "paragraph" !== s.type && "text" !== s.type ? t.push(n) : (s.raw += "\n" + n.raw, - s.text += "\n" + n.text, - this.inlineQueue[this.inlineQueue.length - 1].src = s.text); - else if (n = this.tokenizer.fences(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.heading(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.hr(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.blockquote(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.list(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.html(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.def(e)) - e = e.substring(n.raw.length), - s = t[t.length - 1], - !s || "paragraph" !== s.type && "text" !== s.type ? this.tokens.links[n.tag] || (this.tokens.links[n.tag] = { - href: n.href, - title: n.title - }) : (s.raw += "\n" + n.raw, - s.text += "\n" + n.raw, - this.inlineQueue[this.inlineQueue.length - 1].src = s.text); - else if (n = this.tokenizer.table(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.lheading(e)) - e = e.substring(n.raw.length), - t.push(n); - else { - if (r = e, - this.options.extensions && this.options.extensions.startBlock) { - let t = 1 / 0; - const n = e.slice(1); - let s; - this.options.extensions.startBlock.forEach((e=>{ - s = e.call({ - lexer: this - }, n), - "number" == typeof s && s >= 0 && (t = Math.min(t, s)) - } - )), - t < 1 / 0 && t >= 0 && (r = e.substring(0, t + 1)) - } - if (this.state.top && (n = this.tokenizer.paragraph(r))) - s = t[t.length - 1], - i && "paragraph" === s.type ? (s.raw += "\n" + n.raw, - s.text += "\n" + n.text, - this.inlineQueue.pop(), - this.inlineQueue[this.inlineQueue.length - 1].src = s.text) : t.push(n), - i = r.length !== e.length, - e = e.substring(n.raw.length); - else if (n = this.tokenizer.text(e)) - e = e.substring(n.raw.length), - s = t[t.length - 1], - s && "text" === s.type ? (s.raw += "\n" + n.raw, - s.text += "\n" + n.text, - this.inlineQueue.pop(), - this.inlineQueue[this.inlineQueue.length - 1].src = s.text) : t.push(n); - else if (e) { - const t = "Infinite loop on byte: " + e.charCodeAt(0); - if (this.options.silent) { - console.error(t); - break - } - throw new Error(t) - } - } - return this.state.top = !0, - t - } - inline(e, t=[]) { - return this.inlineQueue.push({ - src: e, - tokens: t - }), - t - } - inlineTokens(e, t=[]) { - let n, s, r, i, l, o, a = e; - if (this.tokens.links) { - const e = Object.keys(this.tokens.links); - if (e.length > 0) - for (; null != (i = this.tokenizer.rules.inline.reflinkSearch.exec(a)); ) - e.includes(i[0].slice(i[0].lastIndexOf("[") + 1, -1)) && (a = a.slice(0, i.index) + "[" + "a".repeat(i[0].length - 2) + "]" + a.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex)) - } - for (; null != (i = this.tokenizer.rules.inline.blockSkip.exec(a)); ) - a = a.slice(0, i.index) + "[" + "a".repeat(i[0].length - 2) + "]" + a.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); - for (; null != (i = this.tokenizer.rules.inline.anyPunctuation.exec(a)); ) - a = a.slice(0, i.index) + "++" + a.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex); - for (; e; ) - if (l || (o = ""), - l = !1, - !(this.options.extensions && this.options.extensions.inline && this.options.extensions.inline.some((s=>!!(n = s.call({ - lexer: this - }, e, t)) && (e = e.substring(n.raw.length), - t.push(n), - !0))))) - if (n = this.tokenizer.escape(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.tag(e)) - e = e.substring(n.raw.length), - s = t[t.length - 1], - s && "text" === n.type && "text" === s.type ? (s.raw += n.raw, - s.text += n.text) : t.push(n); - else if (n = this.tokenizer.link(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.reflink(e, this.tokens.links)) - e = e.substring(n.raw.length), - s = t[t.length - 1], - s && "text" === n.type && "text" === s.type ? (s.raw += n.raw, - s.text += n.text) : t.push(n); - else if (n = this.tokenizer.emStrong(e, a, o)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.codespan(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.br(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.del(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (n = this.tokenizer.autolink(e)) - e = e.substring(n.raw.length), - t.push(n); - else if (this.state.inLink || !(n = this.tokenizer.url(e))) { - if (r = e, - this.options.extensions && this.options.extensions.startInline) { - let t = 1 / 0; - const n = e.slice(1); - let s; - this.options.extensions.startInline.forEach((e=>{ - s = e.call({ - lexer: this - }, n), - "number" == typeof s && s >= 0 && (t = Math.min(t, s)) - } - )), - t < 1 / 0 && t >= 0 && (r = e.substring(0, t + 1)) - } - if (n = this.tokenizer.inlineText(r)) - e = e.substring(n.raw.length), - "_" !== n.raw.slice(-1) && (o = n.raw.slice(-1)), - l = !0, - s = t[t.length - 1], - s && "text" === s.type ? (s.raw += n.raw, - s.text += n.text) : t.push(n); - else if (e) { - const t = "Infinite loop on byte: " + e.charCodeAt(0); - if (this.options.silent) { - console.error(t); - break - } - throw new Error(t) - } - } else - e = e.substring(n.raw.length), - t.push(n); - return t - } - } - class se { - options; - constructor(t) { - this.options = t || e.defaults - } - code(e, t, n) { - const s = (t || "").match(/^\S*/)?.[0]; - return e = e.replace(/\n$/, "") + "\n", - s ? '
' + (n ? e : c(e, !0)) + "
\n" : "
" + (n ? e : c(e, !0)) + "
\n" - } - blockquote(e) { - return `
\n${e}
\n` - } - html(e, t) { - return e - } - heading(e, t, n) { - return `${e}\n` - } - hr() { - return "
\n" - } - list(e, t, n) { - const s = t ? "ol" : "ul"; - return "<" + s + (t && 1 !== n ? ' start="' + n + '"' : "") + ">\n" + e + "\n" - } - listitem(e, t, n) { - return `
  • ${e}
  • \n` - } - checkbox(e) { - return "' - } - paragraph(e) { - return `

    ${e}

    \n` - } - table(e, t) { - return t && (t = `${t}`), - "\n\n" + e + "\n" + t + "
    \n" - } - tablerow(e) { - return `\n${e}\n` - } - tablecell(e, t) { - const n = t.header ? "th" : "td"; - return (t.align ? `<${n} align="${t.align}">` : `<${n}>`) + e + `\n` - } - strong(e) { - return `${e}` - } - em(e) { - return `${e}` - } - codespan(e) { - return `${e}` - } - br() { - return "
    " - } - del(e) { - return `${e}` - } - link(e, t, n) { - const s = g(e); - if (null === s) - return n; - let r = '
    ", - r - } - image(e, t, n) { - const s = g(e); - if (null === s) - return n; - let r = `${n} 0 && "paragraph" === n.tokens[0].type ? (n.tokens[0].text = e + " " + n.tokens[0].text, - n.tokens[0].tokens && n.tokens[0].tokens.length > 0 && "text" === n.tokens[0].tokens[0].type && (n.tokens[0].tokens[0].text = e + " " + n.tokens[0].tokens[0].text)) : n.tokens.unshift({ - type: "text", - text: e + " " - }) : o += e + " " - } - o += this.parse(n.tokens, i), - l += this.renderer.listitem(o, r, !!s) - } - n += this.renderer.list(l, t, s); - continue - } - case "html": - { - const e = r; - n += this.renderer.html(e.text, e.block); - continue - } - case "paragraph": - { - const e = r; - n += this.renderer.paragraph(this.parseInline(e.tokens)); - continue - } - case "text": - { - let i = r - , l = i.tokens ? this.parseInline(i.tokens) : i.text; - for (; s + 1 < e.length && "text" === e[s + 1].type; ) - i = e[++s], - l += "\n" + (i.tokens ? this.parseInline(i.tokens) : i.text); - n += t ? this.renderer.paragraph(l) : l; - continue - } - default: - { - const e = 'Token with "' + r.type + '" type was not found.'; - if (this.options.silent) - return console.error(e), - ""; - throw new Error(e) - } - } - } - return n - } - parseInline(e, t) { - t = t || this.renderer; - let n = ""; - for (let s = 0; s < e.length; s++) { - const r = e[s]; - if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[r.type]) { - const e = this.options.extensions.renderers[r.type].call({ - parser: this - }, r); - if (!1 !== e || !["escape", "html", "link", "image", "strong", "em", "codespan", "br", "del", "text"].includes(r.type)) { - n += e || ""; - continue - } - } - switch (r.type) { - case "escape": - { - const e = r; - n += t.text(e.text); - break - } - case "html": - { - const e = r; - n += t.html(e.text); - break - } - case "link": - { - const e = r; - n += t.link(e.href, e.title, this.parseInline(e.tokens, t)); - break - } - case "image": - { - const e = r; - n += t.image(e.href, e.title, e.text); - break - } - case "strong": - { - const e = r; - n += t.strong(this.parseInline(e.tokens, t)); - break - } - case "em": - { - const e = r; - n += t.em(this.parseInline(e.tokens, t)); - break - } - case "codespan": - { - const e = r; - n += t.codespan(e.text); - break - } - case "br": - n += t.br(); - break; - case "del": - { - const e = r; - n += t.del(this.parseInline(e.tokens, t)); - break - } - case "text": - { - const e = r; - n += t.text(e.text); - break - } - default: - { - const e = 'Token with "' + r.type + '" type was not found.'; - if (this.options.silent) - return console.error(e), - ""; - throw new Error(e) - } - } - } - return n - } - } - class le { - options; - constructor(t) { - this.options = t || e.defaults - } - static passThroughHooks = new Set(["preprocess", "postprocess", "processAllTokens"]); - preprocess(e) { - return e - } - postprocess(e) { - return e - } - processAllTokens(e) { - return e - } - } - class oe { - defaults = { - async: !1, - breaks: !1, - extensions: null, - gfm: !0, - hooks: null, - pedantic: !1, - renderer: null, - silent: !1, - tokenizer: null, - walkTokens: null - }; - options = this.setOptions; - parse = this.#e(ne.lex, ie.parse); - parseInline = this.#e(ne.lexInline, ie.parseInline); - Parser = ie; - Renderer = se; - TextRenderer = re; - Lexer = ne; - Tokenizer = w; - Hooks = le; - constructor(...e) { - this.use(...e) - } - walkTokens(e, t) { - let n = []; - for (const s of e) - switch (n = n.concat(t.call(this, s)), - s.type) { - case "table": - { - const e = s; - for (const s of e.header) - n = n.concat(this.walkTokens(s.tokens, t)); - for (const s of e.rows) - for (const e of s) - n = n.concat(this.walkTokens(e.tokens, t)); - break - } - case "list": - { - const e = s; - n = n.concat(this.walkTokens(e.items, t)); - break - } - default: - { - const e = s; - this.defaults.extensions?.childTokens?.[e.type] ? this.defaults.extensions.childTokens[e.type].forEach((s=>{ - const r = e[s].flat(1 / 0); - n = n.concat(this.walkTokens(r, t)) - } - )) : e.tokens && (n = n.concat(this.walkTokens(e.tokens, t))) - } - } - return n - } - use(...e) { - const t = this.defaults.extensions || { - renderers: {}, - childTokens: {} - }; - return e.forEach((e=>{ - const n = { - ...e - }; - if (n.async = this.defaults.async || n.async || !1, - e.extensions && (e.extensions.forEach((e=>{ - if (!e.name) - throw new Error("extension name required"); - if ("renderer"in e) { - const n = t.renderers[e.name]; - t.renderers[e.name] = n ? function(...t) { - let s = e.renderer.apply(this, t); - return !1 === s && (s = n.apply(this, t)), - s - } - : e.renderer - } - if ("tokenizer"in e) { - if (!e.level || "block" !== e.level && "inline" !== e.level) - throw new Error("extension level must be 'block' or 'inline'"); - const n = t[e.level]; - n ? n.unshift(e.tokenizer) : t[e.level] = [e.tokenizer], - e.start && ("block" === e.level ? t.startBlock ? t.startBlock.push(e.start) : t.startBlock = [e.start] : "inline" === e.level && (t.startInline ? t.startInline.push(e.start) : t.startInline = [e.start])) - } - "childTokens"in e && e.childTokens && (t.childTokens[e.name] = e.childTokens) - } - )), - n.extensions = t), - e.renderer) { - const t = this.defaults.renderer || new se(this.defaults); - for (const n in e.renderer) { - if (!(n in t)) - throw new Error(`renderer '${n}' does not exist`); - if ("options" === n) - continue; - const s = n - , r = e.renderer[s] - , i = t[s]; - t[s] = (...e)=>{ - let n = r.apply(t, e); - return !1 === n && (n = i.apply(t, e)), - n || "" - } - } - n.renderer = t - } - if (e.tokenizer) { - const t = this.defaults.tokenizer || new w(this.defaults); - for (const n in e.tokenizer) { - if (!(n in t)) - throw new Error(`tokenizer '${n}' does not exist`); - if (["options", "rules", "lexer"].includes(n)) - continue; - const s = n - , r = e.tokenizer[s] - , i = t[s]; - t[s] = (...e)=>{ - let n = r.apply(t, e); - return !1 === n && (n = i.apply(t, e)), - n - } - } - n.tokenizer = t - } - if (e.hooks) { - const t = this.defaults.hooks || new le; - for (const n in e.hooks) { - if (!(n in t)) - throw new Error(`hook '${n}' does not exist`); - if ("options" === n) - continue; - const s = n - , r = e.hooks[s] - , i = t[s]; - le.passThroughHooks.has(n) ? t[s] = e=>{ - if (this.defaults.async) - return Promise.resolve(r.call(t, e)).then((e=>i.call(t, e))); - const n = r.call(t, e); - return i.call(t, n) - } - : t[s] = (...e)=>{ - let n = r.apply(t, e); - return !1 === n && (n = i.apply(t, e)), - n - } - } - n.hooks = t - } - if (e.walkTokens) { - const t = this.defaults.walkTokens - , s = e.walkTokens; - n.walkTokens = function(e) { - let n = []; - return n.push(s.call(this, e)), - t && (n = n.concat(t.call(this, e))), - n - } - } - this.defaults = { - ...this.defaults, - ...n - } - } - )), - this - } - setOptions(e) { - return this.defaults = { - ...this.defaults, - ...e - }, - this - } - lexer(e, t) { - return ne.lex(e, t ?? this.defaults) - } - parser(e, t) { - return ie.parse(e, t ?? this.defaults) - } - #e(e, t) { - return (n,s)=>{ - const r = { - ...s - } - , i = { - ...this.defaults, - ...r - }; - !0 === this.defaults.async && !1 === r.async && (i.silent || console.warn("marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored."), - i.async = !0); - const l = this.#t(!!i.silent, !!i.async); - if (null == n) - return l(new Error("marked(): input parameter is undefined or null")); - if ("string" != typeof n) - return l(new Error("marked(): input parameter is of type " + Object.prototype.toString.call(n) + ", string expected")); - if (i.hooks && (i.hooks.options = i), - i.async) - return Promise.resolve(i.hooks ? i.hooks.preprocess(n) : n).then((t=>e(t, i))).then((e=>i.hooks ? i.hooks.processAllTokens(e) : e)).then((e=>i.walkTokens ? Promise.all(this.walkTokens(e, i.walkTokens)).then((()=>e)) : e)).then((e=>t(e, i))).then((e=>i.hooks ? i.hooks.postprocess(e) : e)).catch(l); - try { - i.hooks && (n = i.hooks.preprocess(n)); - let s = e(n, i); - i.hooks && (s = i.hooks.processAllTokens(s)), - i.walkTokens && this.walkTokens(s, i.walkTokens); - let r = t(s, i); - return i.hooks && (r = i.hooks.postprocess(r)), - r - } catch (e) { - return l(e) - } - } - } - #t(e, t) { - return n=>{ - if (n.message += "\nPlease report this to https://github.com/markedjs/marked.", - e) { - const e = "

    An error occurred:

    " + c(n.message + "", !0) + "
    "; - return t ? Promise.resolve(e) : e - } - if (t) - return Promise.reject(n); - throw n - } - } - } - const ae = new oe; - function ce(e, t) { - return ae.parse(e, t) - } - ce.options = ce.setOptions = function(e) { - return ae.setOptions(e), - ce.defaults = ae.defaults, - n(ce.defaults), - ce - } - , - ce.getDefaults = t, - ce.defaults = e.defaults, - ce.use = function(...e) { - return ae.use(...e), - ce.defaults = ae.defaults, - n(ce.defaults), - ce - } - , - ce.walkTokens = function(e, t) { - return ae.walkTokens(e, t) - } - , - ce.parseInline = ae.parseInline, - ce.Parser = ie, - ce.parser = ie.parse, - ce.Renderer = se, - ce.TextRenderer = re, - ce.Lexer = ne, - ce.lexer = ne.lex, - ce.Tokenizer = w, - ce.Hooks = le, - ce.parse = ce; - const he = ce.options - , pe = ce.setOptions - , ue = ce.use - , ke = ce.walkTokens - , ge = ce.parseInline - , fe = ce - , de = ie.parse - , xe = ne.lex; - e.Hooks = le, - e.Lexer = ne, - e.Marked = oe, - e.Parser = ie, - e.Renderer = se, - e.TextRenderer = re, - e.Tokenizer = w, - e.getDefaults = t, - e.lexer = xe, - e.marked = ce, - e.options = he, - e.parse = fe, - e.parseInline = ge, - e.parser = de, - e.setOptions = pe, - e.use = ue, - e.walkTokens = ke - } -)); diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/README-en.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/README-en.md deleted file mode 100644 index 051df6e9c..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/README-en.md +++ /dev/null @@ -1,131 +0,0 @@ -# Spring Cloud Alibaba AI RAG Example - -This sample describes how to implement a RAG (Retrieval Augmented Generation) application using SCA AI and Spring AI RedisVector Store. - -> RAG is a generative model based on retrieval, which combines retrieval and generation to produce more accurate and diverse texts. -> SCA AI: Spring Cloud Alibaba AI, adapting TongYi LLM big model through Spring AI API. -> Spring AI: The Spring AI project aims to simplify the development of applications that include artificial intelligence features and avoid unnecessary complexity. -> Spring AI RedisVector Store: Redis extends the core functionality of Redis OSS to allow Redis to be used as a vector database. Spring AI provides the RedisVector Store adapter. -> Project Code Address: [spring-cloud-ai-rag-example](https://github.com/alibaba/spring-cloud-alibaba/tree/2023.x/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example) - -## 1. Environmental preparation - -Use Docker Compose to deploy a Redis service to store vector data. - -```yaml -version: '3.8' - -services: - redis: - image: redis/redis-stack-server - container_name: redis - hostname: redis - ports: - - 6379:6379 -``` - -Start with `docker compose up -d`, and then you can `docker ps | grep redis` check to see if the container is running properly. - -## 2. Project dependency - -> This project introduces `spring-cloud-alibaba-ai-starter` and `spring-ai-redis-spring-boot-starter` realizes RAG application. - -You need to introduce the following dependencies in the POM. XML: - -```xml - - com.alibaba.cloud - spring-cloud-starter-alibaba-ai - - - - org.springframework.ai - spring-ai-redis-store-spring-boot-starter - ${spring.ai.version} - -``` - -## 3. Configuration - -Configure the following information in application.yml: > Note: It is recommended that you configure apiKey via an environment variable for apiKey security. - -> Note: It is recommended to configure the apiKey via environment variables for apiKey security. -> Reference: https://github.com/alibaba/spring-cloud-alibaba/tree/2023.x/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example#% E6%8E%A5%E5%85%A5-spring-cloud-starter-alibaba-ai: - -```yaml -spring: - ai: - vectorstore: - redis: - # Configure Redis connection URI, default value is redis://127.0.0.1:6379 - # uri: redis://127.0.0.1:6379 - index: peer - Prefix: peer -``` - -## 4. Write the code - -The `loader` classes are as follows: - -```java -@Override -public void run(ApplicationArguments args) throws Exception { - - Map indexInfo = vectorStore.getJedis().ftInfo(properties.getIndex()); - int numDocs = Integer.parseInt((String) indexInfo.getOrDefault("num_docs", "0")); - if (numDocs > 20000) { - logger.info("Embeddings already loaded. Skipping"); - return; - } - - Resource file = data; - if (Objects.requireNonNull(data.getFilename()).endsWith(".gz")) { - GZIPInputStream inputStream = new GZIPInputStream(data.getInputStream()); - file = new InputStreamResource(inputStream, "beers.json.gz"); - } - - logger.info("Creating Embeddings..."); - JsonReader loader = new JsonReader(file, KEYS); - vectorStore.add(loader.get()); - logger.info("Embeddings created."); -} -``` - -The `Service` classes are as follows: - -```java -public Generation retrieve(String message) { - - SearchRequest request = SearchRequest.query(message).withTopK(topK); - List docs = store.similaritySearch(request); - - Message systemMessage = getSystemMessage(docs); - UserMessage userMessage = new UserMessage(message); - - Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); - ChatResponse response = client.call(prompt); - - return response.getResult(); -} - -private Message getSystemMessage(List similarDocuments) { - - String documents = similarDocuments.stream() - .map(Document::getContent) - .collect(Collectors.joining("\n")); - SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemBeerPrompt); - - return systemPromptTemplate.createMessage(Map.of("documents", documents)); -} -``` - -## 5. Run and verify - -You can start the SprigBoot main class and then use a browser to access: - -```shell -# request params is prompt,the default value:What ber pairs well with smoked meats?" -http://localhost:8081/rag/chat -``` - -To experience the RAG application. diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/README.md b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/README.md deleted file mode 100644 index 444b96b61..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# Spring Cloud Alibaba AI RAG Example - -本示例介绍如何使用 SCA AI 和 Spring AI RedisVectorStore 实现 RAG(Retrieval Augmented Generation)应用。 - -> RAG 是一个基于检索的生成模型,它将检索和生成结合在一起,以生成更加准确和多样化的文本。 -> SCA AI: Spring Cloud Alibaba AI, 通过 Spring AI API 适配 TongYi LLM 大模型。 -> Spring AI: Spring AI项目旨在简化包含人工智能功能的应用程序的开发,避免不必要的复杂性。 -> Spring AI RedisVectorStore: Redis 扩展了 Redis OSS 的核心功能,允许将 Redis 用作矢量数据库,Spring AI 提供了 RedisVectorStore 适配器。 -> 项目代码地址:[spring-cloud-ai-rag-example](https://github.com/alibaba/spring-cloud-alibaba/tree/2023.x/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example) - -## 1. 环境准备 - -使用 Docker Compose 部署一个 Redis 服务,用于存储向量数据。 - -```yaml -version: '3.8' - -services: - redis: - image: redis/redis-stack-server - container_name: redis - hostname: redis - ports: - - 6379:6379 -``` - -使用 `docker compose up -d` 启动,然后您可以通过 `docker ps | grep redis` 查看容器是否正常运行。 - -## 2. 项目依赖 - -> 本项目通过引入 `spring-cloud-alibaba-ai-starter` 和 `spring-ai-redis-spring-boot-starter` 实现 RAG 应用。 - -您需要在 pom.xml 中引入如下依赖: - -```xml - - com.alibaba.cloud - spring-cloud-starter-alibaba-ai - - - - org.springframework.ai - spring-ai-redis-store-spring-boot-starter - ${spring.ai.version} - -``` - -## 3. 配置 - -在 application.yml 中配置如下信息: - -> 注意:为了保证 apiKey 安全,建议通过环境变量的方式配置 apiKey。 -> 参考:https://github.com/alibaba/spring-cloud-alibaba/tree/2023.x/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example#%E6%8E%A5%E5%85%A5-spring-cloud-starter-alibaba-ai: - -```yaml -spring: - ai: - vectorstore: - redis: - # Configure the Redis connection URI, default value is redis://127.0.0.1:6379 - # uri: redis://127.0.0.1:6379 - index: peer - prefix: peer -``` - -## 4. 编写代码 - -`loader` 类如下所示: - -```java -@Override -public void run(ApplicationArguments args) throws Exception { - - Map indexInfo = vectorStore.getJedis().ftInfo(properties.getIndex()); - int numDocs = Integer.parseInt((String) indexInfo.getOrDefault("num_docs", "0")); - if (numDocs > 20000) { - logger.info("Embeddings already loaded. Skipping"); - return; - } - - Resource file = data; - if (Objects.requireNonNull(data.getFilename()).endsWith(".gz")) { - GZIPInputStream inputStream = new GZIPInputStream(data.getInputStream()); - file = new InputStreamResource(inputStream, "beers.json.gz"); - } - - logger.info("Creating Embeddings..."); - JsonReader loader = new JsonReader(file, KEYS); - vectorStore.add(loader.get()); - logger.info("Embeddings created."); -} -``` - -`Service` 类如下所示: - -```java -public Generation retrieve(String message) { - - SearchRequest request = SearchRequest.query(message).withTopK(topK); - List docs = store.similaritySearch(request); - - Message systemMessage = getSystemMessage(docs); - UserMessage userMessage = new UserMessage(message); - - Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); - ChatResponse response = client.call(prompt); - - return response.getResult(); -} - -private Message getSystemMessage(List similarDocuments) { - - String documents = similarDocuments.stream() - .map(Document::getContent) - .collect(Collectors.joining("\n")); - SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemBeerPrompt); - - return systemPromptTemplate.createMessage(Map.of("documents", documents)); -} -``` - -## 5. 运行并验证 - -您可以通过启动 SpringBoot 主类,之后使用浏览器访问: - -```shell -# 参数为 prompt,默认值为:What ber pairs well with smoked meats?" -http://localhost:8081/rag/chat -``` - -来体验 RAG 应用。 diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/docker/docker-compose.yml b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/docker/docker-compose.yml deleted file mode 100644 index 3dbd05225..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/docker/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3.8' - -services: - redis: - image: redis/redis-stack-server - container_name: redis - hostname: redis - ports: - - 6379:6379 diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/pom.xml b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/pom.xml deleted file mode 100644 index 9a4ebfbfd..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/pom.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - spring-cloud-alibaba-examples - com.alibaba.cloud - ${revision} - ../../pom.xml - - 4.0.0 - - spring-cloud-ai-rag-example - Spring Cloud Starter Alibaba AI RAG Example - Example build RAG Application By Spring Cloud Alibaba AI - jar - - - 1.0.0-M1 - 5.1.0 - - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-ai - - - - org.springframework.ai - spring-ai-redis-store-spring-boot-starter - ${spring.ai.version} - - - logback-classic - ch.qos.logback - - - - - - redis.clients - jedis - ${redis.jedis.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 - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - false - - - - - diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/RAGApplication.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/RAGApplication.java deleted file mode 100644 index 90a64268b..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/RAGApplication.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2013-2023 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 com.alibaba.cloud.example.ai.rag; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author yuluo - * @author
    yuluo - */ - -@SpringBootApplication -public class RAGApplication { - - private static final Logger logger = LoggerFactory.getLogger(RAGApplication.class); - - public static void main(String[] args) { - - SpringApplication.run(RAGApplication.class, args); - logger.info("RAGApplication started successfully."); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/controller/RAGController.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/controller/RAGController.java deleted file mode 100644 index aed67ecba..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/controller/RAGController.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013-2023 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 com.alibaba.cloud.example.ai.rag.controller; - -import com.alibaba.cloud.example.ai.rag.service.RAGService; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CrossOrigin; -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; - -/** - * @author yuluo - * @author yuluo - */ - -@CrossOrigin -@RestController -@RequestMapping("/rag") -public class RAGController { - - @Autowired - private RAGService ragService; - - @GetMapping("/chat") - public String chatMessage(@RequestParam(value = "prompt", - defaultValue = "What ber pairs well with smoked meats?") String prompt) { - - return ragService.retrieve(prompt) - .getOutput() - .getContent(); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/loader/RAGDataLoader.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/loader/RAGDataLoader.java deleted file mode 100644 index 034af9628..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/loader/RAGDataLoader.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2013-2023 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 com.alibaba.cloud.example.ai.rag.loader; - -import java.util.Map; -import java.util.Objects; -import java.util.zip.GZIPInputStream; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; -import org.springframework.ai.reader.JsonReader; -import org.springframework.ai.vectorstore.RedisVectorStore; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.core.io.InputStreamResource; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Component; - -/** - * @author yuluo - * @author yuluo - */ - -@Component -public class RAGDataLoader implements ApplicationRunner { - - private static final Logger logger = LoggerFactory.getLogger(RAGDataLoader.class); - - private static final String[] KEYS = { "name", "abv", "ibu", "description" }; - - @Value("classpath:/data/beers.json.gz") - private Resource data; - - private final RedisVectorStore vectorStore; - - private final RedisVectorStoreProperties properties; - - public RAGDataLoader(RedisVectorStore vectorStore, RedisVectorStoreProperties properties) { - - this.vectorStore = vectorStore; - this.properties = properties; - } - - @Override - public void run(ApplicationArguments args) throws Exception { - - Map indexInfo = vectorStore.getJedis().ftInfo(properties.getIndex()); - int numDocs = Integer.parseInt((String) indexInfo.getOrDefault("num_docs", "0")); - if (numDocs > 20000) { - logger.info("Embeddings already loaded. Skipping"); - return; - } - - Resource file = data; - if (Objects.requireNonNull(data.getFilename()).endsWith(".gz")) { - GZIPInputStream inputStream = new GZIPInputStream(data.getInputStream()); - file = new InputStreamResource(inputStream, "beers.json.gz"); - } - - logger.info("Creating Embeddings..."); - JsonReader loader = new JsonReader(file, KEYS); - vectorStore.add(loader.get()); - logger.info("Embeddings created."); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/service/RAGService.java b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/service/RAGService.java deleted file mode 100644 index 44d0b763e..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/java/com/alibaba/cloud/example/ai/rag/service/RAGService.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013-2023 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 com.alibaba.cloud.example.ai.rag.service; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.ai.chat.model.Generation; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.SystemPromptTemplate; -import org.springframework.ai.document.Document; -import org.springframework.ai.vectorstore.SearchRequest; -import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Service; - -/** - * @author yuluo - * @author yuluo - */ - -@Service -public class RAGService { - - @Value("classpath:/prompts/system-qa.st") - private Resource systemBeerPrompt; - - @Value("${topk:10}") - private int topK; - - private final ChatModel chatModel; - - private final VectorStore store; - - public RAGService(ChatModel chatModel, VectorStore store) { - - this.chatModel = chatModel; - this.store = store; - } - - public Generation retrieve(String message) { - - SearchRequest request = SearchRequest.query(message).withTopK(topK); - List docs = store.similaritySearch(request); - - Message systemMessage = getSystemMessage(docs); - UserMessage userMessage = new UserMessage(message); - - Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); - ChatResponse response = chatModel.call(prompt); - - return response.getResult(); - } - - private Message getSystemMessage(List similarDocuments) { - - String documents = similarDocuments.stream() - .map(Document::getContent) - .collect(Collectors.joining("\n")); - SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemBeerPrompt); - - return systemPromptTemplate.createMessage(Map.of("documents", documents)); - } - -} diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/application.yml deleted file mode 100644 index 9110af6d9..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/application.yml +++ /dev/null @@ -1,37 +0,0 @@ -# -# Copyright 2023-2024 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. -# - -server: - port: 8081 - -spring: - application: - name: sca-ai-rag-example - cloud: - # We recommend configuring the api-key via an environment variable. - # Please see https://github.com/alibaba/spring-cloud-alibaba/tree/2023.x/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-example#%E6%8E%A5%E5%85%A5-spring-cloud-starter-alibaba-ai: - # ai: - # tongyi: - # connection: - # api-key: sk-xxxxxx - - ai: - vectorstore: - redis: - # Configure the Redis connection URI, default value is redis://127.0.0.1:6379 - # uri: redis://127.0.0.1:6379 - index: peer - prefix: peer diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/data/beers.json.gz b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/data/beers.json.gz deleted file mode 100644 index e32d6b028..000000000 Binary files a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/data/beers.json.gz and /dev/null differ diff --git a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/prompts/system-qa.st b/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/prompts/system-qa.st deleted file mode 100644 index 323908d7c..000000000 --- a/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example/src/main/resources/prompts/system-qa.st +++ /dev/null @@ -1,7 +0,0 @@ -You're assisting with questions about products in a beer catalog. -Use the information from the DOCUMENTS section to provide accurate answers. -The answer involves referring to the ABV or IBU of the beer, include the beer name in the response. -If unsure, simply state that you don't know. - -DOCUMENTS: -{documents} diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml index cf858d2fb..3e0d97aa1 100644 --- a/spring-cloud-alibaba-examples/pom.xml +++ b/spring-cloud-alibaba-examples/pom.xml @@ -51,9 +51,6 @@ integrated-example/integrated-praise-consumer integrated-example/integrated-common integrated-example/integrated-frontend - ai-example/spring-cloud-ai-example - ai-example/spring-cloud-ai-rag-example - ai-example/spring-cloud-ai-chat-msg-context-example spring-cloud-scheduling-example diff --git a/spring-cloud-alibaba-starters/pom.xml b/spring-cloud-alibaba-starters/pom.xml index 112e15014..3414d035e 100644 --- a/spring-cloud-alibaba-starters/pom.xml +++ b/spring-cloud-alibaba-starters/pom.xml @@ -28,7 +28,6 @@ spring-cloud-alibaba-sentinel-datasource spring-cloud-alibaba-sentinel-gateway spring-cloud-alibaba-commons - spring-cloud-starter-alibaba-ai spring-cloud-starter-alibaba-schedulerx diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/README.md b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/README.md deleted file mode 100644 index 51a819b0c..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/README.md +++ /dev/null @@ -1,3 +0,0 @@ -[Spring AI](https://docs.spring.io/spring-ai/reference/0.8-SNAPSHOT/index.html)

    -[通义大模型](https://help.aliyun.com/zh/dashscope/developer-reference/model-introduction)

    -[Spring AI chat completion api](https://docs.spring.io/spring-ai/reference/0.8-SNAPSHOT/api/chatclient.html) diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/pom.xml deleted file mode 100644 index 2658378b2..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/pom.xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - 4.0.0 - - - com.alibaba.cloud - spring-cloud-alibaba-starters - ${revision} - ../pom.xml - - - spring-cloud-starter-alibaba-ai - Spring Cloud Starter Alibaba AI - - - - - org.springframework.ai - spring-ai-core - - - - - org.springframework.boot - spring-boot-actuator - true - - - - org.springframework.boot - spring-boot-starter-web - - - - com.alibaba - dashscope-sdk-java - - - - org.springframework.boot - spring-boot-actuator-autoconfigure - true - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - - org.springframework - spring-context - - - - org.springframework.ai - spring-ai-test - test - - - - org.springframework.boot - spring-boot-starter-test - test - - - - io.projectreactor - reactor-test - test - - - - javax.annotation - jsr250-api - 1.0 - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - false - - - - - \ No newline at end of file diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiAutoConfiguration.java deleted file mode 100644 index 30a75dc0b..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiAutoConfiguration.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi; - -import java.util.Objects; - -import com.alibaba.cloud.ai.tongyi.audio.speech.TongYiAudioSpeechModel; -import com.alibaba.cloud.ai.tongyi.audio.speech.TongYiAudioSpeechProperties; -import com.alibaba.cloud.ai.tongyi.audio.transcription.TongYiAudioTranscriptionModel; -import com.alibaba.cloud.ai.tongyi.audio.transcription.TongYiAudioTranscriptionProperties; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatProperties; -import com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants; -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiException; -import com.alibaba.cloud.ai.tongyi.embedding.TongYiTextEmbeddingModel; -import com.alibaba.cloud.ai.tongyi.embedding.TongYiTextEmbeddingProperties; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties; -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import com.alibaba.dashscope.audio.asr.transcription.Transcription; -import com.alibaba.dashscope.audio.tts.SpeechSynthesizer; -import com.alibaba.dashscope.embeddings.TextEmbedding; -import com.alibaba.dashscope.exception.NoApiKeyException; -import com.alibaba.dashscope.utils.ApiKey; -import com.alibaba.dashscope.utils.Constants; - -import org.springframework.ai.model.function.FunctionCallbackContext; -import org.springframework.boot.autoconfigure.AutoConfiguration; -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.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Scope; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@AutoConfiguration -@ConditionalOnClass({ - TongYiChatModel.class, - TongYiImagesModel.class, - TongYiAudioSpeechModel.class, - TongYiTextEmbeddingModel.class, - TongYiAudioTranscriptionModel.class -}) -@EnableConfigurationProperties({ - TongYiChatProperties.class, - TongYiImagesProperties.class, - TongYiAudioSpeechProperties.class, - TongYiConnectionProperties.class, - TongYiTextEmbeddingProperties.class, - TongYiAudioTranscriptionProperties.class -}) -public class TongYiAutoConfiguration { - - @Bean - @Scope("prototype") - @ConditionalOnMissingBean - public Generation generation() { - - return new Generation(); - } - - @Bean - @Scope("prototype") - @ConditionalOnMissingBean - public ImageSynthesis imageSynthesis() { - - return new ImageSynthesis(); - } - - @Bean - @Scope("prototype") - @ConditionalOnMissingBean - public SpeechSynthesizer speechSynthesizer() { - - return new SpeechSynthesizer(); - } - - @Bean - @ConditionalOnMissingBean - public Transcription transcription() { - - return new Transcription(); - } - - @Bean - @ConditionalOnMissingBean - public TextEmbedding textEmbedding() { - - return new TextEmbedding(); - } - - @Bean - @ConditionalOnMissingBean - public FunctionCallbackContext springAiFunctionManager(ApplicationContext context) { - - FunctionCallbackContext manager = new FunctionCallbackContext(); - manager.setApplicationContext(context); - - return manager; - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiChatProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiChatModel tongYiChatClient(Generation generation, - TongYiChatProperties chatOptions, - TongYiConnectionProperties connectionProperties - ) { - - settingApiKey(connectionProperties); - - return new TongYiChatModel(generation, chatOptions.getOptions()); - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiImagesProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiImagesModel tongYiImagesClient( - ImageSynthesis imageSynthesis, - TongYiImagesProperties imagesOptions, - TongYiConnectionProperties connectionProperties - ) { - - settingApiKey(connectionProperties); - - return new TongYiImagesModel(imageSynthesis, imagesOptions.getOptions()); - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiAudioSpeechProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiAudioSpeechModel tongYiAudioSpeechClient( - SpeechSynthesizer speechSynthesizer, - TongYiAudioSpeechProperties speechProperties, - TongYiConnectionProperties connectionProperties - ) { - - settingApiKey(connectionProperties); - - return new TongYiAudioSpeechModel(speechSynthesizer, speechProperties.getOptions()); - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiAudioTranscriptionProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiAudioTranscriptionModel tongYiAudioTranscriptionClient( - Transcription transcription, - TongYiAudioTranscriptionProperties transcriptionProperties, - TongYiConnectionProperties connectionProperties) { - - settingApiKey(connectionProperties); - - return new TongYiAudioTranscriptionModel( - transcriptionProperties.getOptions(), - transcription - ); - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiTextEmbeddingProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiTextEmbeddingModel tongYiTextEmbeddingClient( - TextEmbedding textEmbedding, - TongYiConnectionProperties connectionProperties - ) { - - settingApiKey(connectionProperties); - return new TongYiTextEmbeddingModel(textEmbedding); - } - - /** - * Setting the API key. - * @param connectionProperties {@link TongYiConnectionProperties} - */ - private void settingApiKey(TongYiConnectionProperties connectionProperties) { - - String apiKey; - - try { - // It is recommended to set the key by defining the api-key in an environment variable. - var envKey = System.getenv(TongYiConstants.SCA_AI_TONGYI_API_KEY); - if (Objects.nonNull(envKey)) { - Constants.apiKey = envKey; - return; - } - if (Objects.nonNull(connectionProperties.getApiKey())) { - apiKey = connectionProperties.getApiKey(); - } - else { - apiKey = ApiKey.getApiKey(null); - } - - Constants.apiKey = apiKey; - } - catch (NoApiKeyException e) { - - throw new TongYiException(e.getMessage()); - } - - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiConnectionProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiConnectionProperties.java deleted file mode 100644 index db15e7367..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiConnectionProperties.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * Spring Cloud Alibaba AI TongYi LLM connection properties. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiConnectionProperties.CONFIG_PREFIX) -public class TongYiConnectionProperties { - - /** - * Spring Cloud Alibaba AI connection configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "connection"; - - /** - * TongYi LLM API key. - */ - private String apiKey; - - public String getApiKey() { - return apiKey; - } - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioSpeechModels.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioSpeechModels.java deleted file mode 100644 index 2c8bd707b..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioSpeechModels.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio; - -/** - * More models see: https://help.aliyun.com/zh/dashscope/model-list?spm=a2c4g.11186623.0.i5 - * Support all models in list. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public final class AudioSpeechModels { - - private AudioSpeechModels() { - } - - /** - * Male Voice of the Tongue(舌尖男声). - * zh & en. - * Default sample rate: 48 Hz. - */ - public static final String SAMBERT_ZHICHU_V1 = "sambert-zhichu-v1"; - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioTranscriptionModels.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioTranscriptionModels.java deleted file mode 100644 index 4b8435629..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioTranscriptionModels.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public final class AudioTranscriptionModels { - - private AudioTranscriptionModels() { - } - - /** - * Paraformer Chinese and English speech recognition model supports audio or video speech recognition with a sampling rate of 16kHz or above. - */ - public static final String Paraformer_V1 = "paraformer-v1"; - /** - * Paraformer Chinese speech recognition model, support 8kHz telephone speech recognition. - */ - public static final String Paraformer_8K_V1 = "paraformer-8k-v1"; - /** - * The Paraformer multilingual speech recognition model supports audio or video speech recognition with a sample rate of 16kHz or above. - */ - public static final String Paraformer_MTL_V1 = "paraformer-mtl-v1"; - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechModel.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechModel.java deleted file mode 100644 index 9b7b8355f..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechModel.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech; - -import java.nio.ByteBuffer; - -import com.alibaba.cloud.ai.tongyi.audio.AudioSpeechModels; -import com.alibaba.cloud.ai.tongyi.audio.speech.api.Speech; -import com.alibaba.cloud.ai.tongyi.audio.speech.api.SpeechModel; -import com.alibaba.cloud.ai.tongyi.audio.speech.api.SpeechPrompt; -import com.alibaba.cloud.ai.tongyi.audio.speech.api.SpeechResponse; -import com.alibaba.cloud.ai.tongyi.audio.speech.api.SpeechStreamModel; -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioSpeechResponseMetadata; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisParam; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult; -import com.alibaba.dashscope.audio.tts.SpeechSynthesizer; -import com.alibaba.dashscope.common.ResultCallback; -import io.reactivex.Flowable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Flux; -import reactor.core.scheduler.Schedulers; - -import org.springframework.util.Assert; - -/** - * TongYiAudioSpeechClient is a client for TongYi audio speech service for Spring Cloud Alibaba AI. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioSpeechModel implements SpeechModel, SpeechStreamModel { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - /** - * Default speed rate. - */ - private static final float SPEED_RATE = 1.0f; - - /** - * TongYi models api. - */ - private final SpeechSynthesizer speechSynthesizer; - - /** - * TongYi models options. - */ - private final TongYiAudioSpeechOptions defaultOptions; - - /** - * TongYiAudioSpeechClient constructor. - * @param speechSynthesizer the speech synthesizer - */ - public TongYiAudioSpeechModel(SpeechSynthesizer speechSynthesizer) { - - this(speechSynthesizer, null); - } - - /** - * TongYiAudioSpeechClient constructor. - * @param speechSynthesizer the speech synthesizer - * @param tongYiAudioOptions the tongYi audio options - */ - public TongYiAudioSpeechModel(SpeechSynthesizer speechSynthesizer, TongYiAudioSpeechOptions tongYiAudioOptions) { - - Assert.notNull(speechSynthesizer, "speechSynthesizer must not be null"); - Assert.notNull(tongYiAudioOptions, "tongYiAudioOptions must not be null"); - - this.speechSynthesizer = speechSynthesizer; - this.defaultOptions = tongYiAudioOptions; - } - - /** - * Call the TongYi audio speech service. - * @param text the text message to be converted to audio. - * @return the audio byte buffer. - */ - @Override - public ByteBuffer call(String text) { - - var speechRequest = new SpeechPrompt(text); - - return call(speechRequest).getResult().getOutput(); - } - - - /** - * Call the TongYi audio speech service. - * @param prompt the speech prompt. - * @return the speech response. - */ - @Override - public SpeechResponse call(SpeechPrompt prompt) { - - var SCASpeechParam = merge(prompt.getOptions()); - var speechSynthesisParams = toSpeechSynthesisParams(SCASpeechParam); - speechSynthesisParams.setText(prompt.getInstructions().getText()); - logger.info(speechSynthesisParams.toString()); - - var res = speechSynthesizer.call(speechSynthesisParams); - - return convert(res, null); - } - - /** - * Call the TongYi audio speech service. - * @param prompt the speech prompt. - * @param callback the result callback. - * {@link SpeechSynthesizer#call(SpeechSynthesisParam, ResultCallback)} - */ - public void call(SpeechPrompt prompt, ResultCallback callback) { - - var SCASpeechParam = merge(prompt.getOptions()); - var speechSynthesisParams = toSpeechSynthesisParams(SCASpeechParam); - speechSynthesisParams.setText(prompt.getInstructions().getText()); - - speechSynthesizer.call(speechSynthesisParams, callback); - } - - /** - * Stream the TongYi audio speech service. - * @param prompt the speech prompt. - * @return the speech response. - * {@link SpeechSynthesizer#streamCall(SpeechSynthesisParam)} - */ - @Override - public Flux stream(SpeechPrompt prompt) { - - var SCASpeechParam = merge(prompt.getOptions()); - - Flowable resultFlowable = speechSynthesizer - .streamCall(toSpeechSynthesisParams(SCASpeechParam)); - - return Flux.from(resultFlowable) - .flatMap( - res -> Flux.just(res.getAudioFrame()) - .map(audio -> { - var speech = new Speech(audio); - var respMetadata = TongYiAudioSpeechResponseMetadata.from(res); - return new SpeechResponse(speech, respMetadata); - }) - ).publishOn(Schedulers.parallel()); - } - - public TongYiAudioSpeechOptions merge(TongYiAudioSpeechOptions target) { - - var mergeBuilder = TongYiAudioSpeechOptions.builder(); - - mergeBuilder.withModel(defaultOptions.getModel() != null ? defaultOptions.getModel() : target.getModel()); - mergeBuilder.withPitch(defaultOptions.getPitch() != null ? defaultOptions.getPitch() : target.getPitch()); - mergeBuilder.withRate(defaultOptions.getRate() != null ? defaultOptions.getRate() : target.getRate()); - mergeBuilder.withFormat(defaultOptions.getFormat() != null ? defaultOptions.getFormat() : target.getFormat()); - mergeBuilder.withSampleRate(defaultOptions.getSampleRate() != null ? defaultOptions.getSampleRate() : target.getSampleRate()); - mergeBuilder.withTextType(defaultOptions.getTextType() != null ? defaultOptions.getTextType() : target.getTextType()); - mergeBuilder.withVolume(defaultOptions.getVolume() != null ? defaultOptions.getVolume() : target.getVolume()); - mergeBuilder.withEnablePhonemeTimestamp(defaultOptions.isEnablePhonemeTimestamp() != null ? defaultOptions.isEnablePhonemeTimestamp() : target.isEnablePhonemeTimestamp()); - mergeBuilder.withEnableWordTimestamp(defaultOptions.isEnableWordTimestamp() != null ? defaultOptions.isEnableWordTimestamp() : target.isEnableWordTimestamp()); - - return mergeBuilder.build(); - } - - public SpeechSynthesisParam toSpeechSynthesisParams(TongYiAudioSpeechOptions source) { - - var mergeBuilder = SpeechSynthesisParam.builder(); - - mergeBuilder.model(source.getModel() != null ? source.getModel() : AudioSpeechModels.SAMBERT_ZHICHU_V1); - mergeBuilder.text(source.getText() != null ? source.getText() : ""); - - if (source.getFormat() != null) { - mergeBuilder.format(source.getFormat()); - } - if (source.getRate() != null) { - mergeBuilder.rate(source.getRate()); - } - if (source.getPitch() != null) { - mergeBuilder.pitch(source.getPitch()); - } - if (source.getTextType() != null) { - mergeBuilder.textType(source.getTextType()); - } - if (source.getSampleRate() != null) { - mergeBuilder.sampleRate(source.getSampleRate()); - } - if (source.isEnablePhonemeTimestamp() != null) { - mergeBuilder.enablePhonemeTimestamp(source.isEnablePhonemeTimestamp()); - } - if (source.isEnableWordTimestamp() != null) { - mergeBuilder.enableWordTimestamp(source.isEnableWordTimestamp()); - } - if (source.getVolume() != null) { - mergeBuilder.volume(source.getVolume()); - } - - return mergeBuilder.build(); - } - - /** - * Convert the TongYi audio speech service result to the speech response. - * @param result the audio byte buffer. - * @param synthesisResult the synthesis result. - * @return the speech response. - */ - private SpeechResponse convert(ByteBuffer result, SpeechSynthesisResult synthesisResult) { - - if (synthesisResult == null) { - - return new SpeechResponse(new Speech(result)); - } - - var responseMetadata = TongYiAudioSpeechResponseMetadata.from(synthesisResult); - var speech = new Speech(synthesisResult.getAudioFrame()); - - return new SpeechResponse(speech, responseMetadata); - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptions.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptions.java deleted file mode 100644 index 843971871..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptions.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech; - -import com.alibaba.cloud.ai.tongyi.audio.AudioSpeechModels; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisTextType; - -import org.springframework.ai.model.ModelOptions; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioSpeechOptions implements ModelOptions { - - - /** - * Audio Speech models. - */ - private String model = AudioSpeechModels.SAMBERT_ZHICHU_V1; - - /** - * Text content. - */ - private String text; - - /** - * Input text type. - */ - private SpeechSynthesisTextType textType = SpeechSynthesisTextType.PLAIN_TEXT; - - /** - * synthesis audio format. - */ - private SpeechSynthesisAudioFormat format = SpeechSynthesisAudioFormat.WAV; - - /** - * synthesis audio sample rate. - */ - private Integer sampleRate = 16000; - - /** - * synthesis audio volume. - */ - private Integer volume = 50; - - /** - * synthesis audio speed. - */ - private Float rate = 1.0f; - - /** - * synthesis audio pitch. - */ - private Float pitch = 1.0f; - - /** - * enable word level timestamp. - */ - private Boolean enableWordTimestamp = false; - - /** - * enable phoneme level timestamp. - */ - private Boolean enablePhonemeTimestamp = false; - - public static Builder builder() { - return new Builder(); - } - - public String getModel() { - - return model; - } - - public void setModel(String model) { - - this.model = model; - } - - public String getText() { - - return text; - } - - public void setText(String text) { - - this.text = text; - } - - public SpeechSynthesisTextType getTextType() { - - return textType; - } - - public void setTextType(SpeechSynthesisTextType textType) { - - this.textType = textType; - } - - public SpeechSynthesisAudioFormat getFormat() { - - return format; - } - - public void setFormat(SpeechSynthesisAudioFormat format) { - - this.format = format; - } - - public Integer getSampleRate() { - - return sampleRate; - } - - public void setSampleRate(Integer sampleRate) { - - this.sampleRate = sampleRate; - } - - public Integer getVolume() { - - return volume; - } - - public void setVolume(Integer volume) { - - this.volume = volume; - } - - public Float getRate() { - - return rate; - } - - public void setRate(Float rate) { - - this.rate = rate; - } - - public Float getPitch() { - - return pitch; - } - - public void setPitch(Float pitch) { - - this.pitch = pitch; - } - - public Boolean isEnableWordTimestamp() { - - return enableWordTimestamp; - } - - public void setEnableWordTimestamp(Boolean enableWordTimestamp) { - - this.enableWordTimestamp = enableWordTimestamp; - } - - public Boolean isEnablePhonemeTimestamp() { - - return enablePhonemeTimestamp; - } - - public void setEnablePhonemeTimestamp(Boolean enablePhonemeTimestamp) { - - this.enablePhonemeTimestamp = enablePhonemeTimestamp; - } - - /** - * Build a options instances. - */ - public static class Builder { - - private final TongYiAudioSpeechOptions options = new TongYiAudioSpeechOptions(); - - public Builder withModel(String model) { - - options.model = model; - return this; - } - - public Builder withText(String text) { - - options.text = text; - return this; - } - - public Builder withTextType(SpeechSynthesisTextType textType) { - - options.textType = textType; - return this; - } - - public Builder withFormat(SpeechSynthesisAudioFormat format) { - - options.format = format; - return this; - } - - public Builder withSampleRate(Integer sampleRate) { - - options.sampleRate = sampleRate; - return this; - } - - public Builder withVolume(Integer volume) { - - options.volume = volume; - return this; - } - - public Builder withRate(Float rate) { - - options.rate = rate; - return this; - } - - public Builder withPitch(Float pitch) { - - options.pitch = pitch; - return this; - } - - public Builder withEnableWordTimestamp(Boolean enableWordTimestamp) { - - options.enableWordTimestamp = enableWordTimestamp; - return this; - } - - public Builder withEnablePhonemeTimestamp(Boolean enablePhonemeTimestamp) { - - options.enablePhonemeTimestamp = enablePhonemeTimestamp; - return this; - } - - public TongYiAudioSpeechOptions build() { - - return options; - } - - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechProperties.java deleted file mode 100644 index 4e8a4786c..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechProperties.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech; - -import com.alibaba.cloud.ai.tongyi.audio.AudioSpeechModels; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * TongYi audio speech configuration properties. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiAudioSpeechProperties.CONFIG_PREFIX) -public class TongYiAudioSpeechProperties { - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "audio.speech"; - /** - * Default TongYi Chat model. - */ - public static final String DEFAULT_AUDIO_MODEL_NAME = AudioSpeechModels.SAMBERT_ZHICHU_V1; - - /** - * Enable TongYiQWEN ai audio client. - */ - private boolean enabled = true; - - @NestedConfigurationProperty - private TongYiAudioSpeechOptions options = TongYiAudioSpeechOptions.builder() - .withModel(DEFAULT_AUDIO_MODEL_NAME) - .withFormat(SpeechSynthesisAudioFormat.WAV) - .build(); - - public TongYiAudioSpeechOptions getOptions() { - - return this.options; - } - - public void setOptions(TongYiAudioSpeechOptions options) { - - this.options = options; - } - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/Speech.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/Speech.java deleted file mode 100644 index 560e490de..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/Speech.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Objects; - -import org.springframework.ai.model.ModelResult; -import org.springframework.lang.Nullable; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class Speech implements ModelResult { - - private final ByteBuffer audio; - - private SpeechMetadata speechMetadata; - - public Speech(ByteBuffer audio) { - this.audio = audio; - } - - @Override - public ByteBuffer getOutput() { - return this.audio; - } - - @Override - public SpeechMetadata getMetadata() { - - return speechMetadata != null ? speechMetadata : SpeechMetadata.NULL; - } - - public Speech withSpeechMetadata(@Nullable SpeechMetadata speechMetadata) { - - this.speechMetadata = speechMetadata; - return this; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - - return true; - } - if (!(o instanceof Speech that)) { - - return false; - } - - return Arrays.equals(audio.array(), that.audio.array()) - && Objects.equals(speechMetadata, that.speechMetadata); - } - - @Override - public int hashCode() { - - return Objects.hash(Arrays.hashCode(audio.array()), speechMetadata); - } - - @Override - public String toString() { - - return "Speech{" + "text=" + audio + ", speechMetadata=" + speechMetadata + '}'; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMessage.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMessage.java deleted file mode 100644 index 9748bcf50..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMessage.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import java.util.Objects; - -/** - * The {@link SpeechMessage} class represents a single text message to - * be converted to speech by the TongYi LLM TTS. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class SpeechMessage { - - private String text; - - /** - * Constructs a new {@link SpeechMessage} object with the given text. - * @param text the text to be converted to speech - */ - public SpeechMessage(String text) { - this.text = text; - } - - /** - * Returns the text of this speech message. - * @return the text of this speech message - */ - public String getText() { - return text; - } - - /** - * Sets the text of this speech message. - * @param text the new text for this speech message - */ - public void setText(String text) { - this.text = text; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - - return true; - } - - if (!(o instanceof SpeechMessage that)) { - - return false; - } - - return Objects.equals(text, that.text); - } - - @Override - public int hashCode() { - - return Objects.hash(text); - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMetadata.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMetadata.java deleted file mode 100644 index 513a2839c..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMetadata.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import org.springframework.ai.model.ResultMetadata; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public interface SpeechMetadata extends ResultMetadata { - - /** - * Null Object. - */ - SpeechMetadata NULL = SpeechMetadata.create(); - - /** - * Factory method used to construct a new {@link SpeechMetadata}. - * @return a new {@link SpeechMetadata} - */ - static SpeechMetadata create() { - return new SpeechMetadata() { - }; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechModel.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechModel.java deleted file mode 100644 index 4b54209c2..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechModel.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import java.nio.ByteBuffer; - -import org.springframework.ai.model.Model; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.0.0-RC1 - */ - -@FunctionalInterface -public interface SpeechModel extends Model { - - /** - * Generates spoken audio from the provided text message. - * @param message the text message to be converted to audio. - * @return the resulting audio bytes. - */ - default ByteBuffer call(String message) { - - SpeechPrompt prompt = new SpeechPrompt(message); - - return call(prompt).getResult().getOutput(); - } - - /** - * Sends a speech request to the TongYi TTS API and returns the resulting speech response. - * @param request the speech prompt containing the input text and other parameters. - * @return the speech response containing the generated audio. - */ - SpeechResponse call(SpeechPrompt request); - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechPrompt.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechPrompt.java deleted file mode 100644 index 42184a83c..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechPrompt.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import java.util.Objects; - -import com.alibaba.cloud.ai.tongyi.audio.speech.TongYiAudioSpeechOptions; - -import org.springframework.ai.model.ModelRequest; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class SpeechPrompt implements ModelRequest { - - private TongYiAudioSpeechOptions speechOptions; - - private final SpeechMessage message; - - public SpeechPrompt(String instructions) { - - this(new SpeechMessage(instructions), TongYiAudioSpeechOptions.builder().build()); - } - - public SpeechPrompt(String instructions, TongYiAudioSpeechOptions speechOptions) { - - this(new SpeechMessage(instructions), speechOptions); - } - - public SpeechPrompt(SpeechMessage speechMessage) { - this(speechMessage, TongYiAudioSpeechOptions.builder().build()); - } - - public SpeechPrompt(SpeechMessage speechMessage, TongYiAudioSpeechOptions speechOptions) { - - this.message = speechMessage; - this.speechOptions = speechOptions; - } - - @Override - public SpeechMessage getInstructions() { - return this.message; - } - - @Override - public TongYiAudioSpeechOptions getOptions() { - - return speechOptions; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - - return true; - } - if (!(o instanceof SpeechPrompt that)) { - - return false; - } - - return Objects.equals(speechOptions, that.speechOptions) && Objects.equals(message, that.message); - } - - @Override - public int hashCode() { - - return Objects.hash(speechOptions, message); - } - - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechResponse.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechResponse.java deleted file mode 100644 index 99485c427..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechResponse.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioSpeechResponseMetadata; - -import org.springframework.ai.model.ModelResponse; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class SpeechResponse implements ModelResponse { - - private final Speech speech; - - private final TongYiAudioSpeechResponseMetadata speechResponseMetadata; - - /** - * Creates a new instance of SpeechResponse with the given speech result. - * @param speech the speech result to be set in the SpeechResponse - * @see Speech - */ - public SpeechResponse(Speech speech) { - this(speech, TongYiAudioSpeechResponseMetadata.NULL); - } - - /** - * Creates a new instance of SpeechResponse with the given speech result and speech - * response metadata. - * @param speech the speech result to be set in the SpeechResponse - * @param speechResponseMetadata the speech response metadata to be set in the - * SpeechResponse - * @see Speech - * @see TongYiAudioSpeechResponseMetadata - */ - public SpeechResponse(Speech speech, TongYiAudioSpeechResponseMetadata speechResponseMetadata) { - - this.speech = speech; - this.speechResponseMetadata = speechResponseMetadata; - } - - @Override - public Speech getResult() { - return speech; - } - - @Override - public List getResults() { - return Collections.singletonList(speech); - } - - @Override - public TongYiAudioSpeechResponseMetadata getMetadata() { - return speechResponseMetadata; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - - return true; - } - if (!(o instanceof SpeechResponse that)) { - - return false; - } - - return Objects.equals(speech, that.speech) - && Objects.equals(speechResponseMetadata, that.speechResponseMetadata); - } - - @Override - public int hashCode() { - - return Objects.hash(speech, speechResponseMetadata); - } - - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechStreamModel.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechStreamModel.java deleted file mode 100644 index 78c99fb8d..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechStreamModel.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import java.nio.ByteBuffer; - -import reactor.core.publisher.Flux; - -import org.springframework.ai.model.StreamingModel; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@FunctionalInterface -public interface SpeechStreamModel extends StreamingModel { - - /** - * Generates a stream of audio bytes from the provided text message. - * - * @param message the text message to be converted to audio - * @return a Flux of audio bytes representing the generated speech - */ - default Flux stream(String message) { - - SpeechPrompt prompt = new SpeechPrompt(message); - return stream(prompt).map(SpeechResponse::getResult).map(Speech::getOutput); - } - - /** - * Sends a speech request to the TongYi TTS API and returns a stream of the resulting - * speech responses. - * @param prompt the speech prompt containing the input text and other parameters. - * @return a Flux of speech responses, each containing a portion of the generated audio. - */ - @Override - Flux stream(SpeechPrompt prompt); - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionModel.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionModel.java deleted file mode 100644 index 7d163e081..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionModel.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.transcription; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import com.alibaba.cloud.ai.tongyi.audio.AudioTranscriptionModels; -import com.alibaba.cloud.ai.tongyi.audio.transcription.api.AudioTranscriptionPrompt; -import com.alibaba.cloud.ai.tongyi.audio.transcription.api.AudioTranscriptionResponse; -import com.alibaba.cloud.ai.tongyi.audio.transcription.api.AudioTranscriptionResult; -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiException; -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioTranscriptionResponseMetadata; -import com.alibaba.dashscope.audio.asr.transcription.Transcription; -import com.alibaba.dashscope.audio.asr.transcription.TranscriptionParam; -import com.alibaba.dashscope.audio.asr.transcription.TranscriptionQueryParam; -import com.alibaba.dashscope.audio.asr.transcription.TranscriptionResult; -import com.alibaba.dashscope.audio.asr.transcription.TranscriptionTaskResult; - -import org.springframework.ai.model.Model; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; - -/** - * TongYiAudioTranscriptionModel is a client for TongYi audio transcription service for - * Spring Cloud Alibaba AI. - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioTranscriptionModel - implements Model { - - /** - * TongYi models options. - */ - private final TongYiAudioTranscriptionOptions defaultOptions; - - /** - * TongYi models api. - */ - private final Transcription transcription; - - public TongYiAudioTranscriptionModel(Transcription transcription) { - this(null, transcription); - } - - public TongYiAudioTranscriptionModel(TongYiAudioTranscriptionOptions defaultOptions, - Transcription transcription) { - Assert.notNull(transcription, "transcription must not be null"); - Assert.notNull(defaultOptions, "defaultOptions must not be null"); - - this.defaultOptions = defaultOptions; - this.transcription = transcription; - } - - @Override - public AudioTranscriptionResponse call(AudioTranscriptionPrompt prompt) { - - TranscriptionParam transcriptionParam; - - if (prompt.getOptions() != null) { - var param = merge(prompt.getOptions()); - transcriptionParam = toTranscriptionParam(param); - transcriptionParam.setFileUrls(prompt.getOptions().getFileUrls()); - } - else { - Resource instructions = prompt.getInstructions(); - try { - transcriptionParam = TranscriptionParam.builder() - .model(AudioTranscriptionModels.Paraformer_V1) - .fileUrls(List.of(String.valueOf(instructions.getURL()))) - .build(); - } - catch (IOException e) { - throw new TongYiException("Failed to create resource", e); - } - } - - List taskResultList; - try { - // Submit a transcription request - TranscriptionResult result = transcription.asyncCall(transcriptionParam); - // Wait for the transcription to complete - result = transcription.wait(TranscriptionQueryParam - .FromTranscriptionParam(transcriptionParam, result.getTaskId())); - // Get the transcription results - System.out.println(result.getOutput().getAsJsonObject()); - taskResultList = result.getResults(); - System.out.println(Arrays.toString(taskResultList.toArray())); - - return new AudioTranscriptionResponse( - taskResultList.stream().map(taskResult -> - new AudioTranscriptionResult(taskResult.getTranscriptionUrl()) - ).collect(Collectors.toList()), - TongYiAudioTranscriptionResponseMetadata.from(result) - ); - } - catch (Exception e) { - throw new TongYiException("Failed to call audio transcription", e); - } - - } - - public TongYiAudioTranscriptionOptions merge(TongYiAudioTranscriptionOptions target) { - var mergeBuilder = TongYiAudioTranscriptionOptions.builder(); - - mergeBuilder - .withModel(defaultOptions.getModel() != null ? defaultOptions.getModel() - : target.getModel()); - mergeBuilder.withChannelId( - defaultOptions.getChannelId() != null ? defaultOptions.getChannelId() - : target.getChannelId()); - mergeBuilder.withDiarizationEnabled(defaultOptions.getDiarizationEnabled() != null - ? defaultOptions.getDiarizationEnabled() - : target.getDiarizationEnabled()); - mergeBuilder.withDisfluencyRemovalEnabled( - defaultOptions.getDisfluencyRemovalEnabled() != null - ? defaultOptions.getDisfluencyRemovalEnabled() - : target.getDisfluencyRemovalEnabled()); - mergeBuilder.withTimestampAlignmentEnabled( - defaultOptions.getTimestampAlignmentEnabled() != null - ? defaultOptions.getTimestampAlignmentEnabled() - : target.getTimestampAlignmentEnabled()); - mergeBuilder.withSpecialWordFilter(defaultOptions.getSpecialWordFilter() != null - ? defaultOptions.getSpecialWordFilter() - : target.getSpecialWordFilter()); - mergeBuilder.withAudioEventDetectionEnabled( - defaultOptions.getAudioEventDetectionEnabled() != null - ? defaultOptions.getAudioEventDetectionEnabled() - : target.getAudioEventDetectionEnabled()); - - return mergeBuilder.build(); - } - - public TranscriptionParam toTranscriptionParam( - TongYiAudioTranscriptionOptions source) { - var mergeBuilder = TranscriptionParam.builder(); - - mergeBuilder.model(source.getModel() != null ? source.getModel() - : AudioTranscriptionModels.Paraformer_V1); - mergeBuilder.fileUrls( - source.getFileUrls() != null ? source.getFileUrls() : new ArrayList<>()); - if (source.getPhraseId() != null) { - mergeBuilder.phraseId(source.getPhraseId()); - } - if (source.getChannelId() != null) { - mergeBuilder.channelId(source.getChannelId()); - } - if (source.getDiarizationEnabled() != null) { - mergeBuilder.diarizationEnabled(source.getDiarizationEnabled()); - } - if (source.getSpeakerCount() != null) { - mergeBuilder.speakerCount(source.getSpeakerCount()); - } - if (source.getDisfluencyRemovalEnabled() != null) { - mergeBuilder.disfluencyRemovalEnabled(source.getDisfluencyRemovalEnabled()); - } - if (source.getTimestampAlignmentEnabled() != null) { - mergeBuilder.timestampAlignmentEnabled(source.getTimestampAlignmentEnabled()); - } - if (source.getSpecialWordFilter() != null) { - mergeBuilder.specialWordFilter(source.getSpecialWordFilter()); - } - if (source.getAudioEventDetectionEnabled() != null) { - mergeBuilder - .audioEventDetectionEnabled(source.getAudioEventDetectionEnabled()); - } - - return mergeBuilder.build(); - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptions.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptions.java deleted file mode 100644 index 121141b20..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptions.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.transcription; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import com.alibaba.cloud.ai.tongyi.audio.AudioTranscriptionModels; - -import org.springframework.ai.model.ModelOptions; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioTranscriptionOptions implements ModelOptions { - - private String model = AudioTranscriptionModels.Paraformer_V1; - - private List fileUrls = new ArrayList<>(); - - private String phraseId = null; - - private List channelId = Collections.singletonList(0); - - private Boolean diarizationEnabled = false; - - private Integer speakerCount = null; - - private Boolean disfluencyRemovalEnabled = false; - - private Boolean timestampAlignmentEnabled = false; - - private String specialWordFilter = ""; - - private Boolean audioEventDetectionEnabled = false; - - public static TongYiAudioTranscriptionOptions.Builder builder() { - - return new TongYiAudioTranscriptionOptions.Builder(); - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public List getFileUrls() { - return fileUrls; - } - - public void setFileUrls(List fileUrls) { - this.fileUrls = fileUrls; - } - - public String getPhraseId() { - return phraseId; - } - - public void setPhraseId(String phraseId) { - this.phraseId = phraseId; - } - - public List getChannelId() { - return channelId; - } - - public void setChannelId(List channelId) { - this.channelId = channelId; - } - - public Boolean getDiarizationEnabled() { - return diarizationEnabled; - } - - public void setDiarizationEnabled(Boolean diarizationEnabled) { - this.diarizationEnabled = diarizationEnabled; - } - - public Integer getSpeakerCount() { - return speakerCount; - } - - public void setSpeakerCount(Integer speakerCount) { - this.speakerCount = speakerCount; - } - - public Boolean getDisfluencyRemovalEnabled() { - return disfluencyRemovalEnabled; - } - - public void setDisfluencyRemovalEnabled(Boolean disfluencyRemovalEnabled) { - this.disfluencyRemovalEnabled = disfluencyRemovalEnabled; - } - - public Boolean getTimestampAlignmentEnabled() { - return timestampAlignmentEnabled; - } - - public void setTimestampAlignmentEnabled(Boolean timestampAlignmentEnabled) { - this.timestampAlignmentEnabled = timestampAlignmentEnabled; - } - - public String getSpecialWordFilter() { - return specialWordFilter; - } - - public void setSpecialWordFilter(String specialWordFilter) { - this.specialWordFilter = specialWordFilter; - } - - public Boolean getAudioEventDetectionEnabled() { - return audioEventDetectionEnabled; - } - - public void setAudioEventDetectionEnabled(Boolean audioEventDetectionEnabled) { - this.audioEventDetectionEnabled = audioEventDetectionEnabled; - } - - /** - * Builder class for constructing TongYiAudioTranscriptionOptions instances. - */ - public static class Builder { - - private final TongYiAudioTranscriptionOptions options = new TongYiAudioTranscriptionOptions(); - - public Builder withModel(String model) { - options.model = model; - return this; - } - - public Builder withFileUrls(List fileUrls) { - options.fileUrls = fileUrls; - return this; - } - - public Builder withPhraseId(String phraseId) { - options.phraseId = phraseId; - return this; - } - - public Builder withChannelId(List channelId) { - options.channelId = channelId; - return this; - } - - public Builder withDiarizationEnabled(Boolean diarizationEnabled) { - options.diarizationEnabled = diarizationEnabled; - return this; - } - - public Builder withSpeakerCount(Integer speakerCount) { - options.speakerCount = speakerCount; - return this; - } - - public Builder withDisfluencyRemovalEnabled(Boolean disfluencyRemovalEnabled) { - options.disfluencyRemovalEnabled = disfluencyRemovalEnabled; - return this; - } - - public Builder withTimestampAlignmentEnabled(Boolean timestampAlignmentEnabled) { - options.timestampAlignmentEnabled = timestampAlignmentEnabled; - return this; - } - - public Builder withSpecialWordFilter(String specialWordFilter) { - options.specialWordFilter = specialWordFilter; - return this; - } - - public Builder withAudioEventDetectionEnabled( - Boolean audioEventDetectionEnabled) { - options.audioEventDetectionEnabled = audioEventDetectionEnabled; - return this; - } - - public TongYiAudioTranscriptionOptions build() { - // Perform any necessary validation here before returning the built object - return options; - } - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionProperties.java deleted file mode 100644 index e98f77acc..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionProperties.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.transcription; - -import com.alibaba.cloud.ai.tongyi.audio.AudioTranscriptionModels; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiAudioTranscriptionProperties.CONFIG_PREFIX) -public class TongYiAudioTranscriptionProperties { - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "audio.transcription"; - - /** - * Default TongYi Chat model. - */ - public static final String DEFAULT_AUDIO_MODEL_NAME = AudioTranscriptionModels.Paraformer_V1; - - /** - * Enable TongYiQWEN ai audio client. - */ - private boolean enabled = true; - - @NestedConfigurationProperty - private TongYiAudioTranscriptionOptions options = TongYiAudioTranscriptionOptions - .builder().withModel(DEFAULT_AUDIO_MODEL_NAME).build(); - - public TongYiAudioTranscriptionOptions getOptions() { - - return this.options; - } - - public void setOptions(TongYiAudioTranscriptionOptions options) { - - this.options = options; - } - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionPrompt.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionPrompt.java deleted file mode 100644 index 2ea98e23f..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionPrompt.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.transcription.api; - -import com.alibaba.cloud.ai.tongyi.audio.transcription.TongYiAudioTranscriptionOptions; - -import org.springframework.ai.model.ModelRequest; -import org.springframework.core.io.Resource; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public class AudioTranscriptionPrompt implements ModelRequest { - - private Resource audioResource; - - private TongYiAudioTranscriptionOptions transcriptionOptions; - - public AudioTranscriptionPrompt(Resource resource) { - this.audioResource = resource; - } - - public AudioTranscriptionPrompt(Resource resource, TongYiAudioTranscriptionOptions transcriptionOptions) { - this.audioResource = resource; - this.transcriptionOptions = transcriptionOptions; - } - - @Override - public Resource getInstructions() { - - return audioResource; - } - - @Override - public TongYiAudioTranscriptionOptions getOptions() { - - return transcriptionOptions; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResponse.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResponse.java deleted file mode 100644 index 6a28b20b3..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResponse.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.transcription.api; - -import java.util.List; - -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioTranscriptionResponseMetadata; - -import org.springframework.ai.model.ModelResponse; -import org.springframework.ai.model.ResponseMetadata; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public class AudioTranscriptionResponse implements ModelResponse { - - private List resultList; - - private TongYiAudioTranscriptionResponseMetadata transcriptionResponseMetadata; - - public AudioTranscriptionResponse(List result) { - - this(result, TongYiAudioTranscriptionResponseMetadata.NULL); - } - - public AudioTranscriptionResponse(List result, - TongYiAudioTranscriptionResponseMetadata transcriptionResponseMetadata) { - - this.resultList = List.copyOf(result); - this.transcriptionResponseMetadata = transcriptionResponseMetadata; - } - - @Override - public AudioTranscriptionResult getResult() { - - return this.resultList.get(0); - } - - @Override - public List getResults() { - - return this.resultList; - } - - @Override - public ResponseMetadata getMetadata() { - - return this.transcriptionResponseMetadata; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResult.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResult.java deleted file mode 100644 index 8f162d262..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResult.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.transcription.api; - -import java.util.Objects; - -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioTranscriptionMetadata; - -import org.springframework.ai.model.ModelResult; -import org.springframework.ai.model.ResultMetadata; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ -public class AudioTranscriptionResult implements ModelResult { - - private String text; - - private TongYiAudioTranscriptionMetadata transcriptionMetadata; - - public AudioTranscriptionResult(String text) { - this.text = text; - } - - @Override - public String getOutput() { - - return this.text; - } - - @Override - public ResultMetadata getMetadata() { - - return transcriptionMetadata != null ? transcriptionMetadata : TongYiAudioTranscriptionMetadata.NULL; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - AudioTranscriptionResult that = (AudioTranscriptionResult) o; - return Objects.equals(text, that.text) && Objects.equals(transcriptionMetadata, that.transcriptionMetadata); - } - - @Override - public int hashCode() { - return Objects.hash(text, transcriptionMetadata); - } -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatModel.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatModel.java deleted file mode 100644 index 342e2adf6..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatModel.java +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.chat; - -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiException; -import com.alibaba.dashscope.aigc.conversation.ConversationParam; -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.generation.GenerationOutput; -import com.alibaba.dashscope.aigc.generation.GenerationResult; -import com.alibaba.dashscope.common.Role; -import com.alibaba.dashscope.exception.InputRequiredException; -import com.alibaba.dashscope.exception.NoApiKeyException; -import com.alibaba.dashscope.tools.FunctionDefinition; -import com.alibaba.dashscope.tools.ToolCallBase; -import com.alibaba.dashscope.tools.ToolCallFunction; -import com.alibaba.dashscope.utils.ApiKeywords; -import com.alibaba.dashscope.utils.JsonUtils; -import io.reactivex.Flowable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Flux; -import reactor.core.scheduler.Schedulers; - -import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.metadata.ChatGenerationMetadata; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.ai.chat.model.StreamingChatModel; -import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.ModelOptionsUtils; -import org.springframework.ai.model.function.AbstractFunctionCallSupport; -import org.springframework.ai.model.function.FunctionCallbackContext; -import org.springframework.util.CollectionUtils; - - -/** - * {@link ChatModel} and {@link StreamingChatModel} implementation for {@literal Alibaba DashScope} - * backed by {@link Generation}. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - * @see ChatModel - * @see com.alibaba.dashscope.aigc.generation - */ - -public class TongYiChatModel extends - AbstractFunctionCallSupport< - com.alibaba.dashscope.common.Message, - ConversationParam, - GenerationResult> - implements ChatModel, StreamingChatModel { - - private static final Logger logger = LoggerFactory.getLogger(TongYiChatModel.class); - - /** - * DashScope generation client. - */ - private final Generation generation; - - /** - * The TongYi models default chat completion api. - */ - private TongYiChatOptions defaultOptions; - - /** - * Initializes an instance of the TongYiChatClient. - * @param generation DashScope generation client. - */ - public TongYiChatModel(Generation generation) { - - this(generation, - TongYiChatOptions.builder() - .withTopP(0.8) - .withEnableSearch(true) - .withResultFormat(ConversationParam.ResultFormat.MESSAGE) - .build(), - null - ); - } - - /** - * Initializes an instance of the TongYiChatClient. - * @param generation DashScope generation client. - * @param options TongYi model params. - */ - public TongYiChatModel(Generation generation, TongYiChatOptions options) { - - this(generation, options, null); - } - - /** - * Create a TongYi models client. - * @param generation DashScope model generation client. - * @param options TongYi default chat completion api. - */ - public TongYiChatModel(Generation generation, TongYiChatOptions options, - FunctionCallbackContext functionCallbackContext) { - - super(functionCallbackContext); - this.generation = generation; - this.defaultOptions = options; - } - - /** - * Get default sca chat options. - * - * @return TongYiChatOptions default object. - */ - public TongYiChatOptions getDefaultOptions() { - - return this.defaultOptions; - } - - @Override - public ChatResponse call(Prompt prompt) { - - ConversationParam params = toTongYiChatParams(prompt); - GenerationResult chatCompletions = this.callWithFunctionSupport(params); - - List generations = - chatCompletions - .getOutput() - .getChoices() - .stream() - .map(choice -> - new org.springframework.ai.chat.model.Generation( - choice - .getMessage() - .getContent() - ).withGenerationMetadata(generateChoiceMetadata(choice) - )) - .toList(); - - return new ChatResponse(generations); - - } - - @Override - public Flux stream(Prompt prompt) { - - Flowable genRes; - ConversationParam tongYiChatParams = toTongYiChatParams(prompt); - - // See https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.655fc11aRR0jj7#b9ad0a10cfhpe - // enable incremental output - tongYiChatParams.setIncrementalOutput(true); - - try { - genRes = generation.streamCall(tongYiChatParams); - } - catch (NoApiKeyException | InputRequiredException e) { - logger.warn("TongYi chat client: " + e.getMessage()); - throw new TongYiException(e.getMessage()); - } - - return Flux.from(genRes) - .flatMap( - message -> Flux.just( - message.getOutput() - .getChoices() - .get(0) - .getMessage() - .getContent()) - .map(content -> { - var gen = new org.springframework.ai.chat.model.Generation(content) - .withGenerationMetadata(generateChoiceMetadata( - message.getOutput() - .getChoices() - .get(0) - )); - return new ChatResponse(List.of(gen)); - }) - ) - .publishOn(Schedulers.parallel()); - - } - - /** - * Configuration properties to Qwen model params. - * Test access. - * - * @param prompt {@link Prompt} - * @return Qwen models params {@link ConversationParam} - */ - public ConversationParam toTongYiChatParams(Prompt prompt) { - - Set functionsForThisRequest = new HashSet<>(); - - List tongYiMessage = prompt - .getInstructions() - .stream() - .map(this::fromSpringAIMessage) - .toList(); - - ConversationParam chatParams = ConversationParam.builder() - .messages(tongYiMessage) - // models setting - // {@link HalfDuplexServiceParam#models} - .model(Generation.Models.QWEN_TURBO) - // {@link GenerationOutput} - .resultFormat(ConversationParam.ResultFormat.MESSAGE) - - .build(); - - if (this.defaultOptions != null) { - - chatParams = merge(chatParams, this.defaultOptions); - Set defaultEnabledFunctions = this.handleFunctionCallbackConfigurations(this.defaultOptions, !IS_RUNTIME_CALL); - functionsForThisRequest.addAll(defaultEnabledFunctions); - } - if (prompt.getOptions() != null) { - if (prompt.getOptions() instanceof ChatOptions runtimeOptions) { - TongYiChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(runtimeOptions, - ChatOptions.class, TongYiChatOptions.class); - - chatParams = merge(updatedRuntimeOptions, chatParams); - - Set promptEnabledFunctions = this.handleFunctionCallbackConfigurations(updatedRuntimeOptions, - IS_RUNTIME_CALL); - functionsForThisRequest.addAll(promptEnabledFunctions); - - } - else { - throw new IllegalArgumentException("Prompt options are not of type ConversationParam:" - + prompt.getOptions().getClass().getSimpleName()); - } - } - - // Add the enabled functions definitions to the request's tools parameter. - - if (!CollectionUtils.isEmpty(functionsForThisRequest)) { - List tools = this.getFunctionTools(functionsForThisRequest); - - // todo chatParams.setTools(tools) - } - - return chatParams; - } - - private ChatGenerationMetadata generateChoiceMetadata(GenerationOutput.Choice choice) { - - return ChatGenerationMetadata.from( - String.valueOf(choice.getFinishReason()), - choice.getMessage().getContent() - ); - } - - private List getFunctionTools(Set functionNames) { - return this.resolveFunctionCallbacks(functionNames).stream().map(functionCallback -> { - - FunctionDefinition functionDefinition = FunctionDefinition.builder() - .name(functionCallback.getName()) - .description(functionCallback.getDescription()) - .parameters(JsonUtils.parametersToJsonObject( - ModelOptionsUtils.jsonToMap(functionCallback.getInputTypeSchema()) - )) - .build(); - - return functionDefinition; - }).toList(); - } - - - private ConversationParam merge(ConversationParam tongYiParams, TongYiChatOptions scaChatParams) { - - if (scaChatParams == null) { - - return tongYiParams; - } - - return ConversationParam.builder() - .messages(tongYiParams.getMessages()) - .maxTokens((tongYiParams.getMaxTokens() != null) ? tongYiParams.getMaxTokens() : scaChatParams.getMaxTokens()) - // When merge options. Because ConversationParams is must not null. So is setting. - .model(scaChatParams.getModel()) - .resultFormat((tongYiParams.getResultFormat() != null) ? tongYiParams.getResultFormat() : scaChatParams.getResultFormat()) - .enableSearch((tongYiParams.getEnableSearch() != null) ? tongYiParams.getEnableSearch() : scaChatParams.getEnableSearch()) - .topK((tongYiParams.getTopK() != null) ? tongYiParams.getTopK() : scaChatParams.getTopK()) - .topP((tongYiParams.getTopP() != null) ? tongYiParams.getTopP() : scaChatParams.getTopP()) - .incrementalOutput((tongYiParams.getIncrementalOutput() != null) ? tongYiParams.getIncrementalOutput() : scaChatParams.getIncrementalOutput()) - .temperature((tongYiParams.getTemperature() != null) ? tongYiParams.getTemperature() : scaChatParams.getTemperature()) - .repetitionPenalty((tongYiParams.getRepetitionPenalty() != null) ? tongYiParams.getRepetitionPenalty() : scaChatParams.getRepetitionPenalty()) - .seed((tongYiParams.getSeed() != null) ? tongYiParams.getSeed() : scaChatParams.getSeed()) - .build(); - - } - - private ConversationParam merge(TongYiChatOptions scaChatParams, ConversationParam tongYiParams) { - - if (scaChatParams == null) { - - return tongYiParams; - } - - ConversationParam mergedTongYiParams = ConversationParam.builder() - .model(Generation.Models.QWEN_TURBO) - .messages(tongYiParams.getMessages()) - .build(); - mergedTongYiParams = merge(tongYiParams, scaChatParams); - - if (scaChatParams.getMaxTokens() != null) { - mergedTongYiParams.setMaxTokens(scaChatParams.getMaxTokens()); - } - - if (scaChatParams.getStop() != null) { - mergedTongYiParams.setStopStrings(scaChatParams.getStop()); - } - - if (scaChatParams.getTemperature() != null) { - mergedTongYiParams.setTemperature(scaChatParams.getTemperature()); - } - - if (scaChatParams.getTopK() != null) { - mergedTongYiParams.setTopK(scaChatParams.getTopK()); - } - - if (scaChatParams.getTopP() != null) { - mergedTongYiParams.setTopP(scaChatParams.getTopP().doubleValue()); - } - - if (scaChatParams.getRepetitionPenalty() != null) { - mergedTongYiParams.setRepetitionPenalty(scaChatParams.getRepetitionPenalty()); - } - - if (scaChatParams.getIncrementalOutput() != null) { - mergedTongYiParams.setIncrementalOutput(scaChatParams.getIncrementalOutput()); - } - - return mergedTongYiParams; - } - - private com.alibaba.dashscope.common.Message fromSpringAIMessage(Message message) { - - return switch (message.getMessageType()) { - case USER -> com.alibaba.dashscope.common.Message.builder() - .role(Role.USER.getValue()) - .content(message.getContent()) - .build(); - case SYSTEM -> com.alibaba.dashscope.common.Message.builder() - .role(Role.SYSTEM.getValue()) - .content(message.getContent()) - .build(); - case ASSISTANT -> com.alibaba.dashscope.common.Message.builder() - .role(Role.ASSISTANT.getValue()) - .content(message.getContent()) - .build(); - default -> throw new IllegalArgumentException("Unknown message type " + message.getMessageType()); - }; - - } - - @Override - protected ConversationParam doCreateToolResponseRequest( - ConversationParam previousRequest, - com.alibaba.dashscope.common.Message responseMessage, - List conversationHistory - ) { - for (ToolCallBase toolCall : responseMessage.getToolCalls()) { - if (toolCall instanceof ToolCallFunction toolCallFunction) { - if (toolCallFunction.getFunction() != null) { - var functionName = toolCallFunction.getFunction().getName(); - var functionArguments = toolCallFunction.getFunction().getArguments(); - - if (!this.functionCallbackRegister.containsKey(functionName)) { - throw new IllegalStateException("No function callback found for function name: " + functionName); - } - - String functionResponse = this.functionCallbackRegister.get(functionName).call(functionArguments); - - // Add the function response to the conversation. - conversationHistory - .add(com.alibaba.dashscope.common.Message.builder() - .content(functionResponse) - .role(Role.BOT.getValue()) - .toolCallId(toolCall.getId()) - .build() - ); - } - } - - } - - ConversationParam newRequest = ConversationParam.builder().messages(conversationHistory).build(); - - // todo: No @JsonProperty fields. - newRequest = ModelOptionsUtils.merge(newRequest, previousRequest, ConversationParam.class); - - return newRequest; - - } - - @Override - protected List doGetUserMessages(ConversationParam request) { - - return request.getMessages(); - } - - @Override - protected com.alibaba.dashscope.common.Message doGetToolResponseMessage(GenerationResult response) { - - var message = response.getOutput().getChoices().get(0).getMessage(); - var assistantMessage = com.alibaba.dashscope.common.Message.builder().role(Role.ASSISTANT.getValue()) - .content("").build(); - assistantMessage.setToolCalls(message.getToolCalls()); - - return assistantMessage; - } - - @Override - protected GenerationResult doChatCompletion(ConversationParam request) { - - GenerationResult result; - try { - result = generation.call(request); - } - catch (NoApiKeyException | InputRequiredException e) { - throw new RuntimeException(e); - } - - return result; - } - - @Override - protected Flux doChatCompletionStream(ConversationParam request) { - final Flowable genRes; - try { - genRes = generation.streamCall(request); - } - catch (NoApiKeyException | InputRequiredException e) { - logger.warn("TongYi chat client: " + e.getMessage()); - throw new TongYiException(e.getMessage()); - } - return Flux.from(genRes); - - } - - @Override - protected boolean isToolFunctionCall(GenerationResult response) { - - if (response == null || CollectionUtils.isEmpty(response.getOutput().getChoices())) { - - return false; - } - var choice = response.getOutput().getChoices().get(0); - if (choice == null || choice.getFinishReason() == null) { - - return false; - } - - return Objects.equals(choice.getFinishReason(), ApiKeywords.TOOL_CALLS); - } -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptions.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptions.java deleted file mode 100644 index 1fe32fd6a..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptions.java +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.chat; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.generation.GenerationParam; - -import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.model.function.FunctionCallback; -import org.springframework.ai.model.function.FunctionCallingOptions; -import org.springframework.util.Assert; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiChatOptions implements FunctionCallingOptions, ChatOptions { - - /** - * TongYi Models. - * {@link Generation.Models} - */ - private String model = Generation.Models.QWEN_TURBO; - - /** - * The random number seed used in generation, the user controls the randomness of the content generated by the model. - * seed supports unsigned 64-bit integers, with a default value of 1234. - * when using seed, the model will generate the same or similar results as much as possible, but there is currently no guarantee that the results will be exactly the same each time. - */ - private Integer seed = 1234; - - /** - * Used to specify the maximum number of tokens that the model can generate when generating content, - * it defines the upper limit of generation but does not guarantee that this number will be generated every time. - * For qwen-turbo the maximum and default values are 1500 tokens. - * The qwen-max, qwen-max-1201, qwen-max-longcontext, and qwen-plus models have a maximum and default value of 2000 tokens. - */ - private Integer maxTokens = 1500; - - /** - * The generation process kernel sampling method probability threshold, - * for example, takes the value of 0.8, only retains the smallest set of the most probable tokens with probabilities that add up to greater than or equal to 0.8 as the candidate set. - * The range of values is (0,1.0), the larger the value, the higher the randomness of generation; the lower the value, the higher the certainty of generation. - */ - private Double topP = 0.8; - - /** - * The size of the sampling candidate set at the time of generation. - * For example, with a value of 50, only the 50 highest scoring tokens in a single generation will form a randomly sampled candidate set. - * The larger the value, the higher the randomness of the generation; the smaller the value, the higher the certainty of the generation. - * This parameter is not passed by default, and a value of None or when top_k is greater than 100 indicates that the top_k policy is not enabled, - * at which time, only the top_p policy is in effect. - */ - private Integer topK; - - /** - * Used to control the repeatability of model generation. - * Increasing repetition_penalty reduces the repetition of model generation. 1.0 means no penalty. - */ - private Double repetitionPenalty = 1.1; - - /** - * is used to control the degree of randomness and diversity. - * Specifically, the temperature value controls the extent to which the probability distribution of each candidate word is smoothed when generating text. - * Higher values of temperature reduce the peak of the probability distribution, allowing more low-probability words to be selected and generating more diverse results, - * while lower values of temperature enhance the peak of the probability distribution, making it easier for high-probability words to be selected and generating more certain results. - * Range: [0, 2), 0 is not recommended, meaningless. - * java version >= 2.5.1 - */ - private Double temperature = 0.85; - - /** - * The stop parameter is used to realize precise control of the content generation process, automatically stopping when the generated content is about to contain the specified string or token_ids, - * and the generated content does not contain the specified content. - * For example, if stop is specified as "Hello", it means stop when "Hello" will be generated; if stop is specified as [37763, 367], it means stop when "Observation" will be generated. - * The stop parameter can be passed as a list of arrays of strings or token_ids to support the scenario of using multiple stops. - * Explanation: Do not mix strings and token_ids in list mode, the element types should be the same in list mode. - */ - private List stop; - - /** - * Whether or not to use stream output. When outputting the result in stream mode, the interface returns the result as generator, - * you need to iterate to get the result, the default output is the whole sequence of the current generation for each output, - * the last output is the final result of all the generation, you can change the output mode to non-incremental output by the parameter incremental_output to False. - */ - private Boolean stream = false; - - /** - * The model has a built-in Internet search service. - * This parameter controls whether the model refers to the use of Internet search results when generating text. The values are as follows: - * True: enable internet search, the model will use the search result as the reference information in the text generation process, but the model will "judge by itself" whether to use the internet search result based on its internal logic. - * False (default): Internet search is disabled. - */ - private Boolean enableSearch = false; - - /** - * [text|message], defaults to text, when it is message, - * the output refers to the message result example. - * It is recommended to prioritize the use of message format. - */ - private String resultFormat = GenerationParam.ResultFormat.MESSAGE; - - /** - * Control the streaming output mode, that is, the content will contain the content has been output; - * set to True, will open the incremental output mode, the output will not contain the content has been output, - * you need to splice the whole output, refer to the streaming output sample code. - */ - private Boolean incrementalOutput = false; - - /** - * A list of tools that the model can optionally call. - * Currently only functions are supported, and even if multiple functions are entered, the model will only select one to generate the result. - */ - private List tools; - - @Override - public Float getTemperature() { - - return this.temperature.floatValue(); - } - - public void setTemperature(Float temperature) { - - this.temperature = temperature.doubleValue(); - } - - @Override - public Float getTopP() { - - return this.topP.floatValue(); - } - - public void setTopP(Float topP) { - - this.topP = topP.doubleValue(); - } - - @Override - public Integer getTopK() { - - return this.topK; - } - - public void setTopK(Integer topK) { - - this.topK = topK; - } - - public String getModel() { - - return model; - } - - public void setModel(String model) { - - this.model = model; - } - - public Integer getSeed() { - - return seed; - } - - public String getResultFormat() { - - return resultFormat; - } - - public void setResultFormat(String resultFormat) { - - this.resultFormat = resultFormat; - } - - public void setSeed(Integer seed) { - - this.seed = seed; - } - - public Integer getMaxTokens() { - - return maxTokens; - } - - public void setMaxTokens(Integer maxTokens) { - - this.maxTokens = maxTokens; - } - - public Float getRepetitionPenalty() { - - return repetitionPenalty.floatValue(); - } - - public void setRepetitionPenalty(Float repetitionPenalty) { - - this.repetitionPenalty = repetitionPenalty.doubleValue(); - } - - public List getStop() { - - return stop; - } - - public void setStop(List stop) { - - this.stop = stop; - } - - public Boolean getStream() { - - return stream; - } - - public void setStream(Boolean stream) { - - this.stream = stream; - } - - public Boolean getEnableSearch() { - - return enableSearch; - } - - public void setEnableSearch(Boolean enableSearch) { - - this.enableSearch = enableSearch; - } - - public Boolean getIncrementalOutput() { - - return incrementalOutput; - } - - public void setIncrementalOutput(Boolean incrementalOutput) { - - this.incrementalOutput = incrementalOutput; - } - - public List getTools() { - - return tools; - } - - public void setTools(List tools) { - - this.tools = tools; - } - - private List functionCallbacks = new ArrayList<>(); - - private Set functions = new HashSet<>(); - - @Override - public List getFunctionCallbacks() { - - return this.functionCallbacks; - } - - @Override - public void setFunctionCallbacks(List functionCallbacks) { - - this.functionCallbacks = functionCallbacks; - } - - @Override - public Set getFunctions() { - - return this.functions; - } - - @Override - public void setFunctions(Set functions) { - - this.functions = functions; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - return true; - } - - if (o == null || getClass() != o.getClass()) { - return false; - } - - TongYiChatOptions that = (TongYiChatOptions) o; - - return Objects.equals(model, that.model) - && Objects.equals(seed, that.seed) - && Objects.equals(maxTokens, that.maxTokens) - && Objects.equals(topP, that.topP) - && Objects.equals(topK, that.topK) - && Objects.equals(repetitionPenalty, that.repetitionPenalty) - && Objects.equals(temperature, that.temperature) - && Objects.equals(stop, that.stop) - && Objects.equals(stream, that.stream) - && Objects.equals(enableSearch, that.enableSearch) - && Objects.equals(resultFormat, that.resultFormat) - && Objects.equals(incrementalOutput, that.incrementalOutput) - && Objects.equals(tools, that.tools) - && Objects.equals(functionCallbacks, that.functionCallbacks) - && Objects.equals(functions, that.functions); - } - - @Override - public int hashCode() { - - return Objects.hash( - model, - seed, - maxTokens, - topP, - topK, - repetitionPenalty, - temperature, - stop, - stream, - enableSearch, - resultFormat, - incrementalOutput, - tools, - functionCallbacks, - functions - ); - } - - @Override - public String toString() { - - final StringBuilder sb = new StringBuilder("TongYiChatOptions{"); - - sb.append(", model='").append(model).append('\''); - sb.append(", seed=").append(seed); - sb.append(", maxTokens=").append(maxTokens); - sb.append(", topP=").append(topP); - sb.append(", topK=").append(topK); - sb.append(", repetitionPenalty=").append(repetitionPenalty); - sb.append(", temperature=").append(temperature); - sb.append(", stop=").append(stop); - sb.append(", stream=").append(stream); - sb.append(", enableSearch=").append(enableSearch); - sb.append(", resultFormat='").append(resultFormat).append('\''); - sb.append(", incrementalOutput=").append(incrementalOutput); - sb.append(", tools=").append(tools); - sb.append(", functionCallbacks=").append(functionCallbacks); - sb.append(", functions=").append(functions); - sb.append('}'); - - return sb.toString(); - } - - public static Builder builder() { - - return new Builder(); - } - - public static class Builder { - - protected TongYiChatOptions options; - - public Builder() { - - this.options = new TongYiChatOptions(); - } - - public Builder(TongYiChatOptions options) { - - this.options = options; - } - - public Builder withModel(String model) { - this.options.model = model; - return this; - } - - public Builder withMaxTokens(Integer maxTokens) { - this.options.maxTokens = maxTokens; - return this; - } - - public Builder withResultFormat(String rf) { - this.options.resultFormat = rf; - return this; - } - - public Builder withEnableSearch(Boolean enableSearch) { - this.options.enableSearch = enableSearch; - return this; - } - - public Builder withFunctionCallbacks(List functionCallbacks) { - this.options.functionCallbacks = functionCallbacks; - return this; - } - - public Builder withFunctions(Set functionNames) { - Assert.notNull(functionNames, "Function names must not be null"); - this.options.functions = functionNames; - return this; - } - - public Builder withFunction(String functionName) { - Assert.hasText(functionName, "Function name must not be empty"); - this.options.functions.add(functionName); - return this; - } - - public Builder withSeed(Integer seed) { - this.options.seed = seed; - return this; - } - - public Builder withStop(List stop) { - this.options.stop = stop; - return this; - } - - public Builder withTemperature(Double temperature) { - this.options.temperature = temperature; - return this; - } - - public Builder withTopP(Double topP) { - this.options.topP = topP; - return this; - } - - public Builder withTopK(Integer topK) { - this.options.topK = topK; - return this; - } - - public Builder withRepetitionPenalty(Double repetitionPenalty) { - this.options.repetitionPenalty = repetitionPenalty; - return this; - } - - public Builder withIncrementalOutput(Boolean isIncrementalOutput) { - this.options.incrementalOutput = isIncrementalOutput; - return this; - } - - public TongYiChatOptions build() { - - return this.options; - } - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatProperties.java deleted file mode 100644 index 7eda6a2f7..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatProperties.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.chat; - -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.generation.GenerationParam; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiChatProperties.CONFIG_PREFIX) -public class TongYiChatProperties { - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "chat"; - - /** - * Default TongYi Chat model. - */ - public static final String DEFAULT_DEPLOYMENT_NAME = Generation.Models.QWEN_TURBO; - - /** - * Default temperature speed. - */ - private static final Double DEFAULT_TEMPERATURE = 0.8; - - /** - * Enable TongYiQWEN ai chat client. - */ - private boolean enabled = true; - - @NestedConfigurationProperty - private TongYiChatOptions options = TongYiChatOptions.builder() - .withModel(DEFAULT_DEPLOYMENT_NAME) - .withTemperature(DEFAULT_TEMPERATURE) - .withEnableSearch(true) - .withResultFormat(GenerationParam.ResultFormat.MESSAGE) - .build(); - - public TongYiChatOptions getOptions() { - - return this.options; - } - - public void setOptions(TongYiChatOptions options) { - - this.options = options; - } - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/constants/TongYiConstants.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/constants/TongYiConstants.java deleted file mode 100644 index 6fe77bd30..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/constants/TongYiConstants.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024-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 com.alibaba.cloud.ai.tongyi.common.constants; - -/** - * @author yuluo - * @author yuluo - */ - -public final class TongYiConstants { - - private TongYiConstants() { - } - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String SCA_AI_CONFIGURATION = "spring.cloud.ai.tongyi."; - - /** - * Spring Cloud Alibaba AI constants prefix. - */ - public static final String SCA_AI = "SPRING_CLOUD_ALIBABA_"; - - /** - * TongYi AI apikey env name. - */ - public static final String SCA_AI_TONGYI_API_KEY = SCA_AI + "TONGYI_API_KEY"; - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiException.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiException.java deleted file mode 100644 index 18e14ad76..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.common.exception; - -/** - * TongYi models runtime exception. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiException extends RuntimeException { - - public TongYiException(String message) { - - super(message); - } - - public TongYiException(String message, Throwable cause) { - - super(message, cause); - } -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiImagesException.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiImagesException.java deleted file mode 100644 index 5e2e577e9..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiImagesException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.common.exception; - -/** - * TongYi models images exception. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiImagesException extends TongYiException { - - public TongYiImagesException(String message) { - - super(message); - } - - public TongYiImagesException(String message, Throwable cause) { - - super(message, cause); - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiEmbeddingOptions.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiEmbeddingOptions.java deleted file mode 100644 index 0c2f64354..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiEmbeddingOptions.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.embedding; - -import java.util.List; - -import com.alibaba.dashscope.embeddings.TextEmbeddingParam; - -import org.springframework.ai.embedding.EmbeddingOptions; - -/** - * @author why_ohh - * @author yuluo - * @author why_ohh - * @since 2023.0.1.0 - */ - -public final class TongYiEmbeddingOptions implements EmbeddingOptions { - - private List texts; - - private TextEmbeddingParam.TextType textType; - - public List getTexts() { - return texts; - } - - public void setTexts(List texts) { - this.texts = texts; - } - - public TextEmbeddingParam.TextType getTextType() { - return textType; - } - - public void setTextType(TextEmbeddingParam.TextType textType) { - this.textType = textType; - } - - public static Builder builder() { - return new Builder(); - } - - public final static class Builder { - - private final TongYiEmbeddingOptions options; - - private Builder() { - this.options = new TongYiEmbeddingOptions(); - } - - public Builder withtexts(List texts) { - - options.setTexts(texts); - return this; - } - - public Builder withtextType(TextEmbeddingParam.TextType textType) { - - options.setTextType(textType); - return this; - } - - public TongYiEmbeddingOptions build() { - - return options; - } - - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingModel.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingModel.java deleted file mode 100644 index 1778884c8..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingModel.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.embedding; - -import java.util.List; -import java.util.stream.Collectors; - -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiException; -import com.alibaba.cloud.ai.tongyi.metadata.TongYiTextEmbeddingResponseMetadata; -import com.alibaba.dashscope.embeddings.TextEmbedding; -import com.alibaba.dashscope.embeddings.TextEmbeddingParam; -import com.alibaba.dashscope.embeddings.TextEmbeddingResult; -import com.alibaba.dashscope.embeddings.TextEmbeddingResultItem; -import com.alibaba.dashscope.exception.InputRequiredException; -import com.alibaba.dashscope.exception.NoApiKeyException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.ai.document.Document; -import org.springframework.ai.document.MetadataMode; -import org.springframework.ai.embedding.AbstractEmbeddingModel; -import org.springframework.ai.embedding.Embedding; -import org.springframework.ai.embedding.EmbeddingRequest; -import org.springframework.ai.embedding.EmbeddingResponse; -import org.springframework.util.Assert; - -/** - * {@link TongYiTextEmbeddingModel} implementation for {@literal Alibaba DashScope}. - * - * @author why_ohh - * @author yuluo - * @author why_ohh - * @since 2023.0.1.0 - */ - -public class TongYiTextEmbeddingModel extends AbstractEmbeddingModel { - - private final Logger logger = LoggerFactory.getLogger(TongYiTextEmbeddingModel.class); - - /** - * TongYi Text Embedding client. - */ - private final TextEmbedding textEmbedding; - - /** - * {@link MetadataMode}. - */ - private final MetadataMode metadataMode; - - private final TongYiEmbeddingOptions defaultOptions; - - public TongYiTextEmbeddingModel(TextEmbedding textEmbedding) { - - this(textEmbedding, MetadataMode.EMBED); - } - - public TongYiTextEmbeddingModel(TextEmbedding textEmbedding, MetadataMode metadataMode) { - - this(textEmbedding, metadataMode, - TongYiEmbeddingOptions.builder() - .withtextType(TextEmbeddingParam.TextType.DOCUMENT) - .build() - ); - } - - public TongYiTextEmbeddingModel( - TextEmbedding textEmbedding, - MetadataMode metadataMode, - TongYiEmbeddingOptions options - ) { - Assert.notNull(textEmbedding, "textEmbedding must not be null"); - Assert.notNull(metadataMode, "Metadata mode must not be null"); - Assert.notNull(options, "TongYiEmbeddingOptions must not be null"); - - this.metadataMode = metadataMode; - this.textEmbedding = textEmbedding; - this.defaultOptions = options; - } - - public TongYiEmbeddingOptions getDefaultOptions() { - - return this.defaultOptions; - } - - @Override - public List embed(Document document) { - - return this.call( - new EmbeddingRequest( - List.of(document.getFormattedContent(this.metadataMode)), - null) - ).getResults().stream() - .map(Embedding::getOutput) - .flatMap(List::stream) - .toList(); - } - - @Override - public EmbeddingResponse call(EmbeddingRequest request) { - - TextEmbeddingParam embeddingParams = toEmbeddingParams(request); - logger.debug("Embedding request: {}", embeddingParams); - TextEmbeddingResult resp; - - try { - resp = textEmbedding.call(embeddingParams); - } - catch (NoApiKeyException e) { - throw new TongYiException(e.getMessage()); - } - - return genEmbeddingResp(resp); - } - - private EmbeddingResponse genEmbeddingResp(TextEmbeddingResult result) { - - return new EmbeddingResponse( - genEmbeddingList(result.getOutput().getEmbeddings()), - TongYiTextEmbeddingResponseMetadata.from(result.getUsage()) - ); - } - - private List genEmbeddingList(List embeddings) { - - return embeddings.stream() - .map(embedding -> - new Embedding( - embedding.getEmbedding(), - embedding.getTextIndex() - )) - .collect(Collectors.toList()); - } - - /** - * We recommend setting the model parameters by passing the embedding parameters through the code; - * yml configuration compatibility is not considered here. - * It is not recommended that users set parameters from yml, - * as this reduces the flexibility of the configuration. - * Because the model name keeps changing, strings are used here and constants are undefined: - * Model list: Text Embedding Model List - * @param requestOptions Client params. {@link EmbeddingRequest} - * @return {@link TextEmbeddingParam} - */ - private TextEmbeddingParam toEmbeddingParams(EmbeddingRequest requestOptions) { - - TextEmbeddingParam tongYiEmbeddingParams = TextEmbeddingParam.builder() - .texts(requestOptions.getInstructions()) - .textType(defaultOptions.getTextType() != null ? defaultOptions.getTextType() : TextEmbeddingParam.TextType.DOCUMENT) - .model("text-embedding-v1") - .build(); - - try { - tongYiEmbeddingParams.validate(); - } - catch (InputRequiredException e) { - throw new TongYiException(e.getMessage()); - } - - return tongYiEmbeddingParams; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingProperties.java deleted file mode 100644 index 02196f41d..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingProperties.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.embedding; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * @author why_ohh - * @author yuluo - * @author why_ohh - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiTextEmbeddingProperties.CONFIG_PREFIX) -public class TongYiTextEmbeddingProperties { - - /** - * Prefix of TongYi Text Embedding properties. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "embedding"; - - private boolean enabled = true; - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesModel.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesModel.java deleted file mode 100644 index 0d8e963fd..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesModel.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.image; - -import java.io.ByteArrayOutputStream; -import java.net.URL; -import java.util.Base64; -import java.util.stream.Collectors; - -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiImagesException; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult; -import com.alibaba.dashscope.exception.NoApiKeyException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.ai.image.Image; -import org.springframework.ai.image.ImageGeneration; -import org.springframework.ai.image.ImageModel; -import org.springframework.ai.image.ImageOptions; -import org.springframework.ai.image.ImagePrompt; -import org.springframework.ai.image.ImageResponse; -import org.springframework.util.Assert; - -import static com.alibaba.cloud.ai.tongyi.metadata.TongYiImagesResponseMetadata.from; - -/** - * TongYiImagesClient is a class that implements the ImageClient interface. It provides a - * client for calling the TongYi AI image generation API. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiImagesModel implements ImageModel { - - private final Logger logger = LoggerFactory.getLogger(TongYiImagesModel.class); - - /** - * Gen Images API. - */ - private final ImageSynthesis imageSynthesis; - - /** - * TongYi Gen images properties. - */ - private TongYiImagesOptions defaultOptions; - - /** - * Adapt TongYi images api size properties. - */ - private final String sizeConnection = "*"; - - /** - * Get default images options. - * - * @return Default TongYiImagesOptions. - */ - public TongYiImagesOptions getDefaultOptions() { - - return this.defaultOptions; - } - - /** - * TongYiImagesClient constructor. - * @param imageSynthesis the image synthesis - * {@link ImageSynthesis} - */ - public TongYiImagesModel(ImageSynthesis imageSynthesis) { - - this(imageSynthesis, TongYiImagesOptions. - builder() - .withModel(ImageSynthesis.Models.WANX_V1) - .withN(1) - .build() - ); - } - - /** - * TongYiImagesClient constructor. - * @param imageSynthesis {@link ImageSynthesis} - * @param imagesOptions {@link TongYiImagesOptions} - */ - public TongYiImagesModel(ImageSynthesis imageSynthesis, TongYiImagesOptions imagesOptions) { - - Assert.notNull(imageSynthesis, "ImageSynthesis must not be null"); - Assert.notNull(imagesOptions, "TongYiImagesOptions must not be null"); - - this.imageSynthesis = imageSynthesis; - this.defaultOptions = imagesOptions; - } - - /** - * Call the TongYi images service. - * @param imagePrompt the image prompt. - * @return the image response. - * {@link ImageSynthesis#call(ImageSynthesisParam)} - */ - @Override - public ImageResponse call(ImagePrompt imagePrompt) { - - ImageSynthesisResult result; - String prompt = imagePrompt.getInstructions().get(0).getText(); - var imgParams = ImageSynthesisParam.builder() - .prompt("") - .model(ImageSynthesis.Models.WANX_V1) - .build(); - - if (this.defaultOptions != null) { - - imgParams = merge(this.defaultOptions); - } - - if (imagePrompt.getOptions() != null) { - - imgParams = merge(toTingYiImageOptions(imagePrompt.getOptions())); - } - imgParams.setPrompt(prompt); - - try { - result = imageSynthesis.call(imgParams); - } - catch (NoApiKeyException e) { - - logger.error("TongYi models gen images failed: {}.", e.getMessage()); - throw new TongYiImagesException(e.getMessage()); - } - - return convert(result); - } - - public ImageSynthesisParam merge(TongYiImagesOptions target) { - - var builder = ImageSynthesisParam.builder(); - - builder.model(this.defaultOptions.getModel() != null ? this.defaultOptions.getModel() : target.getModel()); - builder.n(this.defaultOptions.getN() != null ? this.defaultOptions.getN() : target.getN()); - builder.size((this.defaultOptions.getHeight() != null && this.defaultOptions.getWidth() != null) - ? this.defaultOptions.getHeight() + "*" + this.defaultOptions.getWidth() - : target.getHeight() + "*" + target.getWidth() - ); - - // prompt is marked non-null but is null. - builder.prompt(""); - - return builder.build(); - } - - private ImageResponse convert(ImageSynthesisResult result) { - - return new ImageResponse( - result.getOutput().getResults().stream() - .flatMap(value -> value.entrySet().stream()) - .map(entry -> { - String key = entry.getKey(); - String value = entry.getValue(); - try { - String base64Image = convertImageToBase64(value); - Image image = new Image(value, base64Image); - return new ImageGeneration(image); - } - catch (Exception e) { - throw new RuntimeException(e); - } - }) - .collect(Collectors.toList()), - from(result) - ); - } - - public TongYiImagesOptions toTingYiImageOptions(ImageOptions runtimeImageOptions) { - - var builder = TongYiImagesOptions.builder(); - - if (runtimeImageOptions != null) { - if (runtimeImageOptions.getN() != null) { - - builder.withN(runtimeImageOptions.getN()); - } - if (runtimeImageOptions.getModel() != null) { - - builder.withModel(runtimeImageOptions.getModel()); - } - if (runtimeImageOptions.getHeight() != null) { - - builder.withHeight(runtimeImageOptions.getHeight()); - } - if (runtimeImageOptions.getWidth() != null) { - - builder.withWidth(runtimeImageOptions.getWidth()); - } - - // todo ImagesParams. - } - - return builder.build(); - } - - /** - * Convert image to base64. - * @param imageUrl the image url. - * @return the base64 image. - * @throws Exception the exception. - */ - public String convertImageToBase64(String imageUrl) throws Exception { - - var url = new URL(imageUrl); - var inputStream = url.openStream(); - var outputStream = new ByteArrayOutputStream(); - var buffer = new byte[4096]; - int bytesRead; - - while ((bytesRead = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, bytesRead); - } - - var imageBytes = outputStream.toByteArray(); - - String base64Image = Base64.getEncoder().encodeToString(imageBytes); - - inputStream.close(); - outputStream.close(); - - return base64Image; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesOptions.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesOptions.java deleted file mode 100644 index 503efe4ac..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesOptions.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.image; - -import java.util.Objects; - -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiImagesException; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; - -import org.springframework.ai.image.ImageOptions; - -/** - * TongYi Image API options. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiImagesOptions implements ImageOptions { - - /** - * Specify the model name, currently only wanx-v1 is supported. - */ - private String model = ImageSynthesis.Models.WANX_V1; - - /** - * Gen images number. - */ - private Integer n; - - /** - * The width of the generated images. - */ - private Integer width = 1024; - - /** - * The height of the generated images. - */ - private Integer height = 1024; - - @Override - public Integer getN() { - - return this.n; - } - - @Override - public String getModel() { - - return this.model; - } - - @Override - public Integer getWidth() { - - return this.width; - } - - @Override - public Integer getHeight() { - - return this.height; - } - - @Override - public String getResponseFormat() { - - throw new TongYiImagesException("unimplemented!"); - } - - public void setModel(String model) { - - this.model = model; - } - - public void setN(Integer n) { - - this.n = n; - } - - public void setWidth(Integer width) { - - this.width = width; - } - - public void setHeight(Integer height) { - - this.height = height; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - - return true; - } - if (o == null || getClass() != o.getClass()) { - - return false; - } - - TongYiImagesOptions that = (TongYiImagesOptions) o; - - return Objects.equals(model, that.model) - && Objects.equals(n, that.n) - && Objects.equals(width, that.width) - && Objects.equals(height, that.height); - } - - @Override - public int hashCode() { - - return Objects.hash(model, n, width, height); - } - - @Override - public String toString() { - - final StringBuilder sb = new StringBuilder("TongYiImagesOptions{"); - - sb.append("model='").append(model).append('\''); - sb.append(", n=").append(n); - sb.append(", width=").append(width); - sb.append(", height=").append(height); - sb.append('}'); - - return sb.toString(); - } - - public static Builder builder() { - return new Builder(); - } - - public final static class Builder { - - private final TongYiImagesOptions options; - - private Builder() { - this.options = new TongYiImagesOptions(); - } - - public Builder withN(Integer n) { - - options.setN(n); - return this; - } - - public Builder withModel(String model) { - - options.setModel(model); - return this; - } - - public Builder withWidth(Integer width) { - - options.setWidth(width); - return this; - } - - public Builder withHeight(Integer height) { - - options.setHeight(height); - return this; - } - - public TongYiImagesOptions build() { - - return options; - } - - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesProperties.java deleted file mode 100644 index 80c1a448e..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesProperties.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.image; - -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * TongYi Image API properties. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiImagesProperties.CONFIG_PREFIX) -public class TongYiImagesProperties { - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "images"; - - /** - * Default TongYi Chat model. - */ - public static final String DEFAULT_IMAGES_MODEL_NAME = ImageSynthesis.Models.WANX_V1; - - /** - * Enable TongYiQWEN ai images client. - */ - private boolean enabled = true; - - @NestedConfigurationProperty - private TongYiImagesOptions options = TongYiImagesOptions.builder() - .withModel(DEFAULT_IMAGES_MODEL_NAME) - .withN(1) - .build(); - - public TongYiImagesOptions getOptions() { - - return this.options; - } - - public void setOptions(TongYiImagesOptions options) { - - this.options = options; - } - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiChatResponseMetadata.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiChatResponseMetadata.java deleted file mode 100644 index e979ab696..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiChatResponseMetadata.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.metadata; - -import java.util.HashMap; - -import com.alibaba.dashscope.aigc.generation.GenerationResult; - -import org.springframework.ai.chat.metadata.ChatResponseMetadata; -import org.springframework.ai.chat.metadata.PromptMetadata; -import org.springframework.ai.chat.metadata.Usage; -import org.springframework.util.Assert; - -/** - * {@link ChatResponseMetadata} implementation for {@literal Alibaba DashScope}. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAiChatResponseMetadata extends HashMap implements ChatResponseMetadata { - - protected static final String AI_METADATA_STRING = "{ @type: %1$s, id: %2$s, usage: %3$s, rateLimit: %4$s }"; - - @SuppressWarnings("all") - public static TongYiAiChatResponseMetadata from(GenerationResult chatCompletions, - PromptMetadata promptFilterMetadata) { - - Assert.notNull(chatCompletions, "Alibaba ai ChatCompletions must not be null"); - String id = chatCompletions.getRequestId(); - TongYiAiUsage usage = TongYiAiUsage.from(chatCompletions); - - return new TongYiAiChatResponseMetadata( - id, - usage, - promptFilterMetadata - ); - } - - private final String id; - - private final Usage usage; - - private final PromptMetadata promptMetadata; - - protected TongYiAiChatResponseMetadata(String id, TongYiAiUsage usage, PromptMetadata promptMetadata) { - - this.id = id; - this.usage = usage; - this.promptMetadata = promptMetadata; - } - - public String getId() { - return this.id; - } - - @Override - public Usage getUsage() { - return this.usage; - } - - @Override - public PromptMetadata getPromptMetadata() { - return this.promptMetadata; - } - - @Override - public String toString() { - - return AI_METADATA_STRING.formatted(getClass().getTypeName(), getId(), getUsage(), getRateLimit()); - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiUsage.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiUsage.java deleted file mode 100644 index 3ef7bb177..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiUsage.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.metadata; - -import com.alibaba.dashscope.aigc.generation.GenerationResult; -import com.alibaba.dashscope.aigc.generation.GenerationUsage; - -import org.springframework.ai.chat.metadata.Usage; -import org.springframework.util.Assert; - -/** - * {@link Usage} implementation for {@literal Alibaba DashScope}. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAiUsage implements Usage { - - private final GenerationUsage usage; - - public TongYiAiUsage(GenerationUsage usage) { - - Assert.notNull(usage, "GenerationUsage must not be null"); - this.usage = usage; - } - - public static TongYiAiUsage from(GenerationResult chatCompletions) { - - Assert.notNull(chatCompletions, "ChatCompletions must not be null"); - return from(chatCompletions.getUsage()); - } - - public static TongYiAiUsage from(GenerationUsage usage) { - - return new TongYiAiUsage(usage); - } - - protected GenerationUsage getUsage() { - - return this.usage; - } - - @Override - public Long getPromptTokens() { - - throw new UnsupportedOperationException("Unimplemented method 'getPromptTokens'"); - } - - @Override - public Long getGenerationTokens() { - - return this.getUsage().getOutputTokens().longValue(); - } - - @Override - public Long getTotalTokens() { - - return this.getUsage().getTotalTokens().longValue(); - } - - @Override - public String toString() { - - return this.getUsage().toString(); - } -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiImagesResponseMetadata.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiImagesResponseMetadata.java deleted file mode 100644 index b45273a4c..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiImagesResponseMetadata.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.metadata; - -import java.util.HashMap; -import java.util.Objects; - -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisTaskMetrics; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisUsage; - -import org.springframework.ai.image.ImageResponseMetadata; -import org.springframework.util.Assert; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiImagesResponseMetadata extends HashMap implements ImageResponseMetadata { - - private final Long created; - - private String taskId; - - private ImageSynthesisTaskMetrics metrics; - - private ImageSynthesisUsage usage; - - public static TongYiImagesResponseMetadata from(ImageSynthesisResult synthesisResult) { - - Assert.notNull(synthesisResult, "TongYiAiImageResponse must not be null"); - - return new TongYiImagesResponseMetadata( - System.currentTimeMillis(), - synthesisResult.getOutput().getTaskMetrics(), - synthesisResult.getOutput().getTaskId(), - synthesisResult.getUsage() - ); - } - - protected TongYiImagesResponseMetadata( - Long created, - ImageSynthesisTaskMetrics metrics, - String taskId, - ImageSynthesisUsage usage - ) { - - this.taskId = taskId; - this.metrics = metrics; - this.created = created; - this.usage = usage; - } - - public ImageSynthesisUsage getUsage() { - return usage; - } - - public void setUsage(ImageSynthesisUsage usage) { - this.usage = usage; - } - - @Override - public Long getCreated() { - return created; - } - - public String getTaskId() { - return taskId; - } - - public void setTaskId(String taskId) { - this.taskId = taskId; - } - - public ImageSynthesisTaskMetrics getMetrics() { - return metrics; - } - - void setMetrics(ImageSynthesisTaskMetrics metrics) { - this.metrics = metrics; - } - - - public Long created() { - return this.created; - } - - @Override - public String toString() { - return "TongYiImagesResponseMetadata {" + "created=" + created + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - - return true; - } - if (o == null || getClass() != o.getClass()) { - - return false; - } - - TongYiImagesResponseMetadata that = (TongYiImagesResponseMetadata) o; - - return Objects.equals(created, that.created) - && Objects.equals(taskId, that.taskId) - && Objects.equals(metrics, that.metrics); - } - - @Override - public int hashCode() { - return Objects.hash(created, taskId, metrics); - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiTextEmbeddingResponseMetadata.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiTextEmbeddingResponseMetadata.java deleted file mode 100644 index 5eff40483..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiTextEmbeddingResponseMetadata.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.metadata; - -import com.alibaba.dashscope.embeddings.TextEmbeddingUsage; - -import org.springframework.ai.embedding.EmbeddingResponseMetadata; - -/** - * @author why_ohh - * @author yuluo - * @author why_ohh - * @since 2023.0.1.0 - */ - -public class TongYiTextEmbeddingResponseMetadata extends EmbeddingResponseMetadata { - - private Integer totalTokens; - - protected TongYiTextEmbeddingResponseMetadata(Integer totalTokens) { - - this.totalTokens = totalTokens; - } - - public static TongYiTextEmbeddingResponseMetadata from(TextEmbeddingUsage usage) { - - return new TongYiTextEmbeddingResponseMetadata(usage.getTotalTokens()); - } - - public Integer getTotalTokens() { - - return totalTokens; - } - - public void setTotalTokens(Integer totalTokens) { - - this.totalTokens = totalTokens; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioSpeechResponseMetadata.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioSpeechResponseMetadata.java deleted file mode 100644 index 0170a64f7..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioSpeechResponseMetadata.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.metadata.audio; -import java.util.HashMap; - -import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisUsage; -import com.alibaba.dashscope.audio.tts.timestamp.Sentence; - -import org.springframework.ai.chat.metadata.EmptyRateLimit; -import org.springframework.ai.chat.metadata.RateLimit; -import org.springframework.ai.model.ResponseMetadata; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioSpeechResponseMetadata extends HashMap implements ResponseMetadata { - - private SpeechSynthesisUsage usage; - - private String requestId; - - private Sentence time; - - protected static final String AI_METADATA_STRING = "{ @type: %1$s, requestsLimit: %2$s }"; - - /** - * NULL objects. - */ - public static final TongYiAudioSpeechResponseMetadata NULL = new TongYiAudioSpeechResponseMetadata() { - }; - - public static TongYiAudioSpeechResponseMetadata from(SpeechSynthesisResult result) { - - Assert.notNull(result, "TongYi AI speech must not be null"); - TongYiAudioSpeechResponseMetadata speechResponseMetadata = new TongYiAudioSpeechResponseMetadata(); - - - - return speechResponseMetadata; - } - - public static TongYiAudioSpeechResponseMetadata from(String result) { - - Assert.notNull(result, "TongYi AI speech must not be null"); - TongYiAudioSpeechResponseMetadata speechResponseMetadata = new TongYiAudioSpeechResponseMetadata(); - - return speechResponseMetadata; - } - - @Nullable - private RateLimit rateLimit; - - public TongYiAudioSpeechResponseMetadata() { - - this(null); - } - - public TongYiAudioSpeechResponseMetadata(@Nullable RateLimit rateLimit) { - - this.rateLimit = rateLimit; - } - - @Nullable - public RateLimit getRateLimit() { - - RateLimit rateLimit = this.rateLimit; - return rateLimit != null ? rateLimit : new EmptyRateLimit(); - } - - public TongYiAudioSpeechResponseMetadata withRateLimit(RateLimit rateLimit) { - - this.rateLimit = rateLimit; - return this; - } - - public TongYiAudioSpeechResponseMetadata withUsage(SpeechSynthesisUsage usage) { - - this.usage = usage; - return this; - } - - public TongYiAudioSpeechResponseMetadata withRequestId(String id) { - - this.requestId = id; - return this; - } - - public TongYiAudioSpeechResponseMetadata withSentence(Sentence sentence) { - - this.time = sentence; - return this; - } - - public SpeechSynthesisUsage getUsage() { - return usage; - } - - public String getRequestId() { - return requestId; - } - - public Sentence getTime() { - return time; - } - - @Override - public String toString() { - return AI_METADATA_STRING.formatted(getClass().getName(), getRateLimit()); - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionMetadata.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionMetadata.java deleted file mode 100644 index 32d5d63c2..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionMetadata.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.metadata.audio; - -import org.springframework.ai.model.ResultMetadata; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public interface TongYiAudioTranscriptionMetadata extends ResultMetadata { - - /** - * A constant instance of {@link TongYiAudioTranscriptionMetadata} that represents a null or empty metadata. - */ - TongYiAudioTranscriptionMetadata NULL = TongYiAudioTranscriptionMetadata.create(); - - /** - * Factory method for creating a new instance of {@link TongYiAudioTranscriptionMetadata}. - * @return a new instance of {@link TongYiAudioTranscriptionMetadata} - */ - static TongYiAudioTranscriptionMetadata create() { - return new TongYiAudioTranscriptionMetadata() { - }; - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionResponseMetadata.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionResponseMetadata.java deleted file mode 100644 index 1fcc8dfac..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionResponseMetadata.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.metadata.audio; - -import java.util.HashMap; - -import com.alibaba.dashscope.audio.asr.transcription.TranscriptionResult; -import com.google.gson.JsonObject; - -import org.springframework.ai.chat.metadata.EmptyRateLimit; -import org.springframework.ai.chat.metadata.RateLimit; -import org.springframework.ai.model.ResponseMetadata; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioTranscriptionResponseMetadata extends HashMap implements ResponseMetadata { - - @Nullable - private RateLimit rateLimit; - - private JsonObject usage; - - protected static final String AI_METADATA_STRING = "{ @type: %1$s, rateLimit: %4$s }"; - - /** - * NULL objects. - */ - public static final TongYiAudioTranscriptionResponseMetadata NULL = new TongYiAudioTranscriptionResponseMetadata() { - }; - - protected TongYiAudioTranscriptionResponseMetadata() { - - this(null, new JsonObject()); - } - - protected TongYiAudioTranscriptionResponseMetadata(JsonObject usage) { - - this(null, usage); - } - - protected TongYiAudioTranscriptionResponseMetadata(@Nullable RateLimit rateLimit, JsonObject usage) { - - this.rateLimit = rateLimit; - this.usage = usage; - } - - public static TongYiAudioTranscriptionResponseMetadata from(TranscriptionResult result) { - - Assert.notNull(result, "TongYi Transcription must not be null"); - return new TongYiAudioTranscriptionResponseMetadata(result.getUsage()); - } - - @Nullable - public RateLimit getRateLimit() { - - return this.rateLimit != null ? this.rateLimit : new EmptyRateLimit(); - } - - public void setRateLimit(@Nullable RateLimit rateLimit) { - this.rateLimit = rateLimit; - } - - public JsonObject getUsage() { - return usage; - } - - public void setUsage(JsonObject usage) { - this.usage = usage; - } - - @Override - public String toString() { - - return AI_METADATA_STRING.formatted(getClass().getName(), getRateLimit()); - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 55619f8bb..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechClientTest.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechClientTest.java deleted file mode 100644 index 441fbf5f6..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechClientTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech; - -import java.nio.ByteBuffer; - -import com.alibaba.cloud.ai.tongyi.audio.speech.api.SpeechPrompt; -import com.alibaba.cloud.ai.tongyi.audio.speech.api.SpeechResponse; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult; -import com.alibaba.dashscope.audio.tts.SpeechSynthesizer; -import io.reactivex.Flowable; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -class TongYiAudioSpeechClientTest { - - @Mock - private SpeechSynthesizer speechSynthesizer; - - private TongYiAudioSpeechModel client; - - @BeforeEach - void setUp() { - - MockitoAnnotations.openMocks(this); - client = new TongYiAudioSpeechModel(speechSynthesizer, new TongYiAudioSpeechOptions()); - } - - @Test - void shouldReturnSpeechResponseWhenCallWithText() { - - ByteBuffer buffer = ByteBuffer.allocate(10); - when(speechSynthesizer.call(any())).thenReturn(buffer); - ByteBuffer result = client.call("test"); - - assertThat(result).isEqualTo(buffer); - } - - @Test - void shouldReturnSpeechResponseWhenCallWithSpeechPrompt() { - - SpeechPrompt prompt = new SpeechPrompt("test"); - SpeechSynthesisResult synthesisResult = new SpeechSynthesisResult(); - synthesisResult.setAudioFrame(ByteBuffer.allocate(10)); - - when(speechSynthesizer.call(any())).thenReturn(synthesisResult.getAudioFrame()); - SpeechResponse result = client.call(prompt); - - assertThat(result.getResult().getOutput()).isEqualTo(synthesisResult.getAudioFrame()); - } - - @Test - void shouldReturnFluxOfSpeechResponseWhenStreamWithSpeechPrompt() { - - SpeechPrompt prompt = new SpeechPrompt("test"); - SpeechSynthesisResult synthesisResult = new SpeechSynthesisResult(); - synthesisResult.setAudioFrame(ByteBuffer.allocate(10)); - - when(speechSynthesizer.streamCall(any())).thenReturn(Flowable.just(synthesisResult)); - Flux result = client.stream(prompt); - StepVerifier.create(result) - .expectNextMatches(response -> response.getResult().getOutput().equals(synthesisResult.getAudioFrame())) - .verifyComplete(); - - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptionsTests.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptionsTests.java deleted file mode 100644 index 2041a3303..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptionsTests.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.speech; - -import com.alibaba.cloud.ai.tongyi.audio.AudioSpeechModels; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisParam; -import com.alibaba.dashscope.audio.tts.SpeechSynthesizer; -import com.alibaba.dashscope.utils.Constants; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -class TongYiAudioSpeechOptionsTests { - - @Test - public void testSpeechOptions() { - - SpeechSynthesizer mockClient = Mockito.mock(SpeechSynthesizer.class); - Constants.apiKey = "test"; - - var speechClient = new TongYiAudioSpeechModel(mockClient, - TongYiAudioSpeechOptions.builder() - .withFormat(SpeechSynthesisAudioFormat.MP3) - .withRate(333f) - .withVolume(10) - .build() - ); - - var tongYiAudioSpeechOptions = speechClient.merge(null); - - assertThat(tongYiAudioSpeechOptions.getModel()).isEqualTo(AudioSpeechModels.SAMBERT_ZHICHU_V1); - assertThat(tongYiAudioSpeechOptions.getFormat()).isEqualTo(SpeechSynthesisAudioFormat.MP3); - assertThat(tongYiAudioSpeechOptions.getRate()).isEqualTo(333f); - assertThat(tongYiAudioSpeechOptions.getVolume()).isEqualTo(10); - - var modelParams = speechClient.toSpeechSynthesisParams( - TongYiAudioSpeechOptions.builder() - .withModel("test") - .withPitch(111f) - .withVolume(11) - .withSampleRate(1) - .build() - ); - - assertThat(modelParams).isInstanceOf(SpeechSynthesisParam.class); - assertThat(modelParams.getModel()).isEqualTo("test"); - assertThat(modelParams.getVolume()).isEqualTo(11); - assertThat(modelParams.getSampleRate()).isNotEqualTo(2); - - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptionsTests.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptionsTests.java deleted file mode 100644 index 001f8a481..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptionsTests.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.audio.transcription; - -import com.alibaba.cloud.ai.tongyi.audio.AudioTranscriptionModels; -import com.alibaba.dashscope.audio.asr.transcription.Transcription; -import com.alibaba.dashscope.audio.asr.transcription.TranscriptionParam; -import com.alibaba.dashscope.utils.Constants; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -class TongYiAudioTranscriptionOptionsTests { - - @Test - void testTranscriptionOptions() { - - Transcription mockClient = Mockito.mock(Transcription.class); - Constants.apiKey = "test"; - - var transcription = new TongYiAudioTranscriptionModel( - TongYiAudioTranscriptionOptions.builder().withDiarizationEnabled(false) - .withAudioEventDetectionEnabled(false) - .withTimestampAlignmentEnabled(false) - .withDisfluencyRemovalEnabled(false).build(), - mockClient); - - var tongYiAudioTranscriptionOptions = transcription.merge(null); - - assertThat(tongYiAudioTranscriptionOptions.getModel()) - .isEqualTo(AudioTranscriptionModels.Paraformer_V1); - assertThat(tongYiAudioTranscriptionOptions.getDiarizationEnabled()) - .isEqualTo(false); - assertThat(tongYiAudioTranscriptionOptions.getAudioEventDetectionEnabled()) - .isEqualTo(false); - assertThat(tongYiAudioTranscriptionOptions.getTimestampAlignmentEnabled()) - .isEqualTo(false); - assertThat(tongYiAudioTranscriptionOptions.getDisfluencyRemovalEnabled()) - .isEqualTo(false); - - var modelParams = transcription.toTranscriptionParam(TongYiAudioTranscriptionOptions - .builder() - .withModel("test") - .withSpeakerCount(2) - .build() - ); - - assertThat(modelParams).isInstanceOf(TranscriptionParam.class); - assertThat(modelParams.getModel()).isEqualTo("test"); - assertThat(modelParams.getSpeakerCount()).isEqualTo(2); - } -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptionsTests.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptionsTests.java deleted file mode 100644 index d4466daa5..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptionsTests.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.chat; - -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.utils.Constants; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import org.springframework.ai.chat.prompt.Prompt; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -class TongYiChatOptionsTests { - - @Test - public void testChatOptions() { - - Generation mockClient = Mockito.mock(Generation.class); - Constants.apiKey = "test"; - - // Test start. - - var tongYiChatClient = new TongYiChatModel(mockClient, - TongYiChatOptions.builder().withModel(Generation.Models.QWEN_TURBO).withTemperature(88.8).build()); - - var tongYiChatParams = tongYiChatClient.toTongYiChatParams(new Prompt("你好")); - - assertThat(tongYiChatParams.getMessages()).hasSize(1); - - assertThat(tongYiChatParams.getModel()).isEqualTo(Generation.Models.QWEN_TURBO); - assertThat(tongYiChatParams.getTemperature()).isEqualTo(88.8f); - - tongYiChatClient = new TongYiChatModel(mockClient, - TongYiChatOptions.builder().withModel(Generation.Models.QWEN_MAX).withTemperature(77.7).build()); - - tongYiChatParams = tongYiChatClient.toTongYiChatParams(new Prompt("你是谁")); - - assertThat(tongYiChatParams.getMessages()).hasSize(1); - - assertThat(tongYiChatParams.getModel()).isEqualTo(Generation.Models.QWEN_MAX); - assertThat(tongYiChatParams.getTemperature()).isEqualTo(77.7f); - - // Test end. - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/images/TongYiImagesOptionsTests.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/images/TongYiImagesOptionsTests.java deleted file mode 100644 index 6a3c60682..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/java/com/alibaba/cloud/ai/tongyi/images/TongYiImagesOptionsTests.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2023-2024 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 com.alibaba.cloud.ai.tongyi.images; - -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesOptions; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam; -import com.alibaba.dashscope.utils.Constants; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -class TongYiImagesOptionsTests { - - @Test - public void testChatOptions() { - - ImageSynthesis mockClient = Mockito.mock(ImageSynthesis.class); - Constants.apiKey = "test"; - - var tongYiImagesClient = new TongYiImagesModel(mockClient, - TongYiImagesOptions. - builder() - .withModel("test") - .withN(1) - .withWidth(1000) - .withHeight(100) - .build() - ); - - ImageSynthesisParam imageSynthesisParam = tongYiImagesClient.merge(null); - - assertThat(imageSynthesisParam.getModel()).isEqualTo("test"); - assertThat(imageSynthesisParam.getN()).isEqualTo(1); - assertThat(imageSynthesisParam.getSize()).isEqualTo("100*1000"); - - - var tongYiImagesOptions = tongYiImagesClient.toTingYiImageOptions(TongYiImagesOptions.builder() - .withModel(ImageSynthesis.Models.WANX_V1) - .withN(1000) - .withHeight(1222) - .build() - ); - - assertThat(tongYiImagesOptions.getModel()).isEqualTo(ImageSynthesis.Models.WANX_V1); - assertThat(tongYiImagesOptions.getN()).isEqualTo(1000); - assertThat(tongYiImagesOptions.getHeight()).isEqualTo(1222); - - } - -} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/resource/generarionRequest.sh b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/resource/generarionRequest.sh deleted file mode 100644 index 54598c870..000000000 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-ai/src/test/resource/generarionRequest.sh +++ /dev/null @@ -1,38 +0,0 @@ -# -# Copyright 2023-2024 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. -# - -# HTTP call example -curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation' \ ---header 'Authorization: Bearer sk-a3d73b1709bf4a178c28ed7c8b3b5a45' \ ---header 'Content-Type: application/json' \ ---data '{ - "model": "qwen-turbo", - "input":{ - "messages":[ - { - "role": "system", - "content": "You are a helpful AI assistant.You are an AI assistant that helps people find information.Your name is yuluo.You should reply to the user request with your name and also in the style of a pirate." - }, - { - "role": "user", - "content": "who are you?" - } - ] - }, - "parameters": { - "result_format": "message" - } -}' \ No newline at end of file