ag2 0.9.1a1__py3-none-any.whl → 0.9.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.
Potentially problematic release.
This version of ag2 might be problematic. Click here for more details.
- {ag2-0.9.1a1.dist-info → ag2-0.9.2.dist-info}/METADATA +272 -75
- ag2-0.9.2.dist-info/RECORD +406 -0
- {ag2-0.9.1a1.dist-info → ag2-0.9.2.dist-info}/WHEEL +1 -2
- autogen/__init__.py +89 -0
- autogen/_website/__init__.py +3 -0
- autogen/_website/generate_api_references.py +427 -0
- autogen/_website/generate_mkdocs.py +1174 -0
- autogen/_website/notebook_processor.py +476 -0
- autogen/_website/process_notebooks.py +656 -0
- autogen/_website/utils.py +412 -0
- autogen/agentchat/__init__.py +44 -0
- autogen/agentchat/agent.py +182 -0
- autogen/agentchat/assistant_agent.py +85 -0
- autogen/agentchat/chat.py +309 -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 +429 -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 +566 -0
- autogen/agentchat/contrib/capabilities/transforms_util.py +122 -0
- autogen/agentchat/contrib/capabilities/vision_capability.py +214 -0
- autogen/agentchat/contrib/captainagent/__init__.py +9 -0
- autogen/agentchat/contrib/captainagent/agent_builder.py +790 -0
- autogen/agentchat/contrib/captainagent/captainagent.py +512 -0
- autogen/agentchat/contrib/captainagent/tool_retriever.py +335 -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 +170 -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 +268 -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 +187 -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 +324 -0
- autogen/agentchat/contrib/rag/__init__.py +10 -0
- autogen/agentchat/contrib/rag/chromadb_query_engine.py +272 -0
- autogen/agentchat/contrib/rag/llamaindex_query_engine.py +198 -0
- autogen/agentchat/contrib/rag/mongodb_query_engine.py +329 -0
- autogen/agentchat/contrib/rag/query_engine.py +74 -0
- autogen/agentchat/contrib/retrieve_assistant_agent.py +56 -0
- autogen/agentchat/contrib/retrieve_user_proxy_agent.py +703 -0
- autogen/agentchat/contrib/society_of_mind_agent.py +199 -0
- autogen/agentchat/contrib/swarm_agent.py +1425 -0
- autogen/agentchat/contrib/text_analyzer_agent.py +79 -0
- autogen/agentchat/contrib/vectordb/__init__.py +5 -0
- autogen/agentchat/contrib/vectordb/base.py +232 -0
- autogen/agentchat/contrib/vectordb/chromadb.py +315 -0
- autogen/agentchat/contrib/vectordb/couchbase.py +407 -0
- autogen/agentchat/contrib/vectordb/mongodb.py +550 -0
- autogen/agentchat/contrib/vectordb/pgvectordb.py +928 -0
- autogen/agentchat/contrib/vectordb/qdrant.py +320 -0
- autogen/agentchat/contrib/vectordb/utils.py +126 -0
- autogen/agentchat/contrib/web_surfer.py +303 -0
- autogen/agentchat/conversable_agent.py +4023 -0
- autogen/agentchat/group/__init__.py +64 -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 +41 -0
- autogen/agentchat/group/context_variables.py +192 -0
- autogen/agentchat/group/group_tool_executor.py +202 -0
- autogen/agentchat/group/group_utils.py +591 -0
- autogen/agentchat/group/handoffs.py +244 -0
- autogen/agentchat/group/llm_condition.py +93 -0
- autogen/agentchat/group/multi_agent_chat.py +237 -0
- autogen/agentchat/group/on_condition.py +58 -0
- autogen/agentchat/group/on_context_condition.py +54 -0
- autogen/agentchat/group/patterns/__init__.py +18 -0
- autogen/agentchat/group/patterns/auto.py +159 -0
- autogen/agentchat/group/patterns/manual.py +176 -0
- autogen/agentchat/group/patterns/pattern.py +288 -0
- autogen/agentchat/group/patterns/random.py +106 -0
- autogen/agentchat/group/patterns/round_robin.py +117 -0
- autogen/agentchat/group/reply_result.py +26 -0
- autogen/agentchat/group/speaker_selection_result.py +41 -0
- autogen/agentchat/group/targets/__init__.py +4 -0
- autogen/agentchat/group/targets/group_chat_target.py +132 -0
- autogen/agentchat/group/targets/group_manager_target.py +151 -0
- autogen/agentchat/group/targets/transition_target.py +413 -0
- autogen/agentchat/group/targets/transition_utils.py +6 -0
- autogen/agentchat/groupchat.py +1694 -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 +190 -0
- autogen/agentchat/realtime/experimental/function_observer.py +85 -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 +475 -0
- autogen/agentchat/realtime/experimental/websockets.py +21 -0
- autogen/agentchat/realtime_agent/__init__.py +21 -0
- autogen/agentchat/user_proxy_agent.py +111 -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 +73 -0
- autogen/agents/contrib/time/time_tool_agent.py +51 -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 +316 -0
- autogen/agents/experimental/document_agent/docling_doc_ingest_agent.py +118 -0
- autogen/agents/experimental/document_agent/document_agent.py +461 -0
- autogen/agents/experimental/document_agent/document_conditions.py +50 -0
- autogen/agents/experimental/document_agent/document_utils.py +380 -0
- autogen/agents/experimental/document_agent/inmemory_query_engine.py +220 -0
- autogen/agents/experimental/document_agent/parser_utils.py +130 -0
- autogen/agents/experimental/document_agent/url_utils.py +426 -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 +77 -0
- autogen/agents/experimental/websurfer/__init__.py +7 -0
- autogen/agents/experimental/websurfer/websurfer.py +62 -0
- autogen/agents/experimental/wikipedia/__init__.py +7 -0
- autogen/agents/experimental/wikipedia/wikipedia.py +90 -0
- autogen/browser_utils.py +309 -0
- autogen/cache/__init__.py +10 -0
- autogen/cache/abstract_cache_base.py +75 -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 +102 -0
- autogen/cache/in_memory_cache.py +58 -0
- autogen/cache/redis_cache.py +123 -0
- autogen/code_utils.py +596 -0
- autogen/coding/__init__.py +22 -0
- autogen/coding/base.py +119 -0
- autogen/coding/docker_commandline_code_executor.py +268 -0
- autogen/coding/factory.py +47 -0
- autogen/coding/func_with_reqs.py +202 -0
- autogen/coding/jupyter/__init__.py +23 -0
- autogen/coding/jupyter/base.py +36 -0
- autogen/coding/jupyter/docker_jupyter_server.py +167 -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 +231 -0
- autogen/coding/jupyter/jupyter_code_executor.py +160 -0
- autogen/coding/jupyter/local_jupyter_server.py +172 -0
- autogen/coding/local_commandline_code_executor.py +405 -0
- autogen/coding/markdown_code_extractor.py +45 -0
- autogen/coding/utils.py +56 -0
- autogen/doc_utils.py +34 -0
- autogen/events/__init__.py +7 -0
- autogen/events/agent_events.py +1013 -0
- autogen/events/base_event.py +99 -0
- autogen/events/client_events.py +167 -0
- autogen/events/helpers.py +36 -0
- autogen/events/print_event.py +46 -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 +80 -0
- autogen/fast_depends/core/__init__.py +14 -0
- autogen/fast_depends/core/build.py +225 -0
- autogen/fast_depends/core/model.py +576 -0
- autogen/fast_depends/dependencies/__init__.py +15 -0
- autogen/fast_depends/dependencies/model.py +29 -0
- autogen/fast_depends/dependencies/provider.py +39 -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 +280 -0
- autogen/fast_depends/utils.py +187 -0
- autogen/formatting_utils.py +83 -0
- autogen/function_utils.py +13 -0
- autogen/graph_utils.py +178 -0
- autogen/import_utils.py +526 -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 +155 -0
- autogen/interop/langchain/langchain_tool.py +82 -0
- autogen/interop/litellm/__init__.py +7 -0
- autogen/interop/litellm/litellm_config_factory.py +179 -0
- autogen/interop/pydantic_ai/__init__.py +7 -0
- autogen/interop/pydantic_ai/pydantic_ai.py +168 -0
- autogen/interop/registry.py +69 -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 +56 -0
- autogen/io/run_response.py +293 -0
- autogen/io/thread_io_stream.py +63 -0
- autogen/io/websockets.py +213 -0
- autogen/json_utils.py +43 -0
- autogen/llm_config.py +382 -0
- autogen/logger/__init__.py +11 -0
- autogen/logger/base_logger.py +128 -0
- autogen/logger/file_logger.py +261 -0
- autogen/logger/logger_factory.py +42 -0
- autogen/logger/logger_utils.py +57 -0
- autogen/logger/sqlite_logger.py +523 -0
- autogen/math_utils.py +339 -0
- autogen/mcp/__init__.py +7 -0
- autogen/mcp/__main__.py +78 -0
- autogen/mcp/mcp_client.py +208 -0
- autogen/mcp/mcp_proxy/__init__.py +19 -0
- autogen/mcp/mcp_proxy/fastapi_code_generator_helpers.py +63 -0
- autogen/mcp/mcp_proxy/mcp_proxy.py +581 -0
- autogen/mcp/mcp_proxy/operation_grouping.py +158 -0
- autogen/mcp/mcp_proxy/operation_renaming.py +114 -0
- autogen/mcp/mcp_proxy/patch_fastapi_code_generator.py +98 -0
- autogen/mcp/mcp_proxy/security.py +400 -0
- autogen/mcp/mcp_proxy/security_schema_visitor.py +37 -0
- autogen/messages/__init__.py +7 -0
- autogen/messages/agent_messages.py +948 -0
- autogen/messages/base_message.py +107 -0
- autogen/messages/client_messages.py +171 -0
- autogen/messages/print_message.py +49 -0
- autogen/oai/__init__.py +53 -0
- autogen/oai/anthropic.py +714 -0
- autogen/oai/bedrock.py +628 -0
- autogen/oai/cerebras.py +299 -0
- autogen/oai/client.py +1444 -0
- autogen/oai/client_utils.py +169 -0
- autogen/oai/cohere.py +479 -0
- autogen/oai/gemini.py +998 -0
- autogen/oai/gemini_types.py +155 -0
- autogen/oai/groq.py +305 -0
- autogen/oai/mistral.py +303 -0
- autogen/oai/oai_models/__init__.py +11 -0
- autogen/oai/oai_models/_models.py +16 -0
- autogen/oai/oai_models/chat_completion.py +87 -0
- autogen/oai/oai_models/chat_completion_audio.py +32 -0
- autogen/oai/oai_models/chat_completion_message.py +86 -0
- autogen/oai/oai_models/chat_completion_message_tool_call.py +37 -0
- autogen/oai/oai_models/chat_completion_token_logprob.py +63 -0
- autogen/oai/oai_models/completion_usage.py +60 -0
- autogen/oai/ollama.py +643 -0
- autogen/oai/openai_utils.py +881 -0
- autogen/oai/together.py +370 -0
- autogen/retrieve_utils.py +491 -0
- autogen/runtime_logging.py +160 -0
- autogen/token_count_utils.py +267 -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 +41 -0
- autogen/tools/dependency_injection.py +254 -0
- autogen/tools/experimental/__init__.py +48 -0
- autogen/tools/experimental/browser_use/__init__.py +7 -0
- autogen/tools/experimental/browser_use/browser_use.py +161 -0
- autogen/tools/experimental/crawl4ai/__init__.py +7 -0
- autogen/tools/experimental/crawl4ai/crawl4ai.py +153 -0
- autogen/tools/experimental/deep_research/__init__.py +7 -0
- autogen/tools/experimental/deep_research/deep_research.py +328 -0
- autogen/tools/experimental/duckduckgo/__init__.py +7 -0
- autogen/tools/experimental/duckduckgo/duckduckgo_search.py +109 -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 +288 -0
- autogen/tools/experimental/messageplatform/slack/__init__.py +7 -0
- autogen/tools/experimental/messageplatform/slack/slack.py +391 -0
- autogen/tools/experimental/messageplatform/telegram/__init__.py +7 -0
- autogen/tools/experimental/messageplatform/telegram/telegram.py +275 -0
- autogen/tools/experimental/perplexity/__init__.py +7 -0
- autogen/tools/experimental/perplexity/perplexity_search.py +260 -0
- autogen/tools/experimental/reliable/__init__.py +10 -0
- autogen/tools/experimental/reliable/reliable.py +1316 -0
- autogen/tools/experimental/tavily/__init__.py +7 -0
- autogen/tools/experimental/tavily/tavily_search.py +183 -0
- autogen/tools/experimental/web_search_preview/__init__.py +7 -0
- autogen/tools/experimental/web_search_preview/web_search_preview.py +114 -0
- autogen/tools/experimental/wikipedia/__init__.py +7 -0
- autogen/tools/experimental/wikipedia/wikipedia.py +287 -0
- autogen/tools/function_utils.py +411 -0
- autogen/tools/tool.py +187 -0
- autogen/tools/toolkit.py +86 -0
- autogen/types.py +29 -0
- autogen/version.py +7 -0
- templates/client_template/main.jinja2 +69 -0
- templates/config_template/config.jinja2 +7 -0
- templates/main.jinja2 +61 -0
- ag2-0.9.1a1.dist-info/RECORD +0 -6
- ag2-0.9.1a1.dist-info/top_level.txt +0 -1
- {ag2-0.9.1a1.dist-info → ag2-0.9.2.dist-info/licenses}/LICENSE +0 -0
- {ag2-0.9.1a1.dist-info → ag2-0.9.2.dist-info/licenses}/NOTICE.md +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
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
|
+
# Portions derived from https://github.com/microsoft/autogen are under the MIT License.
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import atexit
|
|
10
|
+
import io
|
|
11
|
+
import logging
|
|
12
|
+
import secrets
|
|
13
|
+
import sys
|
|
14
|
+
import uuid
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from types import TracebackType
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
import docker
|
|
20
|
+
|
|
21
|
+
from ...doc_utils import export_module
|
|
22
|
+
|
|
23
|
+
if sys.version_info >= (3, 11):
|
|
24
|
+
from typing import Self
|
|
25
|
+
else:
|
|
26
|
+
from typing_extensions import Self
|
|
27
|
+
|
|
28
|
+
from ..docker_commandline_code_executor import _wait_for_ready
|
|
29
|
+
from .base import JupyterConnectable, JupyterConnectionInfo
|
|
30
|
+
from .import_utils import require_jupyter_kernel_gateway_installed
|
|
31
|
+
from .jupyter_client import JupyterClient
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@require_jupyter_kernel_gateway_installed()
|
|
35
|
+
@export_module("autogen.coding.jupyter")
|
|
36
|
+
class DockerJupyterServer(JupyterConnectable):
|
|
37
|
+
DEFAULT_DOCKERFILE = """FROM quay.io/jupyter/docker-stacks-foundation
|
|
38
|
+
|
|
39
|
+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
|
40
|
+
|
|
41
|
+
USER ${NB_UID}
|
|
42
|
+
RUN mamba install --yes jupyter_kernel_gateway ipykernel && \
|
|
43
|
+
mamba clean --all -f -y && \
|
|
44
|
+
fix-permissions "${CONDA_DIR}" && \
|
|
45
|
+
fix-permissions "/home/${NB_USER}"
|
|
46
|
+
|
|
47
|
+
ENV TOKEN="UNSET"
|
|
48
|
+
CMD python -m jupyter kernelgateway --KernelGatewayApp.ip=0.0.0.0 \
|
|
49
|
+
--KernelGatewayApp.port=8888 \
|
|
50
|
+
--KernelGatewayApp.auth_token="${TOKEN}" \
|
|
51
|
+
--JupyterApp.answer_yes=true \
|
|
52
|
+
--JupyterWebsocketPersonality.list_kernels=true
|
|
53
|
+
|
|
54
|
+
EXPOSE 8888
|
|
55
|
+
|
|
56
|
+
WORKDIR "${HOME}"
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
class GenerateToken:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
*,
|
|
65
|
+
custom_image_name: Optional[str] = None,
|
|
66
|
+
container_name: Optional[str] = None,
|
|
67
|
+
auto_remove: bool = True,
|
|
68
|
+
stop_container: bool = True,
|
|
69
|
+
docker_env: dict[str, str] = {},
|
|
70
|
+
token: str | GenerateToken = GenerateToken(),
|
|
71
|
+
):
|
|
72
|
+
"""Start a Jupyter kernel gateway server in a Docker container.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
custom_image_name (Optional[str], optional): Custom image to use. If this is None,
|
|
76
|
+
then the bundled image will be built and used. The default image is based on
|
|
77
|
+
quay.io/jupyter/docker-stacks-foundation and extended to include jupyter_kernel_gateway
|
|
78
|
+
container_name (Optional[str], optional): Name of the container to start.
|
|
79
|
+
A name will be generated if None.
|
|
80
|
+
auto_remove (bool, optional): If true the Docker container will be deleted
|
|
81
|
+
when it is stopped.
|
|
82
|
+
stop_container (bool, optional): If true the container will be stopped,
|
|
83
|
+
either by program exit or using the context manager
|
|
84
|
+
docker_env (Dict[str, str], optional): Extra environment variables to pass
|
|
85
|
+
to the running Docker container.
|
|
86
|
+
token (Union[str, GenerateToken], optional): Token to use for authentication.
|
|
87
|
+
If GenerateToken is used, a random token will be generated. Empty string
|
|
88
|
+
will be unauthenticated.
|
|
89
|
+
"""
|
|
90
|
+
if container_name is None:
|
|
91
|
+
container_name = f"autogen-jupyterkernelgateway-{uuid.uuid4()}"
|
|
92
|
+
|
|
93
|
+
client = docker.from_env()
|
|
94
|
+
if custom_image_name is None:
|
|
95
|
+
image_name = "autogen-jupyterkernelgateway"
|
|
96
|
+
# Make sure the image exists
|
|
97
|
+
try:
|
|
98
|
+
client.images.get(image_name)
|
|
99
|
+
except docker.errors.ImageNotFound:
|
|
100
|
+
# Build the image
|
|
101
|
+
# Get this script directory
|
|
102
|
+
here = Path(__file__).parent
|
|
103
|
+
dockerfile = io.BytesIO(self.DEFAULT_DOCKERFILE.encode("utf-8"))
|
|
104
|
+
logging.info(f"Image {image_name} not found. Building it now.")
|
|
105
|
+
client.images.build(path=here, fileobj=dockerfile, tag=image_name)
|
|
106
|
+
logging.info(f"Image {image_name} built successfully.")
|
|
107
|
+
else:
|
|
108
|
+
image_name = custom_image_name
|
|
109
|
+
# Check if the image exists
|
|
110
|
+
try:
|
|
111
|
+
client.images.get(image_name)
|
|
112
|
+
except docker.errors.ImageNotFound:
|
|
113
|
+
raise ValueError(f"Custom image {image_name} does not exist")
|
|
114
|
+
|
|
115
|
+
if isinstance(token, DockerJupyterServer.GenerateToken):
|
|
116
|
+
self._token = secrets.token_hex(32)
|
|
117
|
+
else:
|
|
118
|
+
self._token = token
|
|
119
|
+
|
|
120
|
+
# Run the container
|
|
121
|
+
env = {"TOKEN": self._token}
|
|
122
|
+
env.update(docker_env)
|
|
123
|
+
container = client.containers.run(
|
|
124
|
+
image_name,
|
|
125
|
+
detach=True,
|
|
126
|
+
auto_remove=auto_remove,
|
|
127
|
+
environment=env,
|
|
128
|
+
publish_all_ports=True,
|
|
129
|
+
name=container_name,
|
|
130
|
+
)
|
|
131
|
+
_wait_for_ready(container)
|
|
132
|
+
container_ports = container.ports
|
|
133
|
+
self._port = int(container_ports["8888/tcp"][0]["HostPort"])
|
|
134
|
+
self._container_id = container.id
|
|
135
|
+
|
|
136
|
+
def cleanup() -> None:
|
|
137
|
+
try:
|
|
138
|
+
inner_container = client.containers.get(container.id)
|
|
139
|
+
inner_container.stop()
|
|
140
|
+
except docker.errors.NotFound:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
atexit.unregister(cleanup)
|
|
144
|
+
|
|
145
|
+
if stop_container:
|
|
146
|
+
atexit.register(cleanup)
|
|
147
|
+
|
|
148
|
+
self._cleanup_func = cleanup
|
|
149
|
+
self._stop_container = stop_container
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def connection_info(self) -> JupyterConnectionInfo:
|
|
153
|
+
return JupyterConnectionInfo(host="127.0.0.1", use_https=False, port=self._port, token=self._token)
|
|
154
|
+
|
|
155
|
+
def stop(self) -> None:
|
|
156
|
+
self._cleanup_func()
|
|
157
|
+
|
|
158
|
+
def get_client(self) -> JupyterClient:
|
|
159
|
+
return JupyterClient(self.connection_info)
|
|
160
|
+
|
|
161
|
+
def __enter__(self) -> Self:
|
|
162
|
+
return self
|
|
163
|
+
|
|
164
|
+
def __exit__(
|
|
165
|
+
self, exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]
|
|
166
|
+
) -> None:
|
|
167
|
+
self.stop()
|
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
# Portions derived from https://github.com/microsoft/autogen are under the MIT License.
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
import base64
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import uuid
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from queue import Empty
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel, Field, field_validator
|
|
17
|
+
|
|
18
|
+
from ...doc_utils import export_module
|
|
19
|
+
from ...import_utils import optional_import_block, require_optional_import
|
|
20
|
+
from ..base import CodeBlock, CodeExtractor, IPythonCodeResult
|
|
21
|
+
from ..markdown_code_extractor import MarkdownCodeExtractor
|
|
22
|
+
from .import_utils import require_jupyter_kernel_gateway_installed
|
|
23
|
+
|
|
24
|
+
with optional_import_block():
|
|
25
|
+
from jupyter_client import KernelManager # type: ignore[attr-defined]
|
|
26
|
+
from jupyter_client.kernelspec import KernelSpecManager
|
|
27
|
+
|
|
28
|
+
__all__ = ["EmbeddedIPythonCodeExecutor"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@require_optional_import("jupyter_client", "jupyter-executor")
|
|
32
|
+
@require_jupyter_kernel_gateway_installed()
|
|
33
|
+
@export_module("autogen.coding.jupyter")
|
|
34
|
+
class EmbeddedIPythonCodeExecutor(BaseModel):
|
|
35
|
+
"""(Experimental) A code executor class that executes code statefully using an embedded
|
|
36
|
+
IPython kernel managed by this class.
|
|
37
|
+
|
|
38
|
+
**This will execute LLM generated code on the local machine.**
|
|
39
|
+
|
|
40
|
+
Each execution is stateful and can access variables created from previous
|
|
41
|
+
executions in the same session. The kernel must be installed before using
|
|
42
|
+
this class. The kernel can be installed using the following command:
|
|
43
|
+
`python -m ipykernel install --user --name {kernel_name}`
|
|
44
|
+
where `kernel_name` is the name of the kernel to install.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
timeout: int = Field(default=60, ge=1, description="The timeout for code execution.")
|
|
48
|
+
kernel_name: str = Field(default="python3", description="The kernel name to use. Make sure it is installed.")
|
|
49
|
+
output_dir: str = Field(default=".", description="The directory to save output files.")
|
|
50
|
+
|
|
51
|
+
@field_validator("output_dir")
|
|
52
|
+
@classmethod
|
|
53
|
+
def _output_dir_must_exist(cls, value: str) -> str:
|
|
54
|
+
if not os.path.exists(value):
|
|
55
|
+
raise ValueError(f"Output directory {value} does not exist.")
|
|
56
|
+
return value
|
|
57
|
+
|
|
58
|
+
def __init__(self, **kwargs: Any):
|
|
59
|
+
super().__init__(**kwargs)
|
|
60
|
+
# Check if the kernel is installed.
|
|
61
|
+
if self.kernel_name not in KernelSpecManager().find_kernel_specs():
|
|
62
|
+
raise ValueError(
|
|
63
|
+
f"Kernel {self.kernel_name} is not installed. "
|
|
64
|
+
"Please first install it with "
|
|
65
|
+
f"`python -m ipykernel install --user --name {self.kernel_name}`."
|
|
66
|
+
)
|
|
67
|
+
self._kernel_manager = KernelManager(kernel_name=self.kernel_name)
|
|
68
|
+
self._kernel_manager.start_kernel()
|
|
69
|
+
self._kernel_client = self._kernel_manager.client()
|
|
70
|
+
self._kernel_client.start_channels()
|
|
71
|
+
self._timeout = self.timeout
|
|
72
|
+
self._kernel_name = self.kernel_name
|
|
73
|
+
self._output_dir = Path(self.output_dir)
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def code_extractor(self) -> CodeExtractor:
|
|
77
|
+
"""(Experimental) Export a code extractor that can be used by an agent."""
|
|
78
|
+
return MarkdownCodeExtractor()
|
|
79
|
+
|
|
80
|
+
def execute_code_blocks(self, code_blocks: list[CodeBlock]) -> IPythonCodeResult:
|
|
81
|
+
"""(Experimental) Execute a list of code blocks and return the result.
|
|
82
|
+
|
|
83
|
+
This method executes a list of code blocks as cells in an IPython kernel
|
|
84
|
+
managed by this class.
|
|
85
|
+
See: https://jupyter-client.readthedocs.io/en/stable/messaging.html
|
|
86
|
+
for the message protocol.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
code_blocks (List[CodeBlock]): A list of code blocks to execute.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
IPythonCodeResult: The result of the code execution.
|
|
93
|
+
"""
|
|
94
|
+
self._kernel_client.wait_for_ready()
|
|
95
|
+
outputs = []
|
|
96
|
+
output_files = []
|
|
97
|
+
for code_block in code_blocks:
|
|
98
|
+
code = self._process_code(code_block.code)
|
|
99
|
+
self._kernel_client.execute(code, store_history=True)
|
|
100
|
+
while True:
|
|
101
|
+
try:
|
|
102
|
+
msg = self._kernel_client.get_iopub_msg(timeout=self._timeout)
|
|
103
|
+
msg_type = msg["msg_type"]
|
|
104
|
+
content = msg["content"]
|
|
105
|
+
if msg_type in ["execute_result", "display_data"]:
|
|
106
|
+
for data_type, data in content["data"].items():
|
|
107
|
+
if data_type == "text/plain":
|
|
108
|
+
# Output is a text.
|
|
109
|
+
outputs.append(data)
|
|
110
|
+
elif data_type.startswith("image/"):
|
|
111
|
+
# Output is an image.
|
|
112
|
+
path = self._save_image(data)
|
|
113
|
+
outputs.append(f"Image data saved to {path}")
|
|
114
|
+
output_files.append(path)
|
|
115
|
+
elif data_type == "text/html":
|
|
116
|
+
# Output is an html.
|
|
117
|
+
path = self._save_html(data)
|
|
118
|
+
outputs.append(f"HTML data saved to {path}")
|
|
119
|
+
output_files.append(path)
|
|
120
|
+
else:
|
|
121
|
+
# Output raw data.
|
|
122
|
+
outputs.append(json.dumps(data))
|
|
123
|
+
elif msg_type == "stream":
|
|
124
|
+
# Output is a text.
|
|
125
|
+
outputs.append(content["text"])
|
|
126
|
+
elif msg_type == "error":
|
|
127
|
+
# Output is an error.
|
|
128
|
+
return IPythonCodeResult(
|
|
129
|
+
exit_code=1,
|
|
130
|
+
output=f"ERROR: {content['ename']}: {content['evalue']}\n{content['traceback']}",
|
|
131
|
+
)
|
|
132
|
+
if msg_type == "status" and content["execution_state"] == "idle":
|
|
133
|
+
break
|
|
134
|
+
# handle time outs.
|
|
135
|
+
except Empty:
|
|
136
|
+
return IPythonCodeResult(
|
|
137
|
+
exit_code=1,
|
|
138
|
+
output=f"ERROR: Timeout waiting for output from code block: {code_block.code}",
|
|
139
|
+
)
|
|
140
|
+
# We return the full output.
|
|
141
|
+
return IPythonCodeResult(
|
|
142
|
+
exit_code=0, output="\n".join([str(output) for output in outputs]), output_files=output_files
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def restart(self) -> None:
|
|
146
|
+
"""(Experimental) Restart a new session."""
|
|
147
|
+
self._kernel_client.stop_channels()
|
|
148
|
+
self._kernel_manager.shutdown_kernel()
|
|
149
|
+
self._kernel_manager = KernelManager(kernel_name=self.kernel_name)
|
|
150
|
+
self._kernel_manager.start_kernel()
|
|
151
|
+
self._kernel_client = self._kernel_manager.client()
|
|
152
|
+
self._kernel_client.start_channels()
|
|
153
|
+
|
|
154
|
+
def _save_image(self, image_data_base64: str) -> str:
|
|
155
|
+
"""Save image data to a file."""
|
|
156
|
+
image_data = base64.b64decode(image_data_base64)
|
|
157
|
+
# Randomly generate a filename.
|
|
158
|
+
filename = f"{uuid.uuid4().hex}.png"
|
|
159
|
+
path = os.path.join(self.output_dir, filename)
|
|
160
|
+
with open(path, "wb") as f:
|
|
161
|
+
f.write(image_data)
|
|
162
|
+
return os.path.abspath(path)
|
|
163
|
+
|
|
164
|
+
def _save_html(self, html_data: str) -> str:
|
|
165
|
+
"""Save html data to a file."""
|
|
166
|
+
# Randomly generate a filename.
|
|
167
|
+
filename = f"{uuid.uuid4().hex}.html"
|
|
168
|
+
path = os.path.join(self.output_dir, filename)
|
|
169
|
+
with open(path, "w") as f:
|
|
170
|
+
f.write(html_data)
|
|
171
|
+
return os.path.abspath(path)
|
|
172
|
+
|
|
173
|
+
def _process_code(self, code: str) -> str:
|
|
174
|
+
"""Process code before execution."""
|
|
175
|
+
# Find lines that start with `! pip install` and make sure "-qqq" flag is added.
|
|
176
|
+
lines = code.split("\n")
|
|
177
|
+
for i, line in enumerate(lines):
|
|
178
|
+
# use regex to find lines that start with `! pip install` or `!pip install`.
|
|
179
|
+
match = re.search(r"^! ?pip install", line)
|
|
180
|
+
if match is not None and "-qqq" not in line:
|
|
181
|
+
lines[i] = line.replace(match.group(0), match.group(0) + " -qqq")
|
|
182
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,82 @@
|
|
|
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 functools import lru_cache
|
|
7
|
+
from logging import getLogger
|
|
8
|
+
from typing import Callable, TypeVar
|
|
9
|
+
|
|
10
|
+
from ...import_utils import patch_object
|
|
11
|
+
|
|
12
|
+
logger = getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
__all__ = ["require_jupyter_kernel_gateway_installed", "skip_on_missing_jupyter_kernel_gateway"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@lru_cache
|
|
18
|
+
def is_jupyter_kernel_gateway_installed() -> bool:
|
|
19
|
+
"""Check if jupyter-kernel-gateway is installed."""
|
|
20
|
+
try:
|
|
21
|
+
subprocess.run(
|
|
22
|
+
["jupyter", "kernelgateway", "--version"],
|
|
23
|
+
stdout=subprocess.PIPE,
|
|
24
|
+
stderr=subprocess.PIPE,
|
|
25
|
+
check=True,
|
|
26
|
+
)
|
|
27
|
+
return True
|
|
28
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
29
|
+
logger.warning(
|
|
30
|
+
"jupyter-kernel-gateway is required for JupyterCodeExecutor, please install it with `pip install ag2[jupyter-executor]`"
|
|
31
|
+
)
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
T = TypeVar("T")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def require_jupyter_kernel_gateway_installed() -> Callable[[T], T]:
|
|
39
|
+
"""Decorator that checks if jupyter-kernel-gateway is installed before function execution.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Callable[[T], T]: A decorator function that either:
|
|
43
|
+
- Returns the original function unchanged if jupyter-kernel-gateway is installed
|
|
44
|
+
- Returns a patched version of the function that will raise a helpful error indicating the missing dependency when called
|
|
45
|
+
"""
|
|
46
|
+
if is_jupyter_kernel_gateway_installed():
|
|
47
|
+
|
|
48
|
+
def decorator(o: T) -> T:
|
|
49
|
+
return o
|
|
50
|
+
|
|
51
|
+
else:
|
|
52
|
+
|
|
53
|
+
def decorator(o: T) -> T:
|
|
54
|
+
return patch_object(o, missing_modules={}, dep_target="jupyter-executor")
|
|
55
|
+
|
|
56
|
+
return decorator
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def skip_on_missing_jupyter_kernel_gateway() -> Callable[[T], T]:
|
|
60
|
+
"""Decorator to skip a test if an optional module is missing"""
|
|
61
|
+
# Add pytest.mark.jupyter_executor decorator
|
|
62
|
+
mark_name = "jupyter_executor"
|
|
63
|
+
|
|
64
|
+
if is_jupyter_kernel_gateway_installed():
|
|
65
|
+
|
|
66
|
+
def decorator(o: T) -> T:
|
|
67
|
+
import pytest
|
|
68
|
+
|
|
69
|
+
pytest_mark_o = getattr(pytest.mark, mark_name)(o)
|
|
70
|
+
return pytest_mark_o # type: ignore[no-any-return]
|
|
71
|
+
|
|
72
|
+
else:
|
|
73
|
+
|
|
74
|
+
def decorator(o: T) -> T:
|
|
75
|
+
import pytest
|
|
76
|
+
|
|
77
|
+
pytest_mark_o = getattr(pytest.mark, mark_name)(o)
|
|
78
|
+
return pytest.mark.skip( # type: ignore[return-value,no-any-return]
|
|
79
|
+
reason="jupyter-kernel-gateway is required for JupyterCodeExecutor, please install it with `pip install ag2[jupyter-executor]`"
|
|
80
|
+
)(pytest_mark_o)
|
|
81
|
+
|
|
82
|
+
return decorator
|
|
@@ -0,0 +1,231 @@
|
|
|
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
|
+
# Portions derived from https://github.com/microsoft/autogen are under the MIT License.
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import datetime
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
import uuid
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from types import TracebackType
|
|
15
|
+
from typing import Any, Optional, cast
|
|
16
|
+
|
|
17
|
+
from ...doc_utils import export_module
|
|
18
|
+
|
|
19
|
+
if sys.version_info >= (3, 11):
|
|
20
|
+
from typing import Self
|
|
21
|
+
else:
|
|
22
|
+
from typing_extensions import Self
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
import requests
|
|
26
|
+
from requests.adapters import HTTPAdapter, Retry
|
|
27
|
+
|
|
28
|
+
from ...import_utils import optional_import_block, require_optional_import
|
|
29
|
+
from .base import JupyterConnectionInfo
|
|
30
|
+
|
|
31
|
+
with optional_import_block():
|
|
32
|
+
import websocket
|
|
33
|
+
from websocket import WebSocket
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@export_module("autogen.coding.jupyter")
|
|
37
|
+
class JupyterClient:
|
|
38
|
+
def __init__(self, connection_info: JupyterConnectionInfo):
|
|
39
|
+
"""(Experimental) A client for communicating with a Jupyter gateway server.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
connection_info (JupyterConnectionInfo): Connection information
|
|
43
|
+
"""
|
|
44
|
+
self._connection_info = connection_info
|
|
45
|
+
self._session = requests.Session()
|
|
46
|
+
retries = Retry(total=5, backoff_factor=0.1)
|
|
47
|
+
self._session.mount("http://", HTTPAdapter(max_retries=retries))
|
|
48
|
+
|
|
49
|
+
def _get_headers(self) -> dict[str, str]:
|
|
50
|
+
if self._connection_info.token is None:
|
|
51
|
+
return {}
|
|
52
|
+
return {"Authorization": f"token {self._connection_info.token}"}
|
|
53
|
+
|
|
54
|
+
def _get_api_base_url(self) -> str:
|
|
55
|
+
protocol = "https" if self._connection_info.use_https else "http"
|
|
56
|
+
port = f":{self._connection_info.port}" if self._connection_info.port else ""
|
|
57
|
+
return f"{protocol}://{self._connection_info.host}{port}"
|
|
58
|
+
|
|
59
|
+
def _get_ws_base_url(self) -> str:
|
|
60
|
+
port = f":{self._connection_info.port}" if self._connection_info.port else ""
|
|
61
|
+
return f"ws://{self._connection_info.host}{port}"
|
|
62
|
+
|
|
63
|
+
def list_kernel_specs(self) -> dict[str, dict[str, str]]:
|
|
64
|
+
response = self._session.get(f"{self._get_api_base_url()}/api/kernelspecs", headers=self._get_headers())
|
|
65
|
+
return cast(dict[str, dict[str, str]], response.json())
|
|
66
|
+
|
|
67
|
+
def list_kernels(self) -> list[dict[str, str]]:
|
|
68
|
+
response = self._session.get(f"{self._get_api_base_url()}/api/kernels", headers=self._get_headers())
|
|
69
|
+
return cast(list[dict[str, str]], response.json())
|
|
70
|
+
|
|
71
|
+
def start_kernel(self, kernel_spec_name: str) -> str:
|
|
72
|
+
"""Start a new kernel.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
kernel_spec_name (str): Name of the kernel spec to start
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
str: ID of the started kernel
|
|
79
|
+
"""
|
|
80
|
+
response = self._session.post(
|
|
81
|
+
f"{self._get_api_base_url()}/api/kernels",
|
|
82
|
+
headers=self._get_headers(),
|
|
83
|
+
json={"name": kernel_spec_name},
|
|
84
|
+
)
|
|
85
|
+
return cast(str, response.json()["id"])
|
|
86
|
+
|
|
87
|
+
def delete_kernel(self, kernel_id: str) -> None:
|
|
88
|
+
response = self._session.delete(
|
|
89
|
+
f"{self._get_api_base_url()}/api/kernels/{kernel_id}", headers=self._get_headers()
|
|
90
|
+
)
|
|
91
|
+
response.raise_for_status()
|
|
92
|
+
|
|
93
|
+
def restart_kernel(self, kernel_id: str) -> None:
|
|
94
|
+
response = self._session.post(
|
|
95
|
+
f"{self._get_api_base_url()}/api/kernels/{kernel_id}/restart", headers=self._get_headers()
|
|
96
|
+
)
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
|
|
99
|
+
@require_optional_import("websocket", "jupyter-executor")
|
|
100
|
+
def get_kernel_client(self, kernel_id: str) -> JupyterKernelClient:
|
|
101
|
+
ws_url = f"{self._get_ws_base_url()}/api/kernels/{kernel_id}/channels"
|
|
102
|
+
ws = websocket.create_connection(ws_url, header=self._get_headers())
|
|
103
|
+
return JupyterKernelClient(ws)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@require_optional_import("websocket", "jupyter-executor")
|
|
107
|
+
class JupyterKernelClient:
|
|
108
|
+
"""(Experimental) A client for communicating with a Jupyter kernel."""
|
|
109
|
+
|
|
110
|
+
@dataclass
|
|
111
|
+
class ExecutionResult:
|
|
112
|
+
@dataclass
|
|
113
|
+
class DataItem:
|
|
114
|
+
mime_type: str
|
|
115
|
+
data: str
|
|
116
|
+
|
|
117
|
+
is_ok: bool
|
|
118
|
+
output: str
|
|
119
|
+
data_items: list[DataItem]
|
|
120
|
+
|
|
121
|
+
def __init__(self, websocket: WebSocket): # type: ignore[no-any-unimported]
|
|
122
|
+
self._session_id: str = uuid.uuid4().hex
|
|
123
|
+
self._websocket: WebSocket = websocket # type: ignore[no-any-unimported]
|
|
124
|
+
|
|
125
|
+
def __enter__(self) -> Self:
|
|
126
|
+
return self
|
|
127
|
+
|
|
128
|
+
def __exit__(
|
|
129
|
+
self, exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]
|
|
130
|
+
) -> None:
|
|
131
|
+
self.stop()
|
|
132
|
+
|
|
133
|
+
def stop(self) -> None:
|
|
134
|
+
self._websocket.close()
|
|
135
|
+
|
|
136
|
+
def _send_message(self, *, content: dict[str, Any], channel: str, message_type: str) -> str:
|
|
137
|
+
timestamp = datetime.datetime.now().isoformat()
|
|
138
|
+
message_id = uuid.uuid4().hex
|
|
139
|
+
message = {
|
|
140
|
+
"header": {
|
|
141
|
+
"username": "autogen",
|
|
142
|
+
"version": "5.0",
|
|
143
|
+
"session": self._session_id,
|
|
144
|
+
"msg_id": message_id,
|
|
145
|
+
"msg_type": message_type,
|
|
146
|
+
"date": timestamp,
|
|
147
|
+
},
|
|
148
|
+
"parent_header": {},
|
|
149
|
+
"channel": channel,
|
|
150
|
+
"content": content,
|
|
151
|
+
"metadata": {},
|
|
152
|
+
"buffers": {},
|
|
153
|
+
}
|
|
154
|
+
self._websocket.send_text(json.dumps(message))
|
|
155
|
+
return message_id
|
|
156
|
+
|
|
157
|
+
def _receive_message(self, timeout_seconds: Optional[float]) -> Optional[dict[str, Any]]:
|
|
158
|
+
self._websocket.settimeout(timeout_seconds)
|
|
159
|
+
try:
|
|
160
|
+
data = self._websocket.recv()
|
|
161
|
+
if isinstance(data, bytes):
|
|
162
|
+
data = data.decode("utf-8")
|
|
163
|
+
return cast(dict[str, Any], json.loads(data))
|
|
164
|
+
except websocket.WebSocketTimeoutException:
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
def wait_for_ready(self, timeout_seconds: Optional[float] = None) -> bool:
|
|
168
|
+
message_id = self._send_message(content={}, channel="shell", message_type="kernel_info_request")
|
|
169
|
+
while True:
|
|
170
|
+
message = self._receive_message(timeout_seconds)
|
|
171
|
+
# This means we timed out with no new messages.
|
|
172
|
+
if message is None:
|
|
173
|
+
return False
|
|
174
|
+
if (
|
|
175
|
+
message.get("parent_header", {}).get("msg_id") == message_id
|
|
176
|
+
and message["msg_type"] == "kernel_info_reply"
|
|
177
|
+
):
|
|
178
|
+
return True
|
|
179
|
+
|
|
180
|
+
def execute(self, code: str, timeout_seconds: Optional[float] = None) -> ExecutionResult:
|
|
181
|
+
message_id = self._send_message(
|
|
182
|
+
content={
|
|
183
|
+
"code": code,
|
|
184
|
+
"silent": False,
|
|
185
|
+
"store_history": True,
|
|
186
|
+
"user_expressions": {},
|
|
187
|
+
"allow_stdin": False,
|
|
188
|
+
"stop_on_error": True,
|
|
189
|
+
},
|
|
190
|
+
channel="shell",
|
|
191
|
+
message_type="execute_request",
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
text_output = []
|
|
195
|
+
data_output = []
|
|
196
|
+
while True:
|
|
197
|
+
message = self._receive_message(timeout_seconds)
|
|
198
|
+
if message is None:
|
|
199
|
+
return JupyterKernelClient.ExecutionResult(
|
|
200
|
+
is_ok=False, output="ERROR: Timeout waiting for output from code block.", data_items=[]
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Ignore messages that are not for this execution.
|
|
204
|
+
if message.get("parent_header", {}).get("msg_id") != message_id:
|
|
205
|
+
continue
|
|
206
|
+
|
|
207
|
+
msg_type = message["msg_type"]
|
|
208
|
+
content = message["content"]
|
|
209
|
+
if msg_type in ["execute_result", "display_data"]:
|
|
210
|
+
for data_type, data in content["data"].items():
|
|
211
|
+
if data_type == "text/plain":
|
|
212
|
+
text_output.append(data)
|
|
213
|
+
elif data_type.startswith("image/") or data_type == "text/html":
|
|
214
|
+
data_output.append(self.ExecutionResult.DataItem(mime_type=data_type, data=data))
|
|
215
|
+
else:
|
|
216
|
+
text_output.append(json.dumps(data))
|
|
217
|
+
elif msg_type == "stream":
|
|
218
|
+
text_output.append(content["text"])
|
|
219
|
+
elif msg_type == "error":
|
|
220
|
+
# Output is an error.
|
|
221
|
+
return JupyterKernelClient.ExecutionResult(
|
|
222
|
+
is_ok=False,
|
|
223
|
+
output=f"ERROR: {content['ename']}: {content['evalue']}\n{content['traceback']}",
|
|
224
|
+
data_items=[],
|
|
225
|
+
)
|
|
226
|
+
if msg_type == "status" and content["execution_state"] == "idle":
|
|
227
|
+
break
|
|
228
|
+
|
|
229
|
+
return JupyterKernelClient.ExecutionResult(
|
|
230
|
+
is_ok=True, output="\n".join([str(output) for output in text_output]), data_items=data_output
|
|
231
|
+
)
|