flock-core 0.4.0b43__py3-none-any.whl → 0.4.0b45__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/cli/manage_agents.py +19 -4
- flock/core/api/__init__.py +1 -2
- flock/core/api/endpoints.py +150 -218
- flock/core/api/main.py +134 -653
- flock/core/api/service.py +214 -0
- flock/core/flock.py +192 -134
- flock/core/flock_agent.py +31 -0
- flock/webapp/app/api/agent_management.py +135 -164
- flock/webapp/app/api/execution.py +76 -85
- flock/webapp/app/api/flock_management.py +60 -33
- flock/webapp/app/chat.py +233 -0
- flock/webapp/app/config.py +6 -3
- flock/webapp/app/dependencies.py +95 -0
- flock/webapp/app/main.py +320 -906
- flock/webapp/app/services/flock_service.py +183 -161
- flock/webapp/run.py +176 -100
- flock/webapp/static/css/chat.css +227 -0
- flock/webapp/static/css/components.css +167 -0
- flock/webapp/static/css/header.css +39 -0
- flock/webapp/static/css/layout.css +46 -0
- flock/webapp/static/css/sidebar.css +127 -0
- flock/webapp/templates/base.html +6 -1
- flock/webapp/templates/chat.html +60 -0
- flock/webapp/templates/chat_settings.html +20 -0
- flock/webapp/templates/flock_editor.html +1 -1
- flock/webapp/templates/partials/_agent_detail_form.html +8 -7
- flock/webapp/templates/partials/_agent_list.html +3 -3
- flock/webapp/templates/partials/_agent_manager_view.html +3 -4
- flock/webapp/templates/partials/_chat_container.html +9 -0
- flock/webapp/templates/partials/_chat_messages.html +13 -0
- flock/webapp/templates/partials/_chat_settings_form.html +65 -0
- flock/webapp/templates/partials/_execution_form.html +2 -2
- flock/webapp/templates/partials/_execution_view_container.html +1 -1
- flock/webapp/templates/partials/_flock_properties_form.html +2 -2
- flock/webapp/templates/partials/_registry_viewer_content.html +3 -3
- flock/webapp/templates/partials/_sidebar.html +17 -1
- flock/webapp/templates/registry_viewer.html +3 -3
- {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/METADATA +1 -1
- {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/RECORD +42 -31
- flock/webapp/static/css/custom.css +0 -612
- flock/webapp/templates/partials/_agent_manager_view_old.html +0 -19
- {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/WHEEL +0 -0
- {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,154 +1,180 @@
|
|
|
1
|
-
|
|
1
|
+
# src/flock/webapp/app/services/flock_service.py
|
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
# Conditional import for Flock and FlockFactory for type hinting
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from flock.core.flock import Flock
|
|
9
|
+
from flock.core.flock_factory import FlockFactory
|
|
10
|
+
else:
|
|
11
|
+
Flock = "flock.core.flock.Flock"
|
|
12
|
+
FlockFactory = "flock.core.flock_factory.FlockFactory"
|
|
13
|
+
|
|
14
|
+
from flock.core.api.run_store import RunStore
|
|
4
15
|
from flock.core.flock_registry import get_registry
|
|
5
|
-
from flock.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
)
|
|
16
|
+
from flock.core.logging.logging import get_logger
|
|
17
|
+
from flock.webapp.app.config import FLOCK_FILES_DIR
|
|
18
|
+
from flock.webapp.app.dependencies import set_global_flock_services
|
|
19
|
+
|
|
20
|
+
logger = get_logger("webapp.service")
|
|
10
21
|
|
|
11
22
|
|
|
12
23
|
def get_available_flock_files() -> list[str]:
|
|
24
|
+
"""Returns a sorted list of available .yaml, .yml, or .flock files."""
|
|
13
25
|
if not FLOCK_FILES_DIR.exists():
|
|
14
26
|
return []
|
|
15
27
|
return sorted(
|
|
16
28
|
[
|
|
17
29
|
f.name
|
|
18
30
|
for f in FLOCK_FILES_DIR.iterdir()
|
|
19
|
-
if f.is_file() and (f.suffix in [".yaml", ".yml", ".flock"])
|
|
31
|
+
if f.is_file() and (f.suffix.lower() in [".yaml", ".yml", ".flock"])
|
|
20
32
|
]
|
|
21
33
|
)
|
|
22
34
|
|
|
35
|
+
def _ensure_run_store_in_app_state(app_state: object) -> RunStore: # app_state is Starlette's State
|
|
36
|
+
"""Ensures a RunStore instance exists in app_state, creating if necessary."""
|
|
37
|
+
run_store = getattr(app_state, 'run_store', None)
|
|
38
|
+
if not isinstance(run_store, RunStore):
|
|
39
|
+
logger.info("RunStore not found or invalid in app_state, creating a new one for this session.")
|
|
40
|
+
run_store = RunStore()
|
|
41
|
+
setattr(app_state, 'run_store', run_store)
|
|
42
|
+
return run_store
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def load_flock_from_file_service(filename: str, app_state: object) -> Optional["Flock"]:
|
|
46
|
+
"""Loads a Flock instance from a file.
|
|
47
|
+
Updates app_state with the loaded flock/filename and updates DI services.
|
|
48
|
+
"""
|
|
49
|
+
from flock.core.flock import Flock as ConcreteFlock
|
|
23
50
|
|
|
24
|
-
def load_flock_from_file_service(filename: str) -> Flock | None:
|
|
25
|
-
global CURRENT_FLOCK_INSTANCE, CURRENT_FLOCK_FILENAME
|
|
26
51
|
file_path = FLOCK_FILES_DIR / filename
|
|
27
52
|
if not file_path.exists():
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
CURRENT_FLOCK_FILENAME = None
|
|
53
|
+
logger.error(f"Flock file not found: {file_path}")
|
|
54
|
+
clear_current_flock_service(app_state)
|
|
31
55
|
return None
|
|
32
56
|
try:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
57
|
+
logger.info(f"Loading flock from: {file_path}")
|
|
58
|
+
loaded_flock = ConcreteFlock.load_from_file(str(file_path))
|
|
59
|
+
|
|
60
|
+
setattr(app_state, 'flock_instance', loaded_flock)
|
|
61
|
+
setattr(app_state, 'flock_filename', filename)
|
|
62
|
+
run_store = _ensure_run_store_in_app_state(app_state)
|
|
63
|
+
set_global_flock_services(loaded_flock, run_store)
|
|
64
|
+
|
|
65
|
+
logger.info(f"Service: Successfully loaded flock '{loaded_flock.name}' from '{filename}'. DI updated.")
|
|
66
|
+
return loaded_flock
|
|
42
67
|
except Exception as e:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
CURRENT_FLOCK_FILENAME = None
|
|
68
|
+
logger.error(f"Service: Error loading flock from {file_path}: {e}", exc_info=True)
|
|
69
|
+
clear_current_flock_service(app_state)
|
|
46
70
|
return None
|
|
47
71
|
|
|
48
72
|
|
|
49
73
|
def create_new_flock_service(
|
|
50
|
-
name: str, model: str | None, description: str | None
|
|
51
|
-
) -> Flock:
|
|
52
|
-
|
|
74
|
+
name: str, model: str | None, description: str | None, app_state: object
|
|
75
|
+
) -> "Flock":
|
|
76
|
+
"""Creates a new Flock instance.
|
|
77
|
+
Updates app_state and DI services.
|
|
78
|
+
"""
|
|
79
|
+
from flock.core.flock import Flock as ConcreteFlock
|
|
80
|
+
|
|
53
81
|
effective_model = model.strip() if model and model.strip() else None
|
|
54
|
-
|
|
82
|
+
new_flock = ConcreteFlock(
|
|
55
83
|
name=name,
|
|
56
84
|
model=effective_model,
|
|
57
85
|
description=description,
|
|
58
86
|
show_flock_banner=False,
|
|
59
|
-
enable_logging=
|
|
87
|
+
enable_logging=True,
|
|
60
88
|
)
|
|
61
|
-
|
|
62
|
-
print(f"Created new flock: {name}")
|
|
63
|
-
return CURRENT_FLOCK_INSTANCE
|
|
89
|
+
default_filename = f"{name.replace(' ', '_').lower()}.flock.yaml"
|
|
64
90
|
|
|
91
|
+
setattr(app_state, 'flock_instance', new_flock)
|
|
92
|
+
setattr(app_state, 'flock_filename', default_filename)
|
|
93
|
+
run_store = _ensure_run_store_in_app_state(app_state)
|
|
94
|
+
set_global_flock_services(new_flock, run_store)
|
|
65
95
|
|
|
66
|
-
|
|
67
|
-
return
|
|
96
|
+
logger.info(f"Service: Created new flock '{name}'. DI updated. Default filename: '{default_filename}'.")
|
|
97
|
+
return new_flock
|
|
68
98
|
|
|
69
99
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def set_current_flock_instance_programmatically(flock: Flock, filename: str):
|
|
75
|
-
"""Sets the current flock instance and filename programmatically.
|
|
76
|
-
Used when launching the UI with a pre-loaded flock from an external source (e.g., API server).
|
|
100
|
+
def clear_current_flock_service(app_state: object):
|
|
101
|
+
"""Clears the current Flock from app_state and updates DI services.
|
|
77
102
|
"""
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
f"Programmatically set flock: {filename} (Name: {flock.name if flock else 'None'})"
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def set_current_flock_filename(filename: str | None):
|
|
87
|
-
global CURRENT_FLOCK_FILENAME
|
|
88
|
-
CURRENT_FLOCK_FILENAME = filename
|
|
103
|
+
if hasattr(app_state, 'flock_instance'):
|
|
104
|
+
delattr(app_state, 'flock_instance')
|
|
105
|
+
if hasattr(app_state, 'flock_filename'):
|
|
106
|
+
delattr(app_state, 'flock_filename')
|
|
89
107
|
|
|
108
|
+
run_store = _ensure_run_store_in_app_state(app_state)
|
|
109
|
+
set_global_flock_services(None, run_store)
|
|
110
|
+
logger.info("Service: Current flock cleared from app_state. DI updated (Flock is None).")
|
|
90
111
|
|
|
91
|
-
def clear_current_flock():
|
|
92
|
-
global CURRENT_FLOCK_INSTANCE, CURRENT_FLOCK_FILENAME
|
|
93
|
-
CURRENT_FLOCK_INSTANCE = None
|
|
94
|
-
CURRENT_FLOCK_FILENAME = None
|
|
95
|
-
print("Current flock cleared.")
|
|
96
112
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
113
|
+
def save_current_flock_to_file_service(new_filename: str, app_state: object) -> tuple[bool, str]:
|
|
114
|
+
"""Saves the Flock from app_state to a file. Updates app_state's current filename on success.
|
|
115
|
+
"""
|
|
116
|
+
current_flock: Flock | None = getattr(app_state, 'flock_instance', None)
|
|
117
|
+
if not current_flock:
|
|
101
118
|
return False, "No flock loaded to save."
|
|
102
119
|
if not new_filename.strip():
|
|
103
120
|
return False, "Filename cannot be empty."
|
|
121
|
+
|
|
104
122
|
save_path = FLOCK_FILES_DIR / new_filename
|
|
105
123
|
try:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
124
|
+
current_flock.to_yaml_file(str(save_path))
|
|
125
|
+
setattr(app_state, 'flock_filename', new_filename) # Update filename in app_state
|
|
126
|
+
logger.info(f"Service: Flock '{current_flock.name}' saved to '{new_filename}'.")
|
|
127
|
+
return True, f"Flock saved to '{new_filename}'."
|
|
109
128
|
except Exception as e:
|
|
129
|
+
logger.error(f"Service: Error saving flock '{current_flock.name}' to {save_path}: {e}", exc_info=True)
|
|
110
130
|
return False, f"Error saving flock: {e}"
|
|
111
131
|
|
|
112
132
|
|
|
113
133
|
def update_flock_properties_service(
|
|
114
|
-
name: str, model: str | None, description: str | None
|
|
134
|
+
name: str, model: str | None, description: str | None, app_state: object
|
|
115
135
|
) -> bool:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
"""Updates properties of the Flock in app_state. Updates app_state's filename if name changes.
|
|
137
|
+
"""
|
|
138
|
+
current_flock: Flock | None = getattr(app_state, 'flock_instance', None)
|
|
139
|
+
current_filename: str | None = getattr(app_state, 'flock_filename', None)
|
|
140
|
+
|
|
141
|
+
if not current_flock:
|
|
142
|
+
logger.warning("Service: Attempted to update properties, but no flock loaded.")
|
|
119
143
|
return False
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
|
|
145
|
+
old_name = current_flock.name
|
|
146
|
+
old_name_default_filename = f"{old_name.replace(' ', '_').lower()}.flock.yaml"
|
|
147
|
+
|
|
148
|
+
current_flock.name = name
|
|
149
|
+
current_flock.model = model.strip() if model and model.strip() else None
|
|
150
|
+
current_flock.description = description
|
|
151
|
+
|
|
152
|
+
if current_filename == old_name_default_filename and old_name != name:
|
|
153
|
+
new_default_filename = f"{name.replace(' ', '_').lower()}.flock.yaml"
|
|
154
|
+
setattr(app_state, 'flock_filename', new_default_filename)
|
|
155
|
+
logger.info(f"Service: Default filename updated to '{new_default_filename}' due to flock name change.")
|
|
156
|
+
|
|
157
|
+
logger.info(f"Service: Flock properties updated for '{name}'.")
|
|
134
158
|
return True
|
|
135
159
|
|
|
136
160
|
|
|
137
|
-
def add_agent_to_current_flock_service(agent_config: dict) -> bool:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
161
|
+
def add_agent_to_current_flock_service(agent_config: dict, app_state: object) -> bool:
|
|
162
|
+
"""Adds an agent to the Flock in app_state."""
|
|
163
|
+
from flock.core.flock_factory import FlockFactory as ConcreteFlockFactory
|
|
164
|
+
|
|
165
|
+
current_flock: Flock | None = getattr(app_state, 'flock_instance', None)
|
|
166
|
+
if not current_flock:
|
|
167
|
+
logger.warning("Service: Cannot add agent, no flock loaded.")
|
|
141
168
|
return False
|
|
169
|
+
|
|
142
170
|
registry = get_registry()
|
|
143
171
|
tools_instances = []
|
|
144
172
|
if agent_config.get("tools_names"):
|
|
145
173
|
for tool_name in agent_config["tools_names"]:
|
|
146
|
-
try:
|
|
147
|
-
|
|
148
|
-
except KeyError:
|
|
149
|
-
print(f"Warning: Tool '{tool_name}' not found. Skipping.")
|
|
174
|
+
try: tools_instances.append(registry.get_callable(tool_name))
|
|
175
|
+
except KeyError: logger.warning(f"Service: Tool '{tool_name}' not found for agent '{agent_config['name']}'.")
|
|
150
176
|
try:
|
|
151
|
-
agent =
|
|
177
|
+
agent = ConcreteFlockFactory.create_default_agent(
|
|
152
178
|
name=agent_config["name"],
|
|
153
179
|
description=agent_config.get("description"),
|
|
154
180
|
model=agent_config.get("model"),
|
|
@@ -156,127 +182,121 @@ def add_agent_to_current_flock_service(agent_config: dict) -> bool:
|
|
|
156
182
|
output=agent_config["output"],
|
|
157
183
|
tools=tools_instances or None,
|
|
158
184
|
)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
from flock.routers.default.default_router import DefaultRouterConfig
|
|
162
|
-
|
|
163
|
-
agent.add_component(DefaultRouterConfig(hand_off=handoff_target))
|
|
164
|
-
CURRENT_FLOCK_INSTANCE.add_agent(agent)
|
|
185
|
+
current_flock.add_agent(agent)
|
|
186
|
+
logger.info(f"Service: Agent '{agent.name}' added to flock '{current_flock.name}'.")
|
|
165
187
|
return True
|
|
166
188
|
except Exception as e:
|
|
167
|
-
|
|
189
|
+
logger.error(f"Service: Error adding agent to flock '{current_flock.name}': {e}", exc_info=True)
|
|
168
190
|
return False
|
|
169
191
|
|
|
170
192
|
|
|
171
193
|
def update_agent_in_current_flock_service(
|
|
172
|
-
original_agent_name: str, agent_config: dict
|
|
194
|
+
original_agent_name: str, agent_config: dict, app_state: object
|
|
173
195
|
) -> bool:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if not
|
|
196
|
+
"""Updates an agent in the Flock in app_state."""
|
|
197
|
+
current_flock: Flock | None = getattr(app_state, 'flock_instance', None)
|
|
198
|
+
if not current_flock:
|
|
199
|
+
logger.warning("Service: Cannot update agent, no flock loaded.")
|
|
177
200
|
return False
|
|
178
|
-
|
|
201
|
+
|
|
202
|
+
agent_to_update = current_flock.agents.get(original_agent_name)
|
|
179
203
|
if not agent_to_update:
|
|
204
|
+
logger.warning(f"Service: Agent '{original_agent_name}' not found in flock '{current_flock.name}' for update.")
|
|
180
205
|
return False
|
|
206
|
+
|
|
181
207
|
registry = get_registry()
|
|
182
208
|
tools_instances = []
|
|
183
209
|
if agent_config.get("tools_names"):
|
|
184
210
|
for tool_name in agent_config["tools_names"]:
|
|
185
|
-
try:
|
|
186
|
-
|
|
187
|
-
except KeyError:
|
|
188
|
-
print(f"Warning: Tool '{tool_name}' not found. Skipping.")
|
|
211
|
+
try: tools_instances.append(registry.get_callable(tool_name))
|
|
212
|
+
except KeyError: logger.warning(f"Service: Tool '{tool_name}' not found during agent update.")
|
|
189
213
|
try:
|
|
190
214
|
new_name = agent_config["name"]
|
|
191
215
|
agent_to_update.description = agent_config.get("description")
|
|
192
|
-
|
|
216
|
+
current_agent_model = agent_config.get("model")
|
|
217
|
+
agent_to_update.model = current_agent_model if current_agent_model and current_agent_model.strip() else None
|
|
193
218
|
agent_to_update.input = agent_config["input"]
|
|
194
219
|
agent_to_update.output = agent_config["output"]
|
|
195
|
-
agent_to_update.tools = tools_instances or
|
|
196
|
-
|
|
197
|
-
if handoff_target:
|
|
198
|
-
from flock.routers.default.default_router import DefaultRouterConfig
|
|
199
|
-
|
|
200
|
-
agent_to_update.add_component(
|
|
201
|
-
DefaultRouterConfig(hand_off=handoff_target)
|
|
202
|
-
)
|
|
203
|
-
elif agent_to_update.handoff_router:
|
|
204
|
-
agent_to_update.handoff_router = None
|
|
220
|
+
agent_to_update.tools = tools_instances or []
|
|
221
|
+
|
|
205
222
|
if original_agent_name != new_name:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
)
|
|
209
|
-
|
|
223
|
+
current_flock._agents.pop(original_agent_name)
|
|
224
|
+
agent_to_update.name = new_name
|
|
225
|
+
current_flock.add_agent(agent_to_update)
|
|
226
|
+
logger.info(f"Service: Agent '{original_agent_name}' renamed to '{new_name}' and updated in flock '{current_flock.name}'.")
|
|
227
|
+
else:
|
|
228
|
+
logger.info(f"Service: Agent '{original_agent_name}' updated in flock '{current_flock.name}'.")
|
|
210
229
|
return True
|
|
211
230
|
except Exception as e:
|
|
212
|
-
|
|
231
|
+
logger.error(f"Service: Error updating agent '{original_agent_name}' in flock '{current_flock.name}': {e}", exc_info=True)
|
|
213
232
|
return False
|
|
214
233
|
|
|
215
234
|
|
|
216
|
-
def remove_agent_from_current_flock_service(agent_name: str) -> bool:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if
|
|
220
|
-
not
|
|
221
|
-
|
|
222
|
-
|
|
235
|
+
def remove_agent_from_current_flock_service(agent_name: str, app_state: object) -> bool:
|
|
236
|
+
"""Removes an agent from the Flock in app_state."""
|
|
237
|
+
current_flock: Flock | None = getattr(app_state, 'flock_instance', None)
|
|
238
|
+
if not current_flock or agent_name not in current_flock.agents:
|
|
239
|
+
logger.warning(f"Service: Cannot remove agent '{agent_name}', no flock loaded or agent not found.")
|
|
240
|
+
return False
|
|
241
|
+
try:
|
|
242
|
+
del current_flock._agents[agent_name]
|
|
243
|
+
logger.info(f"Service: Agent '{agent_name}' removed from flock '{current_flock.name}'.")
|
|
244
|
+
return True
|
|
245
|
+
except Exception as e:
|
|
246
|
+
logger.error(f"Service: Error removing agent '{agent_name}' from flock '{current_flock.name}': {e}", exc_info=True)
|
|
223
247
|
return False
|
|
224
|
-
del CURRENT_FLOCK_INSTANCE._agents[agent_name]
|
|
225
|
-
return True
|
|
226
248
|
|
|
227
249
|
|
|
228
250
|
async def run_current_flock_service(
|
|
229
|
-
start_agent_name: str, inputs: dict
|
|
251
|
+
start_agent_name: str, inputs: dict, app_state: object
|
|
230
252
|
) -> dict | str:
|
|
231
|
-
|
|
232
|
-
|
|
253
|
+
"""Runs the Flock instance from app_state."""
|
|
254
|
+
current_flock: Flock | None = getattr(app_state, 'flock_instance', None)
|
|
255
|
+
if not current_flock:
|
|
256
|
+
logger.error("Service: Execution failed, no flock loaded.")
|
|
233
257
|
return "Error: No flock loaded."
|
|
234
|
-
if
|
|
235
|
-
not
|
|
236
|
-
or start_agent_name not in CURRENT_FLOCK_INSTANCE.agents
|
|
237
|
-
):
|
|
258
|
+
if not start_agent_name or start_agent_name not in current_flock.agents:
|
|
259
|
+
logger.error(f"Service: Execution failed, start agent '{start_agent_name}' not found in flock '{current_flock.name}'.")
|
|
238
260
|
return f"Error: Start agent '{start_agent_name}' not found."
|
|
239
261
|
try:
|
|
240
|
-
|
|
262
|
+
logger.info(f"Service: Running flock '{current_flock.name}' starting with agent '{start_agent_name}'.")
|
|
263
|
+
result = await current_flock.run_async(
|
|
241
264
|
start_agent=start_agent_name, input=inputs, box_result=False
|
|
242
265
|
)
|
|
243
|
-
# Don't convert here - let the API route handle it to avoid double conversion
|
|
244
266
|
return result
|
|
245
267
|
except Exception as e:
|
|
246
|
-
|
|
268
|
+
logger.error(f"Service: Error during flock execution for '{current_flock.name}': {e}", exc_info=True)
|
|
247
269
|
return f"Error: {e!s}"
|
|
248
270
|
|
|
249
271
|
|
|
250
|
-
def get_registered_items_service(item_type: str) -> list:
|
|
251
|
-
|
|
272
|
+
def get_registered_items_service(item_type: str) -> list[dict]:
|
|
273
|
+
"""Retrieves items of a specific type from the global FlockRegistry."""
|
|
252
274
|
registry = get_registry()
|
|
253
|
-
|
|
254
|
-
if item_type == "type":
|
|
255
|
-
|
|
256
|
-
elif item_type == "
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
275
|
+
items_dict: dict | None = None
|
|
276
|
+
if item_type == "type": items_dict = registry._types
|
|
277
|
+
elif item_type == "tool": items_dict = registry._callables
|
|
278
|
+
elif item_type == "component": items_dict = registry._components
|
|
279
|
+
else: return []
|
|
280
|
+
|
|
281
|
+
if items_dict is None: return []
|
|
282
|
+
|
|
283
|
+
items = []
|
|
262
284
|
for name, item_obj in items_dict.items():
|
|
263
285
|
module_path = "N/A"
|
|
264
|
-
try:
|
|
265
|
-
|
|
266
|
-
except AttributeError:
|
|
267
|
-
pass
|
|
286
|
+
try: module_path = item_obj.__module__
|
|
287
|
+
except AttributeError: pass
|
|
268
288
|
items.append({"name": name, "module": module_path})
|
|
269
289
|
return sorted(items, key=lambda x: x["name"])
|
|
270
290
|
|
|
271
291
|
|
|
272
292
|
def get_flock_preview_service(filename: str) -> dict | None:
|
|
273
|
-
"""Loads
|
|
293
|
+
"""Loads basic properties of a flock file for preview."""
|
|
274
294
|
file_path = FLOCK_FILES_DIR / filename
|
|
275
295
|
if not file_path.exists():
|
|
296
|
+
logger.warning(f"Service: Preview failed, file not found {file_path}")
|
|
276
297
|
return None
|
|
277
298
|
try:
|
|
278
299
|
with file_path.open("r", encoding="utf-8") as f:
|
|
279
|
-
# Load YAML just to get top-level keys
|
|
280
300
|
data = yaml.safe_load(f)
|
|
281
301
|
if isinstance(data, dict):
|
|
282
302
|
return {
|
|
@@ -284,8 +304,10 @@ def get_flock_preview_service(filename: str) -> dict | None:
|
|
|
284
304
|
"model": data.get("model"),
|
|
285
305
|
"description": data.get("description"),
|
|
286
306
|
"agents_count": len(data.get("agents", {})),
|
|
307
|
+
"enable_temporal": data.get("enable_temporal", False)
|
|
287
308
|
}
|
|
309
|
+
logger.warning(f"Service: Preview failed, '{filename}' is not a valid Flock YAML (not a dict).")
|
|
288
310
|
return {"name": filename, "error": "Not a valid Flock YAML structure"}
|
|
289
311
|
except Exception as e:
|
|
290
|
-
|
|
312
|
+
logger.error(f"Service: Error getting flock preview for {filename}: {e}", exc_info=True)
|
|
291
313
|
return {"name": filename, "error": str(e)}
|