ag2 0.10.2__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.
- ag2-0.10.2.dist-info/METADATA +819 -0
- ag2-0.10.2.dist-info/RECORD +423 -0
- ag2-0.10.2.dist-info/WHEEL +4 -0
- ag2-0.10.2.dist-info/licenses/LICENSE +201 -0
- ag2-0.10.2.dist-info/licenses/NOTICE.md +19 -0
- autogen/__init__.py +88 -0
- autogen/_website/__init__.py +3 -0
- autogen/_website/generate_api_references.py +426 -0
- autogen/_website/generate_mkdocs.py +1216 -0
- autogen/_website/notebook_processor.py +475 -0
- autogen/_website/process_notebooks.py +656 -0
- autogen/_website/utils.py +413 -0
- autogen/a2a/__init__.py +36 -0
- autogen/a2a/agent_executor.py +86 -0
- autogen/a2a/client.py +357 -0
- autogen/a2a/errors.py +18 -0
- autogen/a2a/httpx_client_factory.py +79 -0
- autogen/a2a/server.py +221 -0
- autogen/a2a/utils.py +207 -0
- autogen/agentchat/__init__.py +47 -0
- autogen/agentchat/agent.py +180 -0
- autogen/agentchat/assistant_agent.py +86 -0
- autogen/agentchat/chat.py +325 -0
- autogen/agentchat/contrib/__init__.py +5 -0
- autogen/agentchat/contrib/agent_eval/README.md +7 -0
- autogen/agentchat/contrib/agent_eval/agent_eval.py +108 -0
- autogen/agentchat/contrib/agent_eval/criterion.py +43 -0
- autogen/agentchat/contrib/agent_eval/critic_agent.py +44 -0
- autogen/agentchat/contrib/agent_eval/quantifier_agent.py +39 -0
- autogen/agentchat/contrib/agent_eval/subcritic_agent.py +45 -0
- autogen/agentchat/contrib/agent_eval/task.py +42 -0
- autogen/agentchat/contrib/agent_optimizer.py +432 -0
- autogen/agentchat/contrib/capabilities/__init__.py +5 -0
- autogen/agentchat/contrib/capabilities/agent_capability.py +20 -0
- autogen/agentchat/contrib/capabilities/generate_images.py +301 -0
- autogen/agentchat/contrib/capabilities/teachability.py +393 -0
- autogen/agentchat/contrib/capabilities/text_compressors.py +66 -0
- autogen/agentchat/contrib/capabilities/tools_capability.py +22 -0
- autogen/agentchat/contrib/capabilities/transform_messages.py +93 -0
- autogen/agentchat/contrib/capabilities/transforms.py +578 -0
- autogen/agentchat/contrib/capabilities/transforms_util.py +122 -0
- autogen/agentchat/contrib/capabilities/vision_capability.py +215 -0
- autogen/agentchat/contrib/captainagent/__init__.py +9 -0
- autogen/agentchat/contrib/captainagent/agent_builder.py +790 -0
- autogen/agentchat/contrib/captainagent/captainagent.py +514 -0
- autogen/agentchat/contrib/captainagent/tool_retriever.py +334 -0
- autogen/agentchat/contrib/captainagent/tools/README.md +44 -0
- autogen/agentchat/contrib/captainagent/tools/__init__.py +5 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/calculate_correlation.py +40 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/calculate_skewness_and_kurtosis.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/detect_outlier_iqr.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/detect_outlier_zscore.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/explore_csv.py +21 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/shapiro_wilk_test.py +30 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/arxiv_download.py +27 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/arxiv_search.py +53 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/extract_pdf_image.py +53 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/extract_pdf_text.py +38 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/get_wikipedia_text.py +21 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/get_youtube_caption.py +34 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/image_qa.py +60 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/optical_character_recognition.py +61 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/perform_web_search.py +47 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/scrape_wikipedia_tables.py +33 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/transcribe_audio_file.py +21 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/youtube_download.py +35 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_circle_area_from_diameter.py +21 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_day_of_the_week.py +18 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_fraction_sum.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_matrix_power.py +31 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_reflected_point.py +16 -0
- autogen/agentchat/contrib/captainagent/tools/math/complex_numbers_product.py +25 -0
- autogen/agentchat/contrib/captainagent/tools/math/compute_currency_conversion.py +23 -0
- autogen/agentchat/contrib/captainagent/tools/math/count_distinct_permutations.py +27 -0
- autogen/agentchat/contrib/captainagent/tools/math/evaluate_expression.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/math/find_continuity_point.py +34 -0
- autogen/agentchat/contrib/captainagent/tools/math/fraction_to_mixed_numbers.py +39 -0
- autogen/agentchat/contrib/captainagent/tools/math/modular_inverse_sum.py +23 -0
- autogen/agentchat/contrib/captainagent/tools/math/simplify_mixed_numbers.py +36 -0
- autogen/agentchat/contrib/captainagent/tools/math/sum_of_digit_factorials.py +15 -0
- autogen/agentchat/contrib/captainagent/tools/math/sum_of_primes_below.py +15 -0
- autogen/agentchat/contrib/captainagent/tools/requirements.txt +10 -0
- autogen/agentchat/contrib/captainagent/tools/tool_description.tsv +34 -0
- autogen/agentchat/contrib/gpt_assistant_agent.py +526 -0
- autogen/agentchat/contrib/graph_rag/__init__.py +9 -0
- autogen/agentchat/contrib/graph_rag/document.py +29 -0
- autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py +167 -0
- autogen/agentchat/contrib/graph_rag/falkor_graph_rag_capability.py +103 -0
- autogen/agentchat/contrib/graph_rag/graph_query_engine.py +53 -0
- autogen/agentchat/contrib/graph_rag/graph_rag_capability.py +63 -0
- autogen/agentchat/contrib/graph_rag/neo4j_graph_query_engine.py +263 -0
- autogen/agentchat/contrib/graph_rag/neo4j_graph_rag_capability.py +83 -0
- autogen/agentchat/contrib/graph_rag/neo4j_native_graph_query_engine.py +210 -0
- autogen/agentchat/contrib/graph_rag/neo4j_native_graph_rag_capability.py +93 -0
- autogen/agentchat/contrib/img_utils.py +397 -0
- autogen/agentchat/contrib/llamaindex_conversable_agent.py +117 -0
- autogen/agentchat/contrib/llava_agent.py +189 -0
- autogen/agentchat/contrib/math_user_proxy_agent.py +464 -0
- autogen/agentchat/contrib/multimodal_conversable_agent.py +125 -0
- autogen/agentchat/contrib/qdrant_retrieve_user_proxy_agent.py +325 -0
- autogen/agentchat/contrib/rag/__init__.py +10 -0
- autogen/agentchat/contrib/rag/chromadb_query_engine.py +268 -0
- autogen/agentchat/contrib/rag/llamaindex_query_engine.py +195 -0
- autogen/agentchat/contrib/rag/mongodb_query_engine.py +319 -0
- autogen/agentchat/contrib/rag/query_engine.py +76 -0
- autogen/agentchat/contrib/retrieve_assistant_agent.py +59 -0
- autogen/agentchat/contrib/retrieve_user_proxy_agent.py +704 -0
- autogen/agentchat/contrib/society_of_mind_agent.py +200 -0
- autogen/agentchat/contrib/swarm_agent.py +1404 -0
- autogen/agentchat/contrib/text_analyzer_agent.py +79 -0
- autogen/agentchat/contrib/vectordb/__init__.py +5 -0
- autogen/agentchat/contrib/vectordb/base.py +224 -0
- autogen/agentchat/contrib/vectordb/chromadb.py +316 -0
- autogen/agentchat/contrib/vectordb/couchbase.py +405 -0
- autogen/agentchat/contrib/vectordb/mongodb.py +551 -0
- autogen/agentchat/contrib/vectordb/pgvectordb.py +927 -0
- autogen/agentchat/contrib/vectordb/qdrant.py +320 -0
- autogen/agentchat/contrib/vectordb/utils.py +126 -0
- autogen/agentchat/contrib/web_surfer.py +304 -0
- autogen/agentchat/conversable_agent.py +4307 -0
- autogen/agentchat/group/__init__.py +67 -0
- autogen/agentchat/group/available_condition.py +91 -0
- autogen/agentchat/group/context_condition.py +77 -0
- autogen/agentchat/group/context_expression.py +238 -0
- autogen/agentchat/group/context_str.py +39 -0
- autogen/agentchat/group/context_variables.py +182 -0
- autogen/agentchat/group/events/transition_events.py +111 -0
- autogen/agentchat/group/group_tool_executor.py +324 -0
- autogen/agentchat/group/group_utils.py +659 -0
- autogen/agentchat/group/guardrails.py +179 -0
- autogen/agentchat/group/handoffs.py +303 -0
- autogen/agentchat/group/llm_condition.py +93 -0
- autogen/agentchat/group/multi_agent_chat.py +291 -0
- autogen/agentchat/group/on_condition.py +55 -0
- autogen/agentchat/group/on_context_condition.py +51 -0
- autogen/agentchat/group/patterns/__init__.py +18 -0
- autogen/agentchat/group/patterns/auto.py +160 -0
- autogen/agentchat/group/patterns/manual.py +177 -0
- autogen/agentchat/group/patterns/pattern.py +295 -0
- autogen/agentchat/group/patterns/random.py +106 -0
- autogen/agentchat/group/patterns/round_robin.py +117 -0
- autogen/agentchat/group/reply_result.py +24 -0
- autogen/agentchat/group/safeguards/__init__.py +21 -0
- autogen/agentchat/group/safeguards/api.py +241 -0
- autogen/agentchat/group/safeguards/enforcer.py +1158 -0
- autogen/agentchat/group/safeguards/events.py +140 -0
- autogen/agentchat/group/safeguards/validator.py +435 -0
- autogen/agentchat/group/speaker_selection_result.py +41 -0
- autogen/agentchat/group/targets/__init__.py +4 -0
- autogen/agentchat/group/targets/function_target.py +245 -0
- autogen/agentchat/group/targets/group_chat_target.py +133 -0
- autogen/agentchat/group/targets/group_manager_target.py +151 -0
- autogen/agentchat/group/targets/transition_target.py +424 -0
- autogen/agentchat/group/targets/transition_utils.py +6 -0
- autogen/agentchat/groupchat.py +1832 -0
- autogen/agentchat/realtime/__init__.py +3 -0
- autogen/agentchat/realtime/experimental/__init__.py +20 -0
- autogen/agentchat/realtime/experimental/audio_adapters/__init__.py +8 -0
- autogen/agentchat/realtime/experimental/audio_adapters/twilio_audio_adapter.py +148 -0
- autogen/agentchat/realtime/experimental/audio_adapters/websocket_audio_adapter.py +139 -0
- autogen/agentchat/realtime/experimental/audio_observer.py +42 -0
- autogen/agentchat/realtime/experimental/clients/__init__.py +15 -0
- autogen/agentchat/realtime/experimental/clients/gemini/__init__.py +7 -0
- autogen/agentchat/realtime/experimental/clients/gemini/client.py +274 -0
- autogen/agentchat/realtime/experimental/clients/oai/__init__.py +8 -0
- autogen/agentchat/realtime/experimental/clients/oai/base_client.py +220 -0
- autogen/agentchat/realtime/experimental/clients/oai/rtc_client.py +243 -0
- autogen/agentchat/realtime/experimental/clients/oai/utils.py +48 -0
- autogen/agentchat/realtime/experimental/clients/realtime_client.py +191 -0
- autogen/agentchat/realtime/experimental/function_observer.py +84 -0
- autogen/agentchat/realtime/experimental/realtime_agent.py +158 -0
- autogen/agentchat/realtime/experimental/realtime_events.py +42 -0
- autogen/agentchat/realtime/experimental/realtime_observer.py +100 -0
- autogen/agentchat/realtime/experimental/realtime_swarm.py +533 -0
- autogen/agentchat/realtime/experimental/websockets.py +21 -0
- autogen/agentchat/realtime_agent/__init__.py +21 -0
- autogen/agentchat/user_proxy_agent.py +114 -0
- autogen/agentchat/utils.py +206 -0
- autogen/agents/__init__.py +3 -0
- autogen/agents/contrib/__init__.py +10 -0
- autogen/agents/contrib/time/__init__.py +8 -0
- autogen/agents/contrib/time/time_reply_agent.py +74 -0
- autogen/agents/contrib/time/time_tool_agent.py +52 -0
- autogen/agents/experimental/__init__.py +27 -0
- autogen/agents/experimental/deep_research/__init__.py +7 -0
- autogen/agents/experimental/deep_research/deep_research.py +52 -0
- autogen/agents/experimental/discord/__init__.py +7 -0
- autogen/agents/experimental/discord/discord.py +66 -0
- autogen/agents/experimental/document_agent/__init__.py +19 -0
- autogen/agents/experimental/document_agent/chroma_query_engine.py +301 -0
- autogen/agents/experimental/document_agent/docling_doc_ingest_agent.py +113 -0
- autogen/agents/experimental/document_agent/document_agent.py +643 -0
- autogen/agents/experimental/document_agent/document_conditions.py +50 -0
- autogen/agents/experimental/document_agent/document_utils.py +376 -0
- autogen/agents/experimental/document_agent/inmemory_query_engine.py +214 -0
- autogen/agents/experimental/document_agent/parser_utils.py +134 -0
- autogen/agents/experimental/document_agent/url_utils.py +417 -0
- autogen/agents/experimental/reasoning/__init__.py +7 -0
- autogen/agents/experimental/reasoning/reasoning_agent.py +1178 -0
- autogen/agents/experimental/slack/__init__.py +7 -0
- autogen/agents/experimental/slack/slack.py +73 -0
- autogen/agents/experimental/telegram/__init__.py +7 -0
- autogen/agents/experimental/telegram/telegram.py +76 -0
- autogen/agents/experimental/websurfer/__init__.py +7 -0
- autogen/agents/experimental/websurfer/websurfer.py +70 -0
- autogen/agents/experimental/wikipedia/__init__.py +7 -0
- autogen/agents/experimental/wikipedia/wikipedia.py +88 -0
- autogen/browser_utils.py +309 -0
- autogen/cache/__init__.py +10 -0
- autogen/cache/abstract_cache_base.py +71 -0
- autogen/cache/cache.py +203 -0
- autogen/cache/cache_factory.py +88 -0
- autogen/cache/cosmos_db_cache.py +144 -0
- autogen/cache/disk_cache.py +97 -0
- autogen/cache/in_memory_cache.py +54 -0
- autogen/cache/redis_cache.py +119 -0
- autogen/code_utils.py +598 -0
- autogen/coding/__init__.py +30 -0
- autogen/coding/base.py +120 -0
- autogen/coding/docker_commandline_code_executor.py +283 -0
- autogen/coding/factory.py +56 -0
- autogen/coding/func_with_reqs.py +203 -0
- autogen/coding/jupyter/__init__.py +23 -0
- autogen/coding/jupyter/base.py +36 -0
- autogen/coding/jupyter/docker_jupyter_server.py +160 -0
- autogen/coding/jupyter/embedded_ipython_code_executor.py +182 -0
- autogen/coding/jupyter/import_utils.py +82 -0
- autogen/coding/jupyter/jupyter_client.py +224 -0
- autogen/coding/jupyter/jupyter_code_executor.py +154 -0
- autogen/coding/jupyter/local_jupyter_server.py +164 -0
- autogen/coding/local_commandline_code_executor.py +341 -0
- autogen/coding/markdown_code_extractor.py +44 -0
- autogen/coding/utils.py +55 -0
- autogen/coding/yepcode_code_executor.py +197 -0
- autogen/doc_utils.py +35 -0
- autogen/environments/__init__.py +10 -0
- autogen/environments/docker_python_environment.py +365 -0
- autogen/environments/python_environment.py +125 -0
- autogen/environments/system_python_environment.py +85 -0
- autogen/environments/venv_python_environment.py +220 -0
- autogen/environments/working_directory.py +74 -0
- autogen/events/__init__.py +7 -0
- autogen/events/agent_events.py +1016 -0
- autogen/events/base_event.py +100 -0
- autogen/events/client_events.py +168 -0
- autogen/events/helpers.py +44 -0
- autogen/events/print_event.py +45 -0
- autogen/exception_utils.py +73 -0
- autogen/extensions/__init__.py +5 -0
- autogen/fast_depends/__init__.py +16 -0
- autogen/fast_depends/_compat.py +75 -0
- autogen/fast_depends/core/__init__.py +14 -0
- autogen/fast_depends/core/build.py +206 -0
- autogen/fast_depends/core/model.py +527 -0
- autogen/fast_depends/dependencies/__init__.py +15 -0
- autogen/fast_depends/dependencies/model.py +30 -0
- autogen/fast_depends/dependencies/provider.py +40 -0
- autogen/fast_depends/library/__init__.py +10 -0
- autogen/fast_depends/library/model.py +46 -0
- autogen/fast_depends/py.typed +6 -0
- autogen/fast_depends/schema.py +66 -0
- autogen/fast_depends/use.py +272 -0
- autogen/fast_depends/utils.py +177 -0
- autogen/formatting_utils.py +83 -0
- autogen/function_utils.py +13 -0
- autogen/graph_utils.py +173 -0
- autogen/import_utils.py +539 -0
- autogen/interop/__init__.py +22 -0
- autogen/interop/crewai/__init__.py +7 -0
- autogen/interop/crewai/crewai.py +88 -0
- autogen/interop/interoperability.py +71 -0
- autogen/interop/interoperable.py +46 -0
- autogen/interop/langchain/__init__.py +8 -0
- autogen/interop/langchain/langchain_chat_model_factory.py +156 -0
- autogen/interop/langchain/langchain_tool.py +78 -0
- autogen/interop/litellm/__init__.py +7 -0
- autogen/interop/litellm/litellm_config_factory.py +178 -0
- autogen/interop/pydantic_ai/__init__.py +7 -0
- autogen/interop/pydantic_ai/pydantic_ai.py +172 -0
- autogen/interop/registry.py +70 -0
- autogen/io/__init__.py +15 -0
- autogen/io/base.py +151 -0
- autogen/io/console.py +56 -0
- autogen/io/processors/__init__.py +12 -0
- autogen/io/processors/base.py +21 -0
- autogen/io/processors/console_event_processor.py +61 -0
- autogen/io/run_response.py +294 -0
- autogen/io/thread_io_stream.py +63 -0
- autogen/io/websockets.py +214 -0
- autogen/json_utils.py +42 -0
- autogen/llm_clients/MIGRATION_TO_V2.md +782 -0
- autogen/llm_clients/__init__.py +77 -0
- autogen/llm_clients/client_v2.py +122 -0
- autogen/llm_clients/models/__init__.py +55 -0
- autogen/llm_clients/models/content_blocks.py +389 -0
- autogen/llm_clients/models/unified_message.py +145 -0
- autogen/llm_clients/models/unified_response.py +83 -0
- autogen/llm_clients/openai_completions_client.py +444 -0
- autogen/llm_config/__init__.py +11 -0
- autogen/llm_config/client.py +59 -0
- autogen/llm_config/config.py +461 -0
- autogen/llm_config/entry.py +169 -0
- autogen/llm_config/types.py +37 -0
- autogen/llm_config/utils.py +223 -0
- autogen/logger/__init__.py +11 -0
- autogen/logger/base_logger.py +129 -0
- autogen/logger/file_logger.py +262 -0
- autogen/logger/logger_factory.py +42 -0
- autogen/logger/logger_utils.py +57 -0
- autogen/logger/sqlite_logger.py +524 -0
- autogen/math_utils.py +338 -0
- autogen/mcp/__init__.py +7 -0
- autogen/mcp/__main__.py +78 -0
- autogen/mcp/helpers.py +45 -0
- autogen/mcp/mcp_client.py +349 -0
- autogen/mcp/mcp_proxy/__init__.py +19 -0
- autogen/mcp/mcp_proxy/fastapi_code_generator_helpers.py +62 -0
- autogen/mcp/mcp_proxy/mcp_proxy.py +577 -0
- autogen/mcp/mcp_proxy/operation_grouping.py +166 -0
- autogen/mcp/mcp_proxy/operation_renaming.py +110 -0
- autogen/mcp/mcp_proxy/patch_fastapi_code_generator.py +98 -0
- autogen/mcp/mcp_proxy/security.py +399 -0
- autogen/mcp/mcp_proxy/security_schema_visitor.py +37 -0
- autogen/messages/__init__.py +7 -0
- autogen/messages/agent_messages.py +946 -0
- autogen/messages/base_message.py +108 -0
- autogen/messages/client_messages.py +172 -0
- autogen/messages/print_message.py +48 -0
- autogen/oai/__init__.py +61 -0
- autogen/oai/anthropic.py +1516 -0
- autogen/oai/bedrock.py +800 -0
- autogen/oai/cerebras.py +302 -0
- autogen/oai/client.py +1658 -0
- autogen/oai/client_utils.py +196 -0
- autogen/oai/cohere.py +494 -0
- autogen/oai/gemini.py +1045 -0
- autogen/oai/gemini_types.py +156 -0
- autogen/oai/groq.py +319 -0
- autogen/oai/mistral.py +311 -0
- autogen/oai/oai_models/__init__.py +23 -0
- autogen/oai/oai_models/_models.py +16 -0
- autogen/oai/oai_models/chat_completion.py +86 -0
- autogen/oai/oai_models/chat_completion_audio.py +32 -0
- autogen/oai/oai_models/chat_completion_message.py +97 -0
- autogen/oai/oai_models/chat_completion_message_tool_call.py +60 -0
- autogen/oai/oai_models/chat_completion_token_logprob.py +62 -0
- autogen/oai/oai_models/completion_usage.py +59 -0
- autogen/oai/ollama.py +657 -0
- autogen/oai/openai_responses.py +451 -0
- autogen/oai/openai_utils.py +897 -0
- autogen/oai/together.py +387 -0
- autogen/remote/__init__.py +18 -0
- autogen/remote/agent.py +199 -0
- autogen/remote/agent_service.py +197 -0
- autogen/remote/errors.py +17 -0
- autogen/remote/httpx_client_factory.py +131 -0
- autogen/remote/protocol.py +37 -0
- autogen/remote/retry.py +102 -0
- autogen/remote/runtime.py +96 -0
- autogen/retrieve_utils.py +490 -0
- autogen/runtime_logging.py +161 -0
- autogen/testing/__init__.py +12 -0
- autogen/testing/messages.py +45 -0
- autogen/testing/test_agent.py +111 -0
- autogen/token_count_utils.py +280 -0
- autogen/tools/__init__.py +20 -0
- autogen/tools/contrib/__init__.py +9 -0
- autogen/tools/contrib/time/__init__.py +7 -0
- autogen/tools/contrib/time/time.py +40 -0
- autogen/tools/dependency_injection.py +249 -0
- autogen/tools/experimental/__init__.py +54 -0
- autogen/tools/experimental/browser_use/__init__.py +7 -0
- autogen/tools/experimental/browser_use/browser_use.py +154 -0
- autogen/tools/experimental/code_execution/__init__.py +7 -0
- autogen/tools/experimental/code_execution/python_code_execution.py +86 -0
- autogen/tools/experimental/crawl4ai/__init__.py +7 -0
- autogen/tools/experimental/crawl4ai/crawl4ai.py +150 -0
- autogen/tools/experimental/deep_research/__init__.py +7 -0
- autogen/tools/experimental/deep_research/deep_research.py +329 -0
- autogen/tools/experimental/duckduckgo/__init__.py +7 -0
- autogen/tools/experimental/duckduckgo/duckduckgo_search.py +103 -0
- autogen/tools/experimental/firecrawl/__init__.py +7 -0
- autogen/tools/experimental/firecrawl/firecrawl_tool.py +836 -0
- autogen/tools/experimental/google/__init__.py +14 -0
- autogen/tools/experimental/google/authentication/__init__.py +11 -0
- autogen/tools/experimental/google/authentication/credentials_hosted_provider.py +43 -0
- autogen/tools/experimental/google/authentication/credentials_local_provider.py +91 -0
- autogen/tools/experimental/google/authentication/credentials_provider.py +35 -0
- autogen/tools/experimental/google/drive/__init__.py +9 -0
- autogen/tools/experimental/google/drive/drive_functions.py +124 -0
- autogen/tools/experimental/google/drive/toolkit.py +88 -0
- autogen/tools/experimental/google/model.py +17 -0
- autogen/tools/experimental/google/toolkit_protocol.py +19 -0
- autogen/tools/experimental/google_search/__init__.py +8 -0
- autogen/tools/experimental/google_search/google_search.py +93 -0
- autogen/tools/experimental/google_search/youtube_search.py +181 -0
- autogen/tools/experimental/messageplatform/__init__.py +17 -0
- autogen/tools/experimental/messageplatform/discord/__init__.py +7 -0
- autogen/tools/experimental/messageplatform/discord/discord.py +284 -0
- autogen/tools/experimental/messageplatform/slack/__init__.py +7 -0
- autogen/tools/experimental/messageplatform/slack/slack.py +385 -0
- autogen/tools/experimental/messageplatform/telegram/__init__.py +7 -0
- autogen/tools/experimental/messageplatform/telegram/telegram.py +271 -0
- autogen/tools/experimental/perplexity/__init__.py +7 -0
- autogen/tools/experimental/perplexity/perplexity_search.py +249 -0
- autogen/tools/experimental/reliable/__init__.py +10 -0
- autogen/tools/experimental/reliable/reliable.py +1311 -0
- autogen/tools/experimental/searxng/__init__.py +7 -0
- autogen/tools/experimental/searxng/searxng_search.py +142 -0
- autogen/tools/experimental/tavily/__init__.py +7 -0
- autogen/tools/experimental/tavily/tavily_search.py +176 -0
- autogen/tools/experimental/web_search_preview/__init__.py +7 -0
- autogen/tools/experimental/web_search_preview/web_search_preview.py +120 -0
- autogen/tools/experimental/wikipedia/__init__.py +7 -0
- autogen/tools/experimental/wikipedia/wikipedia.py +284 -0
- autogen/tools/function_utils.py +412 -0
- autogen/tools/tool.py +188 -0
- autogen/tools/toolkit.py +86 -0
- autogen/types.py +29 -0
- autogen/version.py +7 -0
- templates/client_template/main.jinja2 +72 -0
- templates/config_template/config.jinja2 +7 -0
- templates/main.jinja2 +61 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
import tempfile
|
|
10
|
+
import uuid
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from anyio import to_thread
|
|
14
|
+
|
|
15
|
+
from .python_environment import PythonEnvironment
|
|
16
|
+
|
|
17
|
+
__all__ = ["DockerPythonEnvironment"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DockerPythonEnvironment(PythonEnvironment):
|
|
21
|
+
"""A Python environment using Docker containers for isolated execution."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
image: str = "python:3.11-slim",
|
|
26
|
+
container_name_prefix: str = "ag2_docker_env_",
|
|
27
|
+
volumes: dict[str, str] | None = None,
|
|
28
|
+
environment: dict[str, str] | None = None,
|
|
29
|
+
network: str | None = None,
|
|
30
|
+
pip_packages: list[str] | None = None,
|
|
31
|
+
requirements_file: str | None = None,
|
|
32
|
+
dockerfile: str | None = None,
|
|
33
|
+
build_args: dict[str, str] | None = None,
|
|
34
|
+
cleanup_container: bool = True,
|
|
35
|
+
keep_container_running: bool = False,
|
|
36
|
+
container_startup_timeout: int = 30,
|
|
37
|
+
):
|
|
38
|
+
"""Initialize a Docker Python environment.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
image: Docker image to use (ignored if dockerfile is provided)
|
|
42
|
+
container_name_prefix: Prefix for container names
|
|
43
|
+
volumes: Dictionary mapping host paths to container paths for mounting
|
|
44
|
+
environment: Dictionary of environment variables to set in the container
|
|
45
|
+
network: Docker network to attach the container to
|
|
46
|
+
pip_packages: List of pip packages to install in the container
|
|
47
|
+
requirements_file: Path to requirements.txt file to install in the container
|
|
48
|
+
dockerfile: Optional path to a Dockerfile to build and use instead of pulling an image
|
|
49
|
+
build_args: Optional build arguments for the Dockerfile
|
|
50
|
+
cleanup_container: Whether to remove the container after use
|
|
51
|
+
keep_container_running: Whether to keep the container running after execution
|
|
52
|
+
container_startup_timeout: Timeout in seconds for container startup
|
|
53
|
+
"""
|
|
54
|
+
self.image = image
|
|
55
|
+
self.container_name_prefix = container_name_prefix
|
|
56
|
+
self.volumes = volumes or {}
|
|
57
|
+
self.environment = environment or {}
|
|
58
|
+
self.network = network
|
|
59
|
+
self.pip_packages = pip_packages or []
|
|
60
|
+
self.requirements_file = requirements_file
|
|
61
|
+
self.dockerfile = dockerfile
|
|
62
|
+
self.build_args = build_args or {}
|
|
63
|
+
self.cleanup_container = cleanup_container
|
|
64
|
+
self.keep_container_running = keep_container_running
|
|
65
|
+
self.container_startup_timeout = container_startup_timeout
|
|
66
|
+
|
|
67
|
+
# Internal state
|
|
68
|
+
self._container_id = None
|
|
69
|
+
self._container_name = None
|
|
70
|
+
self._custom_image_name = None
|
|
71
|
+
self._temp_dir = None
|
|
72
|
+
|
|
73
|
+
super().__init__()
|
|
74
|
+
|
|
75
|
+
def _setup_environment(self) -> None:
|
|
76
|
+
"""Set up the Docker environment."""
|
|
77
|
+
# Verify Docker is installed and accessible
|
|
78
|
+
try:
|
|
79
|
+
result = subprocess.run(["docker", "--version"], capture_output=True, text=True, check=True)
|
|
80
|
+
logging.info(f"Docker version: {result.stdout.strip()}")
|
|
81
|
+
except (subprocess.SubprocessError, FileNotFoundError) as e:
|
|
82
|
+
raise RuntimeError(
|
|
83
|
+
"Docker not found or not accessible. Please ensure Docker is installed and running."
|
|
84
|
+
) from e
|
|
85
|
+
|
|
86
|
+
# Create a temporary directory for file operations
|
|
87
|
+
self._temp_dir = tempfile.mkdtemp(prefix="ag2_docker_")
|
|
88
|
+
|
|
89
|
+
# Generate a unique container name
|
|
90
|
+
self._container_name = f"{self.container_name_prefix}{uuid.uuid4().hex[:8]}"
|
|
91
|
+
|
|
92
|
+
# Build custom image if Dockerfile is provided
|
|
93
|
+
if self.dockerfile:
|
|
94
|
+
self._build_custom_image()
|
|
95
|
+
else:
|
|
96
|
+
# Pull the specified image
|
|
97
|
+
try:
|
|
98
|
+
subprocess.run(
|
|
99
|
+
["docker", "pull", self.image],
|
|
100
|
+
check=True,
|
|
101
|
+
capture_output=True,
|
|
102
|
+
text=True,
|
|
103
|
+
)
|
|
104
|
+
logging.info(f"Pulled Docker image: {self.image}")
|
|
105
|
+
except subprocess.CalledProcessError as e:
|
|
106
|
+
raise RuntimeError(f"Failed to pull Docker image: {e.stderr}") from e
|
|
107
|
+
|
|
108
|
+
# Start the container
|
|
109
|
+
self._start_container()
|
|
110
|
+
|
|
111
|
+
def _build_custom_image(self) -> None:
|
|
112
|
+
"""Build a custom Docker image from the provided Dockerfile."""
|
|
113
|
+
if not os.path.exists(self.dockerfile):
|
|
114
|
+
raise RuntimeError(f"Dockerfile not found at: {self.dockerfile}")
|
|
115
|
+
|
|
116
|
+
# Create a unique image name
|
|
117
|
+
self._custom_image_name = f"ag2-custom-python-{uuid.uuid4().hex[:8]}"
|
|
118
|
+
|
|
119
|
+
# Build command
|
|
120
|
+
build_cmd = ["docker", "build", "-t", self._custom_image_name]
|
|
121
|
+
|
|
122
|
+
# Add build args
|
|
123
|
+
for arg_name, arg_value in self.build_args.items():
|
|
124
|
+
build_cmd.extend(["--build-arg", f"{arg_name}={arg_value}"])
|
|
125
|
+
|
|
126
|
+
# Add Dockerfile path
|
|
127
|
+
build_cmd.extend(["-f", self.dockerfile, os.path.dirname(self.dockerfile)])
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
logging.info(f"Building custom Docker image: {self._custom_image_name}")
|
|
131
|
+
_ = subprocess.run(
|
|
132
|
+
build_cmd,
|
|
133
|
+
check=True,
|
|
134
|
+
capture_output=True,
|
|
135
|
+
text=True,
|
|
136
|
+
)
|
|
137
|
+
logging.info(f"Built custom Docker image: {self._custom_image_name}")
|
|
138
|
+
except subprocess.CalledProcessError as e:
|
|
139
|
+
raise RuntimeError(f"Failed to build Docker image: {e.stderr}") from e
|
|
140
|
+
|
|
141
|
+
# Use the custom image
|
|
142
|
+
self.image = self._custom_image_name
|
|
143
|
+
|
|
144
|
+
def _start_container(self) -> None:
|
|
145
|
+
"""Start the Docker container."""
|
|
146
|
+
# Basic container run command
|
|
147
|
+
run_cmd = ["docker", "run", "--name", self._container_name]
|
|
148
|
+
|
|
149
|
+
# Add detached mode flag to run container in background
|
|
150
|
+
run_cmd.append("-d")
|
|
151
|
+
|
|
152
|
+
# Add network if specified
|
|
153
|
+
if self.network:
|
|
154
|
+
run_cmd.extend(["--network", self.network])
|
|
155
|
+
|
|
156
|
+
# Add environment variables
|
|
157
|
+
for env_name, env_value in self.environment.items():
|
|
158
|
+
run_cmd.extend(["-e", f"{env_name}={env_value}"])
|
|
159
|
+
|
|
160
|
+
# Add volume mounts including temp directory
|
|
161
|
+
work_dir_mount = f"{self._temp_dir}:/workspace"
|
|
162
|
+
run_cmd.extend(["-v", work_dir_mount])
|
|
163
|
+
|
|
164
|
+
for host_path, container_path in self.volumes.items():
|
|
165
|
+
run_cmd.extend(["-v", f"{host_path}:{container_path}"])
|
|
166
|
+
|
|
167
|
+
# Set workspace as working directory
|
|
168
|
+
run_cmd.extend(["-w", "/workspace"])
|
|
169
|
+
|
|
170
|
+
# Add tty to keep container running
|
|
171
|
+
run_cmd.append("-t")
|
|
172
|
+
|
|
173
|
+
# Add image name
|
|
174
|
+
run_cmd.append(self.image)
|
|
175
|
+
|
|
176
|
+
# Initial command to keep container running
|
|
177
|
+
run_cmd.extend(["tail", "-f", "/dev/null"])
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
# Start the container
|
|
181
|
+
logging.info(f"Starting Docker container: {self._container_name}")
|
|
182
|
+
result = subprocess.run(
|
|
183
|
+
run_cmd,
|
|
184
|
+
check=True,
|
|
185
|
+
capture_output=True,
|
|
186
|
+
text=True,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Get container ID
|
|
190
|
+
self._container_id = result.stdout.strip()
|
|
191
|
+
logging.info(f"Started Docker container: {self._container_name} ({self._container_id})")
|
|
192
|
+
|
|
193
|
+
# Install pip packages if specified
|
|
194
|
+
if self.pip_packages or self.requirements_file:
|
|
195
|
+
self._install_packages()
|
|
196
|
+
|
|
197
|
+
except subprocess.CalledProcessError as e:
|
|
198
|
+
raise RuntimeError(f"Failed to start Docker container: {e.stderr}") from e
|
|
199
|
+
|
|
200
|
+
def _install_packages(self) -> None:
|
|
201
|
+
"""Install Python packages in the running container."""
|
|
202
|
+
# Install pip packages
|
|
203
|
+
if self.pip_packages:
|
|
204
|
+
packages_str = " ".join(self.pip_packages)
|
|
205
|
+
try:
|
|
206
|
+
logging.info(f"Installing pip packages: {packages_str}")
|
|
207
|
+
_ = subprocess.run(
|
|
208
|
+
["docker", "exec", self._container_name, "pip", "install", "--no-cache-dir"] + self.pip_packages,
|
|
209
|
+
check=True,
|
|
210
|
+
capture_output=True,
|
|
211
|
+
text=True,
|
|
212
|
+
)
|
|
213
|
+
logging.info("Successfully installed pip packages")
|
|
214
|
+
except subprocess.CalledProcessError as e:
|
|
215
|
+
logging.warning(f"Failed to install pip packages: {e.stderr}")
|
|
216
|
+
|
|
217
|
+
# Install from requirements file
|
|
218
|
+
if self.requirements_file:
|
|
219
|
+
if os.path.exists(self.requirements_file):
|
|
220
|
+
# Copy requirements file to temp directory
|
|
221
|
+
req_filename = os.path.basename(self.requirements_file)
|
|
222
|
+
temp_req_path = os.path.join(self._temp_dir, req_filename)
|
|
223
|
+
shutil.copy(self.requirements_file, temp_req_path)
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
logging.info(f"Installing requirements from: {req_filename}")
|
|
227
|
+
_ = subprocess.run(
|
|
228
|
+
[
|
|
229
|
+
"docker",
|
|
230
|
+
"exec",
|
|
231
|
+
self._container_name,
|
|
232
|
+
"pip",
|
|
233
|
+
"install",
|
|
234
|
+
"--no-cache-dir",
|
|
235
|
+
"-r",
|
|
236
|
+
f"/workspace/{req_filename}",
|
|
237
|
+
],
|
|
238
|
+
check=True,
|
|
239
|
+
capture_output=True,
|
|
240
|
+
text=True,
|
|
241
|
+
)
|
|
242
|
+
logging.info("Successfully installed requirements")
|
|
243
|
+
except subprocess.CalledProcessError as e:
|
|
244
|
+
logging.warning(f"Failed to install requirements: {e.stderr}")
|
|
245
|
+
else:
|
|
246
|
+
logging.warning(f"Requirements file not found: {self.requirements_file}")
|
|
247
|
+
|
|
248
|
+
def _cleanup_environment(self) -> None:
|
|
249
|
+
"""Clean up the Docker environment."""
|
|
250
|
+
if self._container_id:
|
|
251
|
+
# Stop the container if it's running and we want to clean it up
|
|
252
|
+
if not self.keep_container_running:
|
|
253
|
+
try:
|
|
254
|
+
logging.info(f"Stopping Docker container: {self._container_name}")
|
|
255
|
+
subprocess.run(
|
|
256
|
+
["docker", "stop", self._container_name],
|
|
257
|
+
check=True,
|
|
258
|
+
capture_output=True,
|
|
259
|
+
text=True,
|
|
260
|
+
)
|
|
261
|
+
except subprocess.CalledProcessError:
|
|
262
|
+
logging.warning(f"Failed to stop Docker container: {self._container_name}")
|
|
263
|
+
|
|
264
|
+
# Remove the container if cleanup is enabled
|
|
265
|
+
if self.cleanup_container and not self.keep_container_running:
|
|
266
|
+
try:
|
|
267
|
+
logging.info(f"Removing Docker container: {self._container_name}")
|
|
268
|
+
subprocess.run(
|
|
269
|
+
["docker", "rm", "-f", self._container_name],
|
|
270
|
+
check=True,
|
|
271
|
+
capture_output=True,
|
|
272
|
+
text=True,
|
|
273
|
+
)
|
|
274
|
+
except subprocess.CalledProcessError:
|
|
275
|
+
logging.warning(f"Failed to remove Docker container: {self._container_name}")
|
|
276
|
+
|
|
277
|
+
# Remove the custom image if it was created
|
|
278
|
+
if self._custom_image_name and self.cleanup_container:
|
|
279
|
+
try:
|
|
280
|
+
logging.info(f"Removing custom Docker image: {self._custom_image_name}")
|
|
281
|
+
subprocess.run(
|
|
282
|
+
["docker", "rmi", self._custom_image_name],
|
|
283
|
+
check=True,
|
|
284
|
+
capture_output=True,
|
|
285
|
+
text=True,
|
|
286
|
+
)
|
|
287
|
+
except subprocess.CalledProcessError:
|
|
288
|
+
logging.warning(f"Failed to remove custom Docker image: {self._custom_image_name}")
|
|
289
|
+
|
|
290
|
+
# Clean up the temporary directory
|
|
291
|
+
if self._temp_dir and os.path.exists(self._temp_dir):
|
|
292
|
+
try:
|
|
293
|
+
shutil.rmtree(self._temp_dir)
|
|
294
|
+
except Exception as e:
|
|
295
|
+
logging.warning(f"Failed to remove temporary directory: {e}")
|
|
296
|
+
|
|
297
|
+
def get_executable(self) -> str:
|
|
298
|
+
"""Get the path to the Python executable in the Docker container."""
|
|
299
|
+
# This is a virtual path in the container
|
|
300
|
+
return "python"
|
|
301
|
+
|
|
302
|
+
async def execute_code(self, code: str, script_path: str, timeout: int = 30) -> dict[str, Any]:
|
|
303
|
+
"""Execute code in the Docker container."""
|
|
304
|
+
# Ensure the container is running
|
|
305
|
+
if not self._container_id:
|
|
306
|
+
return {"success": False, "error": "Docker container not started"}
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
# Calculate the relative path within the temp directory
|
|
310
|
+
if os.path.isabs(script_path):
|
|
311
|
+
rel_path = os.path.basename(script_path)
|
|
312
|
+
host_script_path = os.path.join(self._temp_dir, rel_path)
|
|
313
|
+
else:
|
|
314
|
+
rel_path = script_path
|
|
315
|
+
host_script_path = os.path.join(self._temp_dir, rel_path)
|
|
316
|
+
|
|
317
|
+
# Ensure the directory for the script exists
|
|
318
|
+
script_dir = os.path.dirname(host_script_path)
|
|
319
|
+
if script_dir:
|
|
320
|
+
os.makedirs(script_dir, exist_ok=True)
|
|
321
|
+
|
|
322
|
+
# Write the code to the script file on the host
|
|
323
|
+
await to_thread.run_sync(self._write_to_file, host_script_path, code)
|
|
324
|
+
|
|
325
|
+
# Path to the script in the container
|
|
326
|
+
container_script_path = f"/workspace/{rel_path}"
|
|
327
|
+
|
|
328
|
+
# Execute the script in the container
|
|
329
|
+
exec_cmd = ["docker", "exec", self._container_name, "python", container_script_path]
|
|
330
|
+
|
|
331
|
+
# Run the command with a timeout
|
|
332
|
+
result = await to_thread.run_sync(self._run_subprocess_with_timeout, exec_cmd, timeout)
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
"success": result[0],
|
|
336
|
+
"stdout": result[1],
|
|
337
|
+
"stderr": result[2],
|
|
338
|
+
"returncode": result[3] if result[0] else 1,
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
except Exception as e:
|
|
342
|
+
return {"success": False, "error": f"Execution error: {str(e)}"}
|
|
343
|
+
|
|
344
|
+
def _run_subprocess_with_timeout(self, cmd: list[str], timeout: int) -> tuple[bool, str, str, int]:
|
|
345
|
+
"""Run a subprocess with timeout and return status, stdout, stderr, and return code.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
cmd: Command to run as a list of strings
|
|
349
|
+
timeout: Maximum execution time in seconds
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Tuple of (success, stdout, stderr, return_code)
|
|
353
|
+
"""
|
|
354
|
+
try:
|
|
355
|
+
result = subprocess.run(
|
|
356
|
+
cmd,
|
|
357
|
+
capture_output=True,
|
|
358
|
+
text=True,
|
|
359
|
+
timeout=timeout,
|
|
360
|
+
)
|
|
361
|
+
return (result.returncode == 0, result.stdout, result.stderr, result.returncode)
|
|
362
|
+
except subprocess.TimeoutExpired:
|
|
363
|
+
return (False, "", f"Execution timed out after {timeout} seconds", -1)
|
|
364
|
+
except Exception as e:
|
|
365
|
+
return (False, "", str(e), -1)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from contextvars import ContextVar
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
|
|
10
|
+
__all__ = ["PythonEnvironment"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PythonEnvironment(ABC):
|
|
14
|
+
"""Python execution environments base class"""
|
|
15
|
+
|
|
16
|
+
# Shared context variable for tracking the current environment
|
|
17
|
+
_current_python_environment: ContextVar["PythonEnvironment"] = ContextVar("_current_python_environment")
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
"""Initialize the Python environment."""
|
|
21
|
+
self._token = None
|
|
22
|
+
|
|
23
|
+
# Set up the environment
|
|
24
|
+
self._setup_environment()
|
|
25
|
+
|
|
26
|
+
def __enter__(self):
|
|
27
|
+
"""Enter the environment context.
|
|
28
|
+
Sets this environment as the current one.
|
|
29
|
+
"""
|
|
30
|
+
# Set this as the current Python environment in the context
|
|
31
|
+
self._token = PythonEnvironment._current_python_environment.set(self)
|
|
32
|
+
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
36
|
+
"""Exit the environment context.
|
|
37
|
+
Resets the current environment and performs cleanup.
|
|
38
|
+
"""
|
|
39
|
+
# Reset the context variable if this was the active environment
|
|
40
|
+
if self._token is not None:
|
|
41
|
+
PythonEnvironment._current_python_environment.reset(self._token)
|
|
42
|
+
self._token = None
|
|
43
|
+
|
|
44
|
+
# Clean up resources
|
|
45
|
+
self._cleanup_environment()
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def _setup_environment(self) -> None:
|
|
49
|
+
"""Set up the Python environment. Called by __enter__."""
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def _cleanup_environment(self) -> None:
|
|
54
|
+
"""Clean up the Python environment. Called by __exit__."""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def get_executable(self) -> str:
|
|
59
|
+
"""Get the path to the Python executable in this environment.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The full path to the Python executable.
|
|
63
|
+
"""
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
async def execute_code(self, code: str, script_path: str, timeout: int = 30) -> dict[str, Any]:
|
|
68
|
+
"""Execute the given code in this environment.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
code: The Python code to execute.
|
|
72
|
+
script_path: Path where the code should be saved before execution.
|
|
73
|
+
timeout: Maximum execution time in seconds.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
dict with execution results including stdout, stderr, and success status.
|
|
77
|
+
"""
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
# Utility method for subclasses to wrap (for async support)
|
|
81
|
+
def _write_to_file(self, script_path: str, content: str) -> None:
|
|
82
|
+
"""Write content to a file (blocking operation).
|
|
83
|
+
|
|
84
|
+
This is a helper method for use with asyncify in async contexts.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
script_path: Path to the file to write.
|
|
88
|
+
content: Content to write to the file.
|
|
89
|
+
"""
|
|
90
|
+
with open(script_path, "w") as f:
|
|
91
|
+
f.write(content)
|
|
92
|
+
|
|
93
|
+
# Utility method for subclasses to wrap (for async support)
|
|
94
|
+
def _run_subprocess(self, cmd: list[str], timeout: int) -> subprocess.CompletedProcess:
|
|
95
|
+
"""Run a subprocess (blocking operation).
|
|
96
|
+
|
|
97
|
+
This is a helper method for use with asyncify in async contexts.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
cmd: Command to run as a list of strings.
|
|
101
|
+
timeout: Maximum execution time in seconds.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
CompletedProcess instance with results of the subprocess.
|
|
105
|
+
"""
|
|
106
|
+
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def get_current_python_environment(
|
|
110
|
+
cls, python_environment: Optional["PythonEnvironment"] = None
|
|
111
|
+
) -> Optional["PythonEnvironment"]:
|
|
112
|
+
"""Get the current Python environment or the specified one if provided.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
python_environment: Optional environment to return if specified.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
The current Python environment or None if none is active.
|
|
119
|
+
"""
|
|
120
|
+
if python_environment is not None:
|
|
121
|
+
return python_environment
|
|
122
|
+
try:
|
|
123
|
+
return cls._current_python_environment.get()
|
|
124
|
+
except LookupError:
|
|
125
|
+
return None
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from anyio import to_thread
|
|
12
|
+
|
|
13
|
+
from .python_environment import PythonEnvironment
|
|
14
|
+
|
|
15
|
+
__all__ = ["SystemPythonEnvironment"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SystemPythonEnvironment(PythonEnvironment):
|
|
19
|
+
"""A Python environment using the system's Python installation."""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
executable: str | None = None,
|
|
24
|
+
):
|
|
25
|
+
"""Initialize a system Python environment.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
executable: Optional path to a specific Python executable. If None, uses the current Python executable.
|
|
29
|
+
"""
|
|
30
|
+
self._executable = executable or sys.executable
|
|
31
|
+
super().__init__()
|
|
32
|
+
|
|
33
|
+
def _setup_environment(self) -> None:
|
|
34
|
+
"""Set up the system Python environment."""
|
|
35
|
+
# Verify the Python executable exists
|
|
36
|
+
if not os.path.exists(self._executable):
|
|
37
|
+
raise RuntimeError(f"Python executable not found at: {self._executable}")
|
|
38
|
+
|
|
39
|
+
logging.info(f"Using system Python at: {self._executable}")
|
|
40
|
+
|
|
41
|
+
def _cleanup_environment(self) -> None:
|
|
42
|
+
"""Clean up the system Python environment."""
|
|
43
|
+
# No cleanup needed for system Python
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def get_executable(self) -> str:
|
|
47
|
+
"""Get the path to the Python executable."""
|
|
48
|
+
return self._executable
|
|
49
|
+
|
|
50
|
+
async def execute_code(self, code: str, script_path: str, timeout: int = 30) -> dict[str, Any]:
|
|
51
|
+
"""Execute code using the system Python."""
|
|
52
|
+
try:
|
|
53
|
+
# Get the Python executable
|
|
54
|
+
python_executable = self.get_executable()
|
|
55
|
+
|
|
56
|
+
# Verify the executable exists
|
|
57
|
+
if not os.path.exists(python_executable):
|
|
58
|
+
return {"success": False, "error": f"Python executable not found at {python_executable}"}
|
|
59
|
+
|
|
60
|
+
# Ensure the directory for the script exists
|
|
61
|
+
script_dir = os.path.dirname(script_path)
|
|
62
|
+
if script_dir:
|
|
63
|
+
os.makedirs(script_dir, exist_ok=True)
|
|
64
|
+
|
|
65
|
+
# Write the code to the script file using anyio.to_thread.run_sync (from base class)
|
|
66
|
+
await to_thread.run_sync(self._write_to_file, script_path, code)
|
|
67
|
+
|
|
68
|
+
logging.info(f"Wrote code to {script_path}")
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
# Execute directly with subprocess using anyio.to_thread.run_sync for better reliability
|
|
72
|
+
result = await to_thread.run_sync(self._run_subprocess, [python_executable, script_path], timeout)
|
|
73
|
+
|
|
74
|
+
# Main execution result
|
|
75
|
+
return {
|
|
76
|
+
"success": result.returncode == 0,
|
|
77
|
+
"stdout": result.stdout,
|
|
78
|
+
"stderr": result.stderr,
|
|
79
|
+
"returncode": result.returncode,
|
|
80
|
+
}
|
|
81
|
+
except subprocess.TimeoutExpired:
|
|
82
|
+
return {"success": False, "error": f"Execution timed out after {timeout} seconds"}
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
return {"success": False, "error": f"Execution error: {str(e)}"}
|