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,385 @@
|
|
|
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 asyncio
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
from typing import Annotated, Any
|
|
8
|
+
|
|
9
|
+
from .....doc_utils import export_module
|
|
10
|
+
from .....import_utils import optional_import_block, require_optional_import
|
|
11
|
+
from .... import Tool
|
|
12
|
+
from ....dependency_injection import Depends, on
|
|
13
|
+
|
|
14
|
+
__all__ = ["SlackSendTool"]
|
|
15
|
+
|
|
16
|
+
with optional_import_block():
|
|
17
|
+
from slack_sdk import WebClient
|
|
18
|
+
from slack_sdk.errors import SlackApiError
|
|
19
|
+
|
|
20
|
+
MAX_MESSAGE_LENGTH = 40000
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@require_optional_import(["slack_sdk"], "commsagent-slack")
|
|
24
|
+
@export_module("autogen.tools.experimental")
|
|
25
|
+
class SlackSendTool(Tool):
|
|
26
|
+
"""Sends a message to a Slack channel."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, *, bot_token: str, channel_id: str) -> None:
|
|
29
|
+
"""Initialize the SlackSendTool.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
bot_token: Bot User OAuth Token starting with "xoxb-".
|
|
33
|
+
channel_id: Channel ID where messages will be sent.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
# Function that sends the message, uses dependency injection for bot token / channel / guild
|
|
37
|
+
async def slack_send_message(
|
|
38
|
+
message: Annotated[str, "Message to send to the channel."],
|
|
39
|
+
bot_token: Annotated[str, Depends(on(bot_token))],
|
|
40
|
+
channel_id: Annotated[str, Depends(on(channel_id))],
|
|
41
|
+
) -> Any:
|
|
42
|
+
"""Sends a message to a Slack channel.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
message: The message to send to the channel.
|
|
46
|
+
bot_token: The bot token to use for Slack. (uses dependency injection)
|
|
47
|
+
channel_id: The ID of the channel. (uses dependency injection)
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
web_client = WebClient(token=bot_token)
|
|
51
|
+
|
|
52
|
+
# Send the message
|
|
53
|
+
if len(message) > MAX_MESSAGE_LENGTH:
|
|
54
|
+
chunks = [
|
|
55
|
+
message[i : i + (MAX_MESSAGE_LENGTH - 1)]
|
|
56
|
+
for i in range(0, len(message), (MAX_MESSAGE_LENGTH - 1))
|
|
57
|
+
]
|
|
58
|
+
for i, chunk in enumerate(chunks):
|
|
59
|
+
response = web_client.chat_postMessage(channel=channel_id, text=chunk)
|
|
60
|
+
|
|
61
|
+
if not response["ok"]:
|
|
62
|
+
return f"Message send failed on chunk {i + 1}, Slack response error: {response['error']}"
|
|
63
|
+
|
|
64
|
+
# Store ID for the first chunk
|
|
65
|
+
if i == 0:
|
|
66
|
+
sent_message_id = response["ts"]
|
|
67
|
+
|
|
68
|
+
return f"Message sent successfully ({len(chunks)} chunks, first ID: {sent_message_id}):\n{message}"
|
|
69
|
+
else:
|
|
70
|
+
response = web_client.chat_postMessage(channel=channel_id, text=message)
|
|
71
|
+
|
|
72
|
+
if not response["ok"]:
|
|
73
|
+
return f"Message send failed, Slack response error: {response['error']}"
|
|
74
|
+
|
|
75
|
+
return f"Message sent successfully (ID: {response['ts']}):\n{message}"
|
|
76
|
+
except SlackApiError as e:
|
|
77
|
+
return f"Message send failed, Slack API exception: {e.response['error']} (See https://api.slack.com/automation/cli/errors#{e.response['error']})"
|
|
78
|
+
except Exception as e:
|
|
79
|
+
return f"Message send failed, exception: {e}"
|
|
80
|
+
|
|
81
|
+
super().__init__(
|
|
82
|
+
name="slack_send",
|
|
83
|
+
description="Sends a message to a Slack channel.",
|
|
84
|
+
func_or_tool=slack_send_message,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@require_optional_import(["slack_sdk"], "commsagent-slack")
|
|
89
|
+
@export_module("autogen.tools.experimental")
|
|
90
|
+
class SlackRetrieveTool(Tool):
|
|
91
|
+
"""Retrieves messages from a Slack channel."""
|
|
92
|
+
|
|
93
|
+
def __init__(self, *, bot_token: str, channel_id: str) -> None:
|
|
94
|
+
"""Initialize the SlackRetrieveTool.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
bot_token: Bot User OAuth Token starting with "xoxb-".
|
|
98
|
+
channel_id: Channel ID where messages will be sent.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
async def slack_retrieve_messages(
|
|
102
|
+
bot_token: Annotated[str, Depends(on(bot_token))],
|
|
103
|
+
channel_id: Annotated[str, Depends(on(channel_id))],
|
|
104
|
+
messages_since: Annotated[
|
|
105
|
+
str | None,
|
|
106
|
+
"Date to retrieve messages from (ISO format) OR Slack message ID. If None, retrieves latest messages.",
|
|
107
|
+
] = None,
|
|
108
|
+
maximum_messages: Annotated[
|
|
109
|
+
int | None, "Maximum number of messages to retrieve. If None, retrieves all messages since date."
|
|
110
|
+
] = None,
|
|
111
|
+
) -> Any:
|
|
112
|
+
"""Retrieves messages from a Discord channel.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
bot_token: The bot token to use for Discord. (uses dependency injection)
|
|
116
|
+
channel_id: The ID of the channel. (uses dependency injection)
|
|
117
|
+
messages_since: ISO format date string OR Slack message ID, to retrieve messages from. If None, retrieves latest messages.
|
|
118
|
+
maximum_messages: Maximum number of messages to retrieve. If None, retrieves all messages since date.
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
web_client = WebClient(token=bot_token)
|
|
122
|
+
|
|
123
|
+
# Convert ISO datetime to Unix timestamp if needed
|
|
124
|
+
oldest = None
|
|
125
|
+
if messages_since:
|
|
126
|
+
if "." in messages_since: # Likely a Slack message ID
|
|
127
|
+
oldest = messages_since
|
|
128
|
+
else: # Assume ISO format
|
|
129
|
+
try:
|
|
130
|
+
dt = datetime.fromisoformat(messages_since.replace("Z", "+00:00"))
|
|
131
|
+
oldest = str(dt.timestamp())
|
|
132
|
+
except ValueError as e:
|
|
133
|
+
return f"Invalid date format. Please provide either a Slack message ID or ISO format date (e.g., '2025-01-25T00:00:00Z'). Error: {e}"
|
|
134
|
+
|
|
135
|
+
messages = []
|
|
136
|
+
cursor = None
|
|
137
|
+
|
|
138
|
+
while True:
|
|
139
|
+
try:
|
|
140
|
+
# Prepare API call parameters
|
|
141
|
+
params = {
|
|
142
|
+
"channel": channel_id,
|
|
143
|
+
"limit": min(1000, maximum_messages) if maximum_messages else 1000,
|
|
144
|
+
}
|
|
145
|
+
if oldest:
|
|
146
|
+
params["oldest"] = oldest
|
|
147
|
+
if cursor:
|
|
148
|
+
params["cursor"] = cursor
|
|
149
|
+
|
|
150
|
+
# Make API call
|
|
151
|
+
response = web_client.conversations_history(**params) # type: ignore[arg-type]
|
|
152
|
+
|
|
153
|
+
if not response["ok"]:
|
|
154
|
+
return f"Message retrieval failed, Slack response error: {response['error']}"
|
|
155
|
+
|
|
156
|
+
# Add messages to our list
|
|
157
|
+
messages.extend(response["messages"])
|
|
158
|
+
|
|
159
|
+
# Check if we've hit our maximum
|
|
160
|
+
if maximum_messages and len(messages) >= maximum_messages:
|
|
161
|
+
messages = messages[:maximum_messages]
|
|
162
|
+
break
|
|
163
|
+
|
|
164
|
+
# Check if there are more messages
|
|
165
|
+
if not response["has_more"]:
|
|
166
|
+
break
|
|
167
|
+
|
|
168
|
+
cursor = response["response_metadata"]["next_cursor"]
|
|
169
|
+
|
|
170
|
+
except SlackApiError as e:
|
|
171
|
+
return f"Message retrieval failed on pagination, Slack API error: {e.response['error']}"
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
"message_count": len(messages),
|
|
175
|
+
"messages": messages,
|
|
176
|
+
"start_time": oldest or "latest",
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
except SlackApiError as e:
|
|
180
|
+
return f"Message retrieval failed, Slack API exception: {e.response['error']} (See https://api.slack.com/automation/cli/errors#{e.response['error']})"
|
|
181
|
+
except Exception as e:
|
|
182
|
+
return f"Message retrieval failed, exception: {e}"
|
|
183
|
+
|
|
184
|
+
super().__init__(
|
|
185
|
+
name="slack_retrieve",
|
|
186
|
+
description="Retrieves messages from a Slack channel based datetime/message ID and/or number of latest messages.",
|
|
187
|
+
func_or_tool=slack_retrieve_messages,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@require_optional_import(["slack_sdk"], "commsagent-slack")
|
|
192
|
+
@export_module("autogen.tools.experimental")
|
|
193
|
+
class SlackRetrieveRepliesTool(Tool):
|
|
194
|
+
"""Retrieves replies to a specific Slack message from both threads and the channel."""
|
|
195
|
+
|
|
196
|
+
def __init__(self, *, bot_token: str, channel_id: str) -> None:
|
|
197
|
+
"""Initialize the SlackRetrieveRepliesTool.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
bot_token: Bot User OAuth Token starting with "xoxb-".
|
|
201
|
+
channel_id: Channel ID where the parent message exists.
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
async def slack_retrieve_replies(
|
|
205
|
+
message_ts: Annotated[str, "Timestamp (ts) of the parent message to retrieve replies for."],
|
|
206
|
+
bot_token: Annotated[str, Depends(on(bot_token))],
|
|
207
|
+
channel_id: Annotated[str, Depends(on(channel_id))],
|
|
208
|
+
min_replies: Annotated[
|
|
209
|
+
int | None,
|
|
210
|
+
"Minimum number of replies to wait for before returning (thread + channel). If None, returns immediately.",
|
|
211
|
+
] = None,
|
|
212
|
+
timeout_seconds: Annotated[
|
|
213
|
+
int, "Maximum time in seconds to wait for the requested number of replies."
|
|
214
|
+
] = 60,
|
|
215
|
+
poll_interval: Annotated[int, "Time in seconds between polling attempts when waiting for replies."] = 5,
|
|
216
|
+
include_channel_messages: Annotated[
|
|
217
|
+
bool, "Whether to include messages in the channel after the original message."
|
|
218
|
+
] = True,
|
|
219
|
+
) -> Any:
|
|
220
|
+
"""Retrieves replies to a specific Slack message, from both threads and the main channel.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
message_ts: The timestamp (ts) identifier of the parent message.
|
|
224
|
+
bot_token: The bot token to use for Slack. (uses dependency injection)
|
|
225
|
+
channel_id: The ID of the channel. (uses dependency injection)
|
|
226
|
+
min_replies: Minimum number of combined replies to wait for before returning. If None, returns immediately.
|
|
227
|
+
timeout_seconds: Maximum time in seconds to wait for the requested number of replies.
|
|
228
|
+
poll_interval: Time in seconds between polling attempts when waiting for replies.
|
|
229
|
+
include_channel_messages: Whether to include messages posted in the channel after the original message.
|
|
230
|
+
"""
|
|
231
|
+
try:
|
|
232
|
+
web_client = WebClient(token=bot_token)
|
|
233
|
+
|
|
234
|
+
# Function to get current thread replies
|
|
235
|
+
async def get_thread_replies() -> tuple[list[dict[str, Any]] | None, str | None]:
|
|
236
|
+
try:
|
|
237
|
+
response = web_client.conversations_replies(
|
|
238
|
+
channel=channel_id,
|
|
239
|
+
ts=message_ts,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if not response["ok"]:
|
|
243
|
+
return None, f"Thread reply retrieval failed, Slack response error: {response['error']}"
|
|
244
|
+
|
|
245
|
+
# The first message is the parent message itself, so exclude it when counting replies
|
|
246
|
+
replies = response["messages"][1:] if len(response["messages"]) > 0 else []
|
|
247
|
+
return replies, None
|
|
248
|
+
|
|
249
|
+
except SlackApiError as e:
|
|
250
|
+
return None, f"Thread reply retrieval failed, Slack API exception: {e.response['error']}"
|
|
251
|
+
except Exception as e:
|
|
252
|
+
return None, f"Thread reply retrieval failed, exception: {e}"
|
|
253
|
+
|
|
254
|
+
# Function to get messages in the channel after the original message
|
|
255
|
+
async def get_channel_messages() -> tuple[list[dict[str, Any]] | None, str | None]:
|
|
256
|
+
try:
|
|
257
|
+
response = web_client.conversations_history(
|
|
258
|
+
channel=channel_id,
|
|
259
|
+
oldest=message_ts, # Start from the original message timestamp
|
|
260
|
+
inclusive=False, # Don't include the original message
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
if not response["ok"]:
|
|
264
|
+
return None, f"Channel message retrieval failed, Slack response error: {response['error']}"
|
|
265
|
+
|
|
266
|
+
# Return all messages in the channel after the original message
|
|
267
|
+
# We need to filter out any that are part of the thread we're already getting
|
|
268
|
+
messages = []
|
|
269
|
+
for msg in response["messages"]:
|
|
270
|
+
# Skip if the message is part of the thread we're already retrieving
|
|
271
|
+
if "thread_ts" in msg and msg["thread_ts"] == message_ts:
|
|
272
|
+
continue
|
|
273
|
+
messages.append(msg)
|
|
274
|
+
|
|
275
|
+
return messages, None
|
|
276
|
+
|
|
277
|
+
except SlackApiError as e:
|
|
278
|
+
return None, f"Channel message retrieval failed, Slack API exception: {e.response['error']}"
|
|
279
|
+
except Exception as e:
|
|
280
|
+
return None, f"Channel message retrieval failed, exception: {e}"
|
|
281
|
+
|
|
282
|
+
# Function to get all replies (both thread and channel)
|
|
283
|
+
async def get_all_replies() -> tuple[
|
|
284
|
+
list[dict[str, Any]] | None, list[dict[str, Any]] | None, str | None
|
|
285
|
+
]:
|
|
286
|
+
thread_replies, thread_error = await get_thread_replies()
|
|
287
|
+
if thread_error:
|
|
288
|
+
return None, None, thread_error
|
|
289
|
+
|
|
290
|
+
channel_messages: list[dict[str, Any]] = []
|
|
291
|
+
channel_error = None
|
|
292
|
+
|
|
293
|
+
if include_channel_messages:
|
|
294
|
+
channel_results, channel_error = await get_channel_messages()
|
|
295
|
+
if channel_error:
|
|
296
|
+
return thread_replies, None, channel_error
|
|
297
|
+
channel_messages = channel_results if channel_results is not None else []
|
|
298
|
+
|
|
299
|
+
return thread_replies, channel_messages, None
|
|
300
|
+
|
|
301
|
+
# If no waiting is required, just get replies and return
|
|
302
|
+
if min_replies is None:
|
|
303
|
+
thread_replies, channel_messages, error = await get_all_replies()
|
|
304
|
+
if error:
|
|
305
|
+
return error
|
|
306
|
+
|
|
307
|
+
thread_replies_list: list[dict[str, Any]] = [] if thread_replies is None else thread_replies
|
|
308
|
+
channel_messages_list: list[dict[str, Any]] = [] if channel_messages is None else channel_messages
|
|
309
|
+
|
|
310
|
+
# Combine replies for counting but keep them separate in the result
|
|
311
|
+
total_reply_count = len(thread_replies_list) + len(channel_messages_list)
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
"parent_message_ts": message_ts,
|
|
315
|
+
"total_reply_count": total_reply_count,
|
|
316
|
+
"thread_replies": thread_replies_list,
|
|
317
|
+
"thread_reply_count": len(thread_replies_list),
|
|
318
|
+
"channel_messages": channel_messages_list if include_channel_messages else None,
|
|
319
|
+
"channel_message_count": len(channel_messages_list) if include_channel_messages else None,
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
# Wait for the required number of replies with timeout
|
|
323
|
+
start_time = datetime.now()
|
|
324
|
+
end_time = start_time + timedelta(seconds=timeout_seconds)
|
|
325
|
+
|
|
326
|
+
while datetime.now() < end_time:
|
|
327
|
+
thread_replies, channel_messages, error = await get_all_replies()
|
|
328
|
+
if error:
|
|
329
|
+
return error
|
|
330
|
+
|
|
331
|
+
thread_replies_current: list[dict[str, Any]] = [] if thread_replies is None else thread_replies
|
|
332
|
+
channel_messages_current: list[dict[str, Any]] = (
|
|
333
|
+
[] if channel_messages is None else channel_messages
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# Combine replies for counting
|
|
337
|
+
total_reply_count = len(thread_replies_current) + len(channel_messages_current)
|
|
338
|
+
|
|
339
|
+
# If we have enough total replies, return them
|
|
340
|
+
if total_reply_count >= min_replies:
|
|
341
|
+
return {
|
|
342
|
+
"parent_message_ts": message_ts,
|
|
343
|
+
"total_reply_count": total_reply_count,
|
|
344
|
+
"thread_replies": thread_replies_current,
|
|
345
|
+
"thread_reply_count": len(thread_replies_current),
|
|
346
|
+
"channel_messages": channel_messages_current if include_channel_messages else None,
|
|
347
|
+
"channel_message_count": len(channel_messages_current)
|
|
348
|
+
if include_channel_messages
|
|
349
|
+
else None,
|
|
350
|
+
"waited_seconds": (datetime.now() - start_time).total_seconds(),
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
# Wait before checking again
|
|
354
|
+
await asyncio.sleep(poll_interval)
|
|
355
|
+
|
|
356
|
+
# If we reach here, we timed out waiting for replies
|
|
357
|
+
thread_replies, channel_messages, error = await get_all_replies()
|
|
358
|
+
if error:
|
|
359
|
+
return error
|
|
360
|
+
|
|
361
|
+
# Combine replies for counting
|
|
362
|
+
total_reply_count = len(thread_replies or []) + len(channel_messages or [])
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
"parent_message_ts": message_ts,
|
|
366
|
+
"total_reply_count": total_reply_count,
|
|
367
|
+
"thread_replies": thread_replies or [],
|
|
368
|
+
"thread_reply_count": len(thread_replies or []),
|
|
369
|
+
"channel_messages": channel_messages or [] if include_channel_messages else None,
|
|
370
|
+
"channel_message_count": len(channel_messages or []) if include_channel_messages else None,
|
|
371
|
+
"timed_out": True,
|
|
372
|
+
"waited_seconds": timeout_seconds,
|
|
373
|
+
"requested_replies": min_replies,
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
except SlackApiError as e:
|
|
377
|
+
return f"Reply retrieval failed, Slack API exception: {e.response['error']} (See https://api.slack.com/automation/cli/errors#{e.response['error']})"
|
|
378
|
+
except Exception as e:
|
|
379
|
+
return f"Reply retrieval failed, exception: {e}"
|
|
380
|
+
|
|
381
|
+
super().__init__(
|
|
382
|
+
name="slack_retrieve_replies",
|
|
383
|
+
description="Retrieves replies to a specific Slack message, checking both thread replies and messages in the channel after the original message.",
|
|
384
|
+
func_or_tool=slack_retrieve_replies,
|
|
385
|
+
)
|
|
@@ -0,0 +1,7 @@
|
|
|
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
|
+
from .telegram import TelegramRetrieveTool, TelegramSendTool
|
|
6
|
+
|
|
7
|
+
__all__ = ["TelegramRetrieveTool", "TelegramSendTool"]
|
|
@@ -0,0 +1,271 @@
|
|
|
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
|
+
from datetime import datetime
|
|
6
|
+
from typing import Annotated, Any, Union
|
|
7
|
+
|
|
8
|
+
from .....doc_utils import export_module
|
|
9
|
+
from .....import_utils import optional_import_block, require_optional_import
|
|
10
|
+
from .... import Tool
|
|
11
|
+
from ....dependency_injection import Depends, on
|
|
12
|
+
|
|
13
|
+
__all__ = ["TelegramRetrieveTool", "TelegramSendTool"]
|
|
14
|
+
|
|
15
|
+
with optional_import_block():
|
|
16
|
+
from telethon import TelegramClient
|
|
17
|
+
from telethon.tl.types import InputMessagesFilterEmpty, Message, PeerChannel, PeerChat, PeerUser
|
|
18
|
+
|
|
19
|
+
MAX_MESSAGE_LENGTH = 4096
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@require_optional_import(["telethon", "telethon.tl.types"], "commsagent-telegram")
|
|
23
|
+
@export_module("autogen.tools.experimental")
|
|
24
|
+
class BaseTelegramTool:
|
|
25
|
+
"""Base class for Telegram tools containing shared functionality."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, api_id: str, api_hash: str, session_name: str) -> None:
|
|
28
|
+
self._api_id = api_id
|
|
29
|
+
self._api_hash = api_hash
|
|
30
|
+
self._session_name = session_name
|
|
31
|
+
|
|
32
|
+
def _get_client(self) -> "TelegramClient": # type: ignore[no-any-unimported]
|
|
33
|
+
"""Get a fresh TelegramClient instance."""
|
|
34
|
+
return TelegramClient(self._session_name, self._api_id, self._api_hash)
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def _get_peer_from_id(chat_id: str) -> Union["PeerChat", "PeerChannel", "PeerUser"]: # type: ignore[no-any-unimported]
|
|
38
|
+
"""Convert a chat ID string to appropriate Peer type."""
|
|
39
|
+
try:
|
|
40
|
+
# Convert string to integer
|
|
41
|
+
id_int = int(chat_id)
|
|
42
|
+
|
|
43
|
+
# Channel/Supergroup: -100 prefix
|
|
44
|
+
if str(chat_id).startswith("-100"):
|
|
45
|
+
channel_id = int(str(chat_id)[4:]) # Remove -100 prefix
|
|
46
|
+
return PeerChannel(channel_id)
|
|
47
|
+
|
|
48
|
+
# Group: negative number without -100 prefix
|
|
49
|
+
elif id_int < 0:
|
|
50
|
+
group_id = -id_int # Remove the negative sign
|
|
51
|
+
return PeerChat(group_id)
|
|
52
|
+
|
|
53
|
+
# User/Bot: positive number
|
|
54
|
+
else:
|
|
55
|
+
return PeerUser(id_int)
|
|
56
|
+
|
|
57
|
+
except ValueError as e:
|
|
58
|
+
raise ValueError(f"Invalid chat_id format: {chat_id}. Error: {str(e)}")
|
|
59
|
+
|
|
60
|
+
async def _initialize_entity(self, client: "TelegramClient", chat_id: str) -> Any: # type: ignore[no-any-unimported]
|
|
61
|
+
"""Initialize and cache the entity by trying different methods."""
|
|
62
|
+
peer = self._get_peer_from_id(chat_id)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
# Try direct entity resolution first
|
|
66
|
+
entity = await client.get_entity(peer)
|
|
67
|
+
return entity
|
|
68
|
+
except ValueError:
|
|
69
|
+
try:
|
|
70
|
+
# Get all dialogs (conversations)
|
|
71
|
+
async for dialog in client.iter_dialogs():
|
|
72
|
+
# For users/bots, we need to find the dialog with the user
|
|
73
|
+
if (
|
|
74
|
+
isinstance(peer, PeerUser)
|
|
75
|
+
and dialog.entity.id == peer.user_id
|
|
76
|
+
or dialog.entity.id == getattr(peer, "channel_id", getattr(peer, "chat_id", None))
|
|
77
|
+
):
|
|
78
|
+
return dialog.entity
|
|
79
|
+
|
|
80
|
+
# If we get here, we didn't find the entity in dialogs
|
|
81
|
+
raise ValueError(f"Could not find entity {chat_id} in dialogs")
|
|
82
|
+
except Exception as e:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Could not initialize entity for {chat_id}. "
|
|
85
|
+
f"Make sure you have access to this chat. Error: {str(e)}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@require_optional_import(["telethon"], "commsagent-telegram")
|
|
90
|
+
@export_module("autogen.tools.experimental")
|
|
91
|
+
class TelegramSendTool(BaseTelegramTool, Tool):
|
|
92
|
+
"""Sends a message to a Telegram channel, group, or user."""
|
|
93
|
+
|
|
94
|
+
def __init__(self, *, api_id: str, api_hash: str, chat_id: str) -> None:
|
|
95
|
+
"""Initialize the TelegramSendTool.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
api_id: Telegram API ID from https://my.telegram.org/apps.
|
|
99
|
+
api_hash: Telegram API hash from https://my.telegram.org/apps.
|
|
100
|
+
chat_id: The ID of the destination (Channel, Group, or User ID).
|
|
101
|
+
"""
|
|
102
|
+
BaseTelegramTool.__init__(self, api_id, api_hash, "telegram_send_session")
|
|
103
|
+
|
|
104
|
+
async def telegram_send_message(
|
|
105
|
+
message: Annotated[str, "Message to send to the chat."],
|
|
106
|
+
chat_id: Annotated[str, Depends(on(chat_id))],
|
|
107
|
+
) -> Any:
|
|
108
|
+
"""Sends a message to a Telegram chat.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
message: The message to send.
|
|
112
|
+
chat_id: The ID of the destination. (uses dependency injection)
|
|
113
|
+
"""
|
|
114
|
+
try:
|
|
115
|
+
client = self._get_client()
|
|
116
|
+
async with client:
|
|
117
|
+
# Initialize and cache the entity
|
|
118
|
+
entity = await self._initialize_entity(client, chat_id)
|
|
119
|
+
|
|
120
|
+
if len(message) > MAX_MESSAGE_LENGTH:
|
|
121
|
+
chunks = [
|
|
122
|
+
message[i : i + (MAX_MESSAGE_LENGTH - 1)]
|
|
123
|
+
for i in range(0, len(message), (MAX_MESSAGE_LENGTH - 1))
|
|
124
|
+
]
|
|
125
|
+
first_message: Message | None = None # type: ignore[no-any-unimported]
|
|
126
|
+
|
|
127
|
+
for i, chunk in enumerate(chunks):
|
|
128
|
+
sent = await client.send_message(
|
|
129
|
+
entity=entity,
|
|
130
|
+
message=chunk,
|
|
131
|
+
parse_mode="html",
|
|
132
|
+
reply_to=first_message.id if first_message else None,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Store the first message to chain replies
|
|
136
|
+
if i == 0:
|
|
137
|
+
first_message = sent
|
|
138
|
+
sent_message_id = str(sent.id)
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
f"Message sent successfully ({len(chunks)} chunks, first ID: {sent_message_id}):\n{message}"
|
|
142
|
+
)
|
|
143
|
+
else:
|
|
144
|
+
sent = await client.send_message(entity=entity, message=message, parse_mode="html")
|
|
145
|
+
return f"Message sent successfully (ID: {sent.id}):\n{message}"
|
|
146
|
+
|
|
147
|
+
except Exception as e:
|
|
148
|
+
return f"Message send failed, exception: {str(e)}"
|
|
149
|
+
|
|
150
|
+
Tool.__init__(
|
|
151
|
+
self,
|
|
152
|
+
name="telegram_send",
|
|
153
|
+
description="Sends a message to a personal channel, bot channel, group, or channel.",
|
|
154
|
+
func_or_tool=telegram_send_message,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@require_optional_import(["telethon"], "commsagent-telegram")
|
|
159
|
+
@export_module("autogen.tools.experimental")
|
|
160
|
+
class TelegramRetrieveTool(BaseTelegramTool, Tool):
|
|
161
|
+
"""Retrieves messages from a Telegram channel."""
|
|
162
|
+
|
|
163
|
+
def __init__(self, *, api_id: str, api_hash: str, chat_id: str) -> None:
|
|
164
|
+
"""Initialize the TelegramRetrieveTool.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
api_id: Telegram API ID from https://my.telegram.org/apps.
|
|
168
|
+
api_hash: Telegram API hash from https://my.telegram.org/apps.
|
|
169
|
+
chat_id: The ID of the chat to retrieve messages from (Channel, Group, Bot Chat ID).
|
|
170
|
+
"""
|
|
171
|
+
BaseTelegramTool.__init__(self, api_id, api_hash, "telegram_retrieve_session")
|
|
172
|
+
self._chat_id = chat_id
|
|
173
|
+
|
|
174
|
+
async def telegram_retrieve_messages(
|
|
175
|
+
chat_id: Annotated[str, Depends(on(chat_id))],
|
|
176
|
+
messages_since: Annotated[
|
|
177
|
+
str | None,
|
|
178
|
+
"Date to retrieve messages from (ISO format) OR message ID. If None, retrieves latest messages.",
|
|
179
|
+
] = None,
|
|
180
|
+
maximum_messages: Annotated[
|
|
181
|
+
int | None, "Maximum number of messages to retrieve. If None, retrieves all messages since date."
|
|
182
|
+
] = None,
|
|
183
|
+
search: Annotated[str | None, "Optional string to search for in messages."] = None,
|
|
184
|
+
) -> Any:
|
|
185
|
+
"""Retrieves messages from a Telegram chat.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
chat_id: The ID of the chat. (uses dependency injection)
|
|
189
|
+
messages_since: ISO format date string OR message ID to retrieve messages from.
|
|
190
|
+
maximum_messages: Maximum number of messages to retrieve.
|
|
191
|
+
search: Optional string to search for in messages.
|
|
192
|
+
"""
|
|
193
|
+
try:
|
|
194
|
+
client = self._get_client()
|
|
195
|
+
async with client:
|
|
196
|
+
# Initialize and cache the entity
|
|
197
|
+
entity = await self._initialize_entity(client, chat_id)
|
|
198
|
+
|
|
199
|
+
# Setup retrieval parameters
|
|
200
|
+
params = {
|
|
201
|
+
"entity": entity,
|
|
202
|
+
"limit": maximum_messages if maximum_messages else None,
|
|
203
|
+
"search": search if search else None,
|
|
204
|
+
"filter": InputMessagesFilterEmpty(),
|
|
205
|
+
"wait_time": None, # No wait time between requests
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# Handle messages_since parameter
|
|
209
|
+
if messages_since:
|
|
210
|
+
try:
|
|
211
|
+
# Try to parse as message ID first
|
|
212
|
+
msg_id = int(messages_since)
|
|
213
|
+
params["min_id"] = msg_id
|
|
214
|
+
except ValueError:
|
|
215
|
+
# Not a message ID, try as ISO date
|
|
216
|
+
try:
|
|
217
|
+
date = datetime.fromisoformat(messages_since.replace("Z", "+00:00"))
|
|
218
|
+
params["offset_date"] = date
|
|
219
|
+
params["reverse"] = (
|
|
220
|
+
True # Need this because the date gets messages before a certain date by default
|
|
221
|
+
)
|
|
222
|
+
except ValueError:
|
|
223
|
+
return {
|
|
224
|
+
"error": "Invalid messages_since format. Please provide either a message ID or ISO format date (e.g., '2025-01-25T00:00:00Z')"
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Retrieve messages
|
|
228
|
+
messages = []
|
|
229
|
+
count = 0
|
|
230
|
+
# For bot users, we need to get both sent and received messages
|
|
231
|
+
if isinstance(self._get_peer_from_id(chat_id), PeerUser):
|
|
232
|
+
print(f"Retrieving messages for bot chat {chat_id}")
|
|
233
|
+
|
|
234
|
+
async for message in client.iter_messages(**params):
|
|
235
|
+
count += 1
|
|
236
|
+
messages.append({
|
|
237
|
+
"id": str(message.id),
|
|
238
|
+
"date": message.date.isoformat(),
|
|
239
|
+
"from_id": str(message.from_id) if message.from_id else None,
|
|
240
|
+
"text": message.text,
|
|
241
|
+
"reply_to_msg_id": str(message.reply_to_msg_id) if message.reply_to_msg_id else None,
|
|
242
|
+
"forward_from": str(message.forward.from_id) if message.forward else None,
|
|
243
|
+
"edit_date": message.edit_date.isoformat() if message.edit_date else None,
|
|
244
|
+
"media": bool(message.media),
|
|
245
|
+
"entities": [
|
|
246
|
+
{"type": e.__class__.__name__, "offset": e.offset, "length": e.length}
|
|
247
|
+
for e in message.entities
|
|
248
|
+
]
|
|
249
|
+
if message.entities
|
|
250
|
+
else None,
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
# Check if we've hit the maximum
|
|
254
|
+
if maximum_messages and len(messages) >= maximum_messages:
|
|
255
|
+
break
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
"message_count": len(messages),
|
|
259
|
+
"messages": messages,
|
|
260
|
+
"start_time": messages_since or "latest",
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
return f"Message retrieval failed, exception: {str(e)}"
|
|
265
|
+
|
|
266
|
+
Tool.__init__(
|
|
267
|
+
self,
|
|
268
|
+
name="telegram_retrieve",
|
|
269
|
+
description="Retrieves messages from a Telegram chat based on datetime/message ID and/or number of latest messages.",
|
|
270
|
+
func_or_tool=telegram_retrieve_messages,
|
|
271
|
+
)
|