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 Essien-nta
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+ null );
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 , null );
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+ null );
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 ), null );
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+ null );
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+ null );
155+ var choice2 = new org .springframework .ai .openai .api .OpenAiApi .ChatCompletionChunk .ChunkChoice (
156+ OpenAiApi .ChatCompletionFinishReason .TOOL_CALLS , 2 , new OpenAiApi .ChatCompletionMessage (null , null ),
157+ null );
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