Skip to content

Commit a442075

Browse files
committed
fix(graph): HttpNode supports user-defined variable filtering methods.
1 parent 1f0bf74 commit a442075

File tree

1 file changed

+70
-41
lines changed
  • spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/node

1 file changed

+70
-41
lines changed

spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/node/HttpNode.java

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@
1515
*/
1616
package com.alibaba.cloud.ai.graph.node;
1717

18+
import java.net.URI;
19+
import java.nio.charset.StandardCharsets;
20+
import java.time.Duration;
21+
import java.util.ArrayList;
22+
import java.util.Base64;
23+
import java.util.Collections;
24+
import java.util.HashMap;
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.Objects;
28+
import java.util.Optional;
29+
import java.util.UUID;
30+
import java.util.regex.Matcher;
31+
import java.util.function.Function;
32+
import java.util.regex.Pattern;
33+
1834
import com.alibaba.cloud.ai.graph.OverAllState;
1935
import com.alibaba.cloud.ai.graph.action.NodeAction;
2036
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
@@ -25,6 +41,9 @@
2541
import com.fasterxml.jackson.databind.ObjectMapper;
2642
import org.slf4j.Logger;
2743
import org.slf4j.LoggerFactory;
44+
import reactor.core.publisher.Mono;
45+
import reactor.util.retry.Retry;
46+
2847
import org.springframework.core.io.ByteArrayResource;
2948
import org.springframework.http.HttpHeaders;
3049
import org.springframework.http.HttpMethod;
@@ -39,23 +58,6 @@
3958
import org.springframework.web.reactive.function.client.WebClient;
4059
import org.springframework.web.reactive.function.client.WebClientResponseException;
4160
import org.springframework.web.util.UriComponentsBuilder;
42-
import reactor.core.publisher.Mono;
43-
import reactor.util.retry.Retry;
44-
45-
import java.net.URI;
46-
import java.nio.charset.StandardCharsets;
47-
import java.time.Duration;
48-
import java.util.ArrayList;
49-
import java.util.Base64;
50-
import java.util.Collections;
51-
import java.util.HashMap;
52-
import java.util.List;
53-
import java.util.Map;
54-
import java.util.Objects;
55-
import java.util.Optional;
56-
import java.util.UUID;
57-
import java.util.regex.Matcher;
58-
import java.util.regex.Pattern;
5961

6062
import static java.lang.String.format;
6163

