Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f4d2585

Browse files
committed
refactor(core): 优化流处理和支持 VUE_PROJECT 类型代码生成
- 引入 JsonMessageStreamHandler 处理复杂的 VUE_PROJECT 流响应,支持工具调用信息 - 添加 SimpleTextStreamHandler 处理传统 HTML 和 MULTI_FILE 类型文本流 - 新增 StreamHandlerExecutor 统一路由不同类型流处理器 - 在 AppServiceImpl 中集成流处理执行器,重构聊天历史记录处理逻辑 - 实现 VueProjectBuilder,支持 Vue 项目异步构建和命令执行(npm install/build) - 在代码部署逻辑中新增 Vue 项目构建及 dist 目录校验流程 - 调整 AiCodeGeneratorFacade 流处理代码,简化流式转换及错误日志 - 调整配置类 ReasoningStreamingChatModelConfig,支持是否为推理模型的动态配置 - 修正应用创建时的代码生成类型为 VUE_PROJECT - 更新 VUE_PROJECT 项目系统提示,简化技术栈说明,强调生成计划行数和输出约束 - 取消 langchain4j-open-ai 依赖,替换为 langchain4j-open-ai-spring-boot-starter 1.12.2-beta22 版本 - 优化 ChatHistoryService 和其实现类,统一使用 ChatMemory,强化查询及删除逻辑 - 优化代码格式和命名,修正流中变量名大小写错误及多余空行
1 parent e00603d commit f4d2585

14 files changed

Lines changed: 531 additions & 184 deletions

pom.xml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,24 +136,23 @@
136136
<artifactId>spring-boot-starter-aop</artifactId>
137137
</dependency>
138138
<!-- langchain4j open-ai 依赖 -->
139-
<dependency>
140-
<groupId>dev.langchain4j</groupId>
141-
<artifactId>langchain4j-open-ai</artifactId>
142-
<version>1.12.1</version>
143-
<exclusions>
144-
<exclusion>
145-
<groupId>dev.langchain4j</groupId>
146-
<artifactId>langchain4j-http-client-jdk</artifactId>
147-
</exclusion>
148-
</exclusions>
149-
</dependency>
139+
<!-- <dependency>-->
140+
<!-- <groupId>dev.langchain4j</groupId>-->
141+
<!-- <artifactId>langchain4j-open-ai</artifactId>-->
142+
<!-- <version>1.12.1</version>-->
143+
<!-- <exclusions>-->
144+
<!-- <exclusion>-->
145+
<!-- <groupId>dev.langchain4j</groupId>-->
146+
<!-- <artifactId>langchain4j-http-client-jdk</artifactId>-->
147+
<!-- </exclusion>-->
148+
<!-- </exclusions>-->
149+
<!-- </dependency>-->
150150
<!-- langchain4j Spring Boot启动器 -->
151151
<!-- Source: https://mvnrepository.com/artifact/dev.langchain4j/langchain4j-open-ai-spring-boot-starter -->
152152
<dependency>
153153
<groupId>dev.langchain4j</groupId>
154154
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
155155
<version>1.12.2-beta22</version>
156-
<scope>test</scope>
157156
</dependency>
158157
<!-- langchain4j 核心依赖 -->
159158
<dependency>

src/main/java/com/hbpu/aicodebackend/ai/AiCodeGeneratorServiceFactory.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.github.benmanes.caffeine.cache.Cache;
44
import com.github.benmanes.caffeine.cache.Caffeine;
55
import com.hbpu.aicodebackend.ai.tools.FileWriteTool;
6+
import com.hbpu.aicodebackend.config.ReasoningStreamingChatModelConfig;
67
import com.hbpu.aicodebackend.exception.BusinessException;
78
import com.hbpu.aicodebackend.exception.ErrorCode;
89
import com.hbpu.aicodebackend.model.enums.CodeGenTypeEnum;
@@ -15,7 +16,6 @@
1516
import dev.langchain4j.service.AiServices;
1617
import jakarta.annotation.Resource;
1718
import lombok.extern.slf4j.Slf4j;
18-
import org.springframework.context.annotation.Bean;
1919
import org.springframework.context.annotation.Configuration;
2020

