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.

Files changed (44) hide show
  1. flock/cli/manage_agents.py +19 -4
  2. flock/core/api/__init__.py +1 -2
  3. flock/core/api/endpoints.py +150 -218
  4. flock/core/api/main.py +134 -653
  5. flock/core/api/service.py +214 -0
  6. flock/core/flock.py +192 -134
  7. flock/core/flock_agent.py +31 -0
  8. flock/webapp/app/api/agent_management.py +135 -164
  9. flock/webapp/app/api/execution.py +76 -85
  10. flock/webapp/app/api/flock_management.py +60 -33
  11. flock/webapp/app/chat.py +233 -0
  12. flock/webapp/app/config.py +6 -3
  13. flock/webapp/app/dependencies.py +95 -0
  14. flock/webapp/app/main.py +320 -906
  15. flock/webapp/app/services/flock_service.py +183 -161
  16. flock/webapp/run.py +176 -100
  17. flock/webapp/static/css/chat.css +227 -0
  18. flock/webapp/static/css/components.css +167 -0
  19. flock/webapp/static/css/header.css +39 -0
  20. flock/webapp/static/css/layout.css +46 -0
  21. flock/webapp/static/css/sidebar.css +127 -0
  22. flock/webapp/templates/base.html +6 -1
  23. flock/webapp/templates/chat.html +60 -0
  24. flock/webapp/templates/chat_settings.html +20 -0
  25. flock/webapp/templates/flock_editor.html +1 -1
  26. flock/webapp/templates/partials/_agent_detail_form.html +8 -7
  27. flock/webapp/templates/partials/_agent_list.html +3 -3
  28. flock/webapp/templates/partials/_agent_manager_view.html +3 -4
  29. flock/webapp/templates/partials/_chat_container.html +9 -0
  30. flock/webapp/templates/partials/_chat_messages.html +13 -0
  31. flock/webapp/templates/partials/_chat_settings_form.html +65 -0
  32. flock/webapp/templates/partials/_execution_form.html +2 -2
  33. flock/webapp/templates/partials/_execution_view_container.html +1 -1
  34. flock/webapp/templates/partials/_flock_properties_form.html +2 -2
  35. flock/webapp/templates/partials/_registry_viewer_content.html +3 -3
  36. flock/webapp/templates/partials/_sidebar.html +17 -1
  37. flock/webapp/templates/registry_viewer.html +3 -3
  38. {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/METADATA +1 -1
  39. {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/RECORD +42 -31
  40. flock/webapp/static/css/custom.css +0 -612
  41. flock/webapp/templates/partials/_agent_manager_view_old.html +0 -19
  42. {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/WHEEL +0 -0
  43. {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/entry_points.txt +0 -0
  44. {flock_core-0.4.0b43.dist-info → flock_core-0.4.0b45.dist-info}/licenses/LICENSE +0 -0
@@ -1,154 +1,180 @@
1
- import yaml # For parsing issues, if any, during load
1
+ # src/flock/webapp/app/services/flock_service.py
2
+ from typing import TYPE_CHECKING, Optional
2
3
 
3
- from flock.core import Flock, FlockFactory
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.webapp.app.config import (
6
- CURRENT_FLOCK_FILENAME,
7
- CURRENT_FLOCK_INSTANCE,
8
- FLOCK_FILES_DIR,
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
- print(f"Error: File not found {file_path}")
29
- CURRENT_FLOCK_INSTANCE = None
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
- # Temporarily clear registry parts that might be file-specific if needed,
34
- # or ensure load_from_file handles re-registration gracefully.
35
- # For MVP, assume load_from_file is robust enough.
36
- CURRENT_FLOCK_INSTANCE = Flock.load_from_file(str(file_path))
37
- CURRENT_FLOCK_FILENAME = filename
38
- print(
39
- f"Successfully loaded flock: {CURRENT_FLOCK_INSTANCE.name if CURRENT_FLOCK_INSTANCE else 'None'}"
40
- )
41
- return CURRENT_FLOCK_INSTANCE
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
- print(f"Error loading flock from {file_path}: {e}")
44
- CURRENT_FLOCK_INSTANCE = None
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
- global CURRENT_FLOCK_INSTANCE, CURRENT_FLOCK_FILENAME
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
- CURRENT_FLOCK_INSTANCE = Flock(
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=False,
87
+ enable_logging=True,
60
88
  )
61
- CURRENT_FLOCK_FILENAME = f"{name.replace(' ', '_').lower()}.flock.yaml"
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
- def get_current_flock_instance() -> Flock | None:
67
- return CURRENT_FLOCK_INSTANCE
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 get_current_flock_filename() -> str | None:
71
- return CURRENT_FLOCK_FILENAME
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
- global CURRENT_FLOCK_INSTANCE, CURRENT_FLOCK_FILENAME
79
- CURRENT_FLOCK_INSTANCE = flock
80
- CURRENT_FLOCK_FILENAME = filename
81
- print(
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
- def save_current_flock_to_file_service(new_filename: str) -> tuple[bool, str]:
99
- global CURRENT_FLOCK_INSTANCE, CURRENT_FLOCK_FILENAME
100
- if not CURRENT_FLOCK_INSTANCE:
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
- CURRENT_FLOCK_INSTANCE.to_yaml_file(str(save_path))
107
- CURRENT_FLOCK_FILENAME = new_filename
108
- return True, f"Flock saved to {new_filename}."
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
- # ... (same as before)
117
- global CURRENT_FLOCK_INSTANCE, CURRENT_FLOCK_FILENAME
118
- if not CURRENT_FLOCK_INSTANCE:
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
- old_name_default_filename = (
121
- f"{CURRENT_FLOCK_INSTANCE.name.replace(' ', '_').lower()}.flock.yaml"
122
- )
123
- if (
124
- old_name_default_filename == CURRENT_FLOCK_FILENAME
125
- and CURRENT_FLOCK_INSTANCE.name != name
126
- ):
127
- CURRENT_FLOCK_FILENAME = f"{name.replace(' ', '_').lower()}.flock.yaml"
128
-
129
- CURRENT_FLOCK_INSTANCE.name = name
130
- CURRENT_FLOCK_INSTANCE.model = (
131
- model.strip() if model and model.strip() else None
132
- )
133
- CURRENT_FLOCK_INSTANCE.description = description
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
- # ... (same as before) ...
139
- global CURRENT_FLOCK_INSTANCE
140
- if not CURRENT_FLOCK_INSTANCE:
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
- tools_instances.append(registry.get_callable(tool_name))
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 = FlockFactory.create_default_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
- handoff_target = agent_config.get("default_router_handoff")
160
- if handoff_target:
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
- print(f"Error adding agent: {e}")
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
- # ... (same as before) ...
175
- global CURRENT_FLOCK_INSTANCE
176
- if not CURRENT_FLOCK_INSTANCE:
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
- agent_to_update = CURRENT_FLOCK_INSTANCE.agents.get(original_agent_name)
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
- tools_instances.append(registry.get_callable(tool_name))
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
- agent_to_update.model = agent_config.get("model")
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 None
196
- handoff_target = agent_config.get("default_router_handoff")
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
- CURRENT_FLOCK_INSTANCE._agents[new_name] = (
207
- CURRENT_FLOCK_INSTANCE._agents.pop(original_agent_name)
208
- )
209
- agent_to_update.name = new_name
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
- print(f"Error updating agent: {e}")
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
- # ... (same as before) ...
218
- global CURRENT_FLOCK_INSTANCE
219
- if (
220
- not CURRENT_FLOCK_INSTANCE
221
- or agent_name not in CURRENT_FLOCK_INSTANCE.agents
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
- global CURRENT_FLOCK_INSTANCE
232
- if not CURRENT_FLOCK_INSTANCE:
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 start_agent_name
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
- result = await CURRENT_FLOCK_INSTANCE.run_async(
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
- print(f"Error during flock execution: {e}")
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
- # ... (same as before) ...
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
- items, items_dict = [], None
254
- if item_type == "type":
255
- items_dict = registry._types
256
- elif item_type == "tool":
257
- items_dict = registry._callables
258
- elif item_type == "component":
259
- items_dict = registry._components
260
- else:
261
- return []
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
- module_path = item_obj.__module__
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 only basic properties of a flock file for preview without full deserialization."""
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
- print(f"Error getting flock preview for {filename}: {e}")
312
+ logger.error(f"Service: Error getting flock preview for {filename}: {e}", exc_info=True)
291
313
  return {"name": filename, "error": str(e)}