flock-core 0.4.527__py3-none-any.whl → 0.5.0b0__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 (130) hide show
  1. flock/cli/execute_flock.py +1 -1
  2. flock/cli/manage_agents.py +6 -6
  3. flock/components/__init__.py +30 -0
  4. flock/components/evaluation/__init__.py +9 -0
  5. flock/components/evaluation/declarative_evaluation_component.py +222 -0
  6. flock/components/routing/__init__.py +15 -0
  7. flock/{routers/conditional/conditional_router.py → components/routing/conditional_routing_component.py} +61 -53
  8. flock/components/routing/default_routing_component.py +103 -0
  9. flock/components/routing/llm_routing_component.py +206 -0
  10. flock/components/utility/__init__.py +15 -0
  11. flock/{modules/enterprise_memory/enterprise_memory_module.py → components/utility/memory_utility_component.py} +195 -173
  12. flock/{modules/performance/metrics_module.py → components/utility/metrics_utility_component.py} +110 -95
  13. flock/{modules/output/output_module.py → components/utility/output_utility_component.py} +47 -45
  14. flock/core/__init__.py +26 -18
  15. flock/core/agent/__init__.py +16 -0
  16. flock/core/agent/flock_agent_components.py +104 -0
  17. flock/core/agent/flock_agent_execution.py +101 -0
  18. flock/core/agent/flock_agent_integration.py +206 -0
  19. flock/core/agent/flock_agent_lifecycle.py +177 -0
  20. flock/core/agent/flock_agent_serialization.py +381 -0
  21. flock/core/api/endpoints.py +2 -2
  22. flock/core/api/service.py +2 -2
  23. flock/core/component/__init__.py +15 -0
  24. flock/core/{flock_module.py → component/agent_component_base.py} +136 -34
  25. flock/core/component/evaluation_component.py +56 -0
  26. flock/core/component/routing_component.py +74 -0
  27. flock/core/component/utility_component.py +69 -0
  28. flock/core/config/flock_agent_config.py +49 -2
  29. flock/core/evaluation/utils.py +3 -2
  30. flock/core/execution/batch_executor.py +1 -1
  31. flock/core/execution/evaluation_executor.py +2 -2
  32. flock/core/execution/opik_executor.py +1 -1
  33. flock/core/flock.py +147 -493
  34. flock/core/flock_agent.py +195 -1032
  35. flock/core/flock_factory.py +114 -90
  36. flock/core/flock_scheduler.py +1 -1
  37. flock/core/flock_server_manager.py +8 -8
  38. flock/core/logging/logging.py +1 -0
  39. flock/core/mcp/flock_mcp_server.py +53 -48
  40. flock/core/mcp/{flock_mcp_tool_base.py → flock_mcp_tool.py} +2 -2
  41. flock/core/mcp/mcp_client.py +9 -9
  42. flock/core/mcp/mcp_client_manager.py +9 -9
  43. flock/core/mcp/mcp_config.py +24 -24
  44. flock/core/mixin/dspy_integration.py +5 -5
  45. flock/core/orchestration/__init__.py +18 -0
  46. flock/core/orchestration/flock_batch_processor.py +94 -0
  47. flock/core/orchestration/flock_evaluator.py +113 -0
  48. flock/core/orchestration/flock_execution.py +288 -0
  49. flock/core/orchestration/flock_initialization.py +125 -0
  50. flock/core/orchestration/flock_server_manager.py +67 -0
  51. flock/core/orchestration/flock_web_server.py +117 -0
  52. flock/core/registry/__init__.py +45 -0
  53. flock/core/registry/agent_registry.py +69 -0
  54. flock/core/registry/callable_registry.py +139 -0
  55. flock/core/registry/component_discovery.py +142 -0
  56. flock/core/registry/component_registry.py +64 -0
  57. flock/core/registry/config_mapping.py +64 -0
  58. flock/core/registry/decorators.py +137 -0
  59. flock/core/registry/registry_hub.py +205 -0
  60. flock/core/registry/server_registry.py +57 -0
  61. flock/core/registry/type_registry.py +86 -0
  62. flock/core/serialization/flock_serializer.py +36 -32
  63. flock/core/serialization/serialization_utils.py +28 -25
  64. flock/core/util/hydrator.py +1 -1
  65. flock/core/util/input_resolver.py +29 -2
  66. flock/mcp/servers/sse/flock_sse_server.py +10 -10
  67. flock/mcp/servers/stdio/flock_stdio_server.py +10 -10
  68. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +10 -10
  69. flock/mcp/servers/websockets/flock_websocket_server.py +10 -10
  70. flock/platform/docker_tools.py +3 -3
  71. flock/webapp/app/chat.py +1 -1
  72. flock/webapp/app/main.py +9 -5
  73. flock/webapp/app/services/flock_service.py +1 -1
  74. flock/webapp/app/services/sharing_store.py +1 -0
  75. flock/workflow/activities.py +67 -92
  76. flock/workflow/agent_execution_activity.py +6 -6
  77. flock/workflow/flock_workflow.py +1 -1
  78. flock_core-0.5.0b0.dist-info/METADATA +272 -0
  79. {flock_core-0.4.527.dist-info → flock_core-0.5.0b0.dist-info}/RECORD +82 -95
  80. flock/core/flock_evaluator.py +0 -60
  81. flock/core/flock_registry.py +0 -702
  82. flock/core/flock_router.py +0 -83
  83. flock/evaluators/__init__.py +0 -1
  84. flock/evaluators/declarative/__init__.py +0 -1
  85. flock/evaluators/declarative/declarative_evaluator.py +0 -217
  86. flock/evaluators/memory/memory_evaluator.py +0 -90
  87. flock/evaluators/test/test_case_evaluator.py +0 -38
  88. flock/evaluators/zep/zep_evaluator.py +0 -59
  89. flock/modules/__init__.py +0 -1
  90. flock/modules/assertion/__init__.py +0 -1
  91. flock/modules/assertion/assertion_module.py +0 -286
  92. flock/modules/callback/__init__.py +0 -1
  93. flock/modules/callback/callback_module.py +0 -91
  94. flock/modules/enterprise_memory/README.md +0 -99
  95. flock/modules/mem0/__init__.py +0 -1
  96. flock/modules/mem0/mem0_module.py +0 -126
  97. flock/modules/mem0_async/__init__.py +0 -1
  98. flock/modules/mem0_async/async_mem0_module.py +0 -126
  99. flock/modules/memory/__init__.py +0 -1
  100. flock/modules/memory/memory_module.py +0 -429
  101. flock/modules/memory/memory_parser.py +0 -125
  102. flock/modules/memory/memory_storage.py +0 -736
  103. flock/modules/output/__init__.py +0 -1
  104. flock/modules/performance/__init__.py +0 -1
  105. flock/modules/zep/__init__.py +0 -1
  106. flock/modules/zep/zep_module.py +0 -192
  107. flock/routers/__init__.py +0 -1
  108. flock/routers/agent/__init__.py +0 -1
  109. flock/routers/agent/agent_router.py +0 -236
  110. flock/routers/agent/handoff_agent.py +0 -58
  111. flock/routers/default/__init__.py +0 -1
  112. flock/routers/default/default_router.py +0 -80
  113. flock/routers/feedback/feedback_router.py +0 -114
  114. flock/routers/list_generator/list_generator_router.py +0 -166
  115. flock/routers/llm/__init__.py +0 -1
  116. flock/routers/llm/llm_router.py +0 -365
  117. flock/tools/__init__.py +0 -0
  118. flock/tools/azure_tools.py +0 -781
  119. flock/tools/code_tools.py +0 -167
  120. flock/tools/file_tools.py +0 -149
  121. flock/tools/github_tools.py +0 -157
  122. flock/tools/markdown_tools.py +0 -205
  123. flock/tools/system_tools.py +0 -9
  124. flock/tools/text_tools.py +0 -810
  125. flock/tools/web_tools.py +0 -90
  126. flock/tools/zendesk_tools.py +0 -147
  127. flock_core-0.4.527.dist-info/METADATA +0 -674
  128. {flock_core-0.4.527.dist-info → flock_core-0.5.0b0.dist-info}/WHEEL +0 -0
  129. {flock_core-0.4.527.dist-info → flock_core-0.5.0b0.dist-info}/entry_points.txt +0 -0
  130. {flock_core-0.4.527.dist-info → flock_core-0.5.0b0.dist-info}/licenses/LICENSE +0 -0
