From 549d66368d7a237ce6071983e4e2110b2a8e79c7 Mon Sep 17 00:00:00 2001 From: Vinicius Mello Date: Wed, 24 Sep 2025 12:27:16 -0300 Subject: [PATCH 1/7] feat: add timeout configuration for Openlayer tracer --- src/openlayer/lib/tracing/tracer.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/openlayer/lib/tracing/tracer.py b/src/openlayer/lib/tracing/tracer.py index 1471c758..d4ce31ab 100644 --- a/src/openlayer/lib/tracing/tracer.py +++ b/src/openlayer/lib/tracing/tracer.py @@ -40,6 +40,7 @@ def configure( api_key: Optional[str] = None, inference_pipeline_id: Optional[str] = None, base_url: Optional[str] = None, + timeout: Optional[int] = None, ) -> None: """Configure the Openlayer tracer with custom settings. @@ -52,6 +53,7 @@ def configure( If not provided, falls back to OPENLAYER_INFERENCE_PIPELINE_ID environment variable. base_url: The base URL for the Openlayer API. If not provided, falls back to OPENLAYER_BASE_URL environment variable or the default. + timeout: The timeout for the Openlayer API. Defaults to 1 minute. Examples: >>> import openlayer.lib.tracing.tracer as tracer @@ -62,11 +64,12 @@ def configure( >>> def my_function(): ... return "result" """ - global _configured_api_key, _configured_pipeline_id, _configured_base_url, _client + global _configured_api_key, _configured_pipeline_id, _configured_base_url, _configured_timeout, _client _configured_api_key = api_key _configured_pipeline_id = inference_pipeline_id _configured_base_url = base_url + _configured_timeout = timeout # Reset the client so it gets recreated with new configuration _client = None @@ -90,6 +93,9 @@ def _get_client() -> Optional[Openlayer]: if _configured_base_url is not None: client_kwargs["base_url"] = _configured_base_url + if _configured_timeout is not None: + client_kwargs["timeout"] = _configured_timeout + if _verify_ssl: _client = Openlayer(**client_kwargs) else: From e22f017eb3073cc3f9dd68058d552a28ac4b048b Mon Sep 17 00:00:00 2001 From: Vinicius Mello Date: Wed, 24 Sep 2025 12:34:03 -0300 Subject: [PATCH 2/7] feat: add optional timeout configuration to tracer --- src/openlayer/lib/tracing/tracer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/openlayer/lib/tracing/tracer.py b/src/openlayer/lib/tracing/tracer.py index d4ce31ab..bf576743 100644 --- a/src/openlayer/lib/tracing/tracer.py +++ b/src/openlayer/lib/tracing/tracer.py @@ -8,7 +8,7 @@ import traceback from contextlib import contextmanager from functools import wraps -from typing import Any, Awaitable, Dict, Generator, List, Optional, Tuple +from typing import Any, Awaitable, Dict, Generator, List, Optional, Tuple, Union from ..._base_client import DefaultHttpxClient from ..._client import Openlayer @@ -34,13 +34,14 @@ _configured_api_key: Optional[str] = None _configured_pipeline_id: Optional[str] = None _configured_base_url: Optional[str] = None +_configured_timeout: Optional[Union[int, float]] = None def configure( api_key: Optional[str] = None, inference_pipeline_id: Optional[str] = None, base_url: Optional[str] = None, - timeout: Optional[int] = None, + timeout: Optional[Union[int, float]] = None, ) -> None: """Configure the Openlayer tracer with custom settings. @@ -53,7 +54,7 @@ def configure( If not provided, falls back to OPENLAYER_INFERENCE_PIPELINE_ID environment variable. base_url: The base URL for the Openlayer API. If not provided, falls back to OPENLAYER_BASE_URL environment variable or the default. - timeout: The timeout for the Openlayer API. Defaults to 1 minute. + timeout: The timeout for the Openlayer API in seconds (int or float). Defaults to 60 seconds. Examples: >>> import openlayer.lib.tracing.tracer as tracer From aef89019ffc0b617d9bf4e100eefcc719f3c1be3 Mon Sep 17 00:00:00 2001 From: Vinicius Mello Date: Wed, 24 Sep 2025 12:34:12 -0300 Subject: [PATCH 3/7] test: enhance tracer configuration tests to include timeout validation --- tests/test_tracer_configuration.py | 37 +++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/tests/test_tracer_configuration.py b/tests/test_tracer_configuration.py index 7303f139..60cbd2e2 100644 --- a/tests/test_tracer_configuration.py +++ b/tests/test_tracer_configuration.py @@ -15,6 +15,7 @@ def teardown_method(self): tracer._configured_api_key = None tracer._configured_pipeline_id = None tracer._configured_base_url = None + tracer._configured_timeout = None tracer._client = None def test_configure_sets_global_variables(self): @@ -22,12 +23,14 @@ def test_configure_sets_global_variables(self): api_key = "test_api_key" pipeline_id = "test_pipeline_id" base_url = "https://test.api.com" + timeout = 30.5 - tracer.configure(api_key=api_key, inference_pipeline_id=pipeline_id, base_url=base_url) + tracer.configure(api_key=api_key, inference_pipeline_id=pipeline_id, base_url=base_url, timeout=timeout) assert tracer._configured_api_key == api_key assert tracer._configured_pipeline_id == pipeline_id assert tracer._configured_base_url == base_url + assert tracer._configured_timeout == timeout def test_configure_resets_client(self): """Test that configure() resets the client to force recreation.""" @@ -77,6 +80,30 @@ def test_get_client_uses_both_configured_values(self, mock_openlayer: Any) -> No mock_openlayer.assert_called_once_with(api_key=api_key, base_url=base_url) + @patch("openlayer.lib.tracing.tracer.Openlayer") + def test_get_client_uses_configured_timeout(self, mock_openlayer: Any) -> None: + """Test that _get_client() uses the configured timeout.""" + with patch.object(tracer, "_publish", True): + timeout = 45.5 + tracer.configure(timeout=timeout) + + tracer._get_client() + + mock_openlayer.assert_called_once_with(timeout=timeout) + + @patch("openlayer.lib.tracing.tracer.Openlayer") + def test_get_client_uses_all_configured_values(self, mock_openlayer: Any) -> None: + """Test that _get_client() uses all configured values together.""" + with patch.object(tracer, "_publish", True): + api_key = "configured_api_key" + base_url = "https://configured.api.com" + timeout = 25 + tracer.configure(api_key=api_key, base_url=base_url, timeout=timeout) + + tracer._get_client() + + mock_openlayer.assert_called_once_with(api_key=api_key, base_url=base_url, timeout=timeout) + @patch("openlayer.lib.tracing.tracer.DefaultHttpxClient") @patch("openlayer.lib.tracing.tracer.Openlayer") def test_get_client_with_ssl_disabled_and_config(self, mock_openlayer: Any, mock_http_client: Any) -> None: @@ -150,13 +177,17 @@ def test_configure_with_none_values(self): """Test that configure() with None values doesn't overwrite existing config.""" # Set initial configuration tracer.configure( - api_key="initial_key", inference_pipeline_id="initial_pipeline", base_url="https://initial.com" + api_key="initial_key", + inference_pipeline_id="initial_pipeline", + base_url="https://initial.com", + timeout=60.0 ) # Configure with None values - tracer.configure(api_key=None, inference_pipeline_id=None, base_url=None) + tracer.configure(api_key=None, inference_pipeline_id=None, base_url=None, timeout=None) # Values should be set to None (this is the expected behavior) assert tracer._configured_api_key is None assert tracer._configured_pipeline_id is None assert tracer._configured_base_url is None + assert tracer._configured_timeout is None From 78800041c6920e08b250c2b2e3d74e1991c61706 Mon Sep 17 00:00:00 2001 From: Vinicius Mello Date: Wed, 24 Sep 2025 12:34:48 -0300 Subject: [PATCH 4/7] docs: update README to include tracing timeout configuration details --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 4f32c54b..defaeede 100644 --- a/README.md +++ b/README.md @@ -319,6 +319,28 @@ On timeout, an `APITimeoutError` is thrown. Note that requests that time out are [retried twice by default](#retries). +#### Tracing Timeouts + +When using the tracing decorators (`@trace()` and `@trace_async()`), you can also configure timeouts for the underlying API calls to Openlayer using the `tracer.configure()` function: + +```python +from openlayer.lib import trace, configure + +# Configure timeout for tracing API calls (in seconds) +configure( + api_key="your_api_key_here", + inference_pipeline_id="your_pipeline_id_here", + timeout=30.0 # 30 seconds timeout for tracing API calls (int or float) +) + +# Now use the decorators normally +trace() +def my_function(): + return "result" +``` + +This timeout setting applies to all API calls made by the tracer when streaming trace data to Openlayer. If not specified, the default timeout of 60 seconds is used. + ## Advanced ### Logging From 3bf8b7b424687d2bc5d7332f3b1e205fe631d348 Mon Sep 17 00:00:00 2001 From: Vinicius Mello Date: Wed, 24 Sep 2025 12:35:44 -0300 Subject: [PATCH 5/7] feat: add max retries configuration to Openlayer tracer --- src/openlayer/lib/tracing/tracer.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/openlayer/lib/tracing/tracer.py b/src/openlayer/lib/tracing/tracer.py index bf576743..222d8b55 100644 --- a/src/openlayer/lib/tracing/tracer.py +++ b/src/openlayer/lib/tracing/tracer.py @@ -35,6 +35,7 @@ _configured_pipeline_id: Optional[str] = None _configured_base_url: Optional[str] = None _configured_timeout: Optional[Union[int, float]] = None +_configured_max_retries: Optional[int] = None def configure( @@ -42,11 +43,12 @@ def configure( inference_pipeline_id: Optional[str] = None, base_url: Optional[str] = None, timeout: Optional[Union[int, float]] = None, + max_retries: Optional[int] = None, ) -> None: """Configure the Openlayer tracer with custom settings. This function allows you to programmatically set the API key, inference pipeline ID, - and base URL for the Openlayer client, instead of relying on environment variables. + base URL, timeout, and retry settings for the Openlayer client, instead of relying on environment variables. Args: api_key: The Openlayer API key. If not provided, falls back to OPENLAYER_API_KEY environment variable. @@ -55,6 +57,7 @@ def configure( base_url: The base URL for the Openlayer API. If not provided, falls back to OPENLAYER_BASE_URL environment variable or the default. timeout: The timeout for the Openlayer API in seconds (int or float). Defaults to 60 seconds. + max_retries: The maximum number of retries for failed API requests. Defaults to 2. Examples: >>> import openlayer.lib.tracing.tracer as tracer @@ -65,12 +68,13 @@ def configure( >>> def my_function(): ... return "result" """ - global _configured_api_key, _configured_pipeline_id, _configured_base_url, _configured_timeout, _client + global _configured_api_key, _configured_pipeline_id, _configured_base_url, _configured_timeout, _configured_max_retries, _client _configured_api_key = api_key _configured_pipeline_id = inference_pipeline_id _configured_base_url = base_url _configured_timeout = timeout + _configured_max_retries = max_retries # Reset the client so it gets recreated with new configuration _client = None @@ -97,6 +101,9 @@ def _get_client() -> Optional[Openlayer]: if _configured_timeout is not None: client_kwargs["timeout"] = _configured_timeout + if _configured_max_retries is not None: + client_kwargs["max_retries"] = _configured_max_retries + if _verify_ssl: _client = Openlayer(**client_kwargs) else: From e27978045a9fd0f62921d14015900669c71e8e80 Mon Sep 17 00:00:00 2001 From: Vinicius Mello Date: Wed, 24 Sep 2025 12:36:25 -0300 Subject: [PATCH 6/7] docs: update README to clarify tracing configuration with timeouts and retries --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index defaeede..9eb46bdf 100644 --- a/README.md +++ b/README.md @@ -319,27 +319,28 @@ On timeout, an `APITimeoutError` is thrown. Note that requests that time out are [retried twice by default](#retries). -#### Tracing Timeouts +#### Tracing Configuration -When using the tracing decorators (`@trace()` and `@trace_async()`), you can also configure timeouts for the underlying API calls to Openlayer using the `tracer.configure()` function: +When using the tracing decorators (`@trace()` and `@trace_async()`), you can configure timeouts and retries for the underlying API calls to Openlayer using the `configure()` function: ```python from openlayer.lib import trace, configure -# Configure timeout for tracing API calls (in seconds) +# Configure timeout and retries for tracing API calls configure( api_key="your_api_key_here", inference_pipeline_id="your_pipeline_id_here", - timeout=30.0 # 30 seconds timeout for tracing API calls (int or float) + timeout=30.0, # 30 seconds timeout for tracing API calls (int or float) + max_retries=5 # Maximum number of retries for failed requests (default: 2) ) # Now use the decorators normally -trace() +@trace() def my_function(): return "result" ``` -This timeout setting applies to all API calls made by the tracer when streaming trace data to Openlayer. If not specified, the default timeout of 60 seconds is used. +These settings apply to all API calls made by the tracer when streaming trace data to Openlayer. If not specified, the defaults are 60 seconds timeout and 2 retries. ## Advanced From 3d3793b6a59e5a454632a980e32424c2d3c89d9f Mon Sep 17 00:00:00 2001 From: Vinicius Mello Date: Wed, 24 Sep 2025 12:41:29 -0300 Subject: [PATCH 7/7] test: enhance tracer configuration tests to include max retries validation Added tests to verify the correct configuration and usage of the `max_retries` parameter in the tracer. Updated existing tests to incorporate `max_retries` in the configuration and assertions, ensuring that the tracer behaves as expected with this new parameter. --- tests/test_tracer_configuration.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/test_tracer_configuration.py b/tests/test_tracer_configuration.py index 60cbd2e2..a83745c4 100644 --- a/tests/test_tracer_configuration.py +++ b/tests/test_tracer_configuration.py @@ -16,6 +16,7 @@ def teardown_method(self): tracer._configured_pipeline_id = None tracer._configured_base_url = None tracer._configured_timeout = None + tracer._configured_max_retries = None tracer._client = None def test_configure_sets_global_variables(self): @@ -24,13 +25,15 @@ def test_configure_sets_global_variables(self): pipeline_id = "test_pipeline_id" base_url = "https://test.api.com" timeout = 30.5 + max_retries = 5 - tracer.configure(api_key=api_key, inference_pipeline_id=pipeline_id, base_url=base_url, timeout=timeout) + tracer.configure(api_key=api_key, inference_pipeline_id=pipeline_id, base_url=base_url, timeout=timeout, max_retries=max_retries) assert tracer._configured_api_key == api_key assert tracer._configured_pipeline_id == pipeline_id assert tracer._configured_base_url == base_url assert tracer._configured_timeout == timeout + assert tracer._configured_max_retries == max_retries def test_configure_resets_client(self): """Test that configure() resets the client to force recreation.""" @@ -91,6 +94,17 @@ def test_get_client_uses_configured_timeout(self, mock_openlayer: Any) -> None: mock_openlayer.assert_called_once_with(timeout=timeout) + @patch("openlayer.lib.tracing.tracer.Openlayer") + def test_get_client_uses_configured_max_retries(self, mock_openlayer: Any) -> None: + """Test that _get_client() uses the configured max_retries.""" + with patch.object(tracer, "_publish", True): + max_retries = 10 + tracer.configure(max_retries=max_retries) + + tracer._get_client() + + mock_openlayer.assert_called_once_with(max_retries=max_retries) + @patch("openlayer.lib.tracing.tracer.Openlayer") def test_get_client_uses_all_configured_values(self, mock_openlayer: Any) -> None: """Test that _get_client() uses all configured values together.""" @@ -98,11 +112,12 @@ def test_get_client_uses_all_configured_values(self, mock_openlayer: Any) -> Non api_key = "configured_api_key" base_url = "https://configured.api.com" timeout = 25 - tracer.configure(api_key=api_key, base_url=base_url, timeout=timeout) + max_retries = 3 + tracer.configure(api_key=api_key, base_url=base_url, timeout=timeout, max_retries=max_retries) tracer._get_client() - mock_openlayer.assert_called_once_with(api_key=api_key, base_url=base_url, timeout=timeout) + mock_openlayer.assert_called_once_with(api_key=api_key, base_url=base_url, timeout=timeout, max_retries=max_retries) @patch("openlayer.lib.tracing.tracer.DefaultHttpxClient") @patch("openlayer.lib.tracing.tracer.Openlayer") @@ -180,14 +195,16 @@ def test_configure_with_none_values(self): api_key="initial_key", inference_pipeline_id="initial_pipeline", base_url="https://initial.com", - timeout=60.0 + timeout=60.0, + max_retries=5 ) # Configure with None values - tracer.configure(api_key=None, inference_pipeline_id=None, base_url=None, timeout=None) + tracer.configure(api_key=None, inference_pipeline_id=None, base_url=None, timeout=None, max_retries=None) # Values should be set to None (this is the expected behavior) assert tracer._configured_api_key is None assert tracer._configured_pipeline_id is None assert tracer._configured_base_url is None assert tracer._configured_timeout is None + assert tracer._configured_max_retries is None