flock-core 0.4.542__py3-none-any.whl → 0.5.0__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 (501) hide show
  1. flock/__init__.py +12 -217
  2. flock/agent.py +1079 -0
  3. flock/api/themes.py +71 -0
  4. flock/artifacts.py +86 -0
  5. flock/cli.py +147 -0
  6. flock/components.py +189 -0
  7. flock/dashboard/__init__.py +30 -0
  8. flock/dashboard/collector.py +559 -0
  9. flock/dashboard/events.py +188 -0
  10. flock/dashboard/graph_builder.py +563 -0
  11. flock/dashboard/launcher.py +235 -0
  12. flock/dashboard/models/graph.py +156 -0
  13. flock/dashboard/service.py +991 -0
  14. flock/dashboard/static_v2/assets/index-DFRnI_mt.js +111 -0
  15. flock/dashboard/static_v2/assets/index-fPLNdmp1.css +1 -0
  16. flock/dashboard/static_v2/index.html +13 -0
  17. flock/dashboard/websocket.py +246 -0
  18. flock/engines/__init__.py +6 -0
  19. flock/engines/dspy_engine.py +932 -0
  20. flock/examples.py +131 -0
  21. flock/frontend/README.md +778 -0
  22. flock/frontend/docs/DESIGN_SYSTEM.md +1980 -0
  23. flock/frontend/index.html +12 -0
  24. flock/frontend/package-lock.json +4337 -0
  25. flock/frontend/package.json +48 -0
  26. flock/frontend/src/App.tsx +139 -0
  27. flock/frontend/src/__tests__/integration/graph-snapshot.test.tsx +647 -0
  28. flock/frontend/src/__tests__/integration/indexeddb-persistence.test.tsx +699 -0
  29. flock/frontend/src/components/common/BuildInfo.tsx +39 -0
  30. flock/frontend/src/components/common/EmptyState.module.css +115 -0
  31. flock/frontend/src/components/common/EmptyState.tsx +128 -0
  32. flock/frontend/src/components/common/ErrorBoundary.module.css +169 -0
  33. flock/frontend/src/components/common/ErrorBoundary.tsx +118 -0
  34. flock/frontend/src/components/common/KeyboardShortcutsDialog.css +251 -0
  35. flock/frontend/src/components/common/KeyboardShortcutsDialog.tsx +151 -0
  36. flock/frontend/src/components/common/LoadingSpinner.module.css +97 -0
  37. flock/frontend/src/components/common/LoadingSpinner.tsx +29 -0
  38. flock/frontend/src/components/controls/PublishControl.css +547 -0
  39. flock/frontend/src/components/controls/PublishControl.test.tsx +543 -0
  40. flock/frontend/src/components/controls/PublishControl.tsx +432 -0
  41. flock/frontend/src/components/details/DetailWindowContainer.tsx +58 -0
  42. flock/frontend/src/components/details/LiveOutputTab.test.tsx +792 -0
  43. flock/frontend/src/components/details/LiveOutputTab.tsx +220 -0
  44. flock/frontend/src/components/details/MessageDetailWindow.tsx +439 -0
  45. flock/frontend/src/components/details/MessageHistoryTab.tsx +374 -0
  46. flock/frontend/src/components/details/NodeDetailWindow.test.tsx +501 -0
  47. flock/frontend/src/components/details/NodeDetailWindow.tsx +218 -0
  48. flock/frontend/src/components/details/RunStatusTab.tsx +348 -0
  49. flock/frontend/src/components/details/tabs.test.tsx +1015 -0
  50. flock/frontend/src/components/filters/ArtifactTypeFilter.tsx +21 -0
  51. flock/frontend/src/components/filters/CorrelationIDFilter.module.css +102 -0
  52. flock/frontend/src/components/filters/CorrelationIDFilter.test.tsx +197 -0
  53. flock/frontend/src/components/filters/CorrelationIDFilter.tsx +121 -0
  54. flock/frontend/src/components/filters/FilterFlyout.module.css +104 -0
  55. flock/frontend/src/components/filters/FilterFlyout.tsx +80 -0
  56. flock/frontend/src/components/filters/FilterPills.module.css +220 -0
  57. flock/frontend/src/components/filters/FilterPills.test.tsx +189 -0
  58. flock/frontend/src/components/filters/FilterPills.tsx +143 -0
  59. flock/frontend/src/components/filters/ProducerFilter.tsx +21 -0
  60. flock/frontend/src/components/filters/SavedFiltersControl.module.css +60 -0
  61. flock/frontend/src/components/filters/SavedFiltersControl.test.tsx +158 -0
  62. flock/frontend/src/components/filters/SavedFiltersControl.tsx +159 -0
  63. flock/frontend/src/components/filters/TagFilter.tsx +21 -0
  64. flock/frontend/src/components/filters/TimeRangeFilter.module.css +115 -0
  65. flock/frontend/src/components/filters/TimeRangeFilter.test.tsx +154 -0
  66. flock/frontend/src/components/filters/TimeRangeFilter.tsx +110 -0
  67. flock/frontend/src/components/filters/VisibilityFilter.tsx +21 -0
  68. flock/frontend/src/components/graph/AgentNode.test.tsx +77 -0
  69. flock/frontend/src/components/graph/AgentNode.tsx +324 -0
  70. flock/frontend/src/components/graph/GraphCanvas.tsx +613 -0
  71. flock/frontend/src/components/graph/MessageFlowEdge.tsx +128 -0
  72. flock/frontend/src/components/graph/MessageNode.test.tsx +64 -0
  73. flock/frontend/src/components/graph/MessageNode.tsx +129 -0
  74. flock/frontend/src/components/graph/MiniMap.tsx +47 -0
  75. flock/frontend/src/components/graph/TransformEdge.tsx +123 -0
  76. flock/frontend/src/components/layout/DashboardLayout.css +420 -0
  77. flock/frontend/src/components/layout/DashboardLayout.tsx +287 -0
  78. flock/frontend/src/components/layout/Header.module.css +88 -0
  79. flock/frontend/src/components/layout/Header.tsx +52 -0
  80. flock/frontend/src/components/modules/HistoricalArtifactsModule.module.css +288 -0
  81. flock/frontend/src/components/modules/HistoricalArtifactsModule.tsx +450 -0
  82. flock/frontend/src/components/modules/HistoricalArtifactsModuleWrapper.tsx +13 -0
  83. flock/frontend/src/components/modules/JsonAttributeRenderer.tsx +140 -0
  84. flock/frontend/src/components/modules/ModuleRegistry.test.ts +333 -0
  85. flock/frontend/src/components/modules/ModuleRegistry.ts +93 -0
  86. flock/frontend/src/components/modules/ModuleWindow.tsx +223 -0
  87. flock/frontend/src/components/modules/TraceModuleJaeger.tsx +1971 -0
  88. flock/frontend/src/components/modules/TraceModuleJaegerWrapper.tsx +13 -0
  89. flock/frontend/src/components/modules/registerModules.ts +29 -0
  90. flock/frontend/src/components/settings/AdvancedSettings.tsx +175 -0
  91. flock/frontend/src/components/settings/AppearanceSettings.tsx +185 -0
  92. flock/frontend/src/components/settings/GraphSettings.tsx +110 -0
  93. flock/frontend/src/components/settings/MultiSelect.tsx +235 -0
  94. flock/frontend/src/components/settings/SettingsPanel.css +327 -0
  95. flock/frontend/src/components/settings/SettingsPanel.tsx +131 -0
  96. flock/frontend/src/components/settings/ThemeSelector.tsx +298 -0
  97. flock/frontend/src/components/settings/TracingSettings.tsx +404 -0
  98. flock/frontend/src/hooks/useKeyboardShortcuts.ts +148 -0
  99. flock/frontend/src/hooks/useModulePersistence.test.ts +442 -0
  100. flock/frontend/src/hooks/useModulePersistence.ts +154 -0
  101. flock/frontend/src/hooks/useModules.ts +157 -0
  102. flock/frontend/src/hooks/usePersistence.ts +141 -0
  103. flock/frontend/src/main.tsx +13 -0
  104. flock/frontend/src/services/api.ts +337 -0
  105. flock/frontend/src/services/graphService.test.ts +330 -0
  106. flock/frontend/src/services/graphService.ts +75 -0
  107. flock/frontend/src/services/indexeddb.test.ts +793 -0
  108. flock/frontend/src/services/indexeddb.ts +848 -0
  109. flock/frontend/src/services/layout.test.ts +437 -0
  110. flock/frontend/src/services/layout.ts +357 -0
  111. flock/frontend/src/services/themeApplicator.ts +140 -0
  112. flock/frontend/src/services/themeService.ts +77 -0
  113. flock/frontend/src/services/websocket.ts +650 -0
  114. flock/frontend/src/store/filterStore.test.ts +250 -0
  115. flock/frontend/src/store/filterStore.ts +272 -0
  116. flock/frontend/src/store/graphStore.test.ts +570 -0
  117. flock/frontend/src/store/graphStore.ts +462 -0
  118. flock/frontend/src/store/moduleStore.test.ts +253 -0
  119. flock/frontend/src/store/moduleStore.ts +75 -0
  120. flock/frontend/src/store/settingsStore.ts +188 -0
  121. flock/frontend/src/store/streamStore.ts +68 -0
  122. flock/frontend/src/store/uiStore.test.ts +54 -0
  123. flock/frontend/src/store/uiStore.ts +122 -0
  124. flock/frontend/src/store/wsStore.ts +34 -0
  125. flock/frontend/src/styles/index.css +15 -0
  126. flock/frontend/src/styles/scrollbar.css +47 -0
  127. flock/frontend/src/styles/variables.css +488 -0
  128. flock/frontend/src/test/setup.ts +1 -0
  129. flock/frontend/src/types/filters.ts +47 -0
  130. flock/frontend/src/types/graph.ts +95 -0
  131. flock/frontend/src/types/modules.ts +10 -0
  132. flock/frontend/src/types/theme.ts +55 -0
  133. flock/frontend/src/utils/artifacts.ts +24 -0
  134. flock/frontend/src/utils/mockData.ts +98 -0
  135. flock/frontend/src/utils/performance.ts +16 -0
  136. flock/frontend/src/vite-env.d.ts +17 -0
  137. flock/frontend/tsconfig.json +27 -0
  138. flock/frontend/tsconfig.node.json +11 -0
  139. flock/frontend/vite.config.ts +25 -0
  140. flock/frontend/vitest.config.ts +11 -0
  141. flock/{core/util → helper}/cli_helper.py +9 -5
  142. flock/{core/logging → logging}/__init__.py +2 -3
  143. flock/logging/auto_trace.py +159 -0
  144. flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
  145. flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
  146. flock/{core/logging → logging}/formatters/themed_formatter.py +69 -107
  147. flock/{core/logging → logging}/logging.py +78 -61
  148. flock/{core/logging → logging}/telemetry.py +66 -26
  149. flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
  150. flock/logging/telemetry_exporter/duckdb_exporter.py +216 -0
  151. flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +13 -10
  152. flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
  153. flock/logging/trace_and_logged.py +304 -0
  154. flock/mcp/__init__.py +91 -0
  155. flock/{core/mcp/mcp_client.py → mcp/client.py} +131 -158
  156. flock/{core/mcp/mcp_config.py → mcp/config.py} +86 -132
  157. flock/mcp/manager.py +286 -0
  158. flock/mcp/servers/sse/__init__.py +1 -1
  159. flock/mcp/servers/sse/flock_sse_server.py +16 -58
  160. flock/mcp/servers/stdio/__init__.py +1 -1
  161. flock/mcp/servers/stdio/flock_stdio_server.py +13 -53
  162. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +22 -67
  163. flock/mcp/servers/websockets/flock_websocket_server.py +12 -45
  164. flock/{core/mcp/flock_mcp_tool_base.py → mcp/tool.py} +24 -78
  165. flock/mcp/types/__init__.py +42 -0
  166. flock/{core/mcp → mcp}/types/callbacks.py +12 -15
  167. flock/{core/mcp → mcp}/types/factories.py +7 -6
  168. flock/{core/mcp → mcp}/types/handlers.py +13 -18
  169. flock/{core/mcp → mcp}/types/types.py +70 -74
  170. flock/{core/mcp → mcp}/util/helpers.py +3 -3
  171. flock/orchestrator.py +970 -0
  172. flock/registry.py +148 -0
  173. flock/runtime.py +262 -0
  174. flock/service.py +277 -0
  175. flock/store.py +1214 -0
  176. flock/subscription.py +111 -0
  177. flock/themes/andromeda.toml +1 -1
  178. flock/themes/apple-system-colors.toml +1 -1
  179. flock/themes/arcoiris.toml +1 -1
  180. flock/themes/atomonelight.toml +1 -1
  181. flock/themes/ayu copy.toml +1 -1
  182. flock/themes/ayu-light.toml +1 -1
  183. flock/themes/belafonte-day.toml +1 -1
  184. flock/themes/belafonte-night.toml +1 -1
  185. flock/themes/blulocodark.toml +1 -1
  186. flock/themes/breeze.toml +1 -1
  187. flock/themes/broadcast.toml +1 -1
  188. flock/themes/brogrammer.toml +1 -1
  189. flock/themes/builtin-dark.toml +1 -1
  190. flock/themes/builtin-pastel-dark.toml +1 -1
  191. flock/themes/catppuccin-latte.toml +1 -1
  192. flock/themes/catppuccin-macchiato.toml +1 -1
  193. flock/themes/catppuccin-mocha.toml +1 -1
  194. flock/themes/cga.toml +1 -1
  195. flock/themes/chalk.toml +1 -1
  196. flock/themes/ciapre.toml +1 -1
  197. flock/themes/coffee-theme.toml +1 -1
  198. flock/themes/cyberpunkscarletprotocol.toml +1 -1
  199. flock/themes/dark+.toml +1 -1
  200. flock/themes/darkermatrix.toml +1 -1
  201. flock/themes/darkmatrix.toml +2 -2
  202. flock/themes/darkside.toml +1 -1
  203. flock/themes/deep.toml +2 -2
  204. flock/themes/desert.toml +1 -1
  205. flock/themes/django.toml +1 -1
  206. flock/themes/djangosmooth.toml +1 -1
  207. flock/themes/doomone.toml +1 -1
  208. flock/themes/dotgov.toml +1 -1
  209. flock/themes/dracula+.toml +1 -1
  210. flock/themes/duckbones.toml +1 -1
  211. flock/themes/encom.toml +1 -1
  212. flock/themes/espresso.toml +1 -1
  213. flock/themes/everblush.toml +1 -1
  214. flock/themes/fairyfloss.toml +1 -1
  215. flock/themes/fideloper.toml +1 -1
  216. flock/themes/fishtank.toml +1 -1
  217. flock/themes/flexoki-light.toml +1 -1
  218. flock/themes/floraverse.toml +1 -1
  219. flock/themes/framer.toml +1 -1
  220. flock/themes/galizur.toml +1 -1
  221. flock/themes/github.toml +1 -1
  222. flock/themes/grass.toml +1 -1
  223. flock/themes/grey-green.toml +1 -1
  224. flock/themes/gruvboxlight.toml +1 -1
  225. flock/themes/guezwhoz.toml +1 -1
  226. flock/themes/harper.toml +1 -1
  227. flock/themes/hax0r-blue.toml +1 -1
  228. flock/themes/hopscotch.256.toml +1 -1
  229. flock/themes/ic-green-ppl.toml +1 -1
  230. flock/themes/iceberg-dark.toml +1 -1
  231. flock/themes/japanesque.toml +1 -1
  232. flock/themes/jubi.toml +1 -1
  233. flock/themes/kibble.toml +1 -1
  234. flock/themes/kolorit.toml +1 -1
  235. flock/themes/kurokula.toml +1 -1
  236. flock/themes/materialdesigncolors.toml +1 -1
  237. flock/themes/matrix.toml +1 -1
  238. flock/themes/mellifluous.toml +1 -1
  239. flock/themes/midnight-in-mojave.toml +1 -1
  240. flock/themes/monokai-remastered.toml +1 -1
  241. flock/themes/monokai-soda.toml +1 -1
  242. flock/themes/neon.toml +1 -1
  243. flock/themes/neopolitan.toml +5 -5
  244. flock/themes/nord-light.toml +1 -1
  245. flock/themes/ocean.toml +1 -1
  246. flock/themes/onehalfdark.toml +1 -1
  247. flock/themes/onehalflight.toml +1 -1
  248. flock/themes/palenighthc.toml +1 -1
  249. flock/themes/paulmillr.toml +1 -1
  250. flock/themes/pencildark.toml +1 -1
  251. flock/themes/pnevma.toml +1 -1
  252. flock/themes/purple-rain.toml +1 -1
  253. flock/themes/purplepeter.toml +1 -1
  254. flock/themes/raycast-dark.toml +1 -1
  255. flock/themes/red-sands.toml +1 -1
  256. flock/themes/relaxed.toml +1 -1
  257. flock/themes/retro.toml +1 -1
  258. flock/themes/rose-pine.toml +1 -1
  259. flock/themes/royal.toml +1 -1
  260. flock/themes/ryuuko.toml +1 -1
  261. flock/themes/sakura.toml +1 -1
  262. flock/themes/scarlet-protocol.toml +1 -1
  263. flock/themes/seoulbones-dark.toml +1 -1
  264. flock/themes/shades-of-purple.toml +1 -1
  265. flock/themes/smyck.toml +1 -1
  266. flock/themes/softserver.toml +1 -1
  267. flock/themes/solarized-darcula.toml +1 -1
  268. flock/themes/square.toml +1 -1
  269. flock/themes/sugarplum.toml +1 -1
  270. flock/themes/thayer-bright.toml +1 -1
  271. flock/themes/tokyonight.toml +1 -1
  272. flock/themes/tomorrow.toml +1 -1
  273. flock/themes/ubuntu.toml +1 -1
  274. flock/themes/ultradark.toml +1 -1
  275. flock/themes/ultraviolent.toml +1 -1
  276. flock/themes/unikitty.toml +1 -1
  277. flock/themes/urple.toml +1 -1
  278. flock/themes/vesper.toml +1 -1
  279. flock/themes/vimbones.toml +1 -1
  280. flock/themes/wildcherry.toml +1 -1
  281. flock/themes/wilmersdorf.toml +1 -1
  282. flock/themes/wryan.toml +1 -1
  283. flock/themes/xcodedarkhc.toml +1 -1
  284. flock/themes/xcodelight.toml +1 -1
  285. flock/themes/zenbones-light.toml +1 -1
  286. flock/themes/zenwritten-dark.toml +1 -1
  287. flock/utilities.py +301 -0
  288. flock/utility/output_utility_component.py +226 -0
  289. flock/visibility.py +107 -0
  290. flock_core-0.5.0.dist-info/METADATA +964 -0
  291. flock_core-0.5.0.dist-info/RECORD +525 -0
  292. flock_core-0.5.0.dist-info/entry_points.txt +2 -0
  293. {flock_core-0.4.542.dist-info → flock_core-0.5.0.dist-info}/licenses/LICENSE +1 -1
  294. flock/adapter/__init__.py +0 -14
  295. flock/adapter/azure_adapter.py +0 -68
  296. flock/adapter/chroma_adapter.py +0 -73
  297. flock/adapter/faiss_adapter.py +0 -97
  298. flock/adapter/pinecone_adapter.py +0 -51
  299. flock/adapter/vector_base.py +0 -47
  300. flock/cli/assets/release_notes.md +0 -140
  301. flock/cli/config.py +0 -8
  302. flock/cli/constants.py +0 -36
  303. flock/cli/create_agent.py +0 -1
  304. flock/cli/create_flock.py +0 -280
  305. flock/cli/execute_flock.py +0 -620
  306. flock/cli/load_agent.py +0 -1
  307. flock/cli/load_examples.py +0 -1
  308. flock/cli/load_flock.py +0 -192
  309. flock/cli/load_release_notes.py +0 -20
  310. flock/cli/loaded_flock_cli.py +0 -254
  311. flock/cli/manage_agents.py +0 -459
  312. flock/cli/registry_management.py +0 -889
  313. flock/cli/runner.py +0 -41
  314. flock/cli/settings.py +0 -857
  315. flock/cli/utils.py +0 -135
  316. flock/cli/view_results.py +0 -29
  317. flock/cli/yaml_editor.py +0 -396
  318. flock/config.py +0 -56
  319. flock/core/__init__.py +0 -44
  320. flock/core/api/__init__.py +0 -10
  321. flock/core/api/custom_endpoint.py +0 -45
  322. flock/core/api/endpoints.py +0 -262
  323. flock/core/api/main.py +0 -162
  324. flock/core/api/models.py +0 -101
  325. flock/core/api/run_store.py +0 -224
  326. flock/core/api/runner.py +0 -44
  327. flock/core/api/service.py +0 -214
  328. flock/core/config/flock_agent_config.py +0 -11
  329. flock/core/config/scheduled_agent_config.py +0 -40
  330. flock/core/context/context.py +0 -214
  331. flock/core/context/context_manager.py +0 -40
  332. flock/core/context/context_vars.py +0 -11
  333. flock/core/evaluation/utils.py +0 -395
  334. flock/core/execution/batch_executor.py +0 -369
  335. flock/core/execution/evaluation_executor.py +0 -438
  336. flock/core/execution/local_executor.py +0 -31
  337. flock/core/execution/opik_executor.py +0 -103
  338. flock/core/execution/temporal_executor.py +0 -166
  339. flock/core/flock.py +0 -1003
  340. flock/core/flock_agent.py +0 -1258
  341. flock/core/flock_evaluator.py +0 -60
  342. flock/core/flock_factory.py +0 -513
  343. flock/core/flock_module.py +0 -207
  344. flock/core/flock_registry.py +0 -702
  345. flock/core/flock_router.py +0 -83
  346. flock/core/flock_scheduler.py +0 -166
  347. flock/core/flock_server_manager.py +0 -136
  348. flock/core/interpreter/python_interpreter.py +0 -689
  349. flock/core/logging/live_capture.py +0 -137
  350. flock/core/logging/trace_and_logged.py +0 -59
  351. flock/core/mcp/__init__.py +0 -1
  352. flock/core/mcp/flock_mcp_server.py +0 -640
  353. flock/core/mcp/mcp_client_manager.py +0 -201
  354. flock/core/mcp/types/__init__.py +0 -1
  355. flock/core/mixin/dspy_integration.py +0 -445
  356. flock/core/mixin/prompt_parser.py +0 -125
  357. flock/core/serialization/__init__.py +0 -13
  358. flock/core/serialization/callable_registry.py +0 -52
  359. flock/core/serialization/flock_serializer.py +0 -854
  360. flock/core/serialization/json_encoder.py +0 -41
  361. flock/core/serialization/secure_serializer.py +0 -175
  362. flock/core/serialization/serializable.py +0 -342
  363. flock/core/serialization/serialization_utils.py +0 -409
  364. flock/core/util/file_path_utils.py +0 -223
  365. flock/core/util/hydrator.py +0 -309
  366. flock/core/util/input_resolver.py +0 -141
  367. flock/core/util/loader.py +0 -59
  368. flock/core/util/splitter.py +0 -219
  369. flock/di.py +0 -41
  370. flock/evaluators/__init__.py +0 -1
  371. flock/evaluators/declarative/__init__.py +0 -1
  372. flock/evaluators/declarative/declarative_evaluator.py +0 -217
  373. flock/evaluators/memory/memory_evaluator.py +0 -90
  374. flock/evaluators/test/test_case_evaluator.py +0 -38
  375. flock/evaluators/zep/zep_evaluator.py +0 -59
  376. flock/modules/__init__.py +0 -1
  377. flock/modules/assertion/__init__.py +0 -1
  378. flock/modules/assertion/assertion_module.py +0 -286
  379. flock/modules/callback/__init__.py +0 -1
  380. flock/modules/callback/callback_module.py +0 -91
  381. flock/modules/enterprise_memory/README.md +0 -99
  382. flock/modules/enterprise_memory/enterprise_memory_module.py +0 -526
  383. flock/modules/mem0/__init__.py +0 -1
  384. flock/modules/mem0/mem0_module.py +0 -126
  385. flock/modules/mem0_async/__init__.py +0 -1
  386. flock/modules/mem0_async/async_mem0_module.py +0 -126
  387. flock/modules/memory/__init__.py +0 -1
  388. flock/modules/memory/memory_module.py +0 -429
  389. flock/modules/memory/memory_parser.py +0 -125
  390. flock/modules/memory/memory_storage.py +0 -736
  391. flock/modules/output/__init__.py +0 -1
  392. flock/modules/output/output_module.py +0 -196
  393. flock/modules/performance/__init__.py +0 -1
  394. flock/modules/performance/metrics_module.py +0 -678
  395. flock/modules/zep/__init__.py +0 -1
  396. flock/modules/zep/zep_module.py +0 -192
  397. flock/platform/docker_tools.py +0 -49
  398. flock/platform/jaeger_install.py +0 -86
  399. flock/routers/__init__.py +0 -1
  400. flock/routers/agent/__init__.py +0 -1
  401. flock/routers/agent/agent_router.py +0 -236
  402. flock/routers/agent/handoff_agent.py +0 -58
  403. flock/routers/conditional/conditional_router.py +0 -486
  404. flock/routers/default/__init__.py +0 -1
  405. flock/routers/default/default_router.py +0 -80
  406. flock/routers/feedback/feedback_router.py +0 -114
  407. flock/routers/list_generator/list_generator_router.py +0 -166
  408. flock/routers/llm/__init__.py +0 -1
  409. flock/routers/llm/llm_router.py +0 -365
  410. flock/tools/__init__.py +0 -0
  411. flock/tools/azure_tools.py +0 -781
  412. flock/tools/code_tools.py +0 -167
  413. flock/tools/file_tools.py +0 -149
  414. flock/tools/github_tools.py +0 -157
  415. flock/tools/markdown_tools.py +0 -205
  416. flock/tools/system_tools.py +0 -9
  417. flock/tools/text_tools.py +0 -810
  418. flock/tools/web_tools.py +0 -92
  419. flock/tools/zendesk_tools.py +0 -501
  420. flock/webapp/__init__.py +0 -1
  421. flock/webapp/app/__init__.py +0 -0
  422. flock/webapp/app/api/__init__.py +0 -0
  423. flock/webapp/app/api/agent_management.py +0 -237
  424. flock/webapp/app/api/execution.py +0 -503
  425. flock/webapp/app/api/flock_management.py +0 -125
  426. flock/webapp/app/api/registry_viewer.py +0 -29
  427. flock/webapp/app/chat.py +0 -662
  428. flock/webapp/app/config.py +0 -104
  429. flock/webapp/app/dependencies.py +0 -117
  430. flock/webapp/app/main.py +0 -1086
  431. flock/webapp/app/middleware.py +0 -113
  432. flock/webapp/app/models_ui.py +0 -7
  433. flock/webapp/app/services/__init__.py +0 -0
  434. flock/webapp/app/services/feedback_file_service.py +0 -363
  435. flock/webapp/app/services/flock_service.py +0 -345
  436. flock/webapp/app/services/sharing_models.py +0 -81
  437. flock/webapp/app/services/sharing_store.py +0 -597
  438. flock/webapp/app/templates/theme_mapper.html +0 -326
  439. flock/webapp/app/theme_mapper.py +0 -811
  440. flock/webapp/app/utils.py +0 -85
  441. flock/webapp/run.py +0 -219
  442. flock/webapp/static/css/chat.css +0 -301
  443. flock/webapp/static/css/components.css +0 -167
  444. flock/webapp/static/css/header.css +0 -39
  445. flock/webapp/static/css/layout.css +0 -281
  446. flock/webapp/static/css/sidebar.css +0 -127
  447. flock/webapp/static/css/two-pane.css +0 -48
  448. flock/webapp/templates/base.html +0 -389
  449. flock/webapp/templates/chat.html +0 -152
  450. flock/webapp/templates/chat_settings.html +0 -19
  451. flock/webapp/templates/flock_editor.html +0 -16
  452. flock/webapp/templates/index.html +0 -12
  453. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  454. flock/webapp/templates/partials/_agent_list.html +0 -18
  455. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  456. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  457. flock/webapp/templates/partials/_chat_container.html +0 -15
  458. flock/webapp/templates/partials/_chat_messages.html +0 -57
  459. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  460. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  461. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  462. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  463. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  464. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  465. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  466. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  467. flock/webapp/templates/partials/_execution_form.html +0 -127
  468. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  469. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  470. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  471. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  472. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  473. flock/webapp/templates/partials/_live_logs.html +0 -13
  474. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  475. flock/webapp/templates/partials/_registry_table.html +0 -25
  476. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  477. flock/webapp/templates/partials/_results_display.html +0 -78
  478. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  479. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  480. flock/webapp/templates/partials/_settings_view.html +0 -36
  481. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  482. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  483. flock/webapp/templates/partials/_sidebar.html +0 -74
  484. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  485. flock/webapp/templates/partials/_theme_preview.html +0 -36
  486. flock/webapp/templates/registry_viewer.html +0 -84
  487. flock/webapp/templates/shared_run_page.html +0 -140
  488. flock/workflow/__init__.py +0 -0
  489. flock/workflow/activities.py +0 -237
  490. flock/workflow/agent_activities.py +0 -24
  491. flock/workflow/agent_execution_activity.py +0 -240
  492. flock/workflow/flock_workflow.py +0 -225
  493. flock/workflow/temporal_config.py +0 -96
  494. flock/workflow/temporal_setup.py +0 -60
  495. flock_core-0.4.542.dist-info/METADATA +0 -676
  496. flock_core-0.4.542.dist-info/RECORD +0 -572
  497. flock_core-0.4.542.dist-info/entry_points.txt +0 -2
  498. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  499. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  500. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  501. {flock_core-0.4.542.dist-info → flock_core-0.5.0.dist-info}/WHEEL +0 -0
