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,422 @@
1
+ """
2
+ MCP Sampling Implementation
3
+
4
+ Implements the Sampling API per MCP 2025-11-25 specification.
5
+ Sampling allows servers to request LLM completions from clients.
6
+
7
+ Features:
8
+ - Sampling requests with tool calling support
9
+ - Tool choice configuration
10
+ - Model preferences
11
+ - System prompt support
12
+ """
13
+
14
+ import logging
15
+ from dataclasses import dataclass, field
16
+ from enum import Enum
17
+ from typing import Any, Callable, Dict, List, Optional
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class ToolChoiceMode(str, Enum):
23
+ """Tool choice modes per MCP 2025-11-25 specification."""
24
+ AUTO = "auto" # Model decides whether to use tools
25
+ NONE = "none" # Model should not use tools
26
+ ANY = "any" # Model must use at least one tool (any tool)
27
+ TOOL = "tool" # Model must use a specific tool
28
+
29
+
30
+ # Backwards compatibility alias
31
+ ToolChoiceType = ToolChoiceMode
32
+
33
+
34
+ @dataclass
35
+ class ToolDefinition:
36
+ """Tool definition for sampling requests."""
37
+ name: str
38
+ description: str
39
+ input_schema: Dict[str, Any]
40
+
41
+ def to_dict(self) -> Dict[str, Any]:
42
+ return {
43
+ "name": self.name,
44
+ "description": self.description,
45
+ "inputSchema": self.input_schema,
46
+ }
47
+
48
+
49
+ @dataclass
50
+ class ToolChoice:
51
+ """Tool choice configuration per MCP 2025-11-25."""
52
+ mode: ToolChoiceMode
53
+ name: Optional[str] = None # Required when mode is TOOL
54
+
55
+ # Backwards compatibility alias
56
+ @property
57
+ def type(self) -> ToolChoiceMode:
58
+ return self.mode
59
+
60
+ @property
61
+ def tool_name(self) -> Optional[str]:
62
+ return self.name
63
+
64
+ def to_dict(self) -> Dict[str, Any]:
65
+ """Convert to MCP toolChoice format."""
66
+ result = {"mode": self.mode.value}
67
+ if self.mode == ToolChoiceMode.TOOL and self.name:
68
+ result["name"] = self.name
69
+ return result
70
+
71
+ @classmethod
72
+ def auto(cls) -> "ToolChoice":
73
+ return cls(mode=ToolChoiceMode.AUTO)
74
+
75
+ @classmethod
76
+ def none(cls) -> "ToolChoice":
77
+ return cls(mode=ToolChoiceMode.NONE)
78
+
79
+ @classmethod
80
+ def any(cls) -> "ToolChoice":
81
+ return cls(mode=ToolChoiceMode.ANY)
82
+
83
+ @classmethod
84
+ def tool(cls, name: str) -> "ToolChoice":
85
+ return cls(mode=ToolChoiceMode.TOOL, name=name)
86
+
87
+
88
+ @dataclass
89
+ class SamplingMessage:
90
+ """Message for sampling request."""
91
+ role: str # "user", "assistant", "system"
92
+ content: str
93
+
94
+ def to_dict(self) -> Dict[str, Any]:
95
+ return {
96
+ "role": self.role,
97
+ "content": {"type": "text", "text": self.content},
98
+ }
99
+
100
+
101
+ @dataclass
102
+ class ModelPreferences:
103
+ """Model preferences for sampling."""
104
+ hints: List[Dict[str, str]] = field(default_factory=list)
105
+ cost_priority: Optional[float] = None # 0-1, lower = prefer cheaper
106
+ speed_priority: Optional[float] = None # 0-1, lower = prefer faster
107
+ intelligence_priority: Optional[float] = None # 0-1, lower = prefer smarter
108
+
109
+ def to_dict(self) -> Dict[str, Any]:
110
+ result = {}
111
+ if self.hints:
112
+ result["hints"] = self.hints
113
+ if self.cost_priority is not None:
114
+ result["costPriority"] = self.cost_priority
115
+ if self.speed_priority is not None:
116
+ result["speedPriority"] = self.speed_priority
117
+ if self.intelligence_priority is not None:
118
+ result["intelligencePriority"] = self.intelligence_priority
119
+ return result
120
+
121
+
122
+ @dataclass
123
+ class SamplingRequest:
124
+ """
125
+ MCP Sampling request.
126
+
127
+ Allows servers to request LLM completions from clients.
128
+ """
129
+ messages: List[SamplingMessage]
130
+ system_prompt: Optional[str] = None
131
+ model_preferences: Optional[ModelPreferences] = None
132
+ max_tokens: int = 1024
133
+ temperature: Optional[float] = None
134
+ stop_sequences: List[str] = field(default_factory=list)
135
+
136
+ # Tool calling (MCP 2025-11-25)
137
+ tools: List[ToolDefinition] = field(default_factory=list)
138
+ tool_choice: Optional[ToolChoice] = None
139
+
140
+ # Metadata
141
+ include_context: Optional[str] = None # "none", "thisServer", "allServers"
142
+ metadata: Dict[str, Any] = field(default_factory=dict)
143
+
144
+ def to_dict(self) -> Dict[str, Any]:
145
+ result = {
146
+ "messages": [m.to_dict() for m in self.messages],
147
+ "maxTokens": self.max_tokens,
148
+ }
149
+
150
+ if self.system_prompt:
151
+ result["systemPrompt"] = self.system_prompt
152
+
153
+ if self.model_preferences:
154
+ result["modelPreferences"] = self.model_preferences.to_dict()
155
+
156
+ if self.temperature is not None:
157
+ result["temperature"] = self.temperature
158
+
159
+ if self.stop_sequences:
160
+ result["stopSequences"] = self.stop_sequences
161
+
162
+ # Tool calling
163
+ if self.tools:
164
+ result["tools"] = [t.to_dict() for t in self.tools]
165
+
166
+ if self.tool_choice:
167
+ result["toolChoice"] = self.tool_choice.to_dict()
168
+
169
+ if self.include_context:
170
+ result["includeContext"] = self.include_context
171
+
172
+ if self.metadata:
173
+ result["_meta"] = self.metadata
174
+
175
+ return result
176
+
177
+
178
+ @dataclass
179
+ class ToolCall:
180
+ """Tool call from sampling response."""
181
+ id: str
182
+ name: str
183
+ arguments: Dict[str, Any]
184
+
185
+ @classmethod
186
+ def from_dict(cls, data: Dict[str, Any]) -> "ToolCall":
187
+ return cls(
188
+ id=data.get("id", ""),
189
+ name=data.get("name", ""),
190
+ arguments=data.get("arguments", {}),
191
+ )
192
+
193
+
194
+ @dataclass
195
+ class SamplingResponse:
196
+ """
197
+ MCP Sampling response.
198
+
199
+ Contains the LLM completion result.
200
+ """
201
+ role: str
202
+ content: str
203
+ model: Optional[str] = None
204
+ stop_reason: Optional[str] = None
205
+
206
+ # Tool calls (MCP 2025-11-25)
207
+ tool_calls: List[ToolCall] = field(default_factory=list)
208
+
209
+ def to_dict(self) -> Dict[str, Any]:
210
+ result = {
211
+ "role": self.role,
212
+ "content": {"type": "text", "text": self.content},
213
+ }
214
+
215
+ if self.model:
216
+ result["model"] = self.model
217
+
218
+ if self.stop_reason:
219
+ result["stopReason"] = self.stop_reason
220
+
221
+ if self.tool_calls:
222
+ result["toolCalls"] = [
223
+ {"id": tc.id, "name": tc.name, "arguments": tc.arguments}
224
+ for tc in self.tool_calls
225
+ ]
226
+
227
+ return result
228
+
229
+ @classmethod
230
+ def from_dict(cls, data: Dict[str, Any]) -> "SamplingResponse":
231
+ content = data.get("content", {})
232
+ if isinstance(content, dict):
233
+ text = content.get("text", "")
234
+ else:
235
+ text = str(content)
236
+
237
+ tool_calls = []
238
+ if "toolCalls" in data:
239
+ tool_calls = [ToolCall.from_dict(tc) for tc in data["toolCalls"]]
240
+
241
+ return cls(
242
+ role=data.get("role", "assistant"),
243
+ content=text,
244
+ model=data.get("model"),
245
+ stop_reason=data.get("stopReason"),
246
+ tool_calls=tool_calls,
247
+ )
248
+
249
+
250
+ class SamplingHandler:
251
+ """
252
+ Handles sampling requests.
253
+
254
+ Can be configured with different backends:
255
+ - Client callback (for MCP client-side sampling)
256
+ - Direct LLM integration (for server-side sampling)
257
+ """
258
+
259
+ def __init__(
260
+ self,
261
+ callback: Optional[Callable] = None,
262
+ default_model: Optional[str] = None,
263
+ ):
264
+ """
265
+ Initialize sampling handler.
266
+
267
+ Args:
268
+ callback: Async callback for sampling requests
269
+ default_model: Default model to use
270
+ """
271
+ self._callback = callback
272
+ self._default_model = default_model
273
+
274
+ def set_callback(self, callback: Callable) -> None:
275
+ """Set the sampling callback."""
276
+ self._callback = callback
277
+
278
+ async def create_message(
279
+ self,
280
+ request: SamplingRequest,
281
+ ) -> SamplingResponse:
282
+ """
283
+ Create a sampling message.
284
+
285
+ Args:
286
+ request: Sampling request
287
+
288
+ Returns:
289
+ Sampling response
290
+ """
291
+ if self._callback:
292
+ return await self._callback(request)
293
+
294
+ # Fall back to direct LLM integration
295
+ return await self._direct_sampling(request)
296
+
297
+ async def _direct_sampling(
298
+ self,
299
+ request: SamplingRequest,
300
+ ) -> SamplingResponse:
301
+ """
302
+ Direct LLM sampling using praisonaiagents.
303
+
304
+ Falls back to this when no callback is configured.
305
+ """
306
+ try:
307
+ from praisonaiagents import Agent
308
+
309
+ # Build messages
310
+ messages = []
311
+ if request.system_prompt:
312
+ messages.append({"role": "system", "content": request.system_prompt})
313
+
314
+ for msg in request.messages:
315
+ messages.append({"role": msg.role, "content": msg.content})
316
+
317
+ # Create agent for sampling
318
+ agent = Agent(
319
+ instructions=request.system_prompt or "You are a helpful assistant.",
320
+ llm=self._default_model,
321
+ )
322
+
323
+ # Get last user message
324
+ last_message = ""
325
+ for msg in reversed(request.messages):
326
+ if msg.role == "user":
327
+ last_message = msg.content
328
+ break
329
+
330
+ # Execute
331
+ result = agent.chat(last_message)
332
+
333
+ return SamplingResponse(
334
+ role="assistant",
335
+ content=result,
336
+ model=self._default_model,
337
+ stop_reason="end_turn",
338
+ )
339
+
340
+ except ImportError:
341
+ return SamplingResponse(
342
+ role="assistant",
343
+ content="Sampling not available - praisonaiagents not installed",
344
+ stop_reason="error",
345
+ )
346
+ except Exception as e:
347
+ logger.exception("Sampling failed")
348
+ return SamplingResponse(
349
+ role="assistant",
350
+ content=f"Sampling error: {str(e)}",
351
+ stop_reason="error",
352
+ )
353
+
354
+
355
+ # Global sampling handler
356
+ _sampling_handler: Optional[SamplingHandler] = None
357
+
358
+
359
+ def get_sampling_handler() -> SamplingHandler:
360
+ """Get the global sampling handler."""
361
+ global _sampling_handler
362
+ if _sampling_handler is None:
363
+ _sampling_handler = SamplingHandler()
364
+ return _sampling_handler
365
+
366
+
367
+ def set_sampling_handler(handler: SamplingHandler) -> None:
368
+ """Set the global sampling handler."""
369
+ global _sampling_handler
370
+ _sampling_handler = handler
371
+
372
+
373
+ def create_sampling_request(
374
+ prompt: str,
375
+ system_prompt: Optional[str] = None,
376
+ max_tokens: int = 1024,
377
+ tools: Optional[List[Dict[str, Any]]] = None,
378
+ tool_choice: Optional[str] = None,
379
+ ) -> SamplingRequest:
380
+ """
381
+ Create a sampling request.
382
+
383
+ Args:
384
+ prompt: User prompt
385
+ system_prompt: System prompt
386
+ max_tokens: Maximum tokens
387
+ tools: Tool definitions
388
+ tool_choice: Tool choice type
389
+
390
+ Returns:
391
+ SamplingRequest
392
+ """
393
+ messages = [SamplingMessage(role="user", content=prompt)]
394
+
395
+ tool_defs = []
396
+ if tools:
397
+ for tool in tools:
398
+ tool_defs.append(ToolDefinition(
399
+ name=tool.get("name", ""),
400
+ description=tool.get("description", ""),
401
+ input_schema=tool.get("inputSchema", tool.get("input_schema", {})),
402
+ ))
403
+
404
+ tc = None
405
+ if tool_choice:
406
+ if tool_choice == "auto":
407
+ tc = ToolChoice.auto()
408
+ elif tool_choice == "none":
409
+ tc = ToolChoice.none()
410
+ elif tool_choice == "any":
411
+ tc = ToolChoice.any()
412
+ else:
413
+ # Specific tool name
414
+ tc = ToolChoice.tool(tool_choice)
415
+
416
+ return SamplingRequest(
417
+ messages=messages,
418
+ system_prompt=system_prompt,
419
+ max_tokens=max_tokens,
420
+ tools=tool_defs,
421
+ tool_choice=tc,
422
+ )