zero-agent 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentz/agent/base.py +262 -0
- agentz/artifacts/__init__.py +5 -0
- agentz/artifacts/artifact_writer.py +538 -0
- agentz/artifacts/reporter.py +235 -0
- agentz/artifacts/terminal_writer.py +100 -0
- agentz/context/__init__.py +6 -0
- agentz/context/context.py +91 -0
- agentz/context/conversation.py +205 -0
- agentz/context/data_store.py +208 -0
- agentz/llm/llm_setup.py +156 -0
- agentz/mcp/manager.py +142 -0
- agentz/mcp/patches.py +88 -0
- agentz/mcp/servers/chrome_devtools/server.py +14 -0
- agentz/profiles/base.py +108 -0
- agentz/profiles/data/data_analysis.py +38 -0
- agentz/profiles/data/data_loader.py +35 -0
- agentz/profiles/data/evaluation.py +43 -0
- agentz/profiles/data/model_training.py +47 -0
- agentz/profiles/data/preprocessing.py +47 -0
- agentz/profiles/data/visualization.py +47 -0
- agentz/profiles/manager/evaluate.py +51 -0
- agentz/profiles/manager/memory.py +62 -0
- agentz/profiles/manager/observe.py +48 -0
- agentz/profiles/manager/routing.py +66 -0
- agentz/profiles/manager/writer.py +51 -0
- agentz/profiles/mcp/browser.py +21 -0
- agentz/profiles/mcp/chrome.py +21 -0
- agentz/profiles/mcp/notion.py +21 -0
- agentz/runner/__init__.py +74 -0
- agentz/runner/base.py +28 -0
- agentz/runner/executor.py +320 -0
- agentz/runner/hooks.py +110 -0
- agentz/runner/iteration.py +142 -0
- agentz/runner/patterns.py +215 -0
- agentz/runner/tracker.py +188 -0
- agentz/runner/utils.py +45 -0
- agentz/runner/workflow.py +250 -0
- agentz/tools/__init__.py +20 -0
- agentz/tools/data_tools/__init__.py +17 -0
- agentz/tools/data_tools/data_analysis.py +152 -0
- agentz/tools/data_tools/data_loading.py +92 -0
- agentz/tools/data_tools/evaluation.py +175 -0
- agentz/tools/data_tools/helpers.py +120 -0
- agentz/tools/data_tools/model_training.py +192 -0
- agentz/tools/data_tools/preprocessing.py +229 -0
- agentz/tools/data_tools/visualization.py +281 -0
- agentz/utils/__init__.py +69 -0
- agentz/utils/config.py +708 -0
- agentz/utils/helpers.py +10 -0
- agentz/utils/parsers.py +142 -0
- agentz/utils/printer.py +539 -0
- pipelines/base.py +972 -0
- pipelines/data_scientist.py +97 -0
- pipelines/data_scientist_memory.py +151 -0
- pipelines/experience_learner.py +0 -0
- pipelines/prompt_generator.py +0 -0
- pipelines/simple.py +78 -0
- pipelines/simple_browser.py +145 -0
- pipelines/simple_chrome.py +75 -0
- pipelines/simple_notion.py +103 -0
- pipelines/tool_builder.py +0 -0
- zero_agent-0.1.0.dist-info/METADATA +269 -0
- zero_agent-0.1.0.dist-info/RECORD +66 -0
- zero_agent-0.1.0.dist-info/WHEEL +5 -0
- zero_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
- zero_agent-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
"""Iteration management for iterative pipeline workflows."""
|
2
|
+
|
3
|
+
import time
|
4
|
+
from typing import Any, Optional, Tuple
|
5
|
+
|
6
|
+
from loguru import logger
|
7
|
+
|
8
|
+
|
9
|
+
class IterationManager:
|
10
|
+
"""Manages iteration lifecycle for iterative pipelines.
|
11
|
+
|
12
|
+
Handles:
|
13
|
+
- Starting/ending iterations with printer groups
|
14
|
+
- Constraint checking (max iterations, max time)
|
15
|
+
- Final group management
|
16
|
+
"""
|
17
|
+
|
18
|
+
# Constants for iteration group IDs
|
19
|
+
ITERATION_GROUP_PREFIX = "iter"
|
20
|
+
FINAL_GROUP_ID = "iter-final"
|
21
|
+
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
context: Any,
|
25
|
+
max_iterations: int = 5,
|
26
|
+
max_time_minutes: float = 10,
|
27
|
+
start_group_callback: Optional[callable] = None,
|
28
|
+
end_group_callback: Optional[callable] = None,
|
29
|
+
):
|
30
|
+
"""Initialize iteration manager.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
context: Pipeline context with state and begin_iteration/mark_iteration_complete methods
|
34
|
+
max_iterations: Maximum number of iterations allowed
|
35
|
+
max_time_minutes: Maximum time in minutes
|
36
|
+
start_group_callback: Optional callback(group_id, title, border_style, iteration)
|
37
|
+
end_group_callback: Optional callback(group_id, is_done, title)
|
38
|
+
"""
|
39
|
+
self.context = context
|
40
|
+
self.max_iterations = max_iterations
|
41
|
+
self.max_time_minutes = max_time_minutes
|
42
|
+
self.start_group_callback = start_group_callback
|
43
|
+
self.end_group_callback = end_group_callback
|
44
|
+
|
45
|
+
self.start_time: Optional[float] = None
|
46
|
+
self.current_iteration: int = 0
|
47
|
+
|
48
|
+
def start_timer(self) -> None:
|
49
|
+
"""Start the iteration timer for constraint checking."""
|
50
|
+
self.start_time = time.time()
|
51
|
+
|
52
|
+
def begin_iteration(self) -> Tuple[Any, str]:
|
53
|
+
"""Begin a new iteration with printer group.
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
Tuple of (iteration_record, group_id)
|
57
|
+
"""
|
58
|
+
iteration = self.context.begin_iteration()
|
59
|
+
group_id = f"{self.ITERATION_GROUP_PREFIX}-{iteration.index}"
|
60
|
+
|
61
|
+
self.current_iteration = iteration.index
|
62
|
+
|
63
|
+
if self.start_group_callback:
|
64
|
+
self.start_group_callback(
|
65
|
+
group_id,
|
66
|
+
title=f"Iteration {iteration.index}",
|
67
|
+
border_style="white",
|
68
|
+
iteration=iteration.index,
|
69
|
+
)
|
70
|
+
|
71
|
+
return iteration, group_id
|
72
|
+
|
73
|
+
def end_iteration(self, group_id: str) -> None:
|
74
|
+
"""End the current iteration and close printer group.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
group_id: Group ID to close
|
78
|
+
"""
|
79
|
+
self.context.mark_iteration_complete()
|
80
|
+
|
81
|
+
if self.end_group_callback:
|
82
|
+
self.end_group_callback(group_id, is_done=True)
|
83
|
+
|
84
|
+
def start_final_group(self) -> str:
|
85
|
+
"""Start final group for post-iteration work.
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
Final group ID
|
89
|
+
"""
|
90
|
+
if self.start_group_callback:
|
91
|
+
self.start_group_callback(
|
92
|
+
self.FINAL_GROUP_ID,
|
93
|
+
title="Final Report",
|
94
|
+
border_style="white",
|
95
|
+
)
|
96
|
+
return self.FINAL_GROUP_ID
|
97
|
+
|
98
|
+
def end_final_group(self, group_id: str) -> None:
|
99
|
+
"""End final group.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
group_id: Group ID to close
|
103
|
+
"""
|
104
|
+
if self.end_group_callback:
|
105
|
+
self.end_group_callback(group_id, is_done=True)
|
106
|
+
|
107
|
+
def should_continue_iteration(self) -> bool:
|
108
|
+
"""Check if iteration should continue.
|
109
|
+
|
110
|
+
Checks:
|
111
|
+
- State not complete
|
112
|
+
- Within max iterations
|
113
|
+
- Within max time
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
True if should continue, False otherwise
|
117
|
+
"""
|
118
|
+
# Check state completion
|
119
|
+
if hasattr(self.context, 'state') and self.context.state and self.context.state.complete:
|
120
|
+
return False
|
121
|
+
|
122
|
+
return self.check_constraints()
|
123
|
+
|
124
|
+
def check_constraints(self) -> bool:
|
125
|
+
"""Check if we've exceeded our constraints (max iterations or time).
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
True if within constraints, False if exceeded
|
129
|
+
"""
|
130
|
+
if self.current_iteration >= self.max_iterations:
|
131
|
+
logger.info("\n=== Ending Iteration Loop ===")
|
132
|
+
logger.info(f"Reached maximum iterations ({self.max_iterations})")
|
133
|
+
return False
|
134
|
+
|
135
|
+
if self.start_time is not None:
|
136
|
+
elapsed_minutes = (time.time() - self.start_time) / 60
|
137
|
+
if elapsed_minutes >= self.max_time_minutes:
|
138
|
+
logger.info("\n=== Ending Iteration Loop ===")
|
139
|
+
logger.info(f"Reached maximum time ({self.max_time_minutes} minutes)")
|
140
|
+
return False
|
141
|
+
|
142
|
+
return True
|
@@ -0,0 +1,215 @@
|
|
1
|
+
"""High-level execution patterns for common pipeline workflows."""
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
from typing import Any, Dict, List, Optional
|
5
|
+
|
6
|
+
from loguru import logger
|
7
|
+
|
8
|
+
from agentz.runner.utils import record_structured_payload, serialize_output
|
9
|
+
|
10
|
+
|
11
|
+
async def execute_tool_plan(
|
12
|
+
plan: Any,
|
13
|
+
tool_agents: Dict[str, Any],
|
14
|
+
group_id: str,
|
15
|
+
context: Any,
|
16
|
+
agent_step_fn: Any,
|
17
|
+
update_printer_fn: Optional[Any] = None,
|
18
|
+
) -> None:
|
19
|
+
"""Execute a routing plan with tool agents.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
plan: AgentSelectionPlan with tasks to execute
|
23
|
+
tool_agents: Dict mapping agent names to agent instances
|
24
|
+
group_id: Group ID for printer updates
|
25
|
+
context: Pipeline context with state
|
26
|
+
agent_step_fn: Function to execute agent steps
|
27
|
+
update_printer_fn: Optional function for printer updates
|
28
|
+
"""
|
29
|
+
# Import here to avoid circular dependency
|
30
|
+
from agentz.profiles.manager.routing import AgentSelectionPlan, AgentTask
|
31
|
+
from agentz.profiles.base import ToolAgentOutput
|
32
|
+
|
33
|
+
if not isinstance(plan, AgentSelectionPlan) or not plan.tasks:
|
34
|
+
return
|
35
|
+
|
36
|
+
state = context.state
|
37
|
+
state.current_iteration.tools.clear()
|
38
|
+
|
39
|
+
async def run_single(task: AgentTask) -> ToolAgentOutput:
|
40
|
+
agent = tool_agents.get(task.agent)
|
41
|
+
if agent is None:
|
42
|
+
output = ToolAgentOutput(
|
43
|
+
output=f"No implementation found for agent {task.agent}",
|
44
|
+
sources=[],
|
45
|
+
)
|
46
|
+
if update_printer_fn:
|
47
|
+
update_printer_fn(
|
48
|
+
key=f"{group_id}:tool:{task.agent}",
|
49
|
+
message=f"Completed {task.agent}",
|
50
|
+
is_done=True,
|
51
|
+
group_id=group_id,
|
52
|
+
)
|
53
|
+
return output
|
54
|
+
|
55
|
+
raw_result = await agent_step_fn(
|
56
|
+
agent=agent,
|
57
|
+
instructions=task.model_dump_json(),
|
58
|
+
span_name=task.agent,
|
59
|
+
span_type="tool",
|
60
|
+
output_model=ToolAgentOutput,
|
61
|
+
printer_key=f"tool:{task.agent}",
|
62
|
+
printer_title=f"Tool: {task.agent}",
|
63
|
+
printer_group_id=group_id,
|
64
|
+
)
|
65
|
+
|
66
|
+
if isinstance(raw_result, ToolAgentOutput):
|
67
|
+
output = raw_result
|
68
|
+
elif hasattr(raw_result, "final_output_as"):
|
69
|
+
output = raw_result.final_output_as(ToolAgentOutput)
|
70
|
+
elif hasattr(raw_result, "final_output"):
|
71
|
+
output = ToolAgentOutput(output=str(raw_result.final_output), sources=[])
|
72
|
+
else:
|
73
|
+
output = ToolAgentOutput(output=str(raw_result), sources=[])
|
74
|
+
|
75
|
+
try:
|
76
|
+
state.record_payload(output)
|
77
|
+
except Exception as exc:
|
78
|
+
logger.debug(f"Failed to record tool payload for {task.agent}: {exc}")
|
79
|
+
|
80
|
+
if update_printer_fn:
|
81
|
+
update_printer_fn(
|
82
|
+
key=f"{group_id}:tool:{task.agent}",
|
83
|
+
message=f"Completed {task.agent}",
|
84
|
+
is_done=True,
|
85
|
+
group_id=group_id,
|
86
|
+
)
|
87
|
+
return output
|
88
|
+
|
89
|
+
coroutines = [run_single(task) for task in plan.tasks]
|
90
|
+
for coro in asyncio.as_completed(coroutines):
|
91
|
+
tool_output = await coro
|
92
|
+
state.current_iteration.tools.append(tool_output)
|
93
|
+
|
94
|
+
|
95
|
+
async def execute_tools(
|
96
|
+
route_plan: Any,
|
97
|
+
tool_agents: Dict[str, Any],
|
98
|
+
group_id: str,
|
99
|
+
context: Any,
|
100
|
+
agent_step_fn: Any,
|
101
|
+
update_printer_fn: Optional[Any] = None,
|
102
|
+
) -> None:
|
103
|
+
"""Execute tool agents based on routing plan.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
route_plan: The routing plan (can be AgentSelectionPlan or other)
|
107
|
+
tool_agents: Dict mapping agent names to agent instances
|
108
|
+
group_id: Group ID for printer updates
|
109
|
+
context: Pipeline context with state
|
110
|
+
agent_step_fn: Function to execute agent steps
|
111
|
+
update_printer_fn: Optional function for printer updates
|
112
|
+
"""
|
113
|
+
from agentz.profiles.manager.routing import AgentSelectionPlan
|
114
|
+
|
115
|
+
# Retrieve route_plan from payloads if needed
|
116
|
+
plan = None
|
117
|
+
if isinstance(route_plan, AgentSelectionPlan):
|
118
|
+
plan = route_plan
|
119
|
+
elif route_plan and hasattr(context, 'state'):
|
120
|
+
# Try to find AgentSelectionPlan in payloads
|
121
|
+
for payload in context.state.current_iteration.payloads:
|
122
|
+
if isinstance(payload, AgentSelectionPlan):
|
123
|
+
plan = payload
|
124
|
+
break
|
125
|
+
|
126
|
+
if plan and plan.tasks:
|
127
|
+
await execute_tool_plan(
|
128
|
+
plan, tool_agents, group_id, context, agent_step_fn, update_printer_fn
|
129
|
+
)
|
130
|
+
|
131
|
+
|
132
|
+
async def run_manager_tool_loop(
|
133
|
+
manager_agents: Dict[str, Any],
|
134
|
+
tool_agents: Dict[str, Any],
|
135
|
+
workflow: List[str],
|
136
|
+
context: Any,
|
137
|
+
agent_step_fn: Any,
|
138
|
+
run_iterative_loop_fn: Any,
|
139
|
+
update_printer_fn: Optional[Any] = None,
|
140
|
+
) -> Any:
|
141
|
+
"""Execute standard manager-tool iterative pattern.
|
142
|
+
|
143
|
+
This pattern implements: observe → evaluate → route → execute tools → repeat.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
manager_agents: Dict of manager agents (observe, evaluate, routing, writer)
|
147
|
+
tool_agents: Dict of tool agents
|
148
|
+
workflow: List of manager agent names to execute in order (e.g., ["observe", "evaluate", "routing"])
|
149
|
+
context: Pipeline context with state
|
150
|
+
agent_step_fn: Function to execute agent steps
|
151
|
+
run_iterative_loop_fn: Function to run iterative loop
|
152
|
+
update_printer_fn: Optional function for printer updates
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
Result from final step
|
156
|
+
|
157
|
+
Example:
|
158
|
+
result = await run_manager_tool_loop(
|
159
|
+
manager_agents=self.manager_agents,
|
160
|
+
tool_agents=self.tool_agents,
|
161
|
+
workflow=["observe", "evaluate", "routing"],
|
162
|
+
context=self.context,
|
163
|
+
agent_step_fn=self.agent_step,
|
164
|
+
run_iterative_loop_fn=self.run_iterative_loop,
|
165
|
+
update_printer_fn=self.update_printer,
|
166
|
+
)
|
167
|
+
"""
|
168
|
+
async def iteration_step(iteration, group_id: str):
|
169
|
+
"""Execute manager workflow + tool execution."""
|
170
|
+
previous_output = context.state.query
|
171
|
+
|
172
|
+
# Execute manager workflow in sequence
|
173
|
+
for agent_name in workflow:
|
174
|
+
agent = manager_agents.get(agent_name)
|
175
|
+
if agent is None:
|
176
|
+
logger.warning(f"Manager agent '{agent_name}' not found, skipping")
|
177
|
+
continue
|
178
|
+
|
179
|
+
output = await agent(previous_output)
|
180
|
+
|
181
|
+
# Record observation for first step
|
182
|
+
if agent_name == workflow[0]:
|
183
|
+
iteration.observation = serialize_output(output)
|
184
|
+
|
185
|
+
record_structured_payload(context.state, output, context_label=agent_name)
|
186
|
+
previous_output = output
|
187
|
+
|
188
|
+
# Execute tools if not complete
|
189
|
+
if not context.state.complete and previous_output:
|
190
|
+
await execute_tools(
|
191
|
+
previous_output,
|
192
|
+
tool_agents,
|
193
|
+
group_id,
|
194
|
+
context,
|
195
|
+
agent_step_fn,
|
196
|
+
update_printer_fn,
|
197
|
+
)
|
198
|
+
|
199
|
+
async def final_step(final_group: str):
|
200
|
+
"""Generate final report."""
|
201
|
+
if update_printer_fn:
|
202
|
+
update_printer_fn("research", "Research workflow complete", is_done=True)
|
203
|
+
logger.info("Research workflow completed")
|
204
|
+
|
205
|
+
writer = manager_agents.get("writer")
|
206
|
+
if writer:
|
207
|
+
await writer(context.state.findings_text())
|
208
|
+
|
209
|
+
if update_printer_fn:
|
210
|
+
update_printer_fn("research", "Executing research workflow...")
|
211
|
+
|
212
|
+
return await run_iterative_loop_fn(
|
213
|
+
iteration_body=iteration_step,
|
214
|
+
final_body=final_step
|
215
|
+
)
|
agentz/runner/tracker.py
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
"""Runtime state tracking for agent execution operations."""
|
2
|
+
|
3
|
+
from contextlib import nullcontext, contextmanager
|
4
|
+
from contextvars import ContextVar
|
5
|
+
from typing import Any, Dict, Optional
|
6
|
+
|
7
|
+
from agents.tracing.create import trace
|
8
|
+
from agentz.utils import Printer
|
9
|
+
from agentz.context.data_store import DataStore
|
10
|
+
from agentz.artifacts import RunReporter
|
11
|
+
|
12
|
+
# Context variable to store the current runtime tracker
|
13
|
+
# This allows tools to access the tracker without explicit parameter passing
|
14
|
+
_current_runtime_tracker: ContextVar[Optional['RuntimeTracker']] = ContextVar(
|
15
|
+
'current_runtime_tracker',
|
16
|
+
default=None
|
17
|
+
)
|
18
|
+
|
19
|
+
|
20
|
+
class RuntimeTracker:
|
21
|
+
"""Manages runtime state and tracking for agent execution.
|
22
|
+
|
23
|
+
This class encapsulates the runtime infrastructure needed for agent execution including:
|
24
|
+
- Tracing configuration and context creation
|
25
|
+
- Printer for status updates
|
26
|
+
- Reporter for recording execution events
|
27
|
+
- Iteration tracking
|
28
|
+
- Pipeline-scoped data store for sharing objects between agents
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(
|
32
|
+
self,
|
33
|
+
printer: Optional[Printer] = None,
|
34
|
+
enable_tracing: bool = True,
|
35
|
+
trace_sensitive: bool = False,
|
36
|
+
iteration: int = 0,
|
37
|
+
experiment_id: Optional[str] = None,
|
38
|
+
reporter: Optional[RunReporter] = None,
|
39
|
+
):
|
40
|
+
"""Initialize runtime tracker.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
printer: Optional Printer instance for status updates
|
44
|
+
enable_tracing: Whether tracing is enabled
|
45
|
+
trace_sensitive: Whether to include sensitive data in traces
|
46
|
+
iteration: Current iteration number (for iterative workflows)
|
47
|
+
experiment_id: Optional experiment ID for data store tracking
|
48
|
+
reporter: Optional reporter for recording execution events
|
49
|
+
"""
|
50
|
+
self.printer = printer
|
51
|
+
self.enable_tracing = enable_tracing
|
52
|
+
self.trace_sensitive = trace_sensitive
|
53
|
+
self.iteration = iteration
|
54
|
+
self.reporter = reporter
|
55
|
+
self.data_store = DataStore(experiment_id=experiment_id)
|
56
|
+
|
57
|
+
def trace_context(self, name: str, metadata: Optional[Dict[str, Any]] = None):
|
58
|
+
"""Create a trace context manager.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
name: Name for the trace
|
62
|
+
metadata: Optional metadata to attach to trace
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
Trace context manager if tracing enabled, otherwise nullcontext
|
66
|
+
"""
|
67
|
+
if self.enable_tracing:
|
68
|
+
return trace(name, metadata=metadata)
|
69
|
+
return nullcontext()
|
70
|
+
|
71
|
+
def span_context(self, span_factory, **kwargs):
|
72
|
+
"""Create a span context manager.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
span_factory: Factory function for creating spans (agent_span or function_span)
|
76
|
+
**kwargs: Arguments to pass to span factory
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
Span context manager if tracing enabled, otherwise nullcontext
|
80
|
+
"""
|
81
|
+
if self.enable_tracing:
|
82
|
+
return span_factory(**kwargs)
|
83
|
+
return nullcontext()
|
84
|
+
|
85
|
+
def update_printer(
|
86
|
+
self,
|
87
|
+
key: str,
|
88
|
+
message: str,
|
89
|
+
is_done: bool = False,
|
90
|
+
hide_checkmark: bool = False,
|
91
|
+
title: Optional[str] = None,
|
92
|
+
border_style: Optional[str] = None,
|
93
|
+
group_id: Optional[str] = None,
|
94
|
+
) -> None:
|
95
|
+
"""Update printer status if printer is active.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
key: Status key to update
|
99
|
+
message: Status message
|
100
|
+
is_done: Whether the task is complete
|
101
|
+
hide_checkmark: Whether to hide the checkmark when done
|
102
|
+
title: Optional panel title
|
103
|
+
border_style: Optional border color
|
104
|
+
group_id: Optional group to nest this item in
|
105
|
+
"""
|
106
|
+
if self.reporter:
|
107
|
+
self.reporter.record_status_update(
|
108
|
+
item_id=key,
|
109
|
+
content=message,
|
110
|
+
is_done=is_done,
|
111
|
+
title=title,
|
112
|
+
border_style=border_style,
|
113
|
+
group_id=group_id,
|
114
|
+
)
|
115
|
+
if self.printer:
|
116
|
+
self.printer.update_item(
|
117
|
+
key,
|
118
|
+
message,
|
119
|
+
is_done=is_done,
|
120
|
+
hide_checkmark=hide_checkmark,
|
121
|
+
title=title,
|
122
|
+
border_style=border_style,
|
123
|
+
group_id=group_id
|
124
|
+
)
|
125
|
+
|
126
|
+
def log_panel(
|
127
|
+
self,
|
128
|
+
title: str,
|
129
|
+
content: str,
|
130
|
+
*,
|
131
|
+
border_style: Optional[str] = None,
|
132
|
+
iteration: Optional[int] = None,
|
133
|
+
group_id: Optional[str] = None,
|
134
|
+
) -> None:
|
135
|
+
"""Proxy helper for rendering standalone panels via the printer."""
|
136
|
+
if self.reporter:
|
137
|
+
self.reporter.record_panel(
|
138
|
+
title=title,
|
139
|
+
content=content,
|
140
|
+
border_style=border_style,
|
141
|
+
iteration=iteration,
|
142
|
+
group_id=group_id,
|
143
|
+
)
|
144
|
+
if self.printer:
|
145
|
+
self.printer.log_panel(
|
146
|
+
title,
|
147
|
+
content,
|
148
|
+
border_style=border_style,
|
149
|
+
iteration=iteration,
|
150
|
+
)
|
151
|
+
|
152
|
+
@contextmanager
|
153
|
+
def activate(self):
|
154
|
+
"""Context manager to set this tracker as the current runtime tracker.
|
155
|
+
|
156
|
+
This allows tools to access the tracker via get_current_tracker().
|
157
|
+
|
158
|
+
Example:
|
159
|
+
with tracker.activate():
|
160
|
+
# Tools can now access this tracker
|
161
|
+
result = await agent.run(...)
|
162
|
+
"""
|
163
|
+
token = _current_runtime_tracker.set(self)
|
164
|
+
try:
|
165
|
+
yield self
|
166
|
+
finally:
|
167
|
+
_current_runtime_tracker.reset(token)
|
168
|
+
|
169
|
+
|
170
|
+
def get_current_tracker() -> Optional[RuntimeTracker]:
|
171
|
+
"""Get the current runtime tracker (if any).
|
172
|
+
|
173
|
+
Returns:
|
174
|
+
The current RuntimeTracker or None if not in a runtime context
|
175
|
+
"""
|
176
|
+
return _current_runtime_tracker.get()
|
177
|
+
|
178
|
+
|
179
|
+
def get_current_data_store() -> Optional[DataStore]:
|
180
|
+
"""Get the data store from the current runtime tracker (if any).
|
181
|
+
|
182
|
+
This is a convenience function for tools that need to access the data store.
|
183
|
+
|
184
|
+
Returns:
|
185
|
+
The current DataStore or None if not in a runtime context
|
186
|
+
"""
|
187
|
+
tracker = get_current_tracker()
|
188
|
+
return tracker.data_store if tracker else None
|
agentz/runner/utils.py
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
"""Utility functions for pipeline execution."""
|
2
|
+
|
3
|
+
from typing import Any, Optional
|
4
|
+
|
5
|
+
from loguru import logger
|
6
|
+
from pydantic import BaseModel
|
7
|
+
|
8
|
+
|
9
|
+
def record_structured_payload(
|
10
|
+
state: Any,
|
11
|
+
value: object,
|
12
|
+
context_label: Optional[str] = None
|
13
|
+
) -> None:
|
14
|
+
"""Record a structured payload to the current iteration state.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
state: The state object (typically from context.state)
|
18
|
+
value: The payload to record (typically a BaseModel instance)
|
19
|
+
context_label: Optional label for debugging purposes
|
20
|
+
"""
|
21
|
+
if isinstance(value, BaseModel):
|
22
|
+
try:
|
23
|
+
if state:
|
24
|
+
state.record_payload(value)
|
25
|
+
except Exception as exc:
|
26
|
+
if context_label:
|
27
|
+
logger.debug(f"Failed to record payload for {context_label}: {exc}")
|
28
|
+
else:
|
29
|
+
logger.debug(f"Failed to record payload: {exc}")
|
30
|
+
|
31
|
+
|
32
|
+
def serialize_output(output: Any) -> str:
|
33
|
+
"""Serialize agent output to string for storage.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
output: The output to serialize (BaseModel, str, or other)
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
String representation of the output
|
40
|
+
"""
|
41
|
+
if isinstance(output, BaseModel):
|
42
|
+
return output.model_dump_json(indent=2)
|
43
|
+
elif isinstance(output, str):
|
44
|
+
return output
|
45
|
+
return str(output)
|