camel-ai 0.2.59__py3-none-any.whl → 0.2.82__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (506) hide show
  1. camel/__init__.py +3 -3
  2. camel/agents/__init__.py +2 -2
  3. camel/agents/_types.py +9 -4
  4. camel/agents/_utils.py +40 -2
  5. camel/agents/base.py +2 -2
  6. camel/agents/chat_agent.py +5012 -902
  7. camel/agents/critic_agent.py +2 -2
  8. camel/agents/deductive_reasoner_agent.py +56 -56
  9. camel/agents/embodied_agent.py +2 -2
  10. camel/agents/knowledge_graph_agent.py +20 -20
  11. camel/agents/mcp_agent.py +39 -36
  12. camel/agents/multi_hop_generator_agent.py +3 -3
  13. camel/agents/programmed_agent_instruction.py +2 -2
  14. camel/agents/repo_agent.py +4 -3
  15. camel/agents/role_assignment_agent.py +2 -2
  16. camel/agents/search_agent.py +2 -2
  17. camel/agents/task_agent.py +2 -2
  18. camel/agents/tool_agents/__init__.py +2 -2
  19. camel/agents/tool_agents/base.py +2 -2
  20. camel/agents/tool_agents/hugging_face_tool_agent.py +3 -3
  21. camel/benchmarks/__init__.py +2 -2
  22. camel/benchmarks/apibank.py +5 -5
  23. camel/benchmarks/apibench.py +2 -2
  24. camel/benchmarks/base.py +2 -2
  25. camel/benchmarks/browsecomp.py +44 -33
  26. camel/benchmarks/gaia.py +17 -13
  27. camel/benchmarks/mock_website/README.md +94 -0
  28. camel/benchmarks/mock_website/mock_web.py +299 -0
  29. camel/benchmarks/mock_website/requirements.txt +3 -0
  30. camel/benchmarks/mock_website/shopping_mall/app.py +465 -0
  31. camel/benchmarks/mock_website/task.json +104 -0
  32. camel/benchmarks/nexus.py +3 -3
  33. camel/benchmarks/ragbench.py +2 -2
  34. camel/bots/__init__.py +2 -2
  35. camel/bots/discord/__init__.py +2 -2
  36. camel/bots/discord/discord_app.py +2 -2
  37. camel/bots/discord/discord_installation.py +2 -2
  38. camel/bots/discord/discord_store.py +3 -3
  39. camel/bots/slack/__init__.py +2 -2
  40. camel/bots/slack/models.py +4 -4
  41. camel/bots/slack/slack_app.py +2 -2
  42. camel/bots/telegram_bot.py +2 -2
  43. camel/configs/__init__.py +26 -2
  44. camel/configs/aihubmix_config.py +90 -0
  45. camel/configs/aiml_config.py +2 -2
  46. camel/configs/amd_config.py +70 -0
  47. camel/configs/anthropic_config.py +8 -7
  48. camel/configs/base_config.py +2 -2
  49. camel/configs/bedrock_config.py +5 -3
  50. camel/configs/cerebras_config.py +98 -0
  51. camel/configs/cohere_config.py +3 -3
  52. camel/configs/cometapi_config.py +106 -0
  53. camel/configs/crynux_config.py +94 -0
  54. camel/configs/deepseek_config.py +9 -8
  55. camel/configs/gemini_config.py +6 -4
  56. camel/configs/groq_config.py +6 -4
  57. camel/configs/internlm_config.py +6 -4
  58. camel/configs/litellm_config.py +2 -2
  59. camel/configs/lmstudio_config.py +6 -4
  60. camel/configs/minimax_config.py +95 -0
  61. camel/configs/mistral_config.py +3 -3
  62. camel/configs/modelscope_config.py +5 -3
  63. camel/configs/moonshot_config.py +2 -2
  64. camel/configs/nebius_config.py +105 -0
  65. camel/configs/netmind_config.py +2 -2
  66. camel/configs/novita_config.py +2 -2
  67. camel/configs/nvidia_config.py +2 -2
  68. camel/configs/ollama_config.py +2 -2
  69. camel/configs/openai_config.py +8 -3
  70. camel/configs/openrouter_config.py +6 -4
  71. camel/configs/ppio_config.py +2 -2
  72. camel/configs/qianfan_config.py +85 -0
  73. camel/configs/qwen_config.py +2 -2
  74. camel/configs/reka_config.py +3 -3
  75. camel/configs/samba_config.py +8 -6
  76. camel/configs/sglang_config.py +2 -2
  77. camel/configs/siliconflow_config.py +2 -2
  78. camel/configs/togetherai_config.py +2 -2
  79. camel/configs/vllm_config.py +4 -2
  80. camel/configs/watsonx_config.py +2 -2
  81. camel/configs/yi_config.py +6 -4
  82. camel/configs/zhipuai_config.py +6 -4
  83. camel/{data_collector → data_collectors}/__init__.py +2 -2
  84. camel/{data_collector → data_collectors}/alpaca_collector.py +19 -10
  85. camel/{data_collector → data_collectors}/base.py +2 -2
  86. camel/{data_collector → data_collectors}/sharegpt_collector.py +3 -3
  87. camel/datagen/__init__.py +2 -2
  88. camel/datagen/cot_datagen.py +32 -37
  89. camel/datagen/evol_instruct/__init__.py +2 -2
  90. camel/datagen/evol_instruct/evol_instruct.py +2 -2
  91. camel/datagen/evol_instruct/scorer.py +24 -25
  92. camel/datagen/evol_instruct/templates.py +48 -48
  93. camel/datagen/self_improving_cot.py +5 -5
  94. camel/datagen/self_instruct/__init__.py +2 -2
  95. camel/datagen/self_instruct/filter/__init__.py +2 -2
  96. camel/datagen/self_instruct/filter/filter_function.py +2 -2
  97. camel/datagen/self_instruct/filter/filter_registry.py +2 -2
  98. camel/datagen/self_instruct/filter/instruction_filter.py +2 -2
  99. camel/datagen/self_instruct/self_instruct.py +2 -2
  100. camel/datagen/self_instruct/templates.py +47 -47
  101. camel/datagen/source2synth/__init__.py +2 -2
  102. camel/datagen/source2synth/data_processor.py +2 -2
  103. camel/datagen/source2synth/models.py +2 -2
  104. camel/datagen/source2synth/user_data_processor_config.py +2 -2
  105. camel/datahubs/__init__.py +2 -2
  106. camel/datahubs/base.py +2 -2
  107. camel/datahubs/huggingface.py +2 -2
  108. camel/datahubs/models.py +2 -2
  109. camel/datasets/__init__.py +2 -2
  110. camel/datasets/base_generator.py +41 -12
  111. camel/datasets/few_shot_generator.py +18 -18
  112. camel/datasets/models.py +3 -3
  113. camel/datasets/self_instruct_generator.py +2 -2
  114. camel/datasets/static_dataset.py +152 -2
  115. camel/embeddings/__init__.py +2 -2
  116. camel/embeddings/azure_embedding.py +2 -2
  117. camel/embeddings/base.py +2 -2
  118. camel/embeddings/gemini_embedding.py +2 -2
  119. camel/embeddings/jina_embedding.py +10 -3
  120. camel/embeddings/mistral_embedding.py +2 -2
  121. camel/embeddings/openai_compatible_embedding.py +2 -2
  122. camel/embeddings/openai_embedding.py +2 -2
  123. camel/embeddings/sentence_transformers_embeddings.py +4 -4
  124. camel/embeddings/together_embedding.py +2 -2
  125. camel/embeddings/vlm_embedding.py +11 -4
  126. camel/environments/__init__.py +14 -2
  127. camel/environments/models.py +2 -2
  128. camel/environments/multi_step.py +2 -2
  129. camel/environments/rlcards_env.py +860 -0
  130. camel/environments/single_step.py +30 -5
  131. camel/environments/tic_tac_toe.py +3 -3
  132. camel/extractors/__init__.py +2 -2
  133. camel/extractors/base.py +2 -2
  134. camel/extractors/python_strategies.py +2 -2
  135. camel/generators.py +2 -2
  136. camel/human.py +2 -2
  137. camel/interpreters/__init__.py +4 -2
  138. camel/interpreters/base.py +16 -3
  139. camel/interpreters/docker/Dockerfile +53 -7
  140. camel/interpreters/docker_interpreter.py +70 -11
  141. camel/interpreters/e2b_interpreter.py +59 -11
  142. camel/interpreters/internal_python_interpreter.py +81 -4
  143. camel/interpreters/interpreter_error.py +2 -2
  144. camel/interpreters/ipython_interpreter.py +23 -5
  145. camel/interpreters/microsandbox_interpreter.py +395 -0
  146. camel/interpreters/subprocess_interpreter.py +36 -4
  147. camel/loaders/__init__.py +17 -5
  148. camel/loaders/apify_reader.py +2 -2
  149. camel/loaders/base_io.py +2 -2
  150. camel/loaders/base_loader.py +85 -0
  151. camel/loaders/chunkr_reader.py +128 -93
  152. camel/loaders/crawl4ai_reader.py +2 -2
  153. camel/loaders/firecrawl_reader.py +6 -6
  154. camel/loaders/jina_url_reader.py +2 -2
  155. camel/loaders/markitdown.py +2 -2
  156. camel/loaders/mineru_extractor.py +2 -2
  157. camel/loaders/mistral_reader.py +148 -0
  158. camel/loaders/scrapegraph_reader.py +2 -2
  159. camel/loaders/unstructured_io.py +2 -2
  160. camel/logger.py +5 -5
  161. camel/memories/__init__.py +2 -2
  162. camel/memories/agent_memories.py +86 -3
  163. camel/memories/base.py +36 -2
  164. camel/memories/blocks/__init__.py +2 -2
  165. camel/memories/blocks/chat_history_block.py +126 -9
  166. camel/memories/blocks/vectordb_block.py +10 -3
  167. camel/memories/context_creators/__init__.py +2 -2
  168. camel/memories/context_creators/score_based.py +31 -239
  169. camel/memories/records.py +98 -13
  170. camel/messages/__init__.py +2 -2
  171. camel/messages/base.py +193 -46
  172. camel/messages/conversion/__init__.py +2 -2
  173. camel/messages/conversion/alpaca.py +2 -2
  174. camel/messages/conversion/conversation_models.py +2 -2
  175. camel/messages/conversion/sharegpt/__init__.py +2 -2
  176. camel/messages/conversion/sharegpt/function_call_formatter.py +2 -2
  177. camel/messages/conversion/sharegpt/hermes/__init__.py +2 -2
  178. camel/messages/conversion/sharegpt/hermes/hermes_function_formatter.py +2 -2
  179. camel/messages/func_message.py +54 -17
  180. camel/models/__init__.py +18 -2
  181. camel/models/_utils.py +3 -3
  182. camel/models/aihubmix_model.py +83 -0
  183. camel/models/aiml_model.py +11 -18
  184. camel/models/amd_model.py +101 -0
  185. camel/models/anthropic_model.py +127 -20
  186. camel/models/aws_bedrock_model.py +12 -35
  187. camel/models/azure_openai_model.py +263 -63
  188. camel/models/base_audio_model.py +5 -3
  189. camel/models/base_model.py +195 -26
  190. camel/models/cerebras_model.py +83 -0
  191. camel/models/cohere_model.py +81 -21
  192. camel/models/cometapi_model.py +83 -0
  193. camel/models/crynux_model.py +87 -0
  194. camel/models/deepseek_model.py +61 -59
  195. camel/models/fish_audio_model.py +8 -2
  196. camel/models/gemini_model.py +439 -30
  197. camel/models/groq_model.py +11 -19
  198. camel/models/internlm_model.py +11 -18
  199. camel/models/litellm_model.py +94 -34
  200. camel/models/lmstudio_model.py +17 -20
  201. camel/models/minimax_model.py +83 -0
  202. camel/models/mistral_model.py +84 -19
  203. camel/models/model_factory.py +49 -6
  204. camel/models/model_manager.py +33 -11
  205. camel/models/modelscope_model.py +13 -193
  206. camel/models/moonshot_model.py +195 -21
  207. camel/models/nebius_model.py +83 -0
  208. camel/models/nemotron_model.py +19 -9
  209. camel/models/netmind_model.py +11 -18
  210. camel/models/novita_model.py +11 -18
  211. camel/models/nvidia_model.py +11 -18
  212. camel/models/ollama_model.py +14 -21
  213. camel/models/openai_audio_models.py +2 -2
  214. camel/models/openai_compatible_model.py +234 -27
  215. camel/models/openai_model.py +255 -39
  216. camel/models/openrouter_model.py +11 -19
  217. camel/models/ppio_model.py +11 -18
  218. camel/models/qianfan_model.py +89 -0
  219. camel/models/qwen_model.py +13 -193
  220. camel/models/reka_model.py +90 -21
  221. camel/models/reward/__init__.py +2 -2
  222. camel/models/reward/base_reward_model.py +2 -2
  223. camel/models/reward/evaluator.py +2 -2
  224. camel/models/reward/nemotron_model.py +2 -2
  225. camel/models/reward/skywork_model.py +2 -2
  226. camel/models/samba_model.py +117 -49
  227. camel/models/sglang_model.py +162 -42
  228. camel/models/siliconflow_model.py +12 -35
  229. camel/models/stub_model.py +10 -7
  230. camel/models/togetherai_model.py +11 -18
  231. camel/models/vllm_model.py +10 -18
  232. camel/models/volcano_model.py +16 -20
  233. camel/models/watsonx_model.py +69 -19
  234. camel/models/yi_model.py +11 -18
  235. camel/models/zhipuai_model.py +70 -18
  236. camel/parsers/__init__.py +18 -0
  237. camel/parsers/mcp_tool_call_parser.py +176 -0
  238. camel/personas/__init__.py +2 -2
  239. camel/personas/persona.py +2 -2
  240. camel/personas/persona_hub.py +2 -2
  241. camel/prompts/__init__.py +2 -2
  242. camel/prompts/ai_society.py +2 -2
  243. camel/prompts/base.py +2 -2
  244. camel/prompts/code.py +2 -2
  245. camel/prompts/evaluation.py +2 -2
  246. camel/prompts/generate_text_embedding_data.py +2 -2
  247. camel/prompts/image_craft.py +2 -2
  248. camel/prompts/misalignment.py +2 -2
  249. camel/prompts/multi_condition_image_craft.py +2 -2
  250. camel/prompts/object_recognition.py +2 -2
  251. camel/prompts/persona_hub.py +3 -3
  252. camel/prompts/prompt_templates.py +2 -2
  253. camel/prompts/role_description_prompt_template.py +2 -2
  254. camel/prompts/solution_extraction.py +8 -8
  255. camel/prompts/task_prompt_template.py +2 -2
  256. camel/prompts/translation.py +2 -2
  257. camel/prompts/video_description_prompt.py +3 -3
  258. camel/responses/__init__.py +2 -2
  259. camel/responses/agent_responses.py +2 -2
  260. camel/retrievers/__init__.py +2 -2
  261. camel/retrievers/auto_retriever.py +23 -3
  262. camel/retrievers/base.py +2 -2
  263. camel/retrievers/bm25_retriever.py +3 -4
  264. camel/retrievers/cohere_rerank_retriever.py +2 -2
  265. camel/retrievers/hybrid_retrival.py +4 -4
  266. camel/retrievers/vector_retriever.py +2 -2
  267. camel/runtimes/Dockerfile.multi-toolkit +90 -0
  268. camel/{runtime → runtimes}/__init__.py +2 -2
  269. camel/runtimes/api.py +153 -0
  270. camel/{runtime → runtimes}/base.py +2 -2
  271. camel/{runtime → runtimes}/configs.py +13 -13
  272. camel/{runtime → runtimes}/daytona_runtime.py +18 -19
  273. camel/{runtime → runtimes}/docker_runtime.py +13 -13
  274. camel/{runtime → runtimes}/llm_guard_runtime.py +28 -28
  275. camel/{runtime → runtimes}/remote_http_runtime.py +12 -12
  276. camel/{runtime → runtimes}/ubuntu_docker_runtime.py +3 -3
  277. camel/{runtime → runtimes}/utils/__init__.py +2 -2
  278. camel/{runtime → runtimes}/utils/function_risk_toolkit.py +2 -2
  279. camel/{runtime → runtimes}/utils/ignore_risk_toolkit.py +2 -2
  280. camel/schemas/__init__.py +2 -2
  281. camel/schemas/base.py +2 -2
  282. camel/schemas/openai_converter.py +3 -3
  283. camel/schemas/outlines_converter.py +2 -2
  284. camel/services/agent_openapi_server.py +380 -0
  285. camel/societies/__init__.py +4 -2
  286. camel/societies/babyagi_playing.py +2 -2
  287. camel/societies/role_playing.py +201 -80
  288. camel/societies/workforce/__init__.py +10 -3
  289. camel/societies/workforce/base.py +9 -5
  290. camel/societies/workforce/events.py +143 -0
  291. camel/societies/workforce/prompts.py +258 -33
  292. camel/societies/workforce/role_playing_worker.py +95 -30
  293. camel/societies/workforce/single_agent_worker.py +659 -30
  294. camel/societies/workforce/structured_output_handler.py +512 -0
  295. camel/societies/workforce/task_channel.py +182 -38
  296. camel/societies/workforce/utils.py +784 -18
  297. camel/societies/workforce/worker.py +96 -28
  298. camel/societies/workforce/workflow_memory_manager.py +1746 -0
  299. camel/societies/workforce/workforce.py +5730 -366
  300. camel/societies/workforce/workforce_callback.py +103 -0
  301. camel/societies/workforce/workforce_logger.py +647 -0
  302. camel/societies/workforce/workforce_metrics.py +33 -0
  303. camel/storages/__init__.py +10 -2
  304. camel/storages/graph_storages/__init__.py +2 -2
  305. camel/storages/graph_storages/base.py +2 -2
  306. camel/storages/graph_storages/graph_element.py +2 -2
  307. camel/storages/graph_storages/nebula_graph.py +4 -4
  308. camel/storages/graph_storages/neo4j_graph.py +7 -7
  309. camel/storages/key_value_storages/__init__.py +2 -2
  310. camel/storages/key_value_storages/base.py +2 -2
  311. camel/storages/key_value_storages/in_memory.py +2 -2
  312. camel/storages/key_value_storages/json.py +17 -4
  313. camel/storages/key_value_storages/mem0_cloud.py +50 -49
  314. camel/storages/key_value_storages/redis.py +2 -2
  315. camel/storages/object_storages/__init__.py +2 -2
  316. camel/storages/object_storages/amazon_s3.py +2 -2
  317. camel/storages/object_storages/azure_blob.py +2 -2
  318. camel/storages/object_storages/base.py +2 -2
  319. camel/storages/object_storages/google_cloud.py +3 -3
  320. camel/storages/vectordb_storages/__init__.py +12 -2
  321. camel/storages/vectordb_storages/base.py +2 -2
  322. camel/storages/vectordb_storages/chroma.py +731 -0
  323. camel/storages/vectordb_storages/faiss.py +712 -0
  324. camel/storages/vectordb_storages/milvus.py +2 -2
  325. camel/storages/vectordb_storages/oceanbase.py +16 -17
  326. camel/storages/vectordb_storages/pgvector.py +349 -0
  327. camel/storages/vectordb_storages/qdrant.py +6 -6
  328. camel/storages/vectordb_storages/surreal.py +372 -0
  329. camel/storages/vectordb_storages/tidb.py +11 -8
  330. camel/storages/vectordb_storages/weaviate.py +714 -0
  331. camel/tasks/__init__.py +2 -2
  332. camel/tasks/task.py +366 -27
  333. camel/tasks/task_prompt.py +3 -3
  334. camel/terminators/__init__.py +2 -2
  335. camel/terminators/base.py +2 -2
  336. camel/terminators/response_terminator.py +2 -2
  337. camel/terminators/token_limit_terminator.py +2 -2
  338. camel/toolkits/__init__.py +58 -10
  339. camel/toolkits/aci_toolkit.py +66 -21
  340. camel/toolkits/arxiv_toolkit.py +8 -8
  341. camel/toolkits/ask_news_toolkit.py +2 -2
  342. camel/toolkits/async_browser_toolkit.py +174 -575
  343. camel/toolkits/audio_analysis_toolkit.py +3 -3
  344. camel/toolkits/base.py +65 -7
  345. camel/toolkits/bohrium_toolkit.py +318 -0
  346. camel/toolkits/browser_toolkit.py +306 -566
  347. camel/toolkits/browser_toolkit_commons.py +568 -0
  348. camel/toolkits/code_execution.py +67 -11
  349. camel/toolkits/context_summarizer_toolkit.py +684 -0
  350. camel/toolkits/craw4ai_toolkit.py +93 -0
  351. camel/toolkits/dappier_toolkit.py +12 -8
  352. camel/toolkits/data_commons_toolkit.py +2 -2
  353. camel/toolkits/dingtalk.py +1135 -0
  354. camel/toolkits/earth_science_toolkit.py +5367 -0
  355. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  356. camel/toolkits/excel_toolkit.py +910 -70
  357. camel/toolkits/file_toolkit.py +1402 -0
  358. camel/toolkits/function_tool.py +128 -20
  359. camel/toolkits/github_toolkit.py +148 -43
  360. camel/toolkits/gmail_toolkit.py +1839 -0
  361. camel/toolkits/google_calendar_toolkit.py +40 -6
  362. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  363. camel/toolkits/google_maps_toolkit.py +2 -2
  364. camel/toolkits/google_scholar_toolkit.py +2 -2
  365. camel/toolkits/human_toolkit.py +36 -12
  366. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  367. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  368. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  369. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  370. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  371. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4589 -0
  372. camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
  373. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  374. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1929 -0
  375. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  376. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +589 -0
  377. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  378. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  379. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  380. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  381. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +129 -0
  382. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +27 -0
  383. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  384. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1037 -0
  385. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  386. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  387. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  388. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  389. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  390. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  391. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  392. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  393. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  394. camel/toolkits/image_analysis_toolkit.py +3 -3
  395. camel/toolkits/image_generation_toolkit.py +390 -0
  396. camel/toolkits/jina_reranker_toolkit.py +195 -79
  397. camel/toolkits/klavis_toolkit.py +7 -3
  398. camel/toolkits/linkedin_toolkit.py +2 -2
  399. camel/toolkits/markitdown_toolkit.py +104 -0
  400. camel/toolkits/math_toolkit.py +66 -12
  401. camel/toolkits/mcp_toolkit.py +841 -600
  402. camel/toolkits/memory_toolkit.py +7 -3
  403. camel/toolkits/meshy_toolkit.py +2 -2
  404. camel/toolkits/message_agent_toolkit.py +608 -0
  405. camel/toolkits/message_integration.py +724 -0
  406. camel/toolkits/mineru_toolkit.py +2 -2
  407. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  408. camel/toolkits/networkx_toolkit.py +2 -2
  409. camel/toolkits/note_taking_toolkit.py +277 -0
  410. camel/toolkits/notion_mcp_toolkit.py +224 -0
  411. camel/toolkits/notion_toolkit.py +2 -2
  412. camel/toolkits/open_api_specs/biztoc/__init__.py +2 -2
  413. camel/toolkits/open_api_specs/biztoc/ai-plugin.json +1 -1
  414. camel/toolkits/open_api_specs/coursera/__init__.py +2 -2
  415. camel/toolkits/open_api_specs/create_qr_code/__init__.py +2 -2
  416. camel/toolkits/open_api_specs/klarna/__init__.py +2 -2
  417. camel/toolkits/open_api_specs/nasa_apod/__init__.py +2 -2
  418. camel/toolkits/open_api_specs/outschool/__init__.py +2 -2
  419. camel/toolkits/open_api_specs/outschool/ai-plugin.json +1 -1
  420. camel/toolkits/open_api_specs/outschool/openapi.yaml +1 -1
  421. camel/toolkits/open_api_specs/outschool/paths/__init__.py +2 -2
  422. camel/toolkits/open_api_specs/outschool/paths/get_classes.py +2 -2
  423. camel/toolkits/open_api_specs/outschool/paths/search_teachers.py +2 -2
  424. camel/toolkits/open_api_specs/security_config.py +2 -2
  425. camel/toolkits/open_api_specs/speak/__init__.py +2 -2
  426. camel/toolkits/open_api_specs/web_scraper/__init__.py +2 -2
  427. camel/toolkits/open_api_specs/web_scraper/ai-plugin.json +1 -1
  428. camel/toolkits/open_api_specs/web_scraper/paths/__init__.py +2 -2
  429. camel/toolkits/open_api_specs/web_scraper/paths/scraper.py +2 -2
  430. camel/toolkits/open_api_toolkit.py +2 -2
  431. camel/toolkits/openbb_toolkit.py +7 -3
  432. camel/toolkits/origene_mcp_toolkit.py +56 -0
  433. camel/toolkits/page_script.js +86 -74
  434. camel/toolkits/playwright_mcp_toolkit.py +27 -32
  435. camel/toolkits/pptx_toolkit.py +790 -0
  436. camel/toolkits/pubmed_toolkit.py +2 -2
  437. camel/toolkits/pulse_mcp_search_toolkit.py +2 -2
  438. camel/toolkits/pyautogui_toolkit.py +2 -2
  439. camel/toolkits/reddit_toolkit.py +2 -2
  440. camel/toolkits/resend_toolkit.py +168 -0
  441. camel/toolkits/retrieval_toolkit.py +2 -2
  442. camel/toolkits/screenshot_toolkit.py +213 -0
  443. camel/toolkits/search_toolkit.py +539 -146
  444. camel/toolkits/searxng_toolkit.py +2 -2
  445. camel/toolkits/semantic_scholar_toolkit.py +2 -2
  446. camel/toolkits/slack_toolkit.py +108 -58
  447. camel/toolkits/sql_toolkit.py +712 -0
  448. camel/toolkits/stripe_toolkit.py +2 -2
  449. camel/toolkits/sympy_toolkit.py +3 -3
  450. camel/toolkits/task_planning_toolkit.py +134 -0
  451. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  452. camel/toolkits/terminal_toolkit/terminal_toolkit.py +1070 -0
  453. camel/toolkits/terminal_toolkit/utils.py +532 -0
  454. camel/toolkits/thinking_toolkit.py +3 -3
  455. camel/toolkits/twitter_toolkit.py +8 -3
  456. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  457. camel/toolkits/video_analysis_toolkit.py +112 -29
  458. camel/toolkits/video_download_toolkit.py +22 -16
  459. camel/toolkits/weather_toolkit.py +2 -2
  460. camel/toolkits/web_deploy_toolkit.py +1219 -0
  461. camel/toolkits/wechat_official_toolkit.py +483 -0
  462. camel/toolkits/whatsapp_toolkit.py +2 -2
  463. camel/toolkits/wolfram_alpha_toolkit.py +53 -25
  464. camel/toolkits/zapier_toolkit.py +7 -3
  465. camel/types/__init__.py +4 -4
  466. camel/types/agents/__init__.py +2 -2
  467. camel/types/agents/tool_calling_record.py +6 -3
  468. camel/types/enums.py +454 -35
  469. camel/types/mcp_registries.py +2 -2
  470. camel/types/openai_types.py +4 -4
  471. camel/types/unified_model_type.py +43 -6
  472. camel/utils/__init__.py +20 -2
  473. camel/utils/async_func.py +2 -2
  474. camel/utils/chunker/__init__.py +2 -2
  475. camel/utils/chunker/base.py +2 -2
  476. camel/utils/chunker/code_chunker.py +2 -2
  477. camel/utils/chunker/uio_chunker.py +2 -2
  478. camel/utils/commons.py +65 -7
  479. camel/utils/constants.py +5 -2
  480. camel/utils/context_utils.py +1134 -0
  481. camel/utils/deduplication.py +2 -2
  482. camel/utils/filename.py +2 -2
  483. camel/utils/langfuse.py +258 -0
  484. camel/utils/mcp.py +140 -6
  485. camel/utils/mcp_client.py +1056 -0
  486. camel/utils/message_summarizer.py +148 -0
  487. camel/utils/response_format.py +2 -2
  488. camel/utils/token_counting.py +45 -22
  489. camel/utils/tool_result.py +44 -0
  490. camel/verifiers/__init__.py +2 -2
  491. camel/verifiers/base.py +2 -2
  492. camel/verifiers/math_verifier.py +2 -2
  493. camel/verifiers/models.py +2 -2
  494. camel/verifiers/physics_verifier.py +2 -2
  495. camel/verifiers/python_verifier.py +2 -2
  496. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/METADATA +349 -108
  497. camel_ai-0.2.82.dist-info/RECORD +507 -0
  498. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
  499. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/licenses/LICENSE +1 -1
  500. camel/loaders/pandas_reader.py +0 -368
  501. camel/runtime/api.py +0 -97
  502. camel/toolkits/dalle_toolkit.py +0 -171
  503. camel/toolkits/file_write_toolkit.py +0 -395
  504. camel/toolkits/openai_agent_toolkit.py +0 -135
  505. camel/toolkits/terminal_toolkit.py +0 -1037
  506. camel_ai-0.2.59.dist-info/RECORD +0 -410
