ag2 0.9.1a1__py3-none-any.whl → 0.9.1.post0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ag2 might be problematic. Click here for more details.
- {ag2-0.9.1a1.dist-info → ag2-0.9.1.post0.dist-info}/METADATA +264 -73
- ag2-0.9.1.post0.dist-info/RECORD +392 -0
- {ag2-0.9.1a1.dist-info → ag2-0.9.1.post0.dist-info}/WHEEL +1 -2
- autogen/__init__.py +89 -0
- autogen/_website/__init__.py +3 -0
- autogen/_website/generate_api_references.py +427 -0
- autogen/_website/generate_mkdocs.py +1174 -0
- autogen/_website/notebook_processor.py +476 -0
- autogen/_website/process_notebooks.py +656 -0
- autogen/_website/utils.py +412 -0
- autogen/agentchat/__init__.py +44 -0
- autogen/agentchat/agent.py +182 -0
- autogen/agentchat/assistant_agent.py +85 -0
- autogen/agentchat/chat.py +309 -0
- autogen/agentchat/contrib/__init__.py +5 -0
- autogen/agentchat/contrib/agent_eval/README.md +7 -0
- autogen/agentchat/contrib/agent_eval/agent_eval.py +108 -0
- autogen/agentchat/contrib/agent_eval/criterion.py +43 -0
- autogen/agentchat/contrib/agent_eval/critic_agent.py +44 -0
- autogen/agentchat/contrib/agent_eval/quantifier_agent.py +39 -0
- autogen/agentchat/contrib/agent_eval/subcritic_agent.py +45 -0
- autogen/agentchat/contrib/agent_eval/task.py +42 -0
- autogen/agentchat/contrib/agent_optimizer.py +429 -0
- autogen/agentchat/contrib/capabilities/__init__.py +5 -0
- autogen/agentchat/contrib/capabilities/agent_capability.py +20 -0
- autogen/agentchat/contrib/capabilities/generate_images.py +301 -0
- autogen/agentchat/contrib/capabilities/teachability.py +393 -0
- autogen/agentchat/contrib/capabilities/text_compressors.py +66 -0
- autogen/agentchat/contrib/capabilities/tools_capability.py +22 -0
- autogen/agentchat/contrib/capabilities/transform_messages.py +93 -0
- autogen/agentchat/contrib/capabilities/transforms.py +566 -0
- autogen/agentchat/contrib/capabilities/transforms_util.py +122 -0
- autogen/agentchat/contrib/capabilities/vision_capability.py +214 -0
- autogen/agentchat/contrib/captainagent/__init__.py +9 -0
- autogen/agentchat/contrib/captainagent/agent_builder.py +790 -0
- autogen/agentchat/contrib/captainagent/captainagent.py +512 -0
- autogen/agentchat/contrib/captainagent/tool_retriever.py +335 -0
- autogen/agentchat/contrib/captainagent/tools/README.md +44 -0
- autogen/agentchat/contrib/captainagent/tools/__init__.py +5 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/calculate_correlation.py +40 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/calculate_skewness_and_kurtosis.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/detect_outlier_iqr.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/detect_outlier_zscore.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/explore_csv.py +21 -0
- autogen/agentchat/contrib/captainagent/tools/data_analysis/shapiro_wilk_test.py +30 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/arxiv_download.py +27 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/arxiv_search.py +53 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/extract_pdf_image.py +53 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/extract_pdf_text.py +38 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/get_wikipedia_text.py +21 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/get_youtube_caption.py +34 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/image_qa.py +60 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/optical_character_recognition.py +61 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/perform_web_search.py +47 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/scrape_wikipedia_tables.py +33 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/transcribe_audio_file.py +21 -0
- autogen/agentchat/contrib/captainagent/tools/information_retrieval/youtube_download.py +35 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_circle_area_from_diameter.py +21 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_day_of_the_week.py +18 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_fraction_sum.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_matrix_power.py +31 -0
- autogen/agentchat/contrib/captainagent/tools/math/calculate_reflected_point.py +16 -0
- autogen/agentchat/contrib/captainagent/tools/math/complex_numbers_product.py +25 -0
- autogen/agentchat/contrib/captainagent/tools/math/compute_currency_conversion.py +23 -0
- autogen/agentchat/contrib/captainagent/tools/math/count_distinct_permutations.py +27 -0
- autogen/agentchat/contrib/captainagent/tools/math/evaluate_expression.py +28 -0
- autogen/agentchat/contrib/captainagent/tools/math/find_continuity_point.py +34 -0
- autogen/agentchat/contrib/captainagent/tools/math/fraction_to_mixed_numbers.py +39 -0
- autogen/agentchat/contrib/captainagent/tools/math/modular_inverse_sum.py +23 -0
- autogen/agentchat/contrib/captainagent/tools/math/simplify_mixed_numbers.py +36 -0
- autogen/agentchat/contrib/captainagent/tools/math/sum_of_digit_factorials.py +15 -0
- autogen/agentchat/contrib/captainagent/tools/math/sum_of_primes_below.py +15 -0
- autogen/agentchat/contrib/captainagent/tools/requirements.txt +10 -0
- autogen/agentchat/contrib/captainagent/tools/tool_description.tsv +34 -0
- autogen/agentchat/contrib/gpt_assistant_agent.py +526 -0
- autogen/agentchat/contrib/graph_rag/__init__.py +9 -0
- autogen/agentchat/contrib/graph_rag/document.py +29 -0
- autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py +170 -0
- autogen/agentchat/contrib/graph_rag/falkor_graph_rag_capability.py +103 -0
- autogen/agentchat/contrib/graph_rag/graph_query_engine.py +53 -0
- autogen/agentchat/contrib/graph_rag/graph_rag_capability.py +63 -0
- autogen/agentchat/contrib/graph_rag/neo4j_graph_query_engine.py +268 -0
- autogen/agentchat/contrib/graph_rag/neo4j_graph_rag_capability.py +83 -0
- autogen/agentchat/contrib/graph_rag/neo4j_native_graph_query_engine.py +210 -0
- autogen/agentchat/contrib/graph_rag/neo4j_native_graph_rag_capability.py +93 -0
- autogen/agentchat/contrib/img_utils.py +397 -0
- autogen/agentchat/contrib/llamaindex_conversable_agent.py +117 -0
- autogen/agentchat/contrib/llava_agent.py +187 -0
- autogen/agentchat/contrib/math_user_proxy_agent.py +464 -0
- autogen/agentchat/contrib/multimodal_conversable_agent.py +125 -0
- autogen/agentchat/contrib/qdrant_retrieve_user_proxy_agent.py +324 -0
- autogen/agentchat/contrib/rag/__init__.py +10 -0
- autogen/agentchat/contrib/rag/chromadb_query_engine.py +272 -0
- autogen/agentchat/contrib/rag/llamaindex_query_engine.py +198 -0
- autogen/agentchat/contrib/rag/mongodb_query_engine.py +329 -0
- autogen/agentchat/contrib/rag/query_engine.py +74 -0
- autogen/agentchat/contrib/retrieve_assistant_agent.py +56 -0
- autogen/agentchat/contrib/retrieve_user_proxy_agent.py +703 -0
- autogen/agentchat/contrib/society_of_mind_agent.py +199 -0
- autogen/agentchat/contrib/swarm_agent.py +1425 -0
- autogen/agentchat/contrib/text_analyzer_agent.py +79 -0
- autogen/agentchat/contrib/vectordb/__init__.py +5 -0
- autogen/agentchat/contrib/vectordb/base.py +232 -0
- autogen/agentchat/contrib/vectordb/chromadb.py +315 -0
- autogen/agentchat/contrib/vectordb/couchbase.py +407 -0
- autogen/agentchat/contrib/vectordb/mongodb.py +550 -0
- autogen/agentchat/contrib/vectordb/pgvectordb.py +928 -0
- autogen/agentchat/contrib/vectordb/qdrant.py +320 -0
- autogen/agentchat/contrib/vectordb/utils.py +126 -0
- autogen/agentchat/contrib/web_surfer.py +303 -0
- autogen/agentchat/conversable_agent.py +4020 -0
- autogen/agentchat/group/__init__.py +64 -0
- autogen/agentchat/group/available_condition.py +91 -0
- autogen/agentchat/group/context_condition.py +77 -0
- autogen/agentchat/group/context_expression.py +238 -0
- autogen/agentchat/group/context_str.py +41 -0
- autogen/agentchat/group/context_variables.py +192 -0
- autogen/agentchat/group/group_tool_executor.py +202 -0
- autogen/agentchat/group/group_utils.py +591 -0
- autogen/agentchat/group/handoffs.py +244 -0
- autogen/agentchat/group/llm_condition.py +93 -0
- autogen/agentchat/group/multi_agent_chat.py +237 -0
- autogen/agentchat/group/on_condition.py +58 -0
- autogen/agentchat/group/on_context_condition.py +54 -0
- autogen/agentchat/group/patterns/__init__.py +18 -0
- autogen/agentchat/group/patterns/auto.py +159 -0
- autogen/agentchat/group/patterns/manual.py +176 -0
- autogen/agentchat/group/patterns/pattern.py +288 -0
- autogen/agentchat/group/patterns/random.py +106 -0
- autogen/agentchat/group/patterns/round_robin.py +117 -0
- autogen/agentchat/group/reply_result.py +26 -0
- autogen/agentchat/group/speaker_selection_result.py +41 -0
- autogen/agentchat/group/targets/__init__.py +4 -0
- autogen/agentchat/group/targets/group_chat_target.py +132 -0
- autogen/agentchat/group/targets/group_manager_target.py +151 -0
- autogen/agentchat/group/targets/transition_target.py +413 -0
- autogen/agentchat/group/targets/transition_utils.py +6 -0
- autogen/agentchat/groupchat.py +1694 -0
- autogen/agentchat/realtime/__init__.py +3 -0
- autogen/agentchat/realtime/experimental/__init__.py +20 -0
- autogen/agentchat/realtime/experimental/audio_adapters/__init__.py +8 -0
- autogen/agentchat/realtime/experimental/audio_adapters/twilio_audio_adapter.py +148 -0
- autogen/agentchat/realtime/experimental/audio_adapters/websocket_audio_adapter.py +139 -0
- autogen/agentchat/realtime/experimental/audio_observer.py +42 -0
- autogen/agentchat/realtime/experimental/clients/__init__.py +15 -0
- autogen/agentchat/realtime/experimental/clients/gemini/__init__.py +7 -0
- autogen/agentchat/realtime/experimental/clients/gemini/client.py +274 -0
- autogen/agentchat/realtime/experimental/clients/oai/__init__.py +8 -0
- autogen/agentchat/realtime/experimental/clients/oai/base_client.py +220 -0
- autogen/agentchat/realtime/experimental/clients/oai/rtc_client.py +243 -0
- autogen/agentchat/realtime/experimental/clients/oai/utils.py +48 -0
- autogen/agentchat/realtime/experimental/clients/realtime_client.py +190 -0
- autogen/agentchat/realtime/experimental/function_observer.py +85 -0
- autogen/agentchat/realtime/experimental/realtime_agent.py +158 -0
- autogen/agentchat/realtime/experimental/realtime_events.py +42 -0
- autogen/agentchat/realtime/experimental/realtime_observer.py +100 -0
- autogen/agentchat/realtime/experimental/realtime_swarm.py +475 -0
- autogen/agentchat/realtime/experimental/websockets.py +21 -0
- autogen/agentchat/realtime_agent/__init__.py +21 -0
- autogen/agentchat/user_proxy_agent.py +111 -0
- autogen/agentchat/utils.py +206 -0
- autogen/agents/__init__.py +3 -0
- autogen/agents/contrib/__init__.py +10 -0
- autogen/agents/contrib/time/__init__.py +8 -0
- autogen/agents/contrib/time/time_reply_agent.py +73 -0
- autogen/agents/contrib/time/time_tool_agent.py +51 -0
- autogen/agents/experimental/__init__.py +27 -0
- autogen/agents/experimental/deep_research/__init__.py +7 -0
- autogen/agents/experimental/deep_research/deep_research.py +52 -0
- autogen/agents/experimental/discord/__init__.py +7 -0
- autogen/agents/experimental/discord/discord.py +66 -0
- autogen/agents/experimental/document_agent/__init__.py +19 -0
- autogen/agents/experimental/document_agent/chroma_query_engine.py +316 -0
- autogen/agents/experimental/document_agent/docling_doc_ingest_agent.py +118 -0
- autogen/agents/experimental/document_agent/document_agent.py +461 -0
- autogen/agents/experimental/document_agent/document_conditions.py +50 -0
- autogen/agents/experimental/document_agent/document_utils.py +380 -0
- autogen/agents/experimental/document_agent/inmemory_query_engine.py +220 -0
- autogen/agents/experimental/document_agent/parser_utils.py +130 -0
- autogen/agents/experimental/document_agent/url_utils.py +426 -0
- autogen/agents/experimental/reasoning/__init__.py +7 -0
- autogen/agents/experimental/reasoning/reasoning_agent.py +1178 -0
- autogen/agents/experimental/slack/__init__.py +7 -0
- autogen/agents/experimental/slack/slack.py +73 -0
- autogen/agents/experimental/telegram/__init__.py +7 -0
- autogen/agents/experimental/telegram/telegram.py +77 -0
- autogen/agents/experimental/websurfer/__init__.py +7 -0
- autogen/agents/experimental/websurfer/websurfer.py +62 -0
- autogen/agents/experimental/wikipedia/__init__.py +7 -0
- autogen/agents/experimental/wikipedia/wikipedia.py +90 -0
- autogen/browser_utils.py +309 -0
- autogen/cache/__init__.py +10 -0
- autogen/cache/abstract_cache_base.py +75 -0
- autogen/cache/cache.py +203 -0
- autogen/cache/cache_factory.py +88 -0
- autogen/cache/cosmos_db_cache.py +144 -0
- autogen/cache/disk_cache.py +102 -0
- autogen/cache/in_memory_cache.py +58 -0
- autogen/cache/redis_cache.py +123 -0
- autogen/code_utils.py +596 -0
- autogen/coding/__init__.py +22 -0
- autogen/coding/base.py +119 -0
- autogen/coding/docker_commandline_code_executor.py +268 -0
- autogen/coding/factory.py +47 -0
- autogen/coding/func_with_reqs.py +202 -0
- autogen/coding/jupyter/__init__.py +23 -0
- autogen/coding/jupyter/base.py +36 -0
- autogen/coding/jupyter/docker_jupyter_server.py +167 -0
- autogen/coding/jupyter/embedded_ipython_code_executor.py +182 -0
- autogen/coding/jupyter/import_utils.py +82 -0
- autogen/coding/jupyter/jupyter_client.py +231 -0
- autogen/coding/jupyter/jupyter_code_executor.py +160 -0
- autogen/coding/jupyter/local_jupyter_server.py +172 -0
- autogen/coding/local_commandline_code_executor.py +405 -0
- autogen/coding/markdown_code_extractor.py +45 -0
- autogen/coding/utils.py +56 -0
- autogen/doc_utils.py +34 -0
- autogen/events/__init__.py +7 -0
- autogen/events/agent_events.py +1010 -0
- autogen/events/base_event.py +99 -0
- autogen/events/client_events.py +167 -0
- autogen/events/helpers.py +36 -0
- autogen/events/print_event.py +46 -0
- autogen/exception_utils.py +73 -0
- autogen/extensions/__init__.py +5 -0
- autogen/fast_depends/__init__.py +16 -0
- autogen/fast_depends/_compat.py +80 -0
- autogen/fast_depends/core/__init__.py +14 -0
- autogen/fast_depends/core/build.py +225 -0
- autogen/fast_depends/core/model.py +576 -0
- autogen/fast_depends/dependencies/__init__.py +15 -0
- autogen/fast_depends/dependencies/model.py +29 -0
- autogen/fast_depends/dependencies/provider.py +39 -0
- autogen/fast_depends/library/__init__.py +10 -0
- autogen/fast_depends/library/model.py +46 -0
- autogen/fast_depends/py.typed +6 -0
- autogen/fast_depends/schema.py +66 -0
- autogen/fast_depends/use.py +280 -0
- autogen/fast_depends/utils.py +187 -0
- autogen/formatting_utils.py +83 -0
- autogen/function_utils.py +13 -0
- autogen/graph_utils.py +178 -0
- autogen/import_utils.py +526 -0
- autogen/interop/__init__.py +22 -0
- autogen/interop/crewai/__init__.py +7 -0
- autogen/interop/crewai/crewai.py +88 -0
- autogen/interop/interoperability.py +71 -0
- autogen/interop/interoperable.py +46 -0
- autogen/interop/langchain/__init__.py +8 -0
- autogen/interop/langchain/langchain_chat_model_factory.py +155 -0
- autogen/interop/langchain/langchain_tool.py +82 -0
- autogen/interop/litellm/__init__.py +7 -0
- autogen/interop/litellm/litellm_config_factory.py +113 -0
- autogen/interop/pydantic_ai/__init__.py +7 -0
- autogen/interop/pydantic_ai/pydantic_ai.py +168 -0
- autogen/interop/registry.py +69 -0
- autogen/io/__init__.py +15 -0
- autogen/io/base.py +151 -0
- autogen/io/console.py +56 -0
- autogen/io/processors/__init__.py +12 -0
- autogen/io/processors/base.py +21 -0
- autogen/io/processors/console_event_processor.py +56 -0
- autogen/io/run_response.py +293 -0
- autogen/io/thread_io_stream.py +63 -0
- autogen/io/websockets.py +213 -0
- autogen/json_utils.py +43 -0
- autogen/llm_config.py +379 -0
- autogen/logger/__init__.py +11 -0
- autogen/logger/base_logger.py +128 -0
- autogen/logger/file_logger.py +261 -0
- autogen/logger/logger_factory.py +42 -0
- autogen/logger/logger_utils.py +57 -0
- autogen/logger/sqlite_logger.py +523 -0
- autogen/math_utils.py +339 -0
- autogen/mcp/__init__.py +7 -0
- autogen/mcp/mcp_client.py +208 -0
- autogen/messages/__init__.py +7 -0
- autogen/messages/agent_messages.py +948 -0
- autogen/messages/base_message.py +107 -0
- autogen/messages/client_messages.py +171 -0
- autogen/messages/print_message.py +49 -0
- autogen/oai/__init__.py +53 -0
- autogen/oai/anthropic.py +714 -0
- autogen/oai/bedrock.py +628 -0
- autogen/oai/cerebras.py +299 -0
- autogen/oai/client.py +1435 -0
- autogen/oai/client_utils.py +169 -0
- autogen/oai/cohere.py +479 -0
- autogen/oai/gemini.py +990 -0
- autogen/oai/gemini_types.py +129 -0
- autogen/oai/groq.py +305 -0
- autogen/oai/mistral.py +303 -0
- autogen/oai/oai_models/__init__.py +11 -0
- autogen/oai/oai_models/_models.py +16 -0
- autogen/oai/oai_models/chat_completion.py +87 -0
- autogen/oai/oai_models/chat_completion_audio.py +32 -0
- autogen/oai/oai_models/chat_completion_message.py +86 -0
- autogen/oai/oai_models/chat_completion_message_tool_call.py +37 -0
- autogen/oai/oai_models/chat_completion_token_logprob.py +63 -0
- autogen/oai/oai_models/completion_usage.py +60 -0
- autogen/oai/ollama.py +643 -0
- autogen/oai/openai_utils.py +881 -0
- autogen/oai/together.py +370 -0
- autogen/retrieve_utils.py +491 -0
- autogen/runtime_logging.py +160 -0
- autogen/token_count_utils.py +267 -0
- autogen/tools/__init__.py +20 -0
- autogen/tools/contrib/__init__.py +9 -0
- autogen/tools/contrib/time/__init__.py +7 -0
- autogen/tools/contrib/time/time.py +41 -0
- autogen/tools/dependency_injection.py +254 -0
- autogen/tools/experimental/__init__.py +43 -0
- autogen/tools/experimental/browser_use/__init__.py +7 -0
- autogen/tools/experimental/browser_use/browser_use.py +161 -0
- autogen/tools/experimental/crawl4ai/__init__.py +7 -0
- autogen/tools/experimental/crawl4ai/crawl4ai.py +153 -0
- autogen/tools/experimental/deep_research/__init__.py +7 -0
- autogen/tools/experimental/deep_research/deep_research.py +328 -0
- autogen/tools/experimental/duckduckgo/__init__.py +7 -0
- autogen/tools/experimental/duckduckgo/duckduckgo_search.py +109 -0
- autogen/tools/experimental/google/__init__.py +14 -0
- autogen/tools/experimental/google/authentication/__init__.py +11 -0
- autogen/tools/experimental/google/authentication/credentials_hosted_provider.py +43 -0
- autogen/tools/experimental/google/authentication/credentials_local_provider.py +91 -0
- autogen/tools/experimental/google/authentication/credentials_provider.py +35 -0
- autogen/tools/experimental/google/drive/__init__.py +9 -0
- autogen/tools/experimental/google/drive/drive_functions.py +124 -0
- autogen/tools/experimental/google/drive/toolkit.py +88 -0
- autogen/tools/experimental/google/model.py +17 -0
- autogen/tools/experimental/google/toolkit_protocol.py +19 -0
- autogen/tools/experimental/google_search/__init__.py +8 -0
- autogen/tools/experimental/google_search/google_search.py +93 -0
- autogen/tools/experimental/google_search/youtube_search.py +181 -0
- autogen/tools/experimental/messageplatform/__init__.py +17 -0
- autogen/tools/experimental/messageplatform/discord/__init__.py +7 -0
- autogen/tools/experimental/messageplatform/discord/discord.py +288 -0
- autogen/tools/experimental/messageplatform/slack/__init__.py +7 -0
- autogen/tools/experimental/messageplatform/slack/slack.py +391 -0
- autogen/tools/experimental/messageplatform/telegram/__init__.py +7 -0
- autogen/tools/experimental/messageplatform/telegram/telegram.py +275 -0
- autogen/tools/experimental/perplexity/__init__.py +7 -0
- autogen/tools/experimental/perplexity/perplexity_search.py +260 -0
- autogen/tools/experimental/tavily/__init__.py +7 -0
- autogen/tools/experimental/tavily/tavily_search.py +183 -0
- autogen/tools/experimental/web_search_preview/__init__.py +7 -0
- autogen/tools/experimental/web_search_preview/web_search_preview.py +114 -0
- autogen/tools/experimental/wikipedia/__init__.py +7 -0
- autogen/tools/experimental/wikipedia/wikipedia.py +287 -0
- autogen/tools/function_utils.py +411 -0
- autogen/tools/tool.py +187 -0
- autogen/tools/toolkit.py +86 -0
- autogen/types.py +29 -0
- autogen/version.py +7 -0
- ag2-0.9.1a1.dist-info/RECORD +0 -6
- ag2-0.9.1a1.dist-info/top_level.txt +0 -1
- {ag2-0.9.1a1.dist-info → ag2-0.9.1.post0.dist-info/licenses}/LICENSE +0 -0
- {ag2-0.9.1a1.dist-info → ag2-0.9.1.post0.dist-info/licenses}/NOTICE.md +0 -0
|
@@ -0,0 +1,591 @@
|
|
|
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 copy
|
|
6
|
+
from functools import partial
|
|
7
|
+
from types import MethodType
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
|
9
|
+
|
|
10
|
+
from ..agent import Agent
|
|
11
|
+
from ..groupchat import GroupChat, GroupChatManager
|
|
12
|
+
from .context_variables import ContextVariables
|
|
13
|
+
from .group_tool_executor import GroupToolExecutor
|
|
14
|
+
from .targets.group_manager_target import GroupManagerTarget
|
|
15
|
+
from .targets.transition_target import (
|
|
16
|
+
AgentNameTarget,
|
|
17
|
+
AgentTarget,
|
|
18
|
+
TransitionTarget,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from ..conversable_agent import ConversableAgent
|
|
23
|
+
|
|
24
|
+
# Utility functions for group chat preparation and management
|
|
25
|
+
# These are extracted from multi_agent_chat.py to avoid circular imports
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def update_conditional_functions(agent: "ConversableAgent", messages: list[dict[str, Any]]) -> None:
|
|
29
|
+
"""Updates the agent's functions based on the OnCondition's available condition.
|
|
30
|
+
|
|
31
|
+
All functions are removed and then added back if they are available
|
|
32
|
+
"""
|
|
33
|
+
for on_condition in agent.handoffs.llm_conditions:
|
|
34
|
+
is_available = on_condition.available.is_available(agent, messages) if on_condition.available else True
|
|
35
|
+
|
|
36
|
+
# Remove it from their tools
|
|
37
|
+
for tool in agent.tools:
|
|
38
|
+
if tool.name == on_condition.llm_function_name:
|
|
39
|
+
agent.remove_tool_for_llm(tool)
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
# then add the function if it is available, so that the function signature is updated
|
|
43
|
+
if is_available:
|
|
44
|
+
agent._add_single_function(
|
|
45
|
+
_create_on_condition_handoff_function(on_condition.target),
|
|
46
|
+
on_condition.llm_function_name,
|
|
47
|
+
on_condition.condition.get_prompt(agent, messages),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def establish_group_agent(agent: "ConversableAgent") -> None:
|
|
52
|
+
"""Establish the group agent with the group-related attributes and hooks. Not for the tool executor.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
agent ("ConversableAgent"): The agent to establish as a group agent.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def _group_agent_str(self: "ConversableAgent") -> str:
|
|
59
|
+
"""Customise the __str__ method to show the agent name for transition messages."""
|
|
60
|
+
return f"Group agent --> {self.name}"
|
|
61
|
+
|
|
62
|
+
# Register the hook to update agent state (except tool executor)
|
|
63
|
+
agent.register_hook("update_agent_state", update_conditional_functions)
|
|
64
|
+
|
|
65
|
+
# Register a reply function to run Python function-based OnContextConditions before any other reply function
|
|
66
|
+
agent.register_reply(trigger=([Agent, None]), reply_func=_run_oncontextconditions, position=0)
|
|
67
|
+
|
|
68
|
+
agent._get_display_name = MethodType(_group_agent_str, agent) # type: ignore[method-assign]
|
|
69
|
+
|
|
70
|
+
# Mark this agent as established as a group agent
|
|
71
|
+
agent._group_is_established = True # type: ignore[attr-defined]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def link_agents_to_group_manager(agents: list[Agent], group_chat_manager: Agent) -> None:
|
|
75
|
+
"""Link all agents to the GroupChatManager so they can access the underlying GroupChat and other agents.
|
|
76
|
+
|
|
77
|
+
This is primarily used so that agents can get to the tool executor to help set the next agent.
|
|
78
|
+
|
|
79
|
+
Does not link the Tool Executor agent.
|
|
80
|
+
"""
|
|
81
|
+
for agent in agents:
|
|
82
|
+
agent._group_manager = group_chat_manager # type: ignore[attr-defined]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _run_oncontextconditions(
|
|
86
|
+
agent: "ConversableAgent",
|
|
87
|
+
messages: Optional[list[dict[str, Any]]] = None,
|
|
88
|
+
sender: Optional[Agent] = None,
|
|
89
|
+
config: Optional[Any] = None,
|
|
90
|
+
) -> tuple[bool, Optional[Union[str, dict[str, Any]]]]:
|
|
91
|
+
"""Run OnContextConditions for an agent before any other reply function."""
|
|
92
|
+
for on_condition in agent.handoffs.context_conditions: # type: ignore[attr-defined]
|
|
93
|
+
is_available = (
|
|
94
|
+
on_condition.available.is_available(agent, messages if messages else []) if on_condition.available else True
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if is_available and on_condition.condition.evaluate(agent.context_variables):
|
|
98
|
+
# Condition has been met, we'll set the Tool Executor's next target
|
|
99
|
+
# attribute and that will be picked up on the next iteration when
|
|
100
|
+
# _determine_next_agent is called
|
|
101
|
+
for agent in agent._group_manager.groupchat.agents: # type: ignore[attr-defined]
|
|
102
|
+
if isinstance(agent, GroupToolExecutor):
|
|
103
|
+
agent.set_next_target(on_condition.target)
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
transfer_name = on_condition.target.display_name()
|
|
107
|
+
|
|
108
|
+
return True, "[Handing off to " + transfer_name + "]"
|
|
109
|
+
|
|
110
|
+
return False, None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _create_on_condition_handoff_function(target: TransitionTarget) -> Callable[[], TransitionTarget]:
|
|
114
|
+
"""Creates a function that will be used by the tool call reply function when the condition is met.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
target (TransitionTarget): The target to transfer to.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Callable: The transfer function.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def transfer_to_target() -> TransitionTarget:
|
|
124
|
+
return target
|
|
125
|
+
|
|
126
|
+
return transfer_to_target
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def create_on_condition_handoff_functions(agent: "ConversableAgent") -> None:
|
|
130
|
+
"""Creates the functions for the OnConditions so that the current tool handling works.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
agent ("ConversableAgent"): The agent to create the functions for.
|
|
134
|
+
"""
|
|
135
|
+
# Populate the function names for the handoffs
|
|
136
|
+
agent.handoffs.set_llm_function_names()
|
|
137
|
+
|
|
138
|
+
# Create a function for each OnCondition
|
|
139
|
+
for on_condition in agent.handoffs.llm_conditions:
|
|
140
|
+
# Create a function that will be called when the condition is met
|
|
141
|
+
agent._add_single_function(
|
|
142
|
+
_create_on_condition_handoff_function(on_condition.target),
|
|
143
|
+
on_condition.llm_function_name,
|
|
144
|
+
on_condition.condition.get_prompt(agent, []),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def ensure_handoff_agents_in_group(agents: list["ConversableAgent"]) -> None:
|
|
149
|
+
"""Ensure the agents in handoffs are in the group chat."""
|
|
150
|
+
agent_names = [agent.name for agent in agents]
|
|
151
|
+
for agent in agents:
|
|
152
|
+
for llm_conditions in agent.handoffs.llm_conditions:
|
|
153
|
+
if (
|
|
154
|
+
isinstance(llm_conditions.target, (AgentTarget, AgentNameTarget))
|
|
155
|
+
and llm_conditions.target.agent_name not in agent_names
|
|
156
|
+
):
|
|
157
|
+
raise ValueError("Agent in OnCondition Hand-offs must be in the agents list")
|
|
158
|
+
for context_conditions in agent.handoffs.context_conditions:
|
|
159
|
+
if (
|
|
160
|
+
isinstance(context_conditions.target, (AgentTarget, AgentNameTarget))
|
|
161
|
+
and context_conditions.target.agent_name not in agent_names
|
|
162
|
+
):
|
|
163
|
+
raise ValueError("Agent in OnContextCondition Hand-offs must be in the agents list")
|
|
164
|
+
if (
|
|
165
|
+
agent.handoffs.after_work is not None
|
|
166
|
+
and isinstance(agent.handoffs.after_work, (AgentTarget, AgentNameTarget))
|
|
167
|
+
and agent.handoffs.after_work.agent_name not in agent_names
|
|
168
|
+
):
|
|
169
|
+
raise ValueError("Agent in after work target Hand-offs must be in the agents list")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def prepare_exclude_transit_messages(agents: list["ConversableAgent"]) -> None:
|
|
173
|
+
"""Preparation for excluding transit messages by getting all tool names and registering a hook on agents to remove those messages."""
|
|
174
|
+
# get all transit functions names
|
|
175
|
+
to_be_removed: list[str] = []
|
|
176
|
+
for agent in agents:
|
|
177
|
+
for on_condition in agent.handoffs.llm_conditions:
|
|
178
|
+
if on_condition.llm_function_name:
|
|
179
|
+
to_be_removed.append(on_condition.llm_function_name)
|
|
180
|
+
else:
|
|
181
|
+
raise ValueError("OnCondition must have a function name")
|
|
182
|
+
|
|
183
|
+
remove_function = make_remove_function(to_be_removed)
|
|
184
|
+
|
|
185
|
+
# register hook to remove transit messages for group agents
|
|
186
|
+
for agent in agents:
|
|
187
|
+
agent.register_hook("process_all_messages_before_reply", remove_function)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def prepare_group_agents(
|
|
191
|
+
agents: list["ConversableAgent"],
|
|
192
|
+
context_variables: ContextVariables,
|
|
193
|
+
exclude_transit_message: bool = True,
|
|
194
|
+
) -> tuple[GroupToolExecutor, list["ConversableAgent"]]:
|
|
195
|
+
"""Validates agents, create the tool executor, wrap necessary targets in agents.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
agents (list["ConversableAgent"]): List of all agents in the conversation.
|
|
199
|
+
context_variables (ContextVariables): Context variables to assign to all agents.
|
|
200
|
+
exclude_transit_message (bool): Whether to exclude transit messages from the agents.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
"ConversableAgent": The tool executor agent.
|
|
204
|
+
list["ConversableAgent"]: List of wrapped agents.
|
|
205
|
+
"""
|
|
206
|
+
# Initialise all agents as group agents
|
|
207
|
+
for agent in agents:
|
|
208
|
+
if not hasattr(agent, "_group_is_established"):
|
|
209
|
+
establish_group_agent(agent)
|
|
210
|
+
|
|
211
|
+
# Ensure all agents in hand-off after-works are in the passed in agents list
|
|
212
|
+
ensure_handoff_agents_in_group(agents)
|
|
213
|
+
|
|
214
|
+
# Create Tool Executor for the group
|
|
215
|
+
tool_execution = GroupToolExecutor()
|
|
216
|
+
|
|
217
|
+
# Wrap handoff targets in agents that need to be wrapped
|
|
218
|
+
wrapped_chat_agents: list["ConversableAgent"] = []
|
|
219
|
+
for agent in agents:
|
|
220
|
+
wrap_agent_handoff_targets(agent, wrapped_chat_agents)
|
|
221
|
+
|
|
222
|
+
# Create the functions for the OnConditions so that the current tool handling works
|
|
223
|
+
for agent in agents:
|
|
224
|
+
create_on_condition_handoff_functions(agent)
|
|
225
|
+
|
|
226
|
+
# Register all the agents' functions with the tool executor and
|
|
227
|
+
# use dependency injection for the context variables parameter
|
|
228
|
+
# Update tool execution agent with all the functions from all the agents
|
|
229
|
+
tool_execution.register_agents_functions(agents + wrapped_chat_agents, context_variables)
|
|
230
|
+
|
|
231
|
+
if exclude_transit_message:
|
|
232
|
+
prepare_exclude_transit_messages(agents)
|
|
233
|
+
|
|
234
|
+
return tool_execution, wrapped_chat_agents
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def wrap_agent_handoff_targets(agent: "ConversableAgent", wrapped_agent_list: list["ConversableAgent"]) -> None:
|
|
238
|
+
"""Wrap handoff targets in agents that need to be wrapped to be part of the group chat.
|
|
239
|
+
|
|
240
|
+
Example is NestedChatTarget.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
agent ("ConversableAgent"): The agent to wrap the handoff targets for.
|
|
244
|
+
wrapped_agent_list (list["ConversableAgent"]): List of wrapped chat agents that will be appended to.
|
|
245
|
+
"""
|
|
246
|
+
# Wrap OnCondition targets
|
|
247
|
+
for i, handoff_oncondition_requiring_wrapping in enumerate(agent.handoffs.get_llm_conditions_requiring_wrapping()):
|
|
248
|
+
# Create wrapper agent
|
|
249
|
+
wrapper_agent = handoff_oncondition_requiring_wrapping.target.create_wrapper_agent(parent_agent=agent, index=i)
|
|
250
|
+
wrapped_agent_list.append(wrapper_agent)
|
|
251
|
+
|
|
252
|
+
# Change this handoff target to point to the newly created agent
|
|
253
|
+
handoff_oncondition_requiring_wrapping.target = AgentTarget(wrapper_agent)
|
|
254
|
+
|
|
255
|
+
for i, handoff_oncontextcondition_requiring_wrapping in enumerate(
|
|
256
|
+
agent.handoffs.get_context_conditions_requiring_wrapping()
|
|
257
|
+
):
|
|
258
|
+
# Create wrapper agent
|
|
259
|
+
wrapper_agent = handoff_oncontextcondition_requiring_wrapping.target.create_wrapper_agent(
|
|
260
|
+
parent_agent=agent, index=i
|
|
261
|
+
)
|
|
262
|
+
wrapped_agent_list.append(wrapper_agent)
|
|
263
|
+
|
|
264
|
+
# Change this handoff target to point to the newly created agent
|
|
265
|
+
handoff_oncontextcondition_requiring_wrapping.target = AgentTarget(wrapper_agent)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def process_initial_messages(
|
|
269
|
+
messages: Union[list[dict[str, Any]], str],
|
|
270
|
+
user_agent: Optional["ConversableAgent"],
|
|
271
|
+
agents: list["ConversableAgent"],
|
|
272
|
+
wrapped_agents: list["ConversableAgent"],
|
|
273
|
+
) -> tuple[list[dict[str, Any]], Optional["ConversableAgent"], list[str], list[Agent]]:
|
|
274
|
+
"""Process initial messages, validating agent names against messages, and determining the last agent to speak.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
messages: Initial messages to process.
|
|
278
|
+
user_agent: Optional user proxy agent passed in to a_/initiate_group_chat.
|
|
279
|
+
agents: Agents in the group.
|
|
280
|
+
wrapped_agents: List of wrapped agents.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
list[dict[str, Any]]: Processed message(s).
|
|
284
|
+
Agent: Last agent to speak.
|
|
285
|
+
list[str]: List of agent names.
|
|
286
|
+
list[Agent]: List of temporary user proxy agents to add to GroupChat.
|
|
287
|
+
"""
|
|
288
|
+
from ..conversable_agent import ConversableAgent # NEED SOLUTION
|
|
289
|
+
|
|
290
|
+
if isinstance(messages, str):
|
|
291
|
+
messages = [{"role": "user", "content": messages}]
|
|
292
|
+
|
|
293
|
+
group_agent_names = [agent.name for agent in agents + wrapped_agents]
|
|
294
|
+
|
|
295
|
+
# If there's only one message and there's no identified group agent
|
|
296
|
+
# Start with a user proxy agent, creating one if they haven't passed one in
|
|
297
|
+
last_agent: Optional[ConversableAgent]
|
|
298
|
+
temp_user_proxy: Optional[ConversableAgent] = None
|
|
299
|
+
temp_user_list: list[Agent] = []
|
|
300
|
+
if len(messages) == 1 and "name" not in messages[0] and not user_agent:
|
|
301
|
+
temp_user_proxy = ConversableAgent(name="_User", code_execution_config=False, human_input_mode="ALWAYS")
|
|
302
|
+
last_agent = temp_user_proxy
|
|
303
|
+
temp_user_list.append(temp_user_proxy)
|
|
304
|
+
else:
|
|
305
|
+
last_message = messages[0]
|
|
306
|
+
if "name" in last_message:
|
|
307
|
+
if last_message["name"] in group_agent_names:
|
|
308
|
+
last_agent = next(agent for agent in agents + wrapped_agents if agent.name == last_message["name"]) # type: ignore[assignment]
|
|
309
|
+
elif user_agent and last_message["name"] == user_agent.name:
|
|
310
|
+
last_agent = user_agent
|
|
311
|
+
else:
|
|
312
|
+
raise ValueError(f"Invalid group agent name in last message: {last_message['name']}")
|
|
313
|
+
else:
|
|
314
|
+
last_agent = user_agent if user_agent else temp_user_proxy
|
|
315
|
+
|
|
316
|
+
return messages, last_agent, group_agent_names, temp_user_list
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def setup_context_variables(
|
|
320
|
+
tool_execution: "ConversableAgent",
|
|
321
|
+
agents: list["ConversableAgent"],
|
|
322
|
+
manager: GroupChatManager,
|
|
323
|
+
context_variables: ContextVariables,
|
|
324
|
+
) -> None:
|
|
325
|
+
"""Assign a common context_variables reference to all agents in the group, including the tool executor and group chat manager.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
tool_execution: The tool execution agent.
|
|
329
|
+
agents: List of all agents in the conversation.
|
|
330
|
+
manager: GroupChatManager instance.
|
|
331
|
+
context_variables: Context variables to assign to all agents.
|
|
332
|
+
"""
|
|
333
|
+
for agent in agents + [tool_execution] + [manager]:
|
|
334
|
+
agent.context_variables = context_variables
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def cleanup_temp_user_messages(chat_result: Any) -> None:
|
|
338
|
+
"""Remove temporary user proxy agent name from messages before returning.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
chat_result: ChatResult instance.
|
|
342
|
+
"""
|
|
343
|
+
for message in chat_result.chat_history:
|
|
344
|
+
if "name" in message and message["name"] == "_User":
|
|
345
|
+
del message["name"]
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def get_last_agent_speaker(
|
|
349
|
+
groupchat: GroupChat, group_agent_names: list[str], tool_executor: GroupToolExecutor
|
|
350
|
+
) -> Agent:
|
|
351
|
+
"""Get the last group agent from the group chat messages. Not including the tool executor."""
|
|
352
|
+
last_group_speaker = None
|
|
353
|
+
for message in reversed(groupchat.messages):
|
|
354
|
+
if "name" in message and message["name"] in group_agent_names and message["name"] != tool_executor.name:
|
|
355
|
+
agent = groupchat.agent_by_name(name=message["name"])
|
|
356
|
+
if agent:
|
|
357
|
+
last_group_speaker = agent
|
|
358
|
+
break
|
|
359
|
+
if last_group_speaker is None:
|
|
360
|
+
raise ValueError("No group agent found in the message history")
|
|
361
|
+
|
|
362
|
+
return last_group_speaker
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def determine_next_agent(
|
|
366
|
+
last_speaker: "ConversableAgent",
|
|
367
|
+
groupchat: GroupChat,
|
|
368
|
+
initial_agent: "ConversableAgent",
|
|
369
|
+
use_initial_agent: bool,
|
|
370
|
+
tool_executor: GroupToolExecutor,
|
|
371
|
+
group_agent_names: list[str],
|
|
372
|
+
user_agent: Optional["ConversableAgent"],
|
|
373
|
+
group_after_work: TransitionTarget,
|
|
374
|
+
) -> Optional[Union[Agent, str]]:
|
|
375
|
+
"""Determine the next agent in the conversation.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
last_speaker ("ConversableAgent"): The last agent to speak.
|
|
379
|
+
groupchat (GroupChat): GroupChat instance.
|
|
380
|
+
initial_agent ("ConversableAgent"): The initial agent in the conversation.
|
|
381
|
+
use_initial_agent (bool): Whether to use the initial agent straight away.
|
|
382
|
+
tool_executor ("ConversableAgent"): The tool execution agent.
|
|
383
|
+
group_agent_names (list[str]): List of agent names.
|
|
384
|
+
user_agent (UserProxyAgent): Optional user proxy agent.
|
|
385
|
+
group_after_work (TransitionTarget): Group-level Transition option when an agent doesn't select the next agent.
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
Optional[Union[Agent, str]]: The next agent or speaker selection method.
|
|
389
|
+
"""
|
|
390
|
+
|
|
391
|
+
# Logic for determining the next target (anything based on Transition Target: an agent, wrapped agent, TerminateTarget, StayTarget, RevertToUserTarget, GroupManagerTarget, etc.
|
|
392
|
+
# 1. If it's the first response -> initial agent
|
|
393
|
+
# 2. If the last message is a tool call -> tool execution agent
|
|
394
|
+
# 3. If the Tool Executor has determined a next target (e.g. ReplyResult specified target) -> transition to tool reply target
|
|
395
|
+
# 4. If the user last spoke -> return to the previous agent
|
|
396
|
+
# NOW "AFTER WORK":
|
|
397
|
+
# 5. Get the After Work condition (if the agent doesn't have one, get the group-level one)
|
|
398
|
+
# 6. Resolve and return the After Work condition -> agent / wrapped agent / TerminateTarget / StayTarget / RevertToUserTarget / GroupManagerTarget / etc.
|
|
399
|
+
|
|
400
|
+
# 1. If it's the first response, return the initial agent
|
|
401
|
+
if use_initial_agent:
|
|
402
|
+
return initial_agent
|
|
403
|
+
|
|
404
|
+
# 2. If the last message is a tool call, return the tool execution agent
|
|
405
|
+
if "tool_calls" in groupchat.messages[-1]:
|
|
406
|
+
return tool_executor
|
|
407
|
+
|
|
408
|
+
# 3. If the Tool Executor has determined a next target, return that
|
|
409
|
+
if tool_executor.has_next_target():
|
|
410
|
+
next_agent = tool_executor.get_next_target()
|
|
411
|
+
tool_executor.clear_next_target()
|
|
412
|
+
|
|
413
|
+
if next_agent.can_resolve_for_speaker_selection():
|
|
414
|
+
return next_agent.resolve(groupchat, last_speaker, user_agent).get_speaker_selection_result(groupchat)
|
|
415
|
+
else:
|
|
416
|
+
raise ValueError(
|
|
417
|
+
"Tool Executor next target must be a valid TransitionTarget that can resolve for speaker selection."
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
# get the last group agent
|
|
421
|
+
last_agent_speaker = get_last_agent_speaker(groupchat, group_agent_names, tool_executor)
|
|
422
|
+
|
|
423
|
+
# If we are returning from a tool execution, return to the last agent that spoke
|
|
424
|
+
if groupchat.messages[-1]["role"] == "tool":
|
|
425
|
+
return last_agent_speaker
|
|
426
|
+
|
|
427
|
+
# If the user last spoke, return to the agent prior to them (if they don't have an after work, otherwise it's treated like any other agent)
|
|
428
|
+
if user_agent and last_speaker == user_agent:
|
|
429
|
+
if user_agent.handoffs.after_work is None:
|
|
430
|
+
return last_agent_speaker
|
|
431
|
+
else:
|
|
432
|
+
last_agent_speaker = user_agent
|
|
433
|
+
|
|
434
|
+
# AFTER WORK:
|
|
435
|
+
|
|
436
|
+
# Get the appropriate After Work condition (from the agent if they have one, otherwise the group level one)
|
|
437
|
+
after_work_condition = (
|
|
438
|
+
last_agent_speaker.handoffs.after_work # type: ignore[attr-defined]
|
|
439
|
+
if last_agent_speaker.handoffs.after_work is not None # type: ignore[attr-defined]
|
|
440
|
+
else group_after_work
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
# Resolve the next agent, termination, or speaker selection method
|
|
444
|
+
resolved_speaker_selection_result = after_work_condition.resolve(
|
|
445
|
+
groupchat,
|
|
446
|
+
last_agent_speaker, # type: ignore[arg-type]
|
|
447
|
+
user_agent,
|
|
448
|
+
).get_speaker_selection_result(groupchat)
|
|
449
|
+
|
|
450
|
+
return resolved_speaker_selection_result
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def create_group_transition(
|
|
454
|
+
initial_agent: "ConversableAgent",
|
|
455
|
+
tool_execution: GroupToolExecutor,
|
|
456
|
+
group_agent_names: list[str],
|
|
457
|
+
user_agent: Optional["ConversableAgent"],
|
|
458
|
+
group_after_work: TransitionTarget,
|
|
459
|
+
) -> Callable[["ConversableAgent", GroupChat], Optional[Union[Agent, str]]]:
|
|
460
|
+
"""Creates a transition function for group chat with enclosed state for the use_initial_agent.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
initial_agent ("ConversableAgent"): The first agent to speak
|
|
464
|
+
tool_execution (GroupToolExecutor): The tool execution agent
|
|
465
|
+
group_agent_names (list[str]): List of all agent names
|
|
466
|
+
user_agent (UserProxyAgent): Optional user proxy agent
|
|
467
|
+
group_after_work (TransitionTarget): Group-level after work
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
Callable[["ConversableAgent", GroupChat], Optional[Union[Agent, str]]]: The transition function
|
|
471
|
+
"""
|
|
472
|
+
# Create enclosed state, this will be set once per creation so will only be True on the first execution
|
|
473
|
+
# of group_transition
|
|
474
|
+
state = {"use_initial_agent": True}
|
|
475
|
+
|
|
476
|
+
def group_transition(last_speaker: "ConversableAgent", groupchat: GroupChat) -> Optional[Union[Agent, str]]:
|
|
477
|
+
result = determine_next_agent(
|
|
478
|
+
last_speaker=last_speaker,
|
|
479
|
+
groupchat=groupchat,
|
|
480
|
+
initial_agent=initial_agent,
|
|
481
|
+
use_initial_agent=state["use_initial_agent"],
|
|
482
|
+
tool_executor=tool_execution,
|
|
483
|
+
group_agent_names=group_agent_names,
|
|
484
|
+
user_agent=user_agent,
|
|
485
|
+
group_after_work=group_after_work,
|
|
486
|
+
)
|
|
487
|
+
state["use_initial_agent"] = False
|
|
488
|
+
return result
|
|
489
|
+
|
|
490
|
+
return group_transition
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def create_group_manager(
|
|
494
|
+
groupchat: GroupChat,
|
|
495
|
+
group_manager_args: Optional[dict[str, Any]],
|
|
496
|
+
agents: list["ConversableAgent"],
|
|
497
|
+
group_after_work: TransitionTarget,
|
|
498
|
+
) -> GroupChatManager:
|
|
499
|
+
"""Create a GroupChatManager for the group chat utilising any arguments passed in and ensure an LLM Config exists if needed
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
groupchat (GroupChat): The groupchat.
|
|
503
|
+
group_manager_args (dict[str, Any]): Group manager arguments to create the GroupChatManager.
|
|
504
|
+
agents (list["ConversableAgent"]): List of agents in the group to check handoffs and after work.
|
|
505
|
+
group_after_work (TransitionTarget): Group-level after work to check.
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
GroupChatManager: GroupChatManager instance.
|
|
509
|
+
"""
|
|
510
|
+
manager_args = (group_manager_args or {}).copy()
|
|
511
|
+
if "groupchat" in manager_args:
|
|
512
|
+
raise ValueError("'groupchat' cannot be specified in group_manager_args as it is set by initiate_group_chat")
|
|
513
|
+
manager = GroupChatManager(groupchat, **manager_args)
|
|
514
|
+
|
|
515
|
+
# Ensure that our manager has an LLM Config if we have any GroupManagerTarget targets used
|
|
516
|
+
if manager.llm_config is False:
|
|
517
|
+
has_group_manager_target = False
|
|
518
|
+
|
|
519
|
+
if isinstance(group_after_work, GroupManagerTarget):
|
|
520
|
+
# Check group after work
|
|
521
|
+
has_group_manager_target = True
|
|
522
|
+
else:
|
|
523
|
+
# Check agent hand-offs and after work
|
|
524
|
+
for agent in agents:
|
|
525
|
+
if (
|
|
526
|
+
len(agent.handoffs.get_context_conditions_by_target_type(GroupManagerTarget)) > 0
|
|
527
|
+
or len(agent.handoffs.get_llm_conditions_by_target_type(GroupManagerTarget)) > 0
|
|
528
|
+
or (
|
|
529
|
+
agent.handoffs.after_work is not None
|
|
530
|
+
and isinstance(agent.handoffs.after_work, GroupManagerTarget)
|
|
531
|
+
)
|
|
532
|
+
):
|
|
533
|
+
has_group_manager_target = True
|
|
534
|
+
break
|
|
535
|
+
|
|
536
|
+
if has_group_manager_target:
|
|
537
|
+
raise ValueError(
|
|
538
|
+
"The group manager doesn't have an LLM Config and it is required for any targets or after works using a GroupManagerTarget. Use the 'llm_config' in the group_manager_args parameter to specify the LLM Config for the group manager."
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
return manager
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def make_remove_function(tool_msgs_to_remove: list[str]) -> Callable[[list[dict[str, Any]]], list[dict[str, Any]]]:
|
|
545
|
+
"""Create a function to remove messages with tool calls from the messages list.
|
|
546
|
+
|
|
547
|
+
The returned function can be registered as a hook to "process_all_messages_before_reply"" to remove messages with tool calls.
|
|
548
|
+
"""
|
|
549
|
+
|
|
550
|
+
def remove_messages(messages: list[dict[str, Any]], tool_msgs_to_remove: list[str]) -> list[dict[str, Any]]:
|
|
551
|
+
copied = copy.deepcopy(messages)
|
|
552
|
+
new_messages = []
|
|
553
|
+
removed_tool_ids = []
|
|
554
|
+
for message in copied:
|
|
555
|
+
# remove tool calls
|
|
556
|
+
if message.get("tool_calls") is not None:
|
|
557
|
+
filtered_tool_calls = []
|
|
558
|
+
for tool_call in message["tool_calls"]:
|
|
559
|
+
if tool_call.get("function") is not None and tool_call["function"]["name"] in tool_msgs_to_remove:
|
|
560
|
+
# remove
|
|
561
|
+
removed_tool_ids.append(tool_call["id"])
|
|
562
|
+
else:
|
|
563
|
+
filtered_tool_calls.append(tool_call)
|
|
564
|
+
if len(filtered_tool_calls) > 0:
|
|
565
|
+
message["tool_calls"] = filtered_tool_calls
|
|
566
|
+
else:
|
|
567
|
+
del message["tool_calls"]
|
|
568
|
+
if (
|
|
569
|
+
message.get("content") is None
|
|
570
|
+
or message.get("content") == ""
|
|
571
|
+
or message.get("content") == "None"
|
|
572
|
+
):
|
|
573
|
+
continue # if no tool call and no content, skip this message
|
|
574
|
+
# else: keep the message with tool_calls removed
|
|
575
|
+
# remove corresponding tool responses
|
|
576
|
+
elif message.get("tool_responses") is not None:
|
|
577
|
+
filtered_tool_responses = []
|
|
578
|
+
for tool_response in message["tool_responses"]:
|
|
579
|
+
if tool_response["tool_call_id"] not in removed_tool_ids:
|
|
580
|
+
filtered_tool_responses.append(tool_response)
|
|
581
|
+
|
|
582
|
+
if len(filtered_tool_responses) > 0:
|
|
583
|
+
message["tool_responses"] = filtered_tool_responses
|
|
584
|
+
else:
|
|
585
|
+
continue
|
|
586
|
+
|
|
587
|
+
new_messages.append(message)
|
|
588
|
+
|
|
589
|
+
return new_messages
|
|
590
|
+
|
|
591
|
+
return partial(remove_messages, tool_msgs_to_remove=tool_msgs_to_remove)
|