Skip to content

Commit b64a851

Browse files
authored
Merge pull request #414 from SylphAI-Inc/release_1.05a1
Release for version 1.05a1
2 parents 7b4d832 + 5eb72b4 commit b64a851

7 files changed

Lines changed: 81 additions & 51 deletions

File tree

adalflow/adalflow/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.0.4"
1+
__version__ = "1.0.5a1"
22

33
from adalflow.core.component import (
44
Component,

adalflow/adalflow/core/agent.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -244,21 +244,29 @@ def create_default_planner(
244244

245245

246246
class Agent(Component):
247-
"""
248-
An agent is a high-level component that holds (1) a generator as task plannaer (calling tools) and (2) a tool manager to manage tools.
247+
"""A high-level agentic component that orchestrates AI planning and tool execution.
248+
249+
The Agent combines a Generator-based planner for task decomposition with a ToolManager
250+
for function calling. It uses a ReAct (Reasoning and Acting) architecture to iteratively
251+
plan steps and execute tools to solve complex tasks.
249252
250-
It comes with default prompt template that instructs LLM (1) agentic task description (2) template on adding definitions of tools
251-
(3) arguments to fill in history.
253+
The Agent comes with default prompt templates for agentic reasoning, automatic tool
254+
definition integration, and step history tracking. It includes built-in helper tools:
255+
- finish: Terminates execution with the final answer
256+
- llm_tool: Fallback tool using LLM world knowledge for simple queries
252257
253-
Additionally, it comes with two helper tools:
254-
1. finish: to finish the task
255-
2. additional_llm_tool: to answer any input query with llm's world knowledge. Use me as a fallback tool or when the query is simple.
258+
Architecture:
259+
Agent contains two main components:
260+
1. Planner (Generator): Plans and reasons about next actions using an LLM
261+
2. ToolManager: Manages and executes available tools/functions
256262
257263
Attributes:
258-
name (str): Name of the agent
259-
tool_manager (ToolManager): Stores and manages tools
260-
generator (Generator): Handles text generation with a language model
261-
the output_processors must return the type StepOutput
264+
name (str): Unique identifier for the agent instance
265+
tool_manager (ToolManager): Manages available tools and their execution
266+
planner (Generator): LLM-based planner for task decomposition and reasoning
267+
answer_data_type (Type): Expected type for the final answer output
268+
max_steps (int): Maximum number of planning steps allowed
269+
is_thinking_model (bool): Whether the underlying model supports chain-of-thought
262270
"""
263271

264272
def __init__(

adalflow/adalflow/core/generator.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -496,8 +496,8 @@ def _model_client_call(self, api_kwargs: Dict, use_cache: bool = False) -> Any:
496496
completion = self.model_client.call(
497497
api_kwargs=api_kwargs, model_type=self.model_type
498498
)
499-
# prepare cache
500-
if use_cache:
499+
# prepare cache - skip caching for streaming responses which contain unpickleable threading objects
500+
if use_cache and not api_kwargs.get("stream", False):
501501
self._save_cache(index_content, completion)
502502
return completion
503503
except Exception as e:
@@ -521,8 +521,8 @@ async def _async_model_client_call(
521521
completion = await self.model_client.acall(
522522
api_kwargs=api_kwargs, model_type=self.model_type
523523
)
524-
# save to cache
525-
if use_cache:
524+
# save to cache - skip caching for streaming responses which contain unpickleable threading objects
525+
if use_cache and not api_kwargs.get("stream", False):
526526
self._save_cache(index_content, completion)
527527
return completion
528528
except Exception as e:

adalflow/adalflow/core/runner.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,32 @@
6262

6363

6464
class Runner(Component):
65-
"""A runner class that executes an Agent instance with multi-step execution.
66-
67-
It internally maintains a planner LLM and an executor and adds a LLM call to the executor as a tool for the planner.
68-
69-
The output to the planner agent call is expected to be a Function object. The planner iterates through at most
70-
max_steps unless the planner sets the action to "finish" then the planner returns the final response.
71-
72-
If the user optionally specifies the output_type then the Runner parses the Function object to the output_type.
65+
"""Executes Agent instances with multi-step iterative planning and tool execution.
66+
67+
The Runner orchestrates the execution of an Agent through multiple reasoning and action
68+
cycles. It manages the step-by-step execution loop where the Agent's planner generates
69+
Function calls that get executed by the ToolManager, with results fed back into the
70+
planning context for the next iteration.
71+
72+
Execution Flow:
73+
1. Initialize step history and prompt context
74+
2. For each step (up to max_steps):
75+
a. Call Agent's planner to get next Function
76+
b. Execute the Function using ToolManager
77+
c. Add step result to history
78+
d. Check if Function is "finish" to terminate
79+
3. Process final answer to expected output type
80+
81+
The Runner supports both synchronous and asynchronous execution modes, as well as
82+
streaming execution with real-time event emission. It includes comprehensive tracing
83+
and error handling throughout the execution pipeline.
7384
7485
Attributes:
75-
planner (Agent): The agent instance to execute
76-
config (RunnerConfig): Configuration for the runner
77-
max_steps (int): Maximum number of steps to execute
86+
agent (Agent): The Agent instance to execute
87+
max_steps (int): Maximum number of execution steps allowed
88+
answer_data_type (Type): Expected type for final answer processing
89+
step_history (List[StepOutput]): History of all execution steps
90+
ctx (Optional[Dict]): Additional context passed to tools
7891
"""
7992

8093
def __init__(

adalflow/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "adalflow"
3-
version = "1.0.4"
3+
version = "1.0.5a1"
44
description = "The Library to Build and Auto-optimize LLM Applications"
55
authors = ["Li Yin <li@sylphai.com>"]
66
readme = "README.md"

tutorials/v1_agent_runner/agent_runner.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -722,14 +722,14 @@ def run_example(example_name: str, example_func, *args, **kwargs):
722722
# Define all examples to run
723723
examples = [
724724
("Synchronous ReAct Agent", run_react_agent_example),
725-
# ("Primitive Type ReAct Agent", run_react_agent_primitive_type),
726-
# ("Async ReAct Agent", lambda: asyncio.run(arun_react_agent_example())),
727-
# ("Advanced ReAct Agent", run_advanced_react_agent),
725+
("Primitive Type ReAct Agent", run_react_agent_primitive_type),
726+
("Async ReAct Agent", lambda: asyncio.run(arun_react_agent_example())),
727+
("Advanced ReAct Agent", run_advanced_react_agent),
728728
(
729729
"Advanced Async ReAct Agent",
730730
lambda: asyncio.run(arun_advanced_react_agent()),
731731
),
732-
# ("No Structured Output Agent", no_structured_output_run_agent),
732+
("No Structured Output Agent", no_structured_output_run_agent),
733733
("Pydantic Dataclass Agent", pydantic_dataclass_run_agent),
734734
]
735735

tutorials/v1_agent_runner/anthropic_streaming_tutorial.py

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@
3333
from adalflow.core.base_data_class import DataClass
3434
from dataclasses import dataclass
3535

36-
from adalflow.utils.lazy_import import setup_env
36+
from adalflow.utils import setup_env
3737

3838
# Direct Anthropic API imports
3939
import anthropic
4040

41+
import logging
42+
43+
logging.basicConfig(level=logging.DEBUG)
4144

4245
setup_env() # Comment out to prevent FileNotFoundError when .env doesn't exist
4346

@@ -812,9 +815,8 @@ async def test_streaming():
812815
print(f" Event: {type(event).__name__}")
813816
print(f" Event content: {event}")
814817

815-
await streaming_result.wait_for_completion()
816818
print(f" ✓ Final completion status: {streaming_result.is_complete}")
817-
print(f" ✓ Final result type: {type(streaming_result.final_result)}")
819+
print(f" ✓ Final answer type: {type(streaming_result.answer)}")
818820

819821
asyncio.run(test_streaming())
820822

@@ -851,11 +853,12 @@ async def test_streaming():
851853
)
852854

853855
async def test_data_class_streaming():
854-
# Wait for completion
855-
await streaming_result.wait_for_completion()
856+
# Consume stream events to completion
857+
async for event in streaming_result.stream_events():
858+
pass # Process events if needed
856859

857860
assert streaming_result.is_complete, "Should be complete"
858-
assert streaming_result.final_result is not None, "Should have final result"
861+
assert streaming_result.answer is not None, "Should have final answer"
859862

860863
print(" ✓ Streaming completed")
861864
print(" ✓ Final result available")
@@ -1003,14 +1006,14 @@ async def test_complete_workflow():
10031006
print(f" Event: {type(event).__name__}")
10041007
print(f" Event content: {event}")
10051008

1006-
# Verify completion
1009+
# Verify completion - stream_events() automatically handles completion
10071010
assert streaming_result.is_complete, "Workflow should be complete"
1008-
assert streaming_result.final_result is not None, "Should have final result"
1011+
assert streaming_result.answer is not None, "Should have final answer"
10091012
assert len(events_received) > 0, "Should receive events"
10101013

10111014
print(f" ✓ Received {len(events_received)} events")
10121015
print(" ✓ Workflow completed successfully")
1013-
print(f" ✓ Final answer: {streaming_result.final_result.answer}")
1016+
print(f" ✓ Final answer: {streaming_result.answer}")
10141017

10151018
asyncio.run(test_complete_workflow())
10161019

@@ -1042,11 +1045,16 @@ async def test_error_handling():
10421045
try:
10431046
async for event in streaming_result.stream_events():
10441047
pass
1045-
# If we get here, check if error was captured
1046-
if streaming_result.final_result and hasattr(
1047-
streaming_result.final_result, "error"
1048+
# Check if error was captured - stream_events() handles completion
1049+
if (
1050+
hasattr(streaming_result, "_exception")
1051+
and streaming_result._exception
10481052
):
1049-
print(" ✓ Error properly captured in final result")
1053+
print(
1054+
f" ✓ Error captured in streaming result: {streaming_result._exception}"
1055+
)
1056+
elif streaming_result.answer is None:
1057+
print(" ✓ No answer found, likely failed as expected")
10501058
else:
10511059
print(" ⚠ No error found, might have succeeded unexpectedly")
10521060
except Exception as e:
@@ -1096,20 +1104,21 @@ async def compare_async_methods():
10961104

10971105
astream_result = runner.astream(prompt_kwargs={"input_str": "Add 2 and 3"})
10981106

1099-
await astream_result.wait_for_completion()
1107+
# Consume stream events to completion
1108+
async for event in astream_result.stream_events():
1109+
print("THIS IS THE EVENT", event)
1110+
pass # Process events if needed
11001111

11011112
print("\nAsync Compare Agent Final Results:\n")
11021113
print(f"acall result: {acall_result}")
1103-
print(f"astream result: {astream_result.final_result}")
1114+
print(f"astream result: {astream_result.answer}")
11041115

11051116
# Both should complete successfully
11061117
assert hasattr(acall_result, "answer"), "acall should have answer"
1107-
assert (
1108-
astream_result.final_result is not None
1109-
), "astream should have final result"
1118+
assert astream_result.answer is not None, "astream should have final answer"
11101119

11111120
print(f" ✓ acall result type: {type(acall_result)}")
1112-
print(f" ✓ astream result type: {type(astream_result.final_result)}")
1121+
print(f" ✓ astream result answer type: {type(astream_result.answer)}")
11131122

11141123
asyncio.run(compare_async_methods())
11151124

0 commit comments

Comments
 (0)