flock-core 0.5.0b27__py3-none-any.whl → 0.5.0b50__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 (357) hide show
  1. flock/__init__.py +12 -217
  2. flock/agent.py +678 -0
  3. flock/api/themes.py +71 -0
  4. flock/artifacts.py +79 -0
  5. flock/cli.py +75 -0
  6. flock/components.py +173 -0
  7. flock/dashboard/__init__.py +28 -0
  8. flock/dashboard/collector.py +283 -0
  9. flock/dashboard/events.py +182 -0
  10. flock/dashboard/launcher.py +230 -0
  11. flock/dashboard/service.py +537 -0
  12. flock/dashboard/websocket.py +235 -0
  13. flock/engines/__init__.py +6 -0
  14. flock/engines/dspy_engine.py +856 -0
  15. flock/examples.py +128 -0
  16. flock/{core/util → helper}/cli_helper.py +4 -3
  17. flock/{core/logging → logging}/__init__.py +2 -3
  18. flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
  19. flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
  20. flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
  21. flock/{core/logging → logging}/logging.py +77 -61
  22. flock/{core/logging → logging}/telemetry.py +20 -26
  23. flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
  24. flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
  25. flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
  26. flock/{core/logging → logging}/trace_and_logged.py +20 -24
  27. flock/mcp/__init__.py +91 -0
  28. flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
  29. flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
  30. flock/mcp/manager.py +255 -0
  31. flock/mcp/servers/sse/__init__.py +1 -1
  32. flock/mcp/servers/sse/flock_sse_server.py +11 -53
  33. flock/mcp/servers/stdio/__init__.py +1 -1
  34. flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
  35. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
  36. flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
  37. flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
  38. flock/mcp/types/__init__.py +42 -0
  39. flock/{core/mcp → mcp}/types/callbacks.py +9 -15
  40. flock/{core/mcp → mcp}/types/factories.py +7 -6
  41. flock/{core/mcp → mcp}/types/handlers.py +13 -18
  42. flock/{core/mcp → mcp}/types/types.py +70 -74
  43. flock/{core/mcp → mcp}/util/helpers.py +1 -1
  44. flock/orchestrator.py +645 -0
  45. flock/registry.py +148 -0
  46. flock/runtime.py +262 -0
  47. flock/service.py +140 -0
  48. flock/store.py +69 -0
  49. flock/subscription.py +111 -0
  50. flock/themes/andromeda.toml +1 -1
  51. flock/themes/apple-system-colors.toml +1 -1
  52. flock/themes/arcoiris.toml +1 -1
  53. flock/themes/atomonelight.toml +1 -1
  54. flock/themes/ayu copy.toml +1 -1
  55. flock/themes/ayu-light.toml +1 -1
  56. flock/themes/belafonte-day.toml +1 -1
  57. flock/themes/belafonte-night.toml +1 -1
  58. flock/themes/blulocodark.toml +1 -1
  59. flock/themes/breeze.toml +1 -1
  60. flock/themes/broadcast.toml +1 -1
  61. flock/themes/brogrammer.toml +1 -1
  62. flock/themes/builtin-dark.toml +1 -1
  63. flock/themes/builtin-pastel-dark.toml +1 -1
  64. flock/themes/catppuccin-latte.toml +1 -1
  65. flock/themes/catppuccin-macchiato.toml +1 -1
  66. flock/themes/catppuccin-mocha.toml +1 -1
  67. flock/themes/cga.toml +1 -1
  68. flock/themes/chalk.toml +1 -1
  69. flock/themes/ciapre.toml +1 -1
  70. flock/themes/coffee-theme.toml +1 -1
  71. flock/themes/cyberpunkscarletprotocol.toml +1 -1
  72. flock/themes/dark+.toml +1 -1
  73. flock/themes/darkermatrix.toml +1 -1
  74. flock/themes/darkside.toml +1 -1
  75. flock/themes/desert.toml +1 -1
  76. flock/themes/django.toml +1 -1
  77. flock/themes/djangosmooth.toml +1 -1
  78. flock/themes/doomone.toml +1 -1
  79. flock/themes/dotgov.toml +1 -1
  80. flock/themes/dracula+.toml +1 -1
  81. flock/themes/duckbones.toml +1 -1
  82. flock/themes/encom.toml +1 -1
  83. flock/themes/espresso.toml +1 -1
  84. flock/themes/everblush.toml +1 -1
  85. flock/themes/fairyfloss.toml +1 -1
  86. flock/themes/fideloper.toml +1 -1
  87. flock/themes/fishtank.toml +1 -1
  88. flock/themes/flexoki-light.toml +1 -1
  89. flock/themes/floraverse.toml +1 -1
  90. flock/themes/framer.toml +1 -1
  91. flock/themes/galizur.toml +1 -1
  92. flock/themes/github.toml +1 -1
  93. flock/themes/grass.toml +1 -1
  94. flock/themes/grey-green.toml +1 -1
  95. flock/themes/gruvboxlight.toml +1 -1
  96. flock/themes/guezwhoz.toml +1 -1
  97. flock/themes/harper.toml +1 -1
  98. flock/themes/hax0r-blue.toml +1 -1
  99. flock/themes/hopscotch.256.toml +1 -1
  100. flock/themes/ic-green-ppl.toml +1 -1
  101. flock/themes/iceberg-dark.toml +1 -1
  102. flock/themes/japanesque.toml +1 -1
  103. flock/themes/jubi.toml +1 -1
  104. flock/themes/kibble.toml +1 -1
  105. flock/themes/kolorit.toml +1 -1
  106. flock/themes/kurokula.toml +1 -1
  107. flock/themes/materialdesigncolors.toml +1 -1
  108. flock/themes/matrix.toml +1 -1
  109. flock/themes/mellifluous.toml +1 -1
  110. flock/themes/midnight-in-mojave.toml +1 -1
  111. flock/themes/monokai-remastered.toml +1 -1
  112. flock/themes/monokai-soda.toml +1 -1
  113. flock/themes/neon.toml +1 -1
  114. flock/themes/neopolitan.toml +1 -1
  115. flock/themes/nord-light.toml +1 -1
  116. flock/themes/ocean.toml +1 -1
  117. flock/themes/onehalfdark.toml +1 -1
  118. flock/themes/onehalflight.toml +1 -1
  119. flock/themes/palenighthc.toml +1 -1
  120. flock/themes/paulmillr.toml +1 -1
  121. flock/themes/pencildark.toml +1 -1
  122. flock/themes/pnevma.toml +1 -1
  123. flock/themes/purple-rain.toml +1 -1
  124. flock/themes/purplepeter.toml +1 -1
  125. flock/themes/raycast-dark.toml +1 -1
  126. flock/themes/red-sands.toml +1 -1
  127. flock/themes/relaxed.toml +1 -1
  128. flock/themes/retro.toml +1 -1
  129. flock/themes/rose-pine.toml +1 -1
  130. flock/themes/royal.toml +1 -1
  131. flock/themes/ryuuko.toml +1 -1
  132. flock/themes/sakura.toml +1 -1
  133. flock/themes/scarlet-protocol.toml +1 -1
  134. flock/themes/seoulbones-dark.toml +1 -1
  135. flock/themes/shades-of-purple.toml +1 -1
  136. flock/themes/smyck.toml +1 -1
  137. flock/themes/softserver.toml +1 -1
  138. flock/themes/solarized-darcula.toml +1 -1
  139. flock/themes/square.toml +1 -1
  140. flock/themes/sugarplum.toml +1 -1
  141. flock/themes/thayer-bright.toml +1 -1
  142. flock/themes/tokyonight.toml +1 -1
  143. flock/themes/tomorrow.toml +1 -1
  144. flock/themes/ubuntu.toml +1 -1
  145. flock/themes/ultradark.toml +1 -1
  146. flock/themes/ultraviolent.toml +1 -1
  147. flock/themes/unikitty.toml +1 -1
  148. flock/themes/urple.toml +1 -1
  149. flock/themes/vesper.toml +1 -1
  150. flock/themes/vimbones.toml +1 -1
  151. flock/themes/wildcherry.toml +1 -1
  152. flock/themes/wilmersdorf.toml +1 -1
  153. flock/themes/wryan.toml +1 -1
  154. flock/themes/xcodedarkhc.toml +1 -1
  155. flock/themes/xcodelight.toml +1 -1
  156. flock/themes/zenbones-light.toml +1 -1
  157. flock/themes/zenwritten-dark.toml +1 -1
  158. flock/utilities.py +301 -0
  159. flock/{components/utility → utility}/output_utility_component.py +68 -53
  160. flock/visibility.py +107 -0
  161. flock_core-0.5.0b50.dist-info/METADATA +747 -0
  162. flock_core-0.5.0b50.dist-info/RECORD +398 -0
  163. flock_core-0.5.0b50.dist-info/entry_points.txt +2 -0
  164. {flock_core-0.5.0b27.dist-info → flock_core-0.5.0b50.dist-info}/licenses/LICENSE +1 -1
  165. flock/adapter/__init__.py +0 -14
  166. flock/adapter/azure_adapter.py +0 -68
  167. flock/adapter/chroma_adapter.py +0 -73
  168. flock/adapter/faiss_adapter.py +0 -97
  169. flock/adapter/pinecone_adapter.py +0 -51
  170. flock/adapter/vector_base.py +0 -47
  171. flock/cli/assets/release_notes.md +0 -140
  172. flock/cli/config.py +0 -8
  173. flock/cli/constants.py +0 -36
  174. flock/cli/create_agent.py +0 -1
  175. flock/cli/create_flock.py +0 -280
  176. flock/cli/execute_flock.py +0 -620
  177. flock/cli/load_agent.py +0 -1
  178. flock/cli/load_examples.py +0 -1
  179. flock/cli/load_flock.py +0 -192
  180. flock/cli/load_release_notes.py +0 -20
  181. flock/cli/loaded_flock_cli.py +0 -254
  182. flock/cli/manage_agents.py +0 -459
  183. flock/cli/registry_management.py +0 -889
  184. flock/cli/runner.py +0 -41
  185. flock/cli/settings.py +0 -857
  186. flock/cli/utils.py +0 -135
  187. flock/cli/view_results.py +0 -29
  188. flock/cli/yaml_editor.py +0 -396
  189. flock/components/__init__.py +0 -30
  190. flock/components/evaluation/__init__.py +0 -9
  191. flock/components/evaluation/declarative_evaluation_component.py +0 -606
  192. flock/components/routing/__init__.py +0 -15
  193. flock/components/routing/conditional_routing_component.py +0 -494
  194. flock/components/routing/default_routing_component.py +0 -103
  195. flock/components/routing/llm_routing_component.py +0 -206
  196. flock/components/utility/__init__.py +0 -15
  197. flock/components/utility/memory_utility_component.py +0 -550
  198. flock/components/utility/metrics_utility_component.py +0 -700
  199. flock/config.py +0 -61
  200. flock/core/__init__.py +0 -110
  201. flock/core/agent/__init__.py +0 -16
  202. flock/core/agent/default_agent.py +0 -180
  203. flock/core/agent/flock_agent_components.py +0 -104
  204. flock/core/agent/flock_agent_execution.py +0 -101
  205. flock/core/agent/flock_agent_integration.py +0 -260
  206. flock/core/agent/flock_agent_lifecycle.py +0 -186
  207. flock/core/agent/flock_agent_serialization.py +0 -381
  208. flock/core/api/__init__.py +0 -10
  209. flock/core/api/custom_endpoint.py +0 -45
  210. flock/core/api/endpoints.py +0 -254
  211. flock/core/api/main.py +0 -162
  212. flock/core/api/models.py +0 -97
  213. flock/core/api/run_store.py +0 -224
  214. flock/core/api/runner.py +0 -44
  215. flock/core/api/service.py +0 -214
  216. flock/core/component/__init__.py +0 -15
  217. flock/core/component/agent_component_base.py +0 -309
  218. flock/core/component/evaluation_component.py +0 -62
  219. flock/core/component/routing_component.py +0 -74
  220. flock/core/component/utility_component.py +0 -69
  221. flock/core/config/flock_agent_config.py +0 -58
  222. flock/core/config/scheduled_agent_config.py +0 -40
  223. flock/core/context/context.py +0 -213
  224. flock/core/context/context_manager.py +0 -37
  225. flock/core/context/context_vars.py +0 -10
  226. flock/core/evaluation/utils.py +0 -396
  227. flock/core/execution/batch_executor.py +0 -369
  228. flock/core/execution/evaluation_executor.py +0 -438
  229. flock/core/execution/local_executor.py +0 -31
  230. flock/core/execution/opik_executor.py +0 -103
  231. flock/core/execution/temporal_executor.py +0 -164
  232. flock/core/flock.py +0 -634
  233. flock/core/flock_agent.py +0 -336
  234. flock/core/flock_factory.py +0 -551
  235. flock/core/flock_scheduler.py +0 -166
  236. flock/core/flock_server_manager.py +0 -136
  237. flock/core/interpreter/python_interpreter.py +0 -689
  238. flock/core/mcp/__init__.py +0 -1
  239. flock/core/mcp/flock_mcp_server.py +0 -680
  240. flock/core/mcp/mcp_client_manager.py +0 -201
  241. flock/core/mcp/types/__init__.py +0 -1
  242. flock/core/mixin/dspy_integration.py +0 -403
  243. flock/core/mixin/prompt_parser.py +0 -125
  244. flock/core/orchestration/__init__.py +0 -15
  245. flock/core/orchestration/flock_batch_processor.py +0 -94
  246. flock/core/orchestration/flock_evaluator.py +0 -113
  247. flock/core/orchestration/flock_execution.py +0 -295
  248. flock/core/orchestration/flock_initialization.py +0 -149
  249. flock/core/orchestration/flock_server_manager.py +0 -67
  250. flock/core/orchestration/flock_web_server.py +0 -117
  251. flock/core/registry/__init__.py +0 -45
  252. flock/core/registry/agent_registry.py +0 -69
  253. flock/core/registry/callable_registry.py +0 -139
  254. flock/core/registry/component_discovery.py +0 -142
  255. flock/core/registry/component_registry.py +0 -64
  256. flock/core/registry/config_mapping.py +0 -64
  257. flock/core/registry/decorators.py +0 -137
  258. flock/core/registry/registry_hub.py +0 -205
  259. flock/core/registry/server_registry.py +0 -57
  260. flock/core/registry/type_registry.py +0 -86
  261. flock/core/serialization/__init__.py +0 -13
  262. flock/core/serialization/callable_registry.py +0 -52
  263. flock/core/serialization/flock_serializer.py +0 -832
  264. flock/core/serialization/json_encoder.py +0 -41
  265. flock/core/serialization/secure_serializer.py +0 -175
  266. flock/core/serialization/serializable.py +0 -342
  267. flock/core/serialization/serialization_utils.py +0 -412
  268. flock/core/util/file_path_utils.py +0 -223
  269. flock/core/util/hydrator.py +0 -309
  270. flock/core/util/input_resolver.py +0 -164
  271. flock/core/util/loader.py +0 -59
  272. flock/core/util/splitter.py +0 -219
  273. flock/di.py +0 -27
  274. flock/platform/docker_tools.py +0 -49
  275. flock/platform/jaeger_install.py +0 -86
  276. flock/webapp/__init__.py +0 -1
  277. flock/webapp/app/__init__.py +0 -0
  278. flock/webapp/app/api/__init__.py +0 -0
  279. flock/webapp/app/api/agent_management.py +0 -241
  280. flock/webapp/app/api/execution.py +0 -709
  281. flock/webapp/app/api/flock_management.py +0 -129
  282. flock/webapp/app/api/registry_viewer.py +0 -30
  283. flock/webapp/app/chat.py +0 -665
  284. flock/webapp/app/config.py +0 -104
  285. flock/webapp/app/dependencies.py +0 -117
  286. flock/webapp/app/main.py +0 -1070
  287. flock/webapp/app/middleware.py +0 -113
  288. flock/webapp/app/models_ui.py +0 -7
  289. flock/webapp/app/services/__init__.py +0 -0
  290. flock/webapp/app/services/feedback_file_service.py +0 -363
  291. flock/webapp/app/services/flock_service.py +0 -337
  292. flock/webapp/app/services/sharing_models.py +0 -81
  293. flock/webapp/app/services/sharing_store.py +0 -598
  294. flock/webapp/app/templates/theme_mapper.html +0 -326
  295. flock/webapp/app/theme_mapper.py +0 -812
  296. flock/webapp/app/utils.py +0 -85
  297. flock/webapp/run.py +0 -215
  298. flock/webapp/static/css/chat.css +0 -301
  299. flock/webapp/static/css/components.css +0 -167
  300. flock/webapp/static/css/header.css +0 -39
  301. flock/webapp/static/css/layout.css +0 -46
  302. flock/webapp/static/css/sidebar.css +0 -127
  303. flock/webapp/static/css/two-pane.css +0 -48
  304. flock/webapp/templates/base.html +0 -200
  305. flock/webapp/templates/chat.html +0 -152
  306. flock/webapp/templates/chat_settings.html +0 -19
  307. flock/webapp/templates/flock_editor.html +0 -16
  308. flock/webapp/templates/index.html +0 -12
  309. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  310. flock/webapp/templates/partials/_agent_list.html +0 -18
  311. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  312. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  313. flock/webapp/templates/partials/_chat_container.html +0 -15
  314. flock/webapp/templates/partials/_chat_messages.html +0 -57
  315. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  316. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  317. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  318. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  319. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  320. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  321. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  322. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  323. flock/webapp/templates/partials/_execution_form.html +0 -118
  324. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  325. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  326. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  327. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  328. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  329. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  330. flock/webapp/templates/partials/_registry_table.html +0 -25
  331. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  332. flock/webapp/templates/partials/_results_display.html +0 -78
  333. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  334. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  335. flock/webapp/templates/partials/_settings_view.html +0 -36
  336. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  337. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  338. flock/webapp/templates/partials/_sidebar.html +0 -74
  339. flock/webapp/templates/partials/_streaming_results_container.html +0 -195
  340. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  341. flock/webapp/templates/partials/_theme_preview.html +0 -36
  342. flock/webapp/templates/registry_viewer.html +0 -84
  343. flock/webapp/templates/shared_run_page.html +0 -140
  344. flock/workflow/__init__.py +0 -0
  345. flock/workflow/activities.py +0 -196
  346. flock/workflow/agent_activities.py +0 -24
  347. flock/workflow/agent_execution_activity.py +0 -202
  348. flock/workflow/flock_workflow.py +0 -214
  349. flock/workflow/temporal_config.py +0 -96
  350. flock/workflow/temporal_setup.py +0 -68
  351. flock_core-0.5.0b27.dist-info/METADATA +0 -274
  352. flock_core-0.5.0b27.dist-info/RECORD +0 -559
  353. flock_core-0.5.0b27.dist-info/entry_points.txt +0 -2
  354. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  355. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  356. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  357. {flock_core-0.5.0b27.dist-info → flock_core-0.5.0b50.dist-info}/WHEEL +0 -0
