Skip to content

Commit 347846d

Browse files
authored
Merge pull request #1167 from aparekh02/tokenstream
[FEAT] [DOCS] [EXAMPLE]Token Stream in Agent class
2 parents d018b00 + 4aacf32 commit 347846d

3 files changed

Lines changed: 137 additions & 7 deletions

File tree

docs/swarms/structs/agent.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ The `Agent` class establishes a conversational loop with a language model, allow
8383
| `traceback` | `Optional[Any]` | Object used for traceback handling. |
8484
| `traceback_handlers` | `Optional[Any]` | List of traceback handlers. |
8585
| `streaming_on` | `Optional[bool]` | Boolean indicating whether to stream responses. |
86+
| `stream` | `Optional[bool]` | Boolean indicating whether to enable detailed token-by-token streaming with metadata. |
8687
| `docs` | `List[str]` | List of document paths or contents to be ingested. |
8788
| `docs_folder` | `Optional[str]` | Path to a folder containing documents to be ingested. |
8889
| `verbose` | `Optional[bool]` | Boolean indicating whether to print verbose output. |
@@ -759,6 +760,22 @@ print(agent.system_prompt)
759760

760761
```
761762

763+
### Token-by-Token Streaming
764+
765+
```python
766+
from swarms import Agent
767+
768+
# Initialize agent with detailed streaming
769+
agent = Agent(
770+
model_name="gpt-4.1",
771+
max_loops=1,
772+
stream=True, # Enable detailed token-by-token streaming
773+
)
774+
775+
# Run with detailed streaming - each token shows metadata
776+
agent.run("Tell me a short story about a robot learning to paint.")
777+
```
778+
762779
## Agent Structured Outputs
763780

764781
- Create a structured output schema for the agent [List[Dict]]
@@ -1112,4 +1129,4 @@ The `run` method now supports several new parameters for advanced functionality:
11121129
| `tool_retry_attempts` | Configure tool_retry_attempts for robust tool execution in production environments. |
11131130
| `handoffs` | Use handoffs to create specialized agent teams that can intelligently route tasks based on complexity and expertise requirements. |
11141131

1115-
By following these guidelines and leveraging the Swarm Agent's extensive features, you can create powerful, flexible, and efficient autonomous agents for a wide range of applications.
1132+
By following these guidelines and leveraging the Swarm Agent's extensive features, you can create powerful, flexible, and efficient autonomous agents for a wide range of applications.

swarms/structs/agent.py

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ class Agent:
218218
preset_stopping_token (bool): Enable preset stopping token
219219
traceback (Any): The traceback
220220
traceback_handlers (Any): The traceback handlers
221-
streaming_on (bool): Enable streaming
221+
streaming_on (bool): Enable basic streaming with formatted panels
222+
stream (bool): Enable detailed token-by-token streaming with metadata (citations, tokens used, etc.)
222223
docs (List[str]): The list of documents
223224
docs_folder (str): The folder containing the documents
224225
verbose (bool): Enable verbose mode
@@ -310,9 +311,9 @@ class Agent:
310311
>>> print(response)
311312
>>> # Generate a report on the financials.
312313
313-
>>> # Real-time streaming example
314-
>>> agent = Agent(model_name="gpt-4.1", max_loops=1, streaming_on=True)
315-
>>> response = agent.run("Tell me a long story.") # Will stream in real-time
314+
>>> # Detailed token streaming example
315+
>>> agent = Agent(model_name="gpt-4.1", max_loops=1, stream=True)
316+
>>> response = agent.run("Tell me a story.") # Will stream each token with detailed metadata
316317
>>> print(response) # Final complete response
317318
318319
>>> # Fallback model example
@@ -366,6 +367,7 @@ def __init__(
366367
traceback: Optional[Any] = None,
367368
traceback_handlers: Optional[Any] = None,
368369
streaming_on: Optional[bool] = False,
370+
stream: Optional[bool] = False,
369371
docs: List[str] = None,
370372
docs_folder: Optional[str] = None,
371373
verbose: Optional[bool] = False,
@@ -516,6 +518,7 @@ def __init__(
516518
self.traceback = traceback
517519
self.traceback_handlers = traceback_handlers
518520
self.streaming_on = streaming_on
521+
self.stream = stream
519522
self.docs = docs
520523
self.docs_folder = docs_folder
521524
self.verbose = verbose
@@ -1346,6 +1349,8 @@ def _run(
13461349
)
13471350
elif self.streaming_on:
13481351
pass
1352+
elif self.stream:
1353+
pass
13491354
else:
13501355
self.pretty_print(
13511356
response, loop_count
@@ -2566,8 +2571,105 @@ def call_llm(
25662571
del kwargs["is_last"]
25672572

25682573
try:
2569-
# Set streaming parameter in LLM if streaming is enabled
2570-
if self.streaming_on and hasattr(self.llm, "stream"):
2574+
if self.stream and hasattr(self.llm, "stream"):
2575+
original_stream = self.llm.stream
2576+
self.llm.stream = True
2577+
2578+
if img is not None:
2579+
streaming_response = self.llm.run(
2580+
task=task, img=img, *args, **kwargs
2581+
)
2582+
else:
2583+
streaming_response = self.llm.run(
2584+
task=task, *args, **kwargs
2585+
)
2586+
2587+
if hasattr(streaming_response, "__iter__") and not isinstance(streaming_response, str):
2588+
complete_response = ""
2589+
token_count = 0
2590+
final_chunk = None
2591+
first_chunk = None
2592+
2593+
for chunk in streaming_response:
2594+
if first_chunk is None:
2595+
first_chunk = chunk
2596+
2597+
if hasattr(chunk, "choices") and chunk.choices[0].delta.content:
2598+
content = chunk.choices[0].delta.content
2599+
complete_response += content
2600+
token_count += 1
2601+
2602+
# Schema per token outputted
2603+
token_info = {
2604+
"token_index": token_count,
2605+
"model": getattr(chunk, 'model', self.get_current_model()),
2606+
"id": getattr(chunk, 'id', ''),
2607+
"created": getattr(chunk, 'created', int(time.time())),
2608+
"object": getattr(chunk, 'object', 'chat.completion.chunk'),
2609+
"token": content,
2610+
"system_fingerprint": getattr(chunk, 'system_fingerprint', ''),
2611+
"finish_reason": chunk.choices[0].finish_reason,
2612+
"citations": getattr(chunk, 'citations', None),
2613+
"provider_specific_fields": getattr(chunk, 'provider_specific_fields', None),
2614+
"service_tier": getattr(chunk, 'service_tier', 'default'),
2615+
"obfuscation": getattr(chunk, 'obfuscation', None),
2616+
"usage": getattr(chunk, 'usage', None),
2617+
"logprobs": chunk.choices[0].logprobs,
2618+
"timestamp": time.time()
2619+
}
2620+
2621+
print(f"ResponseStream {token_info}")
2622+
2623+
if streaming_callback is not None:
2624+
streaming_callback(token_info)
2625+
2626+
final_chunk = chunk
2627+
2628+
#Final ModelResponse to stream
2629+
if final_chunk and hasattr(final_chunk, 'usage') and final_chunk.usage:
2630+
usage = final_chunk.usage
2631+
print(f"ModelResponseStream(id='{getattr(final_chunk, 'id', 'N/A')}', "
2632+
f"created={getattr(final_chunk, 'created', 'N/A')}, "
2633+
f"model='{getattr(final_chunk, 'model', self.get_current_model())}', "
2634+
f"object='{getattr(final_chunk, 'object', 'chat.completion.chunk')}', "
2635+
f"system_fingerprint='{getattr(final_chunk, 'system_fingerprint', 'N/A')}', "
2636+
f"choices=[StreamingChoices(finish_reason='{final_chunk.choices[0].finish_reason}', "
2637+
f"index=0, delta=Delta(provider_specific_fields=None, content=None, role=None, "
2638+
f"function_call=None, tool_calls=None, audio=None), logprobs=None)], "
2639+
f"provider_specific_fields=None, "
2640+
f"usage=Usage(completion_tokens={usage.completion_tokens}, "
2641+
f"prompt_tokens={usage.prompt_tokens}, "
2642+
f"total_tokens={usage.total_tokens}, "
2643+
f"completion_tokens_details=CompletionTokensDetailsWrapper("
2644+
f"accepted_prediction_tokens={usage.completion_tokens_details.accepted_prediction_tokens}, "
2645+
f"audio_tokens={usage.completion_tokens_details.audio_tokens}, "
2646+
f"reasoning_tokens={usage.completion_tokens_details.reasoning_tokens}, "
2647+
f"rejected_prediction_tokens={usage.completion_tokens_details.rejected_prediction_tokens}, "
2648+
f"text_tokens={usage.completion_tokens_details.text_tokens}), "
2649+
f"prompt_tokens_details=PromptTokensDetailsWrapper("
2650+
f"audio_tokens={usage.prompt_tokens_details.audio_tokens}, "
2651+
f"cached_tokens={usage.prompt_tokens_details.cached_tokens}, "
2652+
f"text_tokens={usage.prompt_tokens_details.text_tokens}, "
2653+
f"image_tokens={usage.prompt_tokens_details.image_tokens})))")
2654+
else:
2655+
print(f"ModelResponseStream(id='{getattr(final_chunk, 'id', 'N/A')}', "
2656+
f"created={getattr(final_chunk, 'created', 'N/A')}, "
2657+
f"model='{getattr(final_chunk, 'model', self.get_current_model())}', "
2658+
f"object='{getattr(final_chunk, 'object', 'chat.completion.chunk')}', "
2659+
f"system_fingerprint='{getattr(final_chunk, 'system_fingerprint', 'N/A')}', "
2660+
f"choices=[StreamingChoices(finish_reason='{final_chunk.choices[0].finish_reason}', "
2661+
f"index=0, delta=Delta(provider_specific_fields=None, content=None, role=None, "
2662+
f"function_call=None, tool_calls=None, audio=None), logprobs=None)], "
2663+
f"provider_specific_fields=None)")
2664+
2665+
2666+
self.llm.stream = original_stream
2667+
return complete_response
2668+
else:
2669+
self.llm.stream = original_stream
2670+
return streaming_response
2671+
2672+
elif self.streaming_on and hasattr(self.llm, "stream"):
25712673
original_stream = self.llm.stream
25722674
self.llm.stream = True
25732675

@@ -3052,6 +3154,8 @@ def pretty_print(self, response: str, loop_count: int):
30523154

30533155
if self.streaming_on:
30543156
pass
3157+
elif self.stream:
3158+
pass
30553159

30563160
if self.print_on:
30573161
formatter.print_panel(
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from swarms.structs.agent import Agent
2+
3+
agent = Agent(
4+
model_name="gpt-4.1",
5+
max_loops=1,
6+
stream=True,
7+
)
8+
9+
agent.run("Tell me a short story about a robot learning to paint.")

0 commit comments

Comments
 (0)