Skip to content

Commit 8927879

Browse files
committed
feat: enhance BeanOutputConverter with customizable text cleaning capabilities
Signed-off-by: liugddx <[email protected]>
1 parent d997c80 commit 8927879

File tree

8 files changed

+90
-99
lines changed

8 files changed

+90
-99
lines changed

spring-ai-model/src/main/java/org/springframework/ai/converter/CompositeResponseTextCleaner.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,3 @@ public CompositeResponseTextCleaner build() {
106106
}
107107

108108
}
109-

spring-ai-model/src/main/java/org/springframework/ai/converter/MarkdownCodeBlockCleaner.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,3 @@ public String clean(String text) {
7171
}
7272

7373
}
74-

spring-ai-model/src/main/java/org/springframework/ai/converter/ResponseTextCleaner.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,3 @@ public interface ResponseTextCleaner {
3535
String clean(String text);
3636

3737
}
38-

spring-ai-model/src/main/java/org/springframework/ai/converter/ThinkingTagCleaner.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,3 @@ public ThinkingTagCleaner build() {
169169
}
170170

171171
}
172-

spring-ai-model/src/main/java/org/springframework/ai/converter/WhitespaceCleaner.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,3 @@ public String clean(String text) {
3030
}
3131

3232
}
33-

spring-ai-model/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java

Lines changed: 83 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -273,97 +273,97 @@ void convertWithThinkingTagsAndMarkdownCodeBlock() {
273273
assertThat(testClass.getSomeString()).isEqualTo("some value");
274274
}
275275

276-
@Test
277-
void convertWithMultipleThinkingTags() {
278-
var converter = new BeanOutputConverter<>(TestClass.class);
279-
String textWithThinkingTags = "<thinking>First thought</thinking><thinking>Second thought</thinking>{ \"someString\": \"some value\" }";
280-
var testClass = converter.convert(textWithThinkingTags);
281-
assertThat(testClass.getSomeString()).isEqualTo("some value");
282-
}
276+
@Test
277+
void convertWithMultipleThinkingTags() {
278+
var converter = new BeanOutputConverter<>(TestClass.class);
279+
String textWithThinkingTags = "<thinking>First thought</thinking><thinking>Second thought</thinking>{ \"someString\": \"some value\" }";
280+
var testClass = converter.convert(textWithThinkingTags);
281+
assertThat(testClass.getSomeString()).isEqualTo("some value");
282+
}
283283

284-
@Test
285-
void convertWithQwenThinkTags() {
286-
// Test Qwen model format: <think>...</think>
287-
var converter = new BeanOutputConverter<>(TestClass.class);
288-
String textWithThinkTags = "<think>Let me analyze this...</think>{ \"someString\": \"qwen test\" }";
289-
var testClass = converter.convert(textWithThinkTags);
290-
assertThat(testClass.getSomeString()).isEqualTo("qwen test");
291-
}
284+
@Test
285+
void convertWithQwenThinkTags() {
286+
// Test Qwen model format: <think>...</think>
287+
var converter = new BeanOutputConverter<>(TestClass.class);
288+
String textWithThinkTags = "<think>Let me analyze this...</think>{ \"someString\": \"qwen test\" }";
289+
var testClass = converter.convert(textWithThinkTags);
290+
assertThat(testClass.getSomeString()).isEqualTo("qwen test");
291+
}
292292

