Skip to content

feat(aicore): transparent proxy routing and BTP Destination Service mode - #271

Open
tiagoek wants to merge 6 commits into
feat/aicore-transparent-tlsfrom
feat/aicore-proxy-routing
Open

feat(aicore): transparent proxy routing and BTP Destination Service mode#271
tiagoek wants to merge 6 commits into
feat/aicore-transparent-tlsfrom
feat/aicore-proxy-routing

Conversation

@tiagoek

@tiagoek tiagoek commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds transparent proxy routing and BTP Destination Service mode to set_aicore_config() — agent code is identical in all environments; the deployer controls routing by choosing which env vars to inject.

Neither proxy mode nor destination mode requires a LiteLLM upstream change. Both work with the current public litellm package.


What changed

set_aicore_config() now detects the routing mode from environment variables (priority: proxy > destination > direct):

Proxy mode — AICORE_PROXY_URL set

Routes all LiteLLM calls through an external LiteLLM proxy:

  • Sets litellm.api_base and litellm.api_key (proxy API key) globally
  • Model strings (e.g. sap/<model>) are passed verbatim — no prefix rewrite. LiteLLM routes through the configured api_base natively
  • No AI Core credentials written to the process environment
  • JWT never reaches the agent process — the proxy holds the OAuth client secret
# Deployer injects these; agent code unchanged
AICORE_PROXY_URL=https://litellm-proxy.cluster.svc
AICORE_PROXY_API_KEY=sk-master-xxx

Destination mode — AICORE_DESTINATION_NAME set

Loads AI Core credentials at startup from a named BTP Destination Service destination:

  • Uses the existing sap_cloud_sdk.destination client — no new dependencies
  • Deployer only needs to inject Destination Service binding credentials into the K8s Secret
  • AICORE_CLIENT_SECRET never needs to be in the K8s Secret — it lives in BTP Destination Service
  • On AuthenticationError, the reactive reload in completion() calls set_aicore_config(), which re-fetches fresh credentials from the Destination Service
# Deployer injects these; agent code unchanged
AICORE_DESTINATION_NAME=aicore-instance
# + Destination Service service binding (clientid/clientsecret/url/uri)

Direct mode (unchanged)

Neither env var set → existing behaviour: load from mounted K8s secret or env vars.


Agent code — identical in all modes

# Same regardless of proxy, destination, or direct mode
set_aicore_config()
response = completion(model="sap/gpt-4o", messages=[{"role": "user", "content": "Hello"}])

Security coverage

Threat Proxy mode Destination mode Direct mode
CLIENT_SECRET in K8s Secret ✅ not needed ✅ not needed ❌ required
CLIENT_SECRET in process env ✅ never ⚠️ present during session ⚠️ present during session
JWT in agent process memory ✅ never (proxy holds it) ❌ agent receives JWT ❌ agent receives JWT
LiteLLM upstream change required ✅ no ✅ no ✅ no
Zero agent code changes

Tests

New unit tests:

  • TestSetAICoreConfigProxyMode (6 tests) — mode detection, litellm globals, precedence, filtering still applied
  • TestSetAICoreConfigDestinationMode (9 tests) — URL extraction, credentials, resource group, error cases

Full suite: 156 passed, 4 skipped (integration tests requiring real BTP).

@tiagoek
tiagoek force-pushed the feat/aicore-clear-client-secret branch from 3e9ef43 to ed315de Compare August 26, 2026 17:37
@tiagoek
tiagoek changed the base branch from feat/aicore-clear-client-secret to feat/aicore-transparent-tls August 26, 2026 20:28
@tiagoek
tiagoek force-pushed the feat/aicore-proxy-routing branch from b2f8869 to b1468fc Compare August 27, 2026 14:40
@tiagoek
tiagoek changed the base branch from feat/aicore-transparent-tls to main August 28, 2026 14:34
@tiagoek tiagoek changed the title feat(aicore): transparent proxy routing and BTP Destination Service mode (Option 3) feat(aicore): transparent proxy routing and BTP Destination Service mode Aug 28, 2026
@tiagoek
tiagoek marked this pull request as ready for review August 28, 2026 15:00
@tiagoek
tiagoek requested a review from a team as a code owner August 28, 2026 15:00
@tiagoek
tiagoek changed the base branch from main to feat/aicore-transparent-tls August 28, 2026 15:09
@tiagoek
tiagoek force-pushed the feat/aicore-proxy-routing branch from 0163bf0 to 6fa7c3c Compare August 28, 2026 17:41
…ce mode

Implements Option 3 from the AFSDK-4306 security alignment meeting: SDK
absorbs all routing complexity so agent code is identical in all environments.
The deployer controls routing by choosing which env vars to inject.

Two new modes in set_aicore_config():

Proxy mode (AICORE_PROXY_URL set):
- Routes all LiteLLM calls through an external LiteLLM proxy
- Sets litellm.api_base / litellm.api_key globally
- Rewrites sap/<model> → litellm_proxy/<model> transparently in
  completion() and acompletion() wrappers (including on auth-error retry)
- No AI Core credentials written to the process environment
- JWT never reaches the agent process (proxy handles OAuth)

Destination mode (AICORE_DESTINATION_NAME set):
- Loads AI Core credentials at startup from a named BTP Destination Service
  destination via the existing sap_cloud_sdk.destination client
- Deployer only injects Destination Service binding — AI Core client_secret
  is never in the K8s Secret, only in BTP Destination Service
- Combined with _clear_client_secret() (PR #257), the secret is removed
  from env after the first successful LiteLLM call

Direct mode (neither set): existing behaviour unchanged, including
transparent TLS (AICORE_TRANSPARENT_TLS).

Adds 30 unit tests covering both new modes and all edge cases.

AFSDK-4306
… mode

Model strings (e.g. sap/<model>) are now passed verbatim to LiteLLM in all
routing modes. LiteLLM natively routes sap/<model> through the configured
litellm.api_base without a prefix rewrite. Removes _rewrite_model_for_proxy(),
_set_proxy_active(), and all associated proxy-aliasing tests (6 unit tests).

Aligns with ADR 0039 which explicitly documents the litellm_proxy/ prefix
approach as a rejected alternative.
The previous name was misleading — the SDK reads the LiteLLM proxy master
API key, not a virtual (per-user/per-team) key. AICORE_PROXY_API_KEY is
accurate for both master key and virtual key usage.

Aligned with Sam Garland (CAD) feedback on ADR 0039 review.
@tiagoek
tiagoek force-pushed the feat/aicore-proxy-routing branch from 6fa7c3c to efd2c18 Compare August 28, 2026 19:55
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.

1 participant