flock/core/flock.py CHANGED
@@ -3,12 +3,8 @@
3
3
 
4
4
  from __future__ import annotations # Ensure forward references work
5
5
 
6
- import asyncio
7
- import contextvars
8
- import os
9
6
  import uuid
10
- from collections.abc import Awaitable, Callable, Sequence
11
- from concurrent.futures import ThreadPoolExecutor
7
+ from collections.abc import Callable, Sequence
12
8
  from pathlib import Path
13
9
  from typing import (
14
10
  TYPE_CHECKING,
@@ -21,21 +17,12 @@ from typing import (
21
17
  from box import Box
22
18
  from temporalio import workflow
23
19
 
24
- from flock.core.flock_server_manager import FlockServerManager
25
- from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
20
+ from flock.core.mcp.flock_mcp_server import FlockMCPServer
26
21
 
27
22
  with workflow.unsafe.imports_passed_through():
28
23
  from datasets import Dataset # type: ignore
29
24
 
30
- # Assuming run_local_workflow is correctly placed and importable
31
- from flock.core.execution.local_executor import (
32
- run_local_workflow,
33
- )
34
-
35
- import opik
36
25
  from opentelemetry import trace
37
- from opentelemetry.baggage import get_baggage, set_baggage
38
- from opik.integrations.dspy.callback import OpikCallback
39
26
  from pandas import DataFrame # type: ignore
40
27
  from pydantic import BaseModel, Field
41
28
 
@@ -45,14 +32,8 @@ from flock.core.api.custom_endpoint import (
45
32
  FlockEndpoint, # Keep for type hinting custom_endpoints
46
33
  )
47
34
  from flock.core.context.context import FlockContext
48
- from flock.core.context.context_manager import initialize_context
49
-
50
- # Assuming run_temporal_workflow is correctly placed and importable
51
- from flock.core.execution.temporal_executor import run_temporal_workflow
52
- from flock.core.flock_evaluator import FlockEvaluator # For type hint
53
35
  from flock.core.logging.logging import get_logger
54
36
  from flock.core.serialization.serializable import Serializable
55
- from flock.core.util.cli_helper import init_console
56
37
  from flock.workflow.temporal_config import TemporalWorkflowConfig
57
38
 
58
39
  # Import FlockAgent using TYPE_CHECKING to avoid circular import at runtime
@@ -62,7 +43,7 @@ if TYPE_CHECKING:
62
43
 
63
44
 
64
45
  # Registry
65
- from flock.core.flock_registry import get_registry
46
+ from flock.core.registry import get_registry
66
47
 
67
48
  try:
68
49
  import pandas as pd # type: ignore
@@ -75,7 +56,7 @@ except ImportError:
75
56
  logger = get_logger("flock.api")
76
57
  TELEMETRY.setup_tracing() # Setup OpenTelemetry
77
58
  tracer = trace.get_tracer(__name__)
78
- FlockRegistry = get_registry() # Get the registry instance
59
+ registry = get_registry() # Get the registry instance
79
60
 
80
61
  # Define TypeVar for generic class methods like from_dict
81
62
  T = TypeVar("T", bound="Flock")
@@ -144,40 +125,78 @@ class Flock(BaseModel, Serializable):
144
125
  _start_input: dict = {} # For potential pre-configuration
145
126
 
146
127
  # Internal server storage - not part of the Pydantic model for direct serialization
147
- _servers: dict[str, FlockMCPServerBase]
128
+ _servers: dict[str, FlockMCPServer]
148
129
 
149
- # Async context-manager for startup and teardown of servers
150
- # Not part of the pydantic model
151
- _mgr: FlockServerManager
130
+ # Note: _mgr is now handled by the server manager helper
152
131
 
153
132
  # Pydantic v2 model config
154
133
  model_config = {
155
134
  "arbitrary_types_allowed": True,
156
- # Assuming FlockRegistry type might not be serializable by default
157
- "ignored_types": (type(FlockRegistry),),
135
+ # Assuming registry type might not be serializable by default
136
+ "ignored_types": (type(registry),),
158
137
  }
159
138
 
160
- def _run_sync(self, coro: Awaitable[_R]) -> _R:
161
- """Execute *coro* synchronously.
139
+ # --- COMPOSITION HELPERS (Lazy-Loaded) ---
140
+ # Following the successful FlockAgent pattern
162
141
 
163
- * If no loop is running → ``asyncio.run``.
164
- * Otherwise run ``asyncio.run`` inside a fresh thread **with**
165
- context-vars propagation.
166
- """
167
- try:
168
- asyncio.get_running_loop()
169
- except RuntimeError: # no loop → simple
170
- return asyncio.run(coro)
171
-
172
- # A loop is already running – Jupyter / ASGI / etc.
173
- ctx = contextvars.copy_context() # propagate baggage
174
- with ThreadPoolExecutor(max_workers=1) as pool:
175
- future = pool.submit(ctx.run, asyncio.run, coro)
176
- try:
177
- return future.result()
178
- finally:
179
- if not future.done():
180
- future.cancel()
142
+ @property
143
+ def _execution(self):
144
+ """Get the execution management helper (lazy-loaded)."""
145
+ if not hasattr(self, '_execution_helper'):
146
+ from flock.core.orchestration.flock_execution import FlockExecution
147
+ self._execution_helper = FlockExecution(self)
148
+ return self._execution_helper
149
+
150
+ @property
151
+ def _server_manager(self):
152
+ """Get the server management helper (lazy-loaded)."""
153
+ if not hasattr(self, '_server_manager_helper'):
154
+ from flock.core.orchestration.flock_server_manager import (
155
+ FlockServerManager,
156
+ )
157
+ self._server_manager_helper = FlockServerManager(self)
158
+ return self._server_manager_helper
159
+
160
+ @property
161
+ def _batch_processor(self):
162
+ """Get the batch processing helper (lazy-loaded)."""
163
+ if not hasattr(self, '_batch_processor_helper'):
164
+ from flock.core.orchestration.flock_batch_processor import (
165
+ FlockBatchProcessor,
166
+ )
167
+ self._batch_processor_helper = FlockBatchProcessor(self)
168
+ return self._batch_processor_helper
169
+
170
+ @property
171
+ def _evaluator(self):
172
+ """Get the evaluation helper (lazy-loaded)."""
173
+ if not hasattr(self, '_evaluator_helper'):
174
+ from flock.core.orchestration.flock_evaluator import FlockEvaluator
175
+ self._evaluator_helper = FlockEvaluator(self)
176
+ return self._evaluator_helper
177
+
178
+ @property
179
+ def _web_server(self):
180
+ """Get the web server helper (lazy-loaded)."""
181
+ if not hasattr(self, '_web_server_helper'):
182
+ from flock.core.orchestration.flock_web_server import FlockWebServer
183
+ self._web_server_helper = FlockWebServer(self)
184
+ return self._web_server_helper
185
+
186
+ @property
187
+ def _initialization(self):
188
+ """Get the initialization helper (lazy-loaded)."""
189
+ if not hasattr(self, '_initialization_helper'):
190
+ from flock.core.orchestration.flock_initialization import (
191
+ FlockInitialization,
192
+ )
193
+ self._initialization_helper = FlockInitialization(self)
194
+ return self._initialization_helper
195
+
196
+ @property
197
+ def _mgr(self):
198
+ """Get the internal server manager for compatibility."""
199
+ return self._server_manager._internal_mgr
181
200
 
182
201
  def __init__(
183
202
  self,
@@ -188,7 +207,7 @@ class Flock(BaseModel, Serializable):
188
207
  enable_temporal: bool = False,
189
208
  enable_opik: bool = False,
190
209
  agents: list[FlockAgent] | None = None,
191
- servers: list[FlockMCPServerBase] | None = None,
210
+ servers: list[FlockMCPServer] | None = None,
192
211
  temporal_config: TemporalWorkflowConfig | None = None,
193
212
  temporal_start_in_process_worker: bool = True,
194
213
  **kwargs,
@@ -215,65 +234,10 @@ class Flock(BaseModel, Serializable):
215
234
  self._servers = {}
216
235
  self._start_agent_name = None
217
236
  self._start_input = {}
218
- self._mgr = FlockServerManager()
219
-
220
- # Register passed servers
221
- # (need to be registered first so that agents can retrieve them from the registry)
222
- # This will also add them to the managed list of self._mgr
223
- if servers:
224
- from flock.core.mcp.flock_mcp_server import (
225
- FlockMCPServerBase as ConcreteFlockMCPServer,
226
- )
227
-
228
- for server in servers:
229
- if isinstance(server, ConcreteFlockMCPServer):
230
- self.add_server(server)
231
- else:
232
- logger.warning(
233
- f"Item provided in 'servers' list is not a FlockMCPServer: {type(server)}"
234
- )
235
-
236
- # Register passed agents
237
- if agents:
238
- from flock.core.flock_agent import (
239
- FlockAgent as ConcreteFlockAgent, # Local import
240
- )
237
+ # Note: _mgr will be handled by the server manager helper
241
238
 
242
- for agent in agents:
243
- if isinstance(agent, ConcreteFlockAgent):
244
- self.add_agent(agent)
245
- else:
246
- logger.warning(
247
- f"Item provided in 'agents' list is not a FlockAgent: {type(agent)}"
248
- )
249
-
250
- # Initialize console if needed for banner
251
- if self.show_flock_banner: # Check instance attribute
252
- init_console(clear_screen=True, show_banner=self.show_flock_banner)
253
-
254
- # Set Temporal debug environment variable
255
- self._set_temporal_debug_flag()
256
-
257
- # Ensure session ID exists in baggage
258
- self._ensure_session_id()
259
-
260
- FlockRegistry.discover_and_register_components()
261
-
262
- if self.enable_opik:
263
- import dspy
264
-
265
- opik.configure(use_local=True, automatic_approvals=True)
266
- opik_callback = OpikCallback(project_name=self.name, log_graph=True)
267
- dspy.settings.configure(
268
- callbacks=[opik_callback],
269
- )
270
-
271
- logger.info(
272
- "Flock instance initialized",
273
- name=self.name,
274
- model=self.model,
275
- enable_temporal=self.enable_temporal,
276
- )
239
+ # Delegate complex initialization to the initialization helper
240
+ self._initialization.setup(agents=agents, servers=servers)
277
241
 
278
242
  def prepare_benchmark(
279
243
  self,
@@ -315,7 +279,7 @@ class Flock(BaseModel, Serializable):
315
279
  agent_input = {self.benchmark_input_field: msg_content}
316
280
 
317
281
  result = await self.run_async(
318
- start_agent=self.benchmark_agent_name,
282
+ agent=self.benchmark_agent_name,
319
283
  input=agent_input,
320
284
  box_result=False,
321
285
  )
@@ -330,61 +294,11 @@ class Flock(BaseModel, Serializable):
330
294
 
331
295
  return run
332
296
 
333
- def _set_temporal_debug_flag(self):
334
- """Set or remove LOCAL_DEBUG env var based on enable_temporal."""
335
- if not self.enable_temporal:
336
- if "LOCAL_DEBUG" not in os.environ:
337
- os.environ["LOCAL_DEBUG"] = "1"
338
- logger.debug(
339
- "Set LOCAL_DEBUG environment variable for local execution."
340
- )
341
- elif "LOCAL_DEBUG" in os.environ:
342
- del os.environ["LOCAL_DEBUG"]
343
- logger.debug(
344
- "Removed LOCAL_DEBUG environment variable for Temporal execution."
345
- )
346
297
 
347
- def _ensure_session_id(self):
348
- """Ensure a session_id exists in the OpenTelemetry baggage."""
349
- session_id = get_baggage("session_id")
350
- if not session_id:
351
- session_id = str(uuid.uuid4())
352
- set_baggage("session_id", session_id)
353
- logger.debug(f"Generated new session_id: {session_id}")
354
-
355
- def add_server(self, server: FlockMCPServerBase) -> FlockMCPServerBase:
356
- """Adds a server instance to this Flock configuration and registry as well as set it up to be managed by self._mgr."""
357
- from flock.core.mcp.flock_mcp_server import (
358
- FlockMCPServerBase as ConcreteFlockMCPServer,
359
- )
360
298
 
361
- if not isinstance(server, ConcreteFlockMCPServer):
362
- raise TypeError("Provided object is not a FlockMCPServer instance.")
363
- if not server.config.name:
364
- raise ValueError("Server must have a name.")
365
-
366
- if server.config.name in self.servers:
367
- raise ValueError(
368
- f"Server with this name already exists. Name: '{server.config.name}'"
369
- )
370
-
371
- self._servers[server.config.name] = server
372
- FlockRegistry.register_server(server) # Register globally.
373
-
374
- # Make sure that the server is also added to
375
- # the server_list managed by FlockServerManager
376
- if not self._mgr:
377
- self._mgr = FlockServerManager()
378
-
379
- # Prepare server to be managed by the FlockServerManager
380
- logger.info(f"Adding server '{server.config.name}' to managed list.")
381
- self._mgr.add_server_sync(server=server)
382
- logger.info(f"Server '{server.config.name}' is now on managed list.")
383
-
384
- logger.info(
385
- f"Server '{server.config.name}' added to Flock '{self.name}'"
386
- )
387
- return server
299
+ def add_server(self, server: FlockMCPServer) -> FlockMCPServer:
300
+ """Adds a server instance to this Flock configuration and registry."""
301
+ return self._server_manager.add_server(server)
388
302
 
389
303
  def add_agent(self, agent: FlockAgent) -> FlockAgent:
390
304
  """Adds an agent instance to this Flock configuration and registry.
@@ -412,7 +326,7 @@ class Flock(BaseModel, Serializable):
412
326
  return agent # Return existing agent
413
327
 
414
328
  self._agents[agent.name] = agent
415
- FlockRegistry.register_agent(agent) # Register globally
329
+ registry.register_agent(agent) # Register globally
416
330
 
417
331
  # Set default model if agent doesn't have one
418
332
  if agent.model is None:
@@ -435,226 +349,56 @@ class Flock(BaseModel, Serializable):
435
349
  return self._agents
436
350
 
437
351
  @property
438
- def servers(self) -> dict[str, FlockMCPServerBase]:
352
+ def servers(self) -> dict[str, FlockMCPServer]:
439
353
  """Returns the dictionary of servers managed by this Flock instance."""
440
- return self._servers
354
+ return self._server_manager.servers
441
355
 
442
356
  def run(
443
357
  self,
444
- start_agent: FlockAgent | str | None = None,
358
+ agent: FlockAgent | str | None = None,
445
359
  input: dict | None = None,
446
360
  context: FlockContext | None = None,
447
361
  run_id: str = "",
448
362
  box_result: bool = True,
449
363
  agents: list[FlockAgent] | None = None,
450
- servers: list[FlockMCPServerBase] | None = None,
364
+ servers: list[FlockMCPServer] | None = None,
451
365
  memo: dict[str, Any] | None = None,
452
366
  ) -> Box | dict:
453
- return self._run_sync(
454
- self.run_async(
455
- start_agent=start_agent,
456
- input=input,
457
- context=context,
458
- run_id=run_id,
459
- box_result=box_result,
460
- agents=agents,
461
- servers=servers,
462
- memo=memo,
463
- )
367
+ """Synchronous execution wrapper."""
368
+ return self._execution.run(
369
+ agent=agent,
370
+ input=input,
371
+ context=context,
372
+ run_id=run_id,
373
+ box_result=box_result,
374
+ agents=agents,
375
+ servers=servers,
376
+ memo=memo,
464
377
  )
465
378
 
466
379
  async def run_async(
467
380
  self,
468
- start_agent: FlockAgent | str | None = None,
381
+ agent: FlockAgent | str | None = None,
469
382
  input: dict | None = None,
470
383
  context: FlockContext | None = None,
471
384
  run_id: str = "",
472
385
  box_result: bool = True,
473
386
  agents: list[FlockAgent] | None = None,
474
- servers: list[FlockMCPServerBase] | None = None,
387
+ servers: list[FlockMCPServer] | None = None,
475
388
  memo: dict[str, Any] | None = None,
476
389
  ) -> Box | dict:
477
390
  """Entry point for running an agent system asynchronously."""
478
- # Import here to allow forward reference resolution
479
- from flock.core.flock_agent import FlockAgent as ConcreteFlockAgent
480
- from flock.core.mcp.flock_mcp_server import (
481
- FlockMCPServerBase as ConcreteFlockServer,
391
+ return await self._execution.run_async(
392
+ agent=agent,
393
+ input=input,
394
+ context=context,
395
+ run_id=run_id,
396
+ box_result=box_result,
397
+ agents=agents,
398
+ servers=servers,
399
+ memo=memo,
482
400
  )
483
401
 
484
- with tracer.start_as_current_span("flock.run_async") as span:
485
- # Add passed servers so that agents have access to them.
486
- if servers:
487
- for server_obj in servers:
488
- if isinstance(server_obj, ConcreteFlockServer):
489
- self.add_server(server=server_obj)
490
- else:
491
- logger.warning(
492
- f"Item in 'servers' list is not a FlockMCPServer: {type(server_obj)}"
493
- )
494
-
495
- # Add passed agents
496
- if agents:
497
- for agent_obj in agents:
498
- if isinstance(agent_obj, ConcreteFlockAgent):
499
- self.add_agent(agent_obj)
500
- else:
501
- logger.warning(
502
- f"Item in 'agents' list is not a FlockAgent: {type(agent_obj)}"
503
- )
504
-
505
- # Determine starting agent name
506
- start_agent_name: str | None = None
507
- if isinstance(start_agent, ConcreteFlockAgent):
508
- start_agent_name = start_agent.name
509
- if (
510
- start_agent_name not in self._agents
511
- ): # Add if not already present
512
- self.add_agent(start_agent)
513
- elif isinstance(start_agent, str):
514
- start_agent_name = start_agent
515
- else: # start_agent is None
516
- start_agent_name = self._start_agent_name
517
-
518
- # Default to first agent if only one exists and none specified
519
- if not start_agent_name and len(self._agents) == 1:
520
- start_agent_name = next(iter(self._agents.keys()))
521
- elif not start_agent_name:
522
- raise ValueError(
523
- "No start_agent specified and multiple/no agents exist in the Flock instance."
524
- )
525
-
526
- # Check if start_agent is in agents
527
- if start_agent_name not in self._agents:
528
- # Try loading from registry if not found locally yet
529
- reg_agent = FlockRegistry.get_agent(start_agent_name)
530
- if reg_agent:
531
- self.add_agent(reg_agent)
532
- logger.info(
533
- f"Loaded start agent '{start_agent_name}' from registry."
534
- )
535
- else:
536
- raise ValueError(
537
- f"Start agent '{start_agent_name}' not found locally or in registry."
538
- )
539
-
540
- run_input = input if input is not None else self._start_input
541
- effective_run_id = run_id or f"flockrun_{uuid.uuid4().hex[:8]}"
542
-
543
- span.set_attribute("start_agent", start_agent_name)
544
- span.set_attribute("input", str(run_input))
545
- span.set_attribute("run_id", effective_run_id)
546
- span.set_attribute("enable_temporal", self.enable_temporal)
547
- logger.info(
548
- f"Initiating Flock run '{self.name}'. Start Agent: '{start_agent_name}'. Temporal: {self.enable_temporal}."
549
- )
550
-
551
- try:
552
- resolved_start_agent = self._agents.get(start_agent_name)
553
- if not resolved_start_agent: # Should have been handled by now
554
- raise ValueError(
555
- f"Start agent '{start_agent_name}' not found after checks."
556
- )
557
-
558
- run_context = context if context else FlockContext()
559
- set_baggage("run_id", effective_run_id) # Set for OpenTelemetry
560
-
561
- initialize_context(
562
- run_context,
563
- start_agent_name,
564
- run_input,
565
- effective_run_id,
566
- not self.enable_temporal, # local_debug is inverse of enable_temporal
567
- self.model or resolved_start_agent.model or DEFAULT_MODEL,
568
- )
569
- # Add agent definitions to context for routing/serialization within workflow
570
- for agent_name_iter, agent_instance_iter in self.agents.items():
571
- agent_dict_repr = (
572
- agent_instance_iter.to_dict()
573
- ) # Agents handle their own serialization
574
- run_context.add_agent_definition(
575
- agent_type=type(agent_instance_iter),
576
- agent_name=agent_name_iter,
577
- agent_data=agent_dict_repr,
578
- )
579
-
580
- # Add temporal config to context if enabled
581
- if self.enable_temporal and self.temporal_config:
582
- run_context.set_variable(
583
- "flock.temporal_workflow_config",
584
- self.temporal_config.model_dump(mode="json"),
585
- )
586
-
587
- # At this point, initial setup is done
588
- # and flock is ready to execute it's agent_workflow.
589
- # Befor that happens, the ServerManager needs to
590
- # get the Servers up and running (Populate pools, build connections, start scripts, etc.)
591
- async with self._mgr:
592
- # Enter the manager's async context,
593
- # running it's __aenter__ method and starting all registered servers
594
- # after this block ends, self._mgr's __aexit__ will be called
595
- # all servers will be torn down.
596
- logger.info(
597
- f"Entering managed server context. Servers starting up."
598
- )
599
-
600
- logger.info(
601
- "Starting agent execution",
602
- agent=start_agent_name,
603
- enable_temporal=self.enable_temporal,
604
- )
605
-
606
- # Execute workflow
607
- if not self.enable_temporal:
608
- result = await run_local_workflow(
609
- run_context,
610
- box_result=False, # Boxing handled below
611
- )
612
- else:
613
- result = await run_temporal_workflow(
614
- self, # Pass the Flock instance
615
- run_context,
616
- box_result=False, # Boxing handled below
617
- memo=memo,
618
- )
619
-
620
- span.set_attribute("result.type", str(type(result)))
621
- result_str = str(result)
622
- span.set_attribute(
623
- "result.preview",
624
- result_str[:1000]
625
- + ("..." if len(result_str) > 1000 else ""),
626
- )
627
-
628
- if box_result:
629
- try:
630
- logger.debug("Boxing final result.")
631
- return Box(result)
632
- except ImportError:
633
- logger.warning(
634
- "Box library not installed, returning raw dict."
635
- )
636
- return result
637
- else:
638
- return result
639
-
640
- # The context of self._mgr ends here, meaning, that servers will
641
- # be cleaned up and shut down.
642
-
643
- except Exception as e:
644
- logger.error(
645
- f"Flock run '{self.name}' failed: {e}", exc_info=True
646
- )
647
- span.record_exception(e)
648
- span.set_status(trace.Status(trace.StatusCode.ERROR, str(e)))
649
- # Return a consistent error structure
650
- error_output = {
651
- "error": str(e),
652
- "details": f"Flock run '{self.name}' failed.",
653
- "run_id": effective_run_id,
654
- "start_agent": start_agent_name,
655
- }
656
- return Box(error_output) if box_result else error_output
657
-
658
402
  # --- Batch Processing (Delegation) ---
659
403
  async def run_batch_async(
660
404
  self,
@@ -673,11 +417,7 @@ class Flock(BaseModel, Serializable):
673
417
  delimiter: str = ",",
674
418
  ) -> list[Box | dict | None | Exception]:
675
419
  """Runs the specified agent/workflow for each item in a batch asynchronously (delegated)."""
676
- # Import processor locally
677
- from flock.core.execution.batch_executor import BatchProcessor
678
-
679
- processor = BatchProcessor(self) # Pass self
680
- return await processor.run_batch_async(
420
+ return await self._batch_processor.run_batch_async(
681
421
  start_agent=start_agent,
682
422
  batch_inputs=batch_inputs,
683
423
  input_mapping=input_mapping,
@@ -709,22 +449,21 @@ class Flock(BaseModel, Serializable):
709
449
  hide_columns: list[str] | None = None,
710
450
  delimiter: str = ",",
711
451
  ) -> list[Box | dict | None | Exception]:
712
- return self._run_sync(
713
- self.run_batch_async(
714
- start_agent=start_agent,
715
- batch_inputs=batch_inputs,
716
- input_mapping=input_mapping,
717
- static_inputs=static_inputs,
718
- parallel=parallel,
719
- max_workers=max_workers,
720
- use_temporal=use_temporal,
721
- box_results=box_results,
722
- return_errors=return_errors,
723
- silent_mode=silent_mode,
724
- write_to_csv=write_to_csv,
725
- hide_columns=hide_columns,
726
- delimiter=delimiter,
727
- )
452
+ """Synchronous wrapper for batch processing."""
453
+ return self._batch_processor.run_batch(
454
+ start_agent=start_agent,
455
+ batch_inputs=batch_inputs,
456
+ input_mapping=input_mapping,
457
+ static_inputs=static_inputs,
458
+ parallel=parallel,
459
+ max_workers=max_workers,
460
+ use_temporal=use_temporal,
461
+ box_results=box_results,
462
+ return_errors=return_errors,
463
+ silent_mode=silent_mode,
464
+ write_to_csv=write_to_csv,
465
+ hide_columns=hide_columns,
466
+ delimiter=delimiter,
728
467
  )
729
468
 
730
469
  # --- Evaluation (Delegation) ---
@@ -738,7 +477,6 @@ class Flock(BaseModel, Serializable):
738
477
  str
739
478
  | Callable[[Any, Any], bool | float | dict[str, Any]]
740
479
  | FlockAgent # Type hint only
741
- | FlockEvaluator # Type hint only
742
480
  ],
743
481
  metric_configs: dict[str, dict[str, Any]] | None = None,
744
482
  static_inputs: dict[str, Any] | None = None,
@@ -752,13 +490,7 @@ class Flock(BaseModel, Serializable):
752
490
  metadata_columns: list[str] | None = None,
753
491
  ) -> DataFrame | list[dict[str, Any]]: # type: ignore
754
492
  """Evaluates the Flock's performance against a dataset (delegated)."""
755
- # Import processor locally
756
- from flock.core.execution.evaluation_executor import (
757
- EvaluationExecutor,
758
- )
759
-
760
- processor = EvaluationExecutor(self) # Pass self
761
- return await processor.evaluate_async(
493
+ return await self._evaluator.evaluate_async(
762
494
  dataset=dataset,
763
495
  start_agent=start_agent,
764
496
  input_mapping=input_mapping,
@@ -786,7 +518,6 @@ class Flock(BaseModel, Serializable):
786
518
  str
787
519
  | Callable[[Any, Any], bool | float | dict[str, Any]]
788
520
  | FlockAgent # Type hint only
789
- | FlockEvaluator # Type hint only
790
521
  ],
791
522
  metric_configs: dict[str, dict[str, Any]] | None = None,
792
523
  static_inputs: dict[str, Any] | None = None,
@@ -799,60 +530,26 @@ class Flock(BaseModel, Serializable):
799
530
  silent_mode: bool = False,
800
531
  metadata_columns: list[str] | None = None,
801
532
  ) -> DataFrame | list[dict[str, Any]]: # type: ignore
802
- return self._run_sync(
803
- self.evaluate_async(
804
- dataset=dataset,
805
- start_agent=start_agent,
806
- input_mapping=input_mapping,
807
- answer_mapping=answer_mapping,
808
- metrics=metrics,
809
- metric_configs=metric_configs,
810
- static_inputs=static_inputs,
811
- parallel=parallel,
812
- max_workers=max_workers,
813
- use_temporal=use_temporal,
814
- error_handling=error_handling,
815
- output_file=output_file,
816
- return_dataframe=return_dataframe,
817
- silent_mode=silent_mode,
818
- metadata_columns=metadata_columns,
819
- )
533
+ """Synchronous wrapper for evaluation."""
534
+ return self._evaluator.evaluate(
535
+ dataset=dataset,
536
+ start_agent=start_agent,
537
+ input_mapping=input_mapping,
538
+ answer_mapping=answer_mapping,
539
+ metrics=metrics,
540
+ metric_configs=metric_configs,
541
+ static_inputs=static_inputs,
542
+ parallel=parallel,
543
+ max_workers=max_workers,
544
+ use_temporal=use_temporal,
545
+ error_handling=error_handling,
546
+ output_file=output_file,
547
+ return_dataframe=return_dataframe,
548
+ silent_mode=silent_mode,
549
+ metadata_columns=metadata_columns,
820
550
  )
821
551
 
822
552
  # --- Server & CLI Starters (Delegation) ---
823
- def start_api(
824
- self,
825
- host: str = "127.0.0.1",
826
- port: int = 8344,
827
- server_name: str = "Flock Server",
828
- create_ui: bool = True, # Default to True for the integrated experience
829
- ui_theme: str | None = None,
830
- custom_endpoints: Sequence[FlockEndpoint]
831
- | dict[tuple[str, list[str] | None], Callable[..., Any]]
832
- | None = None,
833
- ) -> None:
834
- """Starts a unified REST API server and/or Web UI for this Flock instance."""
835
- import warnings
836
-
837
- warnings.warn(
838
- "start_api() is deprecated and will be removed in a future release. "
839
- "Use serve() instead.",
840
- DeprecationWarning,
841
- stacklevel=2,
842
- )
843
- # Delegate to the new serve() method (create_ui maps to ui)
844
- return self.serve(
845
- host=host,
846
- port=port,
847
- server_name=server_name,
848
- ui=create_ui,
849
- ui_theme=ui_theme,
850
- custom_endpoints=custom_endpoints,
851
- )
852
-
853
- # ------------------------------------------------------------------
854
- # New preferred method name
855
- # ------------------------------------------------------------------
856
553
 
857
554
  def serve(
858
555
  self,
@@ -870,44 +567,17 @@ class Flock(BaseModel, Serializable):
870
567
  | dict[tuple[str, list[str] | None], Callable[..., Any]]
871
568
  | None = None,
872
569
  ) -> None:
873
- """Launch an HTTP server that exposes the core REST API and, optionally, the
874
- browser-based UI.
875
-
876
- Args:
877
- host: Bind address for the server (default "127.0.0.1").
878
- port: TCP port to listen on (default 8344).
879
- server_name: Title shown in the OpenAPI docs / logs.
880
- ui: If True (default) the Pico/HTMX web UI routes are included. If False
881
- only the JSON API groups (core & custom) are served.
882
- chat: If True, enable chat routes.
883
- chat_agent: Name of the agent to use for chat.
884
- chat_message_key: Key for chat message in input.
885
- chat_history_key: Key for chat history in input.
886
- chat_response_key: Key for chat response in output.
887
- ui_theme: Optional UI theme name or "random".
888
- custom_endpoints: Additional API routes to add, either as a list of
889
- FlockEndpoint objects or the legacy dict format.
890
- """
891
- try:
892
- from flock.webapp.run import start_unified_server
893
- except ImportError:
894
- logger.error(
895
- "Web application components not found (flock.webapp.run). "
896
- "Cannot start HTTP server. Ensure webapp dependencies are installed."
897
- )
898
- return
899
-
900
- logger.info(
901
- f"Attempting to start server for Flock '{self.name}' on {host}:{port}. UI enabled: {ui}"
902
- )
903
-
904
- start_unified_server(
905
- flock_instance=self,
570
+ """Launch an HTTP server that exposes the core REST API and, optionally, the browser-based UI."""
571
+ return self._web_server.serve(
906
572
  host=host,
907
573
  port=port,
908
- server_title=server_name,
909
- enable_ui_routes=ui,
910
- enable_chat_routes=chat,
574
+ server_name=server_name,
575
+ ui=ui,
576
+ chat=chat,
577
+ chat_agent=chat_agent,
578
+ chat_message_key=chat_message_key,
579
+ chat_history_key=chat_history_key,
580
+ chat_response_key=chat_response_key,
911
581
  ui_theme=ui_theme,
912
582
  custom_endpoints=custom_endpoints,
913
583
  )
@@ -922,24 +592,8 @@ class Flock(BaseModel, Serializable):
922
592
  edit_mode: bool = False,
923
593
  ) -> None:
924
594
  """Starts an interactive CLI for this Flock instance."""
925
- # Import runner locally
926
- try:
927
- from flock.cli.runner import start_flock_cli
928
- except ImportError:
929
- logger.error(
930
- "CLI components not found. Cannot start CLI. "
931
- "Ensure CLI dependencies are installed."
932
- )
933
- return
934
-
935
- # The start_flock_cli function in file_50 doesn't take start_agent
936
- # but the original docs for start_cli did.
937
- # For now, I'll pass it through, assuming start_flock_cli will be updated or ignore it.
938
- # If start_agent is crucial here, start_flock_cli needs to handle it.
939
- logger.info(f"Starting CLI for Flock '{self.name}'...")
940
- start_flock_cli(
941
- flock=self, # Pass the Flock instance
942
- # start_agent=start_agent, # This argument is not in the definition of start_flock_cli in file_50
595
+ return self._web_server.start_cli(
596
+ start_agent=start_agent,
943
597
  server_name=server_name,
944
598
  show_results=show_results,
945
599
  edit_mode=edit_mode,