diff --git a/dapr/aio/clients/grpc/interceptors.py b/dapr/aio/clients/grpc/interceptors.py index 0c1d3653..2802f634 100644 --- a/dapr/aio/clients/grpc/interceptors.py +++ b/dapr/aio/clients/grpc/interceptors.py @@ -41,11 +41,14 @@ class _ClientCallDetailsAsync( class DaprClientTimeoutInterceptorAsync(UnaryUnaryClientInterceptor): def intercept_unary_unary(self, continuation, client_call_details, request): - # If a specific timeout is not set, create a new ClientCallDetails with the default timeout - if client_call_details.timeout is None: + # Only apply a deadline when DAPR_API_TIMEOUT_SECONDS is explicitly configured. + # Without an explicit setting there is no SDK-level default deadline: Dapr's own + # resiliency policies and component timeouts act as the authoritative bounds for + # long-running operations such as LLM calls and workflow activities. + if settings.DAPR_API_TIMEOUT_SECONDS is not None and client_call_details.timeout is None: new_client_call_details = _ClientCallDetailsAsync( client_call_details.method, - settings.DAPR_API_TIMEOUT_SECONDS, + float(settings.DAPR_API_TIMEOUT_SECONDS), client_call_details.metadata, client_call_details.credentials, client_call_details.wait_for_ready, diff --git a/dapr/clients/grpc/interceptors.py b/dapr/clients/grpc/interceptors.py index a574fb8c..24df379a 100644 --- a/dapr/clients/grpc/interceptors.py +++ b/dapr/clients/grpc/interceptors.py @@ -27,11 +27,14 @@ class _ClientCallDetails( class DaprClientTimeoutInterceptor(UnaryUnaryClientInterceptor): def intercept_unary_unary(self, continuation, client_call_details, request): - # If a specific timeout is not set, create a new ClientCallDetails with the default timeout - if client_call_details.timeout is None: + # Only apply a deadline when DAPR_API_TIMEOUT_SECONDS is explicitly configured. + # Without an explicit setting there is no SDK-level default deadline: Dapr's own + # resiliency policies and component timeouts act as the authoritative bounds for + # long-running operations such as LLM calls and workflow activities. + if settings.DAPR_API_TIMEOUT_SECONDS is not None and client_call_details.timeout is None: new_client_call_details = _ClientCallDetails( client_call_details.method, - settings.DAPR_API_TIMEOUT_SECONDS, + float(settings.DAPR_API_TIMEOUT_SECONDS), client_call_details.metadata, client_call_details.credentials, client_call_details.wait_for_ready, diff --git a/dapr/conf/global_settings.py b/dapr/conf/global_settings.py index 5a64e5d4..5179f6a3 100644 --- a/dapr/conf/global_settings.py +++ b/dapr/conf/global_settings.py @@ -28,7 +28,9 @@ DAPR_HEALTH_TIMEOUT = 60 # seconds DAPR_API_MAX_RETRIES = 0 -DAPR_API_TIMEOUT_SECONDS = 60 +DAPR_API_TIMEOUT_SECONDS = ( + None # No default deadline — set DAPR_API_TIMEOUT_SECONDS env var to impose one +) DAPR_API_METHOD_INVOCATION_PROTOCOL = 'http' diff --git a/tests/clients/test_timeout_interceptor.py b/tests/clients/test_timeout_interceptor.py index c60331be..bddec8da 100644 --- a/tests/clients/test_timeout_interceptor.py +++ b/tests/clients/test_timeout_interceptor.py @@ -54,3 +54,20 @@ def test_intercept_unary_unary_without_timeout(self): ) called_client_call_details = continuation.call_args[0][0] self.assertEqual(7, called_client_call_details.timeout) + + @patch.object(settings, 'DAPR_API_TIMEOUT_SECONDS', None) + def test_intercept_unary_unary_no_global_timeout_no_per_call_timeout(self): + continuation = Mock() + request = Mock() + client_call_details = Mock() + client_call_details.method = 'method' + client_call_details.timeout = None + client_call_details.metadata = 'metadata' + client_call_details.credentials = 'credentials' + client_call_details.wait_for_ready = 'wait_for_ready' + client_call_details.compression = 'compression' + + DaprClientTimeoutInterceptor().intercept_unary_unary( + continuation, client_call_details, request + ) + continuation.assert_called_once_with(client_call_details, request) diff --git a/tests/clients/test_timeout_interceptor_async.py b/tests/clients/test_timeout_interceptor_async.py index 88b5831d..0e0651f1 100644 --- a/tests/clients/test_timeout_interceptor_async.py +++ b/tests/clients/test_timeout_interceptor_async.py @@ -52,3 +52,19 @@ def test_intercept_unary_unary_without_timeout(self): ) called_client_call_details = continuation.call_args[0][0] self.assertEqual(7, called_client_call_details.timeout) + + @patch.object(settings, 'DAPR_API_TIMEOUT_SECONDS', None) + def test_intercept_unary_unary_no_global_timeout_no_per_call_timeout(self): + continuation = Mock() + request = Mock() + client_call_details = Mock() + client_call_details.method = 'method' + client_call_details.timeout = None + client_call_details.metadata = 'metadata' + client_call_details.credentials = 'credentials' + client_call_details.wait_for_ready = 'wait_for_ready' + + DaprClientTimeoutInterceptorAsync().intercept_unary_unary( + continuation, client_call_details, request + ) + continuation.assert_called_once_with(client_call_details, request)