@@ -1,369 +0,0 @@
1
- import asyncio
2
- import concurrent.futures # For real parallelism via threads
3
- from pathlib import Path
4
- from typing import TYPE_CHECKING, Any
5
-
6
- from box import Box
7
- from opentelemetry import trace
8
- from pandas import DataFrame
9
- from rich.progress import ( # Import Rich Progress
10
- BarColumn,
11
- Progress,
12
- SpinnerColumn,
13
- TextColumn,
14
- TimeElapsedColumn,
15
- )
16
-
17
- from flock.config import TELEMETRY
18
- from flock.core.context.context import FlockContext
19
- from flock.core.context.context_vars import FLOCK_BATCH_SILENT_MODE
20
- from flock.core.flock_agent import FlockAgent
21
- from flock.core.logging.logging import get_logger
22
-
23
- try:
24
- import pandas as pd
25
-
26
- PANDAS_AVAILABLE = True
27
- except ImportError:
28
- pd = None
29
- PANDAS_AVAILABLE = False
30
-
31
- if TYPE_CHECKING:
32
- from flock.core.flock import Flock
33
-
34
- logger = get_logger("flock")
35
- TELEMETRY.setup_tracing() # Setup OpenTelemetry
36
- tracer = trace.get_tracer(__name__)
37
-
38
-
39
- class BatchProcessor:
40
- def __init__(self, flock_instance: "Flock"):
41
- self.flock = flock_instance
42
-
43
- async def run_batch_async(
44
- self,
45
- start_agent: FlockAgent | str,
46
- batch_inputs: list[dict[str, Any]] | DataFrame | str,
47
- input_mapping: dict[str, str] | None = None,
48
- static_inputs: dict[str, Any] | None = None,
49
- parallel: bool = True,
50
- max_workers: int = 5,
51
- use_temporal: bool | None = None,
52
- box_results: bool = True,
53
- return_errors: bool = False,
54
- silent_mode: bool = False,
55
- write_to_csv: str | None = None,
56
- hide_columns: list[str] | None = None,
57
- delimiter: str = ",",
58
- ) -> list[Box | dict | None | Exception]:
59
- """Runs the specified agent/workflow for each item in a batch asynchronously.
60
-
61
- Args:
62
- start_agent: Agent instance or name to start each run.
63
- batch_inputs: Input data in one of these forms:
64
- - List of dictionaries, each representing inputs for one run
65
- - Pandas DataFrame where each row is inputs for one run
66
- - String path to a CSV file to load as DataFrame
67
- input_mapping: Maps DataFrame/CSV column names to agent input keys (required for DataFrame/CSV).
68
- static_inputs: Dictionary of inputs constant across all batch runs.
69
- parallel: Whether to run local jobs in parallel (ignored if use_temporal=True).
70
- max_workers: Max concurrent local workers (used if parallel=True and use_temporal=False).
71
- use_temporal: Override Flock's 'enable_temporal' setting for this batch.
72
- box_results: Wrap successful dictionary results in Box objects.
73
- return_errors: If True, return Exception objects for failed runs instead of raising.
74
- silent_mode: If True, suppress output and show progress bar instead.
75
- write_to_csv: Path to save results as CSV file.
76
- hide_columns: List of column names to hide from output.
77
-
78
- Returns:
79
- List containing results (Box/dict), None (if error and not return_errors),
80
- or Exception objects (if error and return_errors). Order matches input.
81
-
82
- Raises:
83
- ValueError: For invalid input combinations.
84
- ImportError: If DataFrame/CSV used without pandas.
85
- Exception: First exception from a run if return_errors is False.
86
- """
87
- effective_use_temporal = (
88
- use_temporal
89
- if use_temporal is not None
90
- else self.flock.enable_temporal
91
- )
92
- exec_mode = (
93
- "Temporal"
94
- if effective_use_temporal
95
- else ("Parallel Local" if parallel else "Sequential Local")
96
- )
97
- logger.info(
98
- f"Starting batch run for agent '{start_agent}'. Execution: {exec_mode}, Silent: {silent_mode}"
99
- )
100
-
101
- # --- Input Preparation ---
102
- prepared_batch_inputs: list[dict[str, Any]] = []
103
-
104
- if input_mapping == {}:
105
- input_mapping = None
106
- if static_inputs == {}:
107
- static_inputs = None
108
-
109
- if isinstance(batch_inputs, str):
110
- # Handle CSV file input
111
- try:
112
- df = pd.read_csv(batch_inputs)
113
- logger.debug(
114
- f"Loaded CSV file with {len(df)} rows: {batch_inputs}"
115
- )
116
- batch_inputs = df # Convert to DataFrame for unified handling
117
- except Exception as e:
118
- raise ValueError(
119
- f"Failed to load CSV file '{batch_inputs}': {e}"
120
- )
121
-
122
- if isinstance(batch_inputs, DataFrame):
123
- # Handle DataFrame input
124
- logger.debug(
125
- f"Converting DataFrame ({len(batch_inputs)} rows) to batch inputs."
126
- )
127
- for _, row in batch_inputs.iterrows():
128
- if input_mapping:
129
- item_input = {
130
- agent_key: row[df_col]
131
- for df_col, agent_key in input_mapping.items()
132
- if df_col in row
133
- }
134
- else:
135
- item_input = row.to_dict()
136
- prepared_batch_inputs.append(item_input)
137
- else:
138
- # Handle list of dictionaries
139
- if not isinstance(batch_inputs, list):
140
- raise ValueError(
141
- "batch_inputs must be a list of dictionaries, DataFrame, or CSV file path"
142
- )
143
-
144
- if input_mapping:
145
- # Apply mapping to dictionary inputs
146
- logger.debug("Applying input mapping to dictionary inputs")
147
- for item in batch_inputs:
148
- mapped_input = {}
149
- for df_col, agent_key in input_mapping.items():
150
- if df_col in item:
151
- mapped_input[agent_key] = item[df_col]
152
- else:
153
- logger.warning(
154
- f"Input mapping key '{df_col}' not found in input dictionary"
155
- )
156
- prepared_batch_inputs.append(mapped_input)
157
- else:
158
- # Use dictionaries as-is if no mapping provided
159
- prepared_batch_inputs = batch_inputs
160
-
161
- logger.debug(
162
- f"Using provided list of {len(prepared_batch_inputs)} batch inputs."
163
- )
164
-
165
- if not prepared_batch_inputs:
166
- return []
167
-
168
- # --- Setup Progress Bar if Silent ---
169
- progress_context = None
170
- progress_task_id = None
171
- if silent_mode:
172
- progress = Progress(
173
- SpinnerColumn(),
174
- TextColumn("[progress.description]{task.description}"),
175
- BarColumn(),
176
- TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
177
- TextColumn("({task.completed}/{task.total})"),
178
- TimeElapsedColumn(),
179
- # transient=True # Optionally remove progress bar when done
180
- )
181
- progress_context = progress # Use as context manager
182
- progress_task_id = progress.add_task(
183
- f"Processing Batch ({exec_mode})",
184
- total=len(prepared_batch_inputs),
185
- )
186
- progress.start()
187
-
188
- results = [None] * len(prepared_batch_inputs) # Pre-allocate results list
189
-
190
- # --- Worker Definitions ---
191
- # We implement two flavours:
192
- # * async_worker: used for Temporal or sequential runs (keeps the original behaviour)
193
- # * thread_worker: executes the run in a dedicated thread via ThreadPoolExecutor for true parallelism.
194
-
195
- async def async_worker(index: int, item_inputs: dict[str, Any]):
196
- """Original coroutine worker used for non-threaded execution paths."""
197
- full_input = {**(static_inputs or {}), **item_inputs}
198
- context = FlockContext()
199
- context.set_variable(FLOCK_BATCH_SILENT_MODE, silent_mode)
200
-
201
- run_desc = f"Batch item {index + 1}"
202
- logger.debug(f"{run_desc} started (async).")
203
- try:
204
- result = await self.flock.run_async(
205
- start_agent,
206
- full_input,
207
- box_result=box_results,
208
- context=context,
209
- )
210
- results[index] = result
211
- logger.debug(f"{run_desc} finished successfully.")
212
- except Exception as e:
213
- logger.error(f"{run_desc} failed: {e}", exc_info=not return_errors)
214
- if return_errors:
215
- results[index] = e
216
- else:
217
- raise # Propagate to calling gather
218
- finally:
219
- if progress_context:
220
- progress.update(progress_task_id, advance=1)
221
-
222
- # ThreadPool worker for real parallelism (suitable for blocking I/O)
223
- def _thread_worker(index: int, item_inputs: dict[str, Any]):
224
- """Synchronous helper executed inside a worker thread."""
225
- full_input = {**(static_inputs or {}), **item_inputs}
226
- run_desc = f"Batch item {index + 1}"
227
- logger.debug(f"{run_desc} started (thread).")
228
- try:
229
- # Use the synchronous wrapper to avoid nested event-loop issues inside threads
230
- result = self.flock.run(
231
- agent=start_agent,
232
- input=full_input,
233
- box_result=box_results,
234
- )
235
- logger.debug(f"{run_desc} finished successfully.")
236
- return index, result, None
237
- except Exception as e:
238
- logger.error(f"{run_desc} failed: {e}")
239
- return index, None, e
240
-
241
- async def thread_worker(executor, index: int, item_inputs: dict[str, Any]):
242
- """Coroutine wrapper that submits _thread_worker to the specified executor."""
243
- loop = asyncio.get_running_loop()
244
- idx, res, err = await loop.run_in_executor(
245
- executor, _thread_worker, index, item_inputs
246
- )
247
- # Handle result / error on the asyncio side
248
- if err:
249
- if return_errors:
250
- results[idx] = err
251
- else:
252
- raise err
253
- else:
254
- results[idx] = res
255
- if progress_context:
256
- progress.update(progress_task_id, advance=1)
257
-
258
- tasks = []
259
- try:
260
- if effective_use_temporal:
261
- # Temporal Batching (Simplified: sequential execution for this example)
262
- # A real implementation might use start_workflow or signals
263
- logger.info(
264
- "Running batch using Temporal (executing sequentially for now)..."
265
- )
266
- for i, item_data in enumerate(prepared_batch_inputs):
267
- await async_worker(i, item_data) # Run sequentially for demo
268
- # TODO: Implement true parallel Temporal workflow execution if needed
269
-
270
- elif parallel:
271
- # --- Real parallelism using ThreadPoolExecutor ---
272
- logger.info(
273
- f"Running batch in parallel (threads) with max_workers={max_workers}..."
274
- )
275
- loop = asyncio.get_running_loop()
276
- with concurrent.futures.ThreadPoolExecutor(
277
- max_workers=max_workers, thread_name_prefix="flock-batch"
278
- ) as executor:
279
- for i, item_data in enumerate(prepared_batch_inputs):
280
- tasks.append(
281
- asyncio.create_task(
282
- thread_worker(executor, i, item_data)
283
- )
284
- )
285
-
286
- # Wait for all tasks allowing exceptions to propagate as needed
287
- await asyncio.gather(*tasks)
288
-
289
- else: # Sequential Local
290
- logger.info("Running batch sequentially...")
291
- for i, item_data in enumerate(prepared_batch_inputs):
292
- await async_worker(i, item_data) # Already handles errors internally based on return_errors
293
-
294
- logger.info("Batch execution finished.")
295
-
296
- except Exception as batch_error:
297
- # This catch handles errors re-raised from workers when return_errors=False
298
- logger.error(f"Batch execution stopped due to error: {batch_error}")
299
- # No need to cancel tasks here as gather would have stopped
300
- if not return_errors:
301
- raise # Re-raise the first error encountered if not returning errors
302
- finally:
303
- if progress_context:
304
- progress.stop()
305
-
306
- if write_to_csv:
307
- try:
308
- df = pd.DataFrame(results)
309
- if hide_columns:
310
- df = df.drop(columns=hide_columns)
311
- # create write_to_csv directory if it doesn't exist
312
- Path(write_to_csv).parent.mkdir(parents=True, exist_ok=True)
313
- df.to_csv(write_to_csv, index=False, sep=delimiter)
314
- logger.info(f"Results written to CSV file: {write_to_csv}")
315
- except Exception as e:
316
- logger.error(f"Failed to write results to CSV: {e}")
317
-
318
- return results
319
-
320
- def run_batch( # Synchronous wrapper
321
- self,
322
- start_agent: FlockAgent | str,
323
- batch_inputs: list[dict[str, Any]] | DataFrame | str,
324
- input_mapping: dict[str, str] | None = None,
325
- static_inputs: dict[str, Any] | None = None,
326
- parallel: bool = True,
327
- max_workers: int = 5,
328
- use_temporal: bool | None = None,
329
- box_results: bool = True,
330
- return_errors: bool = False,
331
- silent_mode: bool = False,
332
- write_to_csv: str | None = None,
333
- hide_columns: list[str] | None = None,
334
- delimiter: str = ",",
335
- ) -> list[Box | dict | None | Exception]:
336
- """Synchronous wrapper for run_batch_async."""
337
- # (Standard asyncio run wrapper - same as in previous suggestion)
338
- try:
339
- loop = asyncio.get_running_loop()
340
- if loop.is_closed():
341
- raise RuntimeError("Event loop is closed")
342
- except RuntimeError:
343
- loop = asyncio.new_event_loop()
344
- asyncio.set_event_loop(loop)
345
-
346
- coro = self.run_batch_async(
347
- start_agent=start_agent,
348
- batch_inputs=batch_inputs,
349
- input_mapping=input_mapping,
350
- static_inputs=static_inputs,
351
- parallel=parallel,
352
- max_workers=max_workers,
353
- use_temporal=use_temporal,
354
- box_results=box_results,
355
- return_errors=return_errors,
356
- silent_mode=silent_mode,
357
- write_to_csv=write_to_csv,
358
- hide_columns=hide_columns,
359
- delimiter=delimiter,
360
- )
361
-
362
- if asyncio.get_event_loop() is loop and not loop.is_running():
363
- results = loop.run_until_complete(coro)
364
- # loop.close() # Avoid closing potentially shared loop
365
- return results
366
- else:
367
- # Run within an existing loop
368
- future = asyncio.ensure_future(coro)
369
- return loop.run_until_complete(future)