diff --git a/pom.xml b/pom.xml index d461bb76..57541cb5 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,6 @@ spring-ai-alibaba-core spring-ai-alibaba-starter spring-ai-alibaba-autoconfigure - spring-ai-alibaba-examples community/plugins/spring-ai-alibaba-starter-plugin-time community/plugins/spring-ai-alibaba-starter-plugin-baidusearch community/plugins/spring-ai-alibaba-starter-plugin-bingsearch diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeResponseFormat.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeResponseFormat.java index 5ee4e3d9..73f8470e 100644 --- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeResponseFormat.java +++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeResponseFormat.java @@ -22,10 +22,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * Lets you specify the format of the returned content. - * Valid values: {"type": "text"} or {"type": "json_object"}. - * When set to {"type": "json_object"}, a JSON string in standard format is output. - * Params reference: ... + * Lets you specify the format of the returned content. Valid values: {"type": "text"} or + * {"type": "json_object"}. When set to {"type": "json_object"}, a JSON string in standard + * format is output. Params reference: + * ... * * @author yuluo * @author yuluo @@ -80,6 +80,7 @@ public DashScopeResponseFormat build() { return new DashScopeResponseFormat(this.type); } + } @Override @@ -90,8 +91,10 @@ public String toString() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; DashScopeResponseFormat that = (DashScopeResponseFormat) o; return Objects.equals(type, that.type); } @@ -102,7 +105,8 @@ public int hashCode() { } /** - * DashScopeResponseFormat type. Valid values: {"type": "text"} or {"type": "json_object"}. + * DashScopeResponseFormat type. Valid values: {"type": "text"} or + * {"type": "json_object"}. */ public enum Type { @@ -113,7 +117,8 @@ public enum Type { TEXT, /** - * Enables JSON mode, which guarantees the message the model generates is valid JSON string. + * Enables JSON mode, which guarantees the message the model generates is valid + * JSON string. */ @JsonProperty("json_object") JSON_OBJECT, diff --git a/spring-ai-alibaba-examples/audio-example/src/main/java/com/alibaba/cloud/ai/example/audio/stt/STTController.java b/spring-ai-alibaba-examples/audio-example/src/main/java/com/alibaba/cloud/ai/example/audio/stt/STTController.java index 804f3c80..0f39cf63 100644 --- a/spring-ai-alibaba-examples/audio-example/src/main/java/com/alibaba/cloud/ai/example/audio/stt/STTController.java +++ b/spring-ai-alibaba-examples/audio-example/src/main/java/com/alibaba/cloud/ai/example/audio/stt/STTController.java @@ -53,6 +53,7 @@ public class STTController { private static final String DEFAULT_MODEL_1 = "sensevoice-v1"; private static final String DEFAULT_MODEL_2 = "paraformer-realtime-v2"; + private static final String DEFAULT_MODEL_3 = "paraformer-v2"; private static final String FILE_PATH = "spring-ai-alibaba-examples/audio-example/src/main/resources/stt/count.pcm"; @@ -64,14 +65,9 @@ public class STTController { @GetMapping public DashScopeAudioTranscriptionApi.Response.Output stt() throws MalformedURLException { - AudioTranscriptionResponse response = transcriptionModel.call( - new AudioTranscriptionPrompt( - new UrlResource(AUDIO_RESOURCES_URL), - DashScopeAudioTranscriptionOptions.builder() - .withModel(DEFAULT_MODEL_1) - .build() - ) - ); + AudioTranscriptionResponse response = transcriptionModel + .call(new AudioTranscriptionPrompt(new UrlResource(AUDIO_RESOURCES_URL), + DashScopeAudioTranscriptionOptions.builder().withModel(DEFAULT_MODEL_1).build())); return response.getMetadata().get("output"); } @@ -83,23 +79,16 @@ public String streamSTT() { StringBuilder stringBuilder = new StringBuilder(); Flux response = transcriptionModel - .stream( - new AudioTranscriptionPrompt( - new FileSystemResource(FILE_PATH), - DashScopeAudioTranscriptionOptions.builder() - .withModel(DEFAULT_MODEL_2) - .withSampleRate(16000) - .withFormat(DashScopeAudioTranscriptionOptions.AudioFormat.PCM) - .withDisfluencyRemovalEnabled(false) - .build() - ) - ); - - response.doFinally( - signal -> latch.countDown() - ).subscribe( - resp -> stringBuilder.append(resp.getResult().getOutput()) - ); + .stream(new AudioTranscriptionPrompt(new FileSystemResource(FILE_PATH), + DashScopeAudioTranscriptionOptions.builder() + .withModel(DEFAULT_MODEL_2) + .withSampleRate(16000) + .withFormat(DashScopeAudioTranscriptionOptions.AudioFormat.PCM) + .withDisfluencyRemovalEnabled(false) + .build())); + + response.doFinally(signal -> latch.countDown()) + .subscribe(resp -> stringBuilder.append(resp.getResult().getOutput())); try { latch.await(); @@ -117,21 +106,15 @@ public String asyncSTT() { CountDownLatch latch = new CountDownLatch(1); try { - AudioTranscriptionResponse submitResponse = transcriptionModel.asyncCall( - new AudioTranscriptionPrompt( - new UrlResource(AUDIO_RESOURCES_URL), - DashScopeAudioTranscriptionOptions.builder() - .withModel(DEFAULT_MODEL_3) - .build() - ) - ); - - DashScopeAudioTranscriptionApi.Response.Output submitOutput = Objects.requireNonNull(submitResponse.getMetadata() - .get("output")); + AudioTranscriptionResponse submitResponse = transcriptionModel + .asyncCall(new AudioTranscriptionPrompt(new UrlResource(AUDIO_RESOURCES_URL), + DashScopeAudioTranscriptionOptions.builder().withModel(DEFAULT_MODEL_3).build())); + + DashScopeAudioTranscriptionApi.Response.Output submitOutput = Objects + .requireNonNull(submitResponse.getMetadata().get("output")); String taskId = submitOutput.taskId(); - scheduler.scheduleAtFixedRate( - () -> checkTaskStatus(taskId, stringBuilder, latch), 0, 1, TimeUnit.SECONDS); + scheduler.scheduleAtFixedRate(() -> checkTaskStatus(taskId, stringBuilder, latch), 0, 1, TimeUnit.SECONDS); latch.await(); } @@ -153,8 +136,8 @@ private void checkTaskStatus(String taskId, StringBuilder stringBuilder, CountDo try { AudioTranscriptionResponse fetchResponse = transcriptionModel.fetch(taskId); - DashScopeAudioTranscriptionApi.Response.Output fetchOutput = - Objects.requireNonNull(fetchResponse.getMetadata().get("output")); + DashScopeAudioTranscriptionApi.Response.Output fetchOutput = Objects + .requireNonNull(fetchResponse.getMetadata().get("output")); DashScopeAudioTranscriptionApi.TaskStatus taskStatus = fetchOutput.taskStatus(); if (taskStatus.equals(DashScopeAudioTranscriptionApi.TaskStatus.SUCCEEDED)) { diff --git a/spring-ai-alibaba-examples/audio-example/src/main/java/com/alibaba/cloud/ai/example/audio/tts/TTSController.java b/spring-ai-alibaba-examples/audio-example/src/main/java/com/alibaba/cloud/ai/example/audio/tts/TTSController.java index 8f06fbd1..8d3cb908 100644 --- a/spring-ai-alibaba-examples/audio-example/src/main/java/com/alibaba/cloud/ai/example/audio/tts/TTSController.java +++ b/spring-ai-alibaba-examples/audio-example/src/main/java/com/alibaba/cloud/ai/example/audio/tts/TTSController.java @@ -55,9 +55,7 @@ public class TTSController implements ApplicationRunner { @GetMapping public void tts() throws IOException { - SpeechSynthesisResponse response = speechSynthesisModel.call( - new SpeechSynthesisPrompt(TEXT) - ); + SpeechSynthesisResponse response = speechSynthesisModel.call(new SpeechSynthesisPrompt(TEXT)); File file = new File(FILE_PATH + "output.mp3"); try (FileOutputStream fos = new FileOutputStream(file)) { @@ -72,17 +70,13 @@ public void tts() throws IOException { @GetMapping("/stream") public void streamTTS() { - Flux response = speechSynthesisModel.stream( - new SpeechSynthesisPrompt(TEXT) - ); + Flux response = speechSynthesisModel.stream(new SpeechSynthesisPrompt(TEXT)); CountDownLatch latch = new CountDownLatch(1); File file = new File(FILE_PATH + "output-stream.mp3"); try (FileOutputStream fos = new FileOutputStream(file)) { - response.doFinally( - signal -> latch.countDown() - ).subscribe(synthesisResponse -> { + response.doFinally(signal -> latch.countDown()).subscribe(synthesisResponse -> { ByteBuffer byteBuffer = synthesisResponse.getResult().getOutput().getAudio(); byte[] bytes = new byte[byteBuffer.remaining()]; byteBuffer.get(bytes); diff --git a/spring-ai-alibaba-examples/chatmodel-example/src/main/java/com/alibaba/cloud/ai/example/model/ChatModelController.java b/spring-ai-alibaba-examples/chatmodel-example/src/main/java/com/alibaba/cloud/ai/example/model/ChatModelController.java index 6a8c47fb..2ce12f5c 100644 --- a/spring-ai-alibaba-examples/chatmodel-example/src/main/java/com/alibaba/cloud/ai/example/model/ChatModelController.java +++ b/spring-ai-alibaba-examples/chatmodel-example/src/main/java/com/alibaba/cloud/ai/example/model/ChatModelController.java @@ -51,33 +51,37 @@ public String stream(String input) { StringBuilder res = new StringBuilder(); Flux stream = chatModel.stream(new Prompt(input)); stream.toStream().toList().forEach(resp -> { - res.append(resp.getResult().getOutput().getContent()); + res.append(resp.getResult().getOutput().getContent()); }); return res.toString(); } /** - * Tips: When specifying response types as json, you must include json when entering input, otherwise you will receive an error: - * 400 - {"code":"InvalidParameter","message":"<400> InternalError.Algo.InvalidParameter: 'messages' must contain the word 'json' in some form, to use 'response_format' of type 'json_object'." - * - * For example: In this interface, when mode is true, your input should be "Hello, returned in json format", and the prompt must contain the word json - * request url: ... + * Tips: When specifying response types as json, you must include json when entering + * input, otherwise you will receive an error: 400 - + * {"code":"InvalidParameter","message":"<400> InternalError.Algo.InvalidParameter: + * 'messages' must contain the word 'json' in some form, to use 'response_format' of + * type 'json_object'." * + * For example: In this interface, when mode is true, your input should be "Hello, + * returned in json format", and the prompt must contain the word json request url: ... * @return json string */ @GetMapping("/response_types/{mode}/{input}") - public String responseTypes( - @PathVariable(value = "input") String input, - @PathVariable(value = "mode") Boolean mode - ) { + public String responseTypes(@PathVariable(value = "input") String input, + @PathVariable(value = "mode") Boolean mode) { DashScopeChatOptions.DashscopeChatOptionsBuilder builder = DashScopeChatOptions.builder(); if (!mode) { - builder.withResponseFormat(DashScopeResponseFormat.builder().type(DashScopeResponseFormat.Type.TEXT).build()); - } else { - builder.withResponseFormat(DashScopeResponseFormat.builder().type(DashScopeResponseFormat.Type.JSON_OBJECT).build()); + builder + .withResponseFormat(DashScopeResponseFormat.builder().type(DashScopeResponseFormat.Type.TEXT).build()); + } + else { + builder.withResponseFormat( + DashScopeResponseFormat.builder().type(DashScopeResponseFormat.Type.JSON_OBJECT).build()); } return chatModel.call(new Prompt(input, builder.build())).getResult().getOutput().getContent(); diff --git a/spring-ai-alibaba-examples/chatmodel-example/src/main/java/com/alibaba/cloud/ai/example/model/ImageModelController.java b/spring-ai-alibaba-examples/chatmodel-example/src/main/java/com/alibaba/cloud/ai/example/model/ImageModelController.java index 1f85386d..efb8a61c 100644 --- a/spring-ai-alibaba-examples/chatmodel-example/src/main/java/com/alibaba/cloud/ai/example/model/ImageModelController.java +++ b/spring-ai-alibaba-examples/chatmodel-example/src/main/java/com/alibaba/cloud/ai/example/model/ImageModelController.java @@ -44,11 +44,12 @@ public class ImageModelController { @GetMapping("/image/{input}") public void image(@PathVariable("input") String input, HttpServletResponse response) { - // The options parameter set in this way takes precedence over the parameters in the yaml configuration file. + // The options parameter set in this way takes precedence over the parameters in + // the yaml configuration file. // The default image model is wanx-v1 // ImageOptions options = ImageOptionsBuilder.builder() - // .withModel("wax-2") - // .build(); + // .withModel("wax-2") + // .build(); // ImagePrompt imagePrompt = new ImagePrompt(input, options); ImagePrompt imagePrompt = new ImagePrompt(input); @@ -62,7 +63,8 @@ public void image(@PathVariable("input") String input, HttpServletResponse respo response.setHeader("Content-Type", MediaType.IMAGE_PNG_VALUE); response.getOutputStream().write(in.readAllBytes()); response.getOutputStream().flush(); - } catch (IOException e) { + } + catch (IOException e) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } diff --git a/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingController.java b/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingController.java index 17d77afb..d77bda58 100644 --- a/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingController.java +++ b/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingController.java @@ -27,20 +27,20 @@ @RequestMapping("/ai/func") public class FunctionCallingController { - private final ChatClient chatClient; + private final ChatClient chatClient; - public FunctionCallingController(ChatClient.Builder chatClientBuilder) { - this.chatClient = chatClientBuilder.build(); - } + public FunctionCallingController(ChatClient.Builder chatClientBuilder) { + this.chatClient = chatClientBuilder.build(); + } - @GetMapping("/weather-service") - public String weatherService(String subject) { - return chatClient.prompt() - .function("getWeather", "根据城市查询天气", new MockWeatherService()) - .user(subject) - .call() - .content(); - } + @GetMapping("/weather-service") + public String weatherService(String subject) { + return chatClient.prompt() + .function("getWeather", "根据城市查询天气", new MockWeatherService()) + .user(subject) + .call() + .content(); + } @GetMapping("/order-detail") public String orderDetail() { @@ -53,58 +53,41 @@ public String orderDetail() { @GetMapping("/baidu-search") public String baiduSearch(@RequestParam String query) { - return chatClient.prompt() - .functions("baiduSearchService") - .user(query) - .call() - .content(); + return chatClient.prompt().functions("baiduSearchService").user(query).call().content(); } - + @GetMapping("/bing-search") public String bingSearch(@RequestParam String query) { - return chatClient.prompt() - .functions("bingSearchService") - .user(query) - .call() - .content(); + return chatClient.prompt().functions("bingSearchService").user(query).call().content(); } - @GetMapping("/getTime") - public String getTime(String text) { - return chatClient.prompt() - .functions("getCityTimeFunction") - .user(text) - .call() - .content(); - } + @GetMapping("/getTime") + public String getTime(String text) { + return chatClient.prompt().functions("getCityTimeFunction").user(text).call().content(); + } - @GetMapping("/dingTalk-custom-robot-send") - public String dingTalkCustomRobotSend(String input) { - return chatClient.prompt() - .functions("dingTalkGroupSendMessageByCustomRobotFunction") - .user(String.format("帮我用自定义机器人发送'%s'", input)) - .call() - .content(); - } + @GetMapping("/dingTalk-custom-robot-send") + public String dingTalkCustomRobotSend(String input) { + return chatClient.prompt() + .functions("dingTalkGroupSendMessageByCustomRobotFunction") + .user(String.format("帮我用自定义机器人发送'%s'", input)) + .call() + .content(); + } @GetMapping("/gaode-get-address-weather") public String gaoDeGetAddressWeatherFunction(String input) { return chatClient.prompt() - .functions("gaoDeGetAddressWeatherFunction") - .system("如果用户输入的内容中想询问天气情况,而且还给定了地址,则使用工具获取天气情况,不然提示用户缺少信息") - .user(input) - .call() - .content(); + .functions("gaoDeGetAddressWeatherFunction") + .system("如果用户输入的内容中想询问天气情况,而且还给定了地址,则使用工具获取天气情况,不然提示用户缺少信息") + .user(input) + .call() + .content(); } - @GetMapping("/getWeather") public String getWeather(@RequestParam String text) { - return chatClient.prompt() - .functions("getWeatherService") - .user(text) - .call() - .content(); + return chatClient.prompt().functions("getWeatherService").user(text).call().content(); } } diff --git a/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingExampleApplication.java b/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingExampleApplication.java index b38a4174..17c8dd8f 100644 --- a/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingExampleApplication.java +++ b/spring-ai-alibaba-examples/function-calling-example/src/main/java/com/alibaba/cloud/ai/example/functioncalling/FunctionCallingExampleApplication.java @@ -23,8 +23,7 @@ public static void main(String[] args) { @Bean @Description("根据用户编号和订单编号查询订单信息") - public Function getOrderFunction( - MockOrderService mockOrderService) { + public Function getOrderFunction(MockOrderService mockOrderService) { return mockOrderService::getOrder; } diff --git a/spring-ai-alibaba-examples/multi-model-example/src/main/java/com/alibaba/cloud/ai/example/multi/controller/MultiModelController.java b/spring-ai-alibaba-examples/multi-model-example/src/main/java/com/alibaba/cloud/ai/example/multi/controller/MultiModelController.java index c9e73d27..01dd33bd 100644 --- a/spring-ai-alibaba-examples/multi-model-example/src/main/java/com/alibaba/cloud/ai/example/multi/controller/MultiModelController.java +++ b/spring-ai-alibaba-examples/multi-model-example/src/main/java/com/alibaba/cloud/ai/example/multi/controller/MultiModelController.java @@ -57,108 +57,63 @@ public class MultiModelController { private static final String DEFAULT_MODEL = "qwen-vl-max-latest"; @GetMapping("/image") - public String image( - @RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) - String prompt - ) throws Exception { - - List mediaList = List.of( - new Media( - MimeTypeUtils.IMAGE_PNG, - new URI("https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg").toURL() - ) - ); + public String image(@RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) String prompt) + throws Exception { + + List mediaList = List.of(new Media(MimeTypeUtils.IMAGE_PNG, + new URI("https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg").toURL())); UserMessage message = new UserMessage(prompt, mediaList); message.getMetadata().put(DashScopeChatModel.MESSAGE_FORMAT, MessageFormat.IMAGE); - ChatResponse response = chatModel.call( - new Prompt( - message, - DashScopeChatOptions.builder() - .withModel(DEFAULT_MODEL) - .withMultiModel(true) - .build() - ) - ); + ChatResponse response = chatModel.call(new Prompt(message, + DashScopeChatOptions.builder().withModel(DEFAULT_MODEL).withMultiModel(true).build())); return response.getResult().getOutput().getContent(); } @GetMapping("/video") public String video( - @RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) - String prompt - ) { + @RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) String prompt) { List mediaList = FrameExtraHelper.createMediaList(10); UserMessage message = new UserMessage(prompt, mediaList); message.getMetadata().put(DashScopeChatModel.MESSAGE_FORMAT, MessageFormat.VIDEO); - ChatResponse response = chatModel.call( - new Prompt( - message, - DashScopeChatOptions.builder() - .withModel(DEFAULT_MODEL) - .withMultiModel(true) - .build() - ) - ); + ChatResponse response = chatModel.call(new Prompt(message, + DashScopeChatOptions.builder().withModel(DEFAULT_MODEL).withMultiModel(true).build())); return response.getResult().getOutput().getContent(); } @GetMapping("/image/bin") public String imagesBinary( - @RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) - String prompt - ) { - - UserMessage message = new UserMessage( - prompt, - new Media( - MimeTypeUtils.IMAGE_PNG, - resourceLoader.getResource("classpath:/multimodel/dog_and_girl.jpeg") - )); + @RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) String prompt) { + + UserMessage message = new UserMessage(prompt, new Media(MimeTypeUtils.IMAGE_PNG, + resourceLoader.getResource("classpath:/multimodel/dog_and_girl.jpeg"))); message.getMetadata().put(DashScopeChatModel.MESSAGE_FORMAT, MessageFormat.IMAGE); - ChatResponse response = chatModel.call( - new Prompt( - message, - DashScopeChatOptions.builder() - .withModel(DEFAULT_MODEL) - .withMultiModel(true) - .build() - ) - ); + ChatResponse response = chatModel.call(new Prompt(message, + DashScopeChatOptions.builder().withModel(DEFAULT_MODEL).withMultiModel(true).build())); return response.getResult().getOutput().getContent(); } @GetMapping("/stream/image") public String streamImage( - @RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) - String prompt - ) { - - UserMessage message = new UserMessage( - prompt, - new Media( - MimeTypeUtils.IMAGE_PNG, - resourceLoader.getResource("classpath:/multimodel/dog_and_girl.jpeg") - )); + @RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) String prompt) { + + UserMessage message = new UserMessage(prompt, new Media(MimeTypeUtils.IMAGE_PNG, + resourceLoader.getResource("classpath:/multimodel/dog_and_girl.jpeg"))); message.getMetadata().put(DashScopeChatModel.MESSAGE_FORMAT, MessageFormat.IMAGE); - List response = chatModel.stream( - new Prompt( - message, - DashScopeChatOptions.builder() - .withModel(DEFAULT_MODEL) - .withMultiModel(true) - .build() - ) - ).collectList().block(); + List response = chatModel + .stream(new Prompt(message, + DashScopeChatOptions.builder().withModel(DEFAULT_MODEL).withMultiModel(true).build())) + .collectList() + .block(); StringBuilder result = new StringBuilder(); if (response != null) { diff --git a/spring-ai-alibaba-examples/multi-model-example/src/main/java/com/alibaba/cloud/ai/example/multi/helper/FrameExtraHelper.java b/spring-ai-alibaba-examples/multi-model-example/src/main/java/com/alibaba/cloud/ai/example/multi/helper/FrameExtraHelper.java index 1a9b029c..55ce2c5b 100644 --- a/spring-ai-alibaba-examples/multi-model-example/src/main/java/com/alibaba/cloud/ai/example/multi/helper/FrameExtraHelper.java +++ b/spring-ai-alibaba-examples/multi-model-example/src/main/java/com/alibaba/cloud/ai/example/multi/helper/FrameExtraHelper.java @@ -57,7 +57,8 @@ private FrameExtraHelper() { private static final Map> IMAGE_CACHE = new ConcurrentHashMap<>(); - private static final File videoUrl = new File("spring-ai-alibaba-examples/multi-model-example/src/main/resources/multimodel/video.mp4"); + private static final File videoUrl = new File( + "spring-ai-alibaba-examples/multi-model-example/src/main/resources/multimodel/video.mp4"); private static final String framePath = "spring-ai-alibaba-examples/multi-model-example/src/main/resources/multimodel/frame/"; @@ -71,10 +72,8 @@ public static void getVideoPic() { dir.mkdirs(); } - try ( - FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl.getPath()); - Java2DFrameConverter converter = new Java2DFrameConverter() - ) { + try (FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl.getPath()); + Java2DFrameConverter converter = new Java2DFrameConverter()) { ff.start(); ff.setFormat("mp4"); @@ -86,7 +85,8 @@ public static void getVideoPic() { if (frame.image == null) { continue; } - BufferedImage image = converter.getBufferedImage(frame); ; + BufferedImage image = converter.getBufferedImage(frame); + ; String path = framePath + i + ".png"; File picFile = new File(path); ImageIO.write(image, "png", picFile); @@ -139,12 +139,9 @@ public static List createMediaList(int numberOfImages) { int interval = Math.max(totalFrames / numberOfImages, 1); return IntStream.range(0, numberOfImages) - .mapToObj(i -> imgList.get(i * interval)) - .map(image -> new Media( - MimeType.valueOf("image/png"), - new PathResource(image) - )) - .collect(Collectors.toList()); + .mapToObj(i -> imgList.get(i * interval)) + .map(image -> new Media(MimeType.valueOf("image/png"), new PathResource(image))) + .collect(Collectors.toList()); } } diff --git a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/FileSpanExporterAutoConfiguration.java b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/FileSpanExporterAutoConfiguration.java index fcc606a4..bfcb9fd1 100644 --- a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/FileSpanExporterAutoConfiguration.java +++ b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/FileSpanExporterAutoConfiguration.java @@ -26,13 +26,15 @@ import org.springframework.context.annotation.Configuration; @Configuration -@ConditionalOnClass({OtlpFileSpanExporter.class}) +@ConditionalOnClass({ OtlpFileSpanExporter.class }) public class FileSpanExporterAutoConfiguration { + @Bean @ConditionalOnMissingBean @ConditionalOnEnabledTracing OtlpFileSpanExporter outputSpanExporter() { OtlpFileSpanExporterProvider provider = new OtlpFileSpanExporterProvider(); - return (OtlpFileSpanExporter)provider.createExporter(null); + return (OtlpFileSpanExporter) provider.createExporter(null); } + } diff --git a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/ObservabilityApplication.java b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/ObservabilityApplication.java index 9f49f676..a7d28f53 100644 --- a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/ObservabilityApplication.java +++ b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/ObservabilityApplication.java @@ -25,51 +25,50 @@ @SpringBootApplication public class ObservabilityApplication { - public static void main(String[] args) { - SpringApplication.run(ObservabilityApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(ObservabilityApplication.class, args); + } - @Bean - ChatClient chatClient(ChatClient.Builder builder) { - return builder.build(); - } + @Bean + ChatClient chatClient(ChatClient.Builder builder) { + return builder.build(); + } - @Bean - @ConditionalOnProperty(prefix = DashScopeChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true", - matchIfMissing = true) - public DashScopeChatModel dashscopeChatModel(DashScopeChatProperties chatProperties, List toolFunctionCallbacks, - FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate, - ObjectProvider observationRegistry, DashScopeApi dashScopeApi) { + @Bean + @ConditionalOnProperty(prefix = DashScopeChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true", + matchIfMissing = true) + public DashScopeChatModel dashscopeChatModel(DashScopeChatProperties chatProperties, + List toolFunctionCallbacks, FunctionCallbackContext functionCallbackContext, + RetryTemplate retryTemplate, ObjectProvider observationRegistry, + DashScopeApi dashScopeApi) { - if (!CollectionUtils.isEmpty(toolFunctionCallbacks)) { - chatProperties.getOptions().getFunctionCallbacks().addAll(toolFunctionCallbacks); - } + if (!CollectionUtils.isEmpty(toolFunctionCallbacks)) { + chatProperties.getOptions().getFunctionCallbacks().addAll(toolFunctionCallbacks); + } + + return new DashScopeChatModel(dashScopeApi, chatProperties.getOptions(), functionCallbackContext, retryTemplate, + observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP)); + } - return new DashScopeChatModel(dashScopeApi, chatProperties.getOptions(), functionCallbackContext, retryTemplate, - observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP)); - } } @Controller @ResponseBody class JokeController { - private final ChatClient chatClient; + private final ChatClient chatClient; + + JokeController(ChatClient chatClient) { + this.chatClient = chatClient; + } - JokeController(ChatClient chatClient) { - this.chatClient = chatClient; - } + @GetMapping("/joke") + Map joke() { + var reply = chatClient.prompt().user(""" + tell me a joke. be concise. don't send anything except the joke. + """).call().content(); + Span currentSpan = Span.current(); + return Map.of("joke", reply, "traceId", currentSpan.getSpanContext().getTraceId()); + } - @GetMapping("/joke") - Map joke() { - var reply = chatClient - .prompt() - .user(""" - tell me a joke. be concise. don't send anything except the joke. - """) - .call() - .content(); - Span currentSpan = Span.current(); - return Map.of("joke", reply, "traceId", currentSpan.getSpanContext().getTraceId()); - } } diff --git a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/JsonUtil.java b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/JsonUtil.java index 8522f5d0..4d62545b 100755 --- a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/JsonUtil.java +++ b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/JsonUtil.java @@ -12,15 +12,18 @@ final class JsonUtil { - static final JsonFactory JSON_FACTORY = new JsonFactory(); + static final JsonFactory JSON_FACTORY = new JsonFactory(); - static JsonGenerator create(SegmentedStringWriter stringWriter) { - try { - return JSON_FACTORY.createGenerator(stringWriter); - } catch (IOException e) { - throw new IllegalStateException("Unable to create in-memory JsonGenerator, can't happen.", e); - } - } + static JsonGenerator create(SegmentedStringWriter stringWriter) { + try { + return JSON_FACTORY.createGenerator(stringWriter); + } + catch (IOException e) { + throw new IllegalStateException("Unable to create in-memory JsonGenerator, can't happen.", e); + } + } + + private JsonUtil() { + } - private JsonUtil() {} } diff --git a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/OtlpFileSpanExporter.java b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/OtlpFileSpanExporter.java index e11c117f..4f3714f5 100755 --- a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/OtlpFileSpanExporter.java +++ b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/OtlpFileSpanExporter.java @@ -4,6 +4,7 @@ */ package com.alibaba.cloud.ai.example.observability.exporter.oltp; + import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.io.SegmentedStringWriter; import io.opentelemetry.exporter.internal.otlp.traces.ResourceSpansMarshaler; @@ -17,58 +18,60 @@ import java.util.logging.Logger; /** - * A {@link SpanExporter} which writes {@linkplain SpanData spans} to a {@link Logger} in OTLP JSON - * format. Each log line will include a single {@code ResourceSpans}. + * A {@link SpanExporter} which writes {@linkplain SpanData spans} to a {@link Logger} in + * OTLP JSON format. Each log line will include a single {@code ResourceSpans}. */ public final class OtlpFileSpanExporter implements SpanExporter { - private static final Logger logger = - Logger.getLogger(OtlpFileSpanExporter.class.getName()); + private static final Logger logger = Logger.getLogger(OtlpFileSpanExporter.class.getName()); + + private final AtomicBoolean isShutdown = new AtomicBoolean(); - private final AtomicBoolean isShutdown = new AtomicBoolean(); + /** Returns a new {@link OtlpFileSpanExporter}. */ + public static SpanExporter create() { + return new OtlpFileSpanExporter(); + } - /** Returns a new {@link OtlpFileSpanExporter}. */ - public static SpanExporter create() { - return new OtlpFileSpanExporter(); - } + private OtlpFileSpanExporter() { + } - private OtlpFileSpanExporter() {} + @Override + public CompletableResultCode export(Collection spans) { + if (isShutdown.get()) { + return CompletableResultCode.ofFailure(); + } - @Override - public CompletableResultCode export(Collection spans) { - if (isShutdown.get()) { - return CompletableResultCode.ofFailure(); - } + ResourceSpansMarshaler[] allResourceSpans = ResourceSpansMarshaler.create(spans); + for (ResourceSpansMarshaler resourceSpans : allResourceSpans) { + SegmentedStringWriter sw = new SegmentedStringWriter(JsonUtil.JSON_FACTORY._getBufferRecycler()); + try (JsonGenerator gen = JsonUtil.create(sw)) { + resourceSpans.writeJsonTo(gen); + } + catch (IOException e) { + // Shouldn't happen in practice, just skip it. + continue; + } + try { + logger.log(Level.INFO, sw.getAndClear()); + } + catch (IOException e) { + logger.log(Level.WARNING, "Unable to read OTLP JSON spans", e); + } + } + return CompletableResultCode.ofSuccess(); + } - ResourceSpansMarshaler[] allResourceSpans = ResourceSpansMarshaler.create(spans); - for (ResourceSpansMarshaler resourceSpans : allResourceSpans) { - SegmentedStringWriter sw = - new SegmentedStringWriter(JsonUtil.JSON_FACTORY._getBufferRecycler()); - try (JsonGenerator gen = JsonUtil.create(sw)) { - resourceSpans.writeJsonTo(gen); - } catch (IOException e) { - // Shouldn't happen in practice, just skip it. - continue; - } - try { - logger.log(Level.INFO, sw.getAndClear()); - } catch (IOException e) { - logger.log(Level.WARNING, "Unable to read OTLP JSON spans", e); - } - } - return CompletableResultCode.ofSuccess(); - } + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } - @Override - public CompletableResultCode flush() { - return CompletableResultCode.ofSuccess(); - } + @Override + public CompletableResultCode shutdown() { + if (!isShutdown.compareAndSet(false, true)) { + logger.log(Level.INFO, "Calling shutdown() multiple times."); + } + return CompletableResultCode.ofSuccess(); + } - @Override - public CompletableResultCode shutdown() { - if (!isShutdown.compareAndSet(false, true)) { - logger.log(Level.INFO, "Calling shutdown() multiple times."); - } - return CompletableResultCode.ofSuccess(); - } } diff --git a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/OtlpFileSpanExporterProvider.java b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/OtlpFileSpanExporterProvider.java index 70f2414f..146d776a 100755 --- a/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/OtlpFileSpanExporterProvider.java +++ b/spring-ai-alibaba-examples/observability-example/src/main/java/com/alibaba/cloud/ai/example/observability/exporter/oltp/OtlpFileSpanExporterProvider.java @@ -12,17 +12,20 @@ /** * {@link SpanExporter} SPI implementation for {@link OtlpFileSpanExporter}. * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. + *

+ * This class is internal and is hence not for public use. Its APIs are unstable and can + * change at any time. */ public class OtlpFileSpanExporterProvider implements ConfigurableSpanExporterProvider { - @Override - public SpanExporter createExporter(ConfigProperties config) { - return OtlpFileSpanExporter.create(); - } - @Override - public String getName() { - return "logging-otlp"; - } + @Override + public SpanExporter createExporter(ConfigProperties config) { + return OtlpFileSpanExporter.create(); + } + + @Override + public String getName() { + return "logging-otlp"; + } + } diff --git a/spring-ai-alibaba-examples/output-parser-example/src/main/java/com/alibaba/cloud/ai/example/outparser/controller/OutputParserController.java b/spring-ai-alibaba-examples/output-parser-example/src/main/java/com/alibaba/cloud/ai/example/outparser/controller/OutputParserController.java index 8113d9b0..1c023872 100644 --- a/spring-ai-alibaba-examples/output-parser-example/src/main/java/com/alibaba/cloud/ai/example/outparser/controller/OutputParserController.java +++ b/spring-ai-alibaba-examples/output-parser-example/src/main/java/com/alibaba/cloud/ai/example/outparser/controller/OutputParserController.java @@ -34,43 +34,34 @@ public OutputParserController(ChatClient.Builder builder, ChatModel chatModel) { public ActorsFilms generate(@RequestParam(value = "actor", defaultValue = "Jeff Bridges") String actor) { /** - * Low API: chatModel API - BeanOutputConverter beanOutputConverter = - new BeanOutputConverter<>(ActorsFilms.class); - - String template = """ - Generate the filmography of 5 movies for {actor}. - {format} - """; - return beanOutputConverter.convert( - chatModel.call( - new PromptTemplate( - template, - Map.of("actor", "Tom Hanks", "format", beanOutputConverter.getFormat()) - ).create()).getResult().getOutput().getContent()); + * Low API: chatModel API BeanOutputConverter beanOutputConverter = + * new BeanOutputConverter<>(ActorsFilms.class); + * + * String template = """ Generate the filmography of 5 movies for {actor}. + * {format} """; return beanOutputConverter.convert( chatModel.call( new + * PromptTemplate( template, Map.of("actor", "Tom Hanks", "format", + * beanOutputConverter.getFormat()) + * ).create()).getResult().getOutput().getContent()); */ // ChatClient API return chatClient.prompt() - .user(u -> u.text("Generate the filmography of 5 movies for {actor}.") - .param("actor", "Tom Hanks")) - .call() - .entity(ActorsFilms.class); + .user(u -> u.text("Generate the filmography of 5 movies for {actor}.").param("actor", "Tom Hanks")) + .call() + .entity(ActorsFilms.class); } @GetMapping("/output/stream") - public List generateStream(@RequestParam(value = "actor", defaultValue = "Jeff Bridges") String actor) { + public List generateStream( + @RequestParam(value = "actor", defaultValue = "Jeff Bridges") String actor) { - var converter = new BeanOutputConverter<>(new ParameterizedTypeReference>() { }); + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference>() { + }); - Flux flux = this.chatClient.prompt() - .user(u -> u.text(""" - Generate the filmography for a random actor. - {format} - """) - .param("format", converter.getFormat())) - .stream() - .content(); + Flux flux = this.chatClient.prompt().user(u -> u.text(""" + Generate the filmography for a random actor. + {format} + """).param("format", converter.getFormat())).stream().content(); return converter.convert(String.join("", Objects.requireNonNull(flux.collectList().block()))); } diff --git a/spring-ai-alibaba-examples/output-parser-example/src/main/java/com/alibaba/cloud/ai/example/outparser/entity/ActorsFilms.java b/spring-ai-alibaba-examples/output-parser-example/src/main/java/com/alibaba/cloud/ai/example/outparser/entity/ActorsFilms.java index 0dfb46e9..352c94d9 100644 --- a/spring-ai-alibaba-examples/output-parser-example/src/main/java/com/alibaba/cloud/ai/example/outparser/entity/ActorsFilms.java +++ b/spring-ai-alibaba-examples/output-parser-example/src/main/java/com/alibaba/cloud/ai/example/outparser/entity/ActorsFilms.java @@ -4,32 +4,32 @@ public class ActorsFilms { - private String actor; + private String actor; - private List movies; + private List movies; - public ActorsFilms() { - } + public ActorsFilms() { + } - public String getActor() { - return actor; - } + public String getActor() { + return actor; + } - public void setActor(String actor) { - this.actor = actor; - } + public void setActor(String actor) { + this.actor = actor; + } - public List getMovies() { - return movies; - } + public List getMovies() { + return movies; + } - public void setMovies(List movies) { - this.movies = movies; - } + public void setMovies(List movies) { + this.movies = movies; + } - @Override - public String toString() { - return "ActorsFilms{" + "actor='" + actor + '\'' + ", movies=" + movies + '}'; - } + @Override + public String toString() { + return "ActorsFilms{" + "actor='" + actor + '\'' + ", movies=" + movies + '}'; + } } diff --git a/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/Application.java b/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/Application.java index 8b5634e9..f896ace9 100644 --- a/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/Application.java +++ b/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/Application.java @@ -18,9 +18,8 @@ import org.springframework.core.io.Resource; import org.springframework.web.client.RestClient; - @SpringBootApplication -public class Application { +public class Application { private static final Logger logger = LoggerFactory.getLogger(Application.class); @@ -59,4 +58,5 @@ public ChatMemory chatMemory() { public RestClient.Builder restClientBuilder() { return RestClient.builder(); } + } diff --git a/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/client/AssistantController.java b/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/client/AssistantController.java index 54fdadce..9115bf2b 100644 --- a/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/client/AssistantController.java +++ b/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/client/AssistantController.java @@ -6,7 +6,6 @@ import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; - @RequestMapping("/api/assistant") @RestController public class AssistantController { @@ -17,7 +16,7 @@ public AssistantController(CustomerSupportAssistant agent) { this.agent = agent; } - @RequestMapping(path="/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + @RequestMapping(path = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux chat(String chatId, String userMessage) { return agent.chat(chatId, userMessage); } diff --git a/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/client/BookingController.java b/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/client/BookingController.java index d3ecb979..315cf66b 100644 --- a/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/client/BookingController.java +++ b/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/client/BookingController.java @@ -1,6 +1,5 @@ package ai.spring.demo.ai.playground.client; - import ai.spring.demo.ai.playground.services.BookingTools.BookingDetails; import ai.spring.demo.ai.playground.services.FlightBookingService; import org.springframework.stereotype.Controller; diff --git a/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/services/LoggingAdvisor.java b/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/services/LoggingAdvisor.java index 07da910f..53b1ca75 100644 --- a/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/services/LoggingAdvisor.java +++ b/spring-ai-alibaba-examples/playground-flight-booking/src/main/java/ai/spring/demo/ai/playground/services/LoggingAdvisor.java @@ -13,8 +13,9 @@ public AdvisedRequest adviseRequest(AdvisedRequest request, Map return request; } - @Override - public int getOrder() { - return 0; - } + @Override + public int getOrder() { + return 0; + } + } diff --git a/spring-ai-alibaba-examples/prompt-example/src/main/java/com/alibaba/cloud/ai/example/prompt/PromptTemplateController.java b/spring-ai-alibaba-examples/prompt-example/src/main/java/com/alibaba/cloud/ai/example/prompt/PromptTemplateController.java index 37582a4b..18f0b7ac 100644 --- a/spring-ai-alibaba-examples/prompt-example/src/main/java/com/alibaba/cloud/ai/example/prompt/PromptTemplateController.java +++ b/spring-ai-alibaba-examples/prompt-example/src/main/java/com/alibaba/cloud/ai/example/prompt/PromptTemplateController.java @@ -26,20 +26,19 @@ public class PromptTemplateController { private final ChatClient chatClient; - + private final ConfigurablePromptTemplateFactory configurablePromptTemplateFactory; - - + @Value("classpath:/prompts/joke-prompt.st") private Resource jokeResource; - + @Autowired public PromptTemplateController(ChatClient.Builder builder, ConfigurablePromptTemplateFactory configurablePromptTemplateFactory) { this.chatClient = builder.build(); this.configurablePromptTemplateFactory = configurablePromptTemplateFactory; } - + @GetMapping("/prompt") public AssistantMessage completion(@RequestParam(value = "adjective", defaultValue = "funny") String adjective, @RequestParam(value = "topic", defaultValue = "cows") String topic) { @@ -47,11 +46,12 @@ public AssistantMessage completion(@RequestParam(value = "adjective", defaultVal Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic)); return chatClient.prompt(prompt).call().chatResponse().getResult().getOutput(); } - + /** - * nacos template config [{"name:"test-template","template:"please list the most famous books by this {author}."}] + * nacos template config [{"name:"test-template","template:"please list the most + * famous books by this {author}."}] */ - + @GetMapping("/prompt-template") public AssistantMessage generate(@RequestParam(value = "author") String author) { ConfigurablePromptTemplate template = configurablePromptTemplateFactory.getTemplate("test-template"); @@ -62,7 +62,8 @@ public AssistantMessage generate(@RequestParam(value = "author") String author) Prompt prompt; if (StringUtils.hasText(author)) { prompt = template.create(Map.of("author", author)); - } else { + } + else { prompt = template.create(); } return chatClient.prompt(prompt).call().chatResponse().getResult().getOutput(); diff --git a/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/RagConfiguration.java b/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/RagConfiguration.java index 6452f2a3..ae1cc647 100644 --- a/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/RagConfiguration.java +++ b/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/RagConfiguration.java @@ -23,15 +23,14 @@ import org.springframework.context.annotation.Configuration; /** -* Title Rag configuration.
-* Description Rag configuration.
-* -* @author yuanci.ytb -* @since 1.0.0-M2 -*/ + * Title Rag configuration.
+ * Description Rag configuration.
+ * + * @author yuanci.ytb + * @since 1.0.0-M2 + */ @Configuration public class RagConfiguration { - } diff --git a/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/RagService.java b/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/RagService.java index 0ecdbbb1..5f41e07d 100644 --- a/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/RagService.java +++ b/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/RagService.java @@ -32,4 +32,5 @@ public interface RagService { void importDocuments(); Flux retrieve(String message); + } diff --git a/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/local/LocalRagService.java b/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/local/LocalRagService.java index f2e1b456..90deb2ab 100644 --- a/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/local/LocalRagService.java +++ b/spring-ai-alibaba-examples/rag-example/src/main/java/com/alibaba/cloud/ai/example/rag/local/LocalRagService.java @@ -49,133 +49,133 @@ import java.util.Map; /** -* Title Local rag service.
-* Description Local rag service.
-* -* @author yuanci.ytb -* @since 1.0.0-M2 -*/ + * Title Local rag service.
+ * Description Local rag service.
+ * + * @author yuanci.ytb + * @since 1.0.0-M2 + */ @Service() public class LocalRagService implements RagService { - private static final Logger logger = LoggerFactory.getLogger(LocalRagService.class); + private static final Logger logger = LoggerFactory.getLogger(LocalRagService.class); - private static final String textField = "content"; + private static final String textField = "content"; - private static final String vectorField = "embedding"; + private static final String vectorField = "embedding"; - @Value("classpath:/data/spring_ai_alibaba_quickstart.pdf") - private Resource springAiResource; + @Value("classpath:/data/spring_ai_alibaba_quickstart.pdf") + private Resource springAiResource; - @Value("classpath:/prompts/system-qa.st") - private Resource systemResource; + @Value("classpath:/prompts/system-qa.st") + private Resource systemResource; - private final ChatModel chatModel; + private final ChatModel chatModel; - private final VectorStore vectorStore; - - private final RerankModel rerankModel; - - private final ElasticsearchClient elasticsearchClient; - - private final ElasticsearchVectorStoreProperties options; - - public LocalRagService(ChatModel chatModel, VectorStore vectorStore, RerankModel rerankModel, - ElasticsearchClient elasticsearchClient, ElasticsearchVectorStoreProperties options) { - this.chatModel = chatModel; - this.vectorStore = vectorStore; - this.rerankModel = rerankModel; - this.elasticsearchClient = elasticsearchClient; - this.options = options; - } - - @Override - public void importDocuments() { - // 1. parse document - DocumentReader reader = new PagePdfDocumentReader(springAiResource); - List documents = reader.get(); - logger.info("{} documents loaded", documents.size()); - - // 2. split trunks - List splitDocuments = new TokenTextSplitter().apply(documents); - logger.info("{} documents split", splitDocuments.size()); - - // 3. create embedding and store to vector store - logger.info("create embedding and save to vector store"); - createIndexIfNotExists(); - vectorStore.add(splitDocuments); - } - - public Flux retrieve(String message) { - // Enable hybrid search, both embedding and full text search - SearchRequest searchRequest = SearchRequest.defaults() - .withFilterExpression(new FilterExpressionBuilder().eq(textField, message).build()); - - // Step3 - Retrieve and llm generate - String promptTemplate = getPromptTemplate(systemResource); - ChatClient chatClient = ChatClient.builder(chatModel) - .defaultAdvisors(new RetrievalRerankAdvisor(vectorStore, rerankModel, searchRequest, promptTemplate, 0.1)) - .build(); - - return chatClient.prompt().user(message).stream().chatResponse(); - } - - private void createIndexIfNotExists() { - try { - String indexName = options.getIndexName(); - Integer dimsLength = options.getDimensions(); - - if (StringUtils.isBlank(indexName)) { - throw new IllegalArgumentException("Elastic search index name must be provided"); - } - - boolean exists = elasticsearchClient.indices().exists(idx -> idx.index(indexName)).value(); - if (exists) { - logger.debug("Index {} already exists. Skipping creation.", indexName); - return; - } - - String similarityAlgo = options.getSimilarity().name(); - IndexSettings indexSettings = IndexSettings - .of(settings -> settings.numberOfShards(String.valueOf(1)).numberOfReplicas(String.valueOf(1))); - - // Maybe using json directly? - Map properties = new HashMap<>(); - properties.put(vectorField, Property.of(property -> property.denseVector( - DenseVectorProperty.of(dense -> dense.index(true).dims(dimsLength).similarity(similarityAlgo))))); - properties.put(textField, Property.of(property -> property.text(TextProperty.of(t -> t)))); - - Map metadata = new HashMap<>(); - metadata.put("ref_doc_id", Property.of(property -> property.keyword(KeywordProperty.of(k -> k)))); - - properties.put("metadata", - Property.of(property -> property.object(ObjectProperty.of(op -> op.properties(metadata))))); - - CreateIndexResponse indexResponse = elasticsearchClient.indices() - .create(createIndexBuilder -> createIndexBuilder.index(indexName) - .settings(indexSettings) - .mappings(TypeMapping.of(mappings -> mappings.properties(properties)))); - - if (!indexResponse.acknowledged()) { - throw new RuntimeException("failed to create index"); - } - - logger.info("create elasticsearch index {} successfully", indexName); - } - catch (IOException e) { - logger.error("failed to create index", e); - throw new RuntimeException(e); - } - } - - private String getPromptTemplate(Resource systemResource) { - try { - return systemResource.getContentAsString(StandardCharsets.UTF_8); - } - catch (IOException e) { - throw new RuntimeException(e); - } - } + private final VectorStore vectorStore; + + private final RerankModel rerankModel; + + private final ElasticsearchClient elasticsearchClient; + + private final ElasticsearchVectorStoreProperties options; + + public LocalRagService(ChatModel chatModel, VectorStore vectorStore, RerankModel rerankModel, + ElasticsearchClient elasticsearchClient, ElasticsearchVectorStoreProperties options) { + this.chatModel = chatModel; + this.vectorStore = vectorStore; + this.rerankModel = rerankModel; + this.elasticsearchClient = elasticsearchClient; + this.options = options; + } + + @Override + public void importDocuments() { + // 1. parse document + DocumentReader reader = new PagePdfDocumentReader(springAiResource); + List documents = reader.get(); + logger.info("{} documents loaded", documents.size()); + + // 2. split trunks + List splitDocuments = new TokenTextSplitter().apply(documents); + logger.info("{} documents split", splitDocuments.size()); + + // 3. create embedding and store to vector store + logger.info("create embedding and save to vector store"); + createIndexIfNotExists(); + vectorStore.add(splitDocuments); + } + + public Flux retrieve(String message) { + // Enable hybrid search, both embedding and full text search + SearchRequest searchRequest = SearchRequest.defaults() + .withFilterExpression(new FilterExpressionBuilder().eq(textField, message).build()); + + // Step3 - Retrieve and llm generate + String promptTemplate = getPromptTemplate(systemResource); + ChatClient chatClient = ChatClient.builder(chatModel) + .defaultAdvisors(new RetrievalRerankAdvisor(vectorStore, rerankModel, searchRequest, promptTemplate, 0.1)) + .build(); + + return chatClient.prompt().user(message).stream().chatResponse(); + } + + private void createIndexIfNotExists() { + try { + String indexName = options.getIndexName(); + Integer dimsLength = options.getDimensions(); + + if (StringUtils.isBlank(indexName)) { + throw new IllegalArgumentException("Elastic search index name must be provided"); + } + + boolean exists = elasticsearchClient.indices().exists(idx -> idx.index(indexName)).value(); + if (exists) { + logger.debug("Index {} already exists. Skipping creation.", indexName); + return; + } + + String similarityAlgo = options.getSimilarity().name(); + IndexSettings indexSettings = IndexSettings + .of(settings -> settings.numberOfShards(String.valueOf(1)).numberOfReplicas(String.valueOf(1))); + + // Maybe using json directly? + Map properties = new HashMap<>(); + properties.put(vectorField, Property.of(property -> property.denseVector( + DenseVectorProperty.of(dense -> dense.index(true).dims(dimsLength).similarity(similarityAlgo))))); + properties.put(textField, Property.of(property -> property.text(TextProperty.of(t -> t)))); + + Map metadata = new HashMap<>(); + metadata.put("ref_doc_id", Property.of(property -> property.keyword(KeywordProperty.of(k -> k)))); + + properties.put("metadata", + Property.of(property -> property.object(ObjectProperty.of(op -> op.properties(metadata))))); + + CreateIndexResponse indexResponse = elasticsearchClient.indices() + .create(createIndexBuilder -> createIndexBuilder.index(indexName) + .settings(indexSettings) + .mappings(TypeMapping.of(mappings -> mappings.properties(properties)))); + + if (!indexResponse.acknowledged()) { + throw new RuntimeException("failed to create index"); + } + + logger.info("create elasticsearch index {} successfully", indexName); + } + catch (IOException e) { + logger.error("failed to create index", e); + throw new RuntimeException(e); + } + } + + private String getPromptTemplate(Resource systemResource) { + try { + return systemResource.getContentAsString(StandardCharsets.UTF_8); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } } \ No newline at end of file