Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve types issues in coding #2055

Merged
merged 28 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d08ea65
Improve conversable agent interface, fix some typing
jackgerrits Mar 6, 2024
d3e2f58
formatting
jackgerrits Mar 6, 2024
f63f5b5
Merge branch 'main' into types_1
jackgerrits Mar 6, 2024
873f739
import
jackgerrits Mar 6, 2024
d4a7908
rearrange to fix circular import
jackgerrits Mar 6, 2024
32c4e50
Merge branch 'main' into types_1
jackgerrits Mar 10, 2024
0e995c5
type fixes
jackgerrits Mar 10, 2024
8076835
undo system message change
jackgerrits Mar 10, 2024
8914abf
add coding to check, lint
jackgerrits Mar 10, 2024
e679471
Update conversable_agent.py
jackgerrits Mar 10, 2024
ba3cc77
Apply suggestions from code review
jackgerrits Mar 11, 2024
1283dee
put back # type: ignore
jackgerrits Mar 11, 2024
82c9cc0
formatting
jackgerrits Mar 11, 2024
63eceb4
Merge branch 'main' into types_1
jackgerrits Mar 12, 2024
6ac3a6a
Merge branch 'main' into types_1
jackgerrits Mar 12, 2024
d7a1473
revert conversable agent
jackgerrits Mar 13, 2024
993595a
Merge branch 'main' into types_1
jackgerrits Mar 13, 2024
091c54b
ci fix
jackgerrits Mar 13, 2024
79f2c6d
Merge branch 'main' into types_1
jackgerrits Mar 13, 2024
fe7aef4
Update type-check.yml
jackgerrits Mar 15, 2024
89f6821
Merge branch 'main' into types_1
jackgerrits Mar 15, 2024
77b408f
Merge branch 'main' into types_1
sonichi Mar 15, 2024
8649ac4
Merge branch 'main' into types_1
jackgerrits Mar 18, 2024
665ee08
Merge branch 'main' into types_1
jackgerrits Mar 18, 2024
f2748fd
fix double install, and redudant args, address type ignores
jackgerrits Mar 18, 2024
c878fed
Merge branch 'main' into types_1
jackgerrits Mar 18, 2024
d4a5439
Merge branch 'main' into types_1
davorrunje Mar 19, 2024
c4dbde9
Merge branch 'main' into types_1
jackgerrits Mar 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/type-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
- run: pip install ".[jupyter-executor]" mypy
# As more modules are type check clean, add them here
- run: |
mypy --install-types --non-interactive \
mypy \
autogen/logger \
autogen/exception_utils.py
autogen/exception_utils.py \
autogen/coding
8 changes: 5 additions & 3 deletions autogen/code_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

import docker

from .types import UserMessageImageContentPart, UserMessageTextContentPart

SENTINEL = object()
DEFAULT_MODEL = "gpt-4"
FAST_MODEL = "gpt-3.5-turbo"
Expand All @@ -37,8 +39,8 @@
logger = logging.getLogger(__name__)


def content_str(content: Union[str, List[Dict[str, Any]], None]) -> str:
"""Converts `content` into a string format.
def content_str(content: Union[str, List[Union[UserMessageTextContentPart, UserMessageImageContentPart]], None]) -> str:
"""Converts the `content` field of an OpenAI merssage into a string format.

This function processes content that may be a string, a list of mixed text and image URLs, or None,
and converts it into a string. Text is directly appended to the result string, while image URLs are
Expand Down Expand Up @@ -241,7 +243,7 @@ def get_powershell_command():
raise PermissionError("No permission to run powershell.") from e


def _cmd(lang):
def _cmd(lang: str) -> str:
if lang.startswith("python") or lang in ["bash", "sh"]:
return lang
if lang in ["shell"]:
Expand Down
3 changes: 1 addition & 2 deletions autogen/coding/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .base import CodeBlock, CodeExecutor, CodeExtractor, CodeResult
from .factory import CodeExecutorFactory
from .markdown_code_extractor import MarkdownCodeExtractor
from .local_commandline_code_executor import LocalCommandLineCodeExecutor, CommandLineCodeResult
from .local_commandline_code_executor import LocalCommandLineCodeExecutor
from .docker_commandline_code_executor import DockerCommandLineCodeExecutor

