Skip to content

feat(agent-memory): multitenancy via per-tenant bindings#232

Merged
cassiofariasmachado merged 10 commits into
mainfrom
feat/enable-multitenancy-support-for-agent-memory-using-bindings
Jul 16, 2026
Merged

feat(agent-memory): multitenancy via per-tenant bindings#232
cassiofariasmachado merged 10 commits into
mainfrom
feat/enable-multitenancy-support-for-agent-memory-using-bindings

Conversation

@cassiofariasmachado

@cassiofariasmachado cassiofariasmachado commented Jul 15, 2026

Copy link
Copy Markdown
Member

Disclaimer: Do not include SAP-internal or customer-specific information in this PR (e.g. internal system URLs, customer names, tenant IDs, or confidential configurations). This is a public repository.

Description

Adds multitenancy support to the agent_memory module via a per-tenant binding approach. The runtime provisions one dedicated service instance per tenant, mounted at /etc/secrets/appfnd/hana-agent-memory/<tenant-subdomain>/. Each tenant gets its own HttpTransport loaded from its binding at create_client() time.

How it works

from sap_cloud_sdk.agent_memory import create_client, AccessStrategy

# Subscriber — loads binding from /…/acme-corp/ once, all calls use it
client = create_client(
    access_strategy=AccessStrategy.SUBSCRIBER,
    tenant="acme-corp",
)
memories = client.list_memories(agent_id="x", invoker_id="u")

# Provider — loads default binding, emits logger.warning (no isolation)
client = create_client(access_strategy=AccessStrategy.PROVIDER)

access_strategy and tenant are configured once at create_client() time — they are not parameters on individual methods. The AgentMemoryClient simply holds a single _transport resolved at construction.

Key changes

  • AccessStrategy enum (SUBSCRIBER / PROVIDER) — configured at create_client()
  • _load_config_for_instance(instance) in config.py — loads any named binding instance via the secret resolver (mirrors the destination module's pattern)
  • AgentMemoryClient simplified: single _transport field, no config_loader, no per-call overrides, validation at construction time
  • logger.warning() on AccessStrategy.PROVIDER construction — surfaces the no-isolation risk
  • BDD integration tests with full subscriber + provider coverage sharing all step implementations

Related Issue

Closes #

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Dependency update

How to Test

# Unit tests
pytest tests/agent_memory/unit/    # 252 tests pass

# Integration tests — provider only
pytest tests/agent_memory/integration/   # 14 pass, 12 skip

# Integration tests — with subscriber binding
CLOUD_SDK_CFG_HANA_AGENT_MEMORY_<TENANT>_APPLICATION_URL=... \
CLOUD_SDK_CFG_HANA_AGENT_MEMORY_<TENANT>_UAA=... \
CLOUD_SDK_CFG_HANA_AGENT_MEMORY_DEFAULT_SUBSCRIBER_TENANT=<tenant> \
pytest tests/agent_memory/integration/   # 26 pass

Checklist

  • I have read the Contributing Guidelines
  • I have verified that my changes solve the issue
  • I have added/updated automated tests to cover my changes
  • All tests pass locally
  • I have verified that my code follows the Code Guidelines
  • I have updated documentation (if applicable)
  • I have added type hints for all public APIs
  • My code does not contain sensitive information (credentials, tokens, etc.)
  • I have followed Conventional Commits for commit messages

Breaking Changes

⚠️ Important: create_client() with no args raises AgentMemoryValidationError because the default strategy is SUBSCRIBER and requires a tenant. Individual methods no longer accept access_strategy or tenant parameters.

Migration:

# Before
client = create_client()
client.list_memories(agent_id="x", invoker_id="u", access_strategy=AccessStrategy.PROVIDER)

# After — subscriber tenant
client = create_client(tenant="my-subdomain")
client.list_memories(agent_id="x", invoker_id="u")

# After — provider (no isolation)
client = create_client(access_strategy=AccessStrategy.PROVIDER)
client.list_memories(agent_id="x", invoker_id="u")

Additional Notes

  • The public API is forward-compatible with a future native-multitenancy implementation in the Agent Memory service — once available, only the internal create_client() binding logic changes, zero changes to caller code.
  • logger.warning() is emitted at AgentMemoryClient construction time when PROVIDER is used.
  • Subscriber integration tests skip gracefully when the tenant binding is not mounted/configured.

- create_client() and AgentMemoryClient.__init__ accept access_strategy
  (default SUBSCRIBER_ONLY) and tenant as constructor params
- _resolve_tenant is now an instance method; per-call params override
  instance defaults
- All method signatures change to Optional[AccessStrategy] = None
  (sentinel: use instance default)
- user-guide: add client-level configuration section and PROVIDER_ONLY
  isolation warning
- tests: update TestAccessStrategy and TestCreateClient for instance method
- Add _load_config_for_instance(instance) to config.py — loads secrets
  from /etc/secrets/appfnd/hana-agent-memory/{instance}/ following the
  same pattern as the destination module
- Refactor AgentMemoryClient to maintain a per-tenant HttpTransport cache:
  provider binding ('default') and subscriber bindings (one per tenant)
  are each loaded into their own transport and lazily cached
- create_client() selects the initial binding based on access_strategy and
  tenant, and wires config_loader for per-call tenant override support
- Integration test conftest now skips subscriber tests when the tenant
  binding itself is not configured (not just the tenant name env var)
- Unit tests cover _load_config_for_instance, _get_transport routing,
  per-call binding cache, and create_client() factory wiring
Comment thread src/sap_cloud_sdk/agent_memory/__init__.py Outdated
Comment thread src/sap_cloud_sdk/agent_memory/user-guide.md Outdated
@cassiofariasmachado cassiofariasmachado changed the title feat(agent-memory): multitenancy via per-tenant bindings (temporary) feat(agent-memory): multitenancy via per-tenant bindings Jul 15, 2026
… warning

- SUBSCRIBER_ONLY → SUBSCRIBER, PROVIDER_ONLY → PROVIDER across all
  agent_memory source, tests, feature files, and user guide
- Add logger.warning() when AccessStrategy.PROVIDER is used, making
  the no-isolation behaviour explicit at runtime
NicoleMGomes
NicoleMGomes previously approved these changes Jul 15, 2026
@cassiofariasmachado cassiofariasmachado marked this pull request as ready for review July 16, 2026 16:28
@cassiofariasmachado cassiofariasmachado requested a review from a team as a code owner July 16, 2026 16:28
NicoleMGomes
NicoleMGomes previously approved these changes Jul 16, 2026
NicoleMGomes
NicoleMGomes previously approved these changes Jul 16, 2026
@cassiofariasmachado cassiofariasmachado merged commit eb6648d into main Jul 16, 2026
10 of 11 checks passed
@cassiofariasmachado cassiofariasmachado deleted the feat/enable-multitenancy-support-for-agent-memory-using-bindings branch July 16, 2026 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants