Skip to content

Commit 540a61a

Browse files
authored
[FEAT] Token Stream in Agent class
1 parent 9faf30f commit 540a61a

1 file changed

Lines changed: 110 additions & 6 deletions

File tree

swarms/structs/agent.py

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ class Agent:
215215
preset_stopping_token (bool): Enable preset stopping token
216216
traceback (Any): The traceback
217217
traceback_handlers (Any): The traceback handlers
218-
streaming_on (bool): Enable streaming
218+
streaming_on (bool): Enable basic streaming with formatted panels
219+
stream (bool): Enable detailed token-by-token streaming with metadata (citations, tokens used, etc.)
219220
docs (List[str]): The list of documents
220221
docs_folder (str): The folder containing the documents
221222
verbose (bool): Enable verbose mode
@@ -307,9 +308,9 @@ class Agent:
307308
>>> print(response)
308309
>>> # Generate a report on the financials.
309310
310-
>>> # Real-time streaming example
311-
>>> agent = Agent(model_name="gpt-4.1", max_loops=1, streaming_on=True)
312-
>>> response = agent.run("Tell me a long story.") # Will stream in real-time
311+
>>> # Detailed token streaming example
312+
>>> agent = Agent(model_name="gpt-4.1", max_loops=1, stream=True)
313+
>>> response = agent.run("Tell me a story.") # Will stream each token with detailed metadata
313314
>>> print(response) # Final complete response
314315
315316
>>> # Fallback model example
@@ -363,6 +364,7 @@ def __init__(
363364
traceback: Optional[Any] = None,
364365
traceback_handlers: Optional[Any] = None,
365366
streaming_on: Optional[bool] = False,
367+
stream: Optional[bool] = False,
366368
docs: List[str] = None,
367369
docs_folder: Optional[str] = None,
368370
verbose: Optional[bool] = False,
@@ -512,6 +514,7 @@ def __init__(
512514
self.traceback = traceback
513515
self.traceback_handlers = traceback_handlers
514516
self.streaming_on = streaming_on
517+
self.stream = stream
515518
self.docs = docs
516519
self.docs_folder = docs_folder
517520
self.verbose = verbose
@@ -1317,6 +1320,8 @@ def _run(
13171320
)
13181321
elif self.streaming_on:
13191322
pass
1323+
elif self.stream:
1324+
pass
13201325
else:
13211326
self.pretty_print(
13221327
response, loop_count
@@ -2537,8 +2542,105 @@ def call_llm(
25372542
del kwargs["is_last"]
25382543

25392544
try:
2540-
# Set streaming parameter in LLM if streaming is enabled
2541-
if self.streaming_on and hasattr(self.llm, "stream"):
2545+
if self.stream and hasattr(self.llm, "stream"):
2546+
original_stream = self.llm.stream
2547+
self.llm.stream = True
2548+
2549+
if img is not None:
2550+
streaming_response = self.llm.run(
2551+
task=task, img=img, *args, **kwargs
2552+
)
2553+
else:
2554+
streaming_response = self.llm.run(
2555+
task=task, *args, **kwargs
2556+
)
2557+
2558+
if hasattr(streaming_response, "__iter__") and not isinstance(streaming_response, str):
2559+
complete_response = ""
2560+
token_count = 0
2561+
final_chunk = None
2562+
first_chunk = None
2563+
2564+
for chunk in streaming_response:
2565+
if first_chunk is None:
2566+
first_chunk = chunk
2567+
2568+
if hasattr(chunk, "choices") and chunk.choices[0].delta.content:
2569+
content = chunk.choices[0].delta.content
2570+
complete_response += content
2571+
token_count += 1
2572+
2573+
# Schema per token outputted
2574+
token_info = {
2575+
"token_index": token_count,
2576+
"model": getattr(chunk, 'model', self.get_current_model()),
2577+
"id": getattr(chunk, 'id', ''),
2578+
"created": getattr(chunk, 'created', int(time.time())),
2579+
"object": getattr(chunk, 'object', 'chat.completion.chunk'),
2580+
"token": content,
2581+
"system_fingerprint": getattr(chunk, 'system_fingerprint', ''),
2582+
"finish_reason": chunk.choices[0].finish_reason,
2583+
"citations": getattr(chunk, 'citations', None),
2584+
"provider_specific_fields": getattr(chunk, 'provider_specific_fields', None),
2585+
"service_tier": getattr(chunk, 'service_tier', 'default'),
2586+
"obfuscation": getattr(chunk, 'obfuscation', None),
2587+
"usage": getattr(chunk, 'usage', None),
2588+
"logprobs": chunk.choices[0].logprobs,
2589+
"timestamp": time.time()
2590+
}
2591+
2592+
print(f"ResponseStream {token_info}")
2593+
2594+
if streaming_callback is not None:
2595+
streaming_callback(token_info)
2596+
2597+
final_chunk = chunk
2598+
2599+
#Final ModelResponse to stream
2600+
if final_chunk and hasattr(final_chunk, 'usage') and final_chunk.usage:
2601+
usage = final_chunk.usage
2602+
print(f"ModelResponseStream(id='{getattr(final_chunk, 'id', 'N/A')}', "
2603+
f"created={getattr(final_chunk, 'created', 'N/A')}, "
2604+
f"model='{getattr(final_chunk, 'model', self.get_current_model())}', "
2605+
f"object='{getattr(final_chunk, 'object', 'chat.completion.chunk')}', "
2606+
f"system_fingerprint='{getattr(final_chunk, 'system_fingerprint', 'N/A')}', "
2607+
f"choices=[StreamingChoices(finish_reason='{final_chunk.choices[0].finish_reason}', "
2608+
f"index=0, delta=Delta(provider_specific_fields=None, content=None, role=None, "
2609+
f"function_call=None, tool_calls=None, audio=None), logprobs=None)], "
2610+
f"provider_specific_fields=None, "
2611+
f"usage=Usage(completion_tokens={usage.completion_tokens}, "
2612+
f"prompt_tokens={usage.prompt_tokens}, "
2613+
f"total_tokens={usage.total_tokens}, "
2614+
f"completion_tokens_details=CompletionTokensDetailsWrapper("
2615+
f"accepted_prediction_tokens={usage.completion_tokens_details.accepted_prediction_tokens}, "
2616+
f"audio_tokens={usage.completion_tokens_details.audio_tokens}, "
2617+
f"reasoning_tokens={usage.completion_tokens_details.reasoning_tokens}, "
2618+
f"rejected_prediction_tokens={usage.completion_tokens_details.rejected_prediction_tokens}, "
2619+
f"text_tokens={usage.completion_tokens_details.text_tokens}), "
2620+
f"prompt_tokens_details=PromptTokensDetailsWrapper("
2621+
f"audio_tokens={usage.prompt_tokens_details.audio_tokens}, "
2622+
f"cached_tokens={usage.prompt_tokens_details.cached_tokens}, "
2623+
f"text_tokens={usage.prompt_tokens_details.text_tokens}, "
2624+
f"image_tokens={usage.prompt_tokens_details.image_tokens})))")
2625+
else:
2626+
print(f"ModelResponseStream(id='{getattr(final_chunk, 'id', 'N/A')}', "
2627+
f"created={getattr(final_chunk, 'created', 'N/A')}, "
2628+
f"model='{getattr(final_chunk, 'model', self.get_current_model())}', "
2629+
f"object='{getattr(final_chunk, 'object', 'chat.completion.chunk')}', "
2630+
f"system_fingerprint='{getattr(final_chunk, 'system_fingerprint', 'N/A')}', "
2631+
f"choices=[StreamingChoices(finish_reason='{final_chunk.choices[0].finish_reason}', "
2632+
f"index=0, delta=Delta(provider_specific_fields=None, content=None, role=None, "
2633+
f"function_call=None, tool_calls=None, audio=None), logprobs=None)], "
2634+
f"provider_specific_fields=None)")
2635+
2636+
2637+
self.llm.stream = original_stream
2638+
return complete_response
2639+
else:
2640+
self.llm.stream = original_stream
2641+
return streaming_response
2642+
2643+
elif self.streaming_on and hasattr(self.llm, "stream"):
25422644
original_stream = self.llm.stream
25432645
self.llm.stream = True
25442646

@@ -3023,6 +3125,8 @@ def pretty_print(self, response: str, loop_count: int):
30233125

30243126
if self.streaming_on:
30253127
pass
3128+
elif self.stream:
3129+
pass
30263130

30273131
if self.print_on:
30283132
formatter.print_panel(

0 commit comments

Comments
 (0)