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
176 changes: 176 additions & 0 deletions docs/SPLUNK_AO_ENV_RENAME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Spec: Rename `GALILEO_*` Environment Variables to `SPLUNK_AO_*`

**Jira Tickets:** [HYBIM-713](https://splunk.atlassian.net/browse/HYBIM-713) · [HYBIM-716](https://splunk.atlassian.net/browse/HYBIM-716) · [HYBIM-727](https://splunk.atlassian.net/browse/HYBIM-727)
**Status:** Draft / In Review
**Author:** Aditya Mehra
**Date:** 2026-06-04

---

> **Repository migration notice**
> The changes in this PR are authored against the current `rungalileo/galileo-python` repository.
> Once the Splunk Agent Observability (Splunk AO) project is formally open-sourced, this code will
> be re-homed to **[signalfx/splunk-ao-python](https://github.com/signalfx/splunk-ao-python)** on
> GitHub. A corresponding notice applies to the companion PRs:
> - `sdk-examples` → `signalfx/splunk-ao-python-examples`
> - `e2e-testing` → internal Splunk AO QA repo
>
> These PRs are opened now to facilitate early review. **Do not merge** until the migration is
> complete and the target repositories exist.

---

## 1. Background

The Galileo Python SDK is being rebranded as part of the **Splunk Agent Observability (Splunk AO)**
initiative. Environment variables currently prefixed with `GALILEO_` will be replaced with
`SPLUNK_AO_` to align with Splunk naming conventions and the new product identity.

Tickets in scope for this change:

| Ticket | Title |
|--------|-------|
| HYBIM-713 | Rename `GALILEO_API_KEY` → `SPLUNK_AO_API_KEY` |
| HYBIM-716 | Rename `GALILEO_PROJECT` / `GALILEO_LOG_STREAM` → `SPLUNK_AO_*` |
| HYBIM-727 | Rename remaining `GALILEO_*` env vars to `SPLUNK_AO_*` |

> **Out of scope:** `GALILEO_HEADER_PREFIX` — tracked separately as [HYBIM-729](https://splunk.atlassian.net/browse/HYBIM-729).

---

## 2. Scope of Changes

### 2.1 Environment Variable Mapping

| Old (`GALILEO_*`) | New (`SPLUNK_AO_*`) |
|-------------------|---------------------|
| `GALILEO_API_KEY` | `SPLUNK_AO_API_KEY` |
| `GALILEO_CONSOLE_URL` | `SPLUNK_AO_CONSOLE_URL` |
| `GALILEO_PROJECT` | `SPLUNK_AO_PROJECT` |
| `GALILEO_PROJECT_ID` | `SPLUNK_AO_PROJECT_ID` |
| `GALILEO_LOG_STREAM` | `SPLUNK_AO_LOG_STREAM` |
| `GALILEO_LOG_STREAM_ID` | `SPLUNK_AO_LOG_STREAM_ID` |
| `GALILEO_JWT_TOKEN` | `SPLUNK_AO_JWT_TOKEN` |
| `GALILEO_SSO_ID_TOKEN` | `SPLUNK_AO_SSO_ID_TOKEN` |
| `GALILEO_SSO_PROVIDER` | `SPLUNK_AO_SSO_PROVIDER` |
| `GALILEO_USERNAME` | `SPLUNK_AO_USERNAME` |
| `GALILEO_PASSWORD` | `SPLUNK_AO_PASSWORD` |
| `GALILEO_MODE` | `SPLUNK_AO_MODE` |
| `GALILEO_LOGGING_DISABLED` | `SPLUNK_AO_LOGGING_DISABLED` |
| `GALILEO_INGEST_BETA_DISABLED` | `SPLUNK_AO_INGEST_BETA_DISABLED` |

### 2.2 Compatibility Strategy

This is a **hard cut-over** — only `SPLUNK_AO_*` environment variables are supported after this
change. There is no backward-compatibility shim for external consumers.

**Exception — `galileo-core` bridge:**
The `galileo-core` package (a private dependency) continues to read `GALILEO_*` variables
internally. Until `galileo-core` is updated (tracked separately), the SDK automatically bridges
`SPLUNK_AO_*` → `GALILEO_*` at startup via `GalileoPythonConfig._bridge_env_vars()`. This is a
transparent, temporary compatibility layer that does not expose `GALILEO_*` names to SDK consumers.

```python
# src/galileo/config.py — bridge called inside GalileoPythonConfig.get()
@staticmethod
def _bridge_env_vars() -> None:
"""Copy SPLUNK_AO_* values into GALILEO_* so galileo-core can authenticate.
Only sets GALILEO_* if it is not already present — explicit overrides win.
"""
_BRIDGE = [
("SPLUNK_AO_API_KEY", "GALILEO_API_KEY"),
("SPLUNK_AO_CONSOLE_URL", "GALILEO_CONSOLE_URL"),
("SPLUNK_AO_PROJECT", "GALILEO_PROJECT"),
...
]
for new_key, old_key in _BRIDGE:
if new_key in os.environ and old_key not in os.environ:
os.environ[old_key] = os.environ[new_key]
```

---

## 3. Files Changed

### `galileo-python` (this repo)

| File | Change |
|------|--------|
| `src/galileo/configuration.py` | 14 `ConfigKey.env_var` fields renamed |
| `src/galileo/config.py` | Auth validation + `_bridge_env_vars()` added |
| `src/galileo/utils/env_helpers.py` | `os.getenv()` calls updated |
| `src/galileo/utils/decorators/telemetry_toggle.py` | `GALILEO_LOGGING_DISABLED` renamed |
| `src/galileo/logger/logger.py` | `GALILEO_INGEST_BETA_DISABLED` renamed |
| `src/galileo/{agent_control,decorator,exceptions,experiment,experiments,log_stream,log_streams,metric,middleware/tracing,otel,projects,prompt,shared/exceptions,utils/singleton}.py` | Docstrings / comments / error strings updated |
| `src/galileo/README_API_CLIENT.md` | Docs updated |
| `tests/**` | All `GALILEO_*` references in test files renamed to `SPLUNK_AO_*` |

### `sdk-examples`

| File | Change |
|------|--------|
| `python/agent/langchain-agent/.env.example` | Renamed keys to `SPLUNK_AO_*` |
| `python/agent/langchain-agent/main.py` | `os.environ["GALILEO_*"]` → `os.environ["SPLUNK_AO_*"]` |
| `python/agent/strands-agents/.env.example` | Renamed keys to `SPLUNK_AO_*` |

### `e2e-testing`

| File | Change |
|------|--------|
| `py-sdk-and-ui/.env.example` | New file — `SPLUNK_AO_*` keys, no values |

---

## 4. Testing

### Unit Tests

```bash
cd galileo-python
poetry run pytest # 2015 passed, 5 skipped
```

### E2E Tests

```bash
cd e2e-testing/py-sdk-and-ui
poetry run playwright install --with-deps
poetry run pytest tests/ -k "langchain" --headed
```

Result: all targeted tests passed (one pre-existing 404 on `test_log_session_with_multiple_traces`
is a server-side issue unrelated to this change).

### Examples

```bash
# langchain-agent
cd sdk-examples/python/agent/langchain-agent
source .venv/bin/activate && python main.py
# → "Hello, Erin! 👋" exit 0

# strands-agents
cd sdk-examples/python/agent/strands-agents
source .venv/bin/activate && python agent.py
# → Tool results returned exit 0
```

---

## 5. Decisions & Rationale

| Decision | Choice | Rationale |
|----------|--------|-----------|
| Backward compatibility | Hard cut-over | No dual-support maintenance burden; clean break aligned with rebrand |
| `galileo-core` handling | Bridge only in SDK | `galileo-core` changes tracked separately; bridge is transparent to consumers |
| `GALILEO_HEADER_PREFIX` | **Not renamed** | Separate ticket HYBIM-729 |
| Test files | Renamed to `SPLUNK_AO_*` | Consistency; tests document the new public API |

---

## 6. Open Questions / Follow-ups

- [ ] Update `galileo-core` to read `SPLUNK_AO_*` natively and remove `_bridge_env_vars()`
- [ ] Rename `GALILEO_HEADER_PREFIX` (HYBIM-729)
- [ ] Update CI/CD secrets and deployment configs to use `SPLUNK_AO_*` names
- [ ] Migrate this repo to `signalfx/splunk-ao-python`
2 changes: 1 addition & 1 deletion src/galileo/README_API_CLIENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ First, create a client:
```python
from galileo.api_client import GalileoApiClient

# Make sure you've set the GALILEO_CONSOLE_URL and GALILEO_API_KEY env vars
# Make sure you've set the SPLUNK_AO_CONSOLE_URL and SPLUNK_AO_API_KEY env vars
# Optionally, you can specify both base_url and api_key
client = GalileoApiClient()
```
Expand Down
10 changes: 5 additions & 5 deletions src/galileo/agent_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ def get_agent_control_target(

1. Explicit ``target_id``.
2. Explicit ``log_stream_id`` for ``log_stream`` targets.
3. ``GALILEO_LOG_STREAM_ID`` for ``log_stream`` targets.
3. ``SPLUNK_AO_LOG_STREAM_ID`` for ``log_stream`` targets.
4. An already-initialized ``galileo_context`` logger.

This helper does not resolve log stream names over the network. If only a
log stream name is available, resolve it with the Galileo SDK first and pass
the resulting ID explicitly.
"""
explicit_project_id = _strip_optional_string(project_id)
env_project_id = _strip_optional_string(os.getenv("GALILEO_PROJECT_ID"))
env_project_id = _strip_optional_string(os.getenv("SPLUNK_AO_PROJECT_ID"))
resolved_project_id = explicit_project_id or env_project_id

if target_type == LOG_STREAM_TARGET_TYPE:
Expand All @@ -94,9 +94,9 @@ def get_agent_control_target(
"Provide target_id=<id> explicitly."
)

env_log_stream_id = _strip_optional_string(os.getenv("GALILEO_LOG_STREAM_ID"))
env_log_stream_id = _strip_optional_string(os.getenv("SPLUNK_AO_LOG_STREAM_ID"))
if env_log_stream_id:
_validate_uuid(env_log_stream_id, "GALILEO_LOG_STREAM_ID")
_validate_uuid(env_log_stream_id, "SPLUNK_AO_LOG_STREAM_ID")
return AgentControlTarget(
target_type=LOG_STREAM_TARGET_TYPE, target_id=env_log_stream_id, project_id=resolved_project_id
)
Expand All @@ -112,7 +112,7 @@ def get_agent_control_target(
raise AgentControlTargetUnresolvedError(
"Could not resolve Galileo log stream ID for Agent Control. Provide one of:\n"
" 1. target_id=<uuid> or log_stream_id=<uuid> argument\n"
" 2. GALILEO_LOG_STREAM_ID environment variable\n"
" 2. SPLUNK_AO_LOG_STREAM_ID environment variable\n"
" 3. An initialized galileo_context with a resolved log stream ID"
)

Expand Down
65 changes: 47 additions & 18 deletions src/galileo/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,42 @@ def reset(self) -> None:
@classmethod
def get(cls, **kwargs: Any) -> "GalileoPythonConfig":
if cls._instance is None:
cls._bridge_env_vars()
error_message = cls._check_auth_config(kwargs)
if error_message is not None:
raise ConfigurationError(error_message)
cls._instance = cls._get(cls._instance, **kwargs)
assert cls._instance is not None, "Failed to initialize GalileoPythonConfig"
return cls._instance

@staticmethod
def _bridge_env_vars() -> None:
"""Bridge SPLUNK_AO_* env vars into GALILEO_* for galileo-core compatibility.

galileo-core still reads GALILEO_* env vars. Until galileo-core is updated,
this method propagates any SPLUNK_AO_* values to their GALILEO_* equivalents
so that galileo-core can authenticate successfully.

Only bridges values that are not already set — explicit GALILEO_* overrides win.
"""
_BRIDGE = [
("SPLUNK_AO_API_KEY", "GALILEO_API_KEY"),
("SPLUNK_AO_CONSOLE_URL", "GALILEO_CONSOLE_URL"),
("SPLUNK_AO_PROJECT", "GALILEO_PROJECT"),
("SPLUNK_AO_PROJECT_ID", "GALILEO_PROJECT_ID"),
("SPLUNK_AO_LOG_STREAM", "GALILEO_LOG_STREAM"),
("SPLUNK_AO_LOG_STREAM_ID", "GALILEO_LOG_STREAM_ID"),
("SPLUNK_AO_JWT_TOKEN", "GALILEO_JWT_TOKEN"),
("SPLUNK_AO_SSO_ID_TOKEN", "GALILEO_SSO_ID_TOKEN"),
("SPLUNK_AO_SSO_PROVIDER", "GALILEO_SSO_PROVIDER"),
("SPLUNK_AO_USERNAME", "GALILEO_USERNAME"),
("SPLUNK_AO_PASSWORD", "GALILEO_PASSWORD"),
("SPLUNK_AO_MODE", "GALILEO_MODE"),
]
for new_key, old_key in _BRIDGE:
if new_key in os.environ and old_key not in os.environ:
os.environ[old_key] = os.environ[new_key]

@staticmethod
def _check_auth_config(kwargs: dict) -> str | None:
"""Validate that a complete auth method is configured.
Expand All @@ -40,8 +69,8 @@ def _check_auth_config(kwargs: dict) -> str | None:
message identifying what's missing.

Auth methods supported by the underlying config model:
- API key (standalone): api_key kwarg or GALILEO_API_KEY env
- Pre-exchanged Galileo JWT (standalone): jwt_token or GALILEO_JWT_TOKEN
- API key (standalone): api_key kwarg or SPLUNK_AO_API_KEY env
- Pre-exchanged JWT (standalone): jwt_token or SPLUNK_AO_JWT_TOKEN
- SSO (paired): sso_id_token + sso_provider, both kwargs and env vars
- Username/password (paired): username + password, both kwargs and env vars

Expand All @@ -56,51 +85,51 @@ def _val(kwarg_name: str, env_name: str) -> str | None:
return os.environ.get(env_name)

# Standalone methods — either alone is sufficient.
if _val("api_key", "GALILEO_API_KEY"):
if _val("api_key", "SPLUNK_AO_API_KEY"):
return None
if _val("jwt_token", "GALILEO_JWT_TOKEN"):
if _val("jwt_token", "SPLUNK_AO_JWT_TOKEN"):
return None

# SSO requires BOTH id_token and provider.
sso_id_token = _val("sso_id_token", "GALILEO_SSO_ID_TOKEN")
sso_provider = _val("sso_provider", "GALILEO_SSO_PROVIDER")
sso_id_token = _val("sso_id_token", "SPLUNK_AO_SSO_ID_TOKEN")
sso_provider = _val("sso_provider", "SPLUNK_AO_SSO_PROVIDER")
if sso_id_token and sso_provider:
return None
if sso_id_token and not sso_provider:
return (
"GALILEO_SSO_ID_TOKEN is set but GALILEO_SSO_PROVIDER is missing. "
"SSO authentication requires both. Set GALILEO_SSO_PROVIDER to your "
"SPLUNK_AO_SSO_ID_TOKEN is set but SPLUNK_AO_SSO_PROVIDER is missing. "
"SSO authentication requires both. Set SPLUNK_AO_SSO_PROVIDER to your "
"IdP identifier (e.g. 'okta', 'custom') or pass sso_provider=... "
"as a keyword argument."
)
if sso_provider and not sso_id_token:
return (
"GALILEO_SSO_PROVIDER is set but GALILEO_SSO_ID_TOKEN is missing. "
"SSO authentication requires both. Set GALILEO_SSO_ID_TOKEN to your "
"SPLUNK_AO_SSO_PROVIDER is set but SPLUNK_AO_SSO_ID_TOKEN is missing. "
"SSO authentication requires both. Set SPLUNK_AO_SSO_ID_TOKEN to your "
"IdP-issued ID token or pass sso_id_token=... as a keyword argument."
)

# Username/password requires BOTH.
username = _val("username", "GALILEO_USERNAME")
password = _val("password", "GALILEO_PASSWORD")
username = _val("username", "SPLUNK_AO_USERNAME")
password = _val("password", "SPLUNK_AO_PASSWORD")
if username and password:
return None
if username and not password:
return (
"GALILEO_USERNAME is set but GALILEO_PASSWORD is missing. "
"SPLUNK_AO_USERNAME is set but SPLUNK_AO_PASSWORD is missing. "
"Username/password authentication requires both."
)
if password and not username:
return (
"GALILEO_PASSWORD is set but GALILEO_USERNAME is missing. "
"SPLUNK_AO_PASSWORD is set but SPLUNK_AO_USERNAME is missing. "
"Username/password authentication requires both."
)

# Nothing configured anywhere.
return (
"No Galileo authentication detected. Set one of: "
"GALILEO_API_KEY; GALILEO_SSO_ID_TOKEN with GALILEO_SSO_PROVIDER; "
"or GALILEO_USERNAME with GALILEO_PASSWORD. "
"No Splunk AO authentication detected. Set one of: "
"SPLUNK_AO_API_KEY; SPLUNK_AO_SSO_ID_TOKEN with SPLUNK_AO_SSO_PROVIDER; "
"or SPLUNK_AO_USERNAME with SPLUNK_AO_PASSWORD. "
"Alternatively, pass the equivalent kwargs to GalileoPythonConfig.get(). "
"See https://docs.galileo.ai for setup instructions."
"See https://docs.splunk.com for setup instructions."
)
Loading
Loading