1
+ /*
2
+ * Copyright 2023-2025 the original author or authors.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package org .springframework .ai .openai .api ;
18
+
19
+ import java .util .Arrays ;
20
+ import java .util .Collections ;
21
+ import java .util .List ;
22
+ import java .util .function .Consumer ;
23
+ import static org .assertj .core .api .Assertions .assertThat ;
24
+ import org .junit .jupiter .api .Test ;
25
+ import org .mockito .Mockito ;
26
+
27
+ /**
28
+ * Unit tests for {@link OpenAiStreamFunctionCallingHelper}
29
+ *
30
+ * @author Emmanuel Essiennta
31
+ */
32
+ public class OpenAiStreamFunctionCallingHelperTest {
33
+
34
+ private final OpenAiStreamFunctionCallingHelper helper = new OpenAiStreamFunctionCallingHelper ();
35
+
36
+ @ Test
37
+ public void merge_whenInputIsValid () {
38
+ var expectedResult = new OpenAiApi .ChatCompletionChunk ("id" , Collections .emptyList (),
39
+ System .currentTimeMillis (), "model" , "serviceTier" , "fingerPrint" , "object" , null );
40
+ var previous = new OpenAiApi .ChatCompletionChunk (null , null , expectedResult .created (), expectedResult .model (),
41
+ expectedResult .serviceTier (), null , null , null );
42
+ var current = new OpenAiApi .ChatCompletionChunk (expectedResult .id (), null , null , null , null ,
43
+ expectedResult .systemFingerprint (), expectedResult .object (), expectedResult .usage ());
44
+
45
+ var result = helper .merge (previous , current );
46
+ assertThat (result ).isEqualTo (expectedResult );
47
+ }
48
+
49
+ @ Test
50
+ public void isStreamingToolFunctionCall_whenChatCompletionChunkIsNull () {
51
+ assertThat (helper .isStreamingToolFunctionCall (null )).isFalse ();
52
+ }
53
+
54
+ @ Test
55
+ public void isStreamingToolFunctionCall_whenChatCompletionChunkChoicesIsEmpty () {
56
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , Collections .emptyList (), null , null , null , null , null ,
57
+ null );
58
+ assertThat (helper .isStreamingToolFunctionCall (chunk )).isFalse ();
59
+ }
60
+
61
+ @ Test
62
+ public void isStreamingToolFunctionCall_whenChatCompletionChunkFirstChoiceIsNull () {
63
+ var choice = (org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice ) null ;
64
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , Arrays .asList (choice ), null , null , null , null , null , null );
65
+ assertThat (helper .isStreamingToolFunctionCall (chunk )).isFalse ();
66
+ }
67
+
68
+ @ Test
69
+ public void isStreamingToolFunctionCall_whenChatCompletionChunkFirstChoiceDeltaIsNull () {
70
+ var choice = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (null , null , null ,
71
+ Mockito .mock (OpenAiApi .LogProbs .class ));
72
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , Arrays .asList (choice , null ), null , null , null , null , null ,
73
+ null );
74
+ assertThat (helper .isStreamingToolFunctionCall (chunk )).isFalse ();
75
+ }
76
+
77
+ @ Test
78
+ public void isStreamingToolFunctionCall_whenChatCompletionChunkFirstChoiceDeltaToolCallsIsNullOrEmpty () {
79
+ var assertion = (Consumer <OpenAiApi .ChatCompletionMessage >) (OpenAiApi .ChatCompletionMessage delta ) -> {
80
+ var choice = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (null , null ,
81
+ delta , Mockito .mock (OpenAiApi .LogProbs .class ));
82
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , List .of (choice ), null , null , null , null , null , null );
83
+ assertThat (helper .isStreamingToolFunctionCall (chunk )).isFalse ();
84
+ };
85
+ // Test for null.
86
+ assertion .accept (new OpenAiApi .ChatCompletionMessage (null , null ));
87
+ // Test for empty.
88
+ assertion .accept (
89
+ new OpenAiApi .ChatCompletionMessage (null , null , null , null , Collections .emptyList (), null , null , null ));
90
+ }
91
+
92
+ @ Test
93
+ public void isStreamingToolFunctionCall_whenChatCompletionChunkFirstChoiceDeltaToolCallsIsNonEmpty () {
94
+ var assertion = (Consumer <OpenAiApi .ChatCompletionMessage >) (OpenAiApi .ChatCompletionMessage delta ) -> {
95
+ var choice = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (null , null ,
96
+ delta , null );
97
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , List .of (choice ), null , null , null , null , null , null );
98
+ assertThat (helper .isStreamingToolFunctionCall (chunk )).isTrue ();
99
+ };
100
+ assertion .accept (new OpenAiApi .ChatCompletionMessage (null , null , null , null ,
101
+ List .of (Mockito .mock (org .springframework .ai .openai .api .OpenAiApi .ChatCompletionMessage .ToolCall .class )),
102
+ null , null , null ));
103
+ }
104
+
105
+ @ Test
106
+ public void isStreamingToolFunctionCallFinish_whenChatCompletionChunkIsNull () {
107
+ assertThat (helper .isStreamingToolFunctionCallFinish (null )).isFalse ();
108
+ }
109
+
110
+ @ Test
111
+ public void isStreamingToolFunctionCallFinish_whenChatCompletionChunkChoicesIsEmpty () {
112
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , Collections .emptyList (), null , null , null , null , null ,
113
+ null );
114
+ assertThat (helper .isStreamingToolFunctionCallFinish (chunk )).isFalse ();
115
+ }
116
+
117
+ @ Test
118
+ public void isStreamingToolFunctionCallFinish_whenChatCompletionChunkFirstChoiceIsNull () {
119
+ var choice = (org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice ) null ;
120
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , Arrays .asList (choice ), null , null , null , null , null , null );
121
+ assertThat (helper .isStreamingToolFunctionCallFinish (chunk )).isFalse ();
122
+ }
123
+
124
+ @ Test
125
+ public void isStreamingToolFunctionCallFinish_whenChatCompletionChunkFirstChoiceDeltaIsNull () {
126
+ var choice = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (null , null , null ,
127
+ Mockito .mock (OpenAiApi .LogProbs .class ));
128
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , Arrays .asList (choice , null ), null , null , null , null , null ,
129
+ null );
130
+ assertThat (helper .isStreamingToolFunctionCallFinish (chunk )).isFalse ();
131
+ }
132
+
133
+ @ Test
134
+ public void isStreamingToolFunctionCallFinish_whenChatCompletionChunkFirstChoiceIsNotToolCalls () {
135
+ var choice = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (null , null ,
136
+ new OpenAiApi .ChatCompletionMessage (null , null ), Mockito .mock (OpenAiApi .LogProbs .class ));
137
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , List .of (choice ), null , null , null , null , null , null );
138
+ assertThat (helper .isStreamingToolFunctionCallFinish (chunk )).isFalse ();
139
+ }
140
+
141
+ @ Test
142
+ public void isStreamingToolFunctionCallFinish_whenChatCompletionChunkFirstChoiceIsToolCalls () {
143
+ var choice = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (
144
+ OpenAiApi .ChatCompletionFinishReason .TOOL_CALLS , null , new OpenAiApi .ChatCompletionMessage (null , null ),
145
+ Mockito .mock (OpenAiApi .LogProbs .class ));
146
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , List .of (choice ), null , null , null , null , null , null );
147
+ assertThat (helper .isStreamingToolFunctionCallFinish (chunk )).isTrue ();
148
+ }
149
+
150
+ @ Test
151
+ public void chunkToChatCompletion_whenInputIsValid () {
152
+ var choice1 = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (
153
+ OpenAiApi .ChatCompletionFinishReason .TOOL_CALLS , 1 , new OpenAiApi .ChatCompletionMessage (null , null ),
154
+ Mockito .mock (OpenAiApi .LogProbs .class ));
155
+ var choice2 = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (
156
+ OpenAiApi .ChatCompletionFinishReason .TOOL_CALLS , 2 , new OpenAiApi .ChatCompletionMessage (null , null ),
157
+ Mockito .mock (OpenAiApi .LogProbs .class ));
158
+ var chunk = new OpenAiApi .ChatCompletionChunk (null , List .of (choice1 , choice2 ), null , null , null , null , null ,
159
+ null );
160
+ OpenAiApi .ChatCompletion result = helper .chunkToChatCompletion (chunk );
161
+ assertThat (result .object ()).isEqualTo ("chat.completion" );
162
+ assertThat (result .choices ()).hasSize (2 );
163
+ }
164
+
165
+ }
0 commit comments