Skip to content

Commit d602400

Browse files
authored
Merge branch 'main' into rajan/reduce-sleeps
2 parents 46e73a3 + 989c182 commit d602400

18 files changed

+463
-58
lines changed

autogen/agentchat/conversable_agent.py

+44-24
Original file line numberDiff line numberDiff line change
@@ -2259,30 +2259,54 @@ def generate_init_message(self, message: Union[Dict, str, None], **kwargs) -> Un
22592259
"""
22602260
if message is None:
22612261
message = self.get_human_input(">")
2262+
2263+
return self._handle_carryover(message, kwargs)
2264+
2265+
def _handle_carryover(self, message: Union[str, Dict], kwargs: dict) -> Union[str, Dict]:
2266+
if not kwargs.get("carryover"):
2267+
return message
2268+
22622269
if isinstance(message, str):
22632270
return self._process_carryover(message, kwargs)
2271+
22642272
elif isinstance(message, dict):
2265-
message = message.copy()
2266-
# TODO: Do we need to do the following?
2267-
# if message.get("content") is None:
2268-
# message["content"] = self.get_human_input(">")
2269-
message["content"] = self._process_carryover(message.get("content", ""), kwargs)
2270-
return message
2273+
if isinstance(message.get("content"), str):
2274+
# Makes sure the original message is not mutated
2275+
message = message.copy()
2276+
message["content"] = self._process_carryover(message["content"], kwargs)
2277+
elif isinstance(message.get("content"), list):
2278+
# Makes sure the original message is not mutated
2279+
message = message.copy()
2280+
message["content"] = self._process_multimodal_carryover(message["content"], kwargs)
2281+
else:
2282+
raise InvalidCarryOverType("Carryover should be a string or a list of strings.")
22712283

2272-
def _process_carryover(self, message: str, kwargs: dict) -> str:
2273-
carryover = kwargs.get("carryover")
2274-
if carryover:
2275-
# if carryover is string
2276-
if isinstance(carryover, str):
2277-
message += "\nContext: \n" + carryover
2278-
elif isinstance(carryover, list):
2279-
message += "\nContext: \n" + ("\n").join([t for t in carryover])
2280-
else:
2281-
raise InvalidCarryOverType(
2282-
"Carryover should be a string or a list of strings. Not adding carryover to the message."
2283-
)
22842284
return message
22852285

2286+
def _process_carryover(self, content: str, kwargs: dict) -> str:
2287+
# Makes sure there's a carryover
2288+
if not kwargs.get("carryover"):
2289+
return content
2290+
2291+
# if carryover is string
2292+
if isinstance(kwargs["carryover"], str):
2293+
content += "\nContext: \n" + kwargs["carryover"]
2294+
elif isinstance(kwargs["carryover"], list):
2295+
content += "\nContext: \n" + ("\n").join([t for t in kwargs["carryover"]])
2296+
else:
2297+
raise InvalidCarryOverType(
2298+
"Carryover should be a string or a list of strings. Not adding carryover to the message."
2299+
)
2300+
return content
2301+
2302+
def _process_multimodal_carryover(self, content: List[Dict], kwargs: dict) -> List[Dict]:
2303+
"""Prepends the context to a multimodal message."""
2304+
# Makes sure there's a carryover
2305+
if not kwargs.get("carryover"):
2306+
return content
2307+
2308+
return [{"type": "text", "text": self._process_carryover("", kwargs)}] + content
2309+
22862310
async def a_generate_init_message(self, message: Union[Dict, str, None], **kwargs) -> Union[str, Dict]:
22872311
"""Generate the initial message for the agent.
22882312
If message is None, input() will be called to get the initial message.
@@ -2295,12 +2319,8 @@ async def a_generate_init_message(self, message: Union[Dict, str, None], **kwarg
22952319
"""
22962320
if message is None:
22972321
message = await self.a_get_human_input(">")
2298-
if isinstance(message, str):
2299-
return self._process_carryover(message, kwargs)
2300-
elif isinstance(message, dict):
2301-
message = message.copy()
2302-
message["content"] = self._process_carryover(message["content"], kwargs)
2303-
return message
2322+
2323+
return self._handle_carryover(message, kwargs)
23042324

23052325
def register_function(self, function_map: Dict[str, Union[Callable, None]]):
23062326
"""Register functions to the agent.

autogen/coding/func_with_reqs.py

+71-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
from typing_extensions import ParamSpec
66
from textwrap import indent, dedent
77
from dataclasses import dataclass, field
8+
from importlib.abc import SourceLoader
9+
import importlib
810

911
T = TypeVar("T")
1012
P = ParamSpec("P")
1113

1214

13-
def _to_code(func: Union[FunctionWithRequirements[T, P], Callable[P, T]]) -> str:
15+
def _to_code(func: Union[FunctionWithRequirements[T, P], Callable[P, T], FunctionWithRequirementsStr]) -> str:
16+
if isinstance(func, FunctionWithRequirementsStr):
17+
return func.func
18+
1419
code = inspect.getsource(func)
1520
# Strip the decorator
1621
if code.startswith("@"):
@@ -50,6 +55,57 @@ def to_str(i: Union[str, Alias]) -> str:
5055
return f"from {im.module} import {imports}"
5156

5257

58+
class _StringLoader(SourceLoader):
59+
def __init__(self, data: str):
60+
self.data = data
61+
62+
def get_source(self, fullname: str) -> str:
63+
return self.data
64+
65+
def get_data(self, path: str) -> bytes:
66+
return self.data.encode("utf-8")
67+
68+
def get_filename(self, fullname: str) -> str:
69+
return "<not a real path>/" + fullname + ".py"
70+
71+
72+
@dataclass
73+
class FunctionWithRequirementsStr:
74+
func: str
75+
_compiled_func: Callable[..., Any]
76+
_func_name: str
77+
python_packages: List[str] = field(default_factory=list)
78+
global_imports: List[Import] = field(default_factory=list)
79+
80+
def __init__(self, func: str, python_packages: List[str] = [], global_imports: List[Import] = []):
81+
self.func = func
82+
self.python_packages = python_packages
83+
self.global_imports = global_imports
84+
85+
module_name = "func_module"
86+
loader = _StringLoader(func)
87+
spec = importlib.util.spec_from_loader(module_name, loader)
88+
if spec is None:
89+
raise ValueError("Could not create spec")
90+
module = importlib.util.module_from_spec(spec)
91+
if spec.loader is None:
92+
raise ValueError("Could not create loader")
93+
94+
try:
95+
spec.loader.exec_module(module)
96+
except Exception as e:
97+
raise ValueError(f"Could not compile function: {e}") from e
98+
99+
functions = inspect.getmembers(module, inspect.isfunction)
100+
if len(functions) != 1:
101+
raise ValueError("The string must contain exactly one function")
102+
103+
self._func_name, self._compiled_func = functions[0]
104+
105+
def __call__(self, *args: Any, **kwargs: Any) -> None:
106+
raise NotImplementedError("String based function with requirement objects are not directly callable")
107+
108+
53109
@dataclass
54110
class FunctionWithRequirements(Generic[T, P]):
55111
func: Callable[P, T]
@@ -62,6 +118,12 @@ def from_callable(
62118
) -> FunctionWithRequirements[T, P]:
63119
return cls(python_packages=python_packages, global_imports=global_imports, func=func)
64120

121+
@staticmethod
122+
def from_str(
123+
func: str, python_packages: List[str] = [], global_imports: List[Import] = []
124+
) -> FunctionWithRequirementsStr:
125+
return FunctionWithRequirementsStr(func=func, python_packages=python_packages, global_imports=global_imports)
126+
65127
# Type this based on F
66128
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
67129
return self.func(*args, **kwargs)
@@ -91,11 +153,13 @@ def wrapper(func: Callable[P, T]) -> FunctionWithRequirements[T, P]:
91153
return wrapper
92154

93155

94-
def _build_python_functions_file(funcs: List[Union[FunctionWithRequirements[Any, P], Callable[..., Any]]]) -> str:
156+
def _build_python_functions_file(
157+
funcs: List[Union[FunctionWithRequirements[Any, P], Callable[..., Any], FunctionWithRequirementsStr]]
158+
) -> str:
95159
# First collect all global imports
96160
global_imports = set()
97161
for func in funcs:
98-
if isinstance(func, FunctionWithRequirements):
162+
if isinstance(func, (FunctionWithRequirements, FunctionWithRequirementsStr)):
99163
global_imports.update(func.global_imports)
100164

101165
content = "\n".join(map(_import_to_str, global_imports)) + "\n\n"
@@ -106,7 +170,7 @@ def _build_python_functions_file(funcs: List[Union[FunctionWithRequirements[Any,
106170
return content
107171

108172

109-
def to_stub(func: Callable[..., Any]) -> str:
173+
def to_stub(func: Union[Callable[..., Any], FunctionWithRequirementsStr]) -> str:
110174
"""Generate a stub for a function as a string
111175
112176
Args:
@@ -115,6 +179,9 @@ def to_stub(func: Callable[..., Any]) -> str:
115179
Returns:
116180
str: The stub for the function
117181
"""
182+
if isinstance(func, FunctionWithRequirementsStr):
183+
return to_stub(func._compiled_func)
184+
118185
content = f"def {func.__name__}{inspect.signature(func)}:\n"
119186
docstring = func.__doc__
120187

autogen/coding/local_commandline_code_executor.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
import warnings
77
from typing import Any, Callable, ClassVar, List, TypeVar, Union, cast
88
from typing_extensions import ParamSpec
9-
from autogen.coding.func_with_reqs import FunctionWithRequirements, _build_python_functions_file, to_stub
9+
from autogen.coding.func_with_reqs import (
10+
FunctionWithRequirements,
11+
FunctionWithRequirementsStr,
12+
_build_python_functions_file,
13+
to_stub,
14+
)
1015

1116
from ..code_utils import TIMEOUT_MSG, WIN32, _cmd
1217
from .base import CodeBlock, CodeExecutor, CodeExtractor, CommandLineCodeResult
@@ -39,7 +44,7 @@ def __init__(
3944
self,
4045
timeout: int = 60,
4146
work_dir: Union[Path, str] = Path("."),
42-
functions: List[Union[FunctionWithRequirements[Any, A], Callable[..., Any]]] = [],
47+
functions: List[Union[FunctionWithRequirements[Any, A], Callable[..., Any], FunctionWithRequirementsStr]] = [],
4348
):
4449
"""(Experimental) A code executor class that executes code through a local command line
4550
environment.
@@ -104,7 +109,9 @@ def format_functions_for_prompt(self, prompt_template: str = FUNCTION_PROMPT_TEM
104109
)
105110

106111
@property
107-
def functions(self) -> List[Union[FunctionWithRequirements[Any, A], Callable[..., Any]]]:
112+
def functions(
113+
self,
114+
) -> List[Union[FunctionWithRequirements[Any, A], Callable[..., Any], FunctionWithRequirementsStr]]:
108115
"""(Experimental) The functions that are available to the code executor."""
109116
return self._functions
110117

autogen/io/base.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ def get_default() -> "IOStream":
7676
"""
7777
iostream = IOStream._default_io_stream.get()
7878
if iostream is None:
79-
logger.warning("No default IOStream has been set, defaulting to IOConsole.")
80-
return IOStream.get_global_default()
79+
logger.info("No default IOStream has been set, defaulting to IOConsole.")
80+
iostream = IOStream.get_global_default()
81+
# Set the default IOStream of the current context (thread/cooroutine)
82+
IOStream.set_default(iostream)
8183
return iostream
8284

8385
@staticmethod

autogen/oai/openai_utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,11 @@ def get_config(
541541
"""
542542
config = {"api_key": api_key}
543543
if base_url:
544-
config["base_url"] = base_url
544+
config["base_url"] = os.getenv(base_url, default=base_url)
545545
if api_type:
546-
config["api_type"] = api_type
546+
config["api_type"] = os.getenv(api_type, default=api_type)
547547
if api_version:
548-
config["api_version"] = api_version
548+
config["api_version"] = os.getenv(api_version, default=api_version)
549549
return config
550550

551551

notebook/agentchat_groupchat_RAG.ipynb

+7-3
Original file line numberDiff line numberDiff line change
@@ -117,27 +117,31 @@
117117
" description=\"Assistant who has extra content retrieval power for solving difficult problems.\",\n",
118118
")\n",
119119
"\n",
120+
"\n",
121+
"coder_llm_config = llm_config.copy()\n",
120122
"coder = AssistantAgent(\n",
121123
" name=\"Senior_Python_Engineer\",\n",
122124
" is_termination_msg=termination_msg,\n",
123125
" system_message=\"You are a senior python engineer, you provide python code to answer questions. Reply `TERMINATE` in the end when everything is done.\",\n",
124-
" llm_config=llm_config,\n",
126+
" llm_config={\"config_list\": config_list},\n",
125127
" description=\"Senior Python Engineer who can write code to solve problems and answer questions.\",\n",
126128
")\n",
127129
"\n",
130+
"pm_llm_config = llm_config.copy()\n",
128131
"pm = autogen.AssistantAgent(\n",
129132
" name=\"Product_Manager\",\n",
130133
" is_termination_msg=termination_msg,\n",
131134
" system_message=\"You are a product manager. Reply `TERMINATE` in the end when everything is done.\",\n",
132-
" llm_config=llm_config,\n",
135+
" llm_config={\"config_list\": config_list},\n",
133136
" description=\"Product Manager who can design and plan the project.\",\n",
134137
")\n",
135138
"\n",
139+
"reviewer_llm_config = llm_config.copy()\n",
136140
"reviewer = autogen.AssistantAgent(\n",
137141
" name=\"Code_Reviewer\",\n",
138142
" is_termination_msg=termination_msg,\n",
139143
" system_message=\"You are a code reviewer. Reply `TERMINATE` in the end when everything is done.\",\n",
140-
" llm_config=llm_config,\n",
144+
" llm_config={\"config_list\": config_list},\n",
141145
" description=\"Code Reviewer who can review the code.\",\n",
142146
")\n",
143147
"\n",

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ exclude = [
5252
# This file needs to be either upgraded or removed and therefore should be
5353
# ignore from type checking for now
5454
"math_utils\\.py$",
55-
"samples\\apps\\cap\\py\\autogencap\\proto\\*.py",
55+
"samples\\apps\\cap\\py\\autogencap\\proto\\.*\\.py",
5656
]
5757
ignore-init-module-imports = true
5858
unfixable = ["F401"]

0 commit comments

Comments
 (0)