__all__ = (
Expand All @@ -12,6 +12,5 @@
"CodeExecutorFactory",
"MarkdownCodeExtractor",
"LocalCommandLineCodeExecutor",
"CommandLineCodeResult",
"DockerCommandLineCodeExecutor",
)
25 changes: 22 additions & 3 deletions autogen/coding/base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from typing import Any, Dict, List, Optional, Protocol, Union, runtime_checkable
from __future__ import annotations
from typing import Any, List, Literal, Mapping, Optional, Protocol, TypedDict, Union, runtime_checkable

from pydantic import BaseModel, Field

from ..agentchat.agent import LLMAgent
from ..types import UserMessageImageContentPart, UserMessageTextContentPart

__all__ = ("CodeBlock", "CodeResult", "CodeExtractor", "CodeExecutor")
__all__ = ("CodeBlock", "CodeResult", "CodeExtractor", "CodeExecutor", "CodeExecutionConfig")


class CodeBlock(BaseModel):
Expand All @@ -26,7 +28,9 @@ class CodeResult(BaseModel):
class CodeExtractor(Protocol):
"""(Experimental) A code extractor class that extracts code blocks from a message."""

def extract_code_blocks(self, message: Union[str, List[Dict[str, Any]], None]) -> List[CodeBlock]:
def extract_code_blocks(
self, message: Union[str, List[Union[UserMessageTextContentPart, UserMessageImageContentPart]], None]
) -> List[CodeBlock]:
"""(Experimental) Extract code blocks from a message.

Args:
Expand Down Expand Up @@ -79,6 +83,21 @@ class IPythonCodeResult(CodeResult):
)


CodeExecutionConfig = TypedDict(
"CodeExecutionConfig",
{
"executor": Union[Literal["ipython-embedded", "commandline-local"], CodeExecutor],
"last_n_messages": Union[int, Literal["auto"]],
"timeout": int,
"use_docker": Union[bool, str, List[str]],
"work_dir": str,
"ipython-embedded": Mapping[str, Any],
"commandline-local": Mapping[str, Any],
},
total=False,
)


class CommandLineCodeResult(CodeResult):
"""(Experimental) A code result class for command line code executor."""

Expand Down
9 changes: 4 additions & 5 deletions autogen/coding/docker_commandline_code_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
from time import sleep
from types import TracebackType
import uuid
from typing import List, Optional, Type, Union
from typing import Any, List, Optional, Type, Union
import docker
from docker.models.containers import Container
from docker.errors import ImageNotFound

from .utils import _get_file_name_from_content
Expand All @@ -25,8 +24,8 @@
from typing_extensions import Self


def _wait_for_ready(container: Container, timeout: int = 60, stop_time: int = 0.1) -> None:
elapsed_time = 0
def _wait_for_ready(container: Any, timeout: int = 60, stop_time: float = 0.1) -> None:
elapsed_time = 0.0
while container.status != "running" and elapsed_time < timeout:
sleep(stop_time)
elapsed_time += stop_time
Expand Down Expand Up @@ -114,7 +113,7 @@ def __init__(

_wait_for_ready(self._container)

def cleanup():
def cleanup() -> None:
try:
container = client.containers.get(container_name)
container.stop()
Expand Down
6 changes: 2 additions & 4 deletions autogen/coding/factory.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from typing import Any, Dict

from .base import CodeExecutor
from .base import CodeExecutor, CodeExecutionConfig

__all__ = ("CodeExecutorFactory",)

Expand All @@ -9,7 +7,7 @@ class CodeExecutorFactory:
"""(Experimental) A factory class for creating code executors."""

@staticmethod
def create(code_execution_config: Dict[str, Any]) -> CodeExecutor:
def create(code_execution_config: CodeExecutionConfig) -> CodeExecutor:
"""(Experimental) Get a code executor based on the code execution config.