@@ -0,0 +1,790 @@
1
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+
16
+ import os
17
+ import random
18
+ import re
19
+ from pathlib import Path
20
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
21
+
22
+ if TYPE_CHECKING:
23
+ from pptx import presentation
24
+ from pptx.slide import Slide
25
+ from pptx.text.text import TextFrame
26
+
27
+ from camel.logger import get_logger
28
+ from camel.toolkits.base import BaseToolkit
29
+ from camel.toolkits.function_tool import FunctionTool
30
+ from camel.utils import MCPServer, api_keys_required
31
+
32
+ logger = get_logger(__name__)
33
+
34
+ # Constants
35
+ EMU_TO_INCH_SCALING_FACTOR = 1.0 / 914400
36
+
37
+ STEP_BY_STEP_PROCESS_MARKER = '>> '
38
+
39
+ IMAGE_DISPLAY_PROBABILITY = 1 / 3.0
40
+
41
+ SLIDE_NUMBER_REGEX = re.compile(r"^slide[ ]+\d+:", re.IGNORECASE)
42
+ BOLD_ITALICS_PATTERN = re.compile(r'(\*\*(.*?)\*\*|\*(.*?)\*)')
43
+
44
+
45
+ @MCPServer()
46
+ class PPTXToolkit(BaseToolkit):
47
+ r"""A toolkit for creating and writing PowerPoint presentations (PPTX
48
+ files).
49
+
50
+ This class provides cross-platform support for creating PPTX files with
51
+ title slides, content slides, text formatting, and image embedding.
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ working_directory: Optional[str] = None,
57
+ timeout: Optional[float] = None,
58
+ ) -> None:
59
+ r"""Initialize the PPTXToolkit.
60
+
61
+ Args:
62
+ working_directory (str, optional): The default directory for
63
+ output files. If not provided, it will be determined by the
64
+ `CAMEL_WORKDIR` environment variable (if set). If the
65
+ environment variable is not set, it defaults to
66
+ `camel_working_dir`.
67
+ timeout (Optional[float]): The timeout for the toolkit.
68
+ (default: :obj:`None`)
69
+ """
70
+ super().__init__(timeout=timeout)
71
+
72
+ if working_directory:
73
+ self.working_directory = Path(working_directory).resolve()
74
+ else:
75
+ camel_workdir = os.environ.get("CAMEL_WORKDIR")
76
+ if camel_workdir:
77
+ self.working_directory = Path(camel_workdir).resolve()
78
+ else:
79
+ self.working_directory = Path("./camel_working_dir").resolve()
80
+
81
+ self.working_directory.mkdir(parents=True, exist_ok=True)
82
+ logger.info(
83
+ f"PPTXToolkit initialized with output directory: "
84
+ f"{self.working_directory}"
85
+ )
86
+
87
+ def _resolve_filepath(self, file_path: str) -> Path:
88
+ r"""Convert the given string path to a Path object.
89
+
90
+ If the provided path is not absolute, it is made relative to the
91
+ default output directory. The filename part is sanitized to replace
92
+ spaces and special characters with underscores, ensuring safe usage
93
+ in downstream processing.
94
+
95
+ Args:
96
+ file_path (str): The file path to resolve.
97
+
98
+ Returns:
99
+ Path: A fully resolved (absolute) and sanitized Path object.
100
+ """
101
+ path_obj = Path(file_path)
102
+ if not path_obj.is_absolute():
103
+ path_obj = self.working_directory / path_obj
104
+
105
+ sanitized_filename = self._sanitize_filename(path_obj.name)
106
+ path_obj = path_obj.parent / sanitized_filename
107
+ return path_obj.resolve()
108
+
109
+ def _sanitize_filename(self, filename: str) -> str:
110
+ r"""Sanitize a filename by replacing special characters and spaces.
111
+
112
+ Args:
113
+ filename (str): The filename to sanitize.
114
+
115
+ Returns:
116
+ str: The sanitized filename.
117
+ """
118
+ import re
119
+
120
+ # Replace spaces and special characters with underscores
121
+ sanitized = re.sub(r'[^\w\-_\.]', '_', filename)
122
+ # Remove multiple consecutive underscores
123
+ sanitized = re.sub(r'_+', '_', sanitized)
124
+ return sanitized
125
+
126
+ def _format_text(
127
+ self, frame_paragraph, text: str, set_color_to_white=False
128
+ ) -> None:
129
+ r"""Apply bold and italic formatting while preserving the original
130
+ word order.
131
+
132
+ Args:
133
+ frame_paragraph: The paragraph to format.
134
+ text (str): The text to format.
135
+ set_color_to_white (bool): Whether to set the color to white.
136
+ (default: :obj:`False`)
137
+ """
138
+ from pptx.dml.color import RGBColor
139
+
140
+ matches = list(BOLD_ITALICS_PATTERN.finditer(text))
141
+ last_index = 0
142
+
143
+ for match in matches:
144
+ start, end = match.span()
145
+ if start > last_index:
146
+ run = frame_paragraph.add_run()
147
+ run.text = text[last_index:start]
148
+ if set_color_to_white:
149
+ run.font.color.rgb = RGBColor(255, 255, 255)
150
+
151
+ if match.group(2): # Bold
152
+ run = frame_paragraph.add_run()
153
+ run.text = match.group(2)
154
+ run.font.bold = True
155
+ if set_color_to_white:
156
+ run.font.color.rgb = RGBColor(255, 255, 255)
157
+ elif match.group(3): # Italics
158
+ run = frame_paragraph.add_run()
159
+ run.text = match.group(3)
160
+ run.font.italic = True
161
+ if set_color_to_white:
162
+ run.font.color.rgb = RGBColor(255, 255, 255)
163
+
164
+ last_index = end
165
+
166
+ if last_index < len(text):
167
+ run = frame_paragraph.add_run()
168
+ run.text = text[last_index:]
169
+ if set_color_to_white:
170
+ run.font.color.rgb = RGBColor(255, 255, 255)
171
+
172
+ def _add_bulleted_items(
173
+ self,
174
+ text_frame: "TextFrame",
175
+ flat_items_list: List[Tuple[str, int]],
176
+ set_color_to_white: bool = False,
177
+ ) -> None:
178
+ r"""Add a list of texts as bullet points and apply formatting.
179
+
180
+ Args:
181
+ text_frame (TextFrame): The text frame where text is to be
182
+ displayed.
183
+ flat_items_list (List[Tuple[str, int]]): The list of items to be
184
+ displayed.
185
+ set_color_to_white (bool): Whether to set the font color to white.
186
+ (default: :obj:`False`)
187
+ """
188
+ if not flat_items_list:
189
+ logger.warning("Empty bullet point list provided")
190
+ return
191
+ for idx, item_content in enumerate(flat_items_list):
192
+ item_text, item_level = item_content
193
+
194
+ if idx == 0:
195
+ if not text_frame.paragraphs:
196
+ # Ensure a paragraph exists if the frame is empty or
197
+ # cleared
198
+ paragraph = text_frame.add_paragraph()
199
+ else:
200
+ # Use the first existing paragraph
201
+ paragraph = text_frame.paragraphs[0]
202
+ else:
203
+ paragraph = text_frame.add_paragraph()
204
+
205
+ paragraph.level = item_level
206
+
207
+ self._format_text(
208
+ paragraph,
209
+ item_text.removeprefix(STEP_BY_STEP_PROCESS_MARKER),
210
+ set_color_to_white=set_color_to_white,
211
+ )
212
+
213
+ def _get_flat_list_of_contents(
214
+ self, items: List[Union[str, List[Any]]], level: int
215
+ ) -> List[Tuple[str, int]]:
216
+ r"""Flatten a hierarchical list of bullet points to a single list.
217
+
218
+ Args:
219
+ items (List[Union[str, List[Any]]]): A bullet point (string or
220
+ list).
221
+ level (int): The current level of hierarchy.
222
+
223
+ Returns:
224
+ List[Tuple[str, int]]: A list of (bullet item text, hierarchical
225
+ level) tuples.
226
+ """
227
+ flat_list = []
228
+
229
+ for item in items:
230
+ if isinstance(item, str):
231
+ flat_list.append((item, level))
232
+ elif isinstance(item, list):
233
+ flat_list.extend(
234
+ self._get_flat_list_of_contents(item, level + 1)
235
+ )
236
+
237
+ return flat_list
238
+
239
+ def _get_slide_width_height_inches(
240
+ self, presentation: "presentation.Presentation"
241
+ ) -> Tuple[float, float]:
242
+ r"""Get the dimensions of a slide in inches.
243
+
244
+ Args:
245
+ presentation (presentation.Presentation): The presentation object.
246
+
247
+ Returns:
248
+ Tuple[float, float]: The width and height in inches.
249
+ """
250
+ slide_width_inch = EMU_TO_INCH_SCALING_FACTOR * (
251
+ presentation.slide_width or 0
252
+ )
253
+ slide_height_inch = EMU_TO_INCH_SCALING_FACTOR * (
254
+ presentation.slide_height or 0
255
+ )
256
+ return slide_width_inch, slide_height_inch
257
+
258
+ def _write_pptx_file(
259
+ self,
260
+ file_path: Path,
261
+ content: List[Dict[str, Any]],
262
+ template: Optional[str] = None,
263
+ ) -> None:
264
+ r"""Write text content to a PPTX file with enhanced formatting.
265
+
266
+ Args:
267
+ file_path (Path): The target file path.
268
+ content (List[Dict[str, Any]]): The content to write to the PPTX
269
+ file. Must be a list of dictionaries where:
270
+ - First element: Title slide with keys 'title' and 'subtitle'
271
+ - Subsequent elements: Content slides with keys 'title', 'text'
272
+ template (Optional[str]): The name of the template to use. If not
273
+ provided, the default template will be used. (default: :obj:
274
+ `None`)
275
+ """
276
+ from pptx import Presentation
277
+
278
+ # Use template if provided, otherwise create new presentation
279
+ if template is not None:
280
+ template_path = Path(template).resolve()
281
+ if not template_path.exists():
282
+ logger.warning(
283
+ f"Template file not found: {template_path}, using "
284
+ "default template"
285
+ )
286
+ presentation = Presentation()
287
+ else:
288
+ presentation = Presentation(str(template_path))
289
+ # Clear all existing slides by removing them from the slide
290
+ # list
291
+ while len(presentation.slides) > 0:
292
+ rId = presentation.slides._sldIdLst[-1].rId
293
+ presentation.part.drop_rel(rId)
294
+ del presentation.slides._sldIdLst[-1]
295
+ else:
296
+ presentation = Presentation()
297
+
298
+ slide_width_inch, slide_height_inch = (
299
+ self._get_slide_width_height_inches(presentation)
300
+ )
301
+
302
+ # Process slides
303
+ if content:
304
+ # Title slide (first element)
305
+ title_slide_data = content.pop(0) if content else {}
306
+ title_layout = presentation.slide_layouts[0]
307
+ title_slide = presentation.slides.add_slide(title_layout)
308
+
309
+ # Set title and subtitle
310
+ if title_slide.shapes.title:
311
+ title_slide.shapes.title.text_frame.clear()
312
+ self._format_text(
313
+ title_slide.shapes.title.text_frame.paragraphs[0],
314
+ title_slide_data.get("title", ""),
315
+ )
316
+
317
+ if len(title_slide.placeholders) > 1:
318
+ subtitle = title_slide.placeholders[1]
319
+ subtitle.text_frame.clear()
320
+ self._format_text(
321
+ subtitle.text_frame.paragraphs[0],
322
+ title_slide_data.get("subtitle", ""),
323
+ )
324
+
325
+ # Content slides
326
+ for slide_data in content:
327
+ if not isinstance(slide_data, dict):
328
+ continue
329
+
330
+ # Handle different slide types
331
+ if 'table' in slide_data:
332
+ self._handle_table(
333
+ presentation,
334
+ slide_data,
335
+ )
336
+ elif 'bullet_points' in slide_data:
337
+ if any(
338
+ step.startswith(STEP_BY_STEP_PROCESS_MARKER)
339
+ for step in slide_data['bullet_points']
340
+ ):
341
+ self._handle_step_by_step_process(
342
+ presentation,
343
+ slide_data,
344
+ slide_width_inch,
345
+ slide_height_inch,
346
+ )
347
+ else:
348
+ self._handle_default_display(
349
+ presentation,
350
+ slide_data,
351
+ )
352
+
353
+ # Save the presentation
354
+ presentation.save(str(file_path))
355
+ logger.debug(f"Wrote PPTX to {file_path} with enhanced formatting")
356
+
357
+ def create_presentation(
358
+ self,
359
+ content: str,
360
+ filename: str,
361
+ template: Optional[str] = None,
362
+ ) -> str:
363
+ r"""Create a PowerPoint presentation (PPTX) file.
364
+
365
+ Args:
366
+ content (str): The content to write to the PPTX file as a JSON
367
+ string. Must represent a list of dictionaries with the
368
+ following structure:
369
+ - First dict: title slide {"title": str, "subtitle": str}
370
+ - Other dicts: content slides, which can be one of:
371
+ * Bullet/step slides: {"heading": str, "bullet_points":
372
+ list of str or nested lists, "img_keywords": str
373
+ (optional)}
374
+ - If any bullet point starts with '>> ', it will be
375
+ rendered as a step-by-step process.
376
+ - "img_keywords" can be a URL or search keywords for
377
+ an image (optional).
378
+ * Table slides: {"heading": str, "table": {"headers": list
379
+ of str, "rows": list of list of str}}
380
+ filename (str): The name or path of the file. If a relative path is
381
+ supplied, it is resolved to self.working_directory.
382
+ template (Optional[str]): The path to the template PPTX file.
383
+ Initializes a presentation from a given template file Or PPTX
384
+ file. (default: :obj:`None`)
385
+
386
+ Returns:
387
+ str: A success message indicating the file was created.
388
+
389
+ Example:
390
+ [
391
+ {
392
+ "title": "Presentation Title",
393
+ "subtitle": "Presentation Subtitle"
394
+ },
395
+ {
396
+ "heading": "Slide Title",
397
+ "bullet_points": [
398
+ "**Bold text** for emphasis",
399
+ "*Italic text* for additional emphasis",
400
+ "Regular text for normal content"
401
+ ],
402
+ "img_keywords": "relevant search terms for images"
403
+ },
404
+ {
405
+ "heading": "Step-by-Step Process",
406
+ "bullet_points": [
407
+ ">> **Step 1:** First step description",
408
+ ">> **Step 2:** Second step description",
409
+ ">> **Step 3:** Third step description"
410
+ ],
411
+ "img_keywords": "process workflow steps"
412
+ },
413
+ {
414
+ "heading": "Comparison Table",
415
+ "table": {
416
+ "headers": ["Column 1", "Column 2", "Column 3"],
417
+ "rows": [
418
+ ["Row 1, Col 1", "Row 1, Col 2", "Row 1, Col 3"],
419
+ ["Row 2, Col 1", "Row 2, Col 2", "Row 2, Col 3"]
420
+ ]
421
+ },
422
+ "img_keywords": "comparison visualization"
423
+ }
424
+ ]
425
+ """
426
+ # Ensure filename has .pptx extension
427
+ if not filename.lower().endswith('.pptx'):
428
+ filename += '.pptx'
429
+
430
+ # Resolve file path
431
+ file_path = self._resolve_filepath(filename)
432
+
433
+ # Parse and validate content format
434
+ try:
435
+ import json
436
+
437
+ parsed_content = json.loads(content)
438
+ except json.JSONDecodeError as e:
439
+ logger.error(f"Content must be valid JSON: {e}")
440
+ return "Failed to parse content as JSON"
441
+
442
+ if not isinstance(parsed_content, list):
443
+ logger.error(
444
+ f"PPTX content must be a list of dictionaries, "
445
+ f"got {type(parsed_content).__name__}"
446
+ )
447
+ return "PPTX content must be a list of dictionaries"
448
+
449
+ try:
450
+ # Create the PPTX file
451
+ self._write_pptx_file(file_path, parsed_content.copy(), template)
452
+
453
+ success_msg = (
454
+ f"PowerPoint presentation successfully created: {file_path}"
455
+ )
456
+ logger.info(success_msg)
457
+ return success_msg
458
+
459
+ except Exception as e:
460
+ error_msg = f"Failed to create PPTX file {file_path}: {e!s}"
461
+ logger.error(error_msg)
462
+ return error_msg
463
+
464
+ def _handle_default_display(
465
+ self,
466
+ presentation: "presentation.Presentation",
467
+ slide_json: Dict[str, Any],
468
+ ) -> None:
469
+ r"""Display a list of text in a slide.
470
+
471
+ Args:
472
+ presentation (presentation.Presentation): The presentation object.
473
+ slide_json (Dict[str, Any]): The content of the slide as JSON data.
474
+ """
475
+ status = False
476
+
477
+ if 'img_keywords' in slide_json:
478
+ if random.random() < IMAGE_DISPLAY_PROBABILITY:
479
+ status = self._handle_display_image__in_foreground(
480
+ presentation,
481
+ slide_json,
482
+ )
483
+
484
+ if status:
485
+ return
486
+
487
+ # Image display failed, so display only text
488
+ bullet_slide_layout = presentation.slide_layouts[1]
489
+ slide = presentation.slides.add_slide(bullet_slide_layout)
490
+
491
+ shapes = slide.shapes
492
+ title_shape = shapes.title
493
+
494
+ try:
495
+ body_shape = shapes.placeholders[1]
496
+ except KeyError:
497
+ # Get placeholders from the slide without layout_number
498
+ placeholders = self._get_slide_placeholders(slide)
499
+ body_shape = shapes.placeholders[placeholders[0][0]]
500
+
501
+ title_shape.text = self._remove_slide_number_from_heading(
502
+ slide_json['heading']
503
+ )
504
+ text_frame = body_shape.text_frame
505
+
506
+ flat_items_list = self._get_flat_list_of_contents(
507
+ slide_json['bullet_points'], level=0
508
+ )
509
+ self._add_bulleted_items(text_frame, flat_items_list)
510
+
511
+ @api_keys_required(
512
+ [
513
+ ("api_key", 'PEXELS_API_KEY'),
514
+ ]
515
+ )
516
+ def _handle_display_image__in_foreground(
517
+ self,
518
+ presentation: "presentation.Presentation",
519
+ slide_json: Dict[str, Any],
520
+ ) -> bool:
521
+ r"""Create a slide with text and image using a picture placeholder
522
+ layout.
523
+
524
+ Args:
525
+ presentation (presentation.Presentation): The presentation object.
526
+ slide_json (Dict[str, Any]): The content of the slide as JSON data.
527
+
528
+ Returns:
529
+ bool: True if the slide has been processed.
530
+ """
531
+ from io import BytesIO
532
+
533
+ import requests
534
+
535
+ img_keywords = slide_json.get('img_keywords', '').strip()
536
+ slide = presentation.slide_layouts[8] # Picture with Caption
537
+ slide = presentation.slides.add_slide(slide)
538
+ placeholders = None
539
+
540
+ title_placeholder = slide.shapes.title # type: ignore[attr-defined]
541
+ title_placeholder.text = self._remove_slide_number_from_heading(
542
+ slide_json['heading']
543
+ )
544
+
545
+ try:
546
+ pic_col = slide.shapes.placeholders[1] # type: ignore[attr-defined]
547
+ except KeyError:
548
+ # Get placeholders from the slide without layout_number
549
+ placeholders = self._get_slide_placeholders(slide) # type: ignore[arg-type]
550
+ pic_col = None
551
+ for idx, name in placeholders:
552
+ if 'picture' in name:
553
+ pic_col = slide.shapes.placeholders[idx] # type: ignore[attr-defined]
554
+
555
+ try:
556
+ text_col = slide.shapes.placeholders[2] # type: ignore[attr-defined]
557
+ except KeyError:
558
+ text_col = None
559
+ if not placeholders:
560
+ placeholders = self._get_slide_placeholders(slide) # type: ignore[arg-type]
561
+
562
+ for idx, name in placeholders:
563
+ if 'content' in name:
564
+ text_col = slide.shapes.placeholders[idx] # type: ignore[attr-defined]
565
+
566
+ flat_items_list = self._get_flat_list_of_contents(
567
+ slide_json['bullet_points'], level=0
568
+ )
569
+ self._add_bulleted_items(text_col.text_frame, flat_items_list)
570
+
571
+ if not img_keywords:
572
+ return True
573
+
574
+ if isinstance(img_keywords, str) and img_keywords.startswith(
575
+ ('http://', 'https://')
576
+ ):
577
+ try:
578
+ img_response = requests.get(img_keywords, timeout=30)
579
+ img_response.raise_for_status()
580
+ image_data = BytesIO(img_response.content)
581
+ pic_col.insert_picture(image_data)
582
+ return True
583
+ except Exception as ex:
584
+ logger.error(
585
+ 'Error while downloading image from URL: %s', str(ex)
586
+ )
587
+
588
+ try:
589
+ url = 'https://api.pexels.com/v1/search'
590
+ api_key = os.getenv('PEXELS_API_KEY')
591
+
592
+ headers = {
593
+ 'Authorization': api_key,
594
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) '
595
+ 'Gecko/20100101 Firefox/10.0',
596
+ }
597
+ params = {
598
+ 'query': img_keywords,
599
+ 'size': 'medium',
600
+ 'page': 1,
601
+ 'per_page': 3,
602
+ }
603
+ response = requests.get(
604
+ url, headers=headers, params=params, timeout=12
605
+ )
606
+ response.raise_for_status()
607
+ json_response = response.json()
608
+
609
+ if json_response.get('photos'):
610
+ photo = random.choice(json_response['photos'])
611
+ photo_url = photo.get('src', {}).get('large') or photo.get(
612
+ 'src', {}
613
+ ).get('original')
614
+
615
+ if photo_url:
616
+ # Download and insert the image
617
+ img_response = requests.get(
618
+ photo_url, headers=headers, stream=True, timeout=12
619
+ )
620
+ img_response.raise_for_status()
621
+ image_data = BytesIO(img_response.content)
622
+
623
+ pic_col.insert_picture(image_data)
624
+ except Exception as ex:
625
+ logger.error(
626
+ 'Error occurred while adding image to slide: %s', str(ex)
627
+ )
628
+
629
+ return True
630
+
631
+ def _handle_table(
632
+ self,
633
+ presentation: "presentation.Presentation",
634
+ slide_json: Dict[str, Any],
635
+ ) -> None:
636
+ r"""Add a table to a slide.
637
+
638
+ Args:
639
+ presentation (presentation.Presentation): The presentation object.
640
+ slide_json (Dict[str, Any]): The content of the slide as JSON data.
641
+ """
642
+ headers = slide_json['table'].get('headers', [])
643
+ rows = slide_json['table'].get('rows', [])
644
+ bullet_slide_layout = presentation.slide_layouts[1]
645
+ slide = presentation.slides.add_slide(bullet_slide_layout)
646
+ shapes = slide.shapes
647
+ shapes.title.text = self._remove_slide_number_from_heading(
648
+ slide_json['heading']
649
+ )
650
+ left = slide.placeholders[1].left
651
+ top = slide.placeholders[1].top
652
+ width = slide.placeholders[1].width
653
+ height = slide.placeholders[1].height
654
+ table = slide.shapes.add_table(
655
+ len(rows) + 1, len(headers), left, top, width, height
656
+ ).table
657
+
658
+ # Set headers
659
+ for col_idx, header_text in enumerate(headers):
660
+ table.cell(0, col_idx).text = header_text
661
+ table.cell(0, col_idx).text_frame.paragraphs[0].font.bold = True
662
+
663
+ # Fill in rows
664
+ for row_idx, row_data in enumerate(rows, start=1):
665
+ for col_idx, cell_text in enumerate(row_data):
666
+ table.cell(row_idx, col_idx).text = cell_text
667
+
668
+ def _handle_step_by_step_process(
669
+ self,
670
+ presentation: "presentation.Presentation",
671
+ slide_json: Dict[str, Any],
672
+ slide_width_inch: float,
673
+ slide_height_inch: float,
674
+ ) -> None:
675
+ r"""Add shapes to display a step-by-step process in the slide.
676
+
677
+ Args:
678
+ presentation (presentation.Presentation): The presentation object.
679
+ slide_json (Dict[str, Any]): The content of the slide as JSON data.
680
+ slide_width_inch (float): The width of the slide in inches.
681
+ slide_height_inch (float): The height of the slide in inches.
682
+ """
683
+ from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE
684
+ from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
685
+ from pptx.util import Inches, Pt
686
+
687
+ steps = slide_json['bullet_points']
688
+ n_steps = len(steps)
689
+
690
+ bullet_slide_layout = presentation.slide_layouts[1]
691
+ slide = presentation.slides.add_slide(bullet_slide_layout)
692
+ shapes = slide.shapes
693
+ shapes.title.text = self._remove_slide_number_from_heading(
694
+ slide_json['heading']
695
+ )
696
+
697
+ if 3 <= n_steps <= 4:
698
+ # Horizontal display
699
+ height = Inches(1.5)
700
+ width = Inches(slide_width_inch / n_steps - 0.01)
701
+ top = Inches(slide_height_inch / 2)
702
+ left = Inches(
703
+ (slide_width_inch - width.inches * n_steps) / 2 + 0.05
704
+ )
705
+
706
+ for step in steps:
707
+ shape = shapes.add_shape(
708
+ MSO_AUTO_SHAPE_TYPE.CHEVRON, left, top, width, height
709
+ )
710
+ text_frame = shape.text_frame
711
+ text_frame.clear()
712
+ paragraph = text_frame.paragraphs[0]
713
+ paragraph.alignment = PP_ALIGN.CENTER
714
+ text_frame.vertical_anchor = MSO_ANCHOR.MIDDLE
715
+ self._format_text(
716
+ paragraph, step.removeprefix(STEP_BY_STEP_PROCESS_MARKER)
717
+ )
718
+ for run in paragraph.runs:
719
+ run.font.size = Pt(14)
720
+ left = Inches(left.inches + width.inches - Inches(0.4).inches)
721
+ elif 4 < n_steps <= 6:
722
+ # Vertical display
723
+ height = Inches(0.65)
724
+ top = Inches(slide_height_inch / 4)
725
+ left = Inches(1)
726
+ width = Inches(slide_width_inch * 2 / 3)
727
+
728
+ for step in steps:
729
+ shape = shapes.add_shape(
730
+ MSO_AUTO_SHAPE_TYPE.PENTAGON, left, top, width, height
731
+ )
732
+ text_frame = shape.text_frame
733
+ text_frame.clear()
734
+ paragraph = text_frame.paragraphs[0]
735
+ paragraph.alignment = PP_ALIGN.CENTER
736
+ text_frame.vertical_anchor = MSO_ANCHOR.MIDDLE
737
+ self._format_text(
738
+ paragraph, step.removeprefix(STEP_BY_STEP_PROCESS_MARKER)
739
+ )
740
+ for run in paragraph.runs:
741
+ run.font.size = Pt(14)
742
+ top = Inches(top.inches + height.inches + Inches(0.3).inches)
743
+ left = Inches(left.inches + Inches(0.5).inches)
744
+
745
+ def _remove_slide_number_from_heading(self, header: str) -> str:
746
+ r"""Remove the slide number from a given slide header.
747
+
748
+ Args:
749
+ header (str): The header of a slide.
750
+
751
+ Returns:
752
+ str: The header without slide number.
753
+ """
754
+ if SLIDE_NUMBER_REGEX.match(header):
755
+ idx = header.find(':')
756
+ header = header[idx + 1 :]
757
+ return header
758
+
759
+ def _get_slide_placeholders(
760
+ self,
761
+ slide: "Slide",
762
+ ) -> List[Tuple[int, str]]:
763
+ r"""Return the index and name of all placeholders present in a slide.
764
+
765
+ Args:
766
+ slide (Slide): The slide.
767
+
768
+ Returns:
769
+ List[Tuple[int, str]]: A list containing placeholders (idx, name)
770
+ tuples.
771
+ """
772
+ if hasattr(slide.shapes, 'placeholders'):
773
+ placeholders = [
774
+ (shape.placeholder_format.idx, shape.name.lower())
775
+ for shape in slide.shapes.placeholders
776
+ ]
777
+ if placeholders and len(placeholders) > 0:
778
+ placeholders.pop(0) # Remove the title placeholder
779
+ return placeholders
780
+ return []
781
+
782
+ def get_tools(self) -> List[FunctionTool]:
783
+ r"""Returns a list of FunctionTool objects representing the
784
+ functions in the toolkit.
785
+
786
+ Returns:
787
+ List[FunctionTool]: A list of FunctionTool objects
788
+ representing the functions in the toolkit.
789
+ """
790
+ return [FunctionTool(self.create_presentation)]