PraisonAI 3.0.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.
Files changed (393) hide show
  1. praisonai/__init__.py +54 -0
  2. praisonai/__main__.py +15 -0
  3. praisonai/acp/__init__.py +54 -0
  4. praisonai/acp/config.py +159 -0
  5. praisonai/acp/server.py +587 -0
  6. praisonai/acp/session.py +219 -0
  7. praisonai/adapters/__init__.py +50 -0
  8. praisonai/adapters/readers.py +395 -0
  9. praisonai/adapters/rerankers.py +315 -0
  10. praisonai/adapters/retrievers.py +394 -0
  11. praisonai/adapters/vector_stores.py +409 -0
  12. praisonai/agent_scheduler.py +337 -0
  13. praisonai/agents_generator.py +903 -0
  14. praisonai/api/call.py +292 -0
  15. praisonai/auto.py +1197 -0
  16. praisonai/capabilities/__init__.py +275 -0
  17. praisonai/capabilities/a2a.py +140 -0
  18. praisonai/capabilities/assistants.py +283 -0
  19. praisonai/capabilities/audio.py +320 -0
  20. praisonai/capabilities/batches.py +469 -0
  21. praisonai/capabilities/completions.py +336 -0
  22. praisonai/capabilities/container_files.py +155 -0
  23. praisonai/capabilities/containers.py +93 -0
  24. praisonai/capabilities/embeddings.py +158 -0
  25. praisonai/capabilities/files.py +467 -0
  26. praisonai/capabilities/fine_tuning.py +293 -0
  27. praisonai/capabilities/guardrails.py +182 -0
  28. praisonai/capabilities/images.py +330 -0
  29. praisonai/capabilities/mcp.py +190 -0
  30. praisonai/capabilities/messages.py +270 -0
  31. praisonai/capabilities/moderations.py +154 -0
  32. praisonai/capabilities/ocr.py +217 -0
  33. praisonai/capabilities/passthrough.py +204 -0
  34. praisonai/capabilities/rag.py +207 -0
  35. praisonai/capabilities/realtime.py +160 -0
  36. praisonai/capabilities/rerank.py +165 -0
  37. praisonai/capabilities/responses.py +266 -0
  38. praisonai/capabilities/search.py +109 -0
  39. praisonai/capabilities/skills.py +133 -0
  40. praisonai/capabilities/vector_store_files.py +334 -0
  41. praisonai/capabilities/vector_stores.py +304 -0
  42. praisonai/capabilities/videos.py +141 -0
  43. praisonai/chainlit_ui.py +304 -0
  44. praisonai/chat/__init__.py +106 -0
  45. praisonai/chat/app.py +125 -0
  46. praisonai/cli/__init__.py +26 -0
  47. praisonai/cli/app.py +213 -0
  48. praisonai/cli/commands/__init__.py +75 -0
  49. praisonai/cli/commands/acp.py +70 -0
  50. praisonai/cli/commands/completion.py +333 -0
  51. praisonai/cli/commands/config.py +166 -0
  52. praisonai/cli/commands/debug.py +142 -0
  53. praisonai/cli/commands/diag.py +55 -0
  54. praisonai/cli/commands/doctor.py +166 -0
  55. praisonai/cli/commands/environment.py +179 -0
  56. praisonai/cli/commands/lsp.py +112 -0
  57. praisonai/cli/commands/mcp.py +210 -0
  58. praisonai/cli/commands/profile.py +457 -0
  59. praisonai/cli/commands/run.py +228 -0
  60. praisonai/cli/commands/schedule.py +150 -0
  61. praisonai/cli/commands/serve.py +97 -0
  62. praisonai/cli/commands/session.py +212 -0
  63. praisonai/cli/commands/traces.py +145 -0
  64. praisonai/cli/commands/version.py +101 -0
  65. praisonai/cli/configuration/__init__.py +18 -0
  66. praisonai/cli/configuration/loader.py +353 -0
  67. praisonai/cli/configuration/paths.py +114 -0
  68. praisonai/cli/configuration/schema.py +164 -0
  69. praisonai/cli/features/__init__.py +268 -0
  70. praisonai/cli/features/acp.py +236 -0
  71. praisonai/cli/features/action_orchestrator.py +546 -0
  72. praisonai/cli/features/agent_scheduler.py +773 -0
  73. praisonai/cli/features/agent_tools.py +474 -0
  74. praisonai/cli/features/agents.py +375 -0
  75. praisonai/cli/features/at_mentions.py +471 -0
  76. praisonai/cli/features/auto_memory.py +182 -0
  77. praisonai/cli/features/autonomy_mode.py +490 -0
  78. praisonai/cli/features/background.py +356 -0
  79. praisonai/cli/features/base.py +168 -0
  80. praisonai/cli/features/capabilities.py +1326 -0
  81. praisonai/cli/features/checkpoints.py +338 -0
  82. praisonai/cli/features/code_intelligence.py +652 -0
  83. praisonai/cli/features/compaction.py +294 -0
  84. praisonai/cli/features/compare.py +534 -0
  85. praisonai/cli/features/cost_tracker.py +514 -0
  86. praisonai/cli/features/debug.py +810 -0
  87. praisonai/cli/features/deploy.py +517 -0
  88. praisonai/cli/features/diag.py +289 -0
  89. praisonai/cli/features/doctor/__init__.py +63 -0
  90. praisonai/cli/features/doctor/checks/__init__.py +24 -0
  91. praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
  92. praisonai/cli/features/doctor/checks/config_checks.py +366 -0
  93. praisonai/cli/features/doctor/checks/db_checks.py +366 -0
  94. praisonai/cli/features/doctor/checks/env_checks.py +543 -0
  95. praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
  96. praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
  97. praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
  98. praisonai/cli/features/doctor/checks/network_checks.py +251 -0
  99. praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
  100. praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
  101. praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
  102. praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
  103. praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
  104. praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
  105. praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
  106. praisonai/cli/features/doctor/engine.py +266 -0
  107. praisonai/cli/features/doctor/formatters.py +310 -0
  108. praisonai/cli/features/doctor/handler.py +397 -0
  109. praisonai/cli/features/doctor/models.py +264 -0
  110. praisonai/cli/features/doctor/registry.py +239 -0
  111. praisonai/cli/features/endpoints.py +1019 -0
  112. praisonai/cli/features/eval.py +560 -0
  113. praisonai/cli/features/external_agents.py +231 -0
  114. praisonai/cli/features/fast_context.py +410 -0
  115. praisonai/cli/features/flow_display.py +566 -0
  116. praisonai/cli/features/git_integration.py +651 -0
  117. praisonai/cli/features/guardrail.py +171 -0
  118. praisonai/cli/features/handoff.py +185 -0
  119. praisonai/cli/features/hooks.py +583 -0
  120. praisonai/cli/features/image.py +384 -0
  121. praisonai/cli/features/interactive_runtime.py +585 -0
  122. praisonai/cli/features/interactive_tools.py +380 -0
  123. praisonai/cli/features/interactive_tui.py +603 -0
  124. praisonai/cli/features/jobs.py +632 -0
  125. praisonai/cli/features/knowledge.py +531 -0
  126. praisonai/cli/features/lite.py +244 -0
  127. praisonai/cli/features/lsp_cli.py +225 -0
  128. praisonai/cli/features/mcp.py +169 -0
  129. praisonai/cli/features/message_queue.py +587 -0
  130. praisonai/cli/features/metrics.py +211 -0
  131. praisonai/cli/features/n8n.py +673 -0
  132. praisonai/cli/features/observability.py +293 -0
  133. praisonai/cli/features/ollama.py +361 -0
  134. praisonai/cli/features/output_style.py +273 -0
  135. praisonai/cli/features/package.py +631 -0
  136. praisonai/cli/features/performance.py +308 -0
  137. praisonai/cli/features/persistence.py +636 -0
  138. praisonai/cli/features/profile.py +226 -0
  139. praisonai/cli/features/profiler/__init__.py +81 -0
  140. praisonai/cli/features/profiler/core.py +558 -0
  141. praisonai/cli/features/profiler/optimizations.py +652 -0
  142. praisonai/cli/features/profiler/suite.py +386 -0
  143. praisonai/cli/features/profiling.py +350 -0
  144. praisonai/cli/features/queue/__init__.py +73 -0
  145. praisonai/cli/features/queue/manager.py +395 -0
  146. praisonai/cli/features/queue/models.py +286 -0
  147. praisonai/cli/features/queue/persistence.py +564 -0
  148. praisonai/cli/features/queue/scheduler.py +484 -0
  149. praisonai/cli/features/queue/worker.py +372 -0
  150. praisonai/cli/features/recipe.py +1723 -0
  151. praisonai/cli/features/recipes.py +449 -0
  152. praisonai/cli/features/registry.py +229 -0
  153. praisonai/cli/features/repo_map.py +860 -0
  154. praisonai/cli/features/router.py +466 -0
  155. praisonai/cli/features/sandbox_executor.py +515 -0
  156. praisonai/cli/features/serve.py +829 -0
  157. praisonai/cli/features/session.py +222 -0
  158. praisonai/cli/features/skills.py +856 -0
  159. praisonai/cli/features/slash_commands.py +650 -0
  160. praisonai/cli/features/telemetry.py +179 -0
  161. praisonai/cli/features/templates.py +1384 -0
  162. praisonai/cli/features/thinking.py +305 -0
  163. praisonai/cli/features/todo.py +334 -0
  164. praisonai/cli/features/tools.py +680 -0
  165. praisonai/cli/features/tui/__init__.py +83 -0
  166. praisonai/cli/features/tui/app.py +580 -0
  167. praisonai/cli/features/tui/cli.py +566 -0
  168. praisonai/cli/features/tui/debug.py +511 -0
  169. praisonai/cli/features/tui/events.py +99 -0
  170. praisonai/cli/features/tui/mock_provider.py +328 -0
  171. praisonai/cli/features/tui/orchestrator.py +652 -0
  172. praisonai/cli/features/tui/screens/__init__.py +50 -0
  173. praisonai/cli/features/tui/screens/main.py +245 -0
  174. praisonai/cli/features/tui/screens/queue.py +174 -0
  175. praisonai/cli/features/tui/screens/session.py +124 -0
  176. praisonai/cli/features/tui/screens/settings.py +148 -0
  177. praisonai/cli/features/tui/widgets/__init__.py +56 -0
  178. praisonai/cli/features/tui/widgets/chat.py +261 -0
  179. praisonai/cli/features/tui/widgets/composer.py +224 -0
  180. praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
  181. praisonai/cli/features/tui/widgets/status.py +167 -0
  182. praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
  183. praisonai/cli/features/workflow.py +720 -0
  184. praisonai/cli/legacy.py +236 -0
  185. praisonai/cli/main.py +5559 -0
  186. praisonai/cli/schedule_cli.py +54 -0
  187. praisonai/cli/state/__init__.py +31 -0
  188. praisonai/cli/state/identifiers.py +161 -0
  189. praisonai/cli/state/sessions.py +313 -0
  190. praisonai/code/__init__.py +93 -0
  191. praisonai/code/agent_tools.py +344 -0
  192. praisonai/code/diff/__init__.py +21 -0
  193. praisonai/code/diff/diff_strategy.py +432 -0
  194. praisonai/code/tools/__init__.py +27 -0
  195. praisonai/code/tools/apply_diff.py +221 -0
  196. praisonai/code/tools/execute_command.py +275 -0
  197. praisonai/code/tools/list_files.py +274 -0
  198. praisonai/code/tools/read_file.py +206 -0
  199. praisonai/code/tools/search_replace.py +248 -0
  200. praisonai/code/tools/write_file.py +217 -0
  201. praisonai/code/utils/__init__.py +46 -0
  202. praisonai/code/utils/file_utils.py +307 -0
  203. praisonai/code/utils/ignore_utils.py +308 -0
  204. praisonai/code/utils/text_utils.py +276 -0
  205. praisonai/db/__init__.py +64 -0
  206. praisonai/db/adapter.py +531 -0
  207. praisonai/deploy/__init__.py +62 -0
  208. praisonai/deploy/api.py +231 -0
  209. praisonai/deploy/docker.py +454 -0
  210. praisonai/deploy/doctor.py +367 -0
  211. praisonai/deploy/main.py +327 -0
  212. praisonai/deploy/models.py +179 -0
  213. praisonai/deploy/providers/__init__.py +33 -0
  214. praisonai/deploy/providers/aws.py +331 -0
  215. praisonai/deploy/providers/azure.py +358 -0
  216. praisonai/deploy/providers/base.py +101 -0
  217. praisonai/deploy/providers/gcp.py +314 -0
  218. praisonai/deploy/schema.py +208 -0
  219. praisonai/deploy.py +185 -0
  220. praisonai/endpoints/__init__.py +53 -0
  221. praisonai/endpoints/a2u_server.py +410 -0
  222. praisonai/endpoints/discovery.py +165 -0
  223. praisonai/endpoints/providers/__init__.py +28 -0
  224. praisonai/endpoints/providers/a2a.py +253 -0
  225. praisonai/endpoints/providers/a2u.py +208 -0
  226. praisonai/endpoints/providers/agents_api.py +171 -0
  227. praisonai/endpoints/providers/base.py +231 -0
  228. praisonai/endpoints/providers/mcp.py +263 -0
  229. praisonai/endpoints/providers/recipe.py +206 -0
  230. praisonai/endpoints/providers/tools_mcp.py +150 -0
  231. praisonai/endpoints/registry.py +131 -0
  232. praisonai/endpoints/server.py +161 -0
  233. praisonai/inbuilt_tools/__init__.py +24 -0
  234. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  235. praisonai/inc/__init__.py +2 -0
  236. praisonai/inc/config.py +96 -0
  237. praisonai/inc/models.py +155 -0
  238. praisonai/integrations/__init__.py +56 -0
  239. praisonai/integrations/base.py +303 -0
  240. praisonai/integrations/claude_code.py +270 -0
  241. praisonai/integrations/codex_cli.py +255 -0
  242. praisonai/integrations/cursor_cli.py +195 -0
  243. praisonai/integrations/gemini_cli.py +222 -0
  244. praisonai/jobs/__init__.py +67 -0
  245. praisonai/jobs/executor.py +425 -0
  246. praisonai/jobs/models.py +230 -0
  247. praisonai/jobs/router.py +314 -0
  248. praisonai/jobs/server.py +186 -0
  249. praisonai/jobs/store.py +203 -0
  250. praisonai/llm/__init__.py +66 -0
  251. praisonai/llm/registry.py +382 -0
  252. praisonai/mcp_server/__init__.py +152 -0
  253. praisonai/mcp_server/adapters/__init__.py +74 -0
  254. praisonai/mcp_server/adapters/agents.py +128 -0
  255. praisonai/mcp_server/adapters/capabilities.py +168 -0
  256. praisonai/mcp_server/adapters/cli_tools.py +568 -0
  257. praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
  258. praisonai/mcp_server/adapters/knowledge.py +93 -0
  259. praisonai/mcp_server/adapters/memory.py +104 -0
  260. praisonai/mcp_server/adapters/prompts.py +306 -0
  261. praisonai/mcp_server/adapters/resources.py +124 -0
  262. praisonai/mcp_server/adapters/tools_bridge.py +280 -0
  263. praisonai/mcp_server/auth/__init__.py +48 -0
  264. praisonai/mcp_server/auth/api_key.py +291 -0
  265. praisonai/mcp_server/auth/oauth.py +460 -0
  266. praisonai/mcp_server/auth/oidc.py +289 -0
  267. praisonai/mcp_server/auth/scopes.py +260 -0
  268. praisonai/mcp_server/cli.py +852 -0
  269. praisonai/mcp_server/elicitation.py +445 -0
  270. praisonai/mcp_server/icons.py +302 -0
  271. praisonai/mcp_server/recipe_adapter.py +573 -0
  272. praisonai/mcp_server/recipe_cli.py +824 -0
  273. praisonai/mcp_server/registry.py +703 -0
  274. praisonai/mcp_server/sampling.py +422 -0
  275. praisonai/mcp_server/server.py +490 -0
  276. praisonai/mcp_server/tasks.py +443 -0
  277. praisonai/mcp_server/transports/__init__.py +18 -0
  278. praisonai/mcp_server/transports/http_stream.py +376 -0
  279. praisonai/mcp_server/transports/stdio.py +132 -0
  280. praisonai/persistence/__init__.py +84 -0
  281. praisonai/persistence/config.py +238 -0
  282. praisonai/persistence/conversation/__init__.py +25 -0
  283. praisonai/persistence/conversation/async_mysql.py +427 -0
  284. praisonai/persistence/conversation/async_postgres.py +410 -0
  285. praisonai/persistence/conversation/async_sqlite.py +371 -0
  286. praisonai/persistence/conversation/base.py +151 -0
  287. praisonai/persistence/conversation/json_store.py +250 -0
  288. praisonai/persistence/conversation/mysql.py +387 -0
  289. praisonai/persistence/conversation/postgres.py +401 -0
  290. praisonai/persistence/conversation/singlestore.py +240 -0
  291. praisonai/persistence/conversation/sqlite.py +341 -0
  292. praisonai/persistence/conversation/supabase.py +203 -0
  293. praisonai/persistence/conversation/surrealdb.py +287 -0
  294. praisonai/persistence/factory.py +301 -0
  295. praisonai/persistence/hooks/__init__.py +18 -0
  296. praisonai/persistence/hooks/agent_hooks.py +297 -0
  297. praisonai/persistence/knowledge/__init__.py +26 -0
  298. praisonai/persistence/knowledge/base.py +144 -0
  299. praisonai/persistence/knowledge/cassandra.py +232 -0
  300. praisonai/persistence/knowledge/chroma.py +295 -0
  301. praisonai/persistence/knowledge/clickhouse.py +242 -0
  302. praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
  303. praisonai/persistence/knowledge/couchbase.py +286 -0
  304. praisonai/persistence/knowledge/lancedb.py +216 -0
  305. praisonai/persistence/knowledge/langchain_adapter.py +291 -0
  306. praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
  307. praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
  308. praisonai/persistence/knowledge/milvus.py +277 -0
  309. praisonai/persistence/knowledge/mongodb_vector.py +306 -0
  310. praisonai/persistence/knowledge/pgvector.py +335 -0
  311. praisonai/persistence/knowledge/pinecone.py +253 -0
  312. praisonai/persistence/knowledge/qdrant.py +301 -0
  313. praisonai/persistence/knowledge/redis_vector.py +291 -0
  314. praisonai/persistence/knowledge/singlestore_vector.py +299 -0
  315. praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
  316. praisonai/persistence/knowledge/upstash_vector.py +266 -0
  317. praisonai/persistence/knowledge/weaviate.py +223 -0
  318. praisonai/persistence/migrations/__init__.py +10 -0
  319. praisonai/persistence/migrations/manager.py +251 -0
  320. praisonai/persistence/orchestrator.py +406 -0
  321. praisonai/persistence/state/__init__.py +21 -0
  322. praisonai/persistence/state/async_mongodb.py +200 -0
  323. praisonai/persistence/state/base.py +107 -0
  324. praisonai/persistence/state/dynamodb.py +226 -0
  325. praisonai/persistence/state/firestore.py +175 -0
  326. praisonai/persistence/state/gcs.py +155 -0
  327. praisonai/persistence/state/memory.py +245 -0
  328. praisonai/persistence/state/mongodb.py +158 -0
  329. praisonai/persistence/state/redis.py +190 -0
  330. praisonai/persistence/state/upstash.py +144 -0
  331. praisonai/persistence/tests/__init__.py +3 -0
  332. praisonai/persistence/tests/test_all_backends.py +633 -0
  333. praisonai/profiler.py +1214 -0
  334. praisonai/recipe/__init__.py +134 -0
  335. praisonai/recipe/bridge.py +278 -0
  336. praisonai/recipe/core.py +893 -0
  337. praisonai/recipe/exceptions.py +54 -0
  338. praisonai/recipe/history.py +402 -0
  339. praisonai/recipe/models.py +266 -0
  340. praisonai/recipe/operations.py +440 -0
  341. praisonai/recipe/policy.py +422 -0
  342. praisonai/recipe/registry.py +849 -0
  343. praisonai/recipe/runtime.py +214 -0
  344. praisonai/recipe/security.py +711 -0
  345. praisonai/recipe/serve.py +859 -0
  346. praisonai/recipe/server.py +613 -0
  347. praisonai/scheduler/__init__.py +45 -0
  348. praisonai/scheduler/agent_scheduler.py +552 -0
  349. praisonai/scheduler/base.py +124 -0
  350. praisonai/scheduler/daemon_manager.py +225 -0
  351. praisonai/scheduler/state_manager.py +155 -0
  352. praisonai/scheduler/yaml_loader.py +193 -0
  353. praisonai/scheduler.py +194 -0
  354. praisonai/setup/__init__.py +1 -0
  355. praisonai/setup/build.py +21 -0
  356. praisonai/setup/post_install.py +23 -0
  357. praisonai/setup/setup_conda_env.py +25 -0
  358. praisonai/setup.py +16 -0
  359. praisonai/templates/__init__.py +116 -0
  360. praisonai/templates/cache.py +364 -0
  361. praisonai/templates/dependency_checker.py +358 -0
  362. praisonai/templates/discovery.py +391 -0
  363. praisonai/templates/loader.py +564 -0
  364. praisonai/templates/registry.py +511 -0
  365. praisonai/templates/resolver.py +206 -0
  366. praisonai/templates/security.py +327 -0
  367. praisonai/templates/tool_override.py +498 -0
  368. praisonai/templates/tools_doctor.py +256 -0
  369. praisonai/test.py +105 -0
  370. praisonai/train.py +562 -0
  371. praisonai/train_vision.py +306 -0
  372. praisonai/ui/agents.py +824 -0
  373. praisonai/ui/callbacks.py +57 -0
  374. praisonai/ui/chainlit_compat.py +246 -0
  375. praisonai/ui/chat.py +532 -0
  376. praisonai/ui/code.py +717 -0
  377. praisonai/ui/colab.py +474 -0
  378. praisonai/ui/colab_chainlit.py +81 -0
  379. praisonai/ui/components/aicoder.py +284 -0
  380. praisonai/ui/context.py +283 -0
  381. praisonai/ui/database_config.py +56 -0
  382. praisonai/ui/db.py +294 -0
  383. praisonai/ui/realtime.py +488 -0
  384. praisonai/ui/realtimeclient/__init__.py +756 -0
  385. praisonai/ui/realtimeclient/tools.py +242 -0
  386. praisonai/ui/sql_alchemy.py +710 -0
  387. praisonai/upload_vision.py +140 -0
  388. praisonai/version.py +1 -0
  389. praisonai-3.0.0.dist-info/METADATA +3493 -0
  390. praisonai-3.0.0.dist-info/RECORD +393 -0
  391. praisonai-3.0.0.dist-info/WHEEL +5 -0
  392. praisonai-3.0.0.dist-info/entry_points.txt +4 -0
  393. praisonai-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,534 @@
