init ai backend

This commit is contained in:
Chuck1sn
2025-05-20 21:35:45 +08:00
parent f5a57f9d69
commit f103993960
11 changed files with 156 additions and 19 deletions

View File

@@ -0,0 +1,33 @@
package com.zl.mjga.config.ai;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.service.AiServices;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@RequiredArgsConstructor
public class ChatModelConfig {
private final DeepSeekConfiguration deepSeekConfiguration;
@Bean
public OpenAiStreamingChatModel deepSeekChatModel() {
return OpenAiStreamingChatModel.builder()
.baseUrl(deepSeekConfiguration.getBaseUrl())
.apiKey(deepSeekConfiguration.getApiKey())
.modelName(deepSeekConfiguration.getModelName())
.build();
}
@Bean
public DeepSeekChatAssistant deepSeekChatAssistant(OpenAiStreamingChatModel deepSeekChatModel) {
return AiServices.builder(DeepSeekChatAssistant.class)
.streamingChatModel(deepSeekChatModel)
.systemMessageProvider(chatMemoryId -> "你是一个叫做「知路 AI」的企业级 AI 助手,能帮助用户回答任何问题。")
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10))
.build();
}
}

View File

@@ -0,0 +1,12 @@
package com.zl.mjga.config.ai;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.memory.ChatMemoryAccess;
public interface DeepSeekChatAssistant extends ChatMemoryAccess {
@SystemMessage("You are a good friend of mine. Answer using slang.")
TokenStream chat(@MemoryId String memoryId, @UserMessage String userMessage);
}

View File

@@ -0,0 +1,21 @@
package com.zl.mjga.config.ai;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "deep-seek")
public class DeepSeekConfiguration {
private String baseUrl;
private String apiKey;
private Prompt prompt;
private String modelName;
@Data
public static class Prompt {
private String system;
}
}

View File

@@ -39,6 +39,7 @@ public class WebSecurityConfig {
return new OrRequestMatcher(
new AntPathRequestMatcher("/auth/sign-in", HttpMethod.POST.name()),
new AntPathRequestMatcher("/auth/sign-up", HttpMethod.POST.name()),
new AntPathRequestMatcher("/ai/**", HttpMethod.POST.name()),
new AntPathRequestMatcher("/v3/api-docs/**", HttpMethod.GET.name()),
new AntPathRequestMatcher("/swagger-ui/**", HttpMethod.GET.name()),
new AntPathRequestMatcher("/swagger-ui.html", HttpMethod.GET.name()),

View File

@@ -0,0 +1,41 @@
package com.zl.mjga.controller;
import com.zl.mjga.service.DeepSeekAiService;
import dev.langchain4j.service.TokenStream;
import java.time.Duration;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;
@RestController
@RequestMapping("/ai")
@RequiredArgsConstructor
@Slf4j
public class AiController {
private final DeepSeekAiService deepSeekAiService;
@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chat(@RequestBody String userMessage) {
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
TokenStream chat = deepSeekAiService.chat("123", userMessage);
chat.onPartialResponse(sink::tryEmitNext)
.onCompleteResponse(
r -> {
sink.tryEmitNext("[DONE]");
sink.tryEmitComplete();
})
.onError(sink::tryEmitError)
.start();
return sink.asFlux()
.timeout(Duration.ofSeconds(60))
.onErrorResume(e -> Flux.just("Timeout occurred"));
}
}

View File

@@ -1,6 +1,5 @@
package com.zl.mjga.dto.urp;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.OffsetDateTime;
import java.util.LinkedList;

View File

@@ -0,0 +1,19 @@
package com.zl.mjga.service;
import com.zl.mjga.config.ai.DeepSeekChatAssistant;
import dev.langchain4j.service.TokenStream;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
@Slf4j
public class DeepSeekAiService {
private final DeepSeekChatAssistant deepSeekChatAssistant;
public TokenStream chat(String sessionIdentifier, String userMessage) {
return deepSeekChatAssistant.chat(sessionIdentifier, userMessage);
}
}