Skip to content

Commit 09a4dec

Browse files
fix: update related configurations to saa1.1 (spring-ai-alibaba#307)
* feat: add human feedback data handling and update related configurations * feat: add DB_DIALECT_TYPE handling and improve schema processing logic * fix:refactor the name * fix format * fix docs * fix test * fix docker-compose.yml * fix format * fix log error --------- Co-authored-by: yanjue.zhou <yanjue.zhou@bluefocus.cn>
1 parent 0bc7c65 commit 09a4dec

File tree

18 files changed

+111
-72
lines changed

18 files changed

+111
-72
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
</p>
99

1010
<p>
11-
<a href="https://github.com/alibaba/spring-ai-alibaba"><img src="https://img.shields.io/badge/Spring%20AI%20Alibaba-1.0.4-blue" alt="Spring AI Alibaba"></a>
12-
<img src="https://img.shields.io/badge/Spring%20Boot-3.2+-green" alt="Spring Boot">
11+
<a href="https://github.com/alibaba/spring-ai-alibaba"><img src="https://img.shields.io/badge/Spring%20AI%20Alibaba-1.1.0.0-blue" alt="Spring AI Alibaba"></a>
12+
<img src="https://img.shields.io/badge/Spring%20Boot-3.4.8+-green" alt="Spring Boot">
1313
<img src="https://img.shields.io/badge/Java-17+-orange" alt="Java">
1414
<img src="https://img.shields.io/badge/License-Apache%202.0-red" alt="License">
1515
<a href="https://deepwiki.com/spring-ai-alibaba/DataAgent"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>

data-agent-frontend/src/components/agent/DataSourceConfig.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@
193193
plain
194194
>
195195
<el-icon style="margin-right: 4px"><Connection /></el-icon>
196-
模型配置
196+
逻辑外键配置
197197
</el-button>
198198
<el-button
199199
@click="removeAgentDatasource(scope.row)"
@@ -485,10 +485,10 @@
485485
</div>
486486
</el-dialog>
487487

488-
<!-- 模型配置Dialog(逻辑外键管理) -->
488+
<!-- 逻辑外键配置Dialog(逻辑外键管理) -->
489489
<el-dialog
490490
v-model="foreignKeyDialogVisible"
491-
title="模型配置"
491+
title="逻辑外键配置"
492492
width="900px"
493493
:close-on-click-modal="false"
494494
>

data-agent-management/pom.xml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,6 @@
102102
<artifactId>h2</artifactId>
103103
<scope>runtime</scope>
104104
</dependency>
105-
<dependency>
106-
<groupId>com.alibaba.cloud.ai</groupId>
107-
<artifactId>spring-ai-alibaba-core</artifactId>
108-
<version>${spring-ai-alibaba.version}</version>
109-
</dependency>
110-
111-
112105
<dependency>
113106
<groupId>org.springframework.boot</groupId>
114107
<artifactId>spring-boot-starter-web</artifactId>
@@ -256,6 +249,16 @@
256249
<scope>runtime</scope>
257250
</dependency>
258251

252+
<!-- Docker Java Client -->
253+
<dependency>
254+
<groupId>com.github.docker-java</groupId>
255+
<artifactId>docker-java-core</artifactId>
256+
</dependency>
257+
<dependency>
258+
<groupId>com.github.docker-java</groupId>
259+
<artifactId>docker-java-transport-zerodep</artifactId>
260+
</dependency>
261+
259262
</dependencies>
260263
<repositories>
261264
<repository>

data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/aop/NodeEntryLoggingAspect.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
@Slf4j
3535
public class NodeEntryLoggingAspect {
3636

37-
@Pointcut("execution(* com.alibaba.cloud.ai.dataagent.node..*.apply(com.alibaba.cloud.ai.graph.OverAllState))")
37+
@Pointcut("execution(* com.alibaba.cloud.ai.dataagent.workflow.node..*.apply(com.alibaba.cloud.ai.graph.OverAllState))")
3838
public void nodeEntry() {
3939
}
4040

data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/config/DataAgentConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public StateGraph nl2sqlGraph(NodeBeanUtil nodeBeanUtil, CodeExecutorProperties
136136
keyStrategyHashMap.put(TABLE_RELATION_OUTPUT, KeyStrategy.REPLACE);
137137
keyStrategyHashMap.put(TABLE_RELATION_EXCEPTION_OUTPUT, KeyStrategy.REPLACE);
138138
keyStrategyHashMap.put(TABLE_RELATION_RETRY_COUNT, KeyStrategy.REPLACE);
139+
keyStrategyHashMap.put(DB_DIALECT_TYPE, KeyStrategy.REPLACE);
139140
// Feasibility Assessment 节点输出
140141
keyStrategyHashMap.put(FEASIBILITY_ASSESSMENT_NODE_OUTPUT, KeyStrategy.REPLACE);
141142
// sql generate节点输出
@@ -167,6 +168,7 @@ public StateGraph nl2sqlGraph(NodeBeanUtil nodeBeanUtil, CodeExecutorProperties
167168
keyStrategyHashMap.put(IS_ONLY_NL2SQL, KeyStrategy.REPLACE);
168169
// Human Review keys
169170
keyStrategyHashMap.put(HUMAN_REVIEW_ENABLED, KeyStrategy.REPLACE);
171+
keyStrategyHashMap.put(HUMAN_FEEDBACK_DATA, KeyStrategy.REPLACE);
170172
// Final result
171173
keyStrategyHashMap.put(RESULT, KeyStrategy.REPLACE);
172174
return keyStrategyHashMap;

data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/constant/Constant.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ private Constant() {
147147
// 人类复核相关
148148
public static final String HUMAN_REVIEW_ENABLED = "HUMAN_REVIEW_ENABLED";
149149

150+
// Human feedback data payload
151+
public static final String HUMAN_FEEDBACK_DATA = "HUMAN_FEEDBACK_DATA";
152+
150153
// 控制是否生成Markdown简洁报告
151154
public static final String PLAIN_REPORT = "PLAIN_REPORT";
152155

data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/aimodelconfig/ModelConfigOpsService.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import com.alibaba.cloud.ai.dataagent.enums.ModelType;
2020
import com.alibaba.cloud.ai.dataagent.dto.ModelConfigDTO;
2121
import com.alibaba.cloud.ai.dataagent.entity.ModelConfig;
22-
import com.alibaba.fastjson.JSON;
22+
import com.alibaba.cloud.ai.dataagent.util.JsonUtil;
23+
import com.fasterxml.jackson.core.JsonProcessingException;
24+
import com.fasterxml.jackson.databind.ObjectMapper;
2325
import lombok.AllArgsConstructor;
2426
import lombok.extern.slf4j.Slf4j;
2527
import org.springframework.ai.chat.model.ChatModel;
@@ -39,6 +41,8 @@ public class ModelConfigOpsService {
3941

4042
private final AiModelRegistry aiModelRegistry;
4143

44+
private final ObjectMapper objectMapper = JsonUtil.getObjectMapper();
45+
4246
/**
4347
* 专门处理:更新配置并热刷新的聚合逻辑
4448
*/
@@ -115,7 +119,12 @@ else if (ModelType.EMBEDDING.getCode().equalsIgnoreCase(modelType)) {
115119
}
116120
}
117121
catch (Exception e) {
118-
log.error("Failed to test model connection. Config: {}", JSON.toJSONString(config), e);
122+
try {
123+
log.error("Failed to test model connection. Config: {}", objectMapper.writeValueAsString(config), e);
124+
}
125+
catch (JsonProcessingException e1) {
126+
log.error("Failed to convert config to JSON. Config: {}", config, e1);
127+
}
119128
// 重新抛出异常,让 Controller 捕获并展示给前端
120129
// 如果是 OpenAiHttpException,通常包含具体的 API 错误信息
121130
throw new RuntimeException(parseErrorMessage(e));

data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import com.alibaba.cloud.ai.graph.*;
2525
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
2626
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
27-
import com.alibaba.cloud.ai.graph.state.StateSnapshot;
2827
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
2928
import lombok.extern.slf4j.Slf4j;
3029
import org.springframework.http.codec.ServerSentEvent;
@@ -34,6 +33,7 @@
3433
import reactor.core.publisher.Flux;
3534
import reactor.core.publisher.Sinks;
3635

36+
import java.util.HashMap;
3737
import java.util.Map;
3838
import java.util.UUID;
3939
import java.util.concurrent.CompletableFuture;
@@ -65,7 +65,7 @@ public GraphServiceImpl(StateGraph stateGraph, ExecutorService executorService,
6565
@Override
6666
public String nl2sql(String naturalQuery, String agentId) throws GraphRunnerException {
6767
OverAllState state = compiledGraph
68-
.call(Map.of(IS_ONLY_NL2SQL, true, INPUT_KEY, naturalQuery, AGENT_ID, agentId),
68+
.invoke(Map.of(IS_ONLY_NL2SQL, true, INPUT_KEY, naturalQuery, AGENT_ID, agentId),
6969
RunnableConfig.builder().build())
7070
.orElseThrow();
7171
return state.value(SQL_GENERATE_OUTPUT, "");
@@ -126,7 +126,7 @@ private void handleNewProcess(GraphRequest graphRequest) {
126126
}
127127
String multiTurnContext = multiTurnContextManager.buildContext(threadId);
128128
multiTurnContextManager.beginTurn(threadId, query);
129-
Flux<NodeOutput> nodeOutputFlux = compiledGraph.fluxStream(Map.of(IS_ONLY_NL2SQL, nl2sqlOnly, INPUT_KEY, query,
129+
Flux<NodeOutput> nodeOutputFlux = compiledGraph.stream(Map.of(IS_ONLY_NL2SQL, nl2sqlOnly, INPUT_KEY, query,
130130
AGENT_ID, agentId, HUMAN_REVIEW_ENABLED, humanReviewEnabled, PLAIN_REPORT, graphRequest.isPlainReport(),
131131
MULTI_TURN_CONTEXT, multiTurnContext), RunnableConfig.builder().threadId(threadId).build());
132132
subscribeToFlux(context, nodeOutputFlux, graphRequest, agentId, threadId);
@@ -149,18 +149,26 @@ private void handleHumanFeedback(GraphRequest graphRequest) {
149149
}
150150
Map<String, Object> feedbackData = Map.of("feedback", !graphRequest.isRejectedPlan(), "feedback_content",
151151
feedbackContent);
152-
OverAllState.HumanFeedback humanFeedback = new OverAllState.HumanFeedback(feedbackData, HUMAN_FEEDBACK_NODE);
153-
StateSnapshot stateSnapshot = compiledGraph.getState(RunnableConfig.builder().threadId(threadId).build());
154-
OverAllState resumeState = stateSnapshot.state();
155-
resumeState.withResume();
156-
resumeState.withHumanFeedback(humanFeedback);
157152
if (graphRequest.isRejectedPlan()) {
158153
multiTurnContextManager.restartLastTurn(threadId);
159154
}
160-
resumeState.updateState(Map.of(MULTI_TURN_CONTEXT, multiTurnContextManager.buildContext(threadId)));
155+
Map<String, Object> stateUpdate = new HashMap<>();
156+
stateUpdate.put(HUMAN_FEEDBACK_DATA, feedbackData);
157+
stateUpdate.put(MULTI_TURN_CONTEXT, multiTurnContextManager.buildContext(threadId));
161158

162-
Flux<NodeOutput> nodeOutputFlux = compiledGraph.fluxStreamFromInitialNode(resumeState,
163-
RunnableConfig.builder().threadId(threadId).build());
159+
RunnableConfig baseConfig = RunnableConfig.builder().threadId(threadId).build();
160+
RunnableConfig updatedConfig;
161+
try {
162+
updatedConfig = compiledGraph.updateState(baseConfig, stateUpdate);
163+
}
164+
catch (Exception e) {
165+
throw new IllegalStateException("Failed to update graph state for human feedback", e);
166+
}
167+
RunnableConfig resumeConfig = RunnableConfig.builder(updatedConfig)
168+
.addMetadata(RunnableConfig.HUMAN_FEEDBACK_METADATA_KEY, feedbackData)
169+
.build();
170+
171+
Flux<NodeOutput> nodeOutputFlux = compiledGraph.stream(null, resumeConfig);
164172
subscribeToFlux(context, nodeOutputFlux, graphRequest, agentId, threadId);
165173
}
166174

data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/schema/SchemaServiceImpl.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,26 @@ public class SchemaServiceImpl implements SchemaService {
8282
public void buildSchemaFromDocuments(String agentId, List<Document> currentColumnDocuments,
8383
List<Document> tableDocuments, SchemaDTO schemaDTO) {
8484

85+
// 创建可变列表副本,避免不可变集合异常
86+
List<Document> mutableColumnDocuments = new ArrayList<>(currentColumnDocuments);
87+
List<Document> mutableTableDocuments = new ArrayList<>(tableDocuments);
88+
8589
// 如果外键关系是"订单表.订单ID=订单详情表.订单ID",那么 relatedNamesFromForeignKeys
8690
// 将包含"订单表.订单ID"和"订单详情表.订单ID"
87-
Set<String> relatedNamesFromForeignKeys = extractRelatedNamesFromForeignKeys(tableDocuments);
91+
Set<String> relatedNamesFromForeignKeys = extractRelatedNamesFromForeignKeys(mutableTableDocuments);
8892

8993
// 通过外键加载缺失的表和列
90-
List<String> missingTables = getMissingTableNamesWithForeignKeySet(tableDocuments, relatedNamesFromForeignKeys);
94+
List<String> missingTables = getMissingTableNamesWithForeignKeySet(mutableTableDocuments,
95+
relatedNamesFromForeignKeys);
9196
if (!missingTables.isEmpty()) {
92-
loadMissingTableDocuments(agentId, tableDocuments, missingTables);
93-
loadMissingColDocForMissingTables(agentId, currentColumnDocuments, missingTables);
97+
loadMissingTableDocuments(agentId, mutableTableDocuments, missingTables);
98+
loadMissingColDocForMissingTables(agentId, mutableColumnDocuments, missingTables);
9499
}
95100

96101
// Build table list
97-
List<TableDTO> tableList = buildTableListFromDocuments(tableDocuments);
102+
List<TableDTO> tableList = buildTableListFromDocuments(mutableTableDocuments);
98103
// Attach columns to corresponding tables
99-
attachColumnsToTables(currentColumnDocuments, tableList);
104+
attachColumnsToTables(mutableColumnDocuments, tableList);
100105

101106
// Finally assemble SchemaDTO
102107
schemaDTO.setTable(tableList);

data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/util/FluxUtil.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919
import com.alibaba.cloud.ai.graph.GraphResponse;
2020
import com.alibaba.cloud.ai.graph.OverAllState;
2121
import com.alibaba.cloud.ai.graph.action.NodeAction;
22-
import com.alibaba.cloud.ai.graph.streaming.FluxConverter;
22+
import com.alibaba.cloud.ai.graph.streaming.OutputType;
2323
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
2424
import org.springframework.ai.chat.model.ChatResponse;
2525
import reactor.core.publisher.Flux;
2626
import reactor.core.publisher.Mono;
2727

2828
import java.util.Map;
29+
import java.util.function.Supplier;
2930
import java.util.function.Function;
3031

3132
/**
@@ -100,11 +101,8 @@ public static Flux<GraphResponse<StreamingOutput>> createStreamingGeneratorWithM
100101
if (completionMessage != null) {
101102
wrapperFlux = wrapperFlux.concatWith(Flux.just(ChatResponseUtil.createResponse(completionMessage)));
102103
}
103-
return FluxConverter.builder()
104-
.startingNode(nodeName)
105-
.startingState(state)
106-
.mapResult(r -> resultMapper.apply(collectedResult.toString()))
107-
.build(wrapperFlux);
104+
return toStreamingResponseFlux(nodeName, state, wrapperFlux,
105+
() -> resultMapper.apply(collectedResult.toString()));
108106
}
109107

110108
public static Flux<GraphResponse<StreamingOutput>> createStreamingGeneratorWithMessages(
@@ -130,11 +128,20 @@ public static Flux<GraphResponse<StreamingOutput>> createStreamingGenerator(Clas
130128
// Used to collect actual processing results
131129
final StringBuilder collectedResult = new StringBuilder();
132130
sourceFlux = sourceFlux.doOnNext(r -> collectedResult.append(ChatResponseUtil.getText(r)));
133-
return FluxConverter.builder()
134-
.startingNode(nodeName)
135-
.startingState(state)
136-
.mapResult(r -> sourceMapper.apply(collectedResult.toString()))
137-
.build(Flux.concat(preFlux, sourceFlux, sufFlux));
131+
return toStreamingResponseFlux(nodeName, state, Flux.concat(preFlux, sourceFlux, sufFlux),
132+
() -> sourceMapper.apply(collectedResult.toString()));
133+
}
134+
135+
private static Flux<GraphResponse<StreamingOutput>> toStreamingResponseFlux(String nodeName, OverAllState state,
136+
Flux<ChatResponse> sourceFlux, Supplier<Map<String, Object>> resultSupplier) {
137+
Flux<GraphResponse<StreamingOutput>> streamingFlux = sourceFlux
138+
.filter(response -> response != null && response.getResult() != null
139+
&& response.getResult().getOutput() != null)
140+
.map(response -> GraphResponse.of(new StreamingOutput<>(response.getResult().getOutput(), response,
141+
nodeName, "", state, OutputType.from(true, nodeName))));
142+
143+
return streamingFlux.concatWith(Mono.fromSupplier(() -> GraphResponse.done(resultSupplier.get())))
144+
.onErrorResume(error -> Flux.just(GraphResponse.error(error)));
138145
}
139146

140147
}

0 commit comments

Comments
 (0)