@@ -69,7 +71,26 @@ public class HttpNode implements NodeAction {
6971

7072
private static final long DEFAULT_MAX_RETRY_INTERVAL = 1000;
7173

72-
private static final ObjectMapper mapper = new ObjectMapper();
74+
private static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper();
75+
76+
/**
77+
* Default string replacement function that cleans JSON template strings.
78+
*/
79+
private static final Function<Object, Object> DEFAULT_VARIABLE_FILTER = jsonTemplate -> {
80+
if (jsonTemplate instanceof String && StringUtils.hasText((String) jsonTemplate)) {
81+
return ((String) jsonTemplate).replace("```json", "")
82+
.replace("```", "")
83+
.replace("\n", "")
84+
.replace("\r", "")
85+
.replace("\t", "")
86+
.replace("\"", "\\\"");
87+
}
88+
return jsonTemplate;
89+
};
90+
91+
private final ObjectMapper mapper;
92+
93+
private final Function<Object, Object> variableFilter;
7394

7495
private final WebClient webClient;
7596

@@ -99,6 +120,8 @@ private HttpNode(Builder builder) {
99120
this.authConfig = builder.authConfig;
100121
this.retryConfig = builder.retryConfig;
101122
this.outputKey = builder.outputKey;
123+
this.mapper = builder.objectMapper != null ? builder.objectMapper : DEFAULT_MAPPER;
124+
this.variableFilter = builder.variableFilter != null ? builder.variableFilter : DEFAULT_VARIABLE_FILTER;
102125
}
103126

104127
@Override
@@ -148,8 +171,8 @@ private String replaceVariables(String template, OverAllState state) {
148171
while (matcher.find()) {
149172
String key = matcher.group(1);
150173
Object value = state.value(key).orElse("");
151-
String strReplaced = getStrReplaced(value.toString());
152-
matcher.appendReplacement(result, Matcher.quoteReplacement(strReplaced));
174+
Object replaced = variableFilter.apply(value);
175+
matcher.appendReplacement(result, Matcher.quoteReplacement(replaced != null ? replaced.toString() : ""));
153176
}
154177
matcher.appendTail(result);
155178
return result.toString();
@@ -258,31 +281,16 @@ public String getFilename() {
258281
}
259282
}
260283

261-
/**
262-
* Get string replaced.
263-
* @param jsonTemplate JSON template
264-
* @return string replaced
265-
*/
266-
private static String getStrReplaced(String jsonTemplate) {
267-
return jsonTemplate == null ? ""
268-
: jsonTemplate.replace("```json", "")
269-
.replace("```", "")
270-
.replace("\n", "")
271-
.replace("\r", "")
272-
.replace("\t", "")
273-
.replace("\"", "\\\"");
274-
}
275-
276284
/**
277285
* Parse nested JSON string.
278286
* @param json JSON string
279287
* @return parsed JSON object
280288
* @throws JsonProcessingException if parsing fails
281289
*/
282-
public static Object parseNestedJson(String json) throws JsonProcessingException {
283-
JsonNode rootNode = mapper.readTree(json);
290+
public Object parseNestedJson(String json) throws JsonProcessingException {
291+
JsonNode rootNode = this.mapper.readTree(json);
284292
if (rootNode.isObject()) {
285-
Map<String, Object> map = mapper.convertValue(rootNode, Map.class);
293+
Map<String, Object> map = this.mapper.convertValue(rootNode, Map.class);
286294
for (Map.Entry<String, Object> entry : map.entrySet()) {
287295
Object value = entry.getValue();
288296
if (value instanceof String valueStr) {
@@ -303,7 +311,7 @@ public static Object parseNestedJson(String json) throws JsonProcessingException
303311
return map;
304312
}
305313
else {
306-
return mapper.convertValue(rootNode, Object.class);
314+
return this.mapper.convertValue(rootNode, Object.class);
307315
}
308316
}
309317

@@ -341,8 +349,7 @@ private Map<String, Object> processResponse(ResponseEntity<byte[]> responseEntit
341349
else {
342350
String text = new String(body, StandardCharsets.UTF_8);
343351
try {
344-
ObjectMapper objectMapper = new ObjectMapper();
345-
Map<String, Object> map = objectMapper.readValue(text, Map.class);
352+
Map<String, Object> map = this.mapper.readValue(text, Map.class);
346353
result.put("body", map);
347354
}
348355
catch (Exception ex) {
@@ -416,6 +423,15 @@ public static class Builder {
416423

417424
private String outputKey;
418425

426+
private ObjectMapper objectMapper;
427+
428+
private Function<Object, Object> variableFilter;
429+
430+
public Builder objectMapper(ObjectMapper objectMapper) {
431+
this.objectMapper = objectMapper;
432+
return this;
433+
}
434+
419435
public Builder webClient(WebClient webClient) {
420436
this.webClient = webClient;
421437
return this;
@@ -461,6 +477,19 @@ public Builder outputKey(String outputKey) {
461477
return this;
462478
}
463479

480+
/**
481+
* Set a custom string filter function for processing variable replacement values.
482+
* This function will be applied to all string values during variable replacement.
483+
* If not set, a default filter will be used that cleans JSON template strings.
484+
* @param variableFilter the function to filter string values during variable
485+
* replacement
486+
* @return this builder
487+
*/
488+
public Builder variableFilter(Function<Object, Object> variableFilter) {
489+
this.variableFilter = variableFilter;
490+
return this;
491+
}
492+
464493
public HttpNode build() {
465494
return new HttpNode(this);
466495
}

0 commit comments

Comments
 (0)