From 64ebf36b6b4885c67046d35ba134d359e5747f8a Mon Sep 17 00:00:00 2001 From: Tyler Gillam Date: Wed, 24 Dec 2025 11:48:45 -0600 Subject: [PATCH 1/2] Add deployed ADK agent user agent --- src/gradient/_base_client.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gradient/_base_client.py b/src/gradient/_base_client.py index f038b215..5f4deae4 100644 --- a/src/gradient/_base_client.py +++ b/src/gradient/_base_client.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import sys import json import time @@ -678,7 +679,12 @@ def user_agent(self) -> str: # Format: "Gradient/package/version" package = self._user_agent_package or "Python" version = self._user_agent_version if self._user_agent_package and self._user_agent_version else self._version - return f"{self.__class__.__name__}/{package}/{version}" + base_agent = f"{self.__class__.__name__}/{package}/{version}" + + deployment_uuid = os.environ.get("AGENT_WORKSPACE_DEPLOYMENT_UUID") + if deployment_uuid: + return f"{base_agent}/GRADIENT_ADK_AGENT/{deployment_uuid}" + return base_agent @property def base_url(self) -> URL: From e2d09d4f451c3c4cef7df74efea87583360aa9ec Mon Sep 17 00:00:00 2001 From: Tyler Gillam Date: Wed, 24 Dec 2025 11:51:52 -0600 Subject: [PATCH 2/2] Add user agent test --- tests/test_client.py | 50 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 4b645c08..5b78ff7c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -945,7 +945,9 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float + ) -> None: client = Gradient( base_url=base_url, access_token=access_token, @@ -956,7 +958,9 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) - calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + calculated = client._calculate_retry_timeout( + remaining_retries, options, headers + ) assert calculated == pytest.approx(timeout, rel=0.5 * 0.875) # type: ignore[misc] @mock.patch( @@ -1180,6 +1184,40 @@ def test_follow_redirects_disabled( assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" + def test_user_agent_includes_adk_agent_when_env_var_set(self) -> None: + # Test that user agent includes GRADIENT_ADK_AGENT when AGENT_WORKSPACE_DEPLOYMENT_UUID is set + test_uuid = "test-deployment-uuid-12345" + + with update_env(AGENT_WORKSPACE_DEPLOYMENT_UUID=test_uuid): + client = Gradient( + base_url=base_url, + access_token=access_token, + model_access_key=model_access_key, + agent_access_key=agent_access_key, + _strict_response_validation=True, + ) + user_agent = client.user_agent + assert "GRADIENT_ADK_AGENT" in user_agent + assert test_uuid in user_agent + assert user_agent.endswith(f"/GRADIENT_ADK_AGENT/{test_uuid}") + client.close() + + def test_user_agent_excludes_adk_agent_when_env_var_not_set(self) -> None: + # Test that user agent does not include GRADIENT_ADK_AGENT when env var is not set + from gradient._types import Omit + + with update_env(AGENT_WORKSPACE_DEPLOYMENT_UUID=Omit()): + client = Gradient( + base_url=base_url, + access_token=access_token, + model_access_key=model_access_key, + agent_access_key=agent_access_key, + _strict_response_validation=True, + ) + user_agent = client.user_agent + assert "GRADIENT_ADK_AGENT" not in user_agent + client.close() + class TestAsyncGradient: @pytest.mark.respx(base_url=base_url) @@ -2081,7 +2119,9 @@ class Model(BaseModel): ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @pytest.mark.asyncio - async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float + ) -> None: async_client = AsyncGradient( base_url=base_url, access_token=access_token, @@ -2092,7 +2132,9 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) - calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) + calculated = async_client._calculate_retry_timeout( + remaining_retries, options, headers + ) assert calculated == pytest.approx(timeout, rel=0.5 * 0.875) # type: ignore[misc] @mock.patch(