flock-core 0.2.1__py3-none-any.whl → 0.2.2__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.
Potentially problematic release.
This version of flock-core might be problematic. Click here for more details.
- flock/core/context/context.py +104 -133
- flock/core/execution/temporal_executor.py +22 -6
- flock/core/flock.py +95 -62
- flock/core/flock_agent.py +150 -68
- flock/core/logging/logging.py +22 -4
- flock/core/logging/telemetry.py +21 -0
- flock/core/logging/trace_and_logged.py +55 -0
- flock/core/mixin/dspy_integration.py +40 -14
- flock/core/registry/agent_registry.py +89 -74
- flock/core/tools/basic_tools.py +27 -4
- flock/core/tools/dev_tools/github.py +37 -8
- flock/core/util/cli_helper.py +7 -3
- flock/workflow/activities.py +148 -90
- {flock_core-0.2.1.dist-info → flock_core-0.2.2.dist-info}/METADATA +35 -2
- {flock_core-0.2.1.dist-info → flock_core-0.2.2.dist-info}/RECORD +17 -20
- flock/agents/__init__.py +0 -0
- flock/agents/batch_agent.py +0 -140
- flock/agents/loop_agent.py +0 -117
- flock/agents/trigger_agent.py +0 -113
- flock/agents/user_agent.py +0 -145
- {flock_core-0.2.1.dist-info → flock_core-0.2.2.dist-info}/WHEEL +0 -0
- {flock_core-0.2.1.dist-info → flock_core-0.2.2.dist-info}/licenses/LICENSE +0 -0
flock/core/context/context.py
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
"""A context object for storing state, history, and agent definitions."""
|
|
2
|
-
|
|
3
1
|
from dataclasses import asdict, dataclass, field
|
|
4
2
|
from datetime import datetime
|
|
5
3
|
from typing import Any, Literal
|
|
6
4
|
|
|
5
|
+
from opentelemetry import trace
|
|
6
|
+
|
|
7
7
|
from flock.core.context.context_vars import FLOCK_LAST_AGENT, FLOCK_LAST_RESULT
|
|
8
|
+
from flock.core.logging.logging import get_logger
|
|
8
9
|
from flock.core.util.serializable import Serializable
|
|
9
10
|
|
|
11
|
+
logger = get_logger("context")
|
|
12
|
+
tracer = trace.get_tracer(__name__)
|
|
13
|
+
|
|
10
14
|
|
|
11
15
|
@dataclass
|
|
12
16
|
class AgentRunRecord:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
agent: str = field(default="") # Agent name
|
|
17
|
+
agent: str = field(default="")
|
|
16
18
|
data: dict[str, Any] = field(default_factory=dict)
|
|
17
19
|
timestamp: str = field(default="")
|
|
18
|
-
hand_off: dict = field(
|
|
19
|
-
called_from: str = field(default="")
|
|
20
|
+
hand_off: dict = field(default_factory=dict)
|
|
21
|
+
called_from: str = field(default="")
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
@dataclass
|
|
23
25
|
class AgentDefinition:
|
|
24
|
-
"""A serializable definition for an agent, including the agent type, name, and data."""
|
|
25
|
-
|
|
26
26
|
agent_type: str = field(default="")
|
|
27
27
|
agent_name: str = field(default="")
|
|
28
|
-
agent_data: dict = field(
|
|
28
|
+
agent_data: dict = field(default_factory=dict)
|
|
29
29
|
serializer: Literal["json", "cloudpickle", "msgpack"] = field(
|
|
30
30
|
default="cloudpickle"
|
|
31
31
|
)
|
|
@@ -33,8 +33,6 @@ class AgentDefinition:
|
|
|
33
33
|
|
|
34
34
|
@dataclass
|
|
35
35
|
class FlockContext(Serializable):
|
|
36
|
-
"""A context object for storing state, history, and agent definitions."""
|
|
37
|
-
|
|
38
36
|
state: dict[str, Any] = field(default_factory=dict)
|
|
39
37
|
history: list[AgentRunRecord] = field(default_factory=list)
|
|
40
38
|
agent_definitions: dict[str, AgentDefinition] = field(default_factory=dict)
|
|
@@ -50,162 +48,135 @@ class FlockContext(Serializable):
|
|
|
50
48
|
hand_off: str,
|
|
51
49
|
called_from: str,
|
|
52
50
|
) -> None:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
record = AgentRunRecord(
|
|
52
|
+
agent=agent_name,
|
|
53
|
+
data=data.copy(),
|
|
54
|
+
timestamp=timestamp,
|
|
55
|
+
hand_off=hand_off,
|
|
56
|
+
called_from=called_from,
|
|
57
|
+
)
|
|
58
|
+
self.history.append(record)
|
|
59
|
+
for key, value in data.items():
|
|
60
|
+
self.set_variable(f"{agent_name}.{key}", value)
|
|
61
|
+
self.set_variable(FLOCK_LAST_RESULT, data)
|
|
62
|
+
self.set_variable(FLOCK_LAST_AGENT, agent_name)
|
|
63
|
+
logger.info(
|
|
64
|
+
"Agent run recorded",
|
|
65
|
+
agent=agent_name,
|
|
66
|
+
timestamp=timestamp,
|
|
67
|
+
data=data,
|
|
68
|
+
)
|
|
69
|
+
current_span = trace.get_current_span()
|
|
70
|
+
if current_span.get_span_context().is_valid:
|
|
71
|
+
current_span.add_event(
|
|
72
|
+
"record",
|
|
73
|
+
attributes={"agent": agent_name, "timestamp": timestamp},
|
|
61
74
|
)
|
|
62
|
-
self.history.append(record)
|
|
63
|
-
|
|
64
|
-
for key, value in data.items():
|
|
65
|
-
self.set_variable(f"{agent_name}.{key}", value)
|
|
66
|
-
self.set_variable(FLOCK_LAST_RESULT, data)
|
|
67
|
-
self.set_variable(FLOCK_LAST_AGENT, agent_name)
|
|
68
|
-
except Exception:
|
|
69
|
-
raise
|
|
70
75
|
|
|
71
76
|
def get_variable(self, key: str) -> Any:
|
|
72
|
-
|
|
73
|
-
try:
|
|
74
|
-
return self.state.get(key)
|
|
75
|
-
except Exception:
|
|
76
|
-
raise
|
|
77
|
+
return self.state.get(key)
|
|
77
78
|
|
|
78
79
|
def set_variable(self, key: str, value: Any) -> None:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
old_value = self.state.get(key)
|
|
81
|
+
self.state[key] = value
|
|
82
|
+
if old_value != value:
|
|
83
|
+
logger.info(
|
|
84
|
+
"Context variable updated",
|
|
85
|
+
variable=key,
|
|
86
|
+
old=old_value,
|
|
87
|
+
new=value,
|
|
88
|
+
)
|
|
89
|
+
current_span = trace.get_current_span()
|
|
90
|
+
if current_span.get_span_context().is_valid:
|
|
91
|
+
current_span.add_event(
|
|
92
|
+
"set_variable",
|
|
93
|
+
attributes={
|
|
94
|
+
"key": key,
|
|
95
|
+
"old": str(old_value),
|
|
96
|
+
"new": str(value),
|
|
97
|
+
},
|
|
98
|
+
)
|
|
84
99
|
|
|
85
100
|
def deepcopy(self) -> "FlockContext":
|
|
86
|
-
|
|
87
|
-
try:
|
|
88
|
-
return FlockContext.from_dict(self.to_dict())
|
|
89
|
-
except Exception:
|
|
90
|
-
raise
|
|
101
|
+
return FlockContext.from_dict(self.to_dict())
|
|
91
102
|
|
|
92
103
|
def get_agent_history(self, agent_name: str) -> list[AgentRunRecord]:
|
|
93
|
-
|
|
94
|
-
try:
|
|
95
|
-
return [
|
|
96
|
-
record for record in self.history if record.agent == agent_name
|
|
97
|
-
]
|
|
98
|
-
except Exception:
|
|
99
|
-
raise
|
|
104
|
+
return [record for record in self.history if record.agent == agent_name]
|
|
100
105
|
|
|
101
106
|
def next_input_for(self, agent) -> Any:
|
|
102
|
-
"""By default, the next input for an agent is taken from the context state.
|
|
103
|
-
|
|
104
|
-
If the agent.input is a comma-separated list (e.g., "input1, input2"),
|
|
105
|
-
this method will return a dictionary with keys for each of the input names,
|
|
106
|
-
fetching the latest values from the state.
|
|
107
|
-
|
|
108
|
-
If only a single input is specified, the raw value is returned.
|
|
109
|
-
"""
|
|
110
107
|
try:
|
|
111
108
|
if hasattr(agent, "input") and isinstance(agent.input, str):
|
|
112
109
|
keys = [k.strip() for k in agent.input.split(",") if k.strip()]
|
|
113
|
-
|
|
114
110
|
if len(keys) == 1:
|
|
115
111
|
return self.get_variable(keys[0])
|
|
116
112
|
else:
|
|
117
113
|
return {key: self.get_variable(key) for key in keys}
|
|
118
114
|
else:
|
|
119
|
-
# Fallback to "init_input"
|
|
120
115
|
return self.get_variable("init_input")
|
|
121
|
-
except Exception:
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.error(
|
|
118
|
+
"Error getting next input for agent",
|
|
119
|
+
agent=agent.name,
|
|
120
|
+
error=str(e),
|
|
121
|
+
)
|
|
122
122
|
raise
|
|
123
123
|
|
|
124
124
|
def get_most_recent_value(self, variable_name: str) -> Any:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if variable_name in history_record.data:
|
|
129
|
-
return history_record.data[variable_name]
|
|
130
|
-
except Exception:
|
|
131
|
-
raise
|
|
125
|
+
for history_record in reversed(self.history):
|
|
126
|
+
if variable_name in history_record.data:
|
|
127
|
+
return history_record.data[variable_name]
|
|
132
128
|
|
|
133
129
|
def get_agent_definition(self, agent_name: str) -> AgentDefinition | None:
|
|
134
|
-
|
|
135
|
-
try:
|
|
136
|
-
for definition in self.agent_definitions:
|
|
137
|
-
if definition.name == agent_name:
|
|
138
|
-
return definition
|
|
139
|
-
return None
|
|
140
|
-
except Exception:
|
|
141
|
-
raise
|
|
130
|
+
return self.agent_definitions.get(agent_name)
|
|
142
131
|
|
|
143
132
|
def add_agent_definition(
|
|
144
133
|
self, agent_type: type, agent_name: str, agent_data: Any
|
|
145
134
|
) -> None:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
except Exception:
|
|
155
|
-
raise
|
|
156
|
-
|
|
157
|
-
# Allow dict-like access for convenience.
|
|
135
|
+
definition = AgentDefinition(
|
|
136
|
+
agent_type=agent_type.__name__,
|
|
137
|
+
agent_name=agent_name,
|
|
138
|
+
agent_data=agent_data,
|
|
139
|
+
)
|
|
140
|
+
self.agent_definitions[agent_name] = definition
|
|
141
|
+
|
|
142
|
+
# Use the reactive setter for dict-like access.
|
|
158
143
|
def __getitem__(self, key: str) -> Any:
|
|
159
|
-
|
|
160
|
-
value = self.state[key]
|
|
161
|
-
return value
|
|
144
|
+
return self.get_variable(key)
|
|
162
145
|
|
|
163
146
|
def __setitem__(self, key: str, value: Any) -> None:
|
|
164
|
-
|
|
165
|
-
self.state[key] = value
|
|
147
|
+
self.set_variable(key, value)
|
|
166
148
|
|
|
167
149
|
def to_dict(self) -> dict[str, Any]:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
obj,
|
|
177
|
-
dict_factory=lambda x: {k: convert(v) for k, v in x},
|
|
178
|
-
)
|
|
179
|
-
return obj
|
|
150
|
+
def convert(obj):
|
|
151
|
+
if isinstance(obj, datetime):
|
|
152
|
+
return obj.isoformat()
|
|
153
|
+
if hasattr(obj, "__dataclass_fields__"):
|
|
154
|
+
return asdict(
|
|
155
|
+
obj, dict_factory=lambda x: {k: convert(v) for k, v in x}
|
|
156
|
+
)
|
|
157
|
+
return obj
|
|
180
158
|
|
|
181
|
-
|
|
182
|
-
except Exception:
|
|
183
|
-
raise
|
|
159
|
+
return convert(asdict(self))
|
|
184
160
|
|
|
185
161
|
@classmethod
|
|
186
162
|
def from_dict(cls, data: dict[str, Any]) -> "FlockContext":
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
converted = convert(data)
|
|
209
|
-
return cls(**converted)
|
|
210
|
-
except Exception:
|
|
211
|
-
raise
|
|
163
|
+
def convert(obj):
|
|
164
|
+
if isinstance(obj, dict):
|
|
165
|
+
if "timestamp" in obj:
|
|
166
|
+
return AgentRunRecord(
|
|
167
|
+
**{
|
|
168
|
+
**obj,
|
|
169
|
+
"timestamp": datetime.fromisoformat(
|
|
170
|
+
obj["timestamp"]
|
|
171
|
+
),
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
if "agent_type" in obj:
|
|
175
|
+
return AgentDefinition(**obj)
|
|
176
|
+
return {k: convert(v) for k, v in obj.items()}
|
|
177
|
+
if isinstance(obj, list):
|
|
178
|
+
return [convert(v) for v in obj]
|
|
179
|
+
return obj
|
|
180
|
+
|
|
181
|
+
converted = convert(data)
|
|
182
|
+
return cls(**converted)
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
# src/your_package/core/execution/temporal_executor.py
|
|
2
|
+
from devtools import pprint
|
|
3
|
+
|
|
2
4
|
from flock.core.context.context import FlockContext
|
|
5
|
+
from flock.core.context.context_vars import FLOCK_RUN_ID
|
|
6
|
+
from flock.core.logging.formatters.formatter_factory import FormatterFactory
|
|
3
7
|
from flock.core.logging.logging import get_logger
|
|
4
|
-
from flock.workflow.activities import
|
|
8
|
+
from flock.workflow.activities import (
|
|
9
|
+
run_agent, # Activity function used in Temporal
|
|
10
|
+
)
|
|
5
11
|
from flock.workflow.temporal_setup import create_temporal_client, setup_worker
|
|
6
12
|
from flock.workflow.workflow import FlockWorkflow # Your workflow class
|
|
7
13
|
|
|
8
14
|
logger = get_logger("flock")
|
|
9
15
|
|
|
10
16
|
|
|
11
|
-
async def run_temporal_workflow(
|
|
17
|
+
async def run_temporal_workflow(
|
|
18
|
+
context: FlockContext,
|
|
19
|
+
output_formatter,
|
|
20
|
+
box_result: bool = True,
|
|
21
|
+
) -> dict:
|
|
12
22
|
"""Execute the agent workflow via Temporal for robust, distributed processing.
|
|
13
23
|
|
|
14
24
|
Args:
|
|
@@ -22,16 +32,22 @@ async def run_temporal_workflow(context: FlockContext, box_result: bool = True)
|
|
|
22
32
|
await setup_worker(workflow=FlockWorkflow, activity=run_agent)
|
|
23
33
|
logger.debug("Creating Temporal client")
|
|
24
34
|
flock_client = await create_temporal_client()
|
|
25
|
-
workflow_id = context.get_variable(
|
|
35
|
+
workflow_id = context.get_variable(FLOCK_RUN_ID)
|
|
26
36
|
logger.info("Executing Temporal workflow", workflow_id=workflow_id)
|
|
27
37
|
result = await flock_client.execute_workflow(
|
|
28
|
-
FlockWorkflow.run,
|
|
38
|
+
FlockWorkflow.run,
|
|
39
|
+
context.to_dict(),
|
|
40
|
+
id=workflow_id,
|
|
41
|
+
task_queue="flock-queue",
|
|
29
42
|
)
|
|
30
|
-
from flock.core.logging.formatters.themed_formatter import ThemedAgentResultFormatter
|
|
31
43
|
|
|
32
44
|
agent_name = context.get_variable("FLOCK_CURRENT_AGENT")
|
|
33
45
|
logger.debug("Formatting Temporal result", agent=agent_name)
|
|
34
|
-
|
|
46
|
+
if output_formatter:
|
|
47
|
+
formatter = FormatterFactory.create_formatter(output_formatter)
|
|
48
|
+
formatter.display(result, agent_name, output_formatter.wait_for_input)
|
|
49
|
+
else:
|
|
50
|
+
pprint(result)
|
|
35
51
|
if box_result:
|
|
36
52
|
from box import Box
|
|
37
53
|
|
flock/core/flock.py
CHANGED
|
@@ -4,6 +4,7 @@ import os
|
|
|
4
4
|
import uuid
|
|
5
5
|
from typing import TypeVar
|
|
6
6
|
|
|
7
|
+
from opentelemetry import trace
|
|
7
8
|
from rich.prompt import Prompt
|
|
8
9
|
|
|
9
10
|
from flock.core.context.context import FlockContext
|
|
@@ -14,12 +15,15 @@ from flock.core.flock_agent import FlockAgent
|
|
|
14
15
|
from flock.core.logging.formatters.base_formatter import FormatterOptions
|
|
15
16
|
from flock.core.logging.formatters.pprint_formatter import PrettyPrintFormatter
|
|
16
17
|
from flock.core.logging.logging import get_logger
|
|
18
|
+
from flock.core.logging.telemetry import setup_tracing
|
|
17
19
|
from flock.core.registry.agent_registry import Registry
|
|
18
20
|
from flock.core.util.cli_helper import display_banner
|
|
19
21
|
from flock.core.util.input_resolver import top_level_to_keys
|
|
20
22
|
|
|
21
23
|
T = TypeVar("T", bound=FlockAgent)
|
|
22
24
|
logger = get_logger("flock")
|
|
25
|
+
tracer = setup_tracing()
|
|
26
|
+
tracer = trace.get_tracer(__name__)
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
class Flock:
|
|
@@ -46,29 +50,36 @@ class Flock:
|
|
|
46
50
|
enable_logging (bool): If True, enable verbose logging. Defaults to False.
|
|
47
51
|
output_formatter (FormatterOptions): Options for formatting output results.
|
|
48
52
|
"""
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
with tracer.start_as_current_span("flock_init") as span:
|
|
54
|
+
span.set_attribute("model", model)
|
|
55
|
+
span.set_attribute("local_debug", local_debug)
|
|
56
|
+
span.set_attribute("enable_logging", enable_logging)
|
|
57
|
+
span.set_attribute(
|
|
58
|
+
"output_formatter", output_formatter.formatter.__name__
|
|
59
|
+
)
|
|
60
|
+
logger.info(
|
|
61
|
+
"Initializing Flock",
|
|
62
|
+
model=model,
|
|
63
|
+
local_debug=local_debug,
|
|
64
|
+
enable_logging=enable_logging,
|
|
65
|
+
)
|
|
66
|
+
logger.enable_logging = enable_logging
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
display_banner()
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
self.agents: dict[str, FlockAgent] = {}
|
|
71
|
+
self.registry = Registry()
|
|
72
|
+
self.context = FlockContext()
|
|
73
|
+
self.model = model
|
|
74
|
+
self.local_debug = local_debug
|
|
75
|
+
self.output_formatter = output_formatter
|
|
65
76
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
if local_debug:
|
|
78
|
+
os.environ["LOCAL_DEBUG"] = "1"
|
|
79
|
+
logger.debug("Set LOCAL_DEBUG environment variable")
|
|
80
|
+
elif "LOCAL_DEBUG" in os.environ:
|
|
81
|
+
del os.environ["LOCAL_DEBUG"]
|
|
82
|
+
logger.debug("Removed LOCAL_DEBUG environment variable")
|
|
72
83
|
|
|
73
84
|
def add_agent(self, agent: T) -> T:
|
|
74
85
|
"""Add a new agent to the Flock system.
|
|
@@ -151,49 +162,71 @@ class Flock:
|
|
|
151
162
|
ValueError: If the specified agent is not found in the registry.
|
|
152
163
|
Exception: For any other errors encountered during execution.
|
|
153
164
|
"""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
start_agent
|
|
158
|
-
if
|
|
159
|
-
|
|
160
|
-
raise ValueError(
|
|
161
|
-
f"Agent '{start_agent}' not found in registry"
|
|
162
|
-
)
|
|
163
|
-
if context:
|
|
164
|
-
logger.debug("Using provided context")
|
|
165
|
-
self.context = context
|
|
166
|
-
if not run_id:
|
|
167
|
-
run_id = f"{start_agent.name}_{uuid.uuid4().hex[:4]}"
|
|
168
|
-
logger.debug("Generated run ID", run_id=run_id)
|
|
169
|
-
|
|
170
|
-
# TODO - Add a check for required input keys
|
|
171
|
-
input_keys = top_level_to_keys(start_agent.input)
|
|
172
|
-
for key in input_keys:
|
|
173
|
-
if key.startswith("flock."):
|
|
174
|
-
key = key[6:] # Remove the "flock." prefix
|
|
175
|
-
if key not in input:
|
|
176
|
-
input[key] = Prompt.ask(
|
|
177
|
-
f"Please enter {key} for {start_agent.name}"
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
# Initialize the context with standardized variables
|
|
181
|
-
initialize_context(
|
|
182
|
-
self.context, start_agent.name, input, run_id, self.local_debug
|
|
165
|
+
with tracer.start_as_current_span("run_async") as span:
|
|
166
|
+
span.set_attribute(
|
|
167
|
+
"start_agent",
|
|
168
|
+
start_agent.name
|
|
169
|
+
if hasattr(start_agent, "name")
|
|
170
|
+
else start_agent,
|
|
183
171
|
)
|
|
184
172
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
173
|
+
span.set_attribute("input", str(input))
|
|
174
|
+
span.set_attribute("context", str(context))
|
|
175
|
+
span.set_attribute("run_id", run_id)
|
|
176
|
+
span.set_attribute("box_result", box_result)
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
if isinstance(start_agent, str):
|
|
180
|
+
logger.debug(
|
|
181
|
+
"Looking up agent by name", agent_name=start_agent
|
|
182
|
+
)
|
|
183
|
+
start_agent = self.registry.get_agent(start_agent)
|
|
184
|
+
if not start_agent:
|
|
185
|
+
logger.error("Agent not found", agent_name=start_agent)
|
|
186
|
+
raise ValueError(
|
|
187
|
+
f"Agent '{start_agent}' not found in registry"
|
|
188
|
+
)
|
|
189
|
+
start_agent.resolve_callables(context=self.context)
|
|
190
|
+
if context:
|
|
191
|
+
logger.debug("Using provided context")
|
|
192
|
+
self.context = context
|
|
193
|
+
if not run_id:
|
|
194
|
+
run_id = f"{start_agent.name}_{uuid.uuid4().hex[:4]}"
|
|
195
|
+
logger.debug("Generated run ID", run_id=run_id)
|
|
196
|
+
|
|
197
|
+
# TODO - Add a check for required input keys
|
|
198
|
+
input_keys = top_level_to_keys(start_agent.input)
|
|
199
|
+
for key in input_keys:
|
|
200
|
+
if key.startswith("flock."):
|
|
201
|
+
key = key[6:] # Remove the "flock." prefix
|
|
202
|
+
if key not in input:
|
|
203
|
+
input[key] = Prompt.ask(
|
|
204
|
+
f"Please enter {key} for {start_agent.name}"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Initialize the context with standardized variables
|
|
208
|
+
initialize_context(
|
|
209
|
+
self.context,
|
|
210
|
+
start_agent.name,
|
|
211
|
+
input,
|
|
212
|
+
run_id,
|
|
213
|
+
self.local_debug,
|
|
214
|
+
)
|
|
190
215
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
216
|
+
logger.info(
|
|
217
|
+
"Starting agent execution",
|
|
218
|
+
agent=start_agent.name,
|
|
219
|
+
local_debug=self.local_debug,
|
|
194
220
|
)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
221
|
+
|
|
222
|
+
if self.local_debug:
|
|
223
|
+
return await run_local_workflow(
|
|
224
|
+
self.context, self.output_formatter, box_result
|
|
225
|
+
)
|
|
226
|
+
else:
|
|
227
|
+
return await run_temporal_workflow(
|
|
228
|
+
self.context, self.output_formatter, box_result
|
|
229
|
+
)
|
|
230
|
+
except Exception as e:
|
|
231
|
+
logger.exception("Execution failed", error=str(e))
|
|
232
|
+
raise
|