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,386 @@
1
+ """
2
+ Profile Suite Runner for PraisonAI CLI.
3
+
4
+ Runs a matrix of profiling scenarios and produces aggregated reports.
5
+ Supports cold/warm start, streaming/non-streaming, and multiple iterations.
6
+ """
7
+
8
+ import json
9
+ import statistics
10
+ import subprocess
11
+ import sys
12
+ import time
13
+ from dataclasses import dataclass, field
14
+ from datetime import datetime
15
+ from pathlib import Path
16
+ from typing import Any, Dict, List, Optional
17
+
18
+ from .core import ProfilerConfig, ProfilerResult, QueryProfiler
19
+
20
+
21
+ @dataclass
22
+ class ScenarioConfig:
23
+ """Configuration for a single profiling scenario."""
24
+ name: str
25
+ prompt: str
26
+ model: Optional[str] = None
27
+ stream: bool = False
28
+ iterations: int = 3
29
+ warmup: int = 1
30
+ show_files: bool = True
31
+ limit: int = 20
32
+
33
+
34
+ @dataclass
35
+ class ScenarioResult:
36
+ """Result from running a scenario."""
37
+ name: str
38
+ config: ScenarioConfig
39
+ results: List[ProfilerResult] = field(default_factory=list)
40
+
41
+ @property
42
+ def import_times(self) -> List[float]:
43
+ return [r.timing.imports_ms for r in self.results]
44
+
45
+ @property
46
+ def total_times(self) -> List[float]:
47
+ return [r.timing.total_run_ms for r in self.results]
48
+
49
+ @property
50
+ def first_token_times(self) -> List[float]:
51
+ return [r.timing.first_token_ms for r in self.results if r.timing.first_token_ms > 0]
52
+
53
+ def get_stats(self, values: List[float]) -> Dict[str, float]:
54
+ if not values:
55
+ return {"mean": 0, "min": 0, "max": 0, "stdev": 0, "median": 0}
56
+ return {
57
+ "mean": statistics.mean(values),
58
+ "min": min(values),
59
+ "max": max(values),
60
+ "stdev": statistics.stdev(values) if len(values) > 1 else 0,
61
+ "median": statistics.median(values),
62
+ }
63
+
64
+ def to_dict(self) -> Dict[str, Any]:
65
+ return {
66
+ "name": self.name,
67
+ "config": {
68
+ "prompt": self.config.prompt[:50] + "..." if len(self.config.prompt) > 50 else self.config.prompt,
69
+ "model": self.config.model or "default",
70
+ "stream": self.config.stream,
71
+ "iterations": self.config.iterations,
72
+ },
73
+ "import_time_stats": self.get_stats(self.import_times),
74
+ "total_time_stats": self.get_stats(self.total_times),
75
+ "first_token_stats": self.get_stats(self.first_token_times) if self.first_token_times else None,
76
+ "raw_import_times_ms": self.import_times,
77
+ "raw_total_times_ms": self.total_times,
78
+ }
79
+
80
+
81
+ @dataclass
82
+ class SuiteResult:
83
+ """Result from running a full profiling suite."""
84
+ scenarios: List[ScenarioResult] = field(default_factory=list)
85
+ startup_cold_ms: float = 0.0
86
+ startup_warm_ms: float = 0.0
87
+ import_analysis: List[Dict[str, Any]] = field(default_factory=list)
88
+ timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat() + "Z")
89
+ metadata: Dict[str, Any] = field(default_factory=dict)
90
+
91
+ def to_dict(self) -> Dict[str, Any]:
92
+ return {
93
+ "timestamp": self.timestamp,
94
+ "metadata": self.metadata,
95
+ "startup": {
96
+ "cold_ms": self.startup_cold_ms,
97
+ "warm_ms": self.startup_warm_ms,
98
+ },
99
+ "import_analysis": self.import_analysis[:20],
100
+ "scenarios": [s.to_dict() for s in self.scenarios],
101
+ }
102
+
103
+
104
+ class ProfileSuiteRunner:
105
+ """
106
+ Runs a suite of profiling scenarios.
107
+
108
+ Collects comprehensive timing data across multiple runs and scenarios.
109
+ """
110
+
111
+ DEFAULT_SCENARIOS = [
112
+ ScenarioConfig(name="simple_non_stream", prompt="hi", stream=False, iterations=3),
113
+ ScenarioConfig(name="simple_stream", prompt="hi", stream=True, iterations=3),
114
+ ScenarioConfig(name="medium_non_stream", prompt="Explain what Python is in 2 sentences", stream=False, iterations=2),
115
+ ScenarioConfig(name="medium_stream", prompt="Explain what Python is in 2 sentences", stream=True, iterations=2),
116
+ ]
117
+
118
+ def __init__(
119
+ self,
120
+ scenarios: Optional[List[ScenarioConfig]] = None,
121
+ output_dir: Optional[str] = None,
122
+ verbose: bool = False,
123
+ ):
124
+ self.scenarios = scenarios or self.DEFAULT_SCENARIOS
125
+ self.output_dir = Path(output_dir) if output_dir else Path("/tmp/praisonai_profile_suite")
126
+ self.verbose = verbose
127
+
128
+ def run(self) -> SuiteResult:
129
+ """Run the full profiling suite."""
130
+ result = SuiteResult()
131
+ result.metadata = self._collect_metadata()
132
+
133
+ # Create output directory
134
+ self.output_dir.mkdir(parents=True, exist_ok=True)
135
+
136
+ if self.verbose:
137
+ print("🔬 Starting Profile Suite...")
138
+ print(f" Output: {self.output_dir}")
139
+
140
+ # 1. Measure startup times
141
+ if self.verbose:
142
+ print("\n📊 Measuring startup times...")
143
+ result.startup_cold_ms, result.startup_warm_ms = self._measure_startup()
144
+ if self.verbose:
145
+ print(f" Cold: {result.startup_cold_ms:.2f}ms, Warm: {result.startup_warm_ms:.2f}ms")
146
+
147
+ # 2. Analyze imports
148
+ if self.verbose:
149
+ print("\n📊 Analyzing imports...")
150
+ result.import_analysis = self._analyze_imports()
151
+ if self.verbose and result.import_analysis:
152
+ print(f" Top import: {result.import_analysis[0]['module']} ({result.import_analysis[0]['cumulative_ms']:.2f}ms)")
153
+
154
+ # 3. Run scenarios
155
+ for scenario in self.scenarios:
156
+ if self.verbose:
157
+ print(f"\n📊 Running scenario: {scenario.name}")
158
+ scenario_result = self._run_scenario(scenario)
159
+ result.scenarios.append(scenario_result)
160
+
161
+ if self.verbose:
162
+ stats = scenario_result.get_stats(scenario_result.total_times)
163
+ print(f" Total time: {stats['mean']:.2f}ms (±{stats['stdev']:.2f}ms)")
164
+
165
+ # 4. Save results
166
+ self._save_results(result)
167
+
168
+ if self.verbose:
169
+ print(f"\n✅ Suite complete. Results saved to {self.output_dir}")
170
+
171
+ return result
172
+
173
+ def _collect_metadata(self) -> Dict[str, Any]:
174
+ """Collect system metadata."""
175
+ import platform
176
+ try:
177
+ from praisonai.version import __version__ as praisonai_version
178
+ except ImportError:
179
+ praisonai_version = "unknown"
180
+
181
+ return {
182
+ "python_version": platform.python_version(),
183
+ "platform": platform.platform(),
184
+ "praisonai_version": praisonai_version,
185
+ "timestamp": datetime.utcnow().isoformat() + "Z",
186
+ }
187
+
188
+ def _measure_startup(self) -> tuple:
189
+ """Measure cold and warm startup times."""
190
+ # Cold start
191
+ start = time.perf_counter()
192
+ subprocess.run(
193
+ [sys.executable, "-c", "import praisonai; import praisonai.cli"],
194
+ capture_output=True,
195
+ text=True,
196
+ )
197
+ cold_ms = (time.perf_counter() - start) * 1000
198
+
199
+ # Warm start
200
+ start = time.perf_counter()
201
+ subprocess.run(
202
+ [sys.executable, "-c", "import praisonai; import praisonai.cli"],
203
+ capture_output=True,
204
+ text=True,
205
+ )
206
+ warm_ms = (time.perf_counter() - start) * 1000
207
+
208
+ return cold_ms, warm_ms
209
+
210
+ def _analyze_imports(self) -> List[Dict[str, Any]]:
211
+ """Analyze import times."""
212
+ import re
213
+
214
+ try:
215
+ result = subprocess.run(
216
+ [sys.executable, "-X", "importtime", "-c", "import praisonaiagents"],
217
+ capture_output=True,
218
+ text=True,
219
+ timeout=60,
220
+ )
221
+
222
+ import_times = []
223
+ for line in result.stderr.split('\n'):
224
+ if 'import time:' in line:
225
+ match = re.search(r'import time:\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(.+)', line)
226
+ if match:
227
+ self_time = int(match.group(1))
228
+ cumulative = int(match.group(2))
229
+ module = match.group(3).strip()
230
+ import_times.append({
231
+ "module": module,
232
+ "self_us": self_time,
233
+ "cumulative_us": cumulative,
234
+ "self_ms": self_time / 1000,
235
+ "cumulative_ms": cumulative / 1000,
236
+ })
237
+
238
+ import_times.sort(key=lambda x: x["cumulative_us"], reverse=True)
239
+ return import_times[:30]
240
+
241
+ except Exception:
242
+ return []
243
+
244
+ def _run_scenario(self, scenario: ScenarioConfig) -> ScenarioResult:
245
+ """Run a single scenario with multiple iterations."""
246
+ result = ScenarioResult(name=scenario.name, config=scenario)
247
+
248
+ config = ProfilerConfig(
249
+ stream=scenario.stream,
250
+ show_files=scenario.show_files,
251
+ limit=scenario.limit,
252
+ first_token=scenario.stream,
253
+ )
254
+
255
+ # Warmup runs (discarded)
256
+ for _ in range(scenario.warmup):
257
+ try:
258
+ profiler = QueryProfiler(config)
259
+ profiler.profile_query(scenario.prompt, model=scenario.model, stream=scenario.stream)
260
+ except Exception:
261
+ pass
262
+
263
+ # Actual runs
264
+ for i in range(scenario.iterations):
265
+ try:
266
+ profiler = QueryProfiler(config)
267
+ profile_result = profiler.profile_query(
268
+ scenario.prompt,
269
+ model=scenario.model,
270
+ stream=scenario.stream,
271
+ )
272
+ result.results.append(profile_result)
273
+
274
+ if self.verbose:
275
+ print(f" Iteration {i+1}: {profile_result.timing.total_run_ms:.2f}ms")
276
+
277
+ except Exception as e:
278
+ if self.verbose:
279
+ print(f" Iteration {i+1}: ERROR - {e}")
280
+
281
+ return result
282
+
283
+ def _save_results(self, result: SuiteResult):
284
+ """Save results to files."""
285
+ # Save JSON summary
286
+ json_path = self.output_dir / "suite_results.json"
287
+ with open(json_path, 'w') as f:
288
+ json.dump(result.to_dict(), f, indent=2, default=str)
289
+
290
+ # Save human-readable report
291
+ report_path = self.output_dir / "suite_report.txt"
292
+ with open(report_path, 'w') as f:
293
+ f.write(self._format_report(result))
294
+
295
+ def _format_report(self, result: SuiteResult) -> str:
296
+ """Format a human-readable report."""
297
+ lines = []
298
+ lines.append("=" * 70)
299
+ lines.append("PraisonAI Profile Suite Report")
300
+ lines.append("=" * 70)
301
+ lines.append("")
302
+
303
+ # Metadata
304
+ lines.append("## System Information")
305
+ lines.append(f" Timestamp: {result.timestamp}")
306
+ lines.append(f" Python Version: {result.metadata.get('python_version', 'N/A')}")
307
+ lines.append(f" Platform: {result.metadata.get('platform', 'N/A')}")
308
+ lines.append(f" PraisonAI: {result.metadata.get('praisonai_version', 'N/A')}")
309
+ lines.append("")
310
+
311
+ # Startup times
312
+ lines.append("## Startup Times")
313
+ lines.append(f" Cold Start: {result.startup_cold_ms:>10.2f} ms")
314
+ lines.append(f" Warm Start: {result.startup_warm_ms:>10.2f} ms")
315
+ lines.append("")
316
+
317
+ # Import analysis
318
+ if result.import_analysis:
319
+ lines.append("## Top Import Times")
320
+ lines.append("-" * 70)
321
+ lines.append(f"{'Module':<45} {'Self (ms)':>10} {'Cumul (ms)':>12}")
322
+ lines.append("-" * 70)
323
+ for imp in result.import_analysis[:15]:
324
+ module = imp["module"]
325
+ if len(module) > 43:
326
+ module = "..." + module[-40:]
327
+ lines.append(f"{module:<45} {imp['self_ms']:>10.2f} {imp['cumulative_ms']:>12.2f}")
328
+ lines.append("")
329
+
330
+ # Scenario results
331
+ lines.append("## Scenario Results")
332
+ lines.append("-" * 70)
333
+ lines.append(f"{'Scenario':<25} {'Stream':>8} {'Iters':>6} {'Mean (ms)':>12} {'StdDev':>10}")
334
+ lines.append("-" * 70)
335
+
336
+ for scenario in result.scenarios:
337
+ stats = scenario.get_stats(scenario.total_times)
338
+ stream_str = "Yes" if scenario.config.stream else "No"
339
+ lines.append(
340
+ f"{scenario.name:<25} {stream_str:>8} {len(scenario.results):>6} "
341
+ f"{stats['mean']:>12.2f} {stats['stdev']:>10.2f}"
342
+ )
343
+ lines.append("")
344
+
345
+ # Summary statistics
346
+ all_import_times = []
347
+ all_total_times = []
348
+ for scenario in result.scenarios:
349
+ all_import_times.extend(scenario.import_times)
350
+ all_total_times.extend(scenario.total_times)
351
+
352
+ if all_import_times:
353
+ lines.append("## Overall Statistics")
354
+ lines.append(f" Import Time (mean): {statistics.mean(all_import_times):>10.2f} ms")
355
+ lines.append(f" Total Time (mean): {statistics.mean(all_total_times):>10.2f} ms")
356
+ if len(all_total_times) > 1:
357
+ lines.append(f" Total Time (stdev): {statistics.stdev(all_total_times):>10.2f} ms")
358
+ lines.append("")
359
+
360
+ lines.append("=" * 70)
361
+
362
+ return "\n".join(lines)
363
+
364
+
365
+ def run_profile_suite(
366
+ output_dir: Optional[str] = None,
367
+ scenarios: Optional[List[ScenarioConfig]] = None,
368
+ verbose: bool = True,
369
+ ) -> SuiteResult:
370
+ """
371
+ Run a profile suite and return results.
372
+
373
+ Args:
374
+ output_dir: Directory to save results
375
+ scenarios: Custom scenarios to run
376
+ verbose: Print progress
377
+
378
+ Returns:
379
+ SuiteResult with all profiling data
380
+ """
381
+ runner = ProfileSuiteRunner(
382
+ scenarios=scenarios,
383
+ output_dir=output_dir,
384
+ verbose=verbose,
385
+ )
386
+ return runner.run()