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.
- camel/__init__.py +3 -3
- camel/agents/__init__.py +2 -2
- camel/agents/_types.py +9 -4
- camel/agents/_utils.py +40 -2
- camel/agents/base.py +2 -2
- camel/agents/chat_agent.py +5012 -902
- camel/agents/critic_agent.py +2 -2
- camel/agents/deductive_reasoner_agent.py +56 -56
- camel/agents/embodied_agent.py +2 -2
- camel/agents/knowledge_graph_agent.py +20 -20
- camel/agents/mcp_agent.py +39 -36
- camel/agents/multi_hop_generator_agent.py +3 -3
- camel/agents/programmed_agent_instruction.py +2 -2
- camel/agents/repo_agent.py +4 -3
- camel/agents/role_assignment_agent.py +2 -2
- camel/agents/search_agent.py +2 -2
- camel/agents/task_agent.py +2 -2
- camel/agents/tool_agents/__init__.py +2 -2
- camel/agents/tool_agents/base.py +2 -2
- camel/agents/tool_agents/hugging_face_tool_agent.py +3 -3
- camel/benchmarks/__init__.py +2 -2
- camel/benchmarks/apibank.py +5 -5
- camel/benchmarks/apibench.py +2 -2
- camel/benchmarks/base.py +2 -2
- camel/benchmarks/browsecomp.py +44 -33
- camel/benchmarks/gaia.py +17 -13
- camel/benchmarks/mock_website/README.md +94 -0
- camel/benchmarks/mock_website/mock_web.py +299 -0
- camel/benchmarks/mock_website/requirements.txt +3 -0
- camel/benchmarks/mock_website/shopping_mall/app.py +465 -0
- camel/benchmarks/mock_website/task.json +104 -0
- camel/benchmarks/nexus.py +3 -3
- camel/benchmarks/ragbench.py +2 -2
- camel/bots/__init__.py +2 -2
- camel/bots/discord/__init__.py +2 -2
- camel/bots/discord/discord_app.py +2 -2
- camel/bots/discord/discord_installation.py +2 -2
- camel/bots/discord/discord_store.py +3 -3
- camel/bots/slack/__init__.py +2 -2
- camel/bots/slack/models.py +4 -4
- camel/bots/slack/slack_app.py +2 -2
- camel/bots/telegram_bot.py +2 -2
- camel/configs/__init__.py +26 -2
- camel/configs/aihubmix_config.py +90 -0
- camel/configs/aiml_config.py +2 -2
- camel/configs/amd_config.py +70 -0
- camel/configs/anthropic_config.py +8 -7
- camel/configs/base_config.py +2 -2
- camel/configs/bedrock_config.py +5 -3
- camel/configs/cerebras_config.py +98 -0
- camel/configs/cohere_config.py +3 -3
- camel/configs/cometapi_config.py +106 -0
- camel/configs/crynux_config.py +94 -0
- camel/configs/deepseek_config.py +9 -8
- camel/configs/gemini_config.py +6 -4
- camel/configs/groq_config.py +6 -4
- camel/configs/internlm_config.py +6 -4
- camel/configs/litellm_config.py +2 -2
- camel/configs/lmstudio_config.py +6 -4
- camel/configs/minimax_config.py +95 -0
- camel/configs/mistral_config.py +3 -3
- camel/configs/modelscope_config.py +5 -3
- camel/configs/moonshot_config.py +2 -2
- camel/configs/nebius_config.py +105 -0
- camel/configs/netmind_config.py +2 -2
- camel/configs/novita_config.py +2 -2
- camel/configs/nvidia_config.py +2 -2
- camel/configs/ollama_config.py +2 -2
- camel/configs/openai_config.py +8 -3
- camel/configs/openrouter_config.py +6 -4
- camel/configs/ppio_config.py +2 -2
- camel/configs/qianfan_config.py +85 -0
- camel/configs/qwen_config.py +2 -2
- camel/configs/reka_config.py +3 -3
- camel/configs/samba_config.py +8 -6
- camel/configs/sglang_config.py +2 -2
- camel/configs/siliconflow_config.py +2 -2
- camel/configs/togetherai_config.py +2 -2
- camel/configs/vllm_config.py +4 -2
- camel/configs/watsonx_config.py +2 -2
- camel/configs/yi_config.py +6 -4
- camel/configs/zhipuai_config.py +6 -4
- camel/{data_collector → data_collectors}/__init__.py +2 -2
- camel/{data_collector → data_collectors}/alpaca_collector.py +19 -10
- camel/{data_collector → data_collectors}/base.py +2 -2
- camel/{data_collector → data_collectors}/sharegpt_collector.py +3 -3
- camel/datagen/__init__.py +2 -2
- camel/datagen/cot_datagen.py +32 -37
- camel/datagen/evol_instruct/__init__.py +2 -2
- camel/datagen/evol_instruct/evol_instruct.py +2 -2
- camel/datagen/evol_instruct/scorer.py +24 -25
- camel/datagen/evol_instruct/templates.py +48 -48
- camel/datagen/self_improving_cot.py +5 -5
- camel/datagen/self_instruct/__init__.py +2 -2
- camel/datagen/self_instruct/filter/__init__.py +2 -2
- camel/datagen/self_instruct/filter/filter_function.py +2 -2
- camel/datagen/self_instruct/filter/filter_registry.py +2 -2
- camel/datagen/self_instruct/filter/instruction_filter.py +2 -2
- camel/datagen/self_instruct/self_instruct.py +2 -2
- camel/datagen/self_instruct/templates.py +47 -47
- camel/datagen/source2synth/__init__.py +2 -2
- camel/datagen/source2synth/data_processor.py +2 -2
- camel/datagen/source2synth/models.py +2 -2
- camel/datagen/source2synth/user_data_processor_config.py +2 -2
- camel/datahubs/__init__.py +2 -2
- camel/datahubs/base.py +2 -2
- camel/datahubs/huggingface.py +2 -2
- camel/datahubs/models.py +2 -2
- camel/datasets/__init__.py +2 -2
- camel/datasets/base_generator.py +41 -12
- camel/datasets/few_shot_generator.py +18 -18
- camel/datasets/models.py +3 -3
- camel/datasets/self_instruct_generator.py +2 -2
- camel/datasets/static_dataset.py +152 -2
- camel/embeddings/__init__.py +2 -2
- camel/embeddings/azure_embedding.py +2 -2
- camel/embeddings/base.py +2 -2
- camel/embeddings/gemini_embedding.py +2 -2
- camel/embeddings/jina_embedding.py +10 -3
- camel/embeddings/mistral_embedding.py +2 -2
- camel/embeddings/openai_compatible_embedding.py +2 -2
- camel/embeddings/openai_embedding.py +2 -2
- camel/embeddings/sentence_transformers_embeddings.py +4 -4
- camel/embeddings/together_embedding.py +2 -2
- camel/embeddings/vlm_embedding.py +11 -4
- camel/environments/__init__.py +14 -2
- camel/environments/models.py +2 -2
- camel/environments/multi_step.py +2 -2
- camel/environments/rlcards_env.py +860 -0
- camel/environments/single_step.py +30 -5
- camel/environments/tic_tac_toe.py +3 -3
- camel/extractors/__init__.py +2 -2
- camel/extractors/base.py +2 -2
- camel/extractors/python_strategies.py +2 -2
- camel/generators.py +2 -2
- camel/human.py +2 -2
- camel/interpreters/__init__.py +4 -2
- camel/interpreters/base.py +16 -3
- camel/interpreters/docker/Dockerfile +53 -7
- camel/interpreters/docker_interpreter.py +70 -11
- camel/interpreters/e2b_interpreter.py +59 -11
- camel/interpreters/internal_python_interpreter.py +81 -4
- camel/interpreters/interpreter_error.py +2 -2
- camel/interpreters/ipython_interpreter.py +23 -5
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/interpreters/subprocess_interpreter.py +36 -4
- camel/loaders/__init__.py +17 -5
- camel/loaders/apify_reader.py +2 -2
- camel/loaders/base_io.py +2 -2
- camel/loaders/base_loader.py +85 -0
- camel/loaders/chunkr_reader.py +128 -93
- camel/loaders/crawl4ai_reader.py +2 -2
- camel/loaders/firecrawl_reader.py +6 -6
- camel/loaders/jina_url_reader.py +2 -2
- camel/loaders/markitdown.py +2 -2
- camel/loaders/mineru_extractor.py +2 -2
- camel/loaders/mistral_reader.py +148 -0
- camel/loaders/scrapegraph_reader.py +2 -2
- camel/loaders/unstructured_io.py +2 -2
- camel/logger.py +5 -5
- camel/memories/__init__.py +2 -2
- camel/memories/agent_memories.py +86 -3
- camel/memories/base.py +36 -2
- camel/memories/blocks/__init__.py +2 -2
- camel/memories/blocks/chat_history_block.py +126 -9
- camel/memories/blocks/vectordb_block.py +10 -3
- camel/memories/context_creators/__init__.py +2 -2
- camel/memories/context_creators/score_based.py +31 -239
- camel/memories/records.py +98 -13
- camel/messages/__init__.py +2 -2
- camel/messages/base.py +193 -46
- camel/messages/conversion/__init__.py +2 -2
- camel/messages/conversion/alpaca.py +2 -2
- camel/messages/conversion/conversation_models.py +2 -2
- camel/messages/conversion/sharegpt/__init__.py +2 -2
- camel/messages/conversion/sharegpt/function_call_formatter.py +2 -2
- camel/messages/conversion/sharegpt/hermes/__init__.py +2 -2
- camel/messages/conversion/sharegpt/hermes/hermes_function_formatter.py +2 -2
- camel/messages/func_message.py +54 -17
- camel/models/__init__.py +18 -2
- camel/models/_utils.py +3 -3
- camel/models/aihubmix_model.py +83 -0
- camel/models/aiml_model.py +11 -18
- camel/models/amd_model.py +101 -0
- camel/models/anthropic_model.py +127 -20
- camel/models/aws_bedrock_model.py +12 -35
- camel/models/azure_openai_model.py +263 -63
- camel/models/base_audio_model.py +5 -3
- camel/models/base_model.py +195 -26
- camel/models/cerebras_model.py +83 -0
- camel/models/cohere_model.py +81 -21
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +87 -0
- camel/models/deepseek_model.py +61 -59
- camel/models/fish_audio_model.py +8 -2
- camel/models/gemini_model.py +439 -30
- camel/models/groq_model.py +11 -19
- camel/models/internlm_model.py +11 -18
- camel/models/litellm_model.py +94 -34
- camel/models/lmstudio_model.py +17 -20
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +84 -19
- camel/models/model_factory.py +49 -6
- camel/models/model_manager.py +33 -11
- camel/models/modelscope_model.py +13 -193
- camel/models/moonshot_model.py +195 -21
- camel/models/nebius_model.py +83 -0
- camel/models/nemotron_model.py +19 -9
- camel/models/netmind_model.py +11 -18
- camel/models/novita_model.py +11 -18
- camel/models/nvidia_model.py +11 -18
- camel/models/ollama_model.py +14 -21
- camel/models/openai_audio_models.py +2 -2
- camel/models/openai_compatible_model.py +234 -27
- camel/models/openai_model.py +255 -39
- camel/models/openrouter_model.py +11 -19
- camel/models/ppio_model.py +11 -18
- camel/models/qianfan_model.py +89 -0
- camel/models/qwen_model.py +13 -193
- camel/models/reka_model.py +90 -21
- camel/models/reward/__init__.py +2 -2
- camel/models/reward/base_reward_model.py +2 -2
- camel/models/reward/evaluator.py +2 -2
- camel/models/reward/nemotron_model.py +2 -2
- camel/models/reward/skywork_model.py +2 -2
- camel/models/samba_model.py +117 -49
- camel/models/sglang_model.py +162 -42
- camel/models/siliconflow_model.py +12 -35
- camel/models/stub_model.py +10 -7
- camel/models/togetherai_model.py +11 -18
- camel/models/vllm_model.py +10 -18
- camel/models/volcano_model.py +16 -20
- camel/models/watsonx_model.py +69 -19
- camel/models/yi_model.py +11 -18
- camel/models/zhipuai_model.py +70 -18
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/personas/__init__.py +2 -2
- camel/personas/persona.py +2 -2
- camel/personas/persona_hub.py +2 -2
- camel/prompts/__init__.py +2 -2
- camel/prompts/ai_society.py +2 -2
- camel/prompts/base.py +2 -2
- camel/prompts/code.py +2 -2
- camel/prompts/evaluation.py +2 -2
- camel/prompts/generate_text_embedding_data.py +2 -2
- camel/prompts/image_craft.py +2 -2
- camel/prompts/misalignment.py +2 -2
- camel/prompts/multi_condition_image_craft.py +2 -2
- camel/prompts/object_recognition.py +2 -2
- camel/prompts/persona_hub.py +3 -3
- camel/prompts/prompt_templates.py +2 -2
- camel/prompts/role_description_prompt_template.py +2 -2
- camel/prompts/solution_extraction.py +8 -8
- camel/prompts/task_prompt_template.py +2 -2
- camel/prompts/translation.py +2 -2
- camel/prompts/video_description_prompt.py +3 -3
- camel/responses/__init__.py +2 -2
- camel/responses/agent_responses.py +2 -2
- camel/retrievers/__init__.py +2 -2
- camel/retrievers/auto_retriever.py +23 -3
- camel/retrievers/base.py +2 -2
- camel/retrievers/bm25_retriever.py +3 -4
- camel/retrievers/cohere_rerank_retriever.py +2 -2
- camel/retrievers/hybrid_retrival.py +4 -4
- camel/retrievers/vector_retriever.py +2 -2
- camel/runtimes/Dockerfile.multi-toolkit +90 -0
- camel/{runtime → runtimes}/__init__.py +2 -2
- camel/runtimes/api.py +153 -0
- camel/{runtime → runtimes}/base.py +2 -2
- camel/{runtime → runtimes}/configs.py +13 -13
- camel/{runtime → runtimes}/daytona_runtime.py +18 -19
- camel/{runtime → runtimes}/docker_runtime.py +13 -13
- camel/{runtime → runtimes}/llm_guard_runtime.py +28 -28
- camel/{runtime → runtimes}/remote_http_runtime.py +12 -12
- camel/{runtime → runtimes}/ubuntu_docker_runtime.py +3 -3
- camel/{runtime → runtimes}/utils/__init__.py +2 -2
- camel/{runtime → runtimes}/utils/function_risk_toolkit.py +2 -2
- camel/{runtime → runtimes}/utils/ignore_risk_toolkit.py +2 -2
- camel/schemas/__init__.py +2 -2
- camel/schemas/base.py +2 -2
- camel/schemas/openai_converter.py +3 -3
- camel/schemas/outlines_converter.py +2 -2
- camel/services/agent_openapi_server.py +380 -0
- camel/societies/__init__.py +4 -2
- camel/societies/babyagi_playing.py +2 -2
- camel/societies/role_playing.py +201 -80
- camel/societies/workforce/__init__.py +10 -3
- camel/societies/workforce/base.py +9 -5
- camel/societies/workforce/events.py +143 -0
- camel/societies/workforce/prompts.py +258 -33
- camel/societies/workforce/role_playing_worker.py +95 -30
- camel/societies/workforce/single_agent_worker.py +659 -30
- camel/societies/workforce/structured_output_handler.py +512 -0
- camel/societies/workforce/task_channel.py +182 -38
- camel/societies/workforce/utils.py +784 -18
- camel/societies/workforce/worker.py +96 -28
- camel/societies/workforce/workflow_memory_manager.py +1746 -0
- camel/societies/workforce/workforce.py +5730 -366
- camel/societies/workforce/workforce_callback.py +103 -0
- camel/societies/workforce/workforce_logger.py +647 -0
- camel/societies/workforce/workforce_metrics.py +33 -0
- camel/storages/__init__.py +10 -2
- camel/storages/graph_storages/__init__.py +2 -2
- camel/storages/graph_storages/base.py +2 -2
- camel/storages/graph_storages/graph_element.py +2 -2
- camel/storages/graph_storages/nebula_graph.py +4 -4
- camel/storages/graph_storages/neo4j_graph.py +7 -7
- camel/storages/key_value_storages/__init__.py +2 -2
- camel/storages/key_value_storages/base.py +2 -2
- camel/storages/key_value_storages/in_memory.py +2 -2
- camel/storages/key_value_storages/json.py +17 -4
- camel/storages/key_value_storages/mem0_cloud.py +50 -49
- camel/storages/key_value_storages/redis.py +2 -2
- camel/storages/object_storages/__init__.py +2 -2
- camel/storages/object_storages/amazon_s3.py +2 -2
- camel/storages/object_storages/azure_blob.py +2 -2
- camel/storages/object_storages/base.py +2 -2
- camel/storages/object_storages/google_cloud.py +3 -3
- camel/storages/vectordb_storages/__init__.py +12 -2
- camel/storages/vectordb_storages/base.py +2 -2
- camel/storages/vectordb_storages/chroma.py +731 -0
- camel/storages/vectordb_storages/faiss.py +712 -0
- camel/storages/vectordb_storages/milvus.py +2 -2
- camel/storages/vectordb_storages/oceanbase.py +16 -17
- camel/storages/vectordb_storages/pgvector.py +349 -0
- camel/storages/vectordb_storages/qdrant.py +6 -6
- camel/storages/vectordb_storages/surreal.py +372 -0
- camel/storages/vectordb_storages/tidb.py +11 -8
- camel/storages/vectordb_storages/weaviate.py +714 -0
- camel/tasks/__init__.py +2 -2
- camel/tasks/task.py +366 -27
- camel/tasks/task_prompt.py +3 -3
- camel/terminators/__init__.py +2 -2
- camel/terminators/base.py +2 -2
- camel/terminators/response_terminator.py +2 -2
- camel/terminators/token_limit_terminator.py +2 -2
- camel/toolkits/__init__.py +58 -10
- camel/toolkits/aci_toolkit.py +66 -21
- camel/toolkits/arxiv_toolkit.py +8 -8
- camel/toolkits/ask_news_toolkit.py +2 -2
- camel/toolkits/async_browser_toolkit.py +174 -575
- camel/toolkits/audio_analysis_toolkit.py +3 -3
- camel/toolkits/base.py +65 -7
- camel/toolkits/bohrium_toolkit.py +318 -0
- camel/toolkits/browser_toolkit.py +306 -566
- camel/toolkits/browser_toolkit_commons.py +568 -0
- camel/toolkits/code_execution.py +67 -11
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/craw4ai_toolkit.py +93 -0
- camel/toolkits/dappier_toolkit.py +12 -8
- camel/toolkits/data_commons_toolkit.py +2 -2
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/earth_science_toolkit.py +5367 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
- camel/toolkits/excel_toolkit.py +910 -70
- camel/toolkits/file_toolkit.py +1402 -0
- camel/toolkits/function_tool.py +128 -20
- camel/toolkits/github_toolkit.py +148 -43
- camel/toolkits/gmail_toolkit.py +1839 -0
- camel/toolkits/google_calendar_toolkit.py +40 -6
- camel/toolkits/google_drive_mcp_toolkit.py +54 -0
- camel/toolkits/google_maps_toolkit.py +2 -2
- camel/toolkits/google_scholar_toolkit.py +2 -2
- camel/toolkits/human_toolkit.py +36 -12
- camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4589 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1929 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +589 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +129 -0
- camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +27 -0
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1037 -0
- camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
- camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
- camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
- camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
- camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
- camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
- camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
- camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
- camel/toolkits/image_analysis_toolkit.py +3 -3
- camel/toolkits/image_generation_toolkit.py +390 -0
- camel/toolkits/jina_reranker_toolkit.py +195 -79
- camel/toolkits/klavis_toolkit.py +7 -3
- camel/toolkits/linkedin_toolkit.py +2 -2
- camel/toolkits/markitdown_toolkit.py +104 -0
- camel/toolkits/math_toolkit.py +66 -12
- camel/toolkits/mcp_toolkit.py +841 -600
- camel/toolkits/memory_toolkit.py +7 -3
- camel/toolkits/meshy_toolkit.py +2 -2
- camel/toolkits/message_agent_toolkit.py +608 -0
- camel/toolkits/message_integration.py +724 -0
- camel/toolkits/mineru_toolkit.py +2 -2
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/networkx_toolkit.py +2 -2
- camel/toolkits/note_taking_toolkit.py +277 -0
- camel/toolkits/notion_mcp_toolkit.py +224 -0
- camel/toolkits/notion_toolkit.py +2 -2
- camel/toolkits/open_api_specs/biztoc/__init__.py +2 -2
- camel/toolkits/open_api_specs/biztoc/ai-plugin.json +1 -1
- camel/toolkits/open_api_specs/coursera/__init__.py +2 -2
- camel/toolkits/open_api_specs/create_qr_code/__init__.py +2 -2
- camel/toolkits/open_api_specs/klarna/__init__.py +2 -2
- camel/toolkits/open_api_specs/nasa_apod/__init__.py +2 -2
- camel/toolkits/open_api_specs/outschool/__init__.py +2 -2
- camel/toolkits/open_api_specs/outschool/ai-plugin.json +1 -1
- camel/toolkits/open_api_specs/outschool/openapi.yaml +1 -1
- camel/toolkits/open_api_specs/outschool/paths/__init__.py +2 -2
- camel/toolkits/open_api_specs/outschool/paths/get_classes.py +2 -2
- camel/toolkits/open_api_specs/outschool/paths/search_teachers.py +2 -2
- camel/toolkits/open_api_specs/security_config.py +2 -2
- camel/toolkits/open_api_specs/speak/__init__.py +2 -2
- camel/toolkits/open_api_specs/web_scraper/__init__.py +2 -2
- camel/toolkits/open_api_specs/web_scraper/ai-plugin.json +1 -1
- camel/toolkits/open_api_specs/web_scraper/paths/__init__.py +2 -2
- camel/toolkits/open_api_specs/web_scraper/paths/scraper.py +2 -2
- camel/toolkits/open_api_toolkit.py +2 -2
- camel/toolkits/openbb_toolkit.py +7 -3
- camel/toolkits/origene_mcp_toolkit.py +56 -0
- camel/toolkits/page_script.js +86 -74
- camel/toolkits/playwright_mcp_toolkit.py +27 -32
- camel/toolkits/pptx_toolkit.py +790 -0
- camel/toolkits/pubmed_toolkit.py +2 -2
- camel/toolkits/pulse_mcp_search_toolkit.py +2 -2
- camel/toolkits/pyautogui_toolkit.py +2 -2
- camel/toolkits/reddit_toolkit.py +2 -2
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/retrieval_toolkit.py +2 -2
- camel/toolkits/screenshot_toolkit.py +213 -0
- camel/toolkits/search_toolkit.py +539 -146
- camel/toolkits/searxng_toolkit.py +2 -2
- camel/toolkits/semantic_scholar_toolkit.py +2 -2
- camel/toolkits/slack_toolkit.py +108 -58
- camel/toolkits/sql_toolkit.py +712 -0
- camel/toolkits/stripe_toolkit.py +2 -2
- camel/toolkits/sympy_toolkit.py +3 -3
- camel/toolkits/task_planning_toolkit.py +134 -0
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +1070 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/thinking_toolkit.py +3 -3
- camel/toolkits/twitter_toolkit.py +8 -3
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +112 -29
- camel/toolkits/video_download_toolkit.py +22 -16
- camel/toolkits/weather_toolkit.py +2 -2
- camel/toolkits/web_deploy_toolkit.py +1219 -0
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/toolkits/whatsapp_toolkit.py +2 -2
- camel/toolkits/wolfram_alpha_toolkit.py +53 -25
- camel/toolkits/zapier_toolkit.py +7 -3
- camel/types/__init__.py +4 -4
- camel/types/agents/__init__.py +2 -2
- camel/types/agents/tool_calling_record.py +6 -3
- camel/types/enums.py +454 -35
- camel/types/mcp_registries.py +2 -2
- camel/types/openai_types.py +4 -4
- camel/types/unified_model_type.py +43 -6
- camel/utils/__init__.py +20 -2
- camel/utils/async_func.py +2 -2
- camel/utils/chunker/__init__.py +2 -2
- camel/utils/chunker/base.py +2 -2
- camel/utils/chunker/code_chunker.py +2 -2
- camel/utils/chunker/uio_chunker.py +2 -2
- camel/utils/commons.py +65 -7
- camel/utils/constants.py +5 -2
- camel/utils/context_utils.py +1134 -0
- camel/utils/deduplication.py +2 -2
- camel/utils/filename.py +2 -2
- camel/utils/langfuse.py +258 -0
- camel/utils/mcp.py +140 -6
- camel/utils/mcp_client.py +1056 -0
- camel/utils/message_summarizer.py +148 -0
- camel/utils/response_format.py +2 -2
- camel/utils/token_counting.py +45 -22
- camel/utils/tool_result.py +44 -0
- camel/verifiers/__init__.py +2 -2
- camel/verifiers/base.py +2 -2
- camel/verifiers/math_verifier.py +2 -2
- camel/verifiers/models.py +2 -2
- camel/verifiers/physics_verifier.py +2 -2
- camel/verifiers/python_verifier.py +2 -2
- {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/METADATA +349 -108
- camel_ai-0.2.82.dist-info/RECORD +507 -0
- {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
- {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/licenses/LICENSE +1 -1
- camel/loaders/pandas_reader.py +0 -368
- camel/runtime/api.py +0 -97
- camel/toolkits/dalle_toolkit.py +0 -171
- camel/toolkits/file_write_toolkit.py +0 -395
- camel/toolkits/openai_agent_toolkit.py +0 -135
- camel/toolkits/terminal_toolkit.py +0 -1037
- 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)]
|