mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
fix(sap): change creds priority
This commit is contained in:
parent
881619aa42
commit
76292bc54e
3 changed files with 88 additions and 14 deletions
|
|
@ -220,6 +220,17 @@ model="sap/gemini-2.5-pro"
|
|||
# Incorrect - missing prefix
|
||||
model="gpt-4o" # ❌ Won't work
|
||||
```
|
||||
3. **Environment variables** - Set the following list of credentials in .env file
|
||||
<pre>
|
||||
AICORE_AUTH_URL = "https://* * * .authentication.sap.hana.ondemand.com/oauth/token",
|
||||
AICORE_CLIENT_ID = " *** ",
|
||||
AICORE_CLIENT_SECRET = " *** ",
|
||||
AICORE_RESOURCE_GROUP = " *** ",
|
||||
AICORE_BASE_URL = "https://api.ai.***.cfapps.sap.hana.ondemand.com/v2"
|
||||
</pre>
|
||||
|
||||
Other credential configuration options are also available. For more information, see the [SAP AI Core Documentation](https://help.sap.com/doc/generative-ai-hub-sdk/CLOUD/en-US/_reference/README_sphynx.html#configuration).
|
||||
## Usage - LiteLLM Python SDK
|
||||
|
||||
### Proxy Usage
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import tempfile
|
|||
|
||||
from litellm import sap_service_key
|
||||
from litellm.llms.custom_httpx.http_handler import _get_httpx_client
|
||||
from litellm._logging import verbose_logger
|
||||
|
||||
AUTH_ENDPOINT_SUFFIX = "/oauth/token"
|
||||
|
||||
|
|
@ -154,6 +155,7 @@ def _resolve_value(
|
|||
env: Dict[str, str],
|
||||
config: Dict[str, Any],
|
||||
service_like: Optional[Dict[str, Any]],
|
||||
vcap_service: Optional[Dict[str, Any]]
|
||||
) -> Optional[str]:
|
||||
# 1) explicit kwargs
|
||||
if cred.name in kwargs and kwargs[cred.name] is not None:
|
||||
|
|
@ -165,22 +167,35 @@ def _resolve_value(
|
|||
val = _get_nested(service_like, cred.vcap_key)
|
||||
if val is not None:
|
||||
return val
|
||||
except KeyError as e:
|
||||
raise KeyError(f"Unable to find {cred.name} in service key") from e
|
||||
except KeyError:
|
||||
verbose_logger.debug(f"Unable to find {cred.name} in service key")
|
||||
return None
|
||||
except json.JSONDecodeError:
|
||||
raise KeyError(f"{service_like} is not valid JSON. Please fix or remove it!")
|
||||
raise KeyError("service key variable is not valid JSON. Please fix or remove it!")
|
||||
|
||||
# 3) environment variables (primary name)
|
||||
env_key = _env_name(cred.name)
|
||||
if env_key in env and env[env_key] is not None:
|
||||
return env[env_key]
|
||||
|
||||
# 4) config file (accept both prefixed and plain keys)
|
||||
# 4) VCAP service
|
||||
if vcap_service and cred.vcap_key:
|
||||
try:
|
||||
val = _get_nested(vcap_service, ("credentials",) + cred.vcap_key)
|
||||
if val is not None:
|
||||
return val
|
||||
except KeyError:
|
||||
verbose_logger.debug(f"Unable to find {cred.name} in vcap service")
|
||||
return None
|
||||
except json.JSONDecodeError:
|
||||
raise KeyError("vcap service variable is not valid JSON. Please fix or remove it!")
|
||||
|
||||
# 5) config file (accept both prefixed and plain keys)
|
||||
for key in (env_key, cred.name):
|
||||
if key in config and config[key] is not None:
|
||||
return config[key]
|
||||
|
||||
# 5) default
|
||||
# 6) default
|
||||
return cred.default
|
||||
|
||||
|
||||
|
|
@ -190,25 +205,23 @@ def fetch_credentials(
|
|||
"""
|
||||
Resolution order per key:
|
||||
kwargs
|
||||
> service key
|
||||
> env (AICORE_<NAME>)
|
||||
> vcap service key
|
||||
> config (AICORE_<NAME> or plain <name>)
|
||||
> service-like source from JSON in $AICORE_SERVICE_KEY (same structure as a VCAP service object)
|
||||
falling back to service entry in $VCAP_SERVICES with label 'aicore'
|
||||
> default
|
||||
"""
|
||||
config = init_conf(profile)
|
||||
env = os.environ # snapshot for testability
|
||||
service_like = None
|
||||
|
||||
|
||||
# Prefer AICORE_SERVICE_KEY if present; otherwise fall back to the VCAP service.
|
||||
service_like = service_key or sap_service_key or _load_json_env(SERVICE_KEY_ENV_VAR) or _get_vcap_service(
|
||||
VCAP_AICORE_SERVICE_NAME
|
||||
)
|
||||
service_like = service_key or sap_service_key or _load_json_env(SERVICE_KEY_ENV_VAR)
|
||||
vcap_service = _get_vcap_service(VCAP_AICORE_SERVICE_NAME)
|
||||
|
||||
out: Dict[str, str] = {}
|
||||
for cred in CREDENTIAL_VALUES:
|
||||
value = _resolve_value(cred, kwargs=kwargs, env=env, config=config, service_like=service_like) # type: ignore
|
||||
value = _resolve_value(cred, kwargs=kwargs, env=env, config=config, service_like=service_like, # type: ignore
|
||||
vcap_service=vcap_service)
|
||||
if value is None:
|
||||
continue
|
||||
if cred.transform_fn:
|
||||
|
|
@ -262,7 +275,9 @@ def get_token_creator(
|
|||
# Sanity check
|
||||
if not auth_url or not client_id:
|
||||
raise ValueError(
|
||||
"fetch_credentials did not return valid 'auth_url' or 'client_id'"
|
||||
"SAP AI Core credentials not found."
|
||||
"Please provide credentials by setting appropriate environment variables "
|
||||
"(e.g. AICORE_CLIENT_ID, AICORE_CLIENT_SECRET, etc.)"
|
||||
)
|
||||
|
||||
modes = [
|
||||
|
|
@ -272,6 +287,7 @@ def get_token_creator(
|
|||
]
|
||||
if sum(bool(m) for m in modes) != 1:
|
||||
raise ValueError(
|
||||
"SAP AI Core credentials are incomplete."
|
||||
"Invalid credentials: provide exactly one of client_secret, "
|
||||
"(cert_str & key_str), or (cert_file_path & key_file_path)."
|
||||
)
|
||||
|
|
|
|||
47
tests/test_litellm/llms/sap/test_sap_fetch_creds.py
Normal file
47
tests/test_litellm/llms/sap/test_sap_fetch_creds.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import json
|
||||
from litellm.llms.sap.credentials import fetch_credentials
|
||||
mock_sap_service_key_dict = {
|
||||
"serviceurls":
|
||||
{
|
||||
"AI_API_URL":"https://testurl.hana.ondemand.com/"
|
||||
},
|
||||
"clientid":"mockclientid",
|
||||
"clientsecret":"mockclientsecret",
|
||||
"url":"https://test.sap.hana.ondemand.com/"
|
||||
}
|
||||
expected_creds = {'client_id': "mockclientid",
|
||||
'client_secret': "mockclientsecret",
|
||||
'auth_url': 'https://test.sap.hana.ondemand.com/oauth/token',
|
||||
'base_url': 'https://testurl.hana.ondemand.com/v2',
|
||||
'resource_group': 'gen-ai-hub-sdk'}
|
||||
|
||||
mock_sap_vcap_service_key_dict = {
|
||||
'aicore': [{
|
||||
'label': 'aicore',
|
||||
'name': 'aicore-instance',
|
||||
'instance_guid': '53ad5b47-a49a-4fec-9f0b-cd921c00b828',
|
||||
'credentials': {
|
||||
'serviceurls': {
|
||||
'AI_API_URL': 'vcap-api-url'
|
||||
},
|
||||
'url': 'vcap-auth-url',
|
||||
'clientid': 'vcap-clientid',
|
||||
'clientsecret': 'vcap-clientsecret'
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
def test_sap_fetch_creds_from_env_service_key(monkeypatch):
|
||||
monkeypatch.setenv("AICORE_SERVICE_KEY", json.dumps(mock_sap_service_key_dict))
|
||||
creds = fetch_credentials()
|
||||
assert creds == expected_creds
|
||||
|
||||
def test_sap_fetch_creds_from_api_key_service_key(monkeypatch):
|
||||
creds = fetch_credentials(service_key=json.dumps(mock_sap_service_key_dict))
|
||||
assert creds == expected_creds
|
||||
|
||||
def test_fetch_creds_from_env_vcap_service(monkeypatch):
|
||||
monkeypatch.setenv("VCAP_SERVICES", json.dumps(mock_sap_vcap_service_key_dict))
|
||||
creds = fetch_credentials()
|
||||
assert creds['client_id'] == "vcap-clientid"
|
||||
assert creds['client_secret'] == "vcap-clientsecret"
|
||||
Loading…
Add table
Reference in a new issue