293-
@Test
294-
void convertWithQwenThinkTagsMultiline() {
295-
var converter = new BeanOutputConverter<>(TestClass.class);
296-
String textWithThinkTags = """
297-
<think>
298-
Analyzing the request step by step
299-
First, I need to understand the schema
300-
Then generate the JSON
301-
</think>
302-
{ "someString": "qwen multiline" }
303-
""";
304-
var testClass = converter.convert(textWithThinkTags);
305-
assertThat(testClass.getSomeString()).isEqualTo("qwen multiline");
306-
}
293+
@Test
294+
void convertWithQwenThinkTagsMultiline() {
295+
var converter = new BeanOutputConverter<>(TestClass.class);
296+
String textWithThinkTags = """
297+
<think>
298+
Analyzing the request step by step
299+
First, I need to understand the schema
300+
Then generate the JSON
301+
</think>
302+
{ "someString": "qwen multiline" }
303+
""";
304+
var testClass = converter.convert(textWithThinkTags);
305+
assertThat(testClass.getSomeString()).isEqualTo("qwen multiline");
306+
}
307307

308-
@Test
309-
void convertWithMixedThinkingAndThinkTags() {
310-
// Test mixed format from different models
311-
var converter = new BeanOutputConverter<>(TestClass.class);
312-
String textWithMixedTags = "<thinking>Nova reasoning</thinking><think>Qwen analysis</think>{ \"someString\": \"mixed test\" }";
313-
var testClass = converter.convert(textWithMixedTags);
314-
assertThat(testClass.getSomeString()).isEqualTo("mixed test");
315-
}
308+
@Test
309+
void convertWithMixedThinkingAndThinkTags() {
310+
// Test mixed format from different models
311+
var converter = new BeanOutputConverter<>(TestClass.class);
312+
String textWithMixedTags = "<thinking>Nova reasoning</thinking><think>Qwen analysis</think>{ \"someString\": \"mixed test\" }";
313+
var testClass = converter.convert(textWithMixedTags);
314+
assertThat(testClass.getSomeString()).isEqualTo("mixed test");
315+
}
316316

317-
@Test
318-
void convertWithReasoningTags() {
319-
// Test alternative reasoning tags
320-
var converter = new BeanOutputConverter<>(TestClass.class);
321-
String textWithReasoningTags = "<reasoning>Internal reasoning process</reasoning>{ \"someString\": \"reasoning test\" }";
322-
var testClass = converter.convert(textWithReasoningTags);
323-
assertThat(testClass.getSomeString()).isEqualTo("reasoning test");
324-
}
317+
@Test
318+
void convertWithReasoningTags() {
319+
// Test alternative reasoning tags
320+
var converter = new BeanOutputConverter<>(TestClass.class);
321+
String textWithReasoningTags = "<reasoning>Internal reasoning process</reasoning>{ \"someString\": \"reasoning test\" }";
322+
var testClass = converter.convert(textWithReasoningTags);
323+
assertThat(testClass.getSomeString()).isEqualTo("reasoning test");
324+
}
325325

326-
@Test
327-
void convertWithMarkdownThinkingBlock() {
328-
// Test markdown-style thinking block
329-
var converter = new BeanOutputConverter<>(TestClass.class);
330-
String textWithMarkdownThinking = """
331-
```thinking
332-
This is a markdown-style thinking block
333-
Used by some models
334-
```
335-
{ "someString": "markdown thinking" }
336-
""";
337-
var testClass = converter.convert(textWithMarkdownThinking);
338-
assertThat(testClass.getSomeString()).isEqualTo("markdown thinking");
339-
}
326+
@Test
327+
void convertWithMarkdownThinkingBlock() {
328+
// Test markdown-style thinking block
329+
var converter = new BeanOutputConverter<>(TestClass.class);
330+
String textWithMarkdownThinking = """
331+
```thinking
332+
This is a markdown-style thinking block
333+
Used by some models
334+
```
335+
{ "someString": "markdown thinking" }
336+
""";
337+
var testClass = converter.convert(textWithMarkdownThinking);
338+
assertThat(testClass.getSomeString()).isEqualTo("markdown thinking");
339+
}
340340

