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,577 @@
|
|
|
1
|
+
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import builtins
|
|
5
|
+
import importlib
|
|
6
|
+
import inspect
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
import tempfile
|
|
11
|
+
from collections.abc import Callable, Iterable, Iterator, Mapping
|
|
12
|
+
from contextlib import contextmanager
|
|
13
|
+
from functools import wraps
|
|
14
|
+
from logging import getLogger
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from types import ModuleType
|
|
17
|
+
from typing import (
|
|
18
|
+
TYPE_CHECKING,
|
|
19
|
+
Any,
|
|
20
|
+
Literal,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
import requests
|
|
24
|
+
from pydantic import PydanticInvalidForJsonSchema
|
|
25
|
+
from pydantic_core import PydanticUndefined
|
|
26
|
+
|
|
27
|
+
from autogen.import_utils import optional_import_block, require_optional_import
|
|
28
|
+
|
|
29
|
+
from .security import BaseSecurity, BaseSecurityParameters
|
|
30
|
+
|
|
31
|
+
with optional_import_block() as result:
|
|
32
|
+
import fastapi
|
|
33
|
+
import yaml
|
|
34
|
+
from datamodel_code_generator import DataModelType
|
|
35
|
+
from fastapi_code_generator.__main__ import generate_code
|
|
36
|
+
from jinja2 import Environment, FileSystemLoader
|
|
37
|
+
from mcp.server.fastmcp import FastMCP
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from autogen.agentchat import ConversableAgent
|
|
42
|
+
|
|
43
|
+
__all__ = ["MCPProxy"]
|
|
44
|
+
|
|
45
|
+
logger = getLogger(__name__)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@contextmanager
|
|
49
|
+
def optional_temp_path(path: str | None = None) -> Iterator[Path]:
|
|
50
|
+
if path is None:
|
|
51
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
52
|
+
yield Path(temp_dir)
|
|
53
|
+
else:
|
|
54
|
+
yield Path(path)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@contextmanager
|
|
58
|
+
def add_to_builtins(new_globals: dict[str, Any]) -> Iterator[None]:
|
|
59
|
+
old_globals = {key: getattr(builtins, key, None) for key in new_globals}
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
for key, value in new_globals.items():
|
|
63
|
+
setattr(builtins, key, value) # Inject new global
|
|
64
|
+
yield
|
|
65
|
+
finally:
|
|
66
|
+
for key, value in old_globals.items():
|
|
67
|
+
if value is None:
|
|
68
|
+
delattr(builtins, key) # Remove added globals
|
|
69
|
+
else:
|
|
70
|
+
setattr(builtins, key, value) # Restore original value
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class MCPProxy:
|
|
74
|
+
def __init__(self, servers: list[dict[str, Any]], title: str | None = None, **kwargs: Any) -> None:
|
|
75
|
+
"""Proxy class to generate client from OpenAPI schema."""
|
|
76
|
+
self._servers = servers
|
|
77
|
+
self._title = title or "MCP Proxy"
|
|
78
|
+
self._kwargs = kwargs
|
|
79
|
+
self._registered_funcs: list[Callable[..., Any]] = []
|
|
80
|
+
self._globals: dict[str, Any] = {}
|
|
81
|
+
|
|
82
|
+
self._security: dict[str, list[BaseSecurity]] = {}
|
|
83
|
+
self._security_params: dict[str | None, BaseSecurityParameters] = {}
|
|
84
|
+
self._tags: set[str] = set()
|
|
85
|
+
|
|
86
|
+
self._function_group: dict[str, list[str]] = {}
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def _convert_camel_case_within_braces_to_snake(text: str) -> str:
|
|
90
|
+
# Function to convert camel case to snake case
|
|
91
|
+
def camel_to_snake(match: re.Match[str]) -> str:
|
|
92
|
+
return re.sub(r"(?<!^)(?=[A-Z])", "_", match.group(1)).lower()
|
|
93
|
+
|
|
94
|
+
# Find all occurrences inside curly braces and apply camel_to_snake
|
|
95
|
+
result = re.sub(r"\{([a-zA-Z0-9]+)\}", lambda m: "{" + camel_to_snake(m) + "}", text)
|
|
96
|
+
|
|
97
|
+
return result
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _get_params(path: str, func: Callable[..., Any]) -> tuple[set[str], set[str], str | None, bool]:
|
|
101
|
+
sig = inspect.signature(func)
|
|
102
|
+
|
|
103
|
+
params_names = set(sig.parameters.keys())
|
|
104
|
+
|
|
105
|
+
path_params = set(re.findall(r"\{(.+?)\}", path))
|
|
106
|
+
if not path_params.issubset(params_names):
|
|
107
|
+
raise ValueError(f"Path params {path_params} not in {params_names}")
|
|
108
|
+
|
|
109
|
+
body = "body" if "body" in params_names else None
|
|
110
|
+
|
|
111
|
+
security = "security" in params_names
|
|
112
|
+
|
|
113
|
+
q_params = set(params_names) - path_params - {body} - {"security"}
|
|
114
|
+
|
|
115
|
+
return q_params, path_params, body, security
|
|
116
|
+
|
|
117
|
+
def get_mcp(self, **settings: Any) -> "FastMCP":
|
|
118
|
+
mcp = FastMCP(name=self._title, **settings) # newer mcp
|
|
119
|
+
|
|
120
|
+
for func in self._registered_funcs:
|
|
121
|
+
try:
|
|
122
|
+
mcp.tool()(func)
|
|
123
|
+
except PydanticInvalidForJsonSchema as e:
|
|
124
|
+
logger.warning("Could not register function %s: %s", func.__name__, e)
|
|
125
|
+
|
|
126
|
+
return mcp
|
|
127
|
+
|
|
128
|
+
def _process_params(
|
|
129
|
+
self, process_path: str, func: Callable[[Any], Any], **kwargs: Any
|
|
130
|
+
) -> tuple[str, dict[str, Any], dict[str, Any]]:
|
|
131
|
+
process_path = MCPProxy._convert_camel_case_within_braces_to_snake(process_path)
|
|
132
|
+
q_params, path_params, body, security = MCPProxy._get_params(process_path, func)
|
|
133
|
+
|
|
134
|
+
expanded_path = process_path.format(**{p: kwargs[p] for p in path_params})
|
|
135
|
+
|
|
136
|
+
url = self._servers[0]["url"] + expanded_path
|
|
137
|
+
|
|
138
|
+
body_dict = {}
|
|
139
|
+
if body and body in kwargs:
|
|
140
|
+
body_value = kwargs[body]
|
|
141
|
+
if isinstance(body_value, dict):
|
|
142
|
+
body_dict = {"json": body_value}
|
|
143
|
+
elif hasattr(body_value, "model_dump"):
|
|
144
|
+
body_dict = {"json": body_value.model_dump()}
|
|
145
|
+
else:
|
|
146
|
+
body_dict = {"json": body_value.dict()}
|
|
147
|
+
|
|
148
|
+
body_dict["headers"] = {"Content-Type": "application/json"}
|
|
149
|
+
if security:
|
|
150
|
+
q_params, body_dict = kwargs["security"].add_security(q_params, body_dict)
|
|
151
|
+
# body_dict["headers"][security] = kwargs["security"]
|
|
152
|
+
|
|
153
|
+
params = {k: v for k, v in kwargs.items() if k in q_params}
|
|
154
|
+
|
|
155
|
+
return url, params, body_dict
|
|
156
|
+
|
|
157
|
+
def set_security_params(self, security_params: BaseSecurityParameters, name: str | None = None) -> None:
|
|
158
|
+
if name is not None:
|
|
159
|
+
security = self._security.get(name)
|
|
160
|
+
if security is None:
|
|
161
|
+
raise ValueError(f"Security is not set for '{name}'")
|
|
162
|
+
|
|
163
|
+
for match_security in security:
|
|
164
|
+
if match_security.accept(security_params):
|
|
165
|
+
break
|
|
166
|
+
else:
|
|
167
|
+
raise ValueError(f"Security parameters {security_params} do not match security {security}")
|
|
168
|
+
|
|
169
|
+
self._security_params[name] = security_params
|
|
170
|
+
|
|
171
|
+
def _get_matching_security(
|
|
172
|
+
self, security: list[BaseSecurity], security_params: BaseSecurityParameters
|
|
173
|
+
) -> BaseSecurity:
|
|
174
|
+
# check if security matches security parameters
|
|
175
|
+
for match_security in security:
|
|
176
|
+
if match_security.accept(security_params):
|
|
177
|
+
return match_security
|
|
178
|
+
raise ValueError(f"Security parameters {security_params} does not match any given security {security}")
|
|
179
|
+
|
|
180
|
+
def _get_security_params(self, name: str) -> tuple[BaseSecurityParameters | None, BaseSecurity | None]:
|
|
181
|
+
# check if security is set for the method
|
|
182
|
+
security = self._security.get(name)
|
|
183
|
+
if not security:
|
|
184
|
+
return None, None
|
|
185
|
+
|
|
186
|
+
security_params = self._security_params.get(name)
|
|
187
|
+
if security_params is None:
|
|
188
|
+
# check if default security parameters are set
|
|
189
|
+
security_params = self._security_params.get(None)
|
|
190
|
+
if security_params is None:
|
|
191
|
+
raise ValueError(
|
|
192
|
+
f"Security parameters are not set for {name} and there are no default security parameters"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
match_security = self._get_matching_security(security, security_params)
|
|
196
|
+
|
|
197
|
+
return security_params, match_security
|
|
198
|
+
|
|
199
|
+
def _request(
|
|
200
|
+
self,
|
|
201
|
+
method: Literal["put", "get", "post", "head", "delete", "patch"],
|
|
202
|
+
path: str,
|
|
203
|
+
description: str | None = None,
|
|
204
|
+
security: list[BaseSecurity] | None = None,
|
|
205
|
+
**kwargs: Any,
|
|
206
|
+
) -> Callable[..., dict[str, Any]]:
|
|
207
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., dict[str, Any]]:
|
|
208
|
+
name = func.__name__
|
|
209
|
+
|
|
210
|
+
for tag in kwargs.get("tags", []):
|
|
211
|
+
if tag not in self._function_group:
|
|
212
|
+
self._function_group[tag] = []
|
|
213
|
+
self._function_group[tag].append(name)
|
|
214
|
+
|
|
215
|
+
if security is not None:
|
|
216
|
+
self._security[name] = security
|
|
217
|
+
|
|
218
|
+
@wraps(func)
|
|
219
|
+
def wrapper(*args: Any, **kwargs: Any) -> dict[str, Any]:
|
|
220
|
+
url, params, body_dict = self._process_params(path, func, **kwargs)
|
|
221
|
+
|
|
222
|
+
security = self._security.get(name)
|
|
223
|
+
if security is not None:
|
|
224
|
+
security_params, matched_security = self._get_security_params(name)
|
|
225
|
+
if security_params is None:
|
|
226
|
+
raise ValueError(f"Security parameters are not set for '{name}'")
|
|
227
|
+
else:
|
|
228
|
+
security_params.apply(params, body_dict, matched_security) # type: ignore [arg-type]
|
|
229
|
+
|
|
230
|
+
response = getattr(requests, method)(url, params=params, **body_dict)
|
|
231
|
+
return response.json() # type: ignore [no-any-return]
|
|
232
|
+
|
|
233
|
+
wrapper._description = ( # type: ignore [attr-defined]
|
|
234
|
+
description or func.__doc__.strip() if func.__doc__ is not None else None
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
self._registered_funcs.append(wrapper)
|
|
238
|
+
|
|
239
|
+
return wrapper
|
|
240
|
+
|
|
241
|
+
return decorator # type: ignore [return-value]
|
|
242
|
+
|
|
243
|
+
def put(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
|
|
244
|
+
return self._request("put", path, **kwargs)
|
|
245
|
+
|
|
246
|
+
def get(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
|
|
247
|
+
return self._request("get", path, **kwargs)
|
|
248
|
+
|
|
249
|
+
def post(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
|
|
250
|
+
return self._request("post", path, **kwargs)
|
|
251
|
+
|
|
252
|
+
def delete(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
|
|
253
|
+
return self._request("delete", path, **kwargs)
|
|
254
|
+
|
|
255
|
+
def head(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
|
|
256
|
+
return self._request("head", path, **kwargs)
|
|
257
|
+
|
|
258
|
+
def patch(self, path: str, **kwargs: Any) -> Callable[..., dict[str, Any]]:
|
|
259
|
+
return self._request("patch", path, **kwargs)
|
|
260
|
+
|
|
261
|
+
@classmethod
|
|
262
|
+
def _get_template_dir(cls) -> Path:
|
|
263
|
+
path = Path(__file__).parents[3] / "templates"
|
|
264
|
+
if not path.exists():
|
|
265
|
+
raise RuntimeError(f"Template directory {path.resolve()} not found.")
|
|
266
|
+
return path
|
|
267
|
+
|
|
268
|
+
@classmethod
|
|
269
|
+
@require_optional_import(["datamodel_code_generator", "fastapi_code_generator"], "mcp-proxy-gen")
|
|
270
|
+
def generate_code(
|
|
271
|
+
cls,
|
|
272
|
+
input_text: str,
|
|
273
|
+
output_dir: Path,
|
|
274
|
+
disable_timestamp: bool = False,
|
|
275
|
+
custom_visitors: list[Path] | None = None,
|
|
276
|
+
) -> str:
|
|
277
|
+
if custom_visitors is None:
|
|
278
|
+
custom_visitors = []
|
|
279
|
+
custom_visitors.append(Path(__file__).parent / "security_schema_visitor.py")
|
|
280
|
+
|
|
281
|
+
# with patch_get_parameter_type():
|
|
282
|
+
generate_code(
|
|
283
|
+
input_name="openapi.yaml",
|
|
284
|
+
input_text=input_text,
|
|
285
|
+
encoding="utf-8",
|
|
286
|
+
output_dir=output_dir,
|
|
287
|
+
template_dir=cls._get_template_dir() / "client_template",
|
|
288
|
+
disable_timestamp=disable_timestamp,
|
|
289
|
+
custom_visitors=custom_visitors,
|
|
290
|
+
output_model_type=DataModelType.PydanticV2BaseModel,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
main_path = output_dir / "main.py"
|
|
294
|
+
|
|
295
|
+
with main_path.open("r") as f:
|
|
296
|
+
main_py_code = f.read()
|
|
297
|
+
# main_py_code = main_py_code.replace("from .models import", "from models import")
|
|
298
|
+
main_py_code = main_py_code.replace("from .models", "from models")
|
|
299
|
+
# Removing "from __future__ import annotations" to avoid ForwardRef issues, should be fixed in fastapi_code_generator
|
|
300
|
+
main_py_code = main_py_code.replace("from __future__ import annotations", "")
|
|
301
|
+
|
|
302
|
+
with main_path.open("w") as f:
|
|
303
|
+
f.write(main_py_code)
|
|
304
|
+
|
|
305
|
+
return main_path.stem
|
|
306
|
+
|
|
307
|
+
def set_globals(self, main: ModuleType, suffix: str) -> None:
|
|
308
|
+
xs = {k: v for k, v in main.__dict__.items() if not k.startswith("__")}
|
|
309
|
+
self._globals = {
|
|
310
|
+
k: v for k, v in xs.items() if hasattr(v, "__module__") and v.__module__ in [f"models_{suffix}", "typing"]
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@classmethod
|
|
314
|
+
@require_optional_import(["yaml"], "mcp-proxy-gen")
|
|
315
|
+
def create(
|
|
316
|
+
cls,
|
|
317
|
+
*,
|
|
318
|
+
openapi_specification: str | None = None,
|
|
319
|
+
openapi_url: str | None = None,
|
|
320
|
+
client_source_path: str | None = None,
|
|
321
|
+
servers: list[dict[str, Any]] | None = None,
|
|
322
|
+
rename_functions: bool = False,
|
|
323
|
+
group_functions: bool = False,
|
|
324
|
+
configuration_type: Literal["json", "yaml"] = "json",
|
|
325
|
+
) -> "MCPProxy":
|
|
326
|
+
if (openapi_specification is None) == (openapi_url is None):
|
|
327
|
+
raise ValueError("Either openapi_specification or openapi_url should be provided")
|
|
328
|
+
|
|
329
|
+
if openapi_specification is None and openapi_url is not None:
|
|
330
|
+
with requests.get(openapi_url, timeout=10) as response:
|
|
331
|
+
response.raise_for_status()
|
|
332
|
+
openapi_specification = response.text
|
|
333
|
+
|
|
334
|
+
openapi_parsed = (
|
|
335
|
+
json.loads(openapi_specification) if configuration_type == "json" else yaml.safe_load(openapi_specification)
|
|
336
|
+
) # type: ignore [arg-type]
|
|
337
|
+
|
|
338
|
+
if servers:
|
|
339
|
+
openapi_parsed["servers"] = servers
|
|
340
|
+
|
|
341
|
+
yaml_friendly = yaml.safe_dump(openapi_parsed)
|
|
342
|
+
|
|
343
|
+
with optional_temp_path(client_source_path) as td:
|
|
344
|
+
suffix = td.name # noqa F841
|
|
345
|
+
|
|
346
|
+
custom_visitors = []
|
|
347
|
+
|
|
348
|
+
if rename_functions:
|
|
349
|
+
custom_visitors.append(Path(__file__).parent / "operation_renaming.py")
|
|
350
|
+
|
|
351
|
+
if group_functions:
|
|
352
|
+
custom_visitors.append(Path(__file__).parent / "operation_grouping.py")
|
|
353
|
+
|
|
354
|
+
main_name = cls.generate_code( # noqa F841
|
|
355
|
+
input_text=yaml_friendly, # type: ignore [arg-type]
|
|
356
|
+
output_dir=td,
|
|
357
|
+
custom_visitors=custom_visitors,
|
|
358
|
+
)
|
|
359
|
+
# add td to sys.path
|
|
360
|
+
try:
|
|
361
|
+
sys.path.append(str(td))
|
|
362
|
+
main = importlib.import_module(main_name, package=td.name) # nosemgrep
|
|
363
|
+
finally:
|
|
364
|
+
sys.path.remove(str(td))
|
|
365
|
+
|
|
366
|
+
client: MCPProxy = main.app # type: ignore [attr-defined]
|
|
367
|
+
client.set_globals(main, suffix=suffix)
|
|
368
|
+
|
|
369
|
+
client.dump_configurations(output_dir=td)
|
|
370
|
+
|
|
371
|
+
return client
|
|
372
|
+
|
|
373
|
+
def _get_authentications(self) -> list[dict[str, Any]]:
|
|
374
|
+
seen = set()
|
|
375
|
+
authentications = []
|
|
376
|
+
|
|
377
|
+
for security_list in self._security.values():
|
|
378
|
+
for security in security_list:
|
|
379
|
+
params = security.Parameters().dump()
|
|
380
|
+
|
|
381
|
+
if params.get("type") == "unsupported":
|
|
382
|
+
continue
|
|
383
|
+
|
|
384
|
+
dumped = json.dumps(params) # hashable
|
|
385
|
+
if dumped not in seen:
|
|
386
|
+
seen.add(dumped)
|
|
387
|
+
authentications.append(security.Parameters().dump())
|
|
388
|
+
return authentications
|
|
389
|
+
|
|
390
|
+
def dump_configurations(self, output_dir: Path) -> None:
|
|
391
|
+
for tag in self._function_group:
|
|
392
|
+
output_file = output_dir / f"mcp_config_{tag}.json"
|
|
393
|
+
|
|
394
|
+
functions = [
|
|
395
|
+
registered_function
|
|
396
|
+
for registered_function in self._registered_funcs
|
|
397
|
+
if registered_function.__name__ in self._function_group[tag]
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
self.dump_configuration(output_file, functions)
|
|
401
|
+
|
|
402
|
+
self.dump_configuration(output_dir / "mcp_config.json", self._registered_funcs)
|
|
403
|
+
|
|
404
|
+
def dump_configuration(self, output_file: Path, functions: list[Callable[..., Any]] = None) -> None:
|
|
405
|
+
# Define paths
|
|
406
|
+
template_dir = MCPProxy._get_template_dir() / "config_template"
|
|
407
|
+
template_file = "config.jinja2"
|
|
408
|
+
|
|
409
|
+
# Load Jinja environment
|
|
410
|
+
env = Environment(loader=FileSystemLoader(template_dir), trim_blocks=True, lstrip_blocks=True)
|
|
411
|
+
|
|
412
|
+
# Load the template
|
|
413
|
+
template = env.get_template(template_file)
|
|
414
|
+
# Prepare context for rendering
|
|
415
|
+
context = {
|
|
416
|
+
"server_url": self._servers[0]["url"], # single or list depending on your structure
|
|
417
|
+
"authentications": self._get_authentications(), # list of auth blocks, we will also need to check _security_params
|
|
418
|
+
"operations": [
|
|
419
|
+
{
|
|
420
|
+
"name": op.__name__,
|
|
421
|
+
"description": op._description.replace("\n", " ").replace("\r", "").strip()
|
|
422
|
+
if op._description is not None
|
|
423
|
+
else "",
|
|
424
|
+
}
|
|
425
|
+
for op in functions
|
|
426
|
+
],
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
# Render the template
|
|
430
|
+
rendered_config = template.render(context)
|
|
431
|
+
|
|
432
|
+
# Save the output to a file
|
|
433
|
+
with open(output_file, "w") as f:
|
|
434
|
+
f.write(rendered_config)
|
|
435
|
+
|
|
436
|
+
def load_configuration(self, config_file: str) -> None:
|
|
437
|
+
with Path(config_file).open("r") as f:
|
|
438
|
+
config_data_str = f.read()
|
|
439
|
+
|
|
440
|
+
self.load_configuration_from_string(config_data_str)
|
|
441
|
+
|
|
442
|
+
def load_configuration_from_string(self, config_data_str: str) -> None:
|
|
443
|
+
config_data = json.loads(config_data_str)
|
|
444
|
+
# Load server URL
|
|
445
|
+
self._servers = [{"url": config_data["server"]["url"]}]
|
|
446
|
+
|
|
447
|
+
# Load authentication
|
|
448
|
+
for auth in config_data.get("authentication", []):
|
|
449
|
+
security = BaseSecurity.parse_security_parameters(auth)
|
|
450
|
+
self.set_security_params(security)
|
|
451
|
+
|
|
452
|
+
operation_names = [op["name"] for op in config_data.get("operations", [])]
|
|
453
|
+
|
|
454
|
+
self._registered_funcs = [func for func in self._registered_funcs if func.__name__ in operation_names]
|
|
455
|
+
|
|
456
|
+
def _get_functions_to_register(
|
|
457
|
+
self,
|
|
458
|
+
functions: Iterable[str | Mapping[str, Mapping[str, str]]] | None = None,
|
|
459
|
+
) -> dict[Callable[..., Any], dict[str, str | None]]:
|
|
460
|
+
if functions is None:
|
|
461
|
+
return {
|
|
462
|
+
f: {
|
|
463
|
+
"name": None,
|
|
464
|
+
"description": f._description if hasattr(f, "_description") else None,
|
|
465
|
+
}
|
|
466
|
+
for f in self._registered_funcs
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
functions_with_name_desc: dict[str, dict[str, str | None]] = {}
|
|
470
|
+
|
|
471
|
+
for f in functions:
|
|
472
|
+
if isinstance(f, str):
|
|
473
|
+
functions_with_name_desc[f] = {"name": None, "description": None}
|
|
474
|
+
elif isinstance(f, dict):
|
|
475
|
+
functions_with_name_desc.update({
|
|
476
|
+
k: {
|
|
477
|
+
"name": v.get("name", None),
|
|
478
|
+
"description": v.get("description", None),
|
|
479
|
+
}
|
|
480
|
+
for k, v in f.items()
|
|
481
|
+
})
|
|
482
|
+
else:
|
|
483
|
+
raise ValueError(f"Invalid type {type(f)} for function {f}")
|
|
484
|
+
|
|
485
|
+
funcs_to_register: dict[Callable[..., Any], dict[str, str | None]] = {
|
|
486
|
+
f: functions_with_name_desc[f.__name__]
|
|
487
|
+
for f in self._registered_funcs
|
|
488
|
+
if f.__name__ in functions_with_name_desc
|
|
489
|
+
}
|
|
490
|
+
missing_functions = set(functions_with_name_desc.keys()) - {f.__name__ for f in funcs_to_register}
|
|
491
|
+
if missing_functions:
|
|
492
|
+
raise ValueError(f"Following functions {missing_functions} are not valid functions")
|
|
493
|
+
|
|
494
|
+
return funcs_to_register
|
|
495
|
+
|
|
496
|
+
@staticmethod
|
|
497
|
+
def _remove_pydantic_undefined_from_tools(
|
|
498
|
+
tools: list[dict[str, Any]],
|
|
499
|
+
) -> list[dict[str, Any]]:
|
|
500
|
+
for tool in tools:
|
|
501
|
+
if "function" not in tool:
|
|
502
|
+
continue
|
|
503
|
+
|
|
504
|
+
function = tool["function"]
|
|
505
|
+
if "parameters" not in function or "properties" not in function["parameters"]:
|
|
506
|
+
continue
|
|
507
|
+
|
|
508
|
+
required = function["parameters"].get("required", [])
|
|
509
|
+
for param_name, param_value in function["parameters"]["properties"].items():
|
|
510
|
+
if "default" not in param_value:
|
|
511
|
+
continue
|
|
512
|
+
|
|
513
|
+
default = param_value.get("default")
|
|
514
|
+
if (
|
|
515
|
+
isinstance(default, (fastapi.params.Path, fastapi.params.Query))
|
|
516
|
+
and param_value["default"].default is PydanticUndefined
|
|
517
|
+
):
|
|
518
|
+
param_value.pop("default")
|
|
519
|
+
# We removed the default value, so we need to add the parameter to the required list
|
|
520
|
+
if param_name not in required:
|
|
521
|
+
required.append(param_name)
|
|
522
|
+
|
|
523
|
+
return tools
|
|
524
|
+
|
|
525
|
+
def _register_for_llm(
|
|
526
|
+
self,
|
|
527
|
+
agent: "ConversableAgent",
|
|
528
|
+
functions: Iterable[str | Mapping[str, Mapping[str, str]]] | None = None,
|
|
529
|
+
) -> None:
|
|
530
|
+
funcs_to_register = self._get_functions_to_register(functions)
|
|
531
|
+
|
|
532
|
+
with add_to_builtins(
|
|
533
|
+
new_globals=self._globals,
|
|
534
|
+
):
|
|
535
|
+
for f, v in funcs_to_register.items():
|
|
536
|
+
agent.register_for_llm(name=v["name"], description=v["description"])(f)
|
|
537
|
+
|
|
538
|
+
agent.llm_config["tools"] = MCPProxy._remove_pydantic_undefined_from_tools(agent.llm_config["tools"])
|
|
539
|
+
|
|
540
|
+
def _register_for_execution(
|
|
541
|
+
self,
|
|
542
|
+
agent: "ConversableAgent",
|
|
543
|
+
functions: Iterable[str | Mapping[str, Mapping[str, str]]] | None = None,
|
|
544
|
+
) -> None:
|
|
545
|
+
funcs_to_register = self._get_functions_to_register(functions)
|
|
546
|
+
|
|
547
|
+
for f, v in funcs_to_register.items():
|
|
548
|
+
agent.register_for_execution(name=v["name"])(f)
|
|
549
|
+
|
|
550
|
+
def get_functions(self) -> list[str]:
|
|
551
|
+
raise DeprecationWarning("Use function_names property instead of get_functions method")
|
|
552
|
+
|
|
553
|
+
@property
|
|
554
|
+
def function_names(self) -> list[str]:
|
|
555
|
+
return [f.__name__ for f in self._registered_funcs]
|
|
556
|
+
|
|
557
|
+
def get_function(self, name: str) -> Callable[..., dict[str, Any]]:
|
|
558
|
+
for f in self._registered_funcs:
|
|
559
|
+
if f.__name__ == name:
|
|
560
|
+
return f
|
|
561
|
+
raise ValueError(f"Function {name} not found")
|
|
562
|
+
|
|
563
|
+
def set_function(self, name: str, func: Callable[..., dict[str, Any]]) -> None:
|
|
564
|
+
for i, f in enumerate(self._registered_funcs):
|
|
565
|
+
if f.__name__ == name:
|
|
566
|
+
self._registered_funcs[i] = func
|
|
567
|
+
return
|
|
568
|
+
|
|
569
|
+
raise ValueError(f"Function {name} not found")
|
|
570
|
+
|
|
571
|
+
def inject_parameters(self, name: str, **kwargs: Any) -> None:
|
|
572
|
+
raise NotImplementedError("Injecting parameters is not implemented yet")
|
|
573
|
+
# for f in self._registered_funcs:
|
|
574
|
+
# if f.__name__ == name:
|
|
575
|
+
# return
|
|
576
|
+
|
|
577
|
+
# raise ValueError(f"Function {name} not found")
|