From d2eba638aae0852294d02beef7e8fbb09b7eae3f Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 2 Apr 2026 16:13:28 +0000 Subject: [PATCH 1/7] Add check for if file exists to completion hook --- .../util/genai/_upload/completion_hook.py | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py index 4f22861396a..422d2b4bbff 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py @@ -156,6 +156,24 @@ def __init__( f"Invalid {upload_format=}. Must be one of {_FORMATS}" ) self._format = upload_format + self._content_type = ( + "application/json" + if self._format == "json" + else "application/jsonl" + ) + test_path = posixpath.join( + self._base_path, + f"one_off_test_to_see_if_upload_works.{self._format}", + ) + try: + with self._fs.open( + test_path, "w", content_type=self._content_type + ) as file: + file.write("\n") + except Exception as e: + raise ValueError( + f"Failed to write file to the following path to test if upload working: {test_path}.\n Got error: {e}" + ) # Use a ThreadPoolExecutor for its queueing and thread management. The semaphore # limits the number of queued tasks. If the queue is full, data will be dropped. @@ -271,13 +289,7 @@ def _do_upload( for message_idx, line in enumerate(message_lines): line[_MESSAGE_INDEX_KEY] = message_idx - content_type = ( - "application/json" - if self._format == "json" - else "application/jsonl" - ) - - with self._fs.open(path, "w", content_type=content_type) as file: + with self._fs.open(path, "w", content_type=self._content_type) as file: for message in message_lines: gen_ai_json_dump(message, file) file.write("\n") From 9516457bf58c8fdb01861b248ff181aad142010d Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 2 Apr 2026 16:56:58 +0000 Subject: [PATCH 2/7] Add tests --- util/opentelemetry-util-genai/tests/test_upload.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/util/opentelemetry-util-genai/tests/test_upload.py b/util/opentelemetry-util-genai/tests/test_upload.py index aa5fcb5b4a1..f10fc86894b 100644 --- a/util/opentelemetry-util-genai/tests/test_upload.py +++ b/util/opentelemetry-util-genai/tests/test_upload.py @@ -102,6 +102,8 @@ def setUp(self): self.hook = UploadCompletionHook( base_path=BASE_PATH, max_queue_size=MAXSIZE, lru_cache_max_size=5 ) + # 1 upload is done when creating the UploadHook to ensure upload works. Reset mock. + self.mock_fs.reset_mock() def tearDown(self) -> None: self.hook.shutdown() @@ -145,6 +147,18 @@ def test_upload_then_shutdown(self): "should have uploaded 4 files", ) + def test_failed_upload_causes_initializer_to_throw(self): + self.mock_fs.open.side_effect = ValueError("Failed for some reason!") + with self.assertRaisesRegex( + ValueError, + "Failed to write file to the following path to test if upload working", + ): + UploadCompletionHook( + base_path=BASE_PATH, + max_queue_size=MAXSIZE, + lru_cache_max_size=5, + ) + def test_lru_cache_works(self): record = LogRecord() self.hook.on_completion( From a6f718f4c22e5f41eb1b67255571c9f70281304a Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 2 Apr 2026 18:29:55 +0000 Subject: [PATCH 3/7] Add CHANGELOG entry --- util/opentelemetry-util-genai/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/opentelemetry-util-genai/CHANGELOG.md b/util/opentelemetry-util-genai/CHANGELOG.md index 565a18d5870..646ce399444 100644 --- a/util/opentelemetry-util-genai/CHANGELOG.md +++ b/util/opentelemetry-util-genai/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#4320](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4320)) - Add workflow invocation type to genAI utils ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4310](#4310)) +- Check if upload works at startup in initializer of the `UploadCompletionHook`, instead +of repeatedly failing on every upload ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4390](#4390)). ## Version 0.3b0 (2026-02-20) From 3ce917c416c36c77583964f77b91f170d2a4cebd Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 2 Apr 2026 19:44:56 +0000 Subject: [PATCH 4/7] Try to delete file --- .../util/genai/_upload/completion_hook.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py index 422d2b4bbff..f31bbb3f680 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py @@ -163,7 +163,7 @@ def __init__( ) test_path = posixpath.join( self._base_path, - f"one_off_test_to_see_if_upload_works.{self._format}", + f".one_off_test_to_see_if_upload_works.{self._format}", ) try: with self._fs.open( @@ -172,8 +172,14 @@ def __init__( file.write("\n") except Exception as e: raise ValueError( - f"Failed to write file to the following path to test if upload working: {test_path}.\n Got error: {e}" + f"Failed to write file to the following path, upload is not working: {test_path}.\n Got error: {e}" ) + # Try to delete the file.. But we don't explicitly ask people to grant the GCS delete IAM permission in our + # docs, so if delete fails just leave the file.. + try: + self._fs.rm_file(test_path) + except Exception: + pass # Use a ThreadPoolExecutor for its queueing and thread management. The semaphore # limits the number of queued tasks. If the queue is full, data will be dropped. From 7f1a482010713f4ba4f2f110f340e2183bda96fe Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 2 Apr 2026 19:49:38 +0000 Subject: [PATCH 5/7] Fix linter --- .../src/opentelemetry/util/genai/_upload/completion_hook.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py index f31bbb3f680..8601bd2bcba 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py @@ -170,15 +170,15 @@ def __init__( test_path, "w", content_type=self._content_type ) as file: file.write("\n") - except Exception as e: + except Exception as exception: # pylint: disable=broad-exception-caught raise ValueError( - f"Failed to write file to the following path, upload is not working: {test_path}.\n Got error: {e}" + f"Failed to write file to the following path, upload is not working: {test_path}.\n Got error: {exception}" ) # Try to delete the file.. But we don't explicitly ask people to grant the GCS delete IAM permission in our # docs, so if delete fails just leave the file.. try: self._fs.rm_file(test_path) - except Exception: + except Exception: # pylint: disable=broad-exception-caught pass # Use a ThreadPoolExecutor for its queueing and thread management. The semaphore From e177a516ae0066d6c3ce36eab3a14d3fa5d25024 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 2 Apr 2026 19:51:06 +0000 Subject: [PATCH 6/7] Fix test --- util/opentelemetry-util-genai/tests/test_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/opentelemetry-util-genai/tests/test_upload.py b/util/opentelemetry-util-genai/tests/test_upload.py index f10fc86894b..c9611abf197 100644 --- a/util/opentelemetry-util-genai/tests/test_upload.py +++ b/util/opentelemetry-util-genai/tests/test_upload.py @@ -151,7 +151,7 @@ def test_failed_upload_causes_initializer_to_throw(self): self.mock_fs.open.side_effect = ValueError("Failed for some reason!") with self.assertRaisesRegex( ValueError, - "Failed to write file to the following path to test if upload working", + "Failed to write file to the following path, upload is not working:", ): UploadCompletionHook( base_path=BASE_PATH, From 5bef9ff3bb296553ff582e3e9b96b032b923a86c Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 2 Apr 2026 19:54:37 +0000 Subject: [PATCH 7/7] Ignore unknown function.. --- .../src/opentelemetry/util/genai/_upload/completion_hook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py index 8601bd2bcba..79ebd3a1708 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py @@ -177,7 +177,7 @@ def __init__( # Try to delete the file.. But we don't explicitly ask people to grant the GCS delete IAM permission in our # docs, so if delete fails just leave the file.. try: - self._fs.rm_file(test_path) + self._fs.rm_file(test_path) # pyright: ignore[reportUnknownMemberType] except Exception: # pylint: disable=broad-exception-caught pass