1
+ """
2
+ CLI Compare Feature for PraisonAI.
3
+
4
+ Compares different CLI command modes without performance impact.
5
+ Only loaded when --compare flag is used.
6
+ """
7
+
8
+ import time
9
+ import json
10
+ import logging
11
+ from dataclasses import dataclass, field
12
+ from typing import Dict, Any, List, Optional
13
+ from datetime import datetime
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ COMPARE_MODES = {
18
+ "basic": {},
19
+ "tools": {"tools": "calculator,internet_search"},
20
+ "research": {"research": True},
21
+ "planning": {"planning": True},
22
+ "memory": {"memory": True},
23
+ "router": {"router": True},
24
+ "web_search": {"web_search": True},
25
+ "web_fetch": {"web_fetch": True},
26
+ "query_rewrite": {"query_rewrite": True},
27
+ "expand_prompt": {"expand_prompt": True},
28
+ }
29
+
30
+
31
+ def get_mode_config(mode_name: str, custom_args: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
32
+ """
33
+ Get configuration for a comparison mode.
34
+
35
+ Args:
36
+ mode_name: Name of the mode
37
+ custom_args: Optional custom arguments to merge
38
+
39
+ Returns:
40
+ Configuration dictionary for the mode
41
+ """
42
+ config = COMPARE_MODES.get(mode_name, {}).copy()
43
+ if custom_args:
44
+ config.update(custom_args)
45
+ return config
46
+
47
+
48
+ def list_available_modes() -> List[str]:
49
+ """
50
+ List all available comparison modes.
51
+
52
+ Returns:
53
+ List of mode names
54
+ """
55
+ return list(COMPARE_MODES.keys())
56
+
57
+
58
+ def parse_modes(modes_str: str) -> List[str]:
59
+ """
60
+ Parse comma-separated modes string.
61
+
62
+ Args:
63
+ modes_str: Comma-separated mode names
64
+
65
+ Returns:
66
+ List of mode names
67
+ """
68
+ if not modes_str or not modes_str.strip():
69
+ return ["basic"]
70
+
71
+ modes = [m.strip() for m in modes_str.split(",")]
72
+ return [m for m in modes if m]
73
+
74
+
75
+ @dataclass
76
+ class ModeResult:
77
+ """Result from running a single mode."""
78
+ mode: str
79
+ output: str
80
+ execution_time_ms: float
81
+ model_used: str
82
+ tokens: Optional[Dict[str, int]] = None
83
+ cost: Optional[float] = None
84
+ tools_used: List[str] = field(default_factory=list)
85
+ error: Optional[str] = None
86
+ timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
87
+
88
+ def to_dict(self) -> Dict[str, Any]:
89
+ """Convert to dictionary."""
90
+ return {
91
+ "mode": self.mode,
92
+ "output_preview": self.output[:200] + "..." if len(self.output) > 200 else self.output,
93
+ "execution_time_ms": self.execution_time_ms,
94
+ "model_used": self.model_used,
95
+ "tokens": self.tokens,
96
+ "cost": self.cost,
97
+ "tools_used": self.tools_used,
98
+ "error": self.error,
99
+ "timestamp": self.timestamp,
100
+ }
101
+
102
+
103
+ @dataclass
104
+ class CompareResult:
105
+ """Result from comparing multiple modes."""
106
+ query: str
107
+ comparisons: List[ModeResult] = field(default_factory=list)
108
+ timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
109
+
110
+ def get_summary(self) -> Dict[str, Any]:
111
+ """Generate summary statistics."""
112
+ if not self.comparisons:
113
+ return {}
114
+
115
+ times = [(c.mode, c.execution_time_ms) for c in self.comparisons if c.error is None]
116
+ if not times:
117
+ return {"error": "All modes failed"}
118
+
119
+ sorted_by_time = sorted(times, key=lambda x: x[1])
120
+
121
+ summary = {
122
+ "fastest": sorted_by_time[0][0],
123
+ "slowest": sorted_by_time[-1][0],
124
+ "fastest_time_ms": sorted_by_time[0][1],
125
+ "slowest_time_ms": sorted_by_time[-1][1],
126
+ }
127
+
128
+ total_tokens = 0
129
+ total_cost = 0.0
130
+ for c in self.comparisons:
131
+ if c.tokens:
132
+ total_tokens += c.tokens.get("input", 0) + c.tokens.get("output", 0)
133
+ if c.cost:
134
+ total_cost += c.cost
135
+
136
+ if total_tokens > 0:
137
+ summary["total_tokens"] = total_tokens
138
+ if total_cost > 0:
139
+ summary["total_cost"] = total_cost
140
+
141
+ return summary
142
+
143
+ def to_dict(self) -> Dict[str, Any]:
144
+ """Convert to dictionary."""
145
+ return {
146
+ "query": self.query,
147
+ "timestamp": self.timestamp,
148
+ "comparisons": [c.to_dict() for c in self.comparisons],
149
+ "summary": self.get_summary(),
150
+ }
151
+
152
+ def to_json(self) -> str:
153
+ """Convert to JSON string."""
154
+ return json.dumps(self.to_dict(), indent=2)
155
+
156
+
157
+ def save_compare_result(result: CompareResult, path: str) -> bool:
158
+ """
159
+ Save comparison result to file.
160
+
161
+ Args:
162
+ result: CompareResult to save
163
+ path: File path
164
+
165
+ Returns:
166
+ True if successful
167
+ """
168
+ try:
169
+ from pathlib import Path
170
+
171
+ file_path = Path(path)
172
+ file_path.parent.mkdir(parents=True, exist_ok=True)
173
+ file_path.write_text(result.to_json())
174
+ return True
175
+ except Exception as e:
176
+ logger.error(f"Failed to save comparison result: {e}")
177
+ return False
178
+
179
+
180
+ def format_comparison_table(result: CompareResult, show_responses: bool = True) -> str:
181
+ """
182
+ Format comparison result as a table string with optional response display.
183
+
184
+ Args:
185
+ result: CompareResult to format
186
+ show_responses: Whether to show actual responses from each mode
187
+
188
+ Returns:
189
+ Formatted table string
190
+ """
191
+ try:
192
+ from rich.console import Console
193
+ from rich.table import Table
194
+ from rich.panel import Panel
195
+ from io import StringIO
196
+
197
+ console = Console(file=StringIO(), force_terminal=True)
198
+
199
+ # Main comparison table
200
+ table = Table(title=f"Comparison: {result.query[:50]}...")
201
+ table.add_column("Mode", style="cyan")
202
+ table.add_column("Time (ms)", style="green")
203
+ table.add_column("Model", style="yellow")
204
+ table.add_column("Tools", style="magenta")
205
+ table.add_column("Status", style="bold")
206
+
207
+ for c in result.comparisons:
208
+ status = "✅" if c.error is None else f"❌ {c.error[:20]}"
209
+ tools = ", ".join(c.tools_used) if c.tools_used else "-"
210
+ table.add_row(
211
+ c.mode,
212
+ f"{c.execution_time_ms:.1f}",
213
+ c.model_used,
214
+ tools,
215
+ status
216
+ )
217
+
218
+ summary = result.get_summary()
219
+ if summary.get("fastest"):
220
+ table.add_section()
221
+ table.add_row(
222
+ "Summary",
223
+ f"Fastest: {summary['fastest']}",
224
+ "",
225
+ "",
226
+ f"Δ {summary['slowest_time_ms'] - summary['fastest_time_ms']:.1f}ms"
227
+ )
228
+
229
+ console.print(table)
230
+
231
+ # Show responses if enabled
232
+ if show_responses:
233
+ console.print("\n[bold cyan]📝 Responses:[/bold cyan]")
234
+ for c in result.comparisons:
235
+ if c.error is None:
236
+ # Truncate long responses
237
+ response_preview = c.output[:500] + "..." if len(c.output) > 500 else c.output
238
+ console.print(Panel(
239
+ response_preview,
240
+ title=f"[bold]{c.mode}[/bold] ({c.execution_time_ms:.0f}ms)",
241
+ border_style="cyan"
242
+ ))
243
+ else:
244
+ console.print(Panel(
245
+ f"[red]Error: {c.error}[/red]",
246
+ title=f"[bold]{c.mode}[/bold]",
247
+ border_style="red"
248
+ ))
249
+
250
+ return console.file.getvalue()
251
+ except ImportError:
252
+ lines = [f"Comparison: {result.query}"]
253
+ lines.append("-" * 60)
254
+ for c in result.comparisons:
255
+ status = "OK" if c.error is None else f"ERROR: {c.error}"
256
+ lines.append(f"{c.mode}: {c.execution_time_ms:.1f}ms - {status}")
257
+ if show_responses and c.error is None:
258
+ lines.append(f" Response: {c.output[:200]}...")
259
+ return "\n".join(lines)
260
+
261
+
262
+ class CompareHandler:
263
+ """
264
+ Handler for CLI compare feature.
265
+
266
+ Compares different CLI command modes and generates comparison reports.
267
+ """
268
+
269
+ def __init__(self, verbose: bool = False):
270
+ """
271
+ Initialize the compare handler.
272
+
273
+ Args:
274
+ verbose: Enable verbose output
275
+ """
276
+ self.verbose = verbose
277
+ self.results: List[ModeResult] = []
278
+
279
+ def compare(
280
+ self,
281
+ query: str,
282
+ modes: List[str],
283
+ model: Optional[str] = None,
284
+ **kwargs
285
+ ) -> CompareResult:
286
+ """
287
+ Compare query execution across different modes.
288
+
289
+ Args:
290
+ query: The query to execute
291
+ modes: List of mode names to compare
292
+ model: Optional model override
293
+ **kwargs: Additional arguments
294
+
295
+ Returns:
296
+ CompareResult with all comparisons
297
+ """
298
+ result = CompareResult(query=query)
299
+
300
+ for mode in modes:
301
+ if self.verbose:
302
+ logger.info(f"Running mode: {mode}")
303
+
304
+ try:
305
+ mode_result = self._run_mode(query, mode, model=model, **kwargs)
306
+ result.comparisons.append(mode_result)
307
+ except Exception as e:
308
+ logger.error(f"Error running mode {mode}: {e}")
309
+ result.comparisons.append(ModeResult(
310
+ mode=mode,
311
+ output="",
312
+ execution_time_ms=0,
313
+ model_used=model or "unknown",
314
+ error=str(e)
315
+ ))
316
+
317
+ return result
318
+
319
+ def _run_mode(
320
+ self,
321
+ query: str,
322
+ mode: str,
323
+ model: Optional[str] = None,
324
+ **kwargs
325
+ ) -> ModeResult:
326
+ """
327
+ Run a single mode and capture results.
328
+
329
+ Args:
330
+ query: The query to execute
331
+ mode: Mode name
332
+ model: Optional model override
333
+ **kwargs: Additional arguments
334
+
335
+ Returns:
336
+ ModeResult with execution details
337
+ """
338
+ config = get_mode_config(mode, kwargs)
339
+
340
+ if model:
341
+ config["llm"] = model
342
+
343
+ start_time = time.perf_counter()
344
+ output = ""
345
+ tokens = None
346
+ cost = None
347
+ tools_used = []
348
+ error = None
349
+ model_used = model or "gpt-4o-mini"
350
+
351
+ try:
352
+ from praisonaiagents import Agent
353
+
354
+ agent_config = {
355
+ "name": f"CompareAgent_{mode}",
356
+ "role": "Assistant",
357
+ "goal": "Complete the given task",
358
+ "backstory": "You are a helpful AI assistant",
359
+ "verbose": False,
360
+ }
361
+
362
+ if config.get("llm"):
363
+ agent_config["llm"] = config["llm"]
364
+ model_used = config["llm"]
365
+
366
+ if config.get("tools"):
367
+ tools_list = self._load_tools(config["tools"])
368
+ if tools_list:
369
+ agent_config["tools"] = tools_list
370
+
371
+ if config.get("planning"):
372
+ agent_config["planning"] = True
373
+
374
+ if config.get("memory"):
375
+ agent_config["memory"] = True
376
+
377
+ if config.get("web_search"):
378
+ agent_config["web_search"] = True
379
+
380
+ if config.get("web_fetch"):
381
+ agent_config["web_fetch"] = True
382
+
383
+ agent = Agent(**agent_config)
384
+ result = agent.start(query)
385
+
386
+ if hasattr(result, 'raw'):
387
+ output = str(result.raw)
388
+ else:
389
+ output = str(result)
390
+
391
+ if hasattr(agent, 'tools') and agent.tools:
392
+ for tool in agent.tools:
393
+ if hasattr(tool, '__name__'):
394
+ tools_used.append(tool.__name__)
395
+ elif hasattr(tool, 'name'):
396
+ tools_used.append(tool.name)
397
+
398
+ except Exception as e:
399
+ error = str(e)
400
+ logger.error(f"Mode {mode} failed: {e}")
401
+
402
+ end_time = time.perf_counter()
403
+ execution_time_ms = (end_time - start_time) * 1000
404
+
405
+ return ModeResult(
406
+ mode=mode,
407
+ output=output,
408
+ execution_time_ms=execution_time_ms,
409
+ model_used=model_used,
410
+ tokens=tokens,
411
+ cost=cost,
412
+ tools_used=tools_used,
413
+ error=error
414
+ )
415
+
416
+ def _load_tools(self, tools_str: str) -> List:
417
+ """
418
+ Load tools from string specification.
419
+
420
+ Args:
421
+ tools_str: Comma-separated tool names
422
+
423
+ Returns:
424
+ List of tool functions
425
+ """
426
+ tools = []
427
+ tool_names = [t.strip() for t in tools_str.split(",")]
428
+
429
+ for name in tool_names:
430
+ try:
431
+ if name == "internet_search":
432
+ from praisonaiagents.tools import internet_search
433
+ tools.append(internet_search)
434
+ elif name == "calculator":
435
+ tools.append(_calculator_tool)
436
+ except ImportError:
437
+ logger.warning(f"Could not load tool: {name}")
438
+
439
+ return tools
440
+
441
+ def print_result(self, result: CompareResult) -> None:
442
+ """
443
+ Print comparison result to console.
444
+
445
+ Args:
446
+ result: CompareResult to print
447
+ """
448
+ table_str = format_comparison_table(result)
449
+ print(table_str)
450
+
451
+ def execute(
452
+ self,
453
+ query: str,
454
+ modes_str: str,
455
+ model: Optional[str] = None,
456
+ output_path: Optional[str] = None,
457
+ **kwargs
458
+ ) -> CompareResult:
459
+ """
460
+ Execute comparison from CLI arguments.
461
+
462
+ Args:
463
+ query: The query to execute
464
+ modes_str: Comma-separated mode names
465
+ model: Optional model override
466
+ output_path: Optional path to save results
467
+ **kwargs: Additional arguments
468
+
469
+ Returns:
470
+ CompareResult with all comparisons
471
+ """
472
+ modes = parse_modes(modes_str)
473
+
474
+ if self.verbose:
475
+ print(f"[bold cyan]Comparing modes: {', '.join(modes)}[/bold cyan]")
476
+
477
+ result = self.compare(query, modes, model=model, **kwargs)
478
+
479
+ self.print_result(result)
480
+
481
+ if output_path:
482
+ if save_compare_result(result, output_path):
483
+ print(f"[green]Results saved to: {output_path}[/green]")
484
+ else:
485
+ print(f"[red]Failed to save results to: {output_path}[/red]")
486
+
487
+ return result
488
+
489
+
490
+ def _calculator_tool(expression: str) -> str:
491
+ """
492
+ Evaluate a mathematical expression safely.
493
+
494
+ Args:
495
+ expression: Mathematical expression to evaluate (e.g., "2+2", "15*3", "100/4")
496
+
497
+ Returns:
498
+ The result of the calculation as a string
499
+ """
500
+ import ast
501
+ import operator
502
+
503
+ # Safe operators
504
+ operators = {
505
+ ast.Add: operator.add,
506
+ ast.Sub: operator.sub,
507
+ ast.Mult: operator.mul,
508
+ ast.Div: operator.truediv,
509
+ ast.Pow: operator.pow,
510
+ ast.USub: operator.neg,
511
+ ast.UAdd: operator.pos,
512
+ ast.Mod: operator.mod,
513
+ ast.FloorDiv: operator.floordiv,
514
+ }
515
+
516
+ def _eval(node):
517
+ if isinstance(node, ast.Constant):
518
+ return node.value
519
+ elif isinstance(node, ast.BinOp):
520
+ left = _eval(node.left)
521
+ right = _eval(node.right)
522
+ return operators[type(node.op)](left, right)
523
+ elif isinstance(node, ast.UnaryOp):
524
+ operand = _eval(node.operand)
525
+ return operators[type(node.op)](operand)
526
+ else:
527
+ raise ValueError(f"Unsupported operation: {type(node)}")
528
+
529
+ try:
530
+ tree = ast.parse(expression, mode='eval')
531
+ result = _eval(tree.body)
532
+ return str(result)
533
+ except Exception as e:
534
+ return f"Error evaluating '{expression}': {e}"