341-
@Test
342-
void convertWithCaseInsensitiveTags() {
343-
// Test case insensitive tag matching
344-
var converter = new BeanOutputConverter<>(TestClass.class);
345-
String textWithUpperCaseTags = "<THINKING>UPPERCASE THINKING</THINKING>{ \"someString\": \"case test\" }";
346-
var testClass = converter.convert(textWithUpperCaseTags);
347-
assertThat(testClass.getSomeString()).isEqualTo("case test");
348-
}
341+
@Test
342+
void convertWithCaseInsensitiveTags() {
343+
// Test case insensitive tag matching
344+
var converter = new BeanOutputConverter<>(TestClass.class);
345+
String textWithUpperCaseTags = "<THINKING>UPPERCASE THINKING</THINKING>{ \"someString\": \"case test\" }";
346+
var testClass = converter.convert(textWithUpperCaseTags);
347+
assertThat(testClass.getSomeString()).isEqualTo("case test");
348+
}
349349

350-
@Test
351-
void convertWithComplexNestedStructure() {
352-
// Test complex scenario with multiple formats combined
353-
var converter = new BeanOutputConverter<>(TestClass.class);
354-
String complexText = """
355-
<thinking>Nova model reasoning</thinking>
356-
<think>Qwen model analysis</think>
357-
358-
```json
359-
{ "someString": "complex test" }
360-
```
361-
""";
362-
var testClass = converter.convert(complexText);
363-
assertThat(testClass.getSomeString()).isEqualTo("complex test");
364-
}
350+
@Test
351+
void convertWithComplexNestedStructure() {
352+
// Test complex scenario with multiple formats combined
353+
var converter = new BeanOutputConverter<>(TestClass.class);
354+
String complexText = """
355+
<thinking>Nova model reasoning</thinking>
356+
<think>Qwen model analysis</think>
365357
366-
}
358+
```json
359+
{ "someString": "complex test" }
360+
```
361+
""";
362+
var testClass = converter.convert(complexText);
363+
assertThat(testClass.getSomeString()).isEqualTo("complex test");
364+
}
365+
366+
}
367367

368368
// @checkstyle:off RegexpSinglelineJavaCheck
369369
@Nested

spring-ai-model/src/test/java/org/springframework/ai/converter/CompositeResponseTextCleanerTest.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ void shouldWorkWithMultipleCleaners() {
5252
new MarkdownCodeBlockCleaner());
5353

5454
String input = """
55-
<thinking>Reasoning</thinking>
56-
```json
57-
{"key": "value"}
58-
```
59-
""";
55+
<thinking>Reasoning</thinking>
56+
```json
57+
{"key": "value"}
58+
```
59+
""";
6060
String result = cleaner.clean(input);
6161
assertThat(result).isEqualTo("{\"key\": \"value\"}");
6262
}
@@ -103,4 +103,3 @@ void shouldHandleEmptyCleanersList() {
103103
}
104104

105105
}
106-

spring-ai-model/src/test/java/org/springframework/ai/converter/ThinkingTagCleanerTest.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,14 @@ void shouldSupportBuilderWithAdditionalPatterns() {
139139

140140
@Test
141141
void shouldThrowExceptionWhenPatternsAreNull() {
142-
assertThatThrownBy(() -> new ThinkingTagCleaner((String[]) null))
143-
.isInstanceOf(IllegalArgumentException.class)
142+
assertThatThrownBy(() -> new ThinkingTagCleaner((String[]) null)).isInstanceOf(IllegalArgumentException.class)
144143
.hasMessageContaining("patternStrings cannot be null");
145144
}
146145

147146
@Test
148147
void shouldThrowExceptionWhenPatternsAreEmpty() {
149-
assertThatThrownBy(() -> new ThinkingTagCleaner(new String[0]))
150-
.isInstanceOf(IllegalArgumentException.class)
148+
assertThatThrownBy(() -> new ThinkingTagCleaner(new String[0])).isInstanceOf(IllegalArgumentException.class)
151149
.hasMessageContaining("patternStrings cannot be empty");
152150
}
153151

154152
}
155-

0 commit comments

Comments
 (0)