【代码优化】AI:适配 Spring AI 1.0.6 对 OpenAI 的逻辑

This commit is contained in:
YunaiV 2025-02-23 17:19:19 +08:00
parent 7ef73b7d09
commit 5655ae925c
8 changed files with 33 additions and 27 deletions

View File

@ -98,7 +98,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
ChatResponse chatResponse = chatModel.call(prompt); ChatResponse chatResponse = chatModel.call(prompt);
// 3.4 段式返回 // 3.4 段式返回
String newContent = chatResponse.getResult().getOutput().getContent(); String newContent = chatResponse.getResult().getOutput().getText();
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent)); chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent));
return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)); .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent));
@ -136,7 +136,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
// TODO 注意Schedulers.immediate() 目的是避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题 // TODO 注意Schedulers.immediate() 目的是避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题
StringBuffer contentBuffer = new StringBuffer(); StringBuffer contentBuffer = new StringBuffer();
return streamResponse.map(chunk -> { return streamResponse.map(chunk -> {
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 情况 newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 情况
contentBuffer.append(newContent); contentBuffer.append(newContent);
// 响应结果 // 响应结果

View File

@ -60,7 +60,7 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
List<Document> documents = loader.get(); List<Document> documents = loader.get();
Document document = CollUtil.getFirst(documents); Document document = CollUtil.getFirst(documents);
// 1.2 文档记录入库 // 1.2 文档记录入库
String content = document.getContent(); String content = document.getText();
AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class)
.setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length()) .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length())
.setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus());
@ -77,9 +77,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
List<Document> segments = tokenTextSplitter.apply(documents); List<Document> segments = tokenTextSplitter.apply(documents);
// 2.2 分段内容入库 // 2.2 分段内容入库
List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments, List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments,
segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) segment -> new AiKnowledgeSegmentDO().setContent(segment.getText()).setDocumentId(documentId)
.setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId())
.setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length()) .setTokens(tokenCountEstimator.estimate(segment.getText())).setWordCount(segment.getText().length())
.setStatus(CommonStatusEnum.ENABLE.getStatus())); .setStatus(CommonStatusEnum.ENABLE.getStatus()));
segmentMapper.insertBatch(segmentDOList); segmentMapper.insertBatch(segmentDOList);

View File

@ -106,10 +106,12 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
// 3.1 向量检索 // 3.1 向量检索
List<Document> documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent()) List<Document> documentList = vectorStore.similaritySearch(SearchRequest.builder()
.withTopK(knowledge.getTopK()) .query(reqVO.getContent())
.withSimilarityThreshold(knowledge.getSimilarityThreshold()) .topK(knowledge.getTopK())
.withFilterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build())); .similarityThreshold(knowledge.getSimilarityThreshold())
.filterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build())
.build());
if (CollUtil.isEmpty(documentList)) { if (CollUtil.isEmpty(documentList)) {
return ListUtil.empty(); return ListUtil.empty();
} }

View File

@ -85,7 +85,7 @@ public class AiMindMapServiceImpl implements AiMindMapService {
// 3.2 流式返回 // 3.2 流式返回
StringBuffer contentBuffer = new StringBuffer(); StringBuffer contentBuffer = new StringBuffer();
return streamResponse.map(chunk -> { return streamResponse.map(chunk -> {
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 情况 newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 情况
contentBuffer.append(newContent); contentBuffer.append(newContent);
// 响应结果 // 响应结果

View File

@ -92,7 +92,7 @@ public class AiWriteServiceImpl implements AiWriteService {
// 3.2 流式返回 // 3.2 流式返回
StringBuffer contentBuffer = new StringBuffer(); StringBuffer contentBuffer = new StringBuffer();
return streamResponse.map(chunk -> { return streamResponse.map(chunk -> {
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 情况 newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 情况
contentBuffer.append(newContent); contentBuffer.append(newContent);
// 响应结果 // 响应结果

View File

@ -22,11 +22,16 @@ import java.util.List;
*/ */
public class OpenAIChatModelTests { public class OpenAIChatModelTests {
private final OpenAiApi openAiApi = new OpenAiApi( private static final OpenAiChatModel chatModel = OpenAiChatModel.builder()
"https://api.holdai.top", .openAiApi(OpenAiApi.builder()
"sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf"); .baseUrl("https://api.holdai.top")
private final OpenAiChatModel chatModel = new OpenAiChatModel(openAiApi, .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17") // apiKey
OpenAiChatOptions.builder().model(OpenAiApi.ChatModel.GPT_4_O).build()); .build())
.defaultOptions(OpenAiChatOptions.builder()
.model(OpenAiApi.ChatModel.GPT_4_O) // 模型
.temperature(0.7)
.build())
.build();
@Test @Test
@Disabled @Disabled

View File

@ -5,8 +5,11 @@ import org.junit.jupiter.api.Test;
import org.springframework.ai.image.ImageOptions; import org.springframework.ai.image.ImageOptions;
import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse; import org.springframework.ai.image.ImageResponse;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.OpenAiImageModel; import org.springframework.ai.openai.OpenAiImageModel;
import org.springframework.ai.openai.OpenAiImageOptions; import org.springframework.ai.openai.OpenAiImageOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.openai.api.OpenAiImageApi; import org.springframework.ai.openai.api.OpenAiImageApi;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
@ -17,11 +20,10 @@ import org.springframework.web.client.RestClient;
*/ */
public class OpenAiImageModelTests { public class OpenAiImageModelTests {
private final OpenAiImageApi imageApi = new OpenAiImageApi( private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder()
"https://api.holdai.top", .baseUrl("https://api.holdai.top") // apiKey
"sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf", .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17")
RestClient.builder()); .build());
private final OpenAiImageModel imageModel = new OpenAiImageModel(imageApi);
@Test @Test
@Disabled @Disabled

View File

@ -163,7 +163,7 @@ spring:
zhipuai: # 智谱 AI zhipuai: # 智谱 AI
api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs
openai: # OpenAI 官方 openai: # OpenAI 官方
api-key: sk-yzKea6d8e8212c3bdd99f9f44ced1cae37c097e5aa3BTS7z api-key: sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17
base-url: https://api.gptsapi.net base-url: https://api.gptsapi.net
azure: # OpenAI 微软 azure: # OpenAI 微软
openai: openai:
@ -175,11 +175,8 @@ spring:
model: llama3 model: llama3
stabilityai: stabilityai:
api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx
cloud: dashscope: # 通义千问
ai: api-key: sk-71800982914041848008480000000000
tongyi: # 通义千问
tongyi:
api-key: sk-Zsd81gZYg7
yudao: yudao:
ai: ai: