ag2 0.9.8.post1__py3-none-any.whl → 0.9.10__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.8.post1.dist-info → ag2-0.9.10.dist-info}/METADATA +232 -210
- {ag2-0.9.8.post1.dist-info → ag2-0.9.10.dist-info}/RECORD +88 -80
- autogen/_website/generate_mkdocs.py +3 -3
- autogen/_website/notebook_processor.py +1 -1
- autogen/_website/utils.py +1 -1
- autogen/agentchat/assistant_agent.py +15 -15
- autogen/agentchat/chat.py +52 -40
- autogen/agentchat/contrib/agent_eval/criterion.py +1 -1
- autogen/agentchat/contrib/capabilities/text_compressors.py +5 -5
- autogen/agentchat/contrib/capabilities/tools_capability.py +1 -1
- autogen/agentchat/contrib/capabilities/transforms.py +1 -1
- autogen/agentchat/contrib/captainagent/agent_builder.py +1 -1
- autogen/agentchat/contrib/captainagent/captainagent.py +20 -19
- autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py +2 -5
- autogen/agentchat/contrib/graph_rag/graph_rag_capability.py +5 -5
- autogen/agentchat/contrib/graph_rag/neo4j_graph_query_engine.py +18 -17
- autogen/agentchat/contrib/rag/mongodb_query_engine.py +2 -2
- autogen/agentchat/contrib/rag/query_engine.py +11 -11
- autogen/agentchat/contrib/retrieve_assistant_agent.py +3 -0
- autogen/agentchat/contrib/swarm_agent.py +3 -2
- autogen/agentchat/contrib/vectordb/couchbase.py +1 -1
- autogen/agentchat/contrib/vectordb/mongodb.py +1 -1
- autogen/agentchat/contrib/web_surfer.py +1 -1
- autogen/agentchat/conversable_agent.py +184 -80
- autogen/agentchat/group/context_expression.py +21 -21
- autogen/agentchat/group/handoffs.py +11 -11
- autogen/agentchat/group/multi_agent_chat.py +3 -2
- autogen/agentchat/group/on_condition.py +11 -11
- autogen/agentchat/group/safeguards/__init__.py +21 -0
- autogen/agentchat/group/safeguards/api.py +224 -0
- autogen/agentchat/group/safeguards/enforcer.py +1064 -0
- autogen/agentchat/group/safeguards/events.py +119 -0
- autogen/agentchat/group/safeguards/validator.py +435 -0
- autogen/agentchat/groupchat.py +60 -19
- autogen/agentchat/realtime/experimental/clients/realtime_client.py +2 -2
- autogen/agentchat/realtime/experimental/function_observer.py +2 -3
- autogen/agentchat/realtime/experimental/realtime_agent.py +2 -3
- autogen/agentchat/realtime/experimental/realtime_swarm.py +21 -10
- autogen/agentchat/user_proxy_agent.py +55 -53
- autogen/agents/experimental/document_agent/document_agent.py +1 -10
- autogen/agents/experimental/document_agent/parser_utils.py +5 -1
- autogen/browser_utils.py +4 -4
- autogen/cache/abstract_cache_base.py +2 -6
- autogen/cache/disk_cache.py +1 -6
- autogen/cache/in_memory_cache.py +2 -6
- autogen/cache/redis_cache.py +1 -5
- autogen/coding/__init__.py +10 -2
- autogen/coding/base.py +2 -1
- autogen/coding/docker_commandline_code_executor.py +1 -6
- autogen/coding/factory.py +9 -0
- autogen/coding/jupyter/docker_jupyter_server.py +1 -7
- autogen/coding/jupyter/jupyter_client.py +2 -9
- autogen/coding/jupyter/jupyter_code_executor.py +2 -7
- autogen/coding/jupyter/local_jupyter_server.py +2 -6
- autogen/coding/local_commandline_code_executor.py +0 -65
- autogen/coding/yepcode_code_executor.py +197 -0
- autogen/environments/docker_python_environment.py +3 -3
- autogen/environments/system_python_environment.py +5 -5
- autogen/environments/venv_python_environment.py +5 -5
- autogen/events/agent_events.py +1 -1
- autogen/events/client_events.py +1 -1
- autogen/fast_depends/utils.py +10 -0
- autogen/graph_utils.py +5 -7
- autogen/import_utils.py +28 -15
- autogen/interop/pydantic_ai/pydantic_ai.py +8 -5
- autogen/io/processors/console_event_processor.py +8 -3
- autogen/llm_config/config.py +168 -91
- autogen/llm_config/entry.py +38 -26
- autogen/llm_config/types.py +35 -0
- autogen/llm_config/utils.py +223 -0
- autogen/mcp/mcp_proxy/operation_grouping.py +48 -39
- autogen/messages/agent_messages.py +1 -1
- autogen/messages/client_messages.py +1 -1
- autogen/oai/__init__.py +8 -1
- autogen/oai/client.py +10 -3
- autogen/oai/client_utils.py +1 -1
- autogen/oai/cohere.py +4 -4
- autogen/oai/gemini.py +4 -6
- autogen/oai/gemini_types.py +1 -0
- autogen/oai/openai_utils.py +44 -115
- autogen/tools/dependency_injection.py +4 -8
- autogen/tools/experimental/reliable/reliable.py +3 -2
- autogen/tools/experimental/web_search_preview/web_search_preview.py +1 -1
- autogen/tools/function_utils.py +2 -1
- autogen/version.py +1 -1
- {ag2-0.9.8.post1.dist-info → ag2-0.9.10.dist-info}/WHEEL +0 -0
- {ag2-0.9.8.post1.dist-info → ag2-0.9.10.dist-info}/licenses/LICENSE +0 -0
- {ag2-0.9.8.post1.dist-info → ag2-0.9.10.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -8,7 +8,6 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import atexit
|
|
10
10
|
import logging
|
|
11
|
-
import sys
|
|
12
11
|
import uuid
|
|
13
12
|
from hashlib import md5
|
|
14
13
|
from pathlib import Path
|
|
@@ -18,6 +17,7 @@ from typing import Any, ClassVar
|
|
|
18
17
|
|
|
19
18
|
import docker
|
|
20
19
|
from docker.errors import ImageNotFound
|
|
20
|
+
from typing_extensions import Self
|
|
21
21
|
|
|
22
22
|
from ..code_utils import TIMEOUT_MSG, _cmd
|
|
23
23
|
from ..doc_utils import export_module
|
|
@@ -25,11 +25,6 @@ from .base import CodeBlock, CodeExecutor, CodeExtractor, CommandLineCodeResult
|
|
|
25
25
|
from .markdown_code_extractor import MarkdownCodeExtractor
|
|
26
26
|
from .utils import _get_file_name_from_content, silence_pip
|
|
27
27
|
|
|
28
|
-
if sys.version_info >= (3, 11):
|
|
29
|
-
from typing import Self
|
|
30
|
-
else:
|
|
31
|
-
from typing_extensions import Self
|
|
32
|
-
|
|
33
28
|
|
|
34
29
|
def _wait_for_ready(container: Any, timeout: int = 60, stop_time: float = 0.1) -> None:
|
|
35
30
|
elapsed_time = 0.0
|
autogen/coding/factory.py
CHANGED
|
@@ -43,5 +43,14 @@ class CodeExecutorFactory:
|
|
|
43
43
|
from .local_commandline_code_executor import LocalCommandLineCodeExecutor
|
|
44
44
|
|
|
45
45
|
return LocalCommandLineCodeExecutor(**code_execution_config.get("commandline-local", {}))
|
|
46
|
+
elif executor == "yepcode":
|
|
47
|
+
try:
|
|
48
|
+
from .yepcode_code_executor import YepCodeCodeExecutor
|
|
49
|
+
except ImportError as e:
|
|
50
|
+
raise ImportError(
|
|
51
|
+
"Missing dependencies for YepCodeCodeExecutor. Please install with: pip install ag2[yepcode]"
|
|
52
|
+
) from e
|
|
53
|
+
|
|
54
|
+
return YepCodeCodeExecutor(**code_execution_config.get("yepcode", {}))
|
|
46
55
|
else:
|
|
47
56
|
raise ValueError(f"Unknown code executor {executor}")
|
|
@@ -10,20 +10,14 @@ import atexit
|
|
|
10
10
|
import io
|
|
11
11
|
import logging
|
|
12
12
|
import secrets
|
|
13
|
-
import sys
|
|
14
13
|
import uuid
|
|
15
14
|
from pathlib import Path
|
|
16
15
|
from types import TracebackType
|
|
17
16
|
|
|
18
17
|
import docker
|
|
18
|
+
from typing_extensions import Self
|
|
19
19
|
|
|
20
20
|
from ...doc_utils import export_module
|
|
21
|
-
|
|
22
|
-
if sys.version_info >= (3, 11):
|
|
23
|
-
from typing import Self
|
|
24
|
-
else:
|
|
25
|
-
from typing_extensions import Self
|
|
26
|
-
|
|
27
21
|
from ..docker_commandline_code_executor import _wait_for_ready
|
|
28
22
|
from .base import JupyterConnectable, JupyterConnectionInfo
|
|
29
23
|
from .import_utils import require_jupyter_kernel_gateway_installed
|
|
@@ -8,23 +8,16 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import datetime
|
|
10
10
|
import json
|
|
11
|
-
import sys
|
|
12
11
|
import uuid
|
|
13
12
|
from dataclasses import dataclass
|
|
14
13
|
from types import TracebackType
|
|
15
14
|
from typing import Any, cast
|
|
16
15
|
|
|
17
|
-
from ...doc_utils import export_module
|
|
18
|
-
|
|
19
|
-
if sys.version_info >= (3, 11):
|
|
20
|
-
from typing import Self
|
|
21
|
-
else:
|
|
22
|
-
from typing_extensions import Self
|
|
23
|
-
|
|
24
|
-
|
|
25
16
|
import requests
|
|
26
17
|
from requests.adapters import HTTPAdapter, Retry
|
|
18
|
+
from typing_extensions import Self
|
|
27
19
|
|
|
20
|
+
from ...doc_utils import export_module
|
|
28
21
|
from ...import_utils import optional_import_block, require_optional_import
|
|
29
22
|
from .base import JupyterConnectionInfo
|
|
30
23
|
|
|
@@ -7,18 +7,13 @@
|
|
|
7
7
|
import base64
|
|
8
8
|
import json
|
|
9
9
|
import os
|
|
10
|
-
import sys
|
|
11
10
|
import uuid
|
|
12
11
|
from pathlib import Path
|
|
13
12
|
from types import TracebackType
|
|
14
13
|
|
|
15
|
-
from
|
|
16
|
-
|
|
17
|
-
if sys.version_info >= (3, 11):
|
|
18
|
-
from typing import Self
|
|
19
|
-
else:
|
|
20
|
-
from typing_extensions import Self
|
|
14
|
+
from typing_extensions import Self
|
|
21
15
|
|
|
16
|
+
from ...doc_utils import export_module
|
|
22
17
|
from ..base import CodeBlock, CodeExecutor, CodeExtractor, IPythonCodeResult
|
|
23
18
|
from ..markdown_code_extractor import MarkdownCodeExtractor
|
|
24
19
|
from ..utils import silence_pip
|
|
@@ -14,13 +14,9 @@ import subprocess
|
|
|
14
14
|
import sys
|
|
15
15
|
from types import TracebackType
|
|
16
16
|
|
|
17
|
-
from
|
|
18
|
-
|
|
19
|
-
if sys.version_info >= (3, 11):
|
|
20
|
-
from typing import Self
|
|
21
|
-
else:
|
|
22
|
-
from typing_extensions import Self
|
|
17
|
+
from typing_extensions import Self
|
|
23
18
|
|
|
19
|
+
from ...doc_utils import export_module
|
|
24
20
|
from .base import JupyterConnectable, JupyterConnectionInfo
|
|
25
21
|
from .import_utils import require_jupyter_kernel_gateway_installed
|
|
26
22
|
from .jupyter_client import JupyterClient
|
|
@@ -339,68 +339,3 @@ $functions"""
|
|
|
339
339
|
def restart(self) -> None:
|
|
340
340
|
"""(Experimental) Restart the code executor."""
|
|
341
341
|
warnings.warn("Restarting local command line code executor is not supported. No action is taken.")
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
# From stack overflow: https://stackoverflow.com/a/52087847/2214524
|
|
345
|
-
class _DeprecatedClassMeta(type):
|
|
346
|
-
def __new__(cls, name, bases, classdict, *args, **kwargs): # type: ignore[no-untyped-def]
|
|
347
|
-
alias = classdict.get("_DeprecatedClassMeta__alias")
|
|
348
|
-
|
|
349
|
-
if alias is not None:
|
|
350
|
-
|
|
351
|
-
def new(cls, *args, **kwargs): # type: ignore[no-untyped-def]
|
|
352
|
-
alias = cls._DeprecatedClassMeta__alias
|
|
353
|
-
|
|
354
|
-
if alias is not None:
|
|
355
|
-
warnings.warn(
|
|
356
|
-
f"{cls.__name__} has been renamed to {alias.__name__}, the alias will be removed in the future",
|
|
357
|
-
DeprecationWarning,
|
|
358
|
-
stacklevel=2,
|
|
359
|
-
)
|
|
360
|
-
|
|
361
|
-
return alias(*args, **kwargs)
|
|
362
|
-
|
|
363
|
-
classdict["__new__"] = new
|
|
364
|
-
classdict["_DeprecatedClassMeta__alias"] = alias
|
|
365
|
-
|
|
366
|
-
fixed_bases = []
|
|
367
|
-
|
|
368
|
-
for b in bases:
|
|
369
|
-
alias = getattr(b, "_DeprecatedClassMeta__alias", None)
|
|
370
|
-
|
|
371
|
-
if alias is not None:
|
|
372
|
-
warnings.warn(
|
|
373
|
-
f"{b.__name__} has been renamed to {alias.__name__}, the alias will be removed in the future",
|
|
374
|
-
DeprecationWarning,
|
|
375
|
-
stacklevel=2,
|
|
376
|
-
)
|
|
377
|
-
|
|
378
|
-
# Avoid duplicate base classes.
|
|
379
|
-
b = alias or b
|
|
380
|
-
if b not in fixed_bases:
|
|
381
|
-
fixed_bases.append(b)
|
|
382
|
-
|
|
383
|
-
fixed_bases = tuple(fixed_bases) # type: ignore[assignment]
|
|
384
|
-
|
|
385
|
-
return super().__new__(cls, name, fixed_bases, classdict, *args, **kwargs) # type: ignore[call-overload]
|
|
386
|
-
|
|
387
|
-
def __instancecheck__(cls, instance): # type: ignore[no-untyped-def]
|
|
388
|
-
return any(cls.__subclasscheck__(c) for c in {type(instance), instance.__class__}) # type: ignore[no-untyped-call]
|
|
389
|
-
|
|
390
|
-
def __subclasscheck__(cls, subclass): # type: ignore[no-untyped-def]
|
|
391
|
-
if subclass is cls:
|
|
392
|
-
return True
|
|
393
|
-
else:
|
|
394
|
-
return issubclass(subclass, cls._DeprecatedClassMeta__alias) # type: ignore[attr-defined]
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
class LocalCommandlineCodeExecutor(metaclass=_DeprecatedClassMeta):
|
|
398
|
-
"""LocalCommandlineCodeExecutor renamed to LocalCommandLineCodeExecutor"""
|
|
399
|
-
|
|
400
|
-
_DeprecatedClassMeta__alias = LocalCommandLineCodeExecutor
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
class CommandlineCodeResult(metaclass=_DeprecatedClassMeta):
|
|
404
|
-
"""CommandlineCodeResult renamed to CommandLineCodeResult"""
|
|
405
|
-
|
|
406
|
-
_DeprecatedClassMeta__alias = CommandLineCodeResult
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
# Portions derived from https://github.com/microsoft/autogen are under the MIT License.
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
"""YepCode code executor implementation."""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from typing import ClassVar
|
|
12
|
+
|
|
13
|
+
from pydantic import Field
|
|
14
|
+
|
|
15
|
+
from ..doc_utils import export_module
|
|
16
|
+
from .base import CodeBlock, CodeExecutor, CodeExtractor, CodeResult
|
|
17
|
+
from .markdown_code_extractor import MarkdownCodeExtractor
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from dotenv import load_dotenv
|
|
21
|
+
|
|
22
|
+
_load_dotenv: Callable[[], bool] | None = load_dotenv
|
|
23
|
+
except ImportError:
|
|
24
|
+
_load_dotenv = None
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from yepcode_run import YepCodeApiConfig, YepCodeRun
|
|
28
|
+
except ImportError:
|
|
29
|
+
YepCodeRun = None
|
|
30
|
+
YepCodeApiConfig = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@export_module("autogen.coding")
|
|
34
|
+
class YepCodeCodeResult(CodeResult):
|
|
35
|
+
"""A code result class for YepCode executor."""
|
|
36
|
+
|
|
37
|
+
execution_id: str | None = Field(default=None, description="The YepCode execution ID for this result.")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@export_module("autogen.coding")
|
|
41
|
+
class YepCodeCodeExecutor(CodeExecutor):
|
|
42
|
+
"""A code executor class that executes code using YepCode's serverless runtime.
|
|
43
|
+
|
|
44
|
+
This executor runs code in YepCode's secure, production-grade sandboxes.
|
|
45
|
+
It supports Python and JavaScript execution with access to any external library with automatic discovery and installation.
|
|
46
|
+
|
|
47
|
+
The executor executes code blocks serially in the order they are received.
|
|
48
|
+
Each code block is executed in a separate YepCode execution environment.
|
|
49
|
+
Currently supports Python and JavaScript languages.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
api_token (Optional[str]): YepCode API token. If None, will try to get from YEPCODE_API_TOKEN environment variable.
|
|
53
|
+
timeout (int): The timeout for code execution in seconds. Default is 60.
|
|
54
|
+
remove_on_done (bool): Whether to remove the execution after completion. Default is False.
|
|
55
|
+
sync_execution (bool): Whether to wait for execution to complete. Default is True.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
ImportError: If yepcode-run package is not installed.
|
|
59
|
+
ValueError: If YepCode API token is not provided or timeout is invalid.
|
|
60
|
+
RuntimeError: If YepCode runner initialization fails.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
SUPPORTED_LANGUAGES: ClassVar[list[str]] = ["python", "javascript"]
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
api_token: str | None = None,
|
|
68
|
+
timeout: int = 60,
|
|
69
|
+
remove_on_done: bool = False,
|
|
70
|
+
sync_execution: bool = True,
|
|
71
|
+
):
|
|
72
|
+
if YepCodeRun is None or YepCodeApiConfig is None:
|
|
73
|
+
raise ImportError(
|
|
74
|
+
"Missing dependencies for YepCodeCodeExecutor. Please install with: pip install ag2[yepcode]"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if timeout < 1:
|
|
78
|
+
raise ValueError("Timeout must be greater than or equal to 1.")
|
|
79
|
+
|
|
80
|
+
# Load environment variables from .env file if dotenv is available
|
|
81
|
+
if _load_dotenv is not None:
|
|
82
|
+
_load_dotenv()
|
|
83
|
+
|
|
84
|
+
# Get API token from parameter or environment
|
|
85
|
+
self._api_token = api_token or os.getenv("YEPCODE_API_TOKEN")
|
|
86
|
+
if not self._api_token:
|
|
87
|
+
raise ValueError(
|
|
88
|
+
"YepCode API token is required. Provide it via api_token parameter or YEPCODE_API_TOKEN environment variable."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
self._timeout = timeout
|
|
92
|
+
self._remove_on_done = remove_on_done
|
|
93
|
+
self._sync_execution = sync_execution
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
config = YepCodeApiConfig(api_token=self._api_token)
|
|
97
|
+
self._runner = YepCodeRun(config)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
raise RuntimeError(f"Failed to initialize YepCode runner: {str(e)}") from e
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def code_extractor(self) -> CodeExtractor:
|
|
103
|
+
"""(Experimental) Export a code extractor that can be used by an agent."""
|
|
104
|
+
return MarkdownCodeExtractor()
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def timeout(self) -> int:
|
|
108
|
+
"""The timeout for code execution."""
|
|
109
|
+
return self._timeout
|
|
110
|
+
|
|
111
|
+
def _normalize_language(self, language: str) -> str:
|
|
112
|
+
"""Normalize language name to YepCode format."""
|
|
113
|
+
lang = language.lower()
|
|
114
|
+
if lang in ["js", "javascript"]:
|
|
115
|
+
return "javascript"
|
|
116
|
+
elif lang in ["python", "py"]:
|
|
117
|
+
return "python"
|
|
118
|
+
else:
|
|
119
|
+
return lang
|
|
120
|
+
|
|
121
|
+
def execute_code_blocks(self, code_blocks: list[CodeBlock]) -> YepCodeCodeResult:
|
|
122
|
+
"""Execute the code blocks and return the result.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
code_blocks (List[CodeBlock]): The code blocks to execute.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
YepCodeCodeResult: The result of the code execution.
|
|
129
|
+
"""
|
|
130
|
+
if not code_blocks:
|
|
131
|
+
return YepCodeCodeResult(exit_code=0, output="")
|
|
132
|
+
|
|
133
|
+
outputs: list[str] = []
|
|
134
|
+
last_execution_id: str | None = None
|
|
135
|
+
|
|
136
|
+
for code_block in code_blocks:
|
|
137
|
+
lang = self._normalize_language(code_block.language)
|
|
138
|
+
|
|
139
|
+
if lang not in ["python", "javascript"]:
|
|
140
|
+
return YepCodeCodeResult(
|
|
141
|
+
exit_code=1,
|
|
142
|
+
output=f"Unsupported language: {code_block.language}. Supported languages: {', '.join(self.SUPPORTED_LANGUAGES)}",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
# Execute code using YepCode
|
|
147
|
+
execution = self._runner.run(
|
|
148
|
+
code_block.code,
|
|
149
|
+
{
|
|
150
|
+
"language": lang,
|
|
151
|
+
"removeOnDone": self._remove_on_done,
|
|
152
|
+
"timeout": self._timeout * 1000, # Convert to milliseconds
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
last_execution_id = execution.id
|
|
157
|
+
|
|
158
|
+
if self._sync_execution:
|
|
159
|
+
# Wait for execution to complete
|
|
160
|
+
execution.wait_for_done()
|
|
161
|
+
|
|
162
|
+
logs_output = ""
|
|
163
|
+
# Get logs
|
|
164
|
+
if execution.logs:
|
|
165
|
+
logs_output = "\n\nExecution logs:\n" + "\n".join([
|
|
166
|
+
f"{log.timestamp} - {log.level}: {log.message}" for log in execution.logs
|
|
167
|
+
])
|
|
168
|
+
|
|
169
|
+
# Check if execution was successful
|
|
170
|
+
if execution.error:
|
|
171
|
+
output = f"Execution failed with error:\n{execution.error}{logs_output}"
|
|
172
|
+
|
|
173
|
+
return YepCodeCodeResult(exit_code=1, output=output, execution_id=execution.id)
|
|
174
|
+
|
|
175
|
+
# Get output
|
|
176
|
+
output = ""
|
|
177
|
+
if execution.return_value:
|
|
178
|
+
output = f"Execution result:\n{execution.return_value}"
|
|
179
|
+
|
|
180
|
+
output += logs_output
|
|
181
|
+
|
|
182
|
+
outputs.append(output)
|
|
183
|
+
else:
|
|
184
|
+
outputs.append(f"Execution started with ID: {execution.id}")
|
|
185
|
+
|
|
186
|
+
except Exception as e:
|
|
187
|
+
return YepCodeCodeResult(
|
|
188
|
+
exit_code=1,
|
|
189
|
+
output=f"Error executing code: {str(e)}",
|
|
190
|
+
execution_id=last_execution_id,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return YepCodeCodeResult(exit_code=0, output="\n===\n".join(outputs), execution_id=last_execution_id)
|
|
194
|
+
|
|
195
|
+
def restart(self) -> None:
|
|
196
|
+
"""Restart the code executor."""
|
|
197
|
+
pass
|
|
@@ -10,7 +10,7 @@ import tempfile
|
|
|
10
10
|
import uuid
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
-
from
|
|
13
|
+
from anyio import to_thread
|
|
14
14
|
|
|
15
15
|
from .python_environment import PythonEnvironment
|
|
16
16
|
|
|
@@ -320,7 +320,7 @@ class DockerPythonEnvironment(PythonEnvironment):
|
|
|
320
320
|
os.makedirs(script_dir, exist_ok=True)
|
|
321
321
|
|
|
322
322
|
# Write the code to the script file on the host
|
|
323
|
-
await
|
|
323
|
+
await to_thread.run_sync(self._write_to_file, host_script_path, code)
|
|
324
324
|
|
|
325
325
|
# Path to the script in the container
|
|
326
326
|
container_script_path = f"/workspace/{rel_path}"
|
|
@@ -329,7 +329,7 @@ class DockerPythonEnvironment(PythonEnvironment):
|
|
|
329
329
|
exec_cmd = ["docker", "exec", self._container_name, "python", container_script_path]
|
|
330
330
|
|
|
331
331
|
# Run the command with a timeout
|
|
332
|
-
result = await
|
|
332
|
+
result = await to_thread.run_sync(self._run_subprocess_with_timeout, exec_cmd, timeout)
|
|
333
333
|
|
|
334
334
|
return {
|
|
335
335
|
"success": result[0],
|
|
@@ -8,7 +8,7 @@ import subprocess
|
|
|
8
8
|
import sys
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
-
from
|
|
11
|
+
from anyio import to_thread
|
|
12
12
|
|
|
13
13
|
from .python_environment import PythonEnvironment
|
|
14
14
|
|
|
@@ -62,14 +62,14 @@ class SystemPythonEnvironment(PythonEnvironment):
|
|
|
62
62
|
if script_dir:
|
|
63
63
|
os.makedirs(script_dir, exist_ok=True)
|
|
64
64
|
|
|
65
|
-
# Write the code to the script file using
|
|
66
|
-
await
|
|
65
|
+
# Write the code to the script file using anyio.to_thread.run_sync (from base class)
|
|
66
|
+
await to_thread.run_sync(self._write_to_file, script_path, code)
|
|
67
67
|
|
|
68
68
|
logging.info(f"Wrote code to {script_path}")
|
|
69
69
|
|
|
70
70
|
try:
|
|
71
|
-
# Execute directly with subprocess using
|
|
72
|
-
result = await
|
|
71
|
+
# Execute directly with subprocess using anyio.to_thread.run_sync for better reliability
|
|
72
|
+
result = await to_thread.run_sync(self._run_subprocess, [python_executable, script_path], timeout)
|
|
73
73
|
|
|
74
74
|
# Main execution result
|
|
75
75
|
return {
|
|
@@ -9,7 +9,7 @@ import sys
|
|
|
9
9
|
import tempfile
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from anyio import to_thread
|
|
13
13
|
|
|
14
14
|
from .python_environment import PythonEnvironment
|
|
15
15
|
|
|
@@ -132,14 +132,14 @@ class VenvPythonEnvironment(PythonEnvironment):
|
|
|
132
132
|
if script_dir:
|
|
133
133
|
os.makedirs(script_dir, exist_ok=True)
|
|
134
134
|
|
|
135
|
-
# Write the code to the script file using
|
|
136
|
-
await
|
|
135
|
+
# Write the code to the script file using anyio.to_thread.run_sync (from base class)
|
|
136
|
+
await to_thread.run_sync(self._write_to_file, script_path, code)
|
|
137
137
|
|
|
138
138
|
logging.info(f"Wrote code to {script_path}")
|
|
139
139
|
|
|
140
140
|
try:
|
|
141
|
-
# Execute directly with subprocess using
|
|
142
|
-
result = await
|
|
141
|
+
# Execute directly with subprocess using anyio.to_thread.run_sync for better reliability
|
|
142
|
+
result = await to_thread.run_sync(self._run_subprocess, [python_executable, script_path], timeout)
|
|
143
143
|
|
|
144
144
|
# Main execution result
|
|
145
145
|
return {
|
autogen/events/agent_events.py
CHANGED
|
@@ -946,7 +946,7 @@ class GenerateCodeExecutionReplyEvent(BaseEvent):
|
|
|
946
946
|
else:
|
|
947
947
|
f(
|
|
948
948
|
colored(
|
|
949
|
-
f"\n>>>>>>>> EXECUTING {num_code_blocks} CODE BLOCKS (inferred languages are [{', '.join(
|
|
949
|
+
f"\n>>>>>>>> EXECUTING {num_code_blocks} CODE BLOCKS (inferred languages are [{', '.join(list(self.code_blocks))}])...",
|
|
950
950
|
"red",
|
|
951
951
|
),
|
|
952
952
|
flush=True,
|
autogen/events/client_events.py
CHANGED
|
@@ -62,7 +62,7 @@ def _change_usage_summary_format(
|
|
|
62
62
|
usage_summary_altered_format: dict[str, list[dict[str, Any]]] = {"usages": []}
|
|
63
63
|
for k, v in usage_summary.items():
|
|
64
64
|
if isinstance(k, str) and isinstance(v, dict):
|
|
65
|
-
current_usage =
|
|
65
|
+
current_usage = dict(v.items())
|
|
66
66
|
current_usage["model"] = k
|
|
67
67
|
usage_summary_altered_format["usages"].append(current_usage)
|
|
68
68
|
else:
|
autogen/fast_depends/utils.py
CHANGED
|
@@ -26,6 +26,16 @@ P = ParamSpec("P")
|
|
|
26
26
|
T = TypeVar("T")
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def asyncify(func: Callable[P, T]) -> Callable[P, Awaitable[T]]:
|
|
30
|
+
if is_coroutine_callable(func):
|
|
31
|
+
return cast(Callable[P, Awaitable[T]], func)
|
|
32
|
+
|
|
33
|
+
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
34
|
+
return await run_in_threadpool(func, *args, **kwargs)
|
|
35
|
+
|
|
36
|
+
return wrapper
|
|
37
|
+
|
|
38
|
+
|
|
29
39
|
async def run_async(
|
|
30
40
|
func: Callable[P, T] | Callable[P, Awaitable[T]],
|
|
31
41
|
*args: P.args,
|
autogen/graph_utils.py
CHANGED
|
@@ -24,7 +24,7 @@ def has_self_loops(allowed_speaker_transitions: dict[str, list[Agent]]) -> bool:
|
|
|
24
24
|
Returns:
|
|
25
25
|
True if there are self loops in the allowed_speaker_transitions_Dict.
|
|
26
26
|
"""
|
|
27
|
-
return any(
|
|
27
|
+
return any(key in value for key, value in allowed_speaker_transitions.items())
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def check_graph_validity(
|
|
@@ -58,17 +58,15 @@ def check_graph_validity(
|
|
|
58
58
|
raise ValueError("allowed_speaker_transitions_dict must be a dictionary.")
|
|
59
59
|
|
|
60
60
|
# All values must be lists of Agent or empty
|
|
61
|
-
if not all(
|
|
61
|
+
if not all(isinstance(value, list) for value in allowed_speaker_transitions_dict.values()):
|
|
62
62
|
raise ValueError("allowed_speaker_transitions_dict must be a dictionary with lists as values.")
|
|
63
63
|
|
|
64
64
|
# Check 2. Every key exists in agents
|
|
65
|
-
if not all(
|
|
65
|
+
if not all(key in agents for key in allowed_speaker_transitions_dict):
|
|
66
66
|
raise ValueError("allowed_speaker_transitions_dict has keys not in agents.")
|
|
67
67
|
|
|
68
68
|
# Check 3. Every value is a list of Agents or empty list (not string).
|
|
69
|
-
if not all(
|
|
70
|
-
all([isinstance(agent, Agent) for agent in value]) for value in allowed_speaker_transitions_dict.values()
|
|
71
|
-
]):
|
|
69
|
+
if not all(all(isinstance(agent, Agent) for agent in value) for value in allowed_speaker_transitions_dict.values()):
|
|
72
70
|
raise ValueError("allowed_speaker_transitions_dict has values that are not lists of Agents.")
|
|
73
71
|
|
|
74
72
|
# Warnings
|
|
@@ -129,7 +127,7 @@ def invert_disallowed_to_allowed(
|
|
|
129
127
|
allowed_speaker_transitions_dict: A dictionary of keys and list as values. The keys are the names of the agents, and the values are the names of the agents that the key agent can transition to.
|
|
130
128
|
"""
|
|
131
129
|
# Create a fully connected allowed_speaker_transitions_dict of all agents
|
|
132
|
-
allowed_speaker_transitions_dict = {agent:
|
|
130
|
+
allowed_speaker_transitions_dict = {agent: list(agents) for agent in agents}
|
|
133
131
|
|
|
134
132
|
# Remove edges from allowed_speaker_transitions_dict according to the disallowed_speaker_transitions_dict
|
|
135
133
|
for key, value in disallowed_speaker_transitions_dict.items():
|
autogen/import_utils.py
CHANGED
|
@@ -14,6 +14,10 @@ from logging import getLogger
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from typing import Any, Generic, Optional, TypeVar
|
|
16
16
|
|
|
17
|
+
from packaging import version
|
|
18
|
+
|
|
19
|
+
from .fast_depends.utils import is_coroutine_callable
|
|
20
|
+
|
|
17
21
|
__all__ = [
|
|
18
22
|
"optional_import_block",
|
|
19
23
|
"patch_object",
|
|
@@ -53,25 +57,34 @@ class ModuleInfo:
|
|
|
53
57
|
# Aka similarly named module in the autogen or test directory
|
|
54
58
|
return f"'{self.name}' is not installed."
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
# Ensure that the retrieved version is a string. Some packages might unexpectedly
|
|
61
|
+
# have a __version__ attribute that is not a string (e.g., a module).
|
|
62
|
+
raw_version_attr = (
|
|
57
63
|
sys.modules[self.name].__version__ if hasattr(sys.modules[self.name], "__version__") else None
|
|
58
64
|
)
|
|
65
|
+
installed_version = raw_version_attr if isinstance(raw_version_attr, str) else None
|
|
59
66
|
if installed_version is None and (self.min_version or self.max_version):
|
|
60
67
|
return f"'{self.name}' is installed, but the version is not available."
|
|
61
68
|
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if self.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
if installed_version:
|
|
70
|
+
# Convert to version object for comparison
|
|
71
|
+
installed_ver = version.parse(installed_version)
|
|
72
|
+
|
|
73
|
+
if self.min_version:
|
|
74
|
+
min_ver = version.parse(self.min_version)
|
|
75
|
+
msg = f"'{self.name}' is installed, but the installed version {installed_version} is too low (required '{self}')."
|
|
76
|
+
if not self.min_inclusive and installed_ver == min_ver:
|
|
77
|
+
return msg
|
|
78
|
+
if self.min_inclusive and installed_ver < min_ver:
|
|
79
|
+
return msg
|
|
80
|
+
|
|
81
|
+
if self.max_version:
|
|
82
|
+
max_ver = version.parse(self.max_version)
|
|
83
|
+
msg = f"'{self.name}' is installed, but the installed version {installed_version} is too high (required '{self}')."
|
|
84
|
+
if not self.max_inclusive and installed_ver == max_ver:
|
|
85
|
+
return msg
|
|
86
|
+
if self.max_inclusive and installed_ver > max_ver:
|
|
87
|
+
return msg
|
|
75
88
|
|
|
76
89
|
return None
|
|
77
90
|
|
|
@@ -470,7 +483,7 @@ def run_for_optional_imports(modules: str | Iterable[str], dep_target: str) -> C
|
|
|
470
483
|
if isinstance(o, type):
|
|
471
484
|
wrapped = require_optional_import(modules, dep_target)(o)
|
|
472
485
|
else:
|
|
473
|
-
if
|
|
486
|
+
if is_coroutine_callable(o):
|
|
474
487
|
|
|
475
488
|
@wraps(o)
|
|
476
489
|
async def wrapped(*args: Any, **kwargs: Any) -> Any:
|
|
@@ -63,19 +63,22 @@ class PydanticAIInteroperability:
|
|
|
63
63
|
|
|
64
64
|
@wraps(f)
|
|
65
65
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
66
|
-
if tool_typed.
|
|
66
|
+
current_retry = 0 if ctx_typed is None else ctx_typed.retries.get(tool_typed.name, 0)
|
|
67
|
+
|
|
68
|
+
if current_retry >= max_retries:
|
|
67
69
|
raise ValueError(f"{tool_typed.name} failed after {max_retries} retries")
|
|
68
70
|
|
|
69
71
|
try:
|
|
70
72
|
if ctx_typed is not None:
|
|
71
73
|
kwargs.pop("ctx", None)
|
|
72
|
-
ctx_typed.retry =
|
|
74
|
+
ctx_typed.retry = current_retry
|
|
73
75
|
result = f(**kwargs, ctx=ctx_typed) # type: ignore[call-arg]
|
|
76
|
+
ctx_typed.retries[tool_typed.name] = 0
|
|
74
77
|
else:
|
|
75
78
|
result = f(**kwargs) # type: ignore[call-arg]
|
|
76
|
-
tool_typed.current_retry = 0
|
|
77
79
|
except Exception as e:
|
|
78
|
-
|
|
80
|
+
if ctx_typed is not None:
|
|
81
|
+
ctx_typed.retries[tool_typed.name] += 1
|
|
79
82
|
raise e
|
|
80
83
|
|
|
81
84
|
return result
|
|
@@ -151,7 +154,7 @@ class PydanticAIInteroperability:
|
|
|
151
154
|
name=pydantic_ai_tool.name,
|
|
152
155
|
description=pydantic_ai_tool.description,
|
|
153
156
|
func_or_tool=func,
|
|
154
|
-
parameters_json_schema=pydantic_ai_tool.
|
|
157
|
+
parameters_json_schema=pydantic_ai_tool.function_schema.json_schema,
|
|
155
158
|
)
|
|
156
159
|
|
|
157
160
|
@classmethod
|