Args:
Expand Down
4 changes: 2 additions & 2 deletions autogen/coding/jupyter/docker_jupyter_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def __init__(
self._port = int(container_ports["8888/tcp"][0]["HostPort"])
self._container_id = container.id

def cleanup():
def cleanup() -> None:
try:
inner_container = client.containers.get(container.id)
inner_container.stop()
Expand All @@ -142,7 +142,7 @@ def cleanup():
def connection_info(self) -> JupyterConnectionInfo:
return JupyterConnectionInfo(host="127.0.0.1", use_https=False, port=self._port, token=self._token)

def stop(self):
def stop(self) -> None:
self._cleanup_func()

def get_client(self) -> JupyterClient:
Expand Down
14 changes: 7 additions & 7 deletions autogen/coding/local_commandline_code_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,12 @@ def restart(self) -> None:

# From stack overflow: https://stackoverflow.com/a/52087847/2214524
class _DeprecatedClassMeta(type):
def __new__(cls, name, bases, classdict, *args, **kwargs):
def __new__(cls, name, bases, classdict, *args, **kwargs): # type: ignore[no-untyped-def]
alias = classdict.get("_DeprecatedClassMeta__alias")

if alias is not None:

def new(cls, *args, **kwargs):
def new(cls, *args, **kwargs): # type: ignore[no-untyped-def]
alias = getattr(cls, "_DeprecatedClassMeta__alias")

if alias is not None:
Expand Down Expand Up @@ -209,14 +209,14 @@ def new(cls, *args, **kwargs):
if b not in fixed_bases:
fixed_bases.append(b)

fixed_bases = tuple(fixed_bases)
fixed_bases = tuple(fixed_bases) # type: ignore[assignment]

return super().__new__(cls, name, fixed_bases, classdict, *args, **kwargs)
return super().__new__(cls, name, fixed_bases, classdict, *args, **kwargs) # type: ignore[call-overload]

def __instancecheck__(cls, instance):
return any(cls.__subclasscheck__(c) for c in {type(instance), instance.__class__})
def __instancecheck__(cls, instance): # type: ignore[no-untyped-def]
return any(cls.__subclasscheck__(c) for c in {type(instance), instance.__class__}) # type: ignore[no-untyped-call]

def __subclasscheck__(cls, subclass):
def __subclasscheck__(cls, subclass): # type: ignore[no-untyped-def]
if subclass is cls:
return True
else:
Expand Down
9 changes: 6 additions & 3 deletions autogen/coding/markdown_code_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
from typing import Any, Dict, List, Optional, Union

from ..code_utils import CODE_BLOCK_PATTERN, UNKNOWN, content_str, infer_lang
from .base import CodeBlock
from .base import CodeBlock, CodeExtractor
from ..types import UserMessageImageContentPart, UserMessageTextContentPart

__all__ = ("MarkdownCodeExtractor",)


class MarkdownCodeExtractor:
class MarkdownCodeExtractor(CodeExtractor):
"""(Experimental) A class that extracts code blocks from a message using Markdown syntax."""

def extract_code_blocks(self, message: Union[str, List[Dict[str, Any]], None]) -> List[CodeBlock]:
def extract_code_blocks(
self, message: Union[str, List[Union[UserMessageTextContentPart, UserMessageImageContentPart]], None]
) -> List[CodeBlock]:
"""(Experimental) Extract code blocks from a message. If no code blocks are found,
return an empty list.

Expand Down
12 changes: 12 additions & 0 deletions autogen/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import Dict, List, Literal, TypedDict, Union


class UserMessageTextContentPart(TypedDict):
type: Literal["text"]
text: str


class UserMessageImageContentPart(TypedDict):
type: Literal["image_url"]
# Ignoring the other "detail param for now"
image_url: Dict[Literal["url"], str]
Loading