Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "sap-cloud-sdk"
version = "0.33.1"
version = "0.33.2"
description = "SAP Cloud SDK for Python"
readme = "README.md"
license = "Apache-2.0"
Expand Down
6 changes: 5 additions & 1 deletion src/sap_cloud_sdk/agentgateway/_fragments.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
)

from sap_cloud_sdk.agentgateway.exceptions import MCPServerNotFoundError
from sap_cloud_sdk.core.telemetry import Module

logger = logging.getLogger(__name__)

Expand All @@ -35,7 +36,10 @@ class FragmentLabel(str, Enum):


def _list_fragments_by_label(label: FragmentLabel, tenant_subdomain: str) -> list:
client = create_fragment_client(instance=_DESTINATION_INSTANCE)
client = create_fragment_client(
instance=_DESTINATION_INSTANCE,
_telemetry_source=Module.AGENTGATEWAY,
)
return client.list_instance_fragments(
filter=ListOptions(filter_labels=[Label(key=LABEL_KEY, values=[label.value])]),
tenant=tenant_subdomain,
Expand Down
11 changes: 9 additions & 2 deletions src/sap_cloud_sdk/agentgateway/_lob.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ConsumptionLevel,
ConsumptionOptions,
)
from sap_cloud_sdk.core.telemetry import Module

