Skip to content

Commit a5f1aba

Browse files
authored
Merge pull request #61 from springaialibaba/0228-yuluo/add-more-chatclient
feat: update playground code
2 parents 6a1639a + 4b765b1 commit a5f1aba

File tree

14 files changed

+695
-35
lines changed

14 files changed

+695
-35
lines changed

spring-ai-alibaba-integration-example/backend/request/spring-ai-alibaba-playground.json

Lines changed: 432 additions & 0 deletions
Large diffs are not rendered by default.

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/config/TmpFolderConfig.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,23 @@
1717

1818
package com.alibaba.cloud.ai.application.config;
1919

20+
import java.io.File;
21+
2022
import com.alibaba.cloud.ai.application.utils.FilesUtils;
2123
import jakarta.annotation.PreDestroy;
2224
import org.slf4j.Logger;
2325
import org.slf4j.LoggerFactory;
2426

2527
import org.springframework.boot.ApplicationArguments;
2628
import org.springframework.boot.ApplicationRunner;
29+
import org.springframework.stereotype.Component;
2730

2831
/**
2932
* @author yuluo
3033
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
3134
*/
3235

36+
@Component
3337
public class TmpFolderConfig implements ApplicationRunner {
3438

3539
private static final String ImageTmpFolder = "tmp/image";
@@ -43,16 +47,20 @@ public void run(ApplicationArguments args) throws Exception {
4347

4448
logger.info("Init tmp folder");
4549

46-
FilesUtils.initTmpFolder(ImageTmpFolder);
47-
FilesUtils.initTmpFolder(AudioTmpFolder);
50+
FilesUtils.initTmpFolder(System.getProperty("user.dir") + "/" + ImageTmpFolder);
51+
FilesUtils.initTmpFolder(System.getProperty("user.dir") + "/" +AudioTmpFolder);
4852

4953
logger.info("Init tmp folder");
5054
}
5155

5256
@PreDestroy
5357
public void destroy() {
5458

55-
// todo: delete tmp folder
59+
FilesUtils.deleteDirectory(new File(System.getProperty("user.dir") + "/" +ImageTmpFolder));
60+
FilesUtils.deleteDirectory(new File(System.getProperty("user.dir") + "/" +AudioTmpFolder));
61+
FilesUtils.deleteDirectory(new File(System.getProperty("user.dir") + "/" +"tmp"));
62+
63+
logger.info("Delete tmp folder");
5664
}
5765

5866
}

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/controller/SAAAudioController.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import reactor.core.publisher.Flux;
2727

