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
flock/tools/text_tools.py DELETED
@@ -1,810 +0,0 @@
1
- import hashlib
2
- import json
3
- import re
4
- from collections.abc import Callable
5
- from typing import Any
6
-
7
- import nltk
8
-
9
- from flock.core.logging.trace_and_logged import traced_and_logged
10
-
11
- # Ensure NLTK data is downloaded
12
- try:
13
- nltk.data.find("tokenizers/punkt")
14
- except LookupError:
15
- nltk.download("punkt")
16
-
17
- try:
18
- nltk.data.find("corpora/stopwords")
19
- except LookupError:
20
- nltk.download("stopwords")
21
-
22
-
23
- @traced_and_logged
24
- def text_split_by_sentences(text: str) -> list[str]:
25
- return nltk.sent_tokenize(text)
26
-
27
-
28
- @traced_and_logged
29
- def text_split_by_characters(
30
- text: str, chunk_size: int = 4000, overlap: int = 200
31
- ) -> list[str]:
32
- if chunk_size <= 0:
33
- raise ValueError("chunk_size must be positive")
34
-
35
- if overlap >= chunk_size:
36
- raise ValueError("overlap must be smaller than chunk_size")
37
-
38
- if not text:
39
- return []
40
-
41
- chunks = []
42
- start = 0
43
- text_length = len(text)
44
-
45
- while start < text_length:
46
- end = min(start + chunk_size, text_length)
47
-
48
- # If we're not at the end and the next character isn't a space, try to find a suitable break point
49
- if end < text_length and text[end] not in [
50
- " ",
51
- "\n",
52
- ".",
53
- ",",
54
- "!",
55
- "?",
56
- ";",
57
- ":",
58
- "-",
59
- ]:
60
- # Look for the last occurrence of a good break character
61
- break_chars = [" ", "\n", ".", ",", "!", "?", ";", ":", "-"]
62
- for i in range(end, max(start, end - 100), -1):
63
- if text[i] in break_chars:
64
- end = i + 1 # Include the break character
65
- break
66
-
67
- chunks.append(text[start:end])
68
- start = end - overlap if end < text_length else text_length
69
-
70
- return chunks
71
-
72
-
73
- @traced_and_logged
74
- def text_split_by_tokens(
75
- text: str,
76
- tokenizer: Callable[[str], list[str]],
77
- max_tokens: int = 1024,
78
- overlap_tokens: int = 100,
79
- ) -> list[str]:
80
- tokens = tokenizer(text)
81
- chunks = []
82
-
83
- i = 0
84
- while i < len(tokens):
85
- chunk = tokens[i : i + max_tokens]
86
- chunks.append("".join(chunk))
87
- i += max_tokens - overlap_tokens
88
-
89
- return chunks
90
-
91
-
92
- @traced_and_logged
93
- def text_split_by_separator(text: str, separator: str = "\n\n") -> list[str]:
94
- if not text:
95
- return []
96
-
97
- chunks = text.split(separator)
98
- return [chunk for chunk in chunks if chunk.strip()]
99
-
100
-
101
- @traced_and_logged
102
- def text_recursive_splitter(
103
- text: str,
104
- chunk_size: int = 4000,
105
- separators: list[str] = ["\n\n", "\n", ". ", ", ", " ", ""],
106
- keep_separator: bool = True,
107
- ) -> list[str]:
108
- if not text:
109
- return []
110
-
111
- if len(text) <= chunk_size:
112
- return [text]
113
-
114
- if not separators:
115
- return [
116
- text[:chunk_size],
117
- *text_recursive_splitter(text[chunk_size:], chunk_size, separators),
118
- ]
119
-
120
- separator = separators[0]
121
- new_separators = separators[1:]
122
-
123
- if separator == "":
124
- # If we're at the character level, just split by characters
125
- return text_split_by_characters(text, chunk_size=chunk_size, overlap=0)
126
-
127
- splits = text.split(separator)
128
- separator_len = len(separator) if keep_separator else 0
129
-
130
- # Add separator back to the chunks if needed
131
- if keep_separator and separator:
132
- splits = [f"{split}{separator}" for split in splits[:-1]] + [splits[-1]]
133
-
134
- # Process each split
135
- result = []
136
- current_chunk = []
137
- current_length = 0
138
-
139
- for split in splits:
140
- split_len = len(split)
141
-
142
- if split_len > chunk_size:
143
- # If current split is too large, handle current chunk and recursively split this large piece
144
- if current_chunk:
145
- result.append("".join(current_chunk))
146
- current_chunk = []
147
- current_length = 0
148
-
149
- # Recursively split this large piece
150
- smaller_chunks = text_recursive_splitter(
151
- split, chunk_size, new_separators, keep_separator
152
- )
153
- result.extend(smaller_chunks)
154
- elif current_length + split_len <= chunk_size:
155
- # If we can fit this split in the current chunk, add it
156
- current_chunk.append(split)
157
- current_length += split_len
158
- else:
159
- # If we can't fit this split, complete the current chunk and start a new one
160
- result.append("".join(current_chunk))
161
- current_chunk = [split]
162
- current_length = split_len
163
-
164
- # Don't forget the last chunk
165
- if current_chunk:
166
- result.append("".join(current_chunk))
167
-
168
- return result
169
-
170
-
171
- @traced_and_logged
172
- def text_chunking_for_embedding(
173
- text: str, file_name: str, chunk_size: int = 1000, overlap: int = 100
174
- ) -> list[dict[str, Any]]:
175
- chunks = text_split_by_characters(text, chunk_size=chunk_size, overlap=overlap)
176
-
177
- # Create metadata for each chunk
178
- result = []
179
- for i, chunk in enumerate(chunks):
180
- result.append(
181
- {
182
- "chunk_id": file_name + "_" + str(i),
183
- "text": chunk,
184
- "file": file_name,
185
- "total_chunks": len(chunks),
186
- }
187
- )
188
-
189
- return result
190
-
191
-
192
- @traced_and_logged
193
- def text_split_code_by_functions(code: str) -> list[dict[str, Any]]:
194
- if not code:
195
- return []
196
-
197
- # Basic pattern for Python functions
198
- function_pattern = re.compile(
199
- r"(^|\n)def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\((.*?)\)(?:\s*->.*?)?:"
200
- )
201
- matches = list(function_pattern.finditer(code))
202
-
203
- if not matches:
204
- return [{"name": "Main", "content": code, "type": "code"}]
205
-
206
- functions = []
207
-
208
- # Process each function
209
- for i in range(len(matches)):
210
- current_match = matches[i]
211
- function_name = current_match.group(2)
212
-
213
- # Determine function content
214
- if i < len(matches) - 1:
215
- next_function_start = matches[i + 1].start()
216
- content = code[current_match.start() : next_function_start]
217
- else:
218
- content = code[current_match.start() :]
219
-
220
- functions.append(
221
- {
222
- "name": function_name,
223
- "content": content.strip(),
224
- "type": "function",
225
- }
226
- )
227
-
228
- # Check if there's content before the first function
229
- if matches[0].start() > 0:
230
- preamble = code[: matches[0].start()].strip()
231
- if preamble:
232
- functions.insert(
233
- 0,
234
- {"name": "Imports/Setup", "content": preamble, "type": "code"},
235
- )
236
-
237
- return functions
238
-
239
-
240
- @traced_and_logged
241
- def text_count_tokens(text: str, model: str = "gpt-3.5-turbo") -> int:
242
- """Count tokens using tiktoken."""
243
- if not text:
244
- return 0
245
-
246
- try:
247
- import tiktoken
248
-
249
- # Map model names to encoding types
250
- if model.startswith(("gpt-4", "gpt-3.5")):
251
- encoding_name = "cl100k_base" # For newer OpenAI models
252
- elif model.startswith("text-davinci"):
253
- encoding_name = "p50k_base" # For older OpenAI models
254
- elif "llama" in model.lower() or "mistral" in model.lower():
255
- encoding_name = (
256
- "cl100k_base" # Best approximation for LLaMA/Mistral
257
- )
258
- else:
259
- # Default to cl100k_base as fallback
260
- encoding_name = "cl100k_base"
261
-
262
- # Try to get the specific encoder for the model if available
263
- try:
264
- encoding = tiktoken.encoding_for_model(model)
265
- except KeyError:
266
- # Fall back to the encoding name
267
- encoding = tiktoken.get_encoding(encoding_name)
268
-
269
- # Count tokens
270
- token_integers = encoding.encode(text)
271
- return len(token_integers)
272
-
273
- except ImportError:
274
- # Fallback to character-based estimation if tiktoken is not installed
275
- return text_count_tokens_estimate(text, model)
276
-
277
-
278
- @traced_and_logged
279
- def text_count_tokens_estimate(text: str, model: str = "gpt-3.5-turbo") -> int:
280
- """Estimate token count for different models."""
281
- if not text:
282
- return 0
283
-
284
- # Rough token estimations for different models
285
- if model.startswith(("gpt-3", "gpt-4")):
286
- # OpenAI models: ~4 chars per token
287
- return len(text) // 4 + 1
288
- elif model.startswith("claude"):
289
- # Anthropic models: ~3.5 chars per token
290
- return len(text) // 3.5 + 1
291
- elif "llama" in model.lower():
292
- # LLaMA-based models: ~3.7 chars per token
293
- return len(text) // 3.7 + 1
294
- else:
295
- # Default estimation
296
- return len(text) // 4 + 1
297
-
298
-
299
- @traced_and_logged
300
- def text_truncate_to_token_limit(
301
- text: str, max_tokens: int = 4000, model: str = "gpt-3.5-turbo"
302
- ) -> str:
303
- if not text:
304
- return ""
305
-
306
- # Try to use tiktoken for accurate truncation
307
- try:
308
- import tiktoken
309
-
310
- # Get appropriate encoding
311
- try:
312
- encoding = tiktoken.encoding_for_model(model)
313
- except KeyError:
314
- # Fall back to cl100k_base (used by most newer models)
315
- encoding = tiktoken.get_encoding("cl100k_base")
316
-
317
- # Encode the text to tokens
318
- tokens = encoding.encode(text)
319
-
320
- # If we're already under the limit, return the original text
321
- if len(tokens) <= max_tokens:
322
- return text
323
-
324
- # Truncate tokens and decode back to text
325
- truncated_tokens = tokens[:max_tokens]
326
- return encoding.decode(truncated_tokens)
327
-
328
- except ImportError:
329
- # Fallback to the character-based method if tiktoken is not available
330
- estimated_tokens = text_count_tokens_estimate(text, model)
331
-
332
- if estimated_tokens <= max_tokens:
333
- return text
334
-
335
- # Calculate approximate character limit
336
- char_per_token = 4 # Default for most models
337
- if model.startswith("claude"):
338
- char_per_token = 3.5
339
- elif "llama" in model.lower():
340
- char_per_token = 3.7
341
-
342
- char_limit = int(max_tokens * char_per_token)
343
-
344
- # Try to find a good breaking point
345
- if char_limit < len(text):
346
- # Look for sentence or paragraph break near the limit
347
- for i in range(char_limit - 1, max(0, char_limit - 100), -1):
348
- if i < len(text) and text[i] in [".", "!", "?", "\n"]:
349
- return text[: i + 1]
350
-
351
- # Fallback to hard truncation
352
- return text[:char_limit]
353
-
354
-
355
- @traced_and_logged
356
- def text_extract_keywords(text: str, top_n: int = 10) -> list[str]:
357
- if not text:
358
- return []
359
-
360
- # Get stopwords
361
- try:
362
- from nltk.corpus import stopwords
363
-
364
- stop_words = set(stopwords.words("english"))
365
- except:
366
- # Fallback basic stopwords if NLTK data isn't available
367
- stop_words = {
368
- "i",
369
- "me",
370
- "my",
371
- "myself",
372
- "we",
373
- "our",
374
- "ours",
375
- "ourselves",
376
- "you",
377
- "you're",
378
- "you've",
379
- "you'll",
380
- "you'd",
381
- "your",
382
- "yours",
383
- "yourself",
384
- "yourselves",
385
- "he",
386
- "him",
387
- "his",
388
- "himself",
389
- "she",
390
- "she's",
391
- "her",
392
- "hers",
393
- "herself",
394
- "it",
395
- "it's",
396
- "its",
397
- "itself",
398
- "they",
399
- "them",
400
- "their",
401
- "theirs",
402
- "themselves",
403
- "what",
404
- "which",
405
- "who",
406
- "whom",
407
- "this",
408
- "that",
409
- "that'll",
410
- "these",
411
- "those",
412
- "am",
413
- "is",
414
- "are",
415
- "was",
416
- "were",
417
- "be",
418
- "been",
419
- "being",
420
- "have",
421
- "has",
422
- "had",
423
- "having",
424
- "do",
425
- "does",
426
- "did",
427
- "doing",
428
- "a",
429
- "an",
430
- "the",
431
- "and",
432
- "but",
433
- "if",
434
- "or",
435
- "because",
436
- "as",
437
- "until",
438
- "while",
439
- "of",
440
- "at",
441
- "by",
442
- "for",
443
- "with",
444
- "about",
445
- "against",
446
- "between",
447
- "into",
448
- "through",
449
- "during",
450
- "before",
451
- "after",
452
- "above",
453
- "below",
454
- "to",
455
- "from",
456
- "up",
457
- "down",
458
- "in",
459
- "out",
460
- "on",
461
- "off",
462
- "over",
463
- "under",
464
- "again",
465
- "further",
466
- "then",
467
- "once",
468
- }
469
-
470
- # Tokenize and remove punctuation
471
- words = re.findall(r"\b[a-zA-Z]{3,}\b", text.lower())
472
-
473
- # Remove stopwords
474
- words = [word for word in words if word not in stop_words]
475
-
476
- # Count word frequencies
477
- word_freq = {}
478
- for word in words:
479
- if word in word_freq:
480
- word_freq[word] += 1
481
- else:
482
- word_freq[word] = 1
483
-
484
- # Sort by frequency
485
- sorted_words = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)
486
-
487
- # Return top N keywords
488
- return [word for word, freq in sorted_words[:top_n]]
489
-
490
-
491
- @traced_and_logged
492
- def text_clean_text(
493
- text: str,
494
- remove_urls: bool = True,
495
- remove_html: bool = True,
496
- normalize_whitespace: bool = True,
497
- ) -> str:
498
- if not text:
499
- return ""
500
-
501
- result = text
502
-
503
- # Remove URLs
504
- if remove_urls:
505
- result = re.sub(r"https?://\S+|www\.\S+", "", result)
506
-
507
- # Remove HTML tags
508
- if remove_html:
509
- result = re.sub(r"<.*?>", "", result)
510
-
511
- # Normalize whitespace
512
- if normalize_whitespace:
513
- # Replace multiple spaces, tabs, newlines with a single space
514
- result = re.sub(r"\s+", " ", result)
515
- result = result.strip()
516
-
517
- return result
518
-
519
-
520
- @traced_and_logged
521
- def text_format_chat_history(
522
- messages: list[dict[str, str]],
523
- format_type: str = "text",
524
- system_prefix: str = "System: ",
525
- user_prefix: str = "User: ",
526
- assistant_prefix: str = "Assistant: ",
527
- ) -> str:
528
- if not messages:
529
- return ""
530
-
531
- result = []
532
-
533
- if format_type == "text":
534
- for msg in messages:
535
- role = msg.get("role", "").lower()
536
- content = msg.get("content", "")
537
-
538
- if role == "system":
539
- result.append(f"{system_prefix}{content}")
540
- elif role == "user":
541
- result.append(f"{user_prefix}{content}")
542
- elif role == "assistant":
543
- result.append(f"{assistant_prefix}{content}")
544
- else:
545
- result.append(f"{role.capitalize()}: {content}")
546
-
547
- return "\n\n".join(result)
548
-
549
- elif format_type == "markdown":
550
- for msg in messages:
551
- role = msg.get("role", "").lower()
552
- content = msg.get("content", "")
553
-
554
- if role == "system":
555
- result.append(f"**{system_prefix.strip()}** {content}")
556
- elif role == "user":
557
- result.append(f"**{user_prefix.strip()}** {content}")
558
- elif role == "assistant":
559
- result.append(f"**{assistant_prefix.strip()}** {content}")
560
- else:
561
- result.append(f"**{role.capitalize()}:** {content}")
562
-
563
- return "\n\n".join(result)
564
-
565
- else:
566
- raise ValueError(f"Unsupported format type: {format_type}")
567
-
568
-
569
- @traced_and_logged
570
- def text_extract_json_from_text(text: str) -> dict[str, Any] | None:
571
- if not text:
572
- return None
573
-
574
- # Find JSON-like patterns between curly braces
575
- json_pattern = re.compile(r"({[\s\S]*?})")
576
- json_matches = json_pattern.findall(text)
577
-
578
- # Try to parse each match
579
- for json_str in json_matches:
580
- try:
581
- return json.loads(json_str)
582
- except json.JSONDecodeError:
583
- continue
584
-
585
- # Try to find JSON with markdown code blocks
586
- code_block_pattern = re.compile(r"```(?:json)?\s*([\s\S]*?)\s*```")
587
- code_blocks = code_block_pattern.findall(text)
588
-
589
- for block in code_blocks:
590
- # Clean up any trailing ``` that might have been captured
591
- block = block.replace("```", "")
592
- try:
593
- return json.loads(block)
594
- except json.JSONDecodeError:
595
- continue
596
-
597
- # No valid JSON found
598
- return None
599
-
600
-
601
- @traced_and_logged
602
- def text_calculate_hash(text: str, algorithm: str = "sha256") -> str:
603
- if not text:
604
- return ""
605
-
606
- if algorithm == "md5":
607
- return hashlib.md5(text.encode()).hexdigest()
608
- elif algorithm == "sha1":
609
- return hashlib.sha1(text.encode()).hexdigest()
610
- elif algorithm == "sha256":
611
- return hashlib.sha256(text.encode()).hexdigest()
612
- else:
613
- raise ValueError(f"Unsupported hash algorithm: {algorithm}")
614
-
615
-
616
- @traced_and_logged
617
- def text_format_table_from_dicts(data: list[dict[str, Any]]) -> str:
618
- if not data:
619
- return ""
620
-
621
- # Extract all possible keys
622
- keys = set()
623
- for item in data:
624
- keys.update(item.keys())
625
-
626
- # Convert to list and sort for consistent output
627
- keys = sorted(list(keys))
628
-
629
- # Calculate column widths
630
- widths = {key: len(key) for key in keys}
631
- for item in data:
632
- for key in keys:
633
- if key in item:
634
- value_str = str(item[key])
635
- widths[key] = max(widths[key], len(value_str))
636
-
637
- # Create header
638
- header = " | ".join(f"{key:{widths[key]}}" for key in keys)
639
- separator = "-+-".join("-" * widths[key] for key in keys)
640
-
641
- # Create rows
642
- rows = []
643
- for item in data:
644
- row = " | ".join(f"{item.get(key, '')!s:{widths[key]}}" for key in keys)
645
- rows.append(row)
646
-
647
- # Combine everything
648
- return f"{header}\n{separator}\n" + "\n".join(rows)
649
-
650
-
651
- @traced_and_logged
652
- def text_detect_language(text: str) -> str:
653
- """Simple language detection"""
654
- if not text or len(text.strip()) < 10:
655
- return "unknown"
656
-
657
- try:
658
- # Try to use langdetect if available
659
- from langdetect import detect
660
-
661
- return detect(text)
662
- except ImportError:
663
- # Fallback to simple detection based on character frequency
664
- # This is very simplistic and only works for a few common languages
665
- text = text.lower()
666
-
667
- # Count character frequencies that may indicate certain languages
668
- special_chars = {
669
- "á": 0,
670
- "é": 0,
671
- "í": 0,
672
- "ó": 0,
673
- "ú": 0,
674
- "ü": 0,
675
- "ñ": 0, # Spanish
676
- "ä": 0,
677
- "ö": 0,
678
- "ß": 0, # German
679
- "ç": 0,
680
- "à": 0,
681
- "è": 0,
682
- "ù": 0, # French
683
- "å": 0,
684
- "ø": 0, # Nordic
685
- "й": 0,
686
- "ы": 0,
687
- "ъ": 0,
688
- "э": 0, # Russian/Cyrillic
689
- "的": 0,
690
- "是": 0,
691
- "在": 0, # Chinese
692
- "の": 0,
693
- "は": 0,
694
- "で": 0, # Japanese
695
- "한": 0,
696
- "국": 0,
697
- "어": 0, # Korean
698
- }
699
-
700
- for char in text:
701
- if char in special_chars:
702
- special_chars[char] += 1
703
-
704
- # Detect based on character frequencies
705
- spanish = sum(
706
- special_chars[c] for c in ["á", "é", "í", "ó", "ú", "ü", "ñ"]
707
- )
708
- german = sum(special_chars[c] for c in ["ä", "ö", "ß"])
709
- french = sum(special_chars[c] for c in ["ç", "à", "è", "ù"])
710
- nordic = sum(special_chars[c] for c in ["å", "ø"])
711
- russian = sum(special_chars[c] for c in ["й", "ы", "ъ", "э"])
712
- chinese = sum(special_chars[c] for c in ["的", "是", "在"])
713
- japanese = sum(special_chars[c] for c in ["の", "は", "で"])
714
- korean = sum(special_chars[c] for c in ["한", "국", "어"])
715
-
716
- scores = {
717
- "es": spanish,
718
- "de": german,
719
- "fr": french,
720
- "no": nordic,
721
- "ru": russian,
722
- "zh": chinese,
723
- "ja": japanese,
724
- "ko": korean,
725
- }
726
-
727
- # If we have a clear signal from special characters
728
- max_score = max(scores.values())
729
- if max_score > 0:
730
- return max(scores, key=scores.get)
731
-
732
- # Otherwise assume English (very simplistic)
733
- return "en"
734
-
735
-
736
- @traced_and_logged
737
- def text_tiktoken_split(
738
- text: str,
739
- model: str = "gpt-3.5-turbo",
740
- chunk_size: int = 1000,
741
- overlap: int = 50,
742
- ) -> list[str]:
743
- """Split text based on tiktoken tokens with proper overlap handling."""
744
- if not text:
745
- return []
746
-
747
- try:
748
- import tiktoken
749
-
750
- try:
751
- encoding = tiktoken.encoding_for_model(model)
752
- except KeyError:
753
- encoding = tiktoken.get_encoding("cl100k_base")
754
-
755
- # Encode the text to tokens
756
- tokens = encoding.encode(text)
757
- total_tokens = len(tokens)
758
-
759
- # Check if we need to split at all
760
- if total_tokens <= chunk_size:
761
- return [text]
762
-
763
- # Create chunks with overlap
764
- chunks = []
765
- start_idx = 0
766
-
767
- while start_idx < total_tokens:
768
- # Define the end of this chunk
769
- end_idx = min(start_idx + chunk_size, total_tokens)
770
-
771
- # Decode this chunk of tokens back to text
772
- chunk_tokens = tokens[start_idx:end_idx]
773
- chunk_text = encoding.decode(chunk_tokens)
774
- chunks.append(chunk_text)
775
-
776
- # Move to the next chunk, accounting for overlap
777
- start_idx += chunk_size - overlap
778
-
779
- # Avoid tiny final chunks
780
- if start_idx < total_tokens and start_idx + overlap >= total_tokens:
781
- break
782
-
783
- return chunks
784
- except ImportError:
785
- # Fallback to character-based chunking if tiktoken is not available
786
- return text_split_by_characters(
787
- text, chunk_size=chunk_size * 4, overlap=overlap * 4
788
- )
789
-
790
-
791
- @traced_and_logged
792
- def text_count_words(text: str) -> int:
793
- if not text:
794
- return 0
795
- return len(text.split())
796
-
797
-
798
- @traced_and_logged
799
- def text_extract_urls(text: str) -> list[str]:
800
- if not text:
801
- return []
802
- # A more robust regex might be needed for complex cases
803
- return re.findall(r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+", text)
804
-
805
-
806
- @traced_and_logged
807
- def text_extract_numbers(text: str) -> list[float]:
808
- if not text:
809
- return []
810
- return [float(num) for num in re.findall(r"[-+]?\d*\.?\d+", text)]