diff --git a/src/engram/_base_client.py b/src/engram/_base_client.py index 93cad73..59c7233 100644 --- a/src/engram/_base_client.py +++ b/src/engram/_base_client.py @@ -25,7 +25,7 @@ def __init__( raise ValidationError("Timeout must be greater than 0.") normalized_base_url = base_url.rstrip("/") - default_headers = _build_headers(api_key=api_key, header_overrides=headers or {}) + default_headers = _build_headers(api_key=api_key, extra_headers=headers or {}) self._config = ClientConfig( base_url=normalized_base_url, @@ -43,16 +43,24 @@ def default_headers(self) -> dict[str, str]: return dict(self._config.headers) +CLIENT_ORIGIN_HEADER = "X-Engram-Client" +SDK_CLIENT_TOKEN = f"python-sdk/{__version__}" + + def _build_headers( *, api_key: str, - header_overrides: Mapping[str, str], + extra_headers: Mapping[str, str], ) -> dict[str, str]: - headers: dict[str, str] = { - "Accept": "application/json", - "Content-Type": "application/json", - "User-Agent": f"weaviate-engram/{__version__}", - "Authorization": f"Bearer {api_key}", - } - headers.update(header_overrides) + headers = dict(extra_headers) + headers.update( + { + "Accept": "application/json", + "Content-Type": "application/json", + "Authorization": f"Bearer {api_key}", + } + ) + + caller = extra_headers.get(CLIENT_ORIGIN_HEADER) + headers[CLIENT_ORIGIN_HEADER] = f"{caller} {SDK_CLIENT_TOKEN}" if caller else SDK_CLIENT_TOKEN return headers diff --git a/tests/test_client_async.py b/tests/test_client_async.py index c6ca3c0..faf9889 100644 --- a/tests/test_client_async.py +++ b/tests/test_client_async.py @@ -18,6 +18,7 @@ ) from engram.async_client import DEFAULT_BASE_URL, AsyncEngramClient from engram.errors import APIError, AuthenticationError, ValidationError +from engram.version import __version__ @pytest.mark.asyncio @@ -29,6 +30,7 @@ async def test_async_client_defaults() -> None: assert client.default_headers["Accept"] == "application/json" assert client.default_headers["Content-Type"] == "application/json" assert client.default_headers["Authorization"] == "Bearer test-key" + assert client.default_headers["X-Engram-Client"] == f"python-sdk/{__version__}" finally: await client.aclose() diff --git a/tests/test_client_sync.py b/tests/test_client_sync.py index 94948e0..f71b327 100644 --- a/tests/test_client_sync.py +++ b/tests/test_client_sync.py @@ -18,6 +18,7 @@ ) from engram.client import DEFAULT_BASE_URL, EngramClient from engram.errors import APIError, AuthenticationError, ValidationError +from engram.version import __version__ def test_client_defaults() -> None: @@ -28,6 +29,21 @@ def test_client_defaults() -> None: assert client.default_headers["Accept"] == "application/json" assert client.default_headers["Content-Type"] == "application/json" assert client.default_headers["Authorization"] == "Bearer test-key" + assert client.default_headers["X-Engram-Client"] == f"python-sdk/{__version__}" + finally: + client.close() + + +def test_client_origin_header_composition() -> None: + client = EngramClient( + api_key="test-key", + headers={"X-Engram-Client": "example-integration/1.2.3"}, + ) + try: + assert ( + client.default_headers["X-Engram-Client"] + == f"example-integration/1.2.3 python-sdk/{__version__}" + ) finally: client.close()