flock-core 0.5.0b28__py3-none-any.whl → 0.5.56b0__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 (359) 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.56b0.dist-info/METADATA +747 -0
  162. flock_core-0.5.56b0.dist-info/RECORD +398 -0
  163. flock_core-0.5.56b0.dist-info/entry_points.txt +2 -0
  164. {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.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 -22
  197. flock/components/utility/example_utility_component.py +0 -250
  198. flock/components/utility/feedback_utility_component.py +0 -206
  199. flock/components/utility/memory_utility_component.py +0 -550
  200. flock/components/utility/metrics_utility_component.py +0 -700
  201. flock/config.py +0 -61
  202. flock/core/__init__.py +0 -110
  203. flock/core/agent/__init__.py +0 -16
  204. flock/core/agent/default_agent.py +0 -216
  205. flock/core/agent/flock_agent_components.py +0 -104
  206. flock/core/agent/flock_agent_execution.py +0 -101
  207. flock/core/agent/flock_agent_integration.py +0 -260
  208. flock/core/agent/flock_agent_lifecycle.py +0 -186
  209. flock/core/agent/flock_agent_serialization.py +0 -381
  210. flock/core/api/__init__.py +0 -10
  211. flock/core/api/custom_endpoint.py +0 -45
  212. flock/core/api/endpoints.py +0 -254
  213. flock/core/api/main.py +0 -162
  214. flock/core/api/models.py +0 -97
  215. flock/core/api/run_store.py +0 -224
  216. flock/core/api/runner.py +0 -44
  217. flock/core/api/service.py +0 -214
  218. flock/core/component/__init__.py +0 -15
  219. flock/core/component/agent_component_base.py +0 -309
  220. flock/core/component/evaluation_component.py +0 -62
  221. flock/core/component/routing_component.py +0 -74
  222. flock/core/component/utility_component.py +0 -69
  223. flock/core/config/flock_agent_config.py +0 -58
  224. flock/core/config/scheduled_agent_config.py +0 -40
  225. flock/core/context/context.py +0 -213
  226. flock/core/context/context_manager.py +0 -37
  227. flock/core/context/context_vars.py +0 -10
  228. flock/core/evaluation/utils.py +0 -396
  229. flock/core/execution/batch_executor.py +0 -369
  230. flock/core/execution/evaluation_executor.py +0 -438
  231. flock/core/execution/local_executor.py +0 -31
  232. flock/core/execution/opik_executor.py +0 -103
  233. flock/core/execution/temporal_executor.py +0 -164
  234. flock/core/flock.py +0 -634
  235. flock/core/flock_agent.py +0 -336
  236. flock/core/flock_factory.py +0 -613
  237. flock/core/flock_scheduler.py +0 -166
  238. flock/core/flock_server_manager.py +0 -136
  239. flock/core/interpreter/python_interpreter.py +0 -689
  240. flock/core/mcp/__init__.py +0 -1
  241. flock/core/mcp/flock_mcp_server.py +0 -680
  242. flock/core/mcp/mcp_client_manager.py +0 -201
  243. flock/core/mcp/types/__init__.py +0 -1
  244. flock/core/mixin/dspy_integration.py +0 -403
  245. flock/core/mixin/prompt_parser.py +0 -125
  246. flock/core/orchestration/__init__.py +0 -15
  247. flock/core/orchestration/flock_batch_processor.py +0 -94
  248. flock/core/orchestration/flock_evaluator.py +0 -113
  249. flock/core/orchestration/flock_execution.py +0 -295
  250. flock/core/orchestration/flock_initialization.py +0 -149
  251. flock/core/orchestration/flock_server_manager.py +0 -67
  252. flock/core/orchestration/flock_web_server.py +0 -117
  253. flock/core/registry/__init__.py +0 -45
  254. flock/core/registry/agent_registry.py +0 -69
  255. flock/core/registry/callable_registry.py +0 -139
  256. flock/core/registry/component_discovery.py +0 -142
  257. flock/core/registry/component_registry.py +0 -64
  258. flock/core/registry/config_mapping.py +0 -64
  259. flock/core/registry/decorators.py +0 -137
  260. flock/core/registry/registry_hub.py +0 -205
  261. flock/core/registry/server_registry.py +0 -57
  262. flock/core/registry/type_registry.py +0 -86
  263. flock/core/serialization/__init__.py +0 -13
  264. flock/core/serialization/callable_registry.py +0 -52
  265. flock/core/serialization/flock_serializer.py +0 -832
  266. flock/core/serialization/json_encoder.py +0 -41
  267. flock/core/serialization/secure_serializer.py +0 -175
  268. flock/core/serialization/serializable.py +0 -342
  269. flock/core/serialization/serialization_utils.py +0 -412
  270. flock/core/util/file_path_utils.py +0 -223
  271. flock/core/util/hydrator.py +0 -309
  272. flock/core/util/input_resolver.py +0 -164
  273. flock/core/util/loader.py +0 -59
  274. flock/core/util/splitter.py +0 -219
  275. flock/di.py +0 -27
  276. flock/platform/docker_tools.py +0 -49
  277. flock/platform/jaeger_install.py +0 -86
  278. flock/webapp/__init__.py +0 -1
  279. flock/webapp/app/__init__.py +0 -0
  280. flock/webapp/app/api/__init__.py +0 -0
  281. flock/webapp/app/api/agent_management.py +0 -241
  282. flock/webapp/app/api/execution.py +0 -709
  283. flock/webapp/app/api/flock_management.py +0 -129
  284. flock/webapp/app/api/registry_viewer.py +0 -30
  285. flock/webapp/app/chat.py +0 -665
  286. flock/webapp/app/config.py +0 -104
  287. flock/webapp/app/dependencies.py +0 -117
  288. flock/webapp/app/main.py +0 -1070
  289. flock/webapp/app/middleware.py +0 -113
  290. flock/webapp/app/models_ui.py +0 -7
  291. flock/webapp/app/services/__init__.py +0 -0
  292. flock/webapp/app/services/feedback_file_service.py +0 -363
  293. flock/webapp/app/services/flock_service.py +0 -337
  294. flock/webapp/app/services/sharing_models.py +0 -81
  295. flock/webapp/app/services/sharing_store.py +0 -762
  296. flock/webapp/app/templates/theme_mapper.html +0 -326
  297. flock/webapp/app/theme_mapper.py +0 -812
  298. flock/webapp/app/utils.py +0 -85
  299. flock/webapp/run.py +0 -215
  300. flock/webapp/static/css/chat.css +0 -301
  301. flock/webapp/static/css/components.css +0 -167
  302. flock/webapp/static/css/header.css +0 -39
  303. flock/webapp/static/css/layout.css +0 -46
  304. flock/webapp/static/css/sidebar.css +0 -127
  305. flock/webapp/static/css/two-pane.css +0 -48
  306. flock/webapp/templates/base.html +0 -200
  307. flock/webapp/templates/chat.html +0 -152
  308. flock/webapp/templates/chat_settings.html +0 -19
  309. flock/webapp/templates/flock_editor.html +0 -16
  310. flock/webapp/templates/index.html +0 -12
  311. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  312. flock/webapp/templates/partials/_agent_list.html +0 -18
  313. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  314. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  315. flock/webapp/templates/partials/_chat_container.html +0 -15
  316. flock/webapp/templates/partials/_chat_messages.html +0 -57
  317. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  318. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  319. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  320. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  321. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  322. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  323. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  324. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  325. flock/webapp/templates/partials/_execution_form.html +0 -118
  326. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  327. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  328. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  329. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  330. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  331. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  332. flock/webapp/templates/partials/_registry_table.html +0 -25
  333. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  334. flock/webapp/templates/partials/_results_display.html +0 -78
  335. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  336. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  337. flock/webapp/templates/partials/_settings_view.html +0 -36
  338. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  339. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  340. flock/webapp/templates/partials/_sidebar.html +0 -74
  341. flock/webapp/templates/partials/_streaming_results_container.html +0 -195
  342. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  343. flock/webapp/templates/partials/_theme_preview.html +0 -36
  344. flock/webapp/templates/registry_viewer.html +0 -84
  345. flock/webapp/templates/shared_run_page.html +0 -140
  346. flock/workflow/__init__.py +0 -0
  347. flock/workflow/activities.py +0 -196
  348. flock/workflow/agent_activities.py +0 -24
  349. flock/workflow/agent_execution_activity.py +0 -202
  350. flock/workflow/flock_workflow.py +0 -214
  351. flock/workflow/temporal_config.py +0 -96
  352. flock/workflow/temporal_setup.py +0 -68
  353. flock_core-0.5.0b28.dist-info/METADATA +0 -274
  354. flock_core-0.5.0b28.dist-info/RECORD +0 -561
  355. flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
  356. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  357. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  358. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  359. {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/WHEEL +0 -0
@@ -1,201 +0,0 @@
1
- """Manages a pool of connections for a particular server."""
2
-
3
- import copy
4
- from abc import ABC, abstractmethod
5
- from asyncio import Lock
6
- from typing import Any, Generic, TypeVar
7
-
8
- from opentelemetry import trace
9
- from pydantic import (
10
- BaseModel,
11
- ConfigDict,
12
- Field,
13
- )
14
-
15
- from flock.core.logging.logging import get_logger
16
- from flock.core.mcp.flock_mcp_tool import FlockMCPTool
17
- from flock.core.mcp.mcp_client import (
18
- FlockMCPClient,
19
- )
20
- from flock.core.mcp.mcp_config import FlockMCPConfiguration
21
-
22
- logger = get_logger("mcp.client_manager")
23
- tracer = trace.get_tracer(__name__)
24
-
25
- TClient = TypeVar("TClient", bound="FlockMCPClient")
26
-
27
-
28
- class FlockMCPClientManager(BaseModel, ABC, Generic[TClient]):
29
- """Handles a Pool of MCPClients of type TClient."""
30
-
31
- client_config: FlockMCPConfiguration = Field(
32
- ..., description="Configuration for clients."
33
- )
34
-
35
- lock: Lock = Field(
36
- default_factory=Lock,
37
- description="Lock for mutex access.",
38
- exclude=True,
39
- )
40
-
41
- clients: dict[str, dict[str, FlockMCPClient]] = Field(
42
- default_factory=dict,
43
- exclude=True,
44
- description="Internal Store for the clients.",
45
- )
46
-
47
- # --- Pydantic v2 Configuratioin ---
48
- model_config = ConfigDict(
49
- arbitrary_types_allowed=True,
50
- )
51
-
52
- @abstractmethod
53
- async def make_client(
54
- self,
55
- additional_params: dict[str, Any] | None = None,
56
- ) -> type[TClient]:
57
- """Instantiate-but don't connect yet-a fresh client of the concrete subtype."""
58
- # default implementation
59
- pass
60
-
61
- async def get_client(
62
- self,
63
- agent_id: str,
64
- run_id: str,
65
- additional_params: dict[str, Any] | None = None,
66
- ) -> type[TClient]:
67
- """Provides a client from the pool."""
68
- # Attempt to get a client from the client store.
69
- # clients are stored like this: agent_id -> run_id -> client
70
- with tracer.start_as_current_span("client_manager.get_client") as span:
71
- span.set_attribute("agent_id", agent_id)
72
- span.set_attribute("run_id", run_id)
73
- async with self.lock:
74
- try:
75
- logger.debug(
76
- f"Attempting to get client for server '{self.client_config.name}'"
77
- )
78
- refresh = False
79
- if additional_params:
80
- refresh = bool(
81
- additional_params.get("refresh_client", False)
82
- )
83
- client = None
84
- run_clients = self.clients.get(agent_id, None)
85
- if run_clients is None or refresh:
86
- # This means, that across all runs, no agent has ever needed a client.
87
- # This also means that we need to create a client.
88
- client = await self.make_client(
89
- additional_params=copy.deepcopy(additional_params)
90
- )
91
- # Insert the freshly created client
92
- self.clients[agent_id] = {}
93
- self.clients[agent_id][run_id] = client
94
-
95
- else:
96
- # This means there is at least one entry for the agent_id available
97
- # Now, all we need to do is check if the run_id matches the entrie's run_id
98
- client = run_clients.get(run_id, None)
99
- if client is None or refresh:
100
- # Means no client here with the respective run_id
101
- client = await self.make_client(
102
- additional_params=copy.deepcopy(
103
- additional_params
104
- )
105
- )
106
- # Insert the freshly created client.
107
- self.clients[agent_id][run_id] = client
108
-
109
- return client
110
- except Exception as e:
111
- # Log the exception and raise it so it becomes visible downstream
112
- logger.error(
113
- f"Unexpected Exception ocurred while trying to get client for server '{self.client_config.name}' with agent_id: {agent_id} and run_id: {run_id}: {e}"
114
- )
115
- span.record_exception(e)
116
- raise e
117
-
118
- async def call_tool(
119
- self,
120
- agent_id: str,
121
- run_id: str,
122
- name: str,
123
- arguments: dict[str, Any],
124
- additional_params: dict[str, Any] | None = None,
125
- ) -> Any:
126
- """Call a tool."""
127
- with tracer.start_as_current_span("client_manager.call_tool") as span:
128
- span.set_attribute("agent_id", agent_id)
129
- span.set_attribute("run_id", run_id)
130
- span.set_attribute("tool_name", name)
131
- span.set_attribute("arguments", str(arguments))
132
- try:
133
- client = await self.get_client(
134
- agent_id=agent_id,
135
- run_id=run_id,
136
- additional_params=additional_params,
137
- )
138
- result = await client.call_tool(
139
- agent_id=agent_id,
140
- run_id=run_id,
141
- name=name,
142
- arguments=arguments,
143
- )
144
- return result
145
- except Exception as e:
146
- logger.error(
147
- f"Exception occurred while trying to call tool {name} on server '{self.client_config.name}': {e}"
148
- )
149
- span.record_exception(e)
150
- return None
151
-
152
- async def get_tools(
153
- self,
154
- agent_id: str,
155
- run_id: str,
156
- additional_params: dict[str, Any] | None = None,
157
- ) -> list[FlockMCPTool]:
158
- """Retrieves a list of tools for the agents to act on."""
159
- with tracer.start_as_current_span("client_manager.get_tools") as span:
160
- span.set_attribute("agent_id", agent_id)
161
- span.set_attribute("run_id", run_id)
162
- try:
163
- client = await self.get_client(
164
- agent_id=agent_id,
165
- run_id=run_id,
166
- additional_params=additional_params,
167
- )
168
- tools: list[FlockMCPTool] = await client.get_tools(
169
- agent_id=agent_id, run_id=run_id
170
- )
171
- return tools
172
- except Exception as e:
173
- logger.error(
174
- f"Exception occurred while trying to retrieve Tools for server '{self.client_config.name}' with agent_id: {agent_id} and run_id: {run_id}: {e}"
175
- )
176
- span.record_exception(e)
177
- return []
178
-
179
- async def close_all(self) -> None:
180
- """Closes all connections in the pool and cancels background tasks."""
181
- with tracer.start_as_current_span("client_manager.close_all") as span:
182
- async with self.lock:
183
- for agent_id, run_dict in self.clients.items():
184
- logger.debug(
185
- f"Shutting down all clients for agent_id: {agent_id}"
186
- )
187
- for run_id, client in run_dict.items():
188
- logger.debug(
189
- f"Shutting down client for agent_id {agent_id} and run_id {run_id}"
190
- )
191
- try:
192
- await client.disconnect()
193
- except Exception as e:
194
- logger.error(
195
- f"Error when trying to disconnect client for server '{self.client_config.name}': {e}"
196
- )
197
- span.record_exception(e)
198
- self.clients = {} # Let the GC take care of the rest.
199
- logger.info(
200
- f"All clients disconnected for server '{self.client_config.name}'"
201
- )
@@ -1 +0,0 @@
1
- """MCP Types package."""
@@ -1,403 +0,0 @@
1
- # src/flock/core/mixin/dspy_integration.py
2
- """Mixin class for integrating with the dspy library.
3
-
4
- This mixin centralizes Flock ↔ DSPy interop. It intentionally
5
- delegates more to DSPy’s native builders (Signature, settings.context,
6
- modules) to reduce custom glue and stay aligned with DSPy updates.
7
- """
8
-
9
- import ast
10
- import re # Import re for parsing
11
- import typing
12
- from typing import Any, Literal
13
-
14
- from dspy import Tool
15
-
16
- from flock.core.logging.logging import get_logger
17
- from flock.core.util.splitter import split_top_level
18
-
19
- # Import split_top_level (assuming it's moved or copied appropriately)
20
- # Option 1: If moved to a shared util
21
- # from flock.core.util.parsing_utils import split_top_level
22
- # Option 2: If kept within this file (as in previous example)
23
- # Define split_top_level here or ensure it's imported
24
-
25
- logger = get_logger("mixin.dspy")
26
-
27
- # Type definition for agent type override
28
- AgentType = Literal["ReAct", "Completion", "ChainOfThought"] | None
29
-
30
-
31
- # Helper function to resolve type strings (can be static or module-level)
32
- def _resolve_type_string(type_str: str) -> type:
33
- """Resolves a type string into a Python type object.
34
- Handles built-ins, registered types, and common typing generics like
35
- List, Dict, Optional, Union, Literal.
36
- """
37
- # Import registry here to avoid circular imports
38
- from flock.core.registry import get_registry
39
-
40
- registry = get_registry()
41
-
42
- type_str = type_str.strip()
43
- logger.debug(f"Attempting to resolve type string: '{type_str}'")
44
-
45
- # 1. Check built-ins and registered types directly
46
- try:
47
- # This covers str, int, bool, Any, and types registered by name
48
- resolved_type = registry.get_type(type_str)
49
- logger.debug(f"Resolved '{type_str}' via registry to: {resolved_type}")
50
- return resolved_type
51
- except KeyError:
52
- logger.debug(
53
- f"'{type_str}' not found directly in registry, attempting generic parsing."
54
- )
55
- pass # Not found, continue parsing generics
56
-
57
- # 2. Handle typing generics (List, Dict, Optional, Union, Literal)
58
- # Use regex to match pattern like Generic[InnerType1, InnerType2, ...]
59
- generic_match = re.fullmatch(r"(\w+)\s*\[(.*)\]", type_str)
60
- if generic_match:
61
- base_name = generic_match.group(1).strip()
62
- args_str = generic_match.group(2).strip()
63
- logger.debug(
64
- f"Detected generic pattern: Base='{base_name}', Args='{args_str}'"
65
- )
66
-
67
- try:
68
- # Get the base generic type (e.g., list, dict, Optional) from registry/builtins
69
- BaseType = registry.get_type(
70
- base_name
71
- ) # Expects List, Dict etc. to be registered
72
- logger.debug(
73
- f"Resolved base generic type '{base_name}' to: {BaseType}"
74
- )
75
-
76
- # Special handling for Literal
77
- if BaseType is typing.Literal:
78
- # Split literal values, remove quotes, strip whitespace
79
- def parse_literal_args(args_str: str) -> tuple[str, ...]:
80
- try:
81
- return tuple(ast.literal_eval(f"[{args_str}]"))
82
- except (SyntaxError, ValueError) as exc:
83
- raise ValueError(
84
- f"Cannot parse {args_str!r} as literals"
85
- ) from exc
86
-
87
- literal_args = parse_literal_args(args_str)
88
- logger.debug(
89
- f"Parsing Literal arguments: {args_str} -> {literal_args}"
90
- )
91
- resolved_type = typing.Literal[literal_args] # type: ignore
92
- logger.debug(f"Constructed Literal type: {resolved_type}")
93
- return resolved_type
94
-
95
- # Recursively resolve arguments for other generics
96
- logger.debug(f"Splitting generic arguments: '{args_str}'")
97
- arg_strs = split_top_level(args_str)
98
- logger.debug(f"Split arguments: {arg_strs}")
99
- if not arg_strs:
100
- raise ValueError("Generic type has no arguments.")
101
-
102
- resolved_arg_types = tuple(
103
- _resolve_type_string(arg) for arg in arg_strs
104
- )
105
- logger.debug(f"Resolved generic arguments: {resolved_arg_types}")
106
-
107
- # Construct the generic type hint
108
- if BaseType is typing.Optional:
109
- if len(resolved_arg_types) != 1:
110
- raise ValueError("Optional requires exactly one argument.")
111
- # type: ignore
112
- resolved_type = typing.Union[resolved_arg_types[0], type(None)]
113
- logger.debug(
114
- f"Constructed Optional type as Union: {resolved_type}"
115
- )
116
- return resolved_type
117
- elif BaseType is typing.Union:
118
- if not resolved_arg_types:
119
- raise ValueError("Union requires at least one argument.")
120
- # type: ignore
121
- resolved_type = typing.Union[resolved_arg_types]
122
- logger.debug(f"Constructed Union type: {resolved_type}")
123
- return resolved_type
124
- elif hasattr(
125
- BaseType, "__getitem__"
126
- ): # Check if subscriptable (like list, dict, List, Dict)
127
- resolved_type = BaseType[resolved_arg_types] # type: ignore
128
- logger.debug(
129
- f"Constructed subscripted generic type: {resolved_type}"
130
- )
131
- return resolved_type
132
- else:
133
- # Base type found but cannot be subscripted
134
- logger.warning(
135
- f"Base type '{base_name}' found but is not a standard subscriptable generic. Returning base type."
136
- )
137
- return BaseType
138
-
139
- except (KeyError, ValueError, IndexError, TypeError) as e:
140
- logger.warning(
141
- f"Failed to parse generic type '{type_str}': {e}. Falling back."
142
- )
143
- # Fall through to raise KeyError below if base type itself wasn't found or parsing failed
144
-
145
- # 3. If not resolved by now, raise error
146
- logger.error(f"Type string '{type_str}' could not be resolved.")
147
- raise KeyError(f"Type '{type_str}' could not be resolved.")
148
-
149
-
150
- class DSPyIntegrationMixin:
151
- """Mixin class for integrating with the dspy library."""
152
-
153
- def create_dspy_signature_class(self, agent_name: str, description_spec: str, fields_spec: str) -> Any:
154
- """Create a DSPy Signature using DSPy's native builder.
155
-
156
- We support the Flock spec format: "field: type | description, ... -> ...".
157
- This converts to the dict-based make_signature format with
158
- InputField/OutputField and resolved Python types.
159
- """
160
- try:
161
- import dspy
162
- except ImportError as exc:
163
- logger.error("DSPy is not installed. Install with: pip install dspy-ai")
164
- raise
165
-
166
- # Split input/output part
167
- if "->" in fields_spec:
168
- inputs_spec, outputs_spec = fields_spec.split("->", 1)
169
- else:
170
- inputs_spec, outputs_spec = fields_spec, ""
171
-
172
- def parse_field(field_str: str) -> tuple[str, type, str | None] | None:
173
- field_str = field_str.strip()
174
- if not field_str:
175
- return None
176
- parts = field_str.split("|", 1)
177
- main_part = parts[0].strip()
178
- desc = parts[1].strip() if len(parts) > 1 else None
179
- if ":" in main_part:
180
- name, type_str = [s.strip() for s in main_part.split(":", 1)]
181
- else:
182
- name, type_str = main_part, "str"
183
- try:
184
- py_type = _resolve_type_string(type_str)
185
- except Exception as e:
186
- logger.warning(
187
- f"Type resolution failed for '{type_str}' in field '{name}': {e}. Falling back to str."
188
- )
189
- py_type = str
190
- return name, py_type, desc
191
-
192
- def to_field_tuples(spec: str, kind: str) -> dict[str, tuple[type, Any]]:
193
- mapping: dict[str, tuple[type, Any]] = {}
194
- if not spec.strip():
195
- return mapping
196
- for raw in split_top_level(spec):
197
- parsed = parse_field(raw)
198
- if not parsed:
199
- continue
200
- fname, ftype, fdesc = parsed
201
- FieldClass = dspy.InputField if kind == "input" else dspy.OutputField
202
- finfo = FieldClass(desc=fdesc) if fdesc is not None else FieldClass()
203
- mapping[fname] = (ftype, finfo)
204
- return mapping
205
-
206
- try:
207
- fields: dict[str, tuple[type, Any]] = {
208
- **to_field_tuples(inputs_spec, "input"),
209
- **to_field_tuples(outputs_spec, "output"),
210
- }
211
- sig = dspy.Signature(fields, description_spec or None, signature_name=f"dspy_{agent_name}")
212
- logger.info("Created DSPy Signature %s", sig.__name__)
213
- return sig
214
- except Exception as e: # pragma: no cover - defensive
215
- logger.error("Failed to create DSPy Signature for %s: %s", agent_name, e, exc_info=True)
216
- raise
217
-
218
- def _configure_language_model(
219
- self,
220
- model: str | None,
221
- use_cache: bool,
222
- temperature: float,
223
- max_tokens: int,
224
- ) -> None:
225
- """Initialize and configure the language model using dspy."""
226
- if model is None:
227
- logger.warning(
228
- "No model specified for DSPy configuration. Using DSPy default."
229
- )
230
- # Rely on DSPy's global default or raise error if none configured
231
- # import dspy
232
- # if dspy.settings.lm is None:
233
- # raise ValueError("No model specified for agent and no global DSPy LM configured.")
234
- return
235
-
236
- try:
237
- import dspy
238
- except ImportError:
239
- logger.error("DSPy is not installed; cannot configure LM.")
240
- return
241
-
242
- # Build an LM instance for per-call usage; prefer settings.context over global configure.
243
- try:
244
- lm_instance = dspy.LM(
245
- model=model,
246
- temperature=temperature,
247
- max_tokens=max_tokens,
248
- cache=use_cache,
249
- )
250
- # Do not call settings.configure() here to avoid cross-task/thread conflicts.
251
- # Callers should pass this LM via dspy.settings.context(lm=...) or program.acall(lm=...)
252
- dspy.settings # touch to ensure settings is importable
253
- logger.info(
254
- "Prepared DSPy LM (defer install to settings.context): model=%s temp=%s max_tokens=%s",
255
- model,
256
- temperature,
257
- max_tokens,
258
- )
259
- except Exception as e:
260
- logger.error("Failed to prepare DSPy LM '%s': %s", model, e, exc_info=True)
261
- raise
262
-
263
- def _select_task(
264
- self,
265
- signature: Any,
266
- override_evaluator_type: AgentType,
267
- max_tool_calls: int = 10,
268
- tools: list[Any] | None = None,
269
- mcp_tools: list[Any] | None = None,
270
- kwargs: dict[str, Any] = {},
271
- ) -> Any:
272
- """Select and instantiate the appropriate DSPy Program/Module."""
273
- try:
274
- import dspy
275
- except ImportError:
276
- logger.error(
277
- "DSPy library is not installed. Cannot select DSPy task."
278
- )
279
- raise ImportError("DSPy is required for this functionality.")
280
-
281
- processed_tools = []
282
- if tools:
283
- for tool in tools:
284
- if callable(tool): # Basic check
285
- processed_tools.append(tool)
286
- # Could add more sophisticated tool wrapping/validation here if needed
287
- else:
288
- logger.warning(
289
- f"Item '{tool}' in tools list is not callable, skipping."
290
- )
291
-
292
- processed_mcp_tools = []
293
- if mcp_tools:
294
- for mcp_tool in mcp_tools:
295
- if isinstance(mcp_tool, Tool): # Basic check
296
- processed_mcp_tools.append(mcp_tool)
297
- else:
298
- logger.warning(
299
- f"Item '{mcp_tool}' is not a dspy.primitives.Tool, skipping."
300
- )
301
-
302
- dspy_program = None
303
- selected_type = override_evaluator_type
304
-
305
- # Determine type if not overridden
306
- if not selected_type:
307
- selected_type = "ReAct" if processed_tools or processed_mcp_tools else "Predict"
308
-
309
- # Normalize common aliases/casing
310
- sel = selected_type.lower() if isinstance(selected_type, str) else selected_type
311
- if isinstance(sel, str):
312
- if sel in {"completion", "predict"}:
313
- sel = "predict"
314
- elif sel in {"react"}:
315
- sel = "react"
316
- elif sel in {"chainofthought", "cot", "chain_of_thought"}:
317
- sel = "chain_of_thought"
318
-
319
- logger.debug(
320
- f"Selecting DSPy program type: {selected_type} (Tools provided: {bool(processed_tools)}) (MCP Tools: {bool(processed_mcp_tools)}"
321
- )
322
-
323
- # Merge list of native tools and processed tools.
324
- # This makes mcp tools appear as native code functions to the llm of the agent.
325
- merged_tools = []
326
-
327
- if processed_tools:
328
- merged_tools = merged_tools + processed_tools
329
-
330
- if processed_mcp_tools:
331
- merged_tools = merged_tools + processed_mcp_tools
332
-
333
- try:
334
- if sel == "chain_of_thought":
335
- dspy_program = dspy.ChainOfThought(signature, **kwargs)
336
- elif sel == "react":
337
- if not kwargs:
338
- kwargs = {"max_iters": max_tool_calls}
339
- dspy_program = dspy.ReAct(
340
- signature, tools=merged_tools or [], **kwargs
341
- )
342
- elif sel == "predict":
343
- dspy_program = dspy.Predict(signature)
344
- else: # Fallback or handle unknown type
345
- logger.warning(
346
- f"Unknown or unsupported agent_type_override '{selected_type}'. Defaulting to dspy.Predict."
347
- )
348
- dspy_program = dspy.Predict(signature)
349
-
350
- logger.info(
351
- f"Instantiated DSPy program: {type(dspy_program).__name__}"
352
- )
353
- return dspy_program
354
- except Exception as e:
355
- logger.error(
356
- f"Failed to instantiate DSPy program of type '{selected_type}': {e}",
357
- exc_info=True,
358
- )
359
- raise RuntimeError(f"Could not create DSPy program: {e}") from e
360
-
361
- def _process_result(self, result: Any, inputs: dict[str, Any]) -> tuple[dict[str, Any], float, list]:
362
- """Convert a DSPy Prediction or mapping to a plain dict and attach LM history.
363
-
364
- Returns (result_dict, cost_placeholder, lm_history). The cost is set to 0.0;
365
- use token usage trackers elsewhere for accurate accounting.
366
- """
367
- try:
368
- import dspy
369
- except ImportError:
370
- dspy = None
371
-
372
- if result is None:
373
- logger.warning("DSPy program returned None result.")
374
- return {}, 0.0, []
375
-
376
- try:
377
- # Best-effort extraction from DSPy Prediction
378
- if dspy and isinstance(result, dspy.Prediction):
379
- output_dict = dict(result.items(include_dspy=False))
380
- elif isinstance(result, dict):
381
- output_dict = result
382
- elif hasattr(result, "items") and callable(result.items):
383
- try:
384
- output_dict = dict(result.items())
385
- except Exception:
386
- output_dict = {"raw_result": str(result)}
387
- else:
388
- output_dict = {"raw_result": str(result)}
389
-
390
- final_result = {**inputs, **output_dict}
391
-
392
- lm_history = []
393
- try:
394
- if dspy and dspy.settings.lm is not None and hasattr(dspy.settings.lm, "history"):
395
- lm_history = dspy.settings.lm.history
396
- except Exception:
397
- lm_history = []
398
-
399
- return final_result, 0.0, lm_history
400
-
401
- except Exception as conv_error: # pragma: no cover - defensive
402
- logger.error("Failed to process DSPy result into dictionary: %s", conv_error, exc_info=True)
403
- return {"error": "Failed to process result", "raw_result": str(result)}, 0.0, []