from sap_cloud_sdk.agentgateway._fragments import (
LABEL_KEY,
Expand Down Expand Up @@ -88,7 +89,10 @@ def _fetch_auth_token(
Raises:
MCPServerNotFoundError: If no auth token is returned.
"""
client = create_destination_client(instance=_DESTINATION_INSTANCE)
client = create_destination_client(
instance=_DESTINATION_INSTANCE,
_telemetry_source=Module.AGENTGATEWAY,
)
dest = client.get_destination(
dest_name,
level=ConsumptionLevel.PROVIDER_SUBACCOUNT,
Expand Down Expand Up @@ -130,7 +134,10 @@ def get_ias_client_id_lob() -> str:
Any exception raised by the destination client.
"""
dest_name = _ias_dest_name()
client = create_destination_client(instance=_DESTINATION_INSTANCE)
client = create_destination_client(
instance=_DESTINATION_INSTANCE,
_telemetry_source=Module.AGENTGATEWAY,
)
dest = client.get_destination(
dest_name,
level=ConsumptionLevel.PROVIDER_SUBACCOUNT,
Expand Down
12 changes: 11 additions & 1 deletion src/sap_cloud_sdk/agentgateway/agw_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def __init__(
self,
tenant_subdomain: str | Callable[[], str] | None = None,
config: ClientConfig | None = None,
_telemetry_source: Module | None = None,
):
"""Initialize the Agent Gateway client.

Expand All @@ -115,11 +116,13 @@ def __init__(
Can be a string or a callable returning a string.
Required for LoB agents, ignored for Customer agents.
config: Client configuration. Uses defaults if not provided.
_telemetry_source: Internal telemetry source identifier. Not intended for external use.
"""
self._tenant_subdomain = tenant_subdomain
self._config = config or ClientConfig()
self._token_cache = _TokenCache(self._config)
self._gateway_url_cache = _GatewayUrlCache()
self._telemetry_source = _telemetry_source

@staticmethod
def _resolve_value(
Expand Down Expand Up @@ -580,6 +583,8 @@ def _unwrap_exception_group(exc: BaseException) -> BaseException:
def create_client(
tenant_subdomain: str | Callable[[], str] | None = None,
config: ClientConfig | None = None,
*,
_telemetry_source: Module | None = None,
) -> AgentGatewayClient:
"""Create an Agent Gateway client for discovering and invoking MCP tools.

Expand All @@ -591,6 +596,7 @@ def create_client(
Can be a string or a callable returning a string.
Required for LoB agents, ignored for Customer agents.
config: Client configuration. Uses defaults if not provided.
_telemetry_source: Internal telemetry source identifier. Not intended for external use.

Returns:
AgentGatewayClient instance.
Expand Down Expand Up @@ -644,4 +650,8 @@ def create_client(
user_auth = await agw_client.get_user_auth(user_token="user-jwt")
```
"""
return AgentGatewayClient(tenant_subdomain=tenant_subdomain, config=config)
return AgentGatewayClient(
tenant_subdomain=tenant_subdomain,
config=config,
_telemetry_source=_telemetry_source,
)
5 changes: 4 additions & 1 deletion src/sap_cloud_sdk/core/auditlog_ng/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ def _get_config_from_destination(
create_client as _dest_create_client,
)

dest_client = _dest_create_client(instance=destination_instance)
dest_client = _dest_create_client(
instance=destination_instance,
_telemetry_source=Module.AUDITLOG_NG,
)
options = (
ConsumptionOptions(
fragment_name=fragment_name, fragment_level=ConsumptionLevel.SUBACCOUNT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,13 +357,16 @@ def _cert_from_inline_values(
def _cert_from_destination(self, name: str) -> str:
"""Resolve a combined PEM bundle via Destination and write it to a temp file."""
try:
from sap_cloud_sdk.core.telemetry import Module
from sap_cloud_sdk.destination import (
AccessStrategy,
create_certificate_client,
create_client,
)

destination_client = create_client()
destination_client = create_client(
_telemetry_source=Module.DATA_ANONYMIZATION,
)
destination = destination_client.get_instance_destination(name)
if destination is None:
destination = destination_client.get_subaccount_destination(
Expand All @@ -376,7 +379,9 @@ def _cert_from_destination(self, name: str) -> str:
key_store_location = self._get_destination_keystore_location(
destination, name
)
cert_client = create_certificate_client()
cert_client = create_certificate_client(
_telemetry_source=Module.DATA_ANONYMIZATION,
)
cert = cert_client.get_instance_certificate(key_store_location)
if cert is None:
cert = cert_client.get_subaccount_certificate(
Expand Down
15 changes: 12 additions & 3 deletions src/sap_cloud_sdk/destination/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import os
from typing import Optional

from sap_cloud_sdk.core.telemetry import Module
from sap_cloud_sdk.destination._models import (
Destination,
AuthToken,
Expand Down Expand Up @@ -83,6 +84,7 @@ def create_client(
instance: Optional[str] = None,
config: Optional[DestinationConfig] = None,
use_default_proxy: bool = False,
_telemetry_source: Optional[Module] = None,
):
"""Creates a Destination client with local/cloud detection.

Expand All @@ -98,6 +100,7 @@ def create_client(
will attempt to load transparent proxy configuration from APPFND_CONHOS_TRANSP_PROXY
environment variable. To use a custom proxy, use client.set_proxy() after creation.
Defaults to False.
_telemetry_source: Internal telemetry source identifier. Not intended for external use.

Returns:
DestinationClient or LocalDevDestinationClient: Client implementing the Destination interface.
Expand All @@ -118,7 +121,9 @@ def create_client(
tp = TokenProvider(binding)
http = DestinationHttp(config=binding, token_provider=tp)

return DestinationClient(http, use_default_proxy)
return DestinationClient(
http, use_default_proxy, _telemetry_source=_telemetry_source
)

except Exception as e:
raise ClientCreationError(f"failed to create destination client: {e}")
Expand All @@ -128,6 +133,7 @@ def create_fragment_client(
*,
instance: Optional[str] = None,
config: Optional[DestinationConfig] = None,
_telemetry_source: Optional[Module] = None,
):
"""Creates a Fragment client with local/cloud detection.

Expand All @@ -139,6 +145,7 @@ def create_fragment_client(
Args:
instance: Instance name used for secret resolution in cloud mode. Defaults to "default".
config: Optional explicit DestinationConfig.
_telemetry_source: Internal telemetry source identifier. Not intended for external use.

Returns:
FragmentClient or LocalDevFragmentClient: Client for managing destination fragments.
Expand All @@ -159,7 +166,7 @@ def create_fragment_client(
tp = TokenProvider(binding)
http = DestinationHttp(config=binding, token_provider=tp)

return FragmentClient(http)
return FragmentClient(http, _telemetry_source=_telemetry_source)

except Exception as e:
raise ClientCreationError(f"failed to create fragment client: {e}")
Expand All @@ -169,6 +176,7 @@ def create_certificate_client(
*,
instance: Optional[str] = None,
config: Optional[DestinationConfig] = None,
_telemetry_source: Optional[Module] = None,
):
"""Creates a Certificate client with local/cloud detection.

Expand All @@ -180,6 +188,7 @@ def create_certificate_client(
Args:
instance: Instance name used for secret resolution in cloud mode. Defaults to "default".
config: Optional explicit DestinationConfig.
_telemetry_source: Internal telemetry source identifier. Not intended for external use.

Returns:
CertificateClient or LocalDevCertificateClient: Client for managing certificates.
Expand All @@ -200,7 +209,7 @@ def create_certificate_client(
tp = TokenProvider(binding)
http = DestinationHttp(config=binding, token_provider=tp)

return CertificateClient(http)
return CertificateClient(http, _telemetry_source=_telemetry_source)

except Exception as e:
raise ClientCreationError(f"failed to create certificate client: {e}")
Expand Down
8 changes: 7 additions & 1 deletion src/sap_cloud_sdk/destination/certificate_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,22 @@ class CertificateClient:
```
"""

def __init__(self, http: DestinationHttp) -> None:
def __init__(
self,
http: DestinationHttp,
_telemetry_source: Optional[Module] = None,
) -> None:
"""Initialize CertificateClient with dependency injection.

Args:
http: Configured HTTP transport for the Destination Service.
_telemetry_source: Internal telemetry source identifier. Not intended for external use.

Raises:
DestinationOperationError: If initialization fails.
"""
self._http = http
self._telemetry_source = _telemetry_source

# ---------- Read operations ----------

Expand Down
9 changes: 8 additions & 1 deletion src/sap_cloud_sdk/destination/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ class DestinationClient:
```
"""

def __init__(self, http: DestinationHttp, use_default_proxy: bool = False) -> None:
def __init__(
self,
http: DestinationHttp,
use_default_proxy: bool = False,
_telemetry_source: Optional[Module] = None,
) -> None:
"""Initialize DestinationClient with dependency injection.

Note:
Expand All @@ -108,13 +113,15 @@ def __init__(self, http: DestinationHttp, use_default_proxy: bool = False) -> No
use_default_proxy: Whether to use the default transparent proxy for all get operations.
When True, will attempt to load transparent proxy configuration from
APPFND_CONHOS_TRANSP_PROXY environment variable. Defaults to False.
_telemetry_source: Internal telemetry source identifier. Not intended for external use.

Raises:
DestinationOperationError: If initialization fails.
"""
self._http = http
self._client_proxy_enabled = use_default_proxy
self._transparent_proxy = load_transparent_proxy()
self._telemetry_source = _telemetry_source

def set_proxy(self, transparent_proxy: TransparentProxy) -> None:
"""Set or update the transparent proxy configuration for this client.
Expand Down
8 changes: 7 additions & 1 deletion src/sap_cloud_sdk/destination/fragment_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,22 @@ class FragmentClient:
```
"""

def __init__(self, http: DestinationHttp) -> None:
def __init__(
self,
http: DestinationHttp,
_telemetry_source: Optional[Module] = None,
) -> None:
"""Initialize FragmentClient with dependency injection.

Args:
http: Configured HTTP transport for the Destination Service.
_telemetry_source: Internal telemetry source identifier. Not intended for external use.

Raises:
DestinationOperationError: If initialization fails.
"""
self._http = http
self._telemetry_source = _telemetry_source

# ---------- Read operations ----------

Expand Down
4 changes: 3 additions & 1 deletion src/sap_cloud_sdk/extensibility/_ums_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import httpx

from sap_cloud_sdk.core.telemetry import Module
from sap_cloud_sdk.destination import ConsumptionLevel
from sap_cloud_sdk.destination import create_client as create_destination_client
from sap_cloud_sdk.extensibility._models import (
Expand Down Expand Up @@ -450,7 +451,8 @@ def __init__(self, agent_ord_id: str, config: ExtensibilityConfig) -> None:
self._config = config
self._destination_name = _ums_destination_name(config.destination_name)
self._dest_client = create_destination_client(
instance=config.destination_instance
instance=config.destination_instance,
_telemetry_source=Module.EXTENSIBILITY,
)
self._cache: collections.OrderedDict[
tuple[str, str],
Expand Down
4 changes: 3 additions & 1 deletion src/sap_cloud_sdk/extensibility/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,9 @@ async def call_hook_agw(
)
```
"""
agw_client = create_agw_client(tenant_subdomain)
agw_client = create_agw_client(
tenant_subdomain, _telemetry_source=Module.EXTENSIBILITY
)
execute_tool, get_exec_tool = await self._discover_n8n_tools(
agw_client, user_token
)
Expand Down
21 changes: 21 additions & 0 deletions tests/agentgateway/unit/test_agw_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
MCPTool,
AgentGatewaySDKError,
)
from sap_cloud_sdk.core.telemetry import Module


# ============================================================
Expand Down Expand Up @@ -982,6 +983,26 @@ async def test_wraps_unexpected_error(self):
await agw_client.list_agent_cards()


# ============================================================
# Test: _telemetry_source wiring
# ============================================================


class TestCreateClientTelemetrySource:
"""Verify _telemetry_source kwarg is stored on the AgentGatewayClient."""

def test_default_source_is_none(self):
agw_client = create_client(tenant_subdomain="my-tenant")
assert agw_client._telemetry_source is None

def test_explicit_source_is_stored(self):
agw_client = create_client(
tenant_subdomain="my-tenant",
_telemetry_source=Module.EXTENSIBILITY,
)
assert agw_client._telemetry_source is Module.EXTENSIBILITY


# ============================================================
# Test: get_ias_client_id
# ============================================================
Expand Down
3 changes: 2 additions & 1 deletion tests/agentgateway/unit/test_lob.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ def test_uses_correct_filter_labels(self):

list_mcp_fragments("tenant-sub")

mock_client.assert_called_once_with(instance="default")
mock_client.assert_called_once()
assert mock_client.call_args.kwargs["instance"] == "default"
call_args = mock_client.return_value.list_instance_fragments.call_args
filter_opt = call_args.kwargs.get("filter")
assert filter_opt is not None
Expand Down
Loading
Loading