Skip to content

Commit 5f188c1

Browse files
fix within_timeout error catching bug (#3365)
Co-authored-by: Wendong-Fan <[email protected]>
1 parent a9c8349 commit 5f188c1

File tree

3 files changed

+88
-5
lines changed

3 files changed

+88
-5
lines changed

camel/utils/commons.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,14 +1028,19 @@ def wrapper(*args, **kwargs):
10281028
if loop_running:
10291029
return func(*args, **kwargs)
10301030

1031-
# Container to hold the result of the function call
1031+
# Container to hold the result or exception from the function
1032+
# call
10321033
result_container = []
1034+
exception_container = []
10331035

10341036
def target():
1035-
result_container.append(func(*args, **kwargs))
1037+
try:
1038+
result_container.append(func(*args, **kwargs))
1039+
except Exception as e:
1040+
exception_container.append(e)
10361041

1037-
# Start the function in a new thread
1038-
thread = threading.Thread(target=target)
1042+
# Start the function in a new daemon thread
1043+
thread = threading.Thread(target=target, daemon=True)
10391044
thread.start()
10401045
thread.join(effective_timeout)
10411046

@@ -1046,7 +1051,16 @@ def target():
10461051
f"exceeded {effective_timeout} seconds."
10471052
)
10481053
else:
1049-
return result_container[0]
1054+
# If an exception occurred, re-raise it
1055+
if exception_container:
1056+
raise exception_container[0]
1057+
# Return result if available
1058+
if result_container:
1059+
return result_container[0]
1060+
raise RuntimeError(
1061+
f"Function `{func.__name__}` completed but produced "
1062+
"no result or exception."
1063+
)
10501064

10511065
return wrapper
10521066

test/utils/test_commons.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,59 @@ def fast_function():
383383

384384
result = fast_function()
385385
assert result == "success"
386+
387+
388+
def test_timeout_with_exception():
389+
@with_timeout(2)
390+
def function_that_raises():
391+
raise ValueError("Test exception")
392+
393+
with pytest.raises(ValueError) as exc_info:
394+
function_that_raises()
395+
396+
assert "Test exception" in str(exc_info.value)
397+
398+
399+
def test_timeout_with_exception_type_preserved():
400+
r"""Test that exception type is preserved when re-raised."""
401+
402+
class CustomException(Exception):
403+
pass
404+
405+
@with_timeout(2)
406+
def function_with_custom_exception():
407+
raise CustomException("Custom error message")
408+
409+
with pytest.raises(CustomException) as exc_info:
410+
function_with_custom_exception()
411+
412+
assert "Custom error message" in str(exc_info.value)
413+
414+
415+
def test_timeout_preserves_traceback():
416+
r"""Test that the original traceback is preserved when exception is
417+
re-raised.
418+
"""
419+
420+
@with_timeout(2)
421+
def function_with_nested_calls():
422+
def inner_function():
423+
def deepest_function():
424+
raise ValueError("Error from deepest function")
425+
426+
return deepest_function()
427+
428+
return inner_function()
429+
430+
with pytest.raises(ValueError) as exc_info:
431+
function_with_nested_calls()
432+
433+
# Check that the traceback contains the function names from the original
434+
# call stack
435+
import traceback
436+
437+
tb_str = ''.join(traceback.format_tb(exc_info.tb))
438+
# The traceback should contain references to our nested functions
439+
assert "deepest_function" in tb_str
440+
assert "inner_function" in tb_str
441+
assert "Error from deepest function" in str(exc_info.value)

test/workforce/test_workflow_memory.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@
2626
from camel.tasks.task import Task, TaskState
2727

2828

29+
@pytest.fixture(autouse=True)
30+
def mock_model_backend():
31+
"""Use StubModel to prevent real LLM API calls in tests."""
32+
from camel.models.stub_model import StubModel
33+
from camel.types import ModelType
34+
35+
# Use the existing StubModel designed for unit tests
36+
stub_model = StubModel(model_type=ModelType.STUB)
37+
38+
with patch('camel.models.ModelFactory.create', return_value=stub_model):
39+
yield stub_model
40+
41+
2942
class MockSingleAgentWorker(SingleAgentWorker):
3043
"""A mock worker for testing workflow functionality."""
3144

0 commit comments

Comments
 (0)