2121
import java.time.Duration;
@@ -28,7 +28,7 @@ public class AiCodeGeneratorServiceFactory {
2828
private ChatModel chatModel;
2929

3030
@Resource(name = "openAiStreamingChatModel")
31-
private StreamingChatModel openAIStreamingChatModel;
31+
private StreamingChatModel openAiStreamingChatModel;
3232

3333
@Resource(name = "reasoningStreamingChatModel")
3434
private StreamingChatModel reasoningStreamingChatModel;
@@ -39,22 +39,21 @@ public class AiCodeGeneratorServiceFactory {
3939
@Resource
4040
private ChatHistoryService chatHistoryService;
4141

42-
4342
/**
4443
* AI 服务实例缓存
4544
*/
4645
private final Cache<String, AiCodeGeneratorService> serviceCache = Caffeine.newBuilder()
47-
.maximumSize(1000)
48-
.expireAfterWrite(Duration.ofMinutes(30))
49-
.expireAfterAccess(
50-
Duration.ofMinutes(10))
51-
.removalListener((key, value, cause) -> {
52-
log.debug(
53-
"AI 服务实例被移除,缓存键: {}, 原因: {}",
54-
key, cause
55-
);
56-
})
57-
.build();
46+
.maximumSize(1000)
47+
.expireAfterWrite(Duration.ofMinutes(30))
48+
.expireAfterAccess(
49+
Duration.ofMinutes(10))
50+
.removalListener((key, value, cause) -> {
51+
log.debug(
52+
"AI 服务实例被移除,缓存键: {}, 原因: {}",
53+
key, cause
54+
);
55+
})
56+
.build();
5857

5958
/**
6059
* 根据 appId 获取服务(带缓存)这个方法是为了兼容历史逻辑
@@ -116,7 +115,7 @@ private AiCodeGeneratorService createAiCodeGeneratorService(long appId, CodeGenT
116115
// HTML 和多文件生成使用默认模型
117116
case HTML, MULTI_FILE -> AiServices.builder(AiCodeGeneratorService.class)
118117
.chatModel(chatModel)
119-
.streamingChatModel(openAIStreamingChatModel)
118+
.streamingChatModel(openAiStreamingChatModel)
120119
.chatMemory(chatMemory)
121120
.build();
122121
default -> throw new BusinessException(

src/main/java/com/hbpu/aicodebackend/config/ReasoningStreamingChatModelConfig.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,31 @@
22

33
import dev.langchain4j.model.chat.StreamingChatModel;
44
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
5-
import lombok.Data;
6-
import org.springframework.boot.context.properties.ConfigurationProperties;
5+
import lombok.Getter;
6+
import org.springframework.beans.factory.annotation.Value;
77
import org.springframework.context.annotation.Bean;
88
import org.springframework.context.annotation.Configuration;
99

1010
import java.time.Duration;
1111

1212
@Configuration
13-
@ConfigurationProperties(prefix = "langchain4j.open-ai.chat-model")
14-
@Data
1513
public class ReasoningStreamingChatModelConfig {
1614

15+
@Value("${langchain4j.open-ai.chat-model.base-url}")
1716
private String baseUrl;
1817

18+
@Value("${langchain4j.open-ai.chat-model.api-key}")
1919
private String apiKey;
2020

21-
/**
22-
* 推理流式模型(用于 Vue 项目生成,带工具调用)
23-
*/
24-
@Bean
21+
// 供工厂类注入判断
22+
// 新增:是否是推理模型,yaml 里控制
23+
@Value("${ai.reasoning.is-reasoning-model:false}")
24+
private boolean isReasoningModel;
25+
26+
@Bean("reasoningStreamingChatModel")
2527
public StreamingChatModel reasoningStreamingChatModel() {
26-
// 为了测试方便临时修改
27-
final String modelName = "deepseek-chat";
28-
final int maxTokens = 8192;
29-
// 生产环境使用:
30-
// final String modelName = "deepseek-reasoner";
31-
// final int maxTokens = 32768;
28+
final String modelName = isReasoningModel ? "deepseek-reasoner" : "deepseek-chat";
29+
final int maxTokens = isReasoningModel ? 32768 : 8192;
3230
return OpenAiStreamingChatModel.builder()
3331
.apiKey(apiKey)
3432
.baseUrl(baseUrl)
@@ -39,4 +37,5 @@ public StreamingChatModel reasoningStreamingChatModel() {
3937
.logResponses(true)
4038
.build();
4139
}
42-
}
40+
41+
}

src/main/java/com/hbpu/aicodebackend/controller/AppController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public BaseResponse<Long> addApp(@RequestBody AppAddRequest appAddRequest, HttpS
7777
// 应用名称暂时为 initPrompt 前 12 位
7878
app.setAppName(initPrompt.substring(0, Math.min(initPrompt.length(), 12)));
7979
// 暂时设置为多文件生成
80-
app.setCodeGenType(CodeGenTypeEnum.MULTI_FILE.getValue());
80+
app.setCodeGenType(CodeGenTypeEnum.VUE_PROJECT.getValue());
8181
// 插入数据库
8282
boolean result = appService.save(app);
8383
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);

src/main/java/com/hbpu/aicodebackend/core/AiCodeGeneratorFacade.java

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414
import com.hbpu.aicodebackend.exception.BusinessException;
1515
import com.hbpu.aicodebackend.exception.ErrorCode;
1616
import com.hbpu.aicodebackend.model.enums.CodeGenTypeEnum;
17-
import dev.langchain4j.model.chat.response.ChatResponse;
1817
import dev.langchain4j.service.TokenStream;
19-
import dev.langchain4j.service.tool.ToolExecution;
2018
import jakarta.annotation.Resource;
2119
import lombok.extern.slf4j.Slf4j;
2220
import org.springframework.stereotype.Service;
@@ -129,42 +127,38 @@ private Flux<String> processCodeStream(Flux<String> codeStream, CodeGenTypeEnum
129127
* @return Flux<String> 流式响应
130128
*/
131129
private Flux<String> processTokenStream(TokenStream tokenStream) {
132-
return Flux.create(sink -> {
133-
tokenStream
134-
// 1. 处理正常的 AI 文本回复
135-
.onPartialResponse(partialResponse -> {
136-
AiResponseMessage msg = new AiResponseMessage(partialResponse);
137-
sink.next(JSONUtil.toJsonStr(msg));
138-
})
139-
// 2. 处理 AI 的思考过程(如果你不打算建 ThinkingResponseMessage,可以注释掉这段)
140-
.onPartialThinking(partialThinking -> {
141-
ThinkingResponseMessage msg = new ThinkingResponseMessage(partialThinking.text());
142-
sink.next(JSONUtil.toJsonStr(msg));
143-
})
144-
// 3. 工具即将执行(此时 AI 已经完整生成了工具调用参数)
145-
// 完美适配你的 ToolRequestMessage 构造函数
146-
.beforeToolExecution(beforeExecution -> {
147-
// beforeExecution.request() 返回完整的 ToolExecutionRequest
148-
ToolRequestMessage msg = new ToolRequestMessage(beforeExecution.request());
149-
sink.next(JSONUtil.toJsonStr(msg));
150-
})
151-
// 4. 工具执行完毕(此时本地方法已运行完毕并拿到 result)
152-
// 完美适配你的 ToolExecutedMessage 构造函数
153-
.onToolExecuted(toolExecution -> {
154-
ToolExecutedMessage msg = new ToolExecutedMessage(toolExecution);
155-
sink.next(JSONUtil.toJsonStr(msg));
156-
})
157-
// 5. 整个流式对话结束
158-
.onCompleteResponse(chatResponse -> {
159-
sink.complete();
160-
})
161-
// 6. 异常处理
162-
.onError(error -> {
163-
error.printStackTrace();
164-
sink.error(error);
165-
})
166-
// 必须调用 start 启动流
167-
.start();
168-
});
130+
return Flux.create(sink -> tokenStream
131+
// 1. 处理正常的 AI 文本回复
132+
.onPartialResponse(partialResponse -> {
133+
AiResponseMessage msg = new AiResponseMessage(partialResponse);
134+
sink.next(JSONUtil.toJsonStr(msg));
135+
})
136+
// 2. 处理 AI 的思考过程(如果你不打算建 ThinkingResponseMessage,可以注释掉这段)
137+
.onPartialThinking(partialThinking -> {
138+
ThinkingResponseMessage msg = new ThinkingResponseMessage(partialThinking.text());
139+
sink.next(JSONUtil.toJsonStr(msg));
140+
})
141+
// 3. 工具即将执行(此时 AI 已经完整生成了工具调用参数)
142+
// 完美适配你的 ToolRequestMessage 构造函数
143+
.beforeToolExecution(beforeExecution -> {
144+
// beforeExecution.request() 返回完整的 ToolExecutionRequest
145+
ToolRequestMessage msg = new ToolRequestMessage(beforeExecution.request());
146+
sink.next(JSONUtil.toJsonStr(msg));
147+
})
148+
// 4. 工具执行完毕(此时本地方法已运行完毕并拿到 result)
149+
// 完美适配你的 ToolExecutedMessage 构造函数
150+
.onToolExecuted(toolExecution -> {
151+
ToolExecutedMessage msg = new ToolExecutedMessage(toolExecution);
152+
sink.next(JSONUtil.toJsonStr(msg));
153+
})
154+
// 5. 整个流式对话结束
155+
.onCompleteResponse(chatResponse -> sink.complete())
156+
// 6. 异常处理
157+
.onError(error -> {
158+
log.error("流式处理出错: {}", error.getMessage());
159+
sink.error(error);
160+
})
161+
// 必须调用 start 启动流
162+
.start());
169163
}
170164
}

0 commit comments

Comments
 (0)