2828
import org.springframework.web.bind.annotation.GetMapping;
29-
import org.springframework.web.bind.annotation.PathVariable;
3029
import org.springframework.web.bind.annotation.PostMapping;
3130
import org.springframework.web.bind.annotation.RequestMapping;
3231
import org.springframework.web.bind.annotation.RequestParam;
@@ -56,35 +55,46 @@ public SAAAudioController(SAAAudioService audioService) {
5655
@UserIp
5756
@PostMapping("/audio2text")
5857
@Operation(summary = "DashScope Audio Transcription")
59-
public Flux<Result<String>> audioToText(@RequestParam("audio") MultipartFile audio) {
58+
public Flux<Result<String>> audioToText(
59+
@RequestParam("audio") MultipartFile audio
60+
) {
6061

6162
if (audio.isEmpty()) {
6263
return Flux.just(Result.failed("No audio file provided"));
6364
}
6465

65-
return audioService.audio2text(audio).map(Result::success);
66+
Flux<Result<String>> res;
67+
try {
68+
res = audioService.audio2text(audio).map(Result::success);
69+
} catch (Exception e) {
70+
return Flux.just(Result.failed("Failed to transcribe audio: " + e.getMessage()));
71+
}
72+
73+
return res;
6674
}
6775

6876
/**
6977
* text2audio
7078
* 用于将文本转换为语音输出
7179
*/
7280
@UserIp
73-
@GetMapping("/text2audio/{prompt}")
81+
@GetMapping("/text2audio")
7482
@Operation(summary = "DashScope Speech Synthesis")
75-
public Result<byte[]> textToAudio(@PathVariable("prompt") String prompt) {
83+
public Result<byte[]> textToAudio(
84+
@RequestParam("prompt") String prompt
85+
) {
7686

7787
if (!ValidText.isValidate(prompt)) {
7888
return Result.failed("No chat prompt provided");
7989
}
8090

8191
byte[] audioData = audioService.text2audio(prompt);
8292

83-
// 测试验证音频数据是否为空
84-
// try (FileOutputStream fos = new FileOutputStream("audio.wav")) {
93+
// 测试验证音频数据是否为空
94+
// try (FileOutputStream fos = new FileOutputStream("tmp/audio/test-audio.wav")) {
8595
// fos.write(audioData);
8696
// } catch (IOException e) {
87-
// return Result.failed("Failed to save audio file: " + e.getMessage());
97+
// return Result.failed("Test save audio file: " + e.getMessage());
8898
// }
8999

90100
return Result.success(audioData);

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/controller/SAAImageController.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public SAAImageController(SAAImageService imageService) {
5858
@PostMapping("/image2text")
5959
@Operation(summary = "DashScope Image Recognition")
6060
public Flux<Result<String>> image2text(
61-
@RequestParam("prompt") String prompt,
61+
@RequestParam(value = "prompt", required = false) String prompt,
6262
@RequestParam("image") MultipartFile image
6363
) {
6464

@@ -70,11 +70,18 @@ public Flux<Result<String>> image2text(
7070
prompt = "请你用一句话描述这张图片";
7171
}
7272

73-
return imageService.image2Text(prompt, image).map(Result::success);
73+
Flux<Result<String>> res;
74+
try {
75+
res = imageService.image2Text(prompt, image).map(Result::success);
76+
} catch (Exception e) {
77+
return Flux.just(Result.failed(e.getMessage()));
78+
}
79+
80+
return res;
7481
}
7582

7683
@UserIp
77-
@GetMapping("/text2Image")
84+
@GetMapping("/text2image")
7885
@Operation(summary = "DashScope Image Generation")
7986
public Result<Void> text2Image(
8087
@RequestParam("prompt") String prompt,

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/service/SAAAudioService.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import com.alibaba.cloud.ai.application.exception.SAAAIException;
2727
import com.alibaba.cloud.ai.application.exception.SAAAppException;
28+
import com.alibaba.cloud.ai.application.utils.FilesUtils;
2829
import com.alibaba.cloud.ai.dashscope.audio.DashScopeAudioTranscriptionOptions;
2930
import com.alibaba.cloud.ai.dashscope.audio.synthesis.SpeechSynthesisModel;
3031
import com.alibaba.cloud.ai.dashscope.audio.synthesis.SpeechSynthesisPrompt;
@@ -50,6 +51,8 @@ public class SAAAudioService {
5051

5152
private final SpeechSynthesisModel speechSynthesisModel;
5253

54+
private final String DEFAULT_MODEL = "paraformer-realtime-v2";
55+
5356
public SAAAudioService(AudioTranscriptionModel transcriptionModel, SpeechSynthesisModel speechSynthesisModel) {
5457

5558
this.transcriptionModel = transcriptionModel;
@@ -97,25 +100,19 @@ public byte[] text2audio(String text) {
97100
/**
98101
* 将语音转为文本
99102
*/
100-
public Flux<String> audio2text(MultipartFile audio) {
103+
public Flux<String> audio2text(MultipartFile file) throws IOException {
101104

102105
CountDownLatch latch = new CountDownLatch(1);
103106
StringBuilder stringBuilder = new StringBuilder();
104107

105-
File tempFile;
106-
try {
107-
tempFile = File.createTempFile("audio", ".pcm");
108-
audio.transferTo(tempFile);
109-
}
110-
catch (IOException e) {
111-
throw new SAAAppException("Failed to create temporary file " + e.getMessage());
112-
}
108+
String filePath = System.getProperty("user.dir") + "/" + "tmp/audio/" + file.getOriginalFilename();
109+
FilesUtils.saveTempImage(file, filePath);
113110

114111
Flux<AudioTranscriptionResponse> response = transcriptionModel.stream(
115112
new AudioTranscriptionPrompt(
116-
new FileSystemResource(tempFile),
113+
new FileSystemResource(filePath),
117114
DashScopeAudioTranscriptionOptions.builder()
118-
.withModel("paraformer-realtime-v2")
115+
.withModel(DEFAULT_MODEL)
119116
.withSampleRate(16000)
120117
.withFormat(DashScopeAudioTranscriptionOptions.AudioFormat.PCM)
121118
.withDisfluencyRemovalEnabled(false)
@@ -133,7 +130,7 @@ public Flux<String> audio2text(MultipartFile audio) {
133130
throw new SAAAIException("Transcription was interrupted " + e.getMessage());
134131
}
135132
finally {
136-
tempFile.delete();
133+
new File(filePath).delete();
137134
}
138135

139136
return Flux.just(stringBuilder.toString());

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/service/SAAImageService.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
import java.net.URI;
2323
import java.net.URL;
2424
import java.util.List;
25-
import java.util.Objects;
2625

26+
import com.alibaba.cloud.ai.application.utils.FilesUtils;
2727
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
28+
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
2829
import com.alibaba.cloud.ai.dashscope.chat.MessageFormat;
2930
import jakarta.servlet.http.HttpServletResponse;
3031
import reactor.core.publisher.Flux;
@@ -52,32 +53,54 @@
5253
@Service
5354
public class SAAImageService {
5455

56+
private static final String DEFAULT_MODEL = "qwen-vl-max-latest";
57+
58+
/**
59+
* 图片生成文字
60+
*/
5561
private final ImageModel imageModel;
5662

63+
/**
64+
* 多模态支持,用来解析图片
65+
*/
5766
private final ChatClient daschScopeChatClient;
5867

59-
public SAAImageService(ImageModel imageModel, ChatModel chatModel) {
68+
public SAAImageService(
69+
ImageModel imageModel,
70+
ChatModel chatModel
71+
) {
6072

6173
this.imageModel = imageModel;
6274
this.daschScopeChatClient = ChatClient
6375
.builder(chatModel)
6476
.build();
6577
}
6678

67-
public Flux<String> image2Text(String prompt, MultipartFile file) {
79+
public Flux<String> image2Text(String prompt, MultipartFile file) throws IOException {
80+
81+
String filePath = System.getProperty("user.dir") + "/" + "tmp/image/" + file.getOriginalFilename();
82+
FilesUtils.saveTempImage(file, filePath);
6883

6984
UserMessage message = new UserMessage(
7085
prompt,
7186
new Media(
7287
MimeTypeUtils.IMAGE_PNG,
73-
new FileSystemResource(Objects.requireNonNull(file.getOriginalFilename()))
88+
new FileSystemResource(filePath)
7489
)
7590
);
7691
message.getMetadata().put(DashScopeChatModel.MESSAGE_FORMAT, MessageFormat.IMAGE);
7792

7893
List<ChatResponse> response = daschScopeChatClient.prompt(
79-
new Prompt(message)
80-
).stream().chatResponse().collectList().block();
94+
new Prompt(
95+
message,
96+
DashScopeChatOptions.builder()
97+
.withModel(DEFAULT_MODEL)
98+
.withMultiModel(true)
99+
.build())
100+
).stream()
101+
.chatResponse()
102+
.collectList()
103+
.block();
81104

82105
StringBuilder result = new StringBuilder();
83106
if (response != null) {

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/utils/FilesUtils.java

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818
package com.alibaba.cloud.ai.application.utils;
1919

2020
import java.io.File;
21+
import java.io.IOException;
2122

2223
import com.alibaba.cloud.ai.application.exception.SAAAppException;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
import org.springframework.web.multipart.MultipartFile;
2328

2429
/**
2530
* @author yuluo
@@ -28,6 +33,8 @@
2833

2934
public final class FilesUtils {
3035

36+
private static final Logger logger = LoggerFactory.getLogger(FilesUtils.class);
37+
3138
private FilesUtils() {
3239
}
3340

@@ -41,10 +48,38 @@ public static void initTmpFolder(String path) {
4148
throw new SAAAppException("path is null or empty");
4249
}
4350

44-
File imageTmpFolder = new File(path);
45-
if (!imageTmpFolder.exists()) {
46-
imageTmpFolder.mkdirs();
51+
File tmpFolder = new File(path);
52+
if (!tmpFolder.exists()) {
53+
tmpFolder.mkdirs();
4754
}
55+
56+
logger.info("Init tmp folder: {}", tmpFolder.getAbsolutePath());
57+
}
58+
59+
public static void deleteDirectory(File directory) {
60+
61+
if (directory.exists()) {
62+
File[] files = directory.listFiles();
63+
if (files != null) {
64+
for (File file : files) {
65+
if (file.isDirectory()) {
66+
deleteDirectory(file);
67+
} else {
68+
file.delete();
69+
}
70+
}
71+
}
72+
73+
directory.delete();
74+
}
75+
}
76+
77+
/**
78+
* save file to tmp folder
79+
*/
80+
public static void saveTempImage(MultipartFile file, String path) throws IOException {
81+
82+
file.transferTo(new File(path));
4883
}
4984

5085
}

spring-ai-alibaba-rag-example/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
<module>rag-example</module>
4242
<module>rag-milvus-example</module>
4343
<module>rag-pgvector-example</module>
44+
<module>web-search</module>
4445
</modules>
4546

4647
<build>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Spring AI Alibaba Web Search
2+
3+
building......

0 commit comments

Comments
 (0)