mirror of
https://github.com/microsoft/autogen.git
synced 2025-11-29 08:31:39 +00:00
Merge branch 'main' into copilot/fix-6542
This commit is contained in:
commit
b3e4b44a70
@ -34,7 +34,6 @@ html[data-theme="dark"] {
|
||||
}
|
||||
|
||||
/* Adding header icon hover and focus effects */
|
||||
.bd-header a:hover,
|
||||
.bd-header a:focus-visible {
|
||||
color: var(--pst-color-secondary) !important;
|
||||
text-decoration: underline !important;
|
||||
|
||||
@ -31,6 +31,7 @@ class ModelFamily:
|
||||
GEMINI_1_5_PRO = "gemini-1.5-pro"
|
||||
GEMINI_2_0_FLASH = "gemini-2.0-flash"
|
||||
GEMINI_2_5_PRO = "gemini-2.5-pro"
|
||||
GEMINI_2_5_FLASH = "gemini-2.5-flash"
|
||||
CLAUDE_3_HAIKU = "claude-3-haiku"
|
||||
CLAUDE_3_SONNET = "claude-3-sonnet"
|
||||
CLAUDE_3_OPUS = "claude-3-opus"
|
||||
@ -64,6 +65,7 @@ class ModelFamily:
|
||||
"gemini-1.5-pro",
|
||||
"gemini-2.0-flash",
|
||||
"gemini-2.5-pro",
|
||||
"gemini-2.5-flash"
|
||||
# anthropic_models
|
||||
"claude-3-haiku",
|
||||
"claude-3-sonnet",
|
||||
@ -107,6 +109,7 @@ class ModelFamily:
|
||||
ModelFamily.GEMINI_1_5_PRO,
|
||||
ModelFamily.GEMINI_2_0_FLASH,
|
||||
ModelFamily.GEMINI_2_5_PRO,
|
||||
ModelFamily.GEMINI_2_5_FLASH,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -236,7 +236,10 @@ class DockerJupyterCodeExecutor(CodeExecutor, Component[DockerJupyterCodeExecuto
|
||||
else:
|
||||
outputs.append(json.dumps(data.data))
|
||||
else:
|
||||
return DockerJupyterCodeResult(exit_code=1, output=f"ERROR: {result.output}", output_files=output_files)
|
||||
existing_output = "\n".join([str(output) for output in outputs])
|
||||
return DockerJupyterCodeResult(
|
||||
exit_code=1, output=existing_output + "\nERROR: " + result.output, output_files=output_files
|
||||
)
|
||||
return DockerJupyterCodeResult(
|
||||
exit_code=0, output="\n".join([str(output) for output in outputs]), output_files=output_files
|
||||
)
|
||||
|
||||
@ -39,6 +39,7 @@ class LocalCommandLineCodeExecutorConfig(BaseModel):
|
||||
timeout: int = 60
|
||||
work_dir: Optional[str] = None
|
||||
functions_module: str = "functions"
|
||||
cleanup_temp_files: bool = True
|
||||
|
||||
|
||||
class LocalCommandLineCodeExecutor(CodeExecutor, Component[LocalCommandLineCodeExecutorConfig]):
|
||||
@ -78,6 +79,7 @@ class LocalCommandLineCodeExecutor(CodeExecutor, Component[LocalCommandLineCodeE
|
||||
a default working directory will be used. The default working directory is a temporary directory.
|
||||
functions (List[Union[FunctionWithRequirements[Any, A], Callable[..., Any]]]): A list of functions that are available to the code executor. Default is an empty list.
|
||||
functions_module (str, optional): The name of the module that will be created to store the functions. Defaults to "functions".
|
||||
cleanup_temp_files (bool, optional): Whether to automatically clean up temporary files after execution. Defaults to True.
|
||||
virtual_env_context (Optional[SimpleNamespace], optional): The virtual environment context. Defaults to None.
|
||||
|
||||
.. note::
|
||||
@ -154,10 +156,12 @@ $functions"""
|
||||
]
|
||||
] = [],
|
||||
functions_module: str = "functions",
|
||||
cleanup_temp_files: bool = True,
|
||||
virtual_env_context: Optional[SimpleNamespace] = None,
|
||||
):
|
||||
if timeout < 1:
|
||||
raise ValueError("Timeout must be greater than or equal to 1.")
|
||||
self._timeout = timeout
|
||||
|
||||
self._work_dir: Optional[Path] = None
|
||||
if work_dir is not None:
|
||||
@ -174,13 +178,6 @@ $functions"""
|
||||
self._work_dir = work_dir
|
||||
self._work_dir.mkdir(exist_ok=True)
|
||||
|
||||
if not functions_module.isidentifier():
|
||||
raise ValueError("Module name must be a valid Python identifier")
|
||||
|
||||
self._functions_module = functions_module
|
||||
|
||||
self._timeout = timeout
|
||||
|
||||
self._functions = functions
|
||||
# Setup could take some time so we intentionally wait for the first code block to do it.
|
||||
if len(functions) > 0:
|
||||
@ -188,6 +185,11 @@ $functions"""
|
||||
else:
|
||||
self._setup_functions_complete = True
|
||||
|
||||
if not functions_module.isidentifier():
|
||||
raise ValueError("Module name must be a valid Python identifier")
|
||||
self._functions_module = functions_module
|
||||
|
||||
self._cleanup_temp_files = cleanup_temp_files
|
||||
self._virtual_env_context: Optional[SimpleNamespace] = virtual_env_context
|
||||
|
||||
self._temp_dir: Optional[tempfile.TemporaryDirectory[str]] = None
|
||||
@ -228,15 +230,6 @@ $functions"""
|
||||
functions="\n\n".join([to_stub(func) for func in self._functions]),
|
||||
)
|
||||
|
||||
@property
|
||||
def functions_module(self) -> str:
|
||||
"""(Experimental) The module name for the functions."""
|
||||
return self._functions_module
|
||||
|
||||
@property
|
||||
def functions(self) -> List[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def timeout(self) -> int:
|
||||
"""(Experimental) The timeout for code execution."""
|
||||
@ -254,6 +247,20 @@ $functions"""
|
||||
self._started = True
|
||||
return Path(self._temp_dir.name)
|
||||
|
||||
@property
|
||||
def functions(self) -> List[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def functions_module(self) -> str:
|
||||
"""(Experimental) The module name for the functions."""
|
||||
return self._functions_module
|
||||
|
||||
@property
|
||||
def cleanup_temp_files(self) -> bool:
|
||||
"""(Experimental) Whether to automatically clean up temporary files after execution."""
|
||||
return self._cleanup_temp_files
|
||||
|
||||
async def _setup_functions(self, cancellation_token: CancellationToken) -> None:
|
||||
func_file_content = build_python_functions_file(self._functions)
|
||||
func_file = self.work_dir / f"{self._functions_module}.py"
|
||||
@ -446,7 +453,16 @@ $functions"""
|
||||
break
|
||||
|
||||
code_file = str(file_names[0]) if file_names else None
|
||||
return CommandLineCodeResult(exit_code=exitcode, output=logs_all, code_file=code_file)
|
||||
code_result = CommandLineCodeResult(exit_code=exitcode, output=logs_all, code_file=code_file)
|
||||
|
||||
if self._cleanup_temp_files:
|
||||
for file in file_names:
|
||||
try:
|
||||
file.unlink(missing_ok=True)
|
||||
except OSError as error:
|
||||
logging.error(f"Failed to delete temporary file {file}: {error}")
|
||||
|
||||
return code_result
|
||||
|
||||
async def restart(self) -> None:
|
||||
"""(Experimental) Restart the code executor."""
|
||||
@ -488,6 +504,7 @@ $functions"""
|
||||
timeout=self._timeout,
|
||||
work_dir=str(self.work_dir),
|
||||
functions_module=self._functions_module,
|
||||
cleanup_temp_files=self._cleanup_temp_files,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@ -496,4 +513,5 @@ $functions"""
|
||||
timeout=config.timeout,
|
||||
work_dir=Path(config.work_dir) if config.work_dir is not None else None,
|
||||
functions_module=config.functions_module,
|
||||
cleanup_temp_files=config.cleanup_temp_files,
|
||||
)
|
||||
|
||||
@ -308,6 +308,14 @@ _MODEL_INFO: Dict[str, ModelInfo] = {
|
||||
"structured_output": True,
|
||||
"multiple_system_messages": False,
|
||||
},
|
||||
"gemini-2.5-flash-preview-05-20": {
|
||||
"vision": True,
|
||||
"function_calling": True,
|
||||
"json_output": True,
|
||||
"family": ModelFamily.GEMINI_2_5_FLASH,
|
||||
"structured_output": True,
|
||||
"multiple_system_messages": False,
|
||||
},
|
||||
"claude-3-haiku-20240307": {
|
||||
"vision": True,
|
||||
"function_calling": True,
|
||||
@ -422,6 +430,7 @@ _MODEL_TOKEN_LIMITS: Dict[str, int] = {
|
||||
"gemini-2.0-flash": 1048576,
|
||||
"gemini-2.0-flash-lite-preview-02-05": 1048576,
|
||||
"gemini-2.5-pro-preview-03-25": 2097152,
|
||||
"gemini-2.5-flash-preview-05-20": 1048576,
|
||||
"claude-3-haiku-20240307": 50000,
|
||||
"claude-3-sonnet-20240229": 40000,
|
||||
"claude-3-opus-20240229": 20000,
|
||||
|
||||
@ -12,6 +12,7 @@ import types
|
||||
import venv
|
||||
from pathlib import Path
|
||||
from typing import AsyncGenerator, TypeAlias
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
@ -109,7 +110,7 @@ async def executor_and_temp_dir(
|
||||
request: pytest.FixtureRequest,
|
||||
) -> AsyncGenerator[tuple[LocalCommandLineCodeExecutor, str], None]:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir)
|
||||
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir, cleanup_temp_files=False)
|
||||
await executor.start()
|
||||
yield executor, temp_dir
|
||||
|
||||
@ -399,3 +400,48 @@ async def test_ps1_script(executor_and_temp_dir: ExecutorFixture) -> None:
|
||||
assert result.exit_code == 0
|
||||
assert "hello from powershell!" in result.output
|
||||
assert result.code_file is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cleanup_temp_files_behavior() -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
# Test with cleanup_temp_files=True (default)
|
||||
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir, cleanup_temp_files=True)
|
||||
await executor.start()
|
||||
cancellation_token = CancellationToken()
|
||||
code_blocks = [CodeBlock(code="print('cleanup test')", language="python")]
|
||||
result = await executor.execute_code_blocks(code_blocks, cancellation_token)
|
||||
assert result.exit_code == 0
|
||||
assert "cleanup test" in result.output
|
||||
# The code file should have been deleted
|
||||
assert result.code_file is not None
|
||||
assert not Path(result.code_file).exists()
|
||||
|
||||
# Test with cleanup_temp_files=False
|
||||
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir, cleanup_temp_files=False)
|
||||
await executor.start()
|
||||
cancellation_token = CancellationToken()
|
||||
code_blocks = [CodeBlock(code="print('no cleanup')", language="python")]
|
||||
result = await executor.execute_code_blocks(code_blocks, cancellation_token)
|
||||
assert result.exit_code == 0
|
||||
assert "no cleanup" in result.output
|
||||
# The code file should still exist
|
||||
assert result.code_file is not None
|
||||
assert Path(result.code_file).exists()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cleanup_temp_files_oserror(caplog: pytest.LogCaptureFixture) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir, cleanup_temp_files=True)
|
||||
await executor.start()
|
||||
cancellation_token = CancellationToken()
|
||||
code_blocks = [CodeBlock(code="print('cleanup test')", language="python")]
|
||||
|
||||
# Patch Path.unlink to raise OSError for this test
|
||||
with patch("pathlib.Path.unlink", side_effect=OSError("Mocked OSError")):
|
||||
with caplog.at_level("ERROR"):
|
||||
await executor.execute_code_blocks(code_blocks, cancellation_token)
|
||||
# The code file should have been attempted to be deleted and failed
|
||||
assert any("Failed to delete temporary file" in record.message for record in caplog.records)
|
||||
assert any("Mocked OSError" in record.message for record in caplog.records)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user