ag2 0.9.3__py3-none-any.whl → 0.9.4__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.3.dist-info → ag2-0.9.4.dist-info}/METADATA +1 -1
- {ag2-0.9.3.dist-info → ag2-0.9.4.dist-info}/RECORD +28 -22
- autogen/agentchat/contrib/agent_optimizer.py +6 -3
- autogen/agentchat/conversable_agent.py +51 -5
- autogen/agentchat/group/group_utils.py +16 -7
- autogen/agentchat/group/guardrails.py +171 -0
- autogen/agentchat/group/targets/transition_target.py +10 -0
- autogen/agentchat/groupchat.py +93 -6
- autogen/agents/experimental/websurfer/websurfer.py +9 -1
- autogen/events/agent_events.py +6 -0
- autogen/events/helpers.py +8 -0
- autogen/mcp/helpers.py +45 -0
- autogen/mcp/mcp_proxy/mcp_proxy.py +2 -3
- autogen/messages/agent_messages.py +1 -1
- autogen/oai/gemini.py +39 -24
- autogen/oai/gemini_types.py +1 -1
- autogen/tools/experimental/__init__.py +4 -0
- autogen/tools/experimental/browser_use/browser_use.py +4 -11
- autogen/tools/experimental/firecrawl/__init__.py +7 -0
- autogen/tools/experimental/firecrawl/firecrawl_tool.py +853 -0
- autogen/tools/experimental/searxng/__init__.py +7 -0
- autogen/tools/experimental/searxng/searxng_search.py +141 -0
- autogen/version.py +1 -1
- templates/client_template/main.jinja2 +5 -2
- templates/main.jinja2 +1 -1
- {ag2-0.9.3.dist-info → ag2-0.9.4.dist-info}/WHEEL +0 -0
- {ag2-0.9.3.dist-info → ag2-0.9.4.dist-info}/licenses/LICENSE +0 -0
- {ag2-0.9.3.dist-info → ag2-0.9.4.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -14,7 +14,7 @@ autogen/retrieve_utils.py,sha256=R3Yp5d8dH4o9ayLZrGn4rCjIaY4glOHIiyQjwClmdi8,200
|
|
|
14
14
|
autogen/runtime_logging.py,sha256=yCmZODvwqYR91m8lX3Q4SoPcY-DK48NF4m56CP6Om3c,4692
|
|
15
15
|
autogen/token_count_utils.py,sha256=n4wTFVNHwrfjZkrErFr8kNig2K-YCGgMLWsjDRS9D6g,10797
|
|
16
16
|
autogen/types.py,sha256=qu-7eywhakW2AxQ5lYisLLeIg45UoOW-b3ErIuyRTuw,1000
|
|
17
|
-
autogen/version.py,sha256=
|
|
17
|
+
autogen/version.py,sha256=zukvVxK3WY_ypC-a8Iy7A_baGAoqr3cT-lhYYnSMhhM,193
|
|
18
18
|
autogen/_website/__init__.py,sha256=c8B9TpO07x9neD0zsJWj6AaEdlcP-WvxrvVOGWLtamk,143
|
|
19
19
|
autogen/_website/generate_api_references.py,sha256=yKqyeSP_NE27wwLYWsZbTYRceEoxzNxPXqn6vsIzEvk,14789
|
|
20
20
|
autogen/_website/generate_mkdocs.py,sha256=TkmLnUDv1Ms5cGClXPmenA8nxmwg4kR0E-FHCVjw_og,43246
|
|
@@ -25,12 +25,12 @@ autogen/agentchat/__init__.py,sha256=d5jPpXeavynP9eh5uMIgJKbK3-3tywJBa6l3L9X4l34
|
|
|
25
25
|
autogen/agentchat/agent.py,sha256=HePNJ5BXJTcZtaD2a8CoTeHAoLUUy3scM6Ihm-NsSWk,5828
|
|
26
26
|
autogen/agentchat/assistant_agent.py,sha256=XTJvD66r4qYkdNAJJLr1CC-wTYFJWvhmD5_G0WbbX2I,5741
|
|
27
27
|
autogen/agentchat/chat.py,sha256=6Gx2t1-Xa8kP6ZoUihHBNGOqNlrGhhqLPKrckL0n-RI,14003
|
|
28
|
-
autogen/agentchat/conversable_agent.py,sha256
|
|
29
|
-
autogen/agentchat/groupchat.py,sha256=
|
|
28
|
+
autogen/agentchat/conversable_agent.py,sha256=-BNwxfqSaHQi428a6xgz_53ThPC8LDsdHzHiWzNsPtc,192671
|
|
29
|
+
autogen/agentchat/groupchat.py,sha256=vdK8zUX2CG8fR5pkPGWX7Zn7DFwLvld0bpaRv3vOBEo,89471
|
|
30
30
|
autogen/agentchat/user_proxy_agent.py,sha256=-gbDblRvE09FGuVB6-y5ZT9Cpvv--rM3FMi8PnPIFBA,7445
|
|
31
31
|
autogen/agentchat/utils.py,sha256=2ZweUqe4ynxji0jnvd0r9Uxg3n0elif4a-jZOyeeqcg,8238
|
|
32
32
|
autogen/agentchat/contrib/__init__.py,sha256=tOTe4nwbKj7elHpftAy3zS_embMDzncrKL98XKhY6-c,168
|
|
33
|
-
autogen/agentchat/contrib/agent_optimizer.py,sha256=
|
|
33
|
+
autogen/agentchat/contrib/agent_optimizer.py,sha256=Y0H9unXXCOmBo6P-njtH3L_vmMCdwp0B9Kiihg8HBy4,22431
|
|
34
34
|
autogen/agentchat/contrib/gpt_assistant_agent.py,sha256=0zne7MZBVYlxvYdI-Iu2BRziPSYQPZ1fg0XjfFzt9BA,25051
|
|
35
35
|
autogen/agentchat/contrib/img_utils.py,sha256=BNc1ELE2dSzmFCc_9zwcdazmUxDZZrQv8jgXJZaheCM,15121
|
|
36
36
|
autogen/agentchat/contrib/llamaindex_conversable_agent.py,sha256=sAbBmYIBB4RJt-GhgNzA5QSX4CP0q2eusQNa6K2_LA4,4595
|
|
@@ -132,7 +132,8 @@ autogen/agentchat/group/context_expression.py,sha256=zFWPV3yfV-0ayYXfrhRq6iWQZnR
|
|
|
132
132
|
autogen/agentchat/group/context_str.py,sha256=EHjpDr8MLMl5AMXktMi9Wp4BIL_1hbIeJPMEXlLkTjs,1270
|
|
133
133
|
autogen/agentchat/group/context_variables.py,sha256=d2Q31aoV2o_5QSd3Oh1fYDIV0fDaaHOOsjNQ92TC_H0,5649
|
|
134
134
|
autogen/agentchat/group/group_tool_executor.py,sha256=lCUg0Z_R8u0mRsWE350p6pGNj-6fJUMGqWqs3xmznCQ,9104
|
|
135
|
-
autogen/agentchat/group/group_utils.py,sha256=
|
|
135
|
+
autogen/agentchat/group/group_utils.py,sha256=nNHtAAS2tAvaA3VsXPYDBIV6vBve8PoVvYMs0Ps7GzI,27586
|
|
136
|
+
autogen/agentchat/group/guardrails.py,sha256=OMqQgXtscfYHS6ET1x2HT6Cpi2gtnkEbNKQgWqnZtDw,5964
|
|
136
137
|
autogen/agentchat/group/handoffs.py,sha256=ergiOsXC4z8N44LTUh-abIPrErjkXZWFRMXflinNq1Y,11743
|
|
137
138
|
autogen/agentchat/group/llm_condition.py,sha256=wfuEET1VhyVVGedYxcyuhX_Vus6uZHxUl_SPpu4YIsc,2951
|
|
138
139
|
autogen/agentchat/group/multi_agent_chat.py,sha256=oKAWiziaSZ0otwfGhSWaR26dYbfRQj_vau_P5Z1dvfY,7688
|
|
@@ -149,7 +150,7 @@ autogen/agentchat/group/patterns/round_robin.py,sha256=nS7nsQJKCq9lD0wyCx__gkWK3
|
|
|
149
150
|
autogen/agentchat/group/targets/__init__.py,sha256=AJNSbl9iMe2hiDmZojTp8h889o5OYN3V7f2_2nr8px4,145
|
|
150
151
|
autogen/agentchat/group/targets/group_chat_target.py,sha256=4_bzVg1ODSIx7UfP8O9OdvTlRNtFuqZt77YqKFIwh74,5364
|
|
151
152
|
autogen/agentchat/group/targets/group_manager_target.py,sha256=Cp4Wbmp3a7fZBTN-s5eQEDRNyKWq9KYihRlzx1blnZQ,6276
|
|
152
|
-
autogen/agentchat/group/targets/transition_target.py,sha256=
|
|
153
|
+
autogen/agentchat/group/targets/transition_target.py,sha256=punRNX630N4HQrMmpVAeSCkdHDEd3CG51KHwlUM3JLo,16742
|
|
153
154
|
autogen/agentchat/group/targets/transition_utils.py,sha256=fzRnaJtUEnNswkmamQH08X43xX2kV0SWGIWEV72TtIo,219
|
|
154
155
|
autogen/agentchat/realtime/__init__.py,sha256=c8B9TpO07x9neD0zsJWj6AaEdlcP-WvxrvVOGWLtamk,143
|
|
155
156
|
autogen/agentchat/realtime/experimental/__init__.py,sha256=leYemaQJXulYnp5atRJZE247EL5VJtdDoF_p1XJFQzM,619
|
|
@@ -198,7 +199,7 @@ autogen/agents/experimental/slack/slack.py,sha256=QzT0awsZpAVCs5mmXeHT-V6uhKik8n
|
|
|
198
199
|
autogen/agents/experimental/telegram/__init__.py,sha256=Y-HQJXmeux9QVY-Jjy22WO8cPWryxwaKEbhcypdUImY,209
|
|
199
200
|
autogen/agents/experimental/telegram/telegram.py,sha256=q-1i1xPVYxOUjABUvKRo5B9sPvF3p2bAQLQi9POndMM,3097
|
|
200
201
|
autogen/agents/experimental/websurfer/__init__.py,sha256=I3D0sIBDW9zxSOM0wNXdFfMZQIpiutet-9ikrm0WD0s,212
|
|
201
|
-
autogen/agents/experimental/websurfer/websurfer.py,sha256=
|
|
202
|
+
autogen/agents/experimental/websurfer/websurfer.py,sha256=MT6VIXmPscjY8LdweXL_txTNsNkpHtwSqR_475QYMeM,2908
|
|
202
203
|
autogen/agents/experimental/wikipedia/__init__.py,sha256=ytX_85hqagdyl3I72kYQM2-3bR7rG_SI5cyx8pKSG2I,212
|
|
203
204
|
autogen/agents/experimental/wikipedia/wikipedia.py,sha256=51fjVlmNDZfq3JrGIkknPXz41y_VJcMXfHNnnW58L0s,3942
|
|
204
205
|
autogen/cache/__init__.py,sha256=HF7qJyJpzqTYXSwUyVUxAFTY0eakw5OFwhyg_KO3QwE,375
|
|
@@ -226,10 +227,10 @@ autogen/coding/jupyter/jupyter_client.py,sha256=ROXAWOKG_EJ_oFNuyqUd_3uOBPUTRoTh
|
|
|
226
227
|
autogen/coding/jupyter/jupyter_code_executor.py,sha256=Z2vZvou6QzpMBg0IgOzVRoCADswd15mvfkktIjGhUMY,6374
|
|
227
228
|
autogen/coding/jupyter/local_jupyter_server.py,sha256=7b8yi5qK8ms2e5-PRCrzmXKGp1iC5KgpMU8xiqQ9u8o,6589
|
|
228
229
|
autogen/events/__init__.py,sha256=XwCA6Rsq9AyjgeecGuiwHcAvDQMmKXgGomw5iLDIU5Q,358
|
|
229
|
-
autogen/events/agent_events.py,sha256=
|
|
230
|
+
autogen/events/agent_events.py,sha256=LSCOMwA-nlBSboE5jOh-Da2Pm7hghiwjfzYTz_bS30k,31112
|
|
230
231
|
autogen/events/base_event.py,sha256=5K1wzDBAio9wLxahErSvE0-UbFfMuSTxBp6EI8SbPGU,3475
|
|
231
232
|
autogen/events/client_events.py,sha256=2skE5f9BxQmwJv22vNe-s1ReDXE-DsadeAmMWlrmltc,5466
|
|
232
|
-
autogen/events/helpers.py,sha256=
|
|
233
|
+
autogen/events/helpers.py,sha256=JNwnEfaFylWEaQivURXQ5_7DjsjmCZyiWRI4mQH0ueY,1528
|
|
233
234
|
autogen/events/print_event.py,sha256=_B_60nnB0yFwWpv-LT2c-Zp_b-moREtafP4lDI8uJzk,1275
|
|
234
235
|
autogen/extensions/__init__.py,sha256=tOTe4nwbKj7elHpftAy3zS_embMDzncrKL98XKhY6-c,168
|
|
235
236
|
autogen/fast_depends/__init__.py,sha256=4KNQsBQmoDf5RIZsnOuMFy7t6-Y1hnKjIplGdbo_k8Y,465
|
|
@@ -276,17 +277,18 @@ autogen/logger/logger_utils.py,sha256=H9hcsRyEcUcfxTYWf5cRjtNghF4h3FT8sr4IIuqQum
|
|
|
276
277
|
autogen/logger/sqlite_logger.py,sha256=sRwMx42zh85QWLz1BqKyVySI8OwEB_NjM3ObLOW-mcI,18685
|
|
277
278
|
autogen/mcp/__init__.py,sha256=6BDDmw0sjLZRjyHnd-Gfh9BE-pTKTv5bkow9W6odHtQ,213
|
|
278
279
|
autogen/mcp/__main__.py,sha256=C7nXbWxG3yGsWKRgFnhSsbQOmshLnz6ZOcCxK2TWWio,2487
|
|
280
|
+
autogen/mcp/helpers.py,sha256=J5_J6n3jMJUEJH5K8k9BeUb6ymQgRUI0hC1gbY7rlwM,1533
|
|
279
281
|
autogen/mcp/mcp_client.py,sha256=7c_lHgBJEs77TFYjLcTlVrEu_0z4EafPPY3PgteY87c,7400
|
|
280
282
|
autogen/mcp/mcp_proxy/__init__.py,sha256=3HTU-TqHLk4XSXeBV1UFd9XkQ1B0yOuXXyGseXvDVec,518
|
|
281
283
|
autogen/mcp/mcp_proxy/fastapi_code_generator_helpers.py,sha256=dx-w2tGVMnh8pzY2NuXlMD7oIF7_5Gvc5oSbHIySEpc,2110
|
|
282
|
-
autogen/mcp/mcp_proxy/mcp_proxy.py,sha256=
|
|
284
|
+
autogen/mcp/mcp_proxy/mcp_proxy.py,sha256=NfjNsUnqpPQ_FrKJPiPUL7aDE9SZ_wNB0ZHAsiJHq9I,22131
|
|
283
285
|
autogen/mcp/mcp_proxy/operation_grouping.py,sha256=1n4o5qhkQd2hVr7OaOMsRhInDveDGkCozLudsBVusC4,6349
|
|
284
286
|
autogen/mcp/mcp_proxy/operation_renaming.py,sha256=G5J4VdxUAwSvOo-DsqIUbCfV9TnH3riaHLI6IpctDF8,3956
|
|
285
287
|
autogen/mcp/mcp_proxy/patch_fastapi_code_generator.py,sha256=vBr8P890nsFvK4iuFicrdNskooHlBQeTQ6jbb5kcPAk,3490
|
|
286
288
|
autogen/mcp/mcp_proxy/security.py,sha256=HUcdIE1CCmvuWBPUlvjoKOJV-05sve6pcKHalgm9Z8E,13611
|
|
287
289
|
autogen/mcp/mcp_proxy/security_schema_visitor.py,sha256=UafLMd5zcz0COcc5Vfvt6bP3LQ3jmaBwQIlWhovN9H4,1345
|
|
288
290
|
autogen/messages/__init__.py,sha256=ZuLvvIQRkNE5fotPe6MSS_YzOUkmfIqGSfOZZOZQ3go,321
|
|
289
|
-
autogen/messages/agent_messages.py,sha256=
|
|
291
|
+
autogen/messages/agent_messages.py,sha256=gzsJdzk2slAfSn8ZcXNW6hPegrYPNIEphdhHaqP19Zg,30451
|
|
290
292
|
autogen/messages/base_message.py,sha256=MbYEXM0dWHl31y7o08PGeJNcpK67hpoQpUMgnZ8qTGE,3808
|
|
291
293
|
autogen/messages/client_messages.py,sha256=9yVn4zug44sLIZQFKiCjOK2ML7uFAADflmGudgPV850,5653
|
|
292
294
|
autogen/messages/print_message.py,sha256=51UpH-rkLrRxz_QxAhEMdjVAipiwkLirOuGtkuTvQL4,1393
|
|
@@ -297,8 +299,8 @@ autogen/oai/cerebras.py,sha256=8hiSBq88l2yTXUJPV7AvGXRCtwvW0Y9hIYUnYK2S2os,12462
|
|
|
297
299
|
autogen/oai/client.py,sha256=BB_6Heny6_7lq8q7ZAPKohHAK63zs9UGzRoUknTxjYY,65051
|
|
298
300
|
autogen/oai/client_utils.py,sha256=lVbHyff7OnpdM-tXskC23xLdFccj2AalTdWA4DxzxS4,7543
|
|
299
301
|
autogen/oai/cohere.py,sha256=pRcQWjbzKbZ1RfC1vk9WGjgndwjHbIaOVoKEYdV2L6c,19421
|
|
300
|
-
autogen/oai/gemini.py,sha256=
|
|
301
|
-
autogen/oai/gemini_types.py,sha256=
|
|
302
|
+
autogen/oai/gemini.py,sha256=bc_RQwtoGi7DnwQwPie7ZdvsqpD1AjYwzjVwnIKP39U,43168
|
|
303
|
+
autogen/oai/gemini_types.py,sha256=i4wT8ytD2cO9nCUWm2FKLKVVxyd31wMhOpnrvnIiKIc,5888
|
|
302
304
|
autogen/oai/groq.py,sha256=pQWtaAY_AjT30XKbZNHXDzWsawBys3yFWlfy6K4Nqr8,12431
|
|
303
305
|
autogen/oai/mistral.py,sha256=SlOYPdnNLHuTEHBGCzsemG9sLEgphdUukRurERdMsvI,12677
|
|
304
306
|
autogen/oai/ollama.py,sha256=t0fIgDCoIfsQZ3hhpseam5N-fbpI7-fw82bG55mA8nU,29103
|
|
@@ -320,15 +322,17 @@ autogen/tools/toolkit.py,sha256=1tOmTGJ96RhkJrrtAViKUyEcwacA6ztoIbYbnf8NgTU,2558
|
|
|
320
322
|
autogen/tools/contrib/__init__.py,sha256=DWEjPK6xCR2ihAXXdquQZmiuqRLA3Pqb8QV8W1RtS3k,202
|
|
321
323
|
autogen/tools/contrib/time/__init__.py,sha256=dplie5aBJZ8VoKy6EKcQMLTtSgcCkNDYzpdsC2I0YWk,195
|
|
322
324
|
autogen/tools/contrib/time/time.py,sha256=tPi49vOUwfvujbYA-zS00CWcLW-y18CPyQ1gnJG6iRg,1271
|
|
323
|
-
autogen/tools/experimental/__init__.py,sha256=
|
|
325
|
+
autogen/tools/experimental/__init__.py,sha256=jGyt9GVoJO6VsZdmBLzwTxXZGBbWEwdLrUjn-rWQ6os,1588
|
|
324
326
|
autogen/tools/experimental/browser_use/__init__.py,sha256=kfxCajXcVMDH6CZq-lWh2p8PKxOwT9yjC_Za0jr4zUg,290
|
|
325
|
-
autogen/tools/experimental/browser_use/browser_use.py,sha256=
|
|
327
|
+
autogen/tools/experimental/browser_use/browser_use.py,sha256=KfU4MI_BWaHepv0bDMf9HTDUaHTJThuBJI8R_BPpjmg,5561
|
|
326
328
|
autogen/tools/experimental/crawl4ai/__init__.py,sha256=UjFJLSZ9P5xT6WCV0RDPtwt4MHuwPdK90TU7ByXhLWs,207
|
|
327
329
|
autogen/tools/experimental/crawl4ai/crawl4ai.py,sha256=MsOLtbPHRpRrCnRJPQVVVNwmsBcgsWLSHNXDOF-47ws,6088
|
|
328
330
|
autogen/tools/experimental/deep_research/__init__.py,sha256=9SFcDEj2OHxNSlXP11lf1uHENlfUeO47ROcOSD9GCDs,220
|
|
329
331
|
autogen/tools/experimental/deep_research/deep_research.py,sha256=1ZwZjVR1gKHvlWaT7I9_8qrEPTCYhManMci4ZHApIdE,14970
|
|
330
332
|
autogen/tools/experimental/duckduckgo/__init__.py,sha256=hstpKQfvd5YD7X0iMuvJFlui3MtJVGbz5XpkOSaoy18,232
|
|
331
333
|
autogen/tools/experimental/duckduckgo/duckduckgo_search.py,sha256=NCByIKfJz9CXj_IuRQdM8cIEwP_p_ZIt9aFpOjVfzYc,3532
|
|
334
|
+
autogen/tools/experimental/firecrawl/__init__.py,sha256=L-_y07Pzq4TeIIIoYce-catqiZI8f1UAAQpUoL-iYmA,215
|
|
335
|
+
autogen/tools/experimental/firecrawl/firecrawl_tool.py,sha256=4Qj5rFGdQelWJ0OUEVquMZKkxZg5thE-A6Eo9nRPbsU,34803
|
|
332
336
|
autogen/tools/experimental/google/__init__.py,sha256=_j5i9rzajzilQetKCPh5QkX60p1iOri2Yw69zIKAdU0,461
|
|
333
337
|
autogen/tools/experimental/google/model.py,sha256=hm3ynXSJAjeld85iSHo9QgIehoWEqeVhbSPC3EaHm0M,515
|
|
334
338
|
autogen/tools/experimental/google/toolkit_protocol.py,sha256=Jg9aSy4sKDr-yYVxevYr7zbEcmA9EwFWrJBOogLfKJU,485
|
|
@@ -353,14 +357,16 @@ autogen/tools/experimental/perplexity/__init__.py,sha256=VxWG2HfcxwBhLUV4LnAlte9
|
|
|
353
357
|
autogen/tools/experimental/perplexity/perplexity_search.py,sha256=UZtyS9Pde3_VXufuZVuiBw87MiOiAWwVyXqstzE7Rlk,9976
|
|
354
358
|
autogen/tools/experimental/reliable/__init__.py,sha256=GLvfvsFLfBANFVcOptIa_Tm71YsHt5U7X5N9quqDZW0,479
|
|
355
359
|
autogen/tools/experimental/reliable/reliable.py,sha256=5s3aD5u-6JJrXqbSQBcW2qxNfyvx3MQ--IcZ6wMdsvE,64687
|
|
360
|
+
autogen/tools/experimental/searxng/__init__.py,sha256=IiScuchBMO0wDPUo58s41aQbr0CrTkumHeZj-eSzZJo,223
|
|
361
|
+
autogen/tools/experimental/searxng/searxng_search.py,sha256=Mi_FskvurZqZxFvDfrq59AxyeA8QE02A9lTeDufeNH8,4909
|
|
356
362
|
autogen/tools/experimental/tavily/__init__.py,sha256=rvztagn7FpWiXZwJtZmKbIFBteBenaedwsiUViDyC4Y,220
|
|
357
363
|
autogen/tools/experimental/tavily/tavily_search.py,sha256=S1Aj519kaEIwOAJXTc9Y__GHyY--vvc-P-5nh3ksCOQ,7810
|
|
358
364
|
autogen/tools/experimental/web_search_preview/__init__.py,sha256=8vd1XWS14883MQ4U4dHvMP8aAKAtMILLnqRJNzgNh6A,233
|
|
359
365
|
autogen/tools/experimental/web_search_preview/web_search_preview.py,sha256=bLKZSl0670uh82C3OCceRD0WaRfEPtQ4o06PTsQ3kKk,4818
|
|
360
366
|
autogen/tools/experimental/wikipedia/__init__.py,sha256=jS_gUMyz-uCWr75yKv2esIiGz0ijNDNokt6wovQULLE,274
|
|
361
367
|
autogen/tools/experimental/wikipedia/wikipedia.py,sha256=Cw7luG34J6if_5D40C_039BTA6l_LvOiEg6FY5UysC4,11545
|
|
362
|
-
templates/main.jinja2,sha256=
|
|
363
|
-
templates/client_template/main.jinja2,sha256=
|
|
368
|
+
templates/main.jinja2,sha256=mu7z_4NjI1XMEwQx9KaFQixi4ImfwY0FZQaYUJVBnSc,1765
|
|
369
|
+
templates/client_template/main.jinja2,sha256=9_1pmPNLlPZpfcE0KqXJ6uBVzQtmFqWbxs2xyzCOfxY,2115
|
|
364
370
|
templates/config_template/config.jinja2,sha256=A-p-YBsnEm1iftBIhuIBfIyCY-qw34KWcxalmM6KPWc,167
|
|
365
371
|
autogen/agentchat/contrib/captainagent/tools/README.md,sha256=454O-irP4gjSdYnFgoE0i3BfueXO0qFONxe3GMxMpHg,1677
|
|
366
372
|
autogen/agentchat/contrib/captainagent/tools/__init__.py,sha256=tOTe4nwbKj7elHpftAy3zS_embMDzncrKL98XKhY6-c,168
|
|
@@ -399,8 +405,8 @@ autogen/agentchat/contrib/captainagent/tools/math/modular_inverse_sum.py,sha256=
|
|
|
399
405
|
autogen/agentchat/contrib/captainagent/tools/math/simplify_mixed_numbers.py,sha256=iqgpFJdyBHPPNCqkehSIbeuV8Rabr2eDMilT23Wx7PI,1687
|
|
400
406
|
autogen/agentchat/contrib/captainagent/tools/math/sum_of_digit_factorials.py,sha256=-6T5r6Er4mONPldRxv3F9tLoE7Og3qmeSeTC7Du_tTg,596
|
|
401
407
|
autogen/agentchat/contrib/captainagent/tools/math/sum_of_primes_below.py,sha256=Xig7K3A3DRnbv-UXfyo5bybGZUQYAQsltthfTYW5eV8,509
|
|
402
|
-
ag2-0.9.
|
|
403
|
-
ag2-0.9.
|
|
404
|
-
ag2-0.9.
|
|
405
|
-
ag2-0.9.
|
|
406
|
-
ag2-0.9.
|
|
408
|
+
ag2-0.9.4.dist-info/METADATA,sha256=YnCG5kxaDTMI72s4N3OPKiuuepUwQVYLdKjgte3l6ZA,35268
|
|
409
|
+
ag2-0.9.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
410
|
+
ag2-0.9.4.dist-info/licenses/LICENSE,sha256=GEFQVNayAR-S_rQD5l8hPdgvgyktVdy4Bx5-v90IfRI,11384
|
|
411
|
+
ag2-0.9.4.dist-info/licenses/NOTICE.md,sha256=07iCPQGbth4pQrgkSgZinJGT5nXddkZ6_MGYcBd2oiY,1134
|
|
412
|
+
ag2-0.9.4.dist-info/RECORD,,
|
|
@@ -140,7 +140,7 @@ History:
|
|
|
140
140
|
|
|
141
141
|
According to the information I provide, please take one of four actions to manipulate list B using the functions you know.
|
|
142
142
|
Instead of returning TERMINATE directly or taking no action, you should try your best to optimize the function list. Only take no action if you really think the current list is optimal, as more actions will harm performance in future tasks.
|
|
143
|
-
Even adding a general function that can substitute the assistant
|
|
143
|
+
Even adding a general function that can substitute the assistant's repeated suggestions of Python code with the same functionality could also be helpful.
|
|
144
144
|
"""
|
|
145
145
|
|
|
146
146
|
|
|
@@ -176,14 +176,15 @@ class AgentOptimizer:
|
|
|
176
176
|
def __init__(
|
|
177
177
|
self,
|
|
178
178
|
max_actions_per_step: int,
|
|
179
|
-
llm_config: Union[LLMConfig, dict[str, Any]],
|
|
179
|
+
llm_config: Optional[Union[LLMConfig, dict[str, Any]]] = None,
|
|
180
180
|
optimizer_model: Optional[str] = "gpt-4-1106-preview",
|
|
181
181
|
):
|
|
182
182
|
"""(These APIs are experimental and may change in the future.)
|
|
183
183
|
|
|
184
184
|
Args:
|
|
185
185
|
max_actions_per_step (int): the maximum number of actions that the optimizer can take in one step.
|
|
186
|
-
llm_config (LLMConfig or dict): llm inference configuration.
|
|
186
|
+
llm_config (LLMConfig or dict or None): llm inference configuration.
|
|
187
|
+
If None, the current LLMConfig from context is used.
|
|
187
188
|
Please refer to [OpenAIWrapper.create](https://docs.ag2.ai/latest/docs/api-reference/autogen/OpenAIWrapper/#autogen.OpenAIWrapper.create) for available options.
|
|
188
189
|
When using OpenAI or Azure OpenAI endpoints, please specify a non-empty 'model' either in `llm_config` or in each config of 'config_list' in `llm_config`.
|
|
189
190
|
optimizer_model: the model used for the optimizer.
|
|
@@ -203,6 +204,8 @@ class AgentOptimizer:
|
|
|
203
204
|
self._failure_functions_performance = []
|
|
204
205
|
self._best_performance = -1
|
|
205
206
|
|
|
207
|
+
if llm_config is None:
|
|
208
|
+
llm_config = LLMConfig.current
|
|
206
209
|
assert isinstance(llm_config, (dict, LLMConfig)), "llm_config must be a dict or LLMConfig"
|
|
207
210
|
llm_config = copy.deepcopy(llm_config)
|
|
208
211
|
self.llm_config = llm_config
|
|
@@ -77,13 +77,13 @@ from .chat import (
|
|
|
77
77
|
initiate_chats,
|
|
78
78
|
)
|
|
79
79
|
from .group.context_variables import ContextVariables
|
|
80
|
+
from .group.guardrails import Guardrail
|
|
80
81
|
from .group.handoffs import Handoffs
|
|
81
82
|
from .utils import consolidate_chat_info, gather_usage_summary
|
|
82
83
|
|
|
83
84
|
if TYPE_CHECKING:
|
|
84
85
|
from .group.on_condition import OnCondition
|
|
85
86
|
from .group.on_context_condition import OnContextCondition
|
|
86
|
-
|
|
87
87
|
__all__ = ("ConversableAgent",)
|
|
88
88
|
|
|
89
89
|
logger = logging.getLogger(__name__)
|
|
@@ -224,6 +224,8 @@ class ConversableAgent(LLMAgent):
|
|
|
224
224
|
handoffs (Handoffs): Handoffs object containing all handoff transition conditions.
|
|
225
225
|
"""
|
|
226
226
|
self.handoffs = handoffs if handoffs is not None else Handoffs()
|
|
227
|
+
self.input_guardrails: list["Guardrail"] = []
|
|
228
|
+
self.output_guardrails: list["Guardrail"] = []
|
|
227
229
|
|
|
228
230
|
# we change code_execution_config below and we have to make sure we don't change the input
|
|
229
231
|
# in case of UserProxyAgent, without this we could even change the default value {}
|
|
@@ -1471,7 +1473,11 @@ class ConversableAgent(LLMAgent):
|
|
|
1471
1473
|
self.send(msg2send, recipient, request_reply=True, silent=silent)
|
|
1472
1474
|
|
|
1473
1475
|
else: # No breaks in the for loop, so we have reached max turns
|
|
1474
|
-
iostream.send(
|
|
1476
|
+
iostream.send(
|
|
1477
|
+
TerminationEvent(
|
|
1478
|
+
termination_reason=f"Maximum turns ({max_turns}) reached", sender=self, recipient=recipient
|
|
1479
|
+
)
|
|
1480
|
+
)
|
|
1475
1481
|
else:
|
|
1476
1482
|
self._prepare_chat(recipient, clear_history)
|
|
1477
1483
|
if isinstance(message, Callable):
|
|
@@ -1651,7 +1657,11 @@ class ConversableAgent(LLMAgent):
|
|
|
1651
1657
|
break
|
|
1652
1658
|
await self.a_send(msg2send, recipient, request_reply=True, silent=silent)
|
|
1653
1659
|
else: # No breaks in the for loop, so we have reached max turns
|
|
1654
|
-
iostream.send(
|
|
1660
|
+
iostream.send(
|
|
1661
|
+
TerminationEvent(
|
|
1662
|
+
termination_reason=f"Maximum turns ({max_turns}) reached", sender=self, recipient=recipient
|
|
1663
|
+
)
|
|
1664
|
+
)
|
|
1655
1665
|
else:
|
|
1656
1666
|
self._prepare_chat(recipient, clear_history)
|
|
1657
1667
|
if isinstance(message, Callable):
|
|
@@ -2587,7 +2597,7 @@ class ConversableAgent(LLMAgent):
|
|
|
2587
2597
|
self._consecutive_auto_reply_counter[sender] = 0
|
|
2588
2598
|
|
|
2589
2599
|
if termination_reason:
|
|
2590
|
-
iostream.send(TerminationEvent(termination_reason=termination_reason))
|
|
2600
|
+
iostream.send(TerminationEvent(termination_reason=termination_reason, sender=self, recipient=sender))
|
|
2591
2601
|
|
|
2592
2602
|
return True, None
|
|
2593
2603
|
|
|
@@ -2727,7 +2737,7 @@ class ConversableAgent(LLMAgent):
|
|
|
2727
2737
|
self._consecutive_auto_reply_counter[sender] = 0
|
|
2728
2738
|
|
|
2729
2739
|
if termination_reason:
|
|
2730
|
-
iostream.send(TerminationEvent(termination_reason=termination_reason))
|
|
2740
|
+
iostream.send(TerminationEvent(termination_reason=termination_reason, sender=self, recipient=sender))
|
|
2731
2741
|
|
|
2732
2742
|
return True, None
|
|
2733
2743
|
|
|
@@ -3994,6 +4004,42 @@ class ConversableAgent(LLMAgent):
|
|
|
3994
4004
|
"""
|
|
3995
4005
|
self.handoffs.add_many(conditions)
|
|
3996
4006
|
|
|
4007
|
+
def register_input_guardrail(self, guardrail: "Guardrail") -> None:
|
|
4008
|
+
"""
|
|
4009
|
+
Register a guardrail to be used for input validation.
|
|
4010
|
+
|
|
4011
|
+
Args:
|
|
4012
|
+
guardrail: The guardrail to register.
|
|
4013
|
+
"""
|
|
4014
|
+
self.input_guardrails.append(guardrail)
|
|
4015
|
+
|
|
4016
|
+
def register_input_guardrails(self, guardrails: list["Guardrail"]) -> None:
|
|
4017
|
+
"""
|
|
4018
|
+
Register multiple guardrails to be used for input validation.
|
|
4019
|
+
|
|
4020
|
+
Args:
|
|
4021
|
+
guardrails: List of guardrails to register.
|
|
4022
|
+
"""
|
|
4023
|
+
self.input_guardrails.extend(guardrails)
|
|
4024
|
+
|
|
4025
|
+
def register_output_guardrail(self, guardrail: "Guardrail") -> None:
|
|
4026
|
+
"""
|
|
4027
|
+
Register a guardrail to be used for output validation.
|
|
4028
|
+
|
|
4029
|
+
Args:
|
|
4030
|
+
guardrail: The guardrail to register.
|
|
4031
|
+
"""
|
|
4032
|
+
self.output_guardrails.append(guardrail)
|
|
4033
|
+
|
|
4034
|
+
def register_output_guardrails(self, guardrails: list["Guardrail"]) -> None:
|
|
4035
|
+
"""
|
|
4036
|
+
Register multiple guardrails to be used for output validation.
|
|
4037
|
+
|
|
4038
|
+
Args:
|
|
4039
|
+
guardrails: List of guardrails to register.
|
|
4040
|
+
"""
|
|
4041
|
+
self.output_guardrails.extend(guardrails)
|
|
4042
|
+
|
|
3997
4043
|
|
|
3998
4044
|
@export_module("autogen")
|
|
3999
4045
|
def register_function(
|
|
@@ -137,13 +137,7 @@ def _run_oncontextconditions(
|
|
|
137
137
|
if is_available and (
|
|
138
138
|
on_condition.condition is None or on_condition.condition.evaluate(agent.context_variables)
|
|
139
139
|
):
|
|
140
|
-
#
|
|
141
|
-
# attribute and that will be picked up on the next iteration when
|
|
142
|
-
# _determine_next_agent is called
|
|
143
|
-
for agent in agent._group_manager.groupchat.agents: # type: ignore[attr-defined]
|
|
144
|
-
if isinstance(agent, GroupToolExecutor):
|
|
145
|
-
agent.set_next_target(on_condition.target)
|
|
146
|
-
break
|
|
140
|
+
on_condition.target.activate_target(agent._group_manager.groupchat) # type: ignore[attr-defined]
|
|
147
141
|
|
|
148
142
|
transfer_name = on_condition.target.display_name()
|
|
149
143
|
|
|
@@ -212,6 +206,18 @@ def ensure_handoff_agents_in_group(agents: list["ConversableAgent"]) -> None:
|
|
|
212
206
|
raise ValueError("Agent in after work target Hand-offs must be in the agents list")
|
|
213
207
|
|
|
214
208
|
|
|
209
|
+
def ensure_guardrail_agents_in_group(agents: list["ConversableAgent"]) -> None:
|
|
210
|
+
"""Ensure the agents in handoffs are in the group chat."""
|
|
211
|
+
agent_names = [agent.name for agent in agents]
|
|
212
|
+
for agent in agents:
|
|
213
|
+
for guardrail in agent.input_guardrails + agent.output_guardrails:
|
|
214
|
+
if (
|
|
215
|
+
isinstance(guardrail.target, (AgentTarget, AgentNameTarget))
|
|
216
|
+
and guardrail.target.agent_name not in agent_names
|
|
217
|
+
):
|
|
218
|
+
raise ValueError("Agent in guardrail's target must be in the agents list")
|
|
219
|
+
|
|
220
|
+
|
|
215
221
|
def prepare_exclude_transit_messages(agents: list["ConversableAgent"]) -> None:
|
|
216
222
|
"""Preparation for excluding transit messages by getting all tool names and registering a hook on agents to remove those messages."""
|
|
217
223
|
# get all transit functions names
|
|
@@ -254,6 +260,9 @@ def prepare_group_agents(
|
|
|
254
260
|
# Ensure all agents in hand-off after-works are in the passed in agents list
|
|
255
261
|
ensure_handoff_agents_in_group(agents)
|
|
256
262
|
|
|
263
|
+
# Ensure all agents in guardrails are in the passed in agents list
|
|
264
|
+
ensure_guardrail_agents_in_group(agents)
|
|
265
|
+
|
|
257
266
|
# Create Tool Executor for the group
|
|
258
267
|
tool_execution = GroupToolExecutor()
|
|
259
268
|
|
|
@@ -0,0 +1,171 @@
|
|
|
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 json
|
|
6
|
+
import re
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from ...oai.client import OpenAIWrapper
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from ...llm_config import LLMConfig
|
|
16
|
+
from .targets.transition_target import TransitionTarget
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GuardrailResult(BaseModel):
|
|
20
|
+
"""Represents the outcome of a guardrail check."""
|
|
21
|
+
|
|
22
|
+
activated: bool
|
|
23
|
+
justification: str = Field(default="No justification provided")
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return f"Guardrail Result: {self.activated}\nJustification: {self.justification}"
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def parse(text: str) -> "GuardrailResult":
|
|
30
|
+
"""Parses a JSON string into a GuardrailResult object.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
text (str): The JSON string to parse.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
GuardrailResult: The parsed GuardrailResult object.
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
data = json.loads(text)
|
|
40
|
+
return GuardrailResult(**data)
|
|
41
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
42
|
+
raise ValueError(f"Failed to parse GuardrailResult from text: {text}") from e
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Guardrail(ABC):
|
|
46
|
+
"""Abstract base class for guardrails."""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self, name: str, condition: str, target: "TransitionTarget", activation_message: Optional[str] = None
|
|
50
|
+
) -> None:
|
|
51
|
+
self.name = name
|
|
52
|
+
self.condition = condition
|
|
53
|
+
self.target = target
|
|
54
|
+
self.activation_message = (
|
|
55
|
+
activation_message if activation_message else f"Guardrail '{name}' has been activated."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def check(
|
|
60
|
+
self,
|
|
61
|
+
context: Union[str, list[dict[str, Any]]],
|
|
62
|
+
) -> GuardrailResult:
|
|
63
|
+
"""Checks the text against the guardrail and returns a GuardrailResult.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
context (Union[str, list[dict[str, Any]]]): The context to check against the guardrail.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
GuardrailResult: The result of the guardrail check.
|
|
70
|
+
"""
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class LLMGuardrail(Guardrail):
|
|
75
|
+
"""Guardrail that uses an LLM to check the context."""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
name: str,
|
|
80
|
+
condition: str,
|
|
81
|
+
target: "TransitionTarget",
|
|
82
|
+
llm_config: "LLMConfig",
|
|
83
|
+
activation_message: Optional[str] = None,
|
|
84
|
+
) -> None:
|
|
85
|
+
super().__init__(name, condition, target, activation_message)
|
|
86
|
+
|
|
87
|
+
if not llm_config:
|
|
88
|
+
raise ValueError("LLMConfig is required.")
|
|
89
|
+
|
|
90
|
+
self.llm_config = llm_config.deepcopy()
|
|
91
|
+
setattr(self.llm_config, "response_format", GuardrailResult)
|
|
92
|
+
self.client = OpenAIWrapper(**self.llm_config.model_dump())
|
|
93
|
+
|
|
94
|
+
self.check_prompt = f"""You are a guardrail that checks if a condition is met in the conversation you are given.
|
|
95
|
+
You will activate the guardrail only if the condition is met.
|
|
96
|
+
|
|
97
|
+
**Condition: {self.condition}**"""
|
|
98
|
+
|
|
99
|
+
def check(
|
|
100
|
+
self,
|
|
101
|
+
context: Union[str, list[dict[str, Any]]],
|
|
102
|
+
) -> GuardrailResult:
|
|
103
|
+
"""Checks the context against the guardrail using an LLM.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
context (Union[str, list[dict[str, Any]]]): The context to check against the guardrail.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
GuardrailResult: The result of the guardrail check.
|
|
110
|
+
"""
|
|
111
|
+
# Set the check prompt as the system message
|
|
112
|
+
check_messages = [{"role": "system", "content": self.check_prompt}]
|
|
113
|
+
# If context is a string, wrap it in a user message and append it
|
|
114
|
+
if isinstance(context, str):
|
|
115
|
+
check_messages.append({"role": "user", "content": context})
|
|
116
|
+
# If context is a list of messages, append them
|
|
117
|
+
elif isinstance(context, list):
|
|
118
|
+
check_messages.extend(context)
|
|
119
|
+
else:
|
|
120
|
+
raise ValueError("Context must be a string or a list of messages.")
|
|
121
|
+
# Call the LLM with the check messages
|
|
122
|
+
response = self.client.create(messages=check_messages)
|
|
123
|
+
return GuardrailResult.parse(response.choices[0].message.content) # type: ignore
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class RegexGuardrail(Guardrail):
|
|
127
|
+
"""Guardrail that checks the context against a regular expression."""
|
|
128
|
+
|
|
129
|
+
def __init__(
|
|
130
|
+
self,
|
|
131
|
+
name: str,
|
|
132
|
+
condition: str,
|
|
133
|
+
target: "TransitionTarget",
|
|
134
|
+
activation_message: Optional[str] = None,
|
|
135
|
+
) -> None:
|
|
136
|
+
super().__init__(name, condition, target, activation_message)
|
|
137
|
+
# Compile the regular expression condition
|
|
138
|
+
try:
|
|
139
|
+
self.regex = re.compile(condition)
|
|
140
|
+
except re.error as e:
|
|
141
|
+
raise ValueError(f"Invalid regex pattern '{condition}': {str(e)}")
|
|
142
|
+
|
|
143
|
+
def check(
|
|
144
|
+
self,
|
|
145
|
+
context: Union[str, list[dict[str, Any]]],
|
|
146
|
+
) -> GuardrailResult:
|
|
147
|
+
"""Checks the context against the guardrail using a regular expression.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
context (Union[str, list[dict[str, Any]]]): The context to check against the guardrail.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
GuardrailResult: The result of the guardrail check.
|
|
154
|
+
"""
|
|
155
|
+
# Create a list of the messages to check
|
|
156
|
+
if isinstance(context, str):
|
|
157
|
+
messages = [context]
|
|
158
|
+
elif isinstance(context, list):
|
|
159
|
+
messages = [message.get("content", "") for message in context]
|
|
160
|
+
else:
|
|
161
|
+
raise ValueError("Context must be a string or a list of messages.")
|
|
162
|
+
|
|
163
|
+
# Check each message against the regex
|
|
164
|
+
for message in messages:
|
|
165
|
+
match = self.regex.search(message)
|
|
166
|
+
# If a match is found, activate the guardrail and return the result
|
|
167
|
+
if match:
|
|
168
|
+
activated = True
|
|
169
|
+
justification = f"Match found -> {match.group(0)}"
|
|
170
|
+
return GuardrailResult(activated=activated, justification=justification)
|
|
171
|
+
return GuardrailResult(activated=False, justification="No match found in the context.")
|
|
@@ -68,6 +68,16 @@ class TransitionTarget(BaseModel):
|
|
|
68
68
|
"""Create a wrapper agent for the target if needed."""
|
|
69
69
|
raise NotImplementedError("Requires subclasses to implement.")
|
|
70
70
|
|
|
71
|
+
def activate_target(self, groupchat: "GroupChat") -> None:
|
|
72
|
+
"""Activate the target in the groupchat, setting the next target for GroupToolExecutor.
|
|
73
|
+
|
|
74
|
+
The Tool Executor's next target attribute will be picked up on the next iteration when _determine_next_agent is called"""
|
|
75
|
+
for agent in groupchat.agents: # type: ignore[attr-defined]
|
|
76
|
+
# get the GroupToolExecutor agent
|
|
77
|
+
if type(agent).__name__ == "GroupToolExecutor":
|
|
78
|
+
agent.set_next_target(self) # type: ignore[attr-defined]
|
|
79
|
+
return
|
|
80
|
+
|
|
71
81
|
|
|
72
82
|
class AgentTarget(TransitionTarget):
|
|
73
83
|
"""Target that represents a direct agent reference."""
|