Skip to content

Commit e5aa310

Browse files
fix: functiontool supports asynchronous functions (#2078)
Co-authored-by: Wendong-Fan <[email protected]>
1 parent f57e48d commit e5aa310

File tree

1 file changed

+49
-29
lines changed

1 file changed

+49
-29
lines changed

camel/utils/commons.py

+49-29
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
# See the License for the specific language governing permissions and
1212
# limitations under the License.
1313
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14+
import asyncio
1415
import functools
1516
import importlib
17+
import inspect
1618
import logging
1719
import os
1820
import platform
@@ -978,36 +980,54 @@ def with_timeout(timeout=None):
978980
"""
979981

980982
def decorator(func):
981-
@functools.wraps(func)
982-
def wrapper(*args, **kwargs):
983-
# Determine the effective timeout value
984-
effective_timeout = timeout
985-
if effective_timeout is None and args:
986-
effective_timeout = getattr(args[0], 'timeout', None)
987-
988-
# If no timeout value is provided, execute function normally
989-
if effective_timeout is None:
990-
return func(*args, **kwargs)
991-
992-
# Container to hold the result of the function call
993-
result_container = []
994-
995-
def target():
996-
result_container.append(func(*args, **kwargs))
997-
998-
# Start the function in a new thread
999-
thread = threading.Thread(target=target)
1000-
thread.start()
1001-
thread.join(effective_timeout)
1002-
1003-
# Check if the thread is still alive after the timeout
1004-
if thread.is_alive():
1005-
return (
1006-
f"Function `{func.__name__}` execution timed out, "
1007-
f"exceeded {effective_timeout} seconds."
983+
if inspect.iscoroutinefunction(func):
984+
985+
@functools.wraps(func)
986+
async def async_wrapper(*args, **kwargs):
987+
eff_timeout = timeout
988+
if eff_timeout is None and args:
989+
eff_timeout = getattr(args[0], 'timeout', None)
990+
991+
if eff_timeout is None:
992+
return await func(*args, **kwargs)
993+
994+
return await asyncio.wait_for(
995+
func(*args, **kwargs), timeout=eff_timeout
1008996
)
1009-
else:
1010-
return result_container[0]
997+
998+
return async_wrapper
999+
else:
1000+
1001+
@functools.wraps(func)
1002+
def wrapper(*args, **kwargs):
1003+
# Determine the effective timeout value
1004+
effective_timeout = timeout
1005+
if effective_timeout is None and args:
1006+
effective_timeout = getattr(args[0], 'timeout', None)
1007+
1008+
# If no timeout value is provided, execute function normally
1009+
if effective_timeout is None:
1010+
return func(*args, **kwargs)
1011+
1012+
# Container to hold the result of the function call
1013+
result_container = []
1014+
1015+
def target():
1016+
result_container.append(func(*args, **kwargs))
1017+
1018+
# Start the function in a new thread
1019+
thread = threading.Thread(target=target)
1020+
thread.start()
1021+
thread.join(effective_timeout)
1022+
1023+
# Check if the thread is still alive after the timeout
1024+
if thread.is_alive():
1025+
return (
1026+
f"Function `{func.__name__}` execution timed out, "
1027+
f"exceeded {effective_timeout} seconds."
1028+
)
1029+
else:
1030+
return result_container[0]
10111031

10121032
return wrapper
10131033

0 commit comments

Comments
 (0)