@@ -1,597 +0,0 @@
1
- """Shared link and feedback storage implementations supporting SQLite and Azure Table Storage."""
2
-
3
- import logging
4
- import sqlite3
5
- from abc import ABC, abstractmethod
6
- from pathlib import Path
7
- from typing import Any
8
-
9
- import aiosqlite
10
-
11
- from flock.webapp.app.services.sharing_models import (
12
- FeedbackRecord,
13
- SharedLinkConfig,
14
- )
15
-
16
- # Azure Table Storage imports - will be conditionally imported
17
- try:
18
- from azure.core.exceptions import ResourceExistsError, ResourceNotFoundError
19
- from azure.data.tables.aio import TableServiceClient
20
- AZURE_AVAILABLE = True
21
- except ImportError:
22
- AZURE_AVAILABLE = False
23
- TableServiceClient = None
24
- ResourceNotFoundError = None
25
- ResourceExistsError = None
26
-
27
- # Get a logger instance
28
- logger = logging.getLogger(__name__)
29
-
30
- class SharedLinkStoreInterface(ABC):
31
- """Interface for storing and retrieving shared link configurations."""
32
-
33
- @abstractmethod
34
- async def initialize(self) -> None:
35
- """Initialize the store (e.g., create tables)."""
36
- pass
37
-
38
- @abstractmethod
39
- async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
40
- """Saves a shared link configuration."""
41
- pass
42
-
43
- @abstractmethod
44
- async def get_config(self, share_id: str) -> SharedLinkConfig | None:
45
- """Retrieves a shared link configuration by its ID."""
46
- pass
47
-
48
- @abstractmethod
49
- async def delete_config(self, share_id: str) -> bool:
50
- """Deletes a shared link configuration by its ID. Returns True if deleted, False otherwise."""
51
- pass
52
-
53
- # Feedback
54
- @abstractmethod
55
- async def save_feedback(self, record: FeedbackRecord):
56
- """Persist a feedback record."""
57
- pass
58
-
59
- @abstractmethod
60
- async def get_feedback(self, id: str) -> FeedbackRecord | None:
61
- """Get a single feedback record."""
62
- pass
63
-
64
- @abstractmethod
65
- async def get_all_feedback_records_for_agent(self, agent_name: str) -> list[FeedbackRecord]:
66
- """Get all feedback records for a given agent."""
67
- pass
68
-
69
- class SQLiteSharedLinkStore(SharedLinkStoreInterface):
70
- """SQLite implementation for storing and retrieving shared link configurations."""
71
-
72
- def __init__(self, db_path: str):
73
- """Initialize SQLite store with database path."""
74
- self.db_path = Path(db_path)
75
- self.db_path.parent.mkdir(parents=True, exist_ok=True) # Ensure directory exists
76
- logger.info(f"SQLiteSharedLinkStore initialized with db_path: {self.db_path}")
77
-
78
- async def initialize(self) -> None:
79
- """Initializes the database and creates/updates the table if it doesn't exist."""
80
- try:
81
- async with aiosqlite.connect(self.db_path) as db:
82
- # Ensure the table exists with the base schema first
83
- await db.execute(
84
- """
85
- CREATE TABLE IF NOT EXISTS shared_links (
86
- share_id TEXT PRIMARY KEY,
87
- agent_name TEXT NOT NULL,
88
- flock_definition TEXT NOT NULL,
89
- created_at TEXT NOT NULL
90
- /* New columns will be added below if they don't exist */
91
- )
92
- """
93
- )
94
-
95
- # Add new columns individually, ignoring errors if they already exist
96
- new_columns = [
97
- ("share_type", "TEXT DEFAULT 'agent_run' NOT NULL"),
98
- ("chat_message_key", "TEXT"),
99
- ("chat_history_key", "TEXT"),
100
- ("chat_response_key", "TEXT")
101
- ]
102
-
103
- for column_name, column_type in new_columns:
104
- try:
105
- await db.execute(f"ALTER TABLE shared_links ADD COLUMN {column_name} {column_type}")
106
- logger.info(f"Added column '{column_name}' to shared_links table.")
107
- except sqlite3.OperationalError as e:
108
- if "duplicate column name" in str(e).lower():
109
- logger.debug(f"Column '{column_name}' already exists in shared_links table.")
110
- else:
111
- raise # Re-raise if it's a different operational error
112
-
113
- # Feedback table
114
- await db.execute(
115
- """
116
- CREATE TABLE IF NOT EXISTS feedback (
117
- feedback_id TEXT PRIMARY KEY,
118
- share_id TEXT,
119
- context_type TEXT NOT NULL,
120
- reason TEXT NOT NULL,
121
- expected_response TEXT,
122
- actual_response TEXT,
123
- flock_name TEXT,
124
- agent_name TEXT,
125
- flock_definition TEXT,
126
- created_at TEXT NOT NULL,
127
- FOREIGN KEY(share_id) REFERENCES shared_links(share_id)
128
- )
129
- """
130
- )
131
-
132
- await db.commit()
133
- logger.info(f"Database initialized and shared_links table schema ensured at {self.db_path}")
134
- except sqlite3.Error as e:
135
- logger.error(f"SQLite error during initialization: {e}", exc_info=True)
136
- raise
137
-
138
- async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
139
- """Saves a shared link configuration to the SQLite database."""
140
- try:
141
- async with aiosqlite.connect(self.db_path) as db:
142
- await db.execute(
143
- """INSERT INTO shared_links (
144
- share_id, agent_name, created_at, flock_definition,
145
- share_type, chat_message_key, chat_history_key, chat_response_key
146
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
147
- (
148
- config.share_id,
149
- config.agent_name,
150
- config.created_at.isoformat(),
151
- config.flock_definition,
152
- config.share_type,
153
- config.chat_message_key,
154
- config.chat_history_key,
155
- config.chat_response_key,
156
- ),
157
- )
158
- await db.commit()
159
- logger.info(f"Saved shared link config for ID: {config.share_id} with type: {config.share_type}")
160
- return config
161
- except sqlite3.Error as e:
162
- logger.error(f"SQLite error saving config for ID {config.share_id}: {e}", exc_info=True)
163
- raise
164
-
165
- async def get_config(self, share_id: str) -> SharedLinkConfig | None:
166
- """Retrieves a shared link configuration from SQLite by its ID."""
167
- try:
168
- async with aiosqlite.connect(self.db_path) as db:
169
- async with db.execute(
170
- """SELECT
171
- share_id, agent_name, created_at, flock_definition,
172
- share_type, chat_message_key, chat_history_key, chat_response_key
173
- FROM shared_links WHERE share_id = ?""",
174
- (share_id,)
175
- ) as cursor:
176
- row = await cursor.fetchone()
177
- if row:
178
- logger.debug(f"Retrieved shared link config for ID: {share_id}")
179
- return SharedLinkConfig(
180
- share_id=row[0],
181
- agent_name=row[1],
182
- created_at=row[2], # SQLite stores as TEXT, Pydantic will parse from ISO format
183
- flock_definition=row[3],
184
- share_type=row[4],
185
- chat_message_key=row[5],
186
- chat_history_key=row[6],
187
- chat_response_key=row[7],
188
- )
189
- logger.debug(f"No shared link config found for ID: {share_id}")
190
- return None
191
- except sqlite3.Error as e:
192
- logger.error(f"SQLite error retrieving config for ID {share_id}: {e}", exc_info=True)
193
- return None # Or raise, depending on desired error handling
194
-
195
- async def delete_config(self, share_id: str) -> bool:
196
- """Deletes a shared link configuration from SQLite by its ID."""
197
- try:
198
- async with aiosqlite.connect(self.db_path) as db:
199
- result = await db.execute("DELETE FROM shared_links WHERE share_id = ?", (share_id,))
200
- await db.commit()
201
- deleted_count = result.rowcount
202
- if deleted_count > 0:
203
- logger.info(f"Deleted shared link config for ID: {share_id}")
204
- return True
205
- logger.info(f"Attempted to delete non-existent shared link config for ID: {share_id}")
206
- return False
207
- except sqlite3.Error as e:
208
- logger.error(f"SQLite error deleting config for ID {share_id}: {e}", exc_info=True)
209
- return False # Or raise
210
-
211
- # ----------------------- Feedback methods -----------------------
212
- async def get_feedback(self, id: str) -> FeedbackRecord | None:
213
- """Retrieve a single feedback record from SQLite."""
214
- try:
215
- async with aiosqlite.connect(self.db_path) as db, db.execute(
216
- """SELECT
217
- feedback_id, share_id, context_type, reason,
218
- expected_response, actual_response, flock_name, agent_name, flock_definition, created_at
219
- FROM feedback WHERE feedback_id = ?""",
220
- (id,)
221
- ) as cursor:
222
- row = await cursor.fetchone()
223
-
224
- if row:
225
- logger.debug(f"Retrieved feedback record for ID: {id}")
226
- return FeedbackRecord(
227
- feedback_id=row[0],
228
- share_id=row[1],
229
- context_type=row[2],
230
- reason=row[3],
231
- expected_response=row[4],
232
- actual_response=row[5],
233
- flock_name=row[6],
234
- agent_name=row[7],
235
- flock_definition=row[8],
236
- created_at=row[9], # SQLite stores as TEXT, Pydantic will parse from ISO format
237
- )
238
-
239
- logger.debug(f"No feedback record found for ID: {id}")
240
- return None
241
- except sqlite3.Error as e:
242
- logger.error(f"SQLite error retrieving feedback for ID {id}: {e}", exc_info=True)
243
- return None # Or raise, depending on desired error handling
244
-
245
- async def get_all_feedback_records_for_agent(self, agent_name: str) -> list[FeedbackRecord]:
246
- """Retrieve all feedback records from SQLite."""
247
- try:
248
- async with aiosqlite.connect(self.db_path) as db, db.execute(
249
- """SELECT
250
- feedback_id, share_id, context_type, reason,
251
- expected_response, actual_response, flock_name, agent_name, flock_definition, created_at
252
- FROM feedback WHERE agent_name = ? ORDER BY created_at DESC""",
253
- (agent_name,)
254
- ) as cursor:
255
- rows = await cursor.fetchall()
256
-
257
- records = []
258
- for row in rows:
259
- records.append(FeedbackRecord(
260
- feedback_id=row[0],
261
- share_id=row[1],
262
- context_type=row[2],
263
- reason=row[3],
264
- expected_response=row[4],
265
- actual_response=row[5],
266
- flock_name=row[6],
267
- agent_name=row[7],
268
- flock_definition=row[8],
269
- created_at=row[9], # SQLite stores as TEXT, Pydantic will parse from ISO format
270
- ))
271
-
272
- logger.debug(f"Retrieved {len(records)} feedback records")
273
- return records
274
- except sqlite3.Error as e:
275
- logger.error(f"SQLite error retrieving all feedback records: {e}", exc_info=True)
276
- return [] # Return empty list on error
277
-
278
- async def save_feedback(self, record: FeedbackRecord) -> FeedbackRecord:
279
- """Persist a feedback record to SQLite."""
280
- try:
281
- async with aiosqlite.connect(self.db_path) as db:
282
- await db.execute(
283
- """INSERT INTO feedback (
284
- feedback_id, share_id, context_type, reason,
285
- expected_response, actual_response, flock_name, agent_name, flock_definition, created_at
286
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
287
- (
288
- record.feedback_id,
289
- record.share_id,
290
- record.context_type,
291
- record.reason,
292
- record.expected_response,
293
- record.actual_response,
294
- record.flock_name,
295
- record.agent_name,
296
- record.flock_definition,
297
- record.created_at.isoformat(),
298
- ),
299
- )
300
- await db.commit()
301
- logger.info(f"Saved feedback {record.feedback_id} (share={record.share_id})")
302
- return record
303
- except sqlite3.Error as e:
304
- logger.error(f"SQLite error saving feedback {record.feedback_id}: {e}", exc_info=True)
305
- raise
306
-
307
- # ---------------------------------------------------------------------------
308
- # Azure Table + Blob implementation
309
- # ---------------------------------------------------------------------------
310
-
311
- try:
312
- from azure.storage.blob.aio import BlobServiceClient
313
- AZURE_BLOB_AVAILABLE = True
314
- except ImportError: # blob SDK not installed
315
- AZURE_BLOB_AVAILABLE = False
316
- BlobServiceClient = None
317
-
318
- class AzureTableSharedLinkStore(SharedLinkStoreInterface):
319
- """Store configs in Azure Table; store large flock YAML in Blob Storage."""
320
-
321
- _TABLE_NAME = "flocksharedlinks"
322
- _FEEDBACK_TBL_NAME = "flockfeedback"
323
- _CONTAINER_NAME = "flocksharedlinkdefs" # blobs live here
324
- _PARTITION_KEY = "shared_links"
325
-
326
- def __init__(self, connection_string: str):
327
- if not AZURE_AVAILABLE:
328
- raise ImportError("pip install azure-data-tables")
329
- if not AZURE_BLOB_AVAILABLE:
330
- raise ImportError("pip install azure-storage-blob")
331
-
332
- self.connection_string = connection_string
333
- self.table_svc = TableServiceClient.from_connection_string(connection_string)
334
- self.blob_svc = BlobServiceClient.from_connection_string(connection_string)
335
-
336
- # ------------------------------------------------------------------ init
337
- async def initialize(self) -> None:
338
- # 1. Azure Tables ----------------------------------------------------
339
- try:
340
- await self.table_svc.create_table(self._TABLE_NAME)
341
- logger.info("Created Azure Table '%s'", self._TABLE_NAME)
342
- except ResourceExistsError:
343
- logger.debug("Azure Table '%s' already exists", self._TABLE_NAME)
344
-
345
- try:
346
- await self.table_svc.create_table(self._FEEDBACK_TBL_NAME)
347
- logger.info("Created Azure Table '%s'", self._FEEDBACK_TBL_NAME)
348
- except ResourceExistsError:
349
- logger.debug("Azure Table '%s' already exists", self._FEEDBACK_TBL_NAME)
350
-
351
- # 2. Blob container --------------------------------------------------
352
- try:
353
- await self.blob_svc.create_container(self._CONTAINER_NAME)
354
- logger.info("Created Blob container '%s'", self._CONTAINER_NAME)
355
- except ResourceExistsError:
356
- logger.debug("Blob container '%s' already exists", self._CONTAINER_NAME)
357
-
358
- # ------------------------------------------------------------- save_config
359
- async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
360
- """Upload YAML to Blob, then upsert table row containing the blob name."""
361
- blob_name = f"{config.share_id}.yaml"
362
- blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
363
-
364
- # 1. Upload flock_definition (overwrite in case of retry)
365
- await blob_client.upload_blob(config.flock_definition,
366
- overwrite=True,
367
- content_type="text/yaml")
368
- logger.debug("Uploaded blob '%s' (%d bytes)",
369
- blob_name, len(config.flock_definition.encode()))
370
-
371
- # 2. Persist lightweight record in the table
372
- tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
373
- entity = {
374
- "PartitionKey": self._PARTITION_KEY,
375
- "RowKey": config.share_id,
376
- "agent_name": config.agent_name,
377
- "created_at": config.created_at.isoformat(),
378
- "share_type": config.share_type,
379
- "chat_message_key": config.chat_message_key,
380
- "chat_history_key": config.chat_history_key,
381
- "chat_response_key": config.chat_response_key,
382
- # NEW – just a few bytes, well under 64 KiB
383
- "flock_blob_name": blob_name,
384
- }
385
- await tbl_client.upsert_entity(entity)
386
- logger.info("Saved shared link %s → blob '%s'", config.share_id, blob_name)
387
- return config
388
-
389
- # -------------------------------------------------------------- get_config
390
- async def get_config(self, share_id: str) -> SharedLinkConfig | None:
391
- tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
392
- try:
393
- entity = await tbl_client.get_entity(self._PARTITION_KEY, share_id)
394
- except ResourceNotFoundError:
395
- logger.debug("No config entity for id '%s'", share_id)
396
- return None
397
-
398
- blob_name = entity["flock_blob_name"]
399
- blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
400
- try:
401
- blob_bytes = await (await blob_client.download_blob()).readall()
402
- flock_yaml = blob_bytes.decode()
403
- except Exception as e:
404
- logger.error("Cannot download blob '%s' for share_id=%s: %s",
405
- blob_name, share_id, e, exc_info=True)
406
- raise
407
-
408
- return SharedLinkConfig(
409
- share_id = share_id,
410
- agent_name = entity["agent_name"],
411
- created_at = entity["created_at"],
412
- flock_definition = flock_yaml,
413
- share_type = entity.get("share_type", "agent_run"),
414
- chat_message_key = entity.get("chat_message_key"),
415
- chat_history_key = entity.get("chat_history_key"),
416
- chat_response_key = entity.get("chat_response_key"),
417
- )
418
-
419
- # ----------------------------------------------------------- delete_config
420
- async def delete_config(self, share_id: str) -> bool:
421
- tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
422
- try:
423
- entity = await tbl_client.get_entity(self._PARTITION_KEY, share_id)
424
- except ResourceNotFoundError:
425
- logger.info("Delete: entity %s not found", share_id)
426
- return False
427
-
428
- # 1. Remove blob (ignore missing blob)
429
- blob_name = entity["flock_blob_name"]
430
- blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
431
- try:
432
- await blob_client.delete_blob(delete_snapshots="include")
433
- logger.debug("Deleted blob '%s'", blob_name)
434
- except ResourceNotFoundError:
435
- logger.warning("Blob '%s' already gone", blob_name)
436
-
437
- # 2. Remove table row
438
- await tbl_client.delete_entity(self._PARTITION_KEY, share_id)
439
- logger.info("Deleted shared link %s and its blob", share_id)
440
- return True
441
-
442
- # -------------------------------------------------------- save_feedback --
443
- async def save_feedback(self, record: FeedbackRecord) -> FeedbackRecord:
444
- """Persist a feedback record. If a flock_definition is present, upload it as a blob and store only a reference in the table row to avoid oversized entities (64 KiB limit)."""
445
- tbl_client = self.table_svc.get_table_client(self._FEEDBACK_TBL_NAME)
446
-
447
- # Core entity fields (avoid dumping the full Pydantic model – too many columns / large value)
448
- entity: dict[str, Any] = {
449
- "PartitionKey": "feedback",
450
- "RowKey": record.feedback_id,
451
- "share_id": record.share_id,
452
- "context_type": record.context_type,
453
- "reason": record.reason,
454
- "expected_response": record.expected_response,
455
- "actual_response": record.actual_response,
456
- "created_at": record.created_at.isoformat(),
457
- }
458
-
459
- # additional sanity check
460
-
461
- if record.flock_name is not None:
462
- entity["flock_name"] = record.flock_name
463
- if record.agent_name is not None:
464
- entity["agent_name"] = record.agent_name
465
-
466
- # ------------------------------------------------------------------ YAML → Blob
467
- if record.flock_definition:
468
- blob_name = f"{record.feedback_id}.yaml"
469
- blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
470
- # Overwrite=true so repeated feedback_id uploads (shouldn't happen) won't error
471
- await blob_client.upload_blob(record.flock_definition,
472
- overwrite=True,
473
- content_type="text/yaml")
474
- entity["flock_blob_name"] = blob_name # lightweight reference only
475
-
476
- # ------------------------------------------------------------------ Table upsert
477
- await tbl_client.upsert_entity(entity)
478
- logger.info("Saved feedback %s%s",
479
- record.feedback_id,
480
- f" → blob '{entity['flock_blob_name']}'" if "flock_blob_name" in entity else "")
481
- return record
482
-
483
- # -------------------------------------------------------- get_feedback --
484
- async def get_feedback(self, id: str) -> FeedbackRecord | None:
485
- """Retrieve a single feedback record from Azure Table Storage."""
486
- tbl_client = self.table_svc.get_table_client(self._FEEDBACK_TBL_NAME)
487
- try:
488
- entity = await tbl_client.get_entity("feedback", id)
489
- except ResourceNotFoundError:
490
- logger.debug("No feedback record found for ID: %s", id)
491
- return None
492
-
493
- # Get flock_definition from blob if it exists
494
- flock_definition = None
495
- if "flock_blob_name" in entity:
496
- blob_name = entity["flock_blob_name"]
497
- blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
498
- try:
499
- blob_bytes = await (await blob_client.download_blob()).readall()
500
- flock_definition = blob_bytes.decode()
501
- except Exception as e:
502
- logger.error("Cannot download blob '%s' for feedback_id=%s: %s",
503
- blob_name, id, e, exc_info=True)
504
- # Continue without flock_definition rather than failing
505
-
506
- return FeedbackRecord(
507
- feedback_id=id,
508
- share_id=entity.get("share_id"),
509
- context_type=entity["context_type"],
510
- reason=entity["reason"],
511
- expected_response=entity.get("expected_response"),
512
- actual_response=entity.get("actual_response"),
513
- flock_name=entity.get("flock_name"),
514
- agent_name=entity.get("agent_name"),
515
- flock_definition=flock_definition,
516
- created_at=entity["created_at"],
517
- )
518
-
519
-
520
- # ------------------------------------------------ get_all_feedback_records --
521
- async def get_all_feedback_records_for_agent(self, agent_name: str) -> list[FeedbackRecord]:
522
- """Retrieve all feedback records from Azure Table Storage for a specific agent."""
523
- tbl_client = self.table_svc.get_table_client(self._FEEDBACK_TBL_NAME)
524
-
525
- # Use Azure Table Storage filtering to only get records for the specified agent
526
- escaped_agent_name = agent_name.replace("'", "''")
527
- filter_query = f"agent_name eq '{escaped_agent_name}'"
528
-
529
- logger.debug(f"Querying feedback records with filter: {filter_query}")
530
-
531
- records = []
532
- try:
533
- async for entity in tbl_client.query_entities(filter_query):
534
- # Get flock_definition from blob if it exists
535
- flock_definition = None
536
- if "flock_blob_name" in entity:
537
- blob_name = entity["flock_blob_name"]
538
- blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
539
- try:
540
- blob_bytes = await (await blob_client.download_blob()).readall()
541
- flock_definition = blob_bytes.decode()
542
- except Exception as e:
543
- logger.error("Cannot download blob '%s' for feedback_id=%s: %s",
544
- blob_name, entity["RowKey"], e, exc_info=True)
545
- # Continue without flock_definition rather than failing
546
-
547
- records.append(FeedbackRecord(
548
- feedback_id=entity["RowKey"],
549
- share_id=entity.get("share_id"),
550
- context_type=entity["context_type"],
551
- reason=entity["reason"],
552
- expected_response=entity.get("expected_response"),
553
- actual_response=entity.get("actual_response"),
554
- flock_name=entity.get("flock_name"),
555
- agent_name=entity.get("agent_name"),
556
- flock_definition=flock_definition,
557
- created_at=entity["created_at"],
558
- ))
559
-
560
- logger.debug("Retrieved %d feedback records for agent %s", len(records), agent_name)
561
- return records
562
-
563
- except Exception as e:
564
- # Log the error.
565
- logger.error(
566
- f"Unable to query entries for agent {agent_name}. Exception: {e}"
567
- )
568
- return records
569
-
570
-
571
- # ----------------------- Factory Function -----------------------
572
-
573
- def create_shared_link_store(store_type: str | None = None, connection_string: str | None = None) -> SharedLinkStoreInterface:
574
- """Factory function to create the appropriate shared link store based on configuration.
575
-
576
- Args:
577
- store_type: Type of store to create ("local" for SQLite, "azure-storage" for Azure Table Storage)
578
- connection_string: Connection string for the store (file path for SQLite, connection string for Azure)
579
-
580
- Returns:
581
- Configured SharedLinkStoreInterface implementation
582
- """
583
- import os
584
-
585
- # Get values from environment if not provided
586
- if store_type is None:
587
- store_type = os.getenv("FLOCK_WEBAPP_STORE", "local").lower()
588
-
589
- if connection_string is None:
590
- connection_string = os.getenv("FLOCK_WEBAPP_STORE_CONNECTION", ".flock/shared_links.db")
591
-
592
- if store_type == "local":
593
- return SQLiteSharedLinkStore(connection_string)
594
- elif store_type == "azure-storage":
595
- return AzureTableSharedLinkStore(connection_string)
596
- else:
597
- raise ValueError(f"Unsupported store type: {store_type}. Supported types: 'local', 'azure-storage'")