1717import com .alibaba .dashscope .protocol .HttpMethod ;
1818import com .alibaba .dashscope .protocol .Protocol ;
1919import com .alibaba .dashscope .protocol .StreamingMode ;
20+ import com .alibaba .dashscope .tools .ToolCallBase ;
21+ import com .alibaba .dashscope .tools .ToolCallFunction ;
2022import com .alibaba .dashscope .utils .ParamUtils ;
2123import io .reactivex .Flowable ;
22- import lombok . extern . slf4j . Slf4j ;
24+ import java . util . ArrayList ;
2325import java .util .HashMap ;
24- import java .util .Map ;
2526import java .util .List ;
26- import java .util .ArrayList ;
27- import com .alibaba .dashscope .tools .ToolCallBase ;
28- import com .alibaba .dashscope .tools .ToolCallFunction ;
27+ import java .util .Map ;
28+ import lombok .extern .slf4j .Slf4j ;
2929
3030@ Slf4j
3131public final class Generation {
@@ -169,26 +169,29 @@ public Flowable<GenerationResult> streamCall(HalfDuplexServiceParam param)
169169
170170 serviceOption .setIsSSE (true );
171171 serviceOption .setStreamingMode (StreamingMode .OUT );
172- return syncApi .streamCall (param )
172+ return syncApi
173+ .streamCall (param )
173174 .map (GenerationResult ::fromDashScopeResult )
174- .flatMap (result -> {
175- GenerationResult merged =
176- mergeSingleResponse (result , toMergeResponse , param );
177- if (merged == null ) {
178- return Flowable .empty ();
179- }
180- return Flowable .just (merged );
181- })
182- .doOnComplete (() -> {
183- if (toMergeResponse ) {
184- clearAccumulatedData ();
185- }
186- })
187- .doOnError (throwable -> {
188- if (toMergeResponse ) {
189- clearAccumulatedData ();
190- }
191- });
175+ .flatMap (
176+ result -> {
177+ GenerationResult merged = mergeSingleResponse (result , toMergeResponse , param );
178+ if (merged == null ) {
179+ return Flowable .empty ();
180+ }
181+ return Flowable .just (merged );
182+ })
183+ .doOnComplete (
184+ () -> {
185+ if (toMergeResponse ) {
186+ clearAccumulatedData ();
187+ }
188+ })
189+ .doOnError (
190+ throwable -> {
191+ if (toMergeResponse ) {
192+ clearAccumulatedData ();
193+ }
194+ });
192195 }
193196
194197 public void streamCall (HalfDuplexServiceParam param , ResultCallback <GenerationResult > callback )
@@ -236,9 +239,9 @@ public void onError(Exception e) {
236239 }
237240
238241 /**
239- * Modifies the parameters for internal streaming optimization.
240- * If incrementalOutput is false, modifies the GenerationParam object to set
241- * incrementalOutput to true for internal streaming optimization.
242+ * Modifies the parameters for internal streaming optimization. If incrementalOutput is false,
243+ * modifies the GenerationParam object to set incrementalOutput to true for internal streaming
244+ * optimization.
242245 *
243246 * @param param The parameter object to modify
244247 * @return true if the parameter was modified, false otherwise
@@ -248,8 +251,8 @@ private boolean modifyIncrementalOutput(HalfDuplexServiceParam param) {
248251 if (param instanceof GenerationParam ) {
249252 GenerationParam generationParam = (GenerationParam ) param ;
250253 Boolean incrementalOutput = generationParam .getIncrementalOutput ();
251- if (ParamUtils .shouldModifyIncrementalOutput (param .getModel ()) &&
252- Boolean .FALSE .equals (incrementalOutput )) {
254+ if (ParamUtils .shouldModifyIncrementalOutput (param .getModel ())
255+ && Boolean .FALSE .equals (incrementalOutput )) {
253256 // Modify the GenerationParam object to enable incremental output
254257 generationParam .setIncrementalOutput (true );
255258 return true ;
@@ -259,20 +262,17 @@ private boolean modifyIncrementalOutput(HalfDuplexServiceParam param) {
259262 }
260263
261264 /**
262- * Merges a single GenerationResult with accumulated data for
263- * non-incremental output simulation.
264- * This method accumulates content and tool_calls from streaming responses.
265- * Supports both legacy format (output.text) and new format
266- * (output.choices[].message.content).
265+ * Merges a single GenerationResult with accumulated data for non-incremental output simulation.
266+ * This method accumulates content and tool_calls from streaming responses. Supports both legacy
267+ * format (output.text) and new format (output.choices[].message.content).
267268 *
268269 * @param result The GenerationResult to merge
269- * @param toMergeResponse Whether to perform merging (based on original
270- * incrementalOutput setting)
270+ * @param toMergeResponse Whether to perform merging (based on original incrementalOutput setting)
271271 * @param param The HalfDuplexServiceParam to get n parameter
272272 * @return The merged GenerationResult, or null if should be filtered out
273273 */
274- private GenerationResult mergeSingleResponse (GenerationResult result ,
275- boolean toMergeResponse , HalfDuplexServiceParam param ) {
274+ private GenerationResult mergeSingleResponse (
275+ GenerationResult result , boolean toMergeResponse , HalfDuplexServiceParam param ) {
276276 if (!toMergeResponse || result == null || result .getOutput () == null ) {
277277 return result ;
278278 }
@@ -291,8 +291,7 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
291291
292292 // Check if all choices have been sent (for n > 1 case)
293293 if (n > 1 && !accumulatedData .isEmpty ()) {
294- boolean allSent = accumulatedData .values ().stream ()
295- .allMatch (data -> data .allChoicesSent );
294+ boolean allSent = accumulatedData .values ().stream ().allMatch (data -> data .allChoicesSent );
296295 if (allSent ) {
297296 return null ;
298297 }
@@ -315,13 +314,12 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
315314 }
316315
317316 // Initialize accumulated data for this choice index if not exists
318- AccumulatedData accumulated = accumulatedData . computeIfAbsent (
319- choiceIndex , k -> new AccumulatedData ());
317+ AccumulatedData accumulated =
318+ accumulatedData . computeIfAbsent ( choiceIndex , k -> new AccumulatedData ());
320319
321320 if (choice .getMessage () != null ) {
322321 // Save role if present
323- if (choice .getMessage ().getRole () != null &&
324- !choice .getMessage ().getRole ().isEmpty ()) {
322+ if (choice .getMessage ().getRole () != null && !choice .getMessage ().getRole ().isEmpty ()) {
325323 accumulated .role = choice .getMessage ().getRole ();
326324 }
327325
@@ -356,16 +354,17 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
356354 }
357355
358356 // Restore role if we have it
359- if (accumulated .role != null &&
360- (choice .getMessage ().getRole () == null ||
361- choice .getMessage ().getRole ().isEmpty ())) {
357+ if (accumulated .role != null
358+ && (choice .getMessage ().getRole () == null
359+ || choice .getMessage ().getRole ().isEmpty ())) {
362360 choice .getMessage ().setRole (accumulated .role );
363361 }
364362 }
365363
366364 // Handle logprobs accumulation
367365 if (choice .getLogprobs () != null && choice .getLogprobs ().getContent () != null ) {
368- List <GenerationLogprobs .Content > currentLogprobsContent = choice .getLogprobs ().getContent ();
366+ List <GenerationLogprobs .Content > currentLogprobsContent =
367+ choice .getLogprobs ().getContent ();
369368 if (!currentLogprobsContent .isEmpty ()) {
370369 accumulated .logprobsContent .addAll (currentLogprobsContent );
371370 }
@@ -376,18 +375,18 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
376375 }
377376
378377 // Handle finish_reason for n > 1 case
379- if (n > 1 && choice .getFinishReason () != null &&
380- !choice .getFinishReason ().equals ("null" )) {
378+ if (n > 1 && choice .getFinishReason () != null && !choice .getFinishReason ().equals ("null" )) {
381379 accumulated .finishReason = choice .getFinishReason ();
382380 accumulated .finished = true ;
383381 }
384382 }
385383
386384 // Store output_tokens for each choice when n > 1
387385 // Each streaming packet contains usage info for one specific choice
388- if (n > 1 && result .getUsage () != null &&
389- result .getUsage ().getOutputTokens () != null &&
390- !choices .isEmpty ()) {
386+ if (n > 1
387+ && result .getUsage () != null
388+ && result .getUsage ().getOutputTokens () != null
389+ && !choices .isEmpty ()) {
391390 // Get the choice index from the first choice in this packet
392391 Integer choiceIndex = choices .get (0 ).getIndex ();
393392 if (choiceIndex == null ) {
@@ -414,11 +413,9 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
414413 String currentFinishReason = null ;
415414 Integer currentChoiceIndex = null ;
416415 for (GenerationOutput .Choice choice : choices ) {
417- if (choice .getFinishReason () != null &&
418- !choice .getFinishReason ().equals ("null" )) {
416+ if (choice .getFinishReason () != null && !choice .getFinishReason ().equals ("null" )) {
419417 currentFinishReason = choice .getFinishReason ();
420- currentChoiceIndex =
421- choice .getIndex () != null ? choice .getIndex () : 0 ;
418+ currentChoiceIndex = choice .getIndex () != null ? choice .getIndex () : 0 ;
422419 break ;
423420 }
424421 }
@@ -433,8 +430,7 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
433430 if (finishedCount < n ) {
434431 // Hide finish_reason until all finished
435432 for (GenerationOutput .Choice choice : choices ) {
436- if (choice .getFinishReason () != null &&
437- !choice .getFinishReason ().equals ("null" )) {
433+ if (choice .getFinishReason () != null && !choice .getFinishReason ().equals ("null" )) {
438434 choice .setFinishReason ("null" );
439435 }
440436 }
@@ -446,8 +442,7 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
446442 GenerationOutput output = result .getOutput ();
447443 List <GenerationOutput .Choice > allChoices = new ArrayList <>();
448444 int totalOutputTokens = 0 ;
449- for (Map .Entry <Integer , AccumulatedData > entry :
450- accumulatedData .entrySet ()) {
445+ for (Map .Entry <Integer , AccumulatedData > entry : accumulatedData .entrySet ()) {
451446 Integer index = entry .getKey ();
452447 AccumulatedData data = entry .getValue ();
453448 GenerationOutput .Choice finalChoice = output .new Choice ();
@@ -480,8 +475,9 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
480475 if (result .getUsage () != null && totalOutputTokens > 0 ) {
481476 result .getUsage ().setOutputTokens (totalOutputTokens );
482477 if (result .getUsage ().getInputTokens () != null ) {
483- result .getUsage ().setTotalTokens (
484- result .getUsage ().getInputTokens () + totalOutputTokens );
478+ result
479+ .getUsage ()
480+ .setTotalTokens (result .getUsage ().getInputTokens () + totalOutputTokens );
485481 }
486482 }
487483 }
@@ -494,15 +490,15 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
494490 currentData .allChoicesSent = true ;
495491 // Reuse current choice in result, just update it
496492 for (GenerationOutput .Choice choice : choices ) {
497- if (choice .getIndex () != null &&
498- choice .getIndex ().equals (currentChoiceIndex )) {
493+ if (choice .getIndex () != null && choice .getIndex ().equals (currentChoiceIndex )) {
499494 // Update usage with this choice's output tokens
500495 if (result .getUsage () != null && currentData .outputTokens != null ) {
501496 result .getUsage ().setOutputTokens (currentData .outputTokens );
502497 if (result .getUsage ().getInputTokens () != null ) {
503- result .getUsage ().setTotalTokens (
504- result .getUsage ().getInputTokens () +
505- currentData .outputTokens );
498+ result
499+ .getUsage ()
500+ .setTotalTokens (
501+ result .getUsage ().getInputTokens () + currentData .outputTokens );
506502 }
507503 }
508504 return result ;
@@ -529,10 +525,9 @@ private GenerationResult mergeSingleResponse(GenerationResult result,
529525 return result ;
530526 }
531527
532- /**
533- * Merges tool calls from current response with accumulated tool calls.
534- */
535- private void mergeToolCalls (List <ToolCallBase > currentToolCalls , List <ToolCallBase > accumulatedToolCalls ) {
528+ /** Merges tool calls from current response with accumulated tool calls. */
529+ private void mergeToolCalls (
530+ List <ToolCallBase > currentToolCalls , List <ToolCallBase > accumulatedToolCalls ) {
536531 for (ToolCallBase currentCall : currentToolCalls ) {
537532 if (currentCall == null || currentCall .getIndex () == null ) {
538533 continue ;
@@ -543,15 +538,13 @@ private void mergeToolCalls(List<ToolCallBase> currentToolCalls, List<ToolCallBa
543538 // Find existing accumulated call with same index
544539 ToolCallBase existingCall = null ;
545540 for (ToolCallBase accCall : accumulatedToolCalls ) {
546- if (accCall != null && accCall .getIndex () != null &&
547- accCall .getIndex ().equals (index )) {
541+ if (accCall != null && accCall .getIndex () != null && accCall .getIndex ().equals (index )) {
548542 existingCall = accCall ;
549543 break ;
550544 }
551545 }
552546
553- if (existingCall instanceof ToolCallFunction &&
554- currentCall instanceof ToolCallFunction ) {
547+ if (existingCall instanceof ToolCallFunction && currentCall instanceof ToolCallFunction ) {
555548 // Merge function calls
556549 ToolCallFunction existingFunctionCall = (ToolCallFunction ) existingCall ;
557550 ToolCallFunction currentFunctionCall = (ToolCallFunction ) currentCall ;
@@ -584,7 +577,9 @@ private void mergeToolCalls(List<ToolCallBase> currentToolCalls, List<ToolCallBa
584577
585578 // Update function output if present
586579 if (currentFunctionCall .getFunction ().getOutput () != null ) {
587- existingFunctionCall .getFunction ().setOutput (currentFunctionCall .getFunction ().getOutput ());
580+ existingFunctionCall
581+ .getFunction ()
582+ .setOutput (currentFunctionCall .getFunction ().getOutput ());
588583 }
589584 }
590585
@@ -617,25 +612,24 @@ private void mergeToolCalls(List<ToolCallBase> currentToolCalls, List<ToolCallBa
617612
618613 accumulatedToolCalls .add (newFunctionCall );
619614 } else {
620- // For other types of tool calls, add directly (assuming they are immutable or don't need merging)
615+ // For other types of tool calls, add directly (assuming they are immutable or don't need
616+ // merging)
621617 accumulatedToolCalls .add (currentCall );
622618 }
623619 }
624620 }
625621 }
626622
627623 /**
628- * Clears accumulated data for the current thread.
629- * Should be called when streaming is complete or encounters error.
624+ * Clears accumulated data for the current thread. Should be called when streaming is complete or
625+ * encounters error.
630626 */
631627 private void clearAccumulatedData () {
632628 accumulatedDataMap .get ().clear ();
633629 accumulatedDataMap .remove ();
634630 }
635631
636- /**
637- * Inner class to store accumulated data for response merging.
638- */
632+ /** Inner class to store accumulated data for response merging. */
639633 private static class AccumulatedData {
640634 StringBuilder content = new StringBuilder ();
641635 StringBuilder reasoningContent = new StringBuilder ();
0 commit comments