From 47b917d75c8c4e11b5999a688963d8870821aa44 Mon Sep 17 00:00:00 2001 From: shuningc Date: Thu, 4 Jun 2026 16:49:32 -0700 Subject: [PATCH 1/3] Renaming SDK Identifiers from galileo to splunk_ao --- AGENTS.md | 466 ++++++++++++++++++ README.md | 42 +- galileo-a2a/README.md | 8 +- galileo-a2a/examples/two_agent_demo.py | 4 +- galileo-a2a/src/galileo_a2a/instrumentor.py | 4 +- galileo-adk/README.md | 6 +- galileo-adk/src/galileo_adk/__init__.py | 4 +- .../src/galileo_adk/data_converters.py | 4 +- galileo-adk/src/galileo_adk/decorator.py | 10 +- galileo-adk/src/galileo_adk/observer.py | 18 +- galileo-adk/tests/conftest.py | 2 +- galileo-adk/tests/test_callback.py | 2 +- galileo-adk/tests/test_data_converters.py | 22 +- galileo-adk/tests/test_retriever.py | 34 +- src/galileo/__init__.py | 4 +- src/galileo/agent_control.py | 10 +- src/galileo/constants/__init__.py | 4 +- src/galileo/constants/tracing.py | 8 +- src/galileo/decorator.py | 22 +- src/galileo/experiments.py | 18 +- src/galileo/handlers/base_handler.py | 4 +- src/galileo/handlers/openai_agents/handler.py | 6 +- src/galileo/log_stream.py | 6 +- src/galileo/log_streams.py | 4 +- src/galileo/openai/__init__.py | 36 +- src/galileo/openai/extractors.py | 10 +- src/galileo/openai/response_generator.py | 16 +- src/galileo/otel.py | 6 +- src/galileo/tracing.py | 4 +- src/galileo/utils/decorators/__init__.py | 4 +- .../utils/decorators/telemetry_toggle.py | 6 +- src/galileo/utils/headers_data.py | 2 +- tests/test_agent_control.py | 10 +- tests/test_decorator.py | 326 ++++++------ tests/test_decorator_distributed.py | 60 +-- tests/test_experiments.py | 8 +- tests/test_galileo_context.py | 18 +- tests/test_langchain.py | 4 +- tests/test_langchain_middleware.py | 4 +- tests/test_log_stream.py | 12 +- tests/test_openai.py | 54 +- tests/test_otel.py | 58 +-- tests/utils/test_telemetry_toggle.py | 10 +- 43 files changed, 913 insertions(+), 447 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..ae1bb8e5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,466 @@ +## Project Overview + +Galileo Python SDK (`galileo` on PyPI) - the official Python client library for the Galileo AI platform. It enables logging and tracing of LLM calls, experiments, datasets, prompt management, and more. + +**Key characteristics:** +- Public SDK published to PyPI (external contributors welcome) +- Depends on `galileo-core` for shared schemas and infrastructure (see Known Issues) +- Uses auto-generated API client from OpenAPI specification +- Supports multiple LLM frameworks: OpenAI, LangChain, CrewAI, OpenAI Agents SDK + +## Build & Development Commands + +```bash +# Install dependencies (requires poetry) +poetry install --all-extras --no-root + +# Full setup (install + pre-commit hooks) +inv setup + +# Run all tests (parallel by default) +poetry run pytest + +# Run single test file +poetry run pytest tests/test_decorator.py + +# Run single test +poetry run pytest tests/test_decorator.py::test_function_name -v + +# Run tests with coverage +inv test + +# Type checking +inv type-check + +# Linting (via pre-commit) +poetry run ruff check --fix src/ +poetry run ruff format src/ +``` + +## Architecture + +### Package Structure + +``` +src/galileo/ +├── __future__/ # New object-centric API (WIP) +│ ├── project.py # Project domain object +│ ├── dataset.py # Dataset domain object +│ ├── experiment.py # Experiment domain object +│ ├── prompt.py # Prompt domain object +│ ├── log_stream.py # LogStream domain object +│ ├── configuration.py # Configuration management +│ └── shared/ # Shared utilities (filters, sorting, base classes) +├── logger/ # Core logging functionality +│ └── logger.py # GalileoLogger - central trace/span management +├── handlers/ # Framework-specific integrations +│ ├── langchain/ # LangChain callback handler (GalileoCallback) +│ ├── crewai/ # CrewAI event listener +│ └── openai_agents/ # OpenAI Agents SDK integration +├── openai/ # Drop-in OpenAI client wrapper (auto-logging) +├── resources/ # Auto-generated API client (DO NOT EDIT) +├── schema/ # Pydantic models for SDK-specific types +├── utils/ # Utility functions and helpers +├── datasets.py # Dataset service (current API) +├── experiments.py # Experiment service (current API) +├── prompts.py # Prompt service (current API) +├── projects.py # Project service (current API) +├── log_streams.py # LogStream service (current API) +├── decorator.py # @log decorator and splunk_ao_context +└── config.py # GalileoPythonConfig configuration +``` + +### Core Components + +**GalileoLogger** (`src/galileo/logger/logger.py`): Central class for uploading traces to Galileo. Supports batch and streaming modes. Manages traces, spans (LLM, retriever, tool, workflow, agent), and sessions. + +**Decorators** (`src/galileo/decorator.py`): The `@log` decorator and `splunk_ao_context` context manager for automatic function tracing. Uses ContextVars for thread-safe nested span tracking. + +**Handlers** (`src/galileo/handlers/`): Framework-specific integrations: +- `langchain/` - LangChain callback handler (`GalileoCallback`) +- `crewai/` - CrewAI handler (uses lazy imports to avoid side effects) +- `openai_agents/` - OpenAI Agents SDK integration + +**OpenAI Wrapper** (`src/galileo/openai/`): Drop-in replacement for OpenAI client that auto-logs calls. + +**`__future__` Package** (`src/galileo/__future__/`): New object-centric API implementing the "Golden Flow" patterns. Provides intuitive, Pythonic interfaces for domain objects (Project, Dataset, Prompt, Experiment, LogStream). Released incrementally as stable. + +### Auto-Generated Code + +**Resources** (`src/galileo/resources/`): Auto-generated API client from OpenAPI spec. **Excluded from linting/type-checking.** Never edit manually. + +```bash +# Regenerate API client +./scripts/import-openapi-yaml.sh https://api.galileo.ai/client +./scripts/auto-generate-api-client.sh +``` + +**Important:** The OpenAPI spec comes from the **Client API** (`/client`), not the main API (`/docs`). The Client API is a curated subset designed specifically for SDK consumption. + +### Dependency on galileo-core + +The SDK depends on `galileo-core` for shared schemas, helpers, and base classes: +- `galileo_core.schemas.logging.*` - Span types (LlmSpan, ToolSpan, etc.), Trace, Session +- `galileo_core.helpers.*` - API key management, execution utilities +- `galileo_core.schemas.protect.*` - Protection/guardrails schemas + +**Note:** There is ongoing work to reduce/eliminate this dependency. See Known Issues section. + +## Key Patterns + +### Object-Centric Design (`__future__` package) + +Domain objects follow consistent patterns: + +```python +from galileo.__future__ import Project, Dataset + +# Factory methods (class-level) +project = Project.get(name="my-project") # Retrieve existing +projects = Project.list() # List all + +# Instance creation with lifecycle +project = Project(name="new-project") # LOCAL_ONLY state +project.create() # → SYNCED state + +# Fluent creation +project = Project(name="new-project").create() # 2-in-1 + +# Relationship methods +log_streams = project.list_log_streams() +dataset = project.create_dataset(name="test-data", content=[...]) + +# Child → Parent navigation +dataset.project # Returns parent Project object +``` + +### State Management + +Objects have explicit sync states: `LOCAL_ONLY`, `SYNCED`, `DIRTY`, `FAILED_SYNC`, `DELETED` + +```python +project = Project(name="test") # LOCAL_ONLY +project.create() # → SYNCED +project.name = "renamed" # → DIRTY +project.save() # → SYNCED +project.delete() # → DELETED +``` + +### Service Layer (Current API) + +Services provide functional interfaces for those who prefer procedural style: + +```python +from galileo.datasets import create_dataset, get_dataset, list_datasets +from galileo.experiments import run_experiment + +dataset = create_dataset(name="test", content=[...]) +results = run_experiment( + experiment_name="eval-1", + dataset=dataset, + prompt_template=get_prompt(name="my-prompt"), + metrics=["correctness"], + project="my-project" +) +``` + +### Logging with Decorators + +```python +from galileo import log, splunk_ao_context + +# Auto-trace function calls +@log +def my_workflow(): + call_llm() + call_llm() + +# Explicit span types +@log(span_type="retriever") +def retrieve_docs(query: str): + return ["doc1", "doc2"] + +# Context manager for explicit control +with splunk_ao_context(project="my-project", log_stream="prod"): + my_workflow() +``` + +### Handler Integrations + +```python +# LangChain +from galileo.handlers.langchain import GalileoCallback +callback = GalileoCallback() +llm = ChatOpenAI(callbacks=[callback]) + +# CrewAI +from galileo.handlers.crewai import CrewAIEventListener +listener = CrewAIEventListener(project="my-project") +# Listener auto-registers; use auto_setup_listeners=False in tests + +# OpenAI (drop-in wrapper) +from galileo.openai import openai +client = openai.OpenAI() # Auto-logs all calls +``` + +## Testing + +Tests use pytest with these key fixtures from `tests/conftest.py`: +- `mock_request` - HTTP request mocking (from `galileo_core[testing]`) +- `mock_healthcheck`, `mock_login_api_key`, `mock_get_current_user` - Common API mocks + +### Test Environment + +Environment variables are set in `conftest.py` for pytest-xdist compatibility: +```python +GALILEO_CONSOLE_URL=http://localtest:8088 +GALILEO_API_KEY=api-1234567890 +GALILEO_PROJECT=test-project +GALILEO_LOG_STREAM=test-log-stream +``` + +Tests run with `--disable-socket` to prevent real network calls. + +### Testing Guidelines + +```python +def test_example(mock_request, mock_healthcheck, mock_login_api_key): + # Mock API responses + mock_request.post("/datasets").respond(json={"id": "123", "name": "test"}) + + # Test SDK functionality + dataset = create_dataset(name="test", content=[...]) + assert dataset.id == "123" +``` + +### Handler Testing (CrewAI) + +CrewAI imports have global side effects. Use lazy imports and `auto_setup_listeners=False`: + +```python +def test_crewai_handler(): + # Import inside test, not at module level + from galileo.handlers.crewai import CrewAIEventListener + + listener = CrewAIEventListener( + project="test", + auto_setup_listeners=False # Prevents import side effects + ) +``` + +### Given/When/Then Testing Style + +Use behavioral testing comments to structure tests clearly. Add inline comments before each section: + +- `# Given: ` - Before setup/arrangement code. Describe the preconditions. +- `# When: ` - Before the action being tested. Describe what action is performed. +- `# Then: ` - Before assertions. Describe the expected outcome. + +**Important rules:** + +- Comments must include a human-readable description after the colon - never leave them empty +- Use sentence case for descriptions (e.g., "a user with admin permissions", not "A User With Admin Permissions") +- Keep descriptions concise but meaningful +- For tests where the action raises an exception, use `# When/Then: ` combined + +```python +def test_create_project_success(mock_request, mock_healthcheck, mock_login_api_key): + # Given: a valid project name and mocked API response + mock_request.post("/projects").respond(json={"id": "123", "name": "test"}) + + # When: creating a new project + project = Project(name="test").create() + + # Then: the project is created with the expected ID + assert project.id == "123" + assert project.name == "test" +``` + +## Code Style & Conventions + +- **Line length:** 120 characters +- **Linting:** ruff (replaces flake8, isort, etc.) +- **Type annotations:** Required for public functions (mypy) +- **Docstrings:** numpy convention +- **Pre-commit hooks:** Run ruff and mypy on commit + +### Required Practices + +- Use standard Python logging: `import logging; logger = logging.getLogger(__name__)` +- Duration variables must be suffixed with units: `timeout_seconds`, `delay_ms` +- Commit messages: `type(scope): description` (conventional commits) +- **Imports at top of file**: Always place imports at the module level, not inside functions +- Exception: Lazy imports for optional dependencies (e.g., crewai) - document why +- Use `from __future__ import annotations` for forward references + +### Error Handling Architecture + +The SDK distinguishes between two types of operations with different error handling needs: + +**Resource Management Operations** (raise exceptions): +- Operations where users explicitly request an action and expect feedback +- Examples: `create_project()`, `get_dataset()`, `delete_log_stream()`, `list_projects()` +- These operations should raise exceptions on failure for clear user feedback + +**Telemetry/Ingestion Operations** (resilient): +- Background operations that observe user code without interfering +- Examples: `ingest_traces()`, `ingest_spans()`, `flush()` +- These operations swallow infrastructure errors gracefully +- Principle: Observability code should observe, not interfere + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ User Application │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌─────────────────────┴─────────────────────┐ + │ │ + ▼ ▼ +┌───────────────────┐ ┌───────────────────────┐ +│ Resource Mgmt │ │ Telemetry/Ingestion │ +│ (Raises on Error) │ │ (Resilient) │ +├───────────────────┤ ├───────────────────────┤ +│ Projects │ │ Traces.ingest_*() │ +│ Datasets │ │ Traces.update_*() │ +│ LogStreams │ │ Logger streaming │ +│ Stages │ │ @warn_catch_exception │ +└───────────────────┘ └───────────────────────┘ + │ │ + ▼ ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Generated API Client │ +│ (Always raises HTTP exceptions) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**HTTP-Specific Exceptions:** + +| Status Code | Exception | Meaning | +|-------------|-----------|---------| +| 400 | `BadRequestError` | Invalid request parameters | +| 401 | `AuthenticationError` | Invalid or expired API key | +| 403 | `ForbiddenError` | Insufficient permissions | +| 404 | `NotFoundError` | Resource doesn't exist | +| 409 | `ConflictError` | Resource already exists | +| 422 | `HTTPValidationError` | Request body/params failed Pydantic validation | +| 429 | `RateLimitError` | Too many requests | +| 5xx | `ServerError` | Server-side error | + +**Infrastructure Exceptions** (caught only in telemetry operations): +```python +INFRASTRUCTURE_EXCEPTIONS = ( + httpx.HTTPError, + httpx.TimeoutException, + httpx.ConnectError, + ConnectionError, + TimeoutError, + OSError, +) +``` + +User errors like `TypeError`, `ValueError`, and `ValidationError` are never caught - they propagate immediately. + +### Logging Convention + +```python +import logging +logger = logging.getLogger(__name__) + +# Log lifecycle events with context +logger.info("Project.create: name=%s – started", name) +logger.info("Project.create: id=%s – completed", project_id) +logger.error("Project.update: id=%s – failed: %s", project_id, error) + +# Never log sensitive data (tokens, API keys, PII) +``` + +**When to Add Logging:** +- Service methods that perform writes (create, update, delete) +- Error conditions with full context when catching exceptions +- Long-running operations (start/completion with duration) + +**What NOT to Log:** +- Sensitive data: Passwords, API keys, tokens, PII +- Large payloads: Don't log entire request/response bodies +- High-frequency loops: Use sampling or aggregate metrics + +## Known Issues and Architectural Decisions + +### 1. galileo-core Dependency + +The SDK has a deep dependency on `galileo-core` (private repository). This creates: +- Contributor friction (requires private repo access) +- Contract split between core and SDK +- Inheritance of internal complexity + +**Mitigation in progress:** Gradual migration to OpenAPI-generated types and SDK-owned abstractions. + +### 2. Configuration State Management + +Configuration exists in three places: +- `Configuration` class attributes (new `__future__` API) +- `os.environ` (synced by Configuration) +- `GalileoPythonConfig._instance` (actual authenticated state from core) + +**Known issue:** `connect()` must be called explicitly; lazy initialization is incomplete. + +### 3. Prompt Version Management + +`Prompt.create_version()` creates a NEW prompt (name with timestamp suffix), not a new version of the same template. True version management requires API alignment. + +### 4. Dataset Version Indexing + +API uses **1-based** version indexing, not 0-based: +```python +# Correct: first version is index 1 +version_content = dataset.get_version_content(index=1) + +# Wrong: index 0 doesn't exist +version_content = dataset.get_version_content(index=0) # Raises ValueError +``` + +### 5. Experiment-Playground Conflation + +The SDK's `Experiment` class conflates two distinct API concepts: +- **Playground**: Interactive workspace for prompt iteration +- **Experiment**: Immutable logged run with recorded results + +Version specification for datasets and prompts is implicit (uses "current" version). + +### 6. Metadata Type Handling + +SDK converts all metadata values to strings. API behavior varies: +- Trace API: Skips `None` values and non-primitives +- Dataset API: Keeps `None` as null, JSON-encodes nested dicts + +## Release Process + +Releases use python-semantic-release with conventional commits: + +```bash +# Patch release triggers +fix:, perf:, chore:, docs:, style:, refactor: + +# Version is managed in: +# - src/galileo/__init__.py:__version__ +# - pyproject.toml:project.version +``` + +## Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `GALILEO_CONSOLE_URL` | Yes* | Galileo console URL (default: app.galileo.ai) | +| `GALILEO_API_KEY` | Yes | API key for authentication | +| `GALILEO_PROJECT` | No | Default project name | +| `GALILEO_LOG_STREAM` | No | Default log stream name | +| `GALILEO_LOGGING_DISABLED` | No | Disable trace collection | + +*Required for non-production environments + +## References + +- **PyPI:** https://pypi.org/project/galileo/ +- **GitHub:** https://github.com/rungalileo/galileo-python +- **API Docs:** https://docs.galileo.ai +- **OpenAPI Spec:** `openapi.yaml` (generated from Client API) diff --git a/README.md b/README.md index f9ce03b9..fe1b11be 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,11 @@ Note: if you would like to point to an environment other than `app.galileo.ai`, ```python import os -from galileo import galileo_context +from galileo import splunk_ao_context from galileo.openai import openai # If you've set your GALILEO_PROJECT and GALILEO_LOG_STREAM env vars, you can skip this step -galileo_context.init(project="your-project-name", log_stream="your-log-stream-name") +splunk_ao_context.init(project="your-project-name", log_stream="your-log-stream-name") # Initialize the Galileo wrapped OpenAI client client = openai.OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) @@ -61,7 +61,7 @@ def call_openai(): call_openai() # This will upload the trace to Galileo -galileo_context.flush() +splunk_ao_context.flush() ``` You can also use the `@log` decorator to log spans. Here's how to create a workflow span with two nested LLM spans: @@ -75,7 +75,7 @@ def make_nested_call(): call_openai() # If you've set your GALILEO_PROJECT and GALILEO_LOG_STREAM env vars, you can skip this step -galileo_context.init(project="your-project-name", log_stream="your-log-stream-name") +splunk_ao_context.init(project="your-project-name", log_stream="your-log-stream-name") # This will create a trace with a workflow span and two nested LLM spans containing the OpenAI calls make_nested_call() @@ -107,27 +107,27 @@ def tool_call(input: str = "tool call input"): tool_call(input="question") # This will upload the trace to Galileo -galileo_context.flush() +splunk_ao_context.flush() ``` -In some cases, you may want to wrap a block of code to start and flush a trace automatically. You can do this using the `galileo_context` context manager: +In some cases, you may want to wrap a block of code to start and flush a trace automatically. You can do this using the `splunk_ao_context` context manager: ```python -from galileo import galileo_context +from galileo import splunk_ao_context # This will log a block of code to the project and log stream specified in the context manager -with galileo_context(): +with splunk_ao_context(): content = make_nested_call() print(content) ``` -`galileo_context` also allows you specify a separate project and log stream for the trace: +`splunk_ao_context` also allows you specify a separate project and log stream for the trace: ```python -from galileo import galileo_context +from galileo import splunk_ao_context # This will log to the project and log stream specified in the context manager -with galileo_context(project="gen-ai-project", log_stream="test2"): +with splunk_ao_context(project="gen-ai-project", log_stream="test2"): content = make_nested_call() print(content) ``` @@ -162,9 +162,9 @@ current Galileo log stream as the runtime target: ```python import agent_control -from galileo import galileo_context, get_agent_control_target +from galileo import splunk_ao_context, get_agent_control_target -galileo_context.init(project="my-project", log_stream="prod") +splunk_ao_context.init(project="my-project", log_stream="prod") target = get_agent_control_target() @@ -178,7 +178,7 @@ agent_control.init( ``` The helper resolves an explicit log stream ID, `GALILEO_LOG_STREAM_ID`, or an -already-initialized `galileo_context` logger. It does not import the Agent +already-initialized `splunk_ao_context` logger. It does not import the Agent Control SDK or resolve log stream names over the network. If you use a direct Agent Control client instead of `agent_control.init(...)`, pass `target.target_type` and `target.target_id` on each evaluation call. @@ -210,10 +210,10 @@ In some cases (like long-running processes), it may be necessary to explicitly f ```python import os -from galileo import galileo_context +from galileo import splunk_ao_context from galileo.openai import openai -galileo_context.init(project="your-project-name", log_stream="your-log-stream-name") +splunk_ao_context.init(project="your-project-name", log_stream="your-log-stream-name") # Initialize the Galileo wrapped OpenAI client client = openai.OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) @@ -230,7 +230,7 @@ def call_openai(): call_openai() # This will upload the trace to Galileo -galileo_context.flush() +splunk_ao_context.flush() ``` Using the Langchain callback handler: @@ -404,16 +404,16 @@ logger.conclude() logger.flush() ``` -All of this can also be done using the `galileo_context` context manager: +All of this can also be done using the `splunk_ao_context` context manager: ```python -from galileo import galileo_context +from galileo import splunk_ao_context -session_id = galileo_context.start_session(name="my-session-name") +session_id = splunk_ao_context.start_session(name="my-session-name") # OR -galileo_context.set_session(session_id=session_id) +splunk_ao_context.set_session(session_id=session_id) ``` diff --git a/galileo-a2a/README.md b/galileo-a2a/README.md index 70f257e4..665e5c57 100644 --- a/galileo-a2a/README.md +++ b/galileo-a2a/README.md @@ -44,12 +44,12 @@ pip install galileo-a2a ## Quick Start ```python -from galileo.otel import GalileoSpanProcessor, add_galileo_span_processor +from galileo.otel import GalileoSpanProcessor, add_splunk_ao_span_processor from galileo_a2a import A2AInstrumentor from opentelemetry.sdk.trace import TracerProvider provider = TracerProvider() -add_galileo_span_processor(provider, GalileoSpanProcessor()) +add_splunk_ao_span_processor(provider, GalileoSpanProcessor()) A2AInstrumentor().instrument(tracer_provider=provider, agent_name="orchestrator") ``` @@ -119,7 +119,7 @@ from a2a.types import ( AgentCapabilities, AgentCard, AgentSkill, Message, Role, TaskState, TaskStatus, TaskStatusUpdateEvent, TextPart, ) -from galileo.otel import GalileoSpanProcessor, add_galileo_span_processor +from galileo.otel import GalileoSpanProcessor, add_splunk_ao_span_processor from galileo_a2a import A2AInstrumentor from langchain.agents import create_agent from langchain_core.tools import tool @@ -132,7 +132,7 @@ from typing_extensions import TypedDict # ---- Only 4 lines needed for full distributed tracing ---- provider = TracerProvider() -add_galileo_span_processor(provider, GalileoSpanProcessor()) +add_splunk_ao_span_processor(provider, GalileoSpanProcessor()) A2AInstrumentor().instrument(tracer_provider=provider, agent_name="orchestrator") LangchainInstrumentor().instrument(tracer_provider=provider) diff --git a/galileo-a2a/examples/two_agent_demo.py b/galileo-a2a/examples/two_agent_demo.py index 7b593a6d..91c79e1e 100644 --- a/galileo-a2a/examples/two_agent_demo.py +++ b/galileo-a2a/examples/two_agent_demo.py @@ -51,7 +51,7 @@ from starlette.applications import Starlette from typing_extensions import TypedDict -from galileo.otel import GalileoSpanProcessor, add_galileo_span_processor +from galileo.otel import GalileoSpanProcessor, add_splunk_ao_span_processor from galileo_a2a import A2AInstrumentor load_dotenv(Path(__file__).parent / ".env") @@ -61,7 +61,7 @@ # --------------------------------------------------------------------------- provider = TracerProvider() -add_galileo_span_processor(provider, GalileoSpanProcessor()) +add_splunk_ao_span_processor(provider, GalileoSpanProcessor()) A2AInstrumentor().instrument(tracer_provider=provider, agent_name="orchestrator") LangchainInstrumentor().instrument(tracer_provider=provider) diff --git a/galileo-a2a/src/galileo_a2a/instrumentor.py b/galileo-a2a/src/galileo_a2a/instrumentor.py index 15ae471d..9e643fd9 100644 --- a/galileo-a2a/src/galileo_a2a/instrumentor.py +++ b/galileo-a2a/src/galileo_a2a/instrumentor.py @@ -32,11 +32,11 @@ class A2AInstrumentor(BaseInstrumentor): # type: ignore[misc] Example:: from opentelemetry.sdk.trace import TracerProvider - from galileo.otel import GalileoSpanProcessor, add_galileo_span_processor + from galileo.otel import GalileoSpanProcessor, add_splunk_ao_span_processor from galileo_a2a import A2AInstrumentor provider = TracerProvider() - add_galileo_span_processor(provider, GalileoSpanProcessor()) + add_splunk_ao_span_processor(provider, GalileoSpanProcessor()) A2AInstrumentor().instrument(tracer_provider=provider, agent_name="my-agent") # To disable message content capture (e.g. for PII compliance): diff --git a/galileo-adk/README.md b/galileo-adk/README.md index ea23bf14..08763820 100644 --- a/galileo-adk/README.md +++ b/galileo-adk/README.md @@ -164,13 +164,13 @@ if __name__ == "__main__": ### Retriever Spans -By default, all `FunctionTool` calls are logged as tool spans. To log a retriever function as a **retriever span** (enabling RAG quality metrics in Galileo), decorate it with `@galileo_retriever`: +By default, all `FunctionTool` calls are logged as tool spans. To log a retriever function as a **retriever span** (enabling RAG quality metrics in Galileo), decorate it with `@splunk_ao_retriever`: ```python -from galileo_adk import galileo_retriever +from galileo_adk import splunk_ao_retriever from google.adk.tools import FunctionTool -@galileo_retriever +@splunk_ao_retriever def search_docs(query: str) -> str: """Search the knowledge base.""" results = my_vector_db.search(query) diff --git a/galileo-adk/src/galileo_adk/__init__.py b/galileo-adk/src/galileo_adk/__init__.py index 8f709707..209c3e6d 100644 --- a/galileo-adk/src/galileo_adk/__init__.py +++ b/galileo-adk/src/galileo_adk/__init__.py @@ -1,13 +1,13 @@ __version__ = "2.0.1" from galileo_adk.callback import GalileoADKCallback -from galileo_adk.decorator import galileo_retriever +from galileo_adk.decorator import splunk_ao_retriever from galileo_adk.observer import get_custom_metadata from galileo_adk.plugin import GalileoADKPlugin __all__ = [ "GalileoADKPlugin", "GalileoADKCallback", - "galileo_retriever", + "splunk_ao_retriever", "get_custom_metadata", ] diff --git a/galileo-adk/src/galileo_adk/data_converters.py b/galileo-adk/src/galileo_adk/data_converters.py index 832e04a5..beb8b196 100644 --- a/galileo-adk/src/galileo_adk/data_converters.py +++ b/galileo-adk/src/galileo_adk/data_converters.py @@ -18,7 +18,7 @@ def generate_tool_call_id(name: str, index: int = 0) -> str: return f"call_{name}_{index}_{uuid.uuid4().hex[:8]}" -def convert_adk_content_to_galileo_messages(content: Any) -> list[Message]: +def convert_adk_content_to_splunk_ao_messages(content: Any) -> list[Message]: """Convert ADK Content to list of Galileo Messages, preserving part order. Tracks call IDs for function calls and links them to their responses. @@ -158,7 +158,7 @@ def _map_adk_role_to_galileo(adk_role: str) -> MessageRole: return _ADK_ROLE_TO_GALILEO.get(adk_role.lower(), MessageRole.user) -def convert_adk_tools_to_galileo_format(tools: Any) -> list[dict[str, Any]]: +def convert_adk_tools_to_splunk_ao_format(tools: Any) -> list[dict[str, Any]]: """Convert ADK tools to OpenAI-compatible format.""" galileo_tools: list[dict[str, Any]] = [] for tool in tools: diff --git a/galileo-adk/src/galileo_adk/decorator.py b/galileo-adk/src/galileo_adk/decorator.py index 32a6c228..bcc0d206 100644 --- a/galileo-adk/src/galileo_adk/decorator.py +++ b/galileo-adk/src/galileo_adk/decorator.py @@ -6,20 +6,20 @@ from typing import Any -def galileo_retriever(func: Callable[..., Any]) -> Callable[..., Any]: +def splunk_ao_retriever(func: Callable[..., Any]) -> Callable[..., Any]: """Mark a function as a retriever for Galileo observability. - When a function decorated with @galileo_retriever is wrapped in a + When a function decorated with @splunk_ao_retriever is wrapped in a FunctionTool, Galileo will log it as a retriever span (node_type="retriever") instead of a tool span, enabling RAG quality metrics. Example ------- - >>> from galileo_adk import galileo_retriever - >>> @galileo_retriever + >>> from galileo_adk import splunk_ao_retriever + >>> @splunk_ao_retriever ... def my_search(query: str) -> str: ... return search_docs(query) >>> tool = FunctionTool(my_search) """ - func._galileo_is_retriever = True # type: ignore[attr-defined] + func._splunk_ao_is_retriever = True # type: ignore[attr-defined] return func diff --git a/galileo-adk/src/galileo_adk/observer.py b/galileo-adk/src/galileo_adk/observer.py index 2d0f82b6..93f15619 100644 --- a/galileo-adk/src/galileo_adk/observer.py +++ b/galileo-adk/src/galileo_adk/observer.py @@ -10,13 +10,13 @@ from typing import Any from uuid import UUID -from galileo import galileo_context +from galileo import splunk_ao_context from galileo.handlers.base_handler import GalileoBaseHandler from galileo.schema.trace import TracesIngestRequest from galileo.utils.serialization import serialize_to_str from galileo_adk.data_converters import ( - convert_adk_content_to_galileo_messages, - convert_adk_tools_to_galileo_format, + convert_adk_content_to_splunk_ao_messages, + convert_adk_tools_to_splunk_ao_format, extract_text_from_adk_content, ) from galileo_adk.span_manager import SpanManager @@ -155,7 +155,7 @@ def __init__( ) else: self._trace_builder = None - galileo_logger = galileo_context.get_logger_instance(project=project, log_stream=log_stream) + galileo_logger = splunk_ao_context.get_logger_instance(project=project, log_stream=log_stream) self._handler = GalileoBaseHandler( galileo_logger=galileo_logger, start_new_trace=True, @@ -392,12 +392,12 @@ def _is_retriever_tool(self, tool: Any) -> bool: Detection uses two strategies: 1. isinstance check against google.adk.tools.retrieval.BaseRetrievalTool - 2. Custom functions decorated with @galileo_retriever (sets _galileo_is_retriever on func) + 2. Custom functions decorated with @splunk_ao_retriever (sets _splunk_ao_is_retriever on func) """ if _BaseRetrievalTool is not None and isinstance(tool, _BaseRetrievalTool): return True func = getattr(tool, "func", None) - return bool(func is not None and getattr(func, "_galileo_is_retriever", False)) + return bool(func is not None and getattr(func, "_splunk_ao_is_retriever", False)) def on_tool_start( self, @@ -468,13 +468,13 @@ def _extract_llm_input(self, llm_request: Any) -> list[Any]: return [] messages = [] for content in llm_request.contents: - messages.extend(convert_adk_content_to_galileo_messages(content)) + messages.extend(convert_adk_content_to_splunk_ao_messages(content)) return messages def _extract_llm_output(self, llm_response: Any) -> list[Any]: """Extract LLM output as list of Messages to preserve all parts including tool_calls.""" if llm_response and hasattr(llm_response, "content"): - return convert_adk_content_to_galileo_messages(llm_response.content) + return convert_adk_content_to_splunk_ao_messages(llm_response.content) return [] def _extract_model_name(self, llm_request: Any) -> str | None: @@ -494,7 +494,7 @@ def _extract_tools(self, llm_request: Any) -> list[dict[str, Any]] | None: if hasattr(llm_request, "config") and llm_request.config: tools = getattr(llm_request.config, "tools", None) if tools: - return convert_adk_tools_to_galileo_format(tools) + return convert_adk_tools_to_splunk_ao_format(tools) return None def _extract_usage_metadata(self, llm_response: Any) -> dict[str, Any]: diff --git a/galileo-adk/tests/conftest.py b/galileo-adk/tests/conftest.py index 843976a5..c6745dbe 100644 --- a/galileo-adk/tests/conftest.py +++ b/galileo-adk/tests/conftest.py @@ -51,7 +51,7 @@ def mock_decode_jwt() -> Generator[MagicMock, None, None]: @pytest.fixture def mock_projects(mock_request: Callable) -> Generator[None, None, None]: - """Mock the projects endpoint used by galileo_context.get_logger_instance(). + """Mock the projects endpoint used by splunk_ao_context.get_logger_instance(). ProjectDB schema requires: - id, created_by, created_by_user (UserInfo), runs, created_at, updated_at diff --git a/galileo-adk/tests/test_callback.py b/galileo-adk/tests/test_callback.py index f346a818..49775a4e 100644 --- a/galileo-adk/tests/test_callback.py +++ b/galileo-adk/tests/test_callback.py @@ -123,7 +123,7 @@ def test_initialization_with_ingestion_hook(self) -> None: assert callback._handler is not None def test_initialization_with_project_and_log_stream(self) -> None: - with patch("galileo_adk.observer.galileo_context") as mock_context: + with patch("galileo_adk.observer.splunk_ao_context") as mock_context: mock_logger = MagicMock() mock_context.get_logger_instance.return_value = mock_logger diff --git a/galileo-adk/tests/test_data_converters.py b/galileo-adk/tests/test_data_converters.py index 2ab97971..a6c4c2dc 100644 --- a/galileo-adk/tests/test_data_converters.py +++ b/galileo-adk/tests/test_data_converters.py @@ -9,7 +9,7 @@ _try_direct_attributes, _try_function_declarations, _try_to_dict, - convert_adk_content_to_galileo_messages, + convert_adk_content_to_splunk_ao_messages, ) @@ -28,7 +28,7 @@ def __init__(self, parts: list[MockPart], role: str = "user") -> None: class TestConvertADKContent: def test_text_part(self) -> None: content = MockContent([MockPart(text="hello")]) - messages = convert_adk_content_to_galileo_messages(content) + messages = convert_adk_content_to_splunk_ao_messages(content) assert len(messages) == 1 assert messages[0].content == "hello" assert messages[0].role == MessageRole.user @@ -36,7 +36,7 @@ def test_text_part(self) -> None: def test_inline_data_part(self) -> None: inline = MockPart(mime_type="image/png", data=b"\x89PNG") content = MockContent([MockPart(inline_data=inline)]) - messages = convert_adk_content_to_galileo_messages(content) + messages = convert_adk_content_to_splunk_ao_messages(content) assert len(messages) == 1 payload = json.loads(messages[0].content) assert payload["type"] == "inline_data" @@ -46,7 +46,7 @@ def test_inline_data_part(self) -> None: def test_file_data_part(self) -> None: file_d = MockPart(file_uri="gs://bucket/file.pdf", mime_type="application/pdf") content = MockContent([MockPart(file_data=file_d)]) - messages = convert_adk_content_to_galileo_messages(content) + messages = convert_adk_content_to_splunk_ao_messages(content) assert len(messages) == 1 payload = json.loads(messages[0].content) assert payload["type"] == "file_data" @@ -55,7 +55,7 @@ def test_file_data_part(self) -> None: def test_function_call_part(self) -> None: func_call = MockPart(name="search", args={"query": "weather"}) content = MockContent([MockPart(function_call=func_call)], role="model") - messages = convert_adk_content_to_galileo_messages(content) + messages = convert_adk_content_to_splunk_ao_messages(content) assert len(messages) == 1 assert messages[0].role == MessageRole.assistant assert messages[0].content == "" @@ -67,7 +67,7 @@ def test_function_call_part(self) -> None: def test_function_response_part(self) -> None: func_resp = MockPart(name="search", response={"result": "sunny"}) content = MockContent([MockPart(function_response=func_resp)]) - messages = convert_adk_content_to_galileo_messages(content) + messages = convert_adk_content_to_splunk_ao_messages(content) assert len(messages) == 1 assert messages[0].role == MessageRole.tool payload = json.loads(messages[0].content) @@ -81,7 +81,7 @@ def test_mixed_parts_preserve_order(self) -> None: MockPart(text="Step 2"), ] content = MockContent(parts, role="model") - messages = convert_adk_content_to_galileo_messages(content) + messages = convert_adk_content_to_splunk_ao_messages(content) assert len(messages) == 4 assert messages[0].content == "Step 1" assert messages[1].role == MessageRole.assistant @@ -92,11 +92,11 @@ def test_mixed_parts_preserve_order(self) -> None: def test_empty_content(self) -> None: content = MockContent([]) - assert convert_adk_content_to_galileo_messages(content) == [] + assert convert_adk_content_to_splunk_ao_messages(content) == [] def test_unknown_part_skipped(self) -> None: content = MockContent([MockPart(unknown_field="value")]) - assert convert_adk_content_to_galileo_messages(content) == [] + assert convert_adk_content_to_splunk_ao_messages(content) == [] def test_multiple_function_calls_same_name_with_unique_ids(self) -> None: """Multiple function calls with same name but unique IDs should link correctly.""" @@ -107,7 +107,7 @@ def test_multiple_function_calls_same_name_with_unique_ids(self) -> None: MockPart(function_response=MockPart(id="call_2", name="search", response="result_2")), ] content = MockContent(parts, role="model") - messages = convert_adk_content_to_galileo_messages(content) + messages = convert_adk_content_to_splunk_ao_messages(content) assert len(messages) == 4 # First call and its response should share the same call_id @@ -126,7 +126,7 @@ def test_function_call_response_without_id_no_linking(self) -> None: MockPart(function_response=MockPart(name="calc", response=42)), ] content = MockContent(parts, role="model") - messages = convert_adk_content_to_galileo_messages(content) + messages = convert_adk_content_to_splunk_ao_messages(content) assert len(messages) == 2 # Without ADK ids, responses can't link to calls (different part indices) diff --git a/galileo-adk/tests/test_retriever.py b/galileo-adk/tests/test_retriever.py index 9beddf92..d428ffa3 100644 --- a/galileo-adk/tests/test_retriever.py +++ b/galileo-adk/tests/test_retriever.py @@ -7,7 +7,7 @@ import pytest -from galileo_adk.decorator import galileo_retriever +from galileo_adk.decorator import splunk_ao_retriever from galileo_adk.observer import GalileoObserver from .mocks import MockTool, MockToolContext @@ -42,26 +42,26 @@ def observer() -> GalileoObserver: class TestGalileoRetrieverDecorator: - """Tests for the @galileo_retriever decorator.""" + """Tests for the @splunk_ao_retriever decorator.""" def test_decorator_sets_attribute(self) -> None: # Given: a plain function def my_search(query: str) -> str: return "results" - # When: decorating with @galileo_retriever - decorated = galileo_retriever(my_search) + # When: decorating with @splunk_ao_retriever + decorated = splunk_ao_retriever(my_search) - # Then: the function has _galileo_is_retriever = True - assert getattr(decorated, "_galileo_is_retriever", False) is True + # Then: the function has _splunk_ao_is_retriever = True + assert getattr(decorated, "_splunk_ao_is_retriever", False) is True def test_decorator_preserves_function(self) -> None: # Given: a function with specific behavior def my_search(query: str) -> str: return f"results for {query}" - # When: decorating with @galileo_retriever - decorated = galileo_retriever(my_search) + # When: decorating with @splunk_ao_retriever + decorated = splunk_ao_retriever(my_search) # Then: the function still works as expected assert decorated("test") == "results for test" @@ -69,12 +69,12 @@ def my_search(query: str) -> str: def test_decorator_syntax(self) -> None: # Given/When: using decorator syntax - @galileo_retriever + @splunk_ao_retriever def my_search(query: str) -> str: return "results" # Then: the function is marked as a retriever - assert getattr(my_search, "_galileo_is_retriever", False) is True + assert getattr(my_search, "_splunk_ao_is_retriever", False) is True class TestIsRetrieverTool: @@ -113,8 +113,8 @@ def test_base_retrieval_tool_subclass_is_detected(self, observer: GalileoObserve assert result is True def test_decorated_function_tool_is_detected(self, observer: GalileoObserver) -> None: - # Given: a function decorated with @galileo_retriever wrapped in FunctionTool - @galileo_retriever + # Given: a function decorated with @splunk_ao_retriever wrapped in FunctionTool + @splunk_ao_retriever def my_search(query: str) -> str: return "results" @@ -127,7 +127,7 @@ def my_search(query: str) -> str: assert result is True def test_undecorated_function_tool_is_not_retriever(self, observer: GalileoObserver) -> None: - # Given: a function NOT decorated with @galileo_retriever wrapped in FunctionTool + # Given: a function NOT decorated with @splunk_ao_retriever wrapped in FunctionTool def my_calculator(expression: str) -> str: return "42" @@ -165,8 +165,8 @@ class TestOnToolStartRetriever: """Tests for on_tool_start retriever span creation.""" def test_decorated_retriever_creates_retriever_span(self, observer: GalileoObserver) -> None: - # Given: a function decorated with @galileo_retriever wrapped in FunctionTool - @galileo_retriever + # Given: a function decorated with @splunk_ao_retriever wrapped in FunctionTool + @splunk_ao_retriever def my_search(query: str) -> str: return "results" @@ -190,7 +190,7 @@ def my_search(query: str) -> str: def test_retriever_tool_extracts_query_from_tool_args(self, observer: GalileoObserver) -> None: # Given: a retriever tool with a "query" key in tool_args - @galileo_retriever + @splunk_ao_retriever def my_search(query: str) -> str: return "results" @@ -212,7 +212,7 @@ def my_search(query: str) -> str: def test_retriever_tool_falls_back_when_no_query_key(self, observer: GalileoObserver) -> None: # Given: a retriever tool without a "query" key in tool_args - @galileo_retriever + @splunk_ao_retriever def my_search(search_text: str) -> str: return "results" diff --git a/src/galileo/__init__.py b/src/galileo/__init__.py index cef66d91..e5498b85 100644 --- a/src/galileo/__init__.py +++ b/src/galileo/__init__.py @@ -4,7 +4,7 @@ from galileo.collaborator import Collaborator, CollaboratorRole from galileo.configuration import Configuration from galileo.dataset import Dataset -from galileo.decorator import GalileoDecorator, galileo_context, log, start_session +from galileo.decorator import GalileoDecorator, splunk_ao_context, log, start_session from galileo.exceptions import ( AuthenticationError, BadRequestError, @@ -149,7 +149,6 @@ "create_protect_stage", "delete_api_key", "enable_console_logging", - "galileo_context", "get_agent_control_target", "get_protect_stage", "get_tracing_headers", @@ -160,6 +159,7 @@ "pause_protect_stage", "resume_protect_stage", "setup_agent_control_bridge", + "splunk_ao_context", "start_session", "update_protect_stage", ] diff --git a/src/galileo/agent_control.py b/src/galileo/agent_control.py index 4d5a73a7..3004ce93 100644 --- a/src/galileo/agent_control.py +++ b/src/galileo/agent_control.py @@ -13,7 +13,7 @@ from dataclasses import dataclass from uuid import UUID -from galileo.decorator import galileo_context +from galileo.decorator import splunk_ao_context from galileo.utils.env_helpers import _get_log_stream_or_default, _get_project_or_default from galileo.utils.singleton import GalileoLoggerSingleton @@ -61,7 +61,7 @@ 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. - 4. An already-initialized ``galileo_context`` logger. + 4. An already-initialized ``splunk_ao_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 @@ -113,7 +113,7 @@ def get_agent_control_target( "Could not resolve Galileo log stream ID for Agent Control. Provide one of:\n" " 1. target_id= or log_stream_id= argument\n" " 2. GALILEO_LOG_STREAM_ID environment variable\n" - " 3. An initialized galileo_context with a resolved log stream ID" + " 3. An initialized splunk_ao_context with a resolved log stream ID" ) @@ -131,8 +131,8 @@ def _strip_optional_string(value: str | None) -> str | None: def _resolve_log_stream_from_cached_context() -> AgentControlTarget | None: - current_project = _get_project_or_default(galileo_context.get_current_project()) - current_log_stream = _get_log_stream_or_default(galileo_context.get_current_log_stream()) + current_project = _get_project_or_default(splunk_ao_context.get_current_project()) + current_log_stream = _get_log_stream_or_default(splunk_ao_context.get_current_log_stream()) current_thread_name = threading.current_thread().name # Read cached logger state directly so this helper never creates or resolves diff --git a/src/galileo/constants/__init__.py b/src/galileo/constants/__init__.py index 0d0bacaf..04d1c393 100644 --- a/src/galileo/constants/__init__.py +++ b/src/galileo/constants/__init__.py @@ -8,7 +8,7 @@ DEFAULT_CONSOLE_URL = "https://app.galileo.ai/" # HTTP header prefix for all Galileo headers -GALILEO_HEADER_PREFIX = "X-Galileo" +SPLUNK_AO_HEADER_PREFIX = "X-Galileo" # Type definitions LoggerModeType = Literal["batch", "distributed"] @@ -19,6 +19,6 @@ "DEFAULT_LOG_STREAM_NAME", "DEFAULT_MODE", "DEFAULT_PROJECT_NAME", - "GALILEO_HEADER_PREFIX", + "SPLUNK_AO_HEADER_PREFIX", "LoggerModeType", ) diff --git a/src/galileo/constants/tracing.py b/src/galileo/constants/tracing.py index 29f971f2..f28a9512 100644 --- a/src/galileo/constants/tracing.py +++ b/src/galileo/constants/tracing.py @@ -1,8 +1,8 @@ """Constants for distributed tracing.""" -from galileo.constants import GALILEO_HEADER_PREFIX +from galileo.constants import SPLUNK_AO_HEADER_PREFIX # HTTP header names for propagating distributed tracing context -# These headers follow the pattern of namespaced custom headers (X-Galileo-*) -TRACE_ID_HEADER = f"{GALILEO_HEADER_PREFIX}-Trace-ID" -PARENT_ID_HEADER = f"{GALILEO_HEADER_PREFIX}-Parent-ID" +# These headers follow the pattern of namespaced custom headers (Splunk-AO-*) +TRACE_ID_HEADER = f"{SPLUNK_AO_HEADER_PREFIX}-Trace-ID" +PARENT_ID_HEADER = f"{SPLUNK_AO_HEADER_PREFIX}-Parent-ID" diff --git a/src/galileo/decorator.py b/src/galileo/decorator.py index a0a91c81..60cddd80 100644 --- a/src/galileo/decorator.py +++ b/src/galileo/decorator.py @@ -29,16 +29,16 @@ def call_llm(prompt, temperature=0.7): 3. Using context manager for grouping related operations: ```python - from galileo import galileo_context + from galileo import splunk_ao_context - with galileo_context(project="my-project", log_stream="production"): + with splunk_ao_context(project="my-project", log_stream="production"): result1 = my_function() result2 = another_function() ``` Setup requirements: - Galileo API key must be set (via environment variable GALILEO_API_KEY or programmatically) -- Project and Log Stream names should be defined if using the `log` decorator (either via environment variables GALILEO_PROJECT and GALILEO_LOG_STREAM, or via `galileo_context.init()`) +- Project and Log Stream names should be defined if using the `log` decorator (either via environment variables GALILEO_PROJECT and GALILEO_LOG_STREAM, or via `splunk_ao_context.init()`) For more examples and detailed usage, see the Galileo SDK documentation. """ @@ -194,7 +194,7 @@ def __call__( This allows usage like: ```python - with galileo_context(project="my_project", log_stream="my_stream"): + with splunk_ao_context(project="my_project", log_stream="my_stream"): # Code to be traced ``` @@ -1285,13 +1285,13 @@ def set_session(self, session_id: str) -> None: self.get_logger_instance().set_session(session_id) -galileo_context = GalileoDecorator() -log = galileo_context.log -start_session = galileo_context.start_session +splunk_ao_context = GalileoDecorator() +log = splunk_ao_context.log +start_session = splunk_ao_context.start_session @contextmanager -def galileo_dataset_context( +def splunk_ao_dataset_context( *, dataset_input: str | None = None, dataset_output: str | None = None, @@ -1315,10 +1315,10 @@ def galileo_dataset_context( Examples -------- - >>> from galileo.decorator import galileo_dataset_context + >>> from galileo.decorator import splunk_ao_dataset_context >>> >>> # Set ground truth for a single agent call - >>> with galileo_dataset_context( + >>> with splunk_ao_dataset_context( ... dataset_input="What is the capital of France?", ... dataset_output="Paris", ... dataset_metadata={"source": "geography_quiz"} @@ -1327,7 +1327,7 @@ def galileo_dataset_context( >>> >>> # Use with experiment datasets >>> for row in dataset: - ... with galileo_dataset_context( + ... with splunk_ao_dataset_context( ... dataset_input=row["input"], ... dataset_output=row["expected_output"], ... ): diff --git a/src/galileo/experiments.py b/src/galileo/experiments.py index c2573d2e..56510090 100644 --- a/src/galileo/experiments.py +++ b/src/galileo/experiments.py @@ -10,7 +10,7 @@ from galileo.config import GalileoPythonConfig from galileo.datasets import Dataset, convert_dataset_row_to_record -from galileo.decorator import galileo_context, galileo_dataset_context, log +from galileo.decorator import splunk_ao_context, splunk_ao_dataset_context, log from galileo.experiment_tags import upsert_experiment_tag from galileo.projects import Project, Projects from galileo.prompts import PromptTemplate @@ -234,7 +234,7 @@ def run_with_function( if dataset_obj is None and records is None: raise ValueError("Either dataset_obj or records must be provided") results = [] - galileo_context.init(project=project_obj.name, experiment_id=experiment_obj.id, local_metrics=local_metrics) + splunk_ao_context.init(project=project_obj.name, experiment_id=experiment_obj.id, local_metrics=local_metrics) def logged_process_func(row: DatasetRecord) -> Callable: return log(name=experiment_obj.name, dataset_record=row)(func) @@ -244,10 +244,10 @@ def logged_process_func(row: DatasetRecord) -> Callable: _logger.info(f"Processing {len(records)} rows from dataset") for row in records: results.append(process_row(row, logged_process_func(row))) - galileo_context.reset_trace_context() + splunk_ao_context.reset_trace_context() if getsizeof(results) > MAX_REQUEST_SIZE_BYTES or len(results) >= MAX_INGEST_BATCH_SIZE: _logger.info("Flushing logger due to size limit") - galileo_context.flush(on_error=on_error) + splunk_ao_context.flush(on_error=on_error) results = [] # For dataset object, paginate through content elif dataset_obj is not None: @@ -267,16 +267,16 @@ def logged_process_func(row: DatasetRecord) -> Callable: for row in batch_records: results.append(process_row(row, logged_process_func(row))) - galileo_context.reset_trace_context() + splunk_ao_context.reset_trace_context() if getsizeof(results) > MAX_REQUEST_SIZE_BYTES or len(results) >= MAX_INGEST_BATCH_SIZE: _logger.info("Flushing logger due to size limit") - galileo_context.flush(on_error=on_error) + splunk_ao_context.flush(on_error=on_error) results = [] starting_token += len(batch_records) # flush the logger - galileo_context.flush(on_error=on_error) + splunk_ao_context.flush(on_error=on_error) _logger.info(f" {len(results)} rows processed for experiment {experiment_obj.name}.") @@ -292,9 +292,9 @@ def process_row(row: DatasetRecord, process_func: Callable) -> str: try: # Set dataset context for OTEL spans (ground truth for scorers) # This ensures OTEL-instrumented frameworks get dataset fields attached to their spans - with galileo_dataset_context(dataset_input=row.input, dataset_output=row.output, dataset_metadata=row.metadata): + with splunk_ao_dataset_context(dataset_input=row.input, dataset_output=row.output, dataset_metadata=row.metadata): output = process_func(row.deserialized_input) - log = galileo_context.get_logger_instance() + log = splunk_ao_context.get_logger_instance() log.conclude(output) except Exception as exc: output = f"error during executing: {process_func.__name__}: {exc}" diff --git a/src/galileo/handlers/base_handler.py b/src/galileo/handlers/base_handler.py index bab4c97d..05fa5a15 100644 --- a/src/galileo/handlers/base_handler.py +++ b/src/galileo/handlers/base_handler.py @@ -5,7 +5,7 @@ from typing import Any from uuid import UUID -from galileo import galileo_context +from galileo import splunk_ao_context from galileo.logger import GalileoLogger from galileo.schema.handlers import INTEGRATION, NODE_TYPE, Node from galileo.schema.trace import TracesIngestRequest @@ -44,7 +44,7 @@ def __init__( flush_on_chain_end: bool = True, ingestion_hook: Callable[[TracesIngestRequest], None] | None = None, ): - self._galileo_logger: GalileoLogger = galileo_logger or galileo_context.get_logger_instance( + self._galileo_logger: GalileoLogger = galileo_logger or splunk_ao_context.get_logger_instance( ingestion_hook=ingestion_hook ) if galileo_logger and ingestion_hook: diff --git a/src/galileo/handlers/openai_agents/handler.py b/src/galileo/handlers/openai_agents/handler.py index 8cc13ce9..fc05d77f 100644 --- a/src/galileo/handlers/openai_agents/handler.py +++ b/src/galileo/handlers/openai_agents/handler.py @@ -6,7 +6,7 @@ from agents import Span, Trace, TracingProcessor from agents.tracing import ResponseSpanData, get_current_span, get_trace_provider -from galileo import GalileoLogger, galileo_context +from galileo import GalileoLogger, splunk_ao_context from galileo.schema.handlers import Node from galileo.utils import _get_timestamp from galileo.utils.openai_agents import ( @@ -52,7 +52,7 @@ def __init__(self, galileo_logger: GalileoLogger | None = None, flush_on_trace_e flush_on_trace_end : bool Whether to automatically flush the log batch to Galileo when a trace ends. """ - self._galileo_logger: GalileoLogger = galileo_logger or galileo_context.get_logger_instance() + self._galileo_logger: GalileoLogger = galileo_logger or splunk_ao_context.get_logger_instance() self._flush_on_trace_end: bool = flush_on_trace_end self._nodes: dict[str, Node] = {} self._last_output: Any = None @@ -492,7 +492,7 @@ def _extract_tool_output(self, item_dict: dict[str, Any], item_type: str) -> str return serialize_to_str(item_dict.get("output") or item_dict.get("results")) @staticmethod - def add_galileo_custom_span(span: GalileoSpan) -> Span[GalileoCustomSpan]: + def add_splunk_ao_custom_span(span: GalileoSpan) -> Span[GalileoCustomSpan]: """Add a Galileo custom span to the trace.""" trace_provider = get_trace_provider() current_span = get_current_span() diff --git a/src/galileo/log_stream.py b/src/galileo/log_stream.py index 48cc58fe..7914ba53 100644 --- a/src/galileo/log_stream.py +++ b/src/galileo/log_stream.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any from galileo.config import GalileoPythonConfig -from galileo.decorator import galileo_context +from galileo.decorator import splunk_ao_context from galileo.export import ExportClient from galileo.log_streams import LogStreams from galileo.resources.api.trace import ( @@ -820,7 +820,7 @@ def context(self) -> Any: """ Get a galileo context manager for this log stream. - This is a convenient method that returns a pre-configured galileo_context + This is a convenient method that returns a pre-configured splunk_ao_context for this log stream, eliminating the need to specify project and log stream names. Returns @@ -838,7 +838,7 @@ def context(self) -> Any: # Your logging code here response = openai_client.chat.completions.create(...) """ - return galileo_context(project=self.project.name if self.project else None, log_stream=self.name) + return splunk_ao_context(project=self.project.name if self.project else None, log_stream=self.name) def _get_columns(self, api_func: Any, error_msg: str) -> LogRecordsAvailableColumnsResponse: """Helper method to retrieve available columns from the API.""" diff --git a/src/galileo/log_streams.py b/src/galileo/log_streams.py index 70a9b2cf..ebad53aa 100644 --- a/src/galileo/log_streams.py +++ b/src/galileo/log_streams.py @@ -68,9 +68,9 @@ class LogStream(LogStreamResponse): # Use a log stream with the context manager from galileo.openai import openai - from galileo import galileo_context + from galileo import splunk_ao_context - with galileo_context(project="My AI Project", log_stream="Production Logs"): + with splunk_ao_context(project="My AI Project", log_stream="Production Logs"): response = openai.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello, world!"}] diff --git a/src/galileo/openai/__init__.py b/src/galileo/openai/__init__.py index 0f94ea4e..d94ba0d7 100644 --- a/src/galileo/openai/__init__.py +++ b/src/galileo/openai/__init__.py @@ -25,10 +25,10 @@ # All prompts and responses are automatically logged to Galileo print(response.choices[0].message.content) -# You can also use it with the galileo_context for more control -from galileo import galileo_context +# You can also use it with the splunk_ao_context for more control +from galileo import splunk_ao_context -with galileo_context(project="my-project", log_stream="my-log-stream"): +with splunk_ao_context(project="my-project", log_stream="my-log-stream"): response = openai.chat.completions.create( model="gpt-4o", messages=[ @@ -46,11 +46,11 @@ import httpx from wrapt import wrap_function_wrapper # type: ignore[import-untyped] -from galileo.decorator import galileo_context +from galileo.decorator import splunk_ao_context from galileo.logger import GalileoLogger from galileo.openai.extractors import ( OpenAiArgsExtractor, - convert_to_galileo_message, + convert_to_splunk_ao_message, extract_data_from_default_response, extract_input_data_from_kwargs, has_pending_function_calls, @@ -114,24 +114,24 @@ def _safe_initialize_logger(initialize: Callable[[], GalileoLogger | None]) -> G ] -def _galileo_wrapper(func: Callable) -> Callable: - def _with_galileo(open_ai_definitions: OpenAiModuleDefinition, initialize: Callable) -> Callable: +def _splunk_ao_wrapper(func: Callable) -> Callable: + def _with_splunk_ao(open_ai_definitions: OpenAiModuleDefinition, initialize: Callable) -> Callable: def wrapper(wrapped: Callable, instance: Any, args: dict, kwargs: dict) -> Any: return func(open_ai_definitions, initialize, wrapped, args, kwargs) return wrapper - return _with_galileo + return _with_splunk_ao -@_galileo_wrapper +@_splunk_ao_wrapper def _wrap( open_ai_resource: OpenAiModuleDefinition, initialize: Callable, wrapped: Callable, args: dict, kwargs: dict ) -> Any: start_time = _get_timestamp() arg_extractor = OpenAiArgsExtractor(*args, **kwargs) - input_data = extract_input_data_from_kwargs(open_ai_resource, start_time, arg_extractor.get_galileo_args()) + input_data = extract_input_data_from_kwargs(open_ai_resource, start_time, arg_extractor.get_splunk_ao_args()) galileo_logger = _safe_initialize_logger(initialize) if galileo_logger is None: @@ -145,9 +145,9 @@ def _wrap( # We will conclude it at the end # convert to list of galileo messages since we can't send list of messages to span and want consistency if isinstance(input_data.input, list): - trace_input_messages = [convert_to_galileo_message(msg) for msg in input_data.input] + trace_input_messages = [convert_to_splunk_ao_message(msg) for msg in input_data.input] else: - trace_input_messages = [convert_to_galileo_message(input_data.input)] + trace_input_messages = [convert_to_splunk_ao_message(input_data.input)] # Serialize with "messages" wrapper for UI compatibility trace_input = {"messages": [msg.model_dump(exclude_none=True) for msg in trace_input_messages]} @@ -187,9 +187,9 @@ def _wrap( # convert to list of galileo messages since we can't send a regular list to span input if isinstance(input_data.input, list): - span_input = [convert_to_galileo_message(msg) for msg in input_data.input] + span_input = [convert_to_splunk_ao_message(msg) for msg in input_data.input] else: - span_input = [convert_to_galileo_message(input_data.input)] + span_input = [convert_to_splunk_ao_message(input_data.input)] # Process Responses API output items sequentially if present final_conversation_context = span_input.copy() @@ -226,7 +226,7 @@ def _wrap( ) else: # For non-Responses API (chat or completion), create the main span as before - span_output = convert_to_galileo_message(completion, "assistant") + span_output = convert_to_splunk_ao_message(completion, "assistant") # Add a span to the current trace or span (if this is a nested trace) span = galileo_logger.add_llm_span( @@ -262,9 +262,9 @@ def _wrap( # For other APIs, add the final span output full_conversation = [] if isinstance(input_data.input, list): - full_conversation.extend([convert_to_galileo_message(msg) for msg in input_data.input]) + full_conversation.extend([convert_to_splunk_ao_message(msg) for msg in input_data.input]) else: - full_conversation.append(convert_to_galileo_message(input_data.input)) + full_conversation.append(convert_to_splunk_ao_message(input_data.input)) full_conversation.append(span_output) # Serialize with "messages" wrapper for UI compatibility @@ -312,7 +312,7 @@ def initialize(self) -> GalileoLogger | None: Optional[GalileoLogger] The initialized Galileo logger instance. """ - self._galileo_logger = galileo_context.get_logger_instance() + self._galileo_logger = splunk_ao_context.get_logger_instance() return self._galileo_logger diff --git a/src/galileo/openai/extractors.py b/src/galileo/openai/extractors.py index 8e6f6011..96597961 100644 --- a/src/galileo/openai/extractors.py +++ b/src/galileo/openai/extractors.py @@ -199,7 +199,7 @@ def __init__(self, name: str | None = None, metadata: dict | None = None, **kwar } self.kwargs = kwargs - def get_galileo_args(self) -> dict[str, Any]: + def get_splunk_ao_args(self) -> dict[str, Any]: return {**self.args, **self.kwargs} def get_openai_args(self) -> dict[str, Any]: @@ -215,7 +215,7 @@ def get_openai_args(self) -> dict[str, Any]: return self.kwargs -def convert_to_galileo_message(data: Any, default_role: str = "user") -> Message: +def convert_to_splunk_ao_message(data: Any, default_role: str = "user") -> Message: """Convert OpenAI response data to a Galileo Message object.""" if hasattr(data, "type") and data.type == "function_call": tool_call = ToolCall( @@ -585,7 +585,7 @@ def process_output_items( # Add the final response message if final_message_content: - response_message = convert_to_galileo_message(final_message_content, "assistant") + response_message = convert_to_splunk_ao_message(final_message_content, "assistant") consolidated_output_messages.append(response_message) # Create the final consolidated output for the LLM span with content and reasoning @@ -594,7 +594,7 @@ def process_output_items( # Otherwise, serialize the array of Messages and reasoning objects into a string if final_message_content: # Simple case: just a message with no reasoning - consolidated_output = convert_to_galileo_message(final_message_content, "assistant") + consolidated_output = convert_to_splunk_ao_message(final_message_content, "assistant") else: # Complex case: serialize the array of Messages and reasoning objects # WORKAROUND: Serialize into a string since LLM span output @@ -613,7 +613,7 @@ def process_output_items( messages_serialized = json.dumps(serialized_items, indent=2) # Create a single Message with the serialized array as content - consolidated_output = convert_to_galileo_message(messages_serialized, "assistant") + consolidated_output = convert_to_splunk_ao_message(messages_serialized, "assistant") # Add tool calls if present if final_tool_calls: diff --git a/src/galileo/openai/response_generator.py b/src/galileo/openai/response_generator.py index 69bd2b45..1c1f9ace 100644 --- a/src/galileo/openai/response_generator.py +++ b/src/galileo/openai/response_generator.py @@ -4,7 +4,7 @@ from galileo import GalileoLogger from galileo.openai.extractors import ( - convert_to_galileo_message, + convert_to_splunk_ao_message, extract_streamed_openai_response, has_pending_function_calls, process_function_call_outputs, @@ -110,9 +110,9 @@ def _finalize(self) -> None: ) if isinstance(self.input_data.input, list): - span_input = [convert_to_galileo_message(msg) for msg in self.input_data.input] + span_input = [convert_to_splunk_ao_message(msg) for msg in self.input_data.input] else: - span_input = [convert_to_galileo_message(self.input_data.input)] + span_input = [convert_to_splunk_ao_message(self.input_data.input)] # probably can create a shared function for handling both streaming and non-streaming # Process Responses API output items sequentially if present (same as non-streaming) @@ -142,7 +142,7 @@ def _finalize(self) -> None: ) else: # Fallback: create basic span if no output items - span_output = convert_to_galileo_message(completion, "assistant") + span_output = convert_to_splunk_ao_message(completion, "assistant") span = self.logger.add_llm_span( input=span_input, output=span_output, @@ -161,7 +161,7 @@ def _finalize(self) -> None: span.metrics.num_cached_input_tokens = usage.get("cached_tokens", 0) if usage else 0 else: # For non-Responses API (chat or completion), create the main span as before - span_output = convert_to_galileo_message(completion, "assistant") + span_output = convert_to_splunk_ao_message(completion, "assistant") # Add a span to the current trace or span (if this is a nested trace) span = self.logger.add_llm_span( @@ -195,10 +195,10 @@ def _finalize(self) -> None: # For other APIs, add the final span output full_conversation = [] if isinstance(self.input_data.input, list): - full_conversation.extend([convert_to_galileo_message(msg) for msg in self.input_data.input]) + full_conversation.extend([convert_to_splunk_ao_message(msg) for msg in self.input_data.input]) else: - full_conversation.append(convert_to_galileo_message(self.input_data.input)) - full_conversation.append(convert_to_galileo_message(completion, "assistant")) + full_conversation.append(convert_to_splunk_ao_message(self.input_data.input)) + full_conversation.append(convert_to_splunk_ao_message(completion, "assistant")) # Serialize with "messages" wrapper for UI compatibility trace_output = {"messages": [msg.model_dump(exclude_none=True) for msg in full_conversation]} diff --git a/src/galileo/otel.py b/src/galileo/otel.py index 94198763..250c9a4b 100644 --- a/src/galileo/otel.py +++ b/src/galileo/otel.py @@ -211,7 +211,7 @@ class GalileoSpanProcessor(SpanProcessor): >>> from opentelemetry.sdk.trace import TracerProvider >>> tracer_provider = TracerProvider() >>> processor = GalileoSpanProcessor(project="my-project") - >>> add_galileo_span_processor(tracer_provider, processor) + >>> add_splunk_ao_span_processor(tracer_provider, processor) """ def __init__( @@ -312,7 +312,7 @@ def processor(self) -> SpanProcessor: return self._processor -def add_galileo_span_processor(tracer_provider: TracerProvider, processor: GalileoSpanProcessor) -> None: +def add_splunk_ao_span_processor(tracer_provider: TracerProvider, processor: GalileoSpanProcessor) -> None: """Add the Galileo span processor to the tracer provider.""" tracer_provider.add_span_processor(processor) _TRACE_PROVIDER_CONTEXT_VAR.set(tracer_provider) @@ -401,7 +401,7 @@ def _set_workflow_span_attributes(span: trace.Span, galileo_span: WorkflowSpan) @contextmanager -def start_galileo_span(galileo_span: GalileoSpan) -> Generator[trace.Span, Any, None]: +def start_splunk_ao_span(galileo_span: GalileoSpan) -> Generator[trace.Span, Any, None]: tracer_provider = _TRACE_PROVIDER_CONTEXT_VAR.get() if tracer_provider is None: tracer_provider = trace.get_tracer_provider() diff --git a/src/galileo/tracing.py b/src/galileo/tracing.py index 8bd4c986..dee1cfb8 100644 --- a/src/galileo/tracing.py +++ b/src/galileo/tracing.py @@ -1,6 +1,6 @@ """Utilities for distributed tracing with Galileo.""" -from galileo.decorator import galileo_context +from galileo.decorator import splunk_ao_context def get_tracing_headers() -> dict[str, str]: @@ -42,4 +42,4 @@ async def orchestrator(): ) ``` """ - return galileo_context.get_logger_instance().get_tracing_headers() + return splunk_ao_context.get_logger_instance().get_tracing_headers() diff --git a/src/galileo/utils/decorators/__init__.py b/src/galileo/utils/decorators/__init__.py index fe58bd18..13980a5c 100644 --- a/src/galileo/utils/decorators/__init__.py +++ b/src/galileo/utils/decorators/__init__.py @@ -14,15 +14,15 @@ retry_on_transient_http_error, warn_catch_exception, ) -from galileo.utils.decorators.telemetry_toggle import galileo_logging_enabled, nop_async, nop_sync +from galileo.utils.decorators.telemetry_toggle import nop_async, nop_sync, splunk_ao_logging_enabled __all__ = [ "INFRASTRUCTURE_EXCEPTIONS", "RETRYABLE_STATUS_CODES", "async_warn_catch_exception", - "galileo_logging_enabled", "nop_async", "nop_sync", "retry_on_transient_http_error", + "splunk_ao_logging_enabled", "warn_catch_exception", ] diff --git a/src/galileo/utils/decorators/telemetry_toggle.py b/src/galileo/utils/decorators/telemetry_toggle.py index 8c646ea4..630ff4a5 100644 --- a/src/galileo/utils/decorators/telemetry_toggle.py +++ b/src/galileo/utils/decorators/telemetry_toggle.py @@ -14,7 +14,7 @@ _logger = logging.getLogger(__name__) -def galileo_logging_enabled() -> bool: +def splunk_ao_logging_enabled() -> bool: """ Check if Galileo logging/telemetry is enabled. @@ -46,7 +46,7 @@ def nop_sync(f: Callable) -> Callable: @functools.wraps(f) def decorated(*args: Any, **kwargs: Any) -> Any: - if galileo_logging_enabled(): + if splunk_ao_logging_enabled(): return f(*args, **kwargs) _logger.debug(f"Bypassing logging for {f.__name__}. Logging is currently disabled.") return None @@ -74,7 +74,7 @@ def nop_async(f: Callable) -> Callable: @functools.wraps(f) async def decorated(*args: Any, **kwargs: Any) -> Any: - if galileo_logging_enabled(): + if splunk_ao_logging_enabled(): return await f(*args, **kwargs) _logger.debug(f"Bypassing logging for {f.__name__}. Logging is currently disabled.") return None diff --git a/src/galileo/utils/headers_data.py b/src/galileo/utils/headers_data.py index bd361e87..30add8b3 100644 --- a/src/galileo/utils/headers_data.py +++ b/src/galileo/utils/headers_data.py @@ -61,7 +61,7 @@ def get_method_name() -> str: def get_sdk_header() -> str: - """Build the X-Galileo-SDK header value.""" + """Build the Splunk-AO-SDK header value.""" version = get_package_version() method_name = get_method_name() diff --git a/tests/test_agent_control.py b/tests/test_agent_control.py index 236347b1..b1eb38b4 100644 --- a/tests/test_agent_control.py +++ b/tests/test_agent_control.py @@ -6,7 +6,7 @@ from galileo import AgentControlTarget, AgentControlTargetUnresolvedError, get_agent_control_target from galileo.constants import DEFAULT_LOG_STREAM_NAME, DEFAULT_PROJECT_NAME -from galileo.decorator import galileo_context +from galileo.decorator import splunk_ao_context from galileo.utils.singleton import GalileoLoggerSingleton @@ -20,14 +20,14 @@ def reset_agent_control_helper_state(monkeypatch): GalileoLoggerSingleton().reset_all() monkeypatch.setattr(GalileoLoggerSingleton, "get_all_loggers", lambda self: {}) monkeypatch.setattr( - galileo_context, "get_logger_instance", lambda *args, **kwargs: SimpleNamespace(flush=lambda: None) + splunk_ao_context, "get_logger_instance", lambda *args, **kwargs: SimpleNamespace(flush=lambda: None) ) - galileo_context.reset() + splunk_ao_context.reset() yield # Then: context and singleton state are restored for following tests - galileo_context.reset() + splunk_ao_context.reset() GalileoLoggerSingleton().reset_all() @@ -128,7 +128,7 @@ def test_get_agent_control_target_uses_cached_context_logger(monkeypatch) -> Non _stub_cached_logger(monkeypatch, logger) # When: resolving an Agent Control target - with galileo_context(project="project-a", log_stream="stream-a"): + with splunk_ao_context(project="project-a", log_stream="stream-a"): target = get_agent_control_target() # Then: the helper reads the resolved IDs without creating a new logger diff --git a/tests/test_decorator.py b/tests/test_decorator.py index 96394664..b1a56ff3 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -5,7 +5,7 @@ import pytest from pydantic import BaseModel -from galileo import Message, MessageRole, galileo_context, log, start_session +from galileo import Message, MessageRole, splunk_ao_context, log, start_session from galileo.decorator import _session_id_context from galileo.schema.content_blocks import DataContentBlock, TextContentBlock from galileo_core.schemas.logging.span import AgentSpan, LlmSpan, RetrieverSpan, ToolSpan, WorkflowSpan @@ -16,9 +16,9 @@ @pytest.fixture def reset_context(): - galileo_context.reset() + splunk_ao_context.reset() yield - galileo_context.reset() + splunk_ao_context.reset() @patch("galileo.logger.logger.LogStreams") @@ -31,27 +31,27 @@ def test_decorator_context_reset( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") @log(span_type="llm") def llm_call(query: str) -> str: return "response" - assert galileo_context.get_current_trace() is None + assert splunk_ao_context.get_current_trace() is None llm_call(query="input") - assert len(galileo_context.get_logger_instance().traces) == 1 - assert galileo_context.get_current_trace() is not None - assert galileo_context.get_current_project() == "project-X" - assert galileo_context.get_current_log_stream() == "log-stream-X" + assert len(splunk_ao_context.get_logger_instance().traces) == 1 + assert splunk_ao_context.get_current_trace() is not None + assert splunk_ao_context.get_current_project() == "project-X" + assert splunk_ao_context.get_current_log_stream() == "log-stream-X" - galileo_context.reset() + splunk_ao_context.reset() - assert len(galileo_context.get_logger_instance().traces) == 0 - assert galileo_context.get_current_trace() is None - assert galileo_context.get_current_project() is None - assert galileo_context.get_current_log_stream() is None + assert len(splunk_ao_context.get_logger_instance().traces) == 0 + assert splunk_ao_context.get_current_trace() is None + assert splunk_ao_context.get_current_project() is None + assert splunk_ao_context.get_current_log_stream() is None @patch("galileo.logger.logger.LogStreams") @@ -64,15 +64,15 @@ def test_decorator_context_init( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") - assert galileo_context.get_current_project() == "project-X" - assert galileo_context.get_current_log_stream() == "log-stream-X" + assert splunk_ao_context.get_current_project() == "project-X" + assert splunk_ao_context.get_current_log_stream() == "log-stream-X" - galileo_context.reset() + splunk_ao_context.reset() - assert galileo_context.get_current_project() is None - assert galileo_context.get_current_log_stream() is None + assert splunk_ao_context.get_current_project() is None + assert splunk_ao_context.get_current_log_stream() is None @patch("galileo.logger.logger.LogStreams") @@ -89,13 +89,13 @@ def test_decorator_context_flush( def llm_call(query: str) -> str: return "response" - assert galileo_context.get_current_trace() is None + assert splunk_ao_context.get_current_trace() is None llm_call(query="input") - assert galileo_context.get_current_trace() is not None + assert splunk_ao_context.get_current_trace() is not None - galileo_context.flush() + splunk_ao_context.flush() # Check if ingest_traces (async) was called instead of ingest_traces if mock_traces_client_instance.ingest_traces.call_args is not None: @@ -105,8 +105,8 @@ def llm_call(query: str) -> str: assert len(payload.traces) == 1 assert len(payload.traces[0].spans) == 1 - assert galileo_context.get_current_trace() is None - assert galileo_context.get_current_span_stack() == [] + assert splunk_ao_context.get_current_trace() is None + assert splunk_ao_context.get_current_span_stack() == [] @patch("galileo.logger.logger.LogStreams") @@ -119,43 +119,43 @@ def test_decorator_context_flush_specific_project_and_log_stream( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") @log(span_type="llm") def llm_call(query: str) -> str: return "response" - assert galileo_context.get_current_trace() is None + assert splunk_ao_context.get_current_trace() is None llm_call(query="input") - assert galileo_context.get_current_trace() is not None + assert splunk_ao_context.get_current_trace() is not None - galileo_context.init(project="project-Y", log_stream="log-stream-Y") + splunk_ao_context.init(project="project-Y", log_stream="log-stream-Y") - assert galileo_context.get_current_trace() is None + assert splunk_ao_context.get_current_trace() is None llm_call(query="input") - assert galileo_context.get_current_trace() is not None + assert splunk_ao_context.get_current_trace() is not None - galileo_context.flush(project="project-X", log_stream="log-stream-X") + splunk_ao_context.flush(project="project-X", log_stream="log-stream-X") payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 assert len(payload.traces[0].spans) == 1 - assert galileo_context.get_current_trace() is not None + assert splunk_ao_context.get_current_trace() is not None - galileo_context.flush(project="project-Y", log_stream="log-stream-Y") + splunk_ao_context.flush(project="project-Y", log_stream="log-stream-Y") payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 assert len(payload.traces[0].spans) == 1 - assert galileo_context.get_current_trace() is None + assert splunk_ao_context.get_current_trace() is None @patch("galileo.logger.logger.LogStreams") @@ -172,37 +172,37 @@ def test_decorator_context_flush_all( def llm_call(query: str) -> str: return "response" - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") llm_call(query="input_X") - trace_X = galileo_context.get_current_trace() + trace_X = splunk_ao_context.get_current_trace() assert trace_X.input == '{"query": "input_X"}' - logger_X = galileo_context.get_logger_instance(project="project-X", log_stream="log-stream-X") + logger_X = splunk_ao_context.get_logger_instance(project="project-X", log_stream="log-stream-X") assert len(logger_X.traces) == 1 - galileo_context.init(project="project-Y", log_stream="log-stream-Y") + splunk_ao_context.init(project="project-Y", log_stream="log-stream-Y") llm_call(query="input_Y") - trace_Y = galileo_context.get_current_trace() + trace_Y = splunk_ao_context.get_current_trace() assert trace_Y.input == '{"query": "input_Y"}' - logger_Y = galileo_context.get_logger_instance(project="project-Y", log_stream="log-stream-Y") + logger_Y = splunk_ao_context.get_logger_instance(project="project-Y", log_stream="log-stream-Y") assert len(logger_Y.traces) == 1 # Flush both loggers - galileo_context.flush_all() + splunk_ao_context.flush_all() - logger_X = galileo_context.get_logger_instance(project="project-X", log_stream="log-stream-X") + logger_X = splunk_ao_context.get_logger_instance(project="project-X", log_stream="log-stream-X") assert len(logger_X.traces) == 0 - logger_Y = galileo_context.get_logger_instance(project="project-Y", log_stream="log-stream-Y") + logger_Y = splunk_ao_context.get_logger_instance(project="project-Y", log_stream="log-stream-Y") assert len(logger_Y.traces) == 0 - assert galileo_context.get_current_trace() is None - assert galileo_context.get_current_span_stack() == [] + assert splunk_ao_context.get_current_trace() is None + assert splunk_ao_context.get_current_span_stack() == [] @patch("galileo.logger.logger.LogStreams") @@ -220,7 +220,7 @@ def llm_call(query: str) -> str: return "response" llm_call(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -247,7 +247,7 @@ def my_function(arg1, arg2): return arg1 + arg2 my_function(1, 2) - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -276,7 +276,7 @@ def my_function(system: Message, user: Message): my_function( Message(content="system prompt", role=MessageRole.system), Message(content="query", role=MessageRole.user) ) - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -311,7 +311,7 @@ def my_function(system: Message, user: Message): my_function( Message(content="system prompt", role=MessageRole.system), Message(content="query", role=MessageRole.user) ) - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -344,7 +344,7 @@ def my_function(arg1: str, arg2: str) -> str: return f"{arg1} {arg2}" my_function("arg1", "arg2") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -371,7 +371,7 @@ def my_function(arg1: str, arg2: str) -> str: return f"{arg1} {arg2}" my_function("arg1", "arg2") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -403,7 +403,7 @@ def my_function(arg1: str, arg2: str): return my_tool_function(arg1) my_function("arg1", "arg2") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -440,7 +440,7 @@ def nested_call(nested_query: str): return llm_call(query=nested_query) output = nested_call(nested_query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -477,7 +477,7 @@ def nested_call(nested_query: str) -> str: return "new response" output = nested_call(nested_query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -509,7 +509,7 @@ def retriever_call(query: str) -> str: return "response1" retriever_call(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -533,7 +533,7 @@ def retriever_call(query: str): return ["response1", "response2"] retriever_call(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -560,7 +560,7 @@ def retriever_call(query: str): return [{"content": "response1", "metadata": {"key": "value"}}, {"content": "response2", "metadata": None}] retriever_call(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -587,7 +587,7 @@ def retriever_call(query: str): return [Document(content="response1", metadata={"key": "value"}), Document(content="response2", metadata=None)] retriever_call(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -616,7 +616,7 @@ def foo() -> NoReturn: with pytest.raises(Exception): foo() - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -639,14 +639,14 @@ def foo() -> str: return "response" foo() - galileo_context.start_session( + splunk_ao_context.start_session( name="test-session", previous_session_id="6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9e", external_id="test" ) - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() assert logger.session_id == "6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -673,11 +673,11 @@ def foo() -> str: name="test-session", previous_session_id="6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9e", external_id="test" ) - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() assert logger.session_id == "6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c" assert session_id == "6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -699,12 +699,12 @@ def foo() -> str: return "response" foo() - galileo_context.start_session() + splunk_ao_context.start_session() - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() assert logger.session_id == "6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -726,16 +726,16 @@ def foo() -> str: return "response" foo() - galileo_context.start_session(name="test-session") + splunk_ao_context.start_session(name="test-session") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() assert logger.session_id == "6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c" - galileo_context.clear_session() + splunk_ao_context.clear_session() assert logger.session_id is None - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -757,12 +757,12 @@ def foo() -> str: return "response" foo() - galileo_context.set_session(session_id="6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c") + splunk_ao_context.set_session(session_id="6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() assert logger.session_id == "6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -804,7 +804,7 @@ def my_function(complex_input: dict) -> str: complex_input = {"nested": {"key": "value", "number": 42}, "list": [1, 2, 3], "string": "test"} my_function(complex_input=complex_input) - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -832,7 +832,7 @@ def llm_call_returning_list(query: str): return ["response1", "response2", "response3"] llm_call_returning_list(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -858,7 +858,7 @@ def llm_call_returning_tuple(query: str): return ("response1", "response2") llm_call_returning_tuple(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -885,7 +885,7 @@ def llm_call_returning_dict(query: str): return {"response": "value", "number": 42} llm_call_returning_dict(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -917,7 +917,7 @@ def workflow_with_complex_output(query: str): } workflow_with_complex_output(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -945,7 +945,7 @@ def process_model(model: TestPydanticModel) -> str: test_model = TestPydanticModel(name="test", value=42) process_model(model=test_model) - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -973,7 +973,7 @@ def create_model(name: str, value: int): return TestPydanticModel(name=name, value=value) create_model(name="output_test", value=123) - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -1000,7 +1000,7 @@ def function_returning_none(query: str) -> None: return None function_returning_none(query="input") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -1026,7 +1026,7 @@ def tool_with_complex_output(input_data: str): return {"tool_result": input_data, "status": "success", "items": [1, 2, 3]} tool_with_complex_output(input_data="test") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -1054,7 +1054,7 @@ def agent_with_complex_output(query: str): return {"agent_response": query, "confidence": 0.95, "actions": ["analyze", "respond"]} agent_with_complex_output(query="test query") - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] span = payload.traces[0].spans[0] @@ -1088,7 +1088,7 @@ def workflow_returning_content_blocks(query: str): # When: the workflow is executed and flushed workflow_returning_content_blocks(query="analyze this") - galileo_context.flush() + splunk_ao_context.flush() # Then: content blocks are preserved as a list on the trace (not stringified) payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -1123,7 +1123,7 @@ def workflow_returning_messages(query: str): # When: the workflow is executed and flushed workflow_returning_messages(query="chat") - galileo_context.flush() + splunk_ao_context.flush() # Then: messages are serialized to string on the trace (not preserved as list) payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -1149,9 +1149,9 @@ def test_mode_context_init_default( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") - assert galileo_context.get_current_mode() == "batch" + assert splunk_ao_context.get_current_mode() == "batch" @patch("galileo.logger.logger.LogStreams") @@ -1165,9 +1165,9 @@ def test_mode_context_init_explicit( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="distributed") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="distributed") - assert galileo_context.get_current_mode() == "distributed" + assert splunk_ao_context.get_current_mode() == "distributed" @patch("galileo.logger.logger.LogStreams") @@ -1181,8 +1181,8 @@ def test_mode_context_call_default( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - with galileo_context(project="project-X", log_stream="log-stream-X"): - assert galileo_context.get_current_mode() == "batch" + with splunk_ao_context(project="project-X", log_stream="log-stream-X"): + assert splunk_ao_context.get_current_mode() == "batch" @patch("galileo.logger.logger.LogStreams") @@ -1196,8 +1196,8 @@ def test_mode_context_call_explicit( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - with galileo_context(project="project-X", log_stream="log-stream-X", mode="distributed"): - assert galileo_context.get_current_mode() == "distributed" + with splunk_ao_context(project="project-X", log_stream="log-stream-X", mode="distributed"): + assert splunk_ao_context.get_current_mode() == "distributed" @patch("galileo.logger.logger.LogStreams") @@ -1212,15 +1212,15 @@ def test_mode_context_nested_push_pop( setup_mock_logstreams_client(mock_logstreams_client) # Set initial mode - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="batch") - assert galileo_context.get_current_mode() == "batch" + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="batch") + assert splunk_ao_context.get_current_mode() == "batch" # Enter nested context with different mode - with galileo_context(project="project-Y", log_stream="log-stream-Y", mode="distributed"): - assert galileo_context.get_current_mode() == "distributed" + with splunk_ao_context(project="project-Y", log_stream="log-stream-Y", mode="distributed"): + assert splunk_ao_context.get_current_mode() == "distributed" # After exiting, mode should be restored - assert galileo_context.get_current_mode() == "batch" + assert splunk_ao_context.get_current_mode() == "batch" @patch("galileo.logger.logger.LogStreams") @@ -1235,22 +1235,22 @@ def test_mode_context_multiple_nested_levels( setup_mock_logstreams_client(mock_logstreams_client) # Set initial mode - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="batch") - assert galileo_context.get_current_mode() == "batch" + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="batch") + assert splunk_ao_context.get_current_mode() == "batch" # First nested level - with galileo_context(project="project-Y", log_stream="log-stream-Y", mode="distributed"): - assert galileo_context.get_current_mode() == "distributed" + with splunk_ao_context(project="project-Y", log_stream="log-stream-Y", mode="distributed"): + assert splunk_ao_context.get_current_mode() == "distributed" # Second nested level - defaults back to batch - with galileo_context(project="project-Z", log_stream="log-stream-Z"): - assert galileo_context.get_current_mode() == "batch" + with splunk_ao_context(project="project-Z", log_stream="log-stream-Z"): + assert splunk_ao_context.get_current_mode() == "batch" # Back to first nested level - assert galileo_context.get_current_mode() == "distributed" + assert splunk_ao_context.get_current_mode() == "distributed" # Back to original - assert galileo_context.get_current_mode() == "batch" + assert splunk_ao_context.get_current_mode() == "batch" @patch("galileo.logger.logger.LogStreams") @@ -1264,11 +1264,11 @@ def test_mode_context_reset( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="distributed") - assert galileo_context.get_current_mode() == "distributed" + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="distributed") + assert splunk_ao_context.get_current_mode() == "distributed" - galileo_context.reset() - assert galileo_context.get_current_mode() == "batch" + splunk_ao_context.reset() + assert splunk_ao_context.get_current_mode() == "batch" @patch("galileo.logger.logger.LogStreams") @@ -1283,23 +1283,23 @@ def test_mode_flush_with_explicit_mode( setup_mock_logstreams_client(mock_logstreams_client) # Initialize with batch mode - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="batch") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="batch") @log(span_type="llm") def llm_call(query: str) -> str: return "response" llm_call(query="input") - assert galileo_context.get_current_trace() is not None + assert splunk_ao_context.get_current_trace() is not None # Flush with explicit mode - galileo_context.flush(project="project-X", log_stream="log-stream-X", mode="batch") + splunk_ao_context.flush(project="project-X", log_stream="log-stream-X", mode="batch") payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 # Trace context should be reset since we flushed the current context - assert galileo_context.get_current_trace() is None + assert splunk_ao_context.get_current_trace() is None @patch("galileo.logger.logger.LogStreams") @@ -1314,23 +1314,23 @@ def test_mode_flush_different_mode_no_reset( setup_mock_logstreams_client(mock_logstreams_client) # Initialize with batch mode - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="batch") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="batch") @log(span_type="llm") def llm_call(query: str) -> str: return "response" llm_call(query="input") - current_trace = galileo_context.get_current_trace() + current_trace = splunk_ao_context.get_current_trace() assert current_trace is not None # Flush with a different mode shouldn't reset the current trace context # Since the logger instances are different - galileo_context.flush(project="project-X", log_stream="log-stream-X", mode="distributed") + splunk_ao_context.flush(project="project-X", log_stream="log-stream-X", mode="distributed") # Current trace should still exist since we didn't flush the batch mode instance - assert galileo_context.get_current_trace() is not None - assert galileo_context.get_current_trace() == current_trace + assert splunk_ao_context.get_current_trace() is not None + assert splunk_ao_context.get_current_trace() == current_trace @patch("galileo.logger.logger.LogStreams") @@ -1346,9 +1346,9 @@ def test_mode_from_environment_variable( setup_mock_logstreams_client(mock_logstreams_client) # When mode is not specified, it should use the environment variable - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") - assert galileo_context.get_current_mode() == "distributed" + assert splunk_ao_context.get_current_mode() == "distributed" @patch("galileo.logger.logger.LogStreams") @@ -1364,9 +1364,9 @@ def test_mode_explicit_overrides_environment( setup_mock_logstreams_client(mock_logstreams_client) # Explicit mode should override environment variable - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="batch") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="batch") - assert galileo_context.get_current_mode() == "batch" + assert splunk_ao_context.get_current_mode() == "batch" @patch("galileo.logger.logger.LogStreams") @@ -1381,11 +1381,11 @@ def test_get_logger_instance_with_explicit_mode( setup_mock_logstreams_client(mock_logstreams_client) # Initialize with batch mode - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="batch") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="batch") # Get logger instance with different mode - logger_batch = galileo_context.get_logger_instance(project="project-X", log_stream="log-stream-X", mode="batch") - logger_distributed = galileo_context.get_logger_instance( + logger_batch = splunk_ao_context.get_logger_instance(project="project-X", log_stream="log-stream-X", mode="batch") + logger_distributed = splunk_ao_context.get_logger_instance( project="project-X", log_stream="log-stream-X", mode="distributed" ) @@ -1408,7 +1408,7 @@ def test_multiple_workflow_calls_create_one_trace_with_multiple_spans( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="project-X", log_stream="log-stream-X", mode="batch") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X", mode="batch") @log(span_type="workflow") def process_query(query: str) -> str: @@ -1425,7 +1425,7 @@ def process_query(query: str) -> str: assert result3 == "Processed: query 3" # Before flush, verify only 1 trace was created with 3 workflow spans - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() assert len(logger.traces) == 1, f"Expected 1 trace, got {len(logger.traces)}" # Verify the single trace has 3 workflow spans @@ -1438,7 +1438,7 @@ def process_query(query: str) -> str: assert trace.spans[2].input == '{"query": "query 3"}' # Flush the trace - galileo_context.flush() + splunk_ao_context.flush() # Verify ingest_traces was called with 1 trace containing 3 spans payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -1446,7 +1446,7 @@ def process_query(query: str) -> str: assert len(payload.traces[0].spans) == 3 # After flush, trace context should be cleared - assert galileo_context.get_current_trace() is None + assert splunk_ao_context.get_current_trace() is None assert len(logger.traces) == 0 @@ -1467,9 +1467,9 @@ def test_session_id_context_manager( def foo() -> str: return "response" - galileo_context.init(project="test-project", log_stream="test-stream") - with galileo_context(session_id=test_session_id): - assert galileo_context.get_logger_instance().session_id == test_session_id + splunk_ao_context.init(project="test-project", log_stream="test-stream") + with splunk_ao_context(session_id=test_session_id): + assert splunk_ao_context.get_logger_instance().session_id == test_session_id foo() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -1491,19 +1491,19 @@ def test_session_id_nested_context_stacking( session_2 = "3c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9d" # No session initially - assert galileo_context.get_logger_instance().session_id is None + assert splunk_ao_context.get_logger_instance().session_id is None - with galileo_context(project="p1", log_stream="s1", session_id=session_1): - assert galileo_context.get_logger_instance().session_id == session_1 + with splunk_ao_context(project="p1", log_stream="s1", session_id=session_1): + assert splunk_ao_context.get_logger_instance().session_id == session_1 - with galileo_context(project="p2", log_stream="s2", session_id=session_2): - assert galileo_context.get_logger_instance().session_id == session_2 + with splunk_ao_context(project="p2", log_stream="s2", session_id=session_2): + assert splunk_ao_context.get_logger_instance().session_id == session_2 # Restored after nested context exits - assert galileo_context.get_logger_instance().session_id == session_1 + assert splunk_ao_context.get_logger_instance().session_id == session_1 # Cleared after all contexts exit - assert galileo_context.get_logger_instance().session_id is None + assert splunk_ao_context.get_logger_instance().session_id is None @patch("galileo.logger.logger.LogStreams") @@ -1517,19 +1517,19 @@ def test_session_id_cleared_on_reset_and_init( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="project-X", log_stream="log-stream-X") - galileo_context.start_session(name="test-session") - assert galileo_context.get_logger_instance().session_id == "6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c" + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.start_session(name="test-session") + assert splunk_ao_context.get_logger_instance().session_id == "6c4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9c" # Reset clears session - galileo_context.reset() - assert galileo_context.get_logger_instance().session_id is None + splunk_ao_context.reset() + assert splunk_ao_context.get_logger_instance().session_id is None # Re-init also clears session - galileo_context.init(project="project-X", log_stream="log-stream-X") - galileo_context.start_session(name="test-session") - galileo_context.init(project="project-Y", log_stream="log-stream-Y") - assert galileo_context.get_logger_instance().session_id is None + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.start_session(name="test-session") + splunk_ao_context.init(project="project-Y", log_stream="log-stream-Y") + assert splunk_ao_context.get_logger_instance().session_id is None @patch("galileo.logger.logger.LogStreams") @@ -1545,12 +1545,12 @@ def test_start_session_overrides_context_session( context_session = "6d4e3f7e-4a9a-4e7e-8c1f-3a9a3a9a3a9d" - with galileo_context(project="test-project", log_stream="test-stream", session_id=context_session): - assert galileo_context.get_logger_instance().session_id == context_session + with splunk_ao_context(project="test-project", log_stream="test-stream", session_id=context_session): + assert splunk_ao_context.get_logger_instance().session_id == context_session # start_session overrides context session - new_session_id = galileo_context.start_session(name="new-session") - assert galileo_context.get_logger_instance().session_id == new_session_id + new_session_id = splunk_ao_context.start_session(name="new-session") + assert splunk_ao_context.get_logger_instance().session_id == new_session_id assert _session_id_context.get() == new_session_id @@ -1569,7 +1569,7 @@ def test_flush_on_error_called_when_flush_raises( on_error = Mock() - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") @log(span_type="llm") def llm_call(query: str) -> str: @@ -1578,7 +1578,7 @@ def llm_call(query: str) -> str: llm_call(query="input") # When: flush is called with on_error - galileo_context.flush(on_error=on_error) + splunk_ao_context.flush(on_error=on_error) # Then: on_error is invoked with the exception; no warning is raised on_error.assert_called_once() @@ -1597,7 +1597,7 @@ def test_flush_warns_when_flush_raises_without_on_error( setup_mock_logstreams_client(mock_logstreams_client) mock_traces_client_instance.ingest_traces.side_effect = RuntimeError("network error") - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") @log(span_type="llm") def llm_call(query: str) -> str: @@ -1608,7 +1608,7 @@ def llm_call(query: str) -> str: # When/Then: flush does not raise; a warning is logged instead with patch("galileo.decorator._logger") as mock_logger: - galileo_context.flush() + splunk_ao_context.flush() mock_logger.warning.assert_called_once() assert "flush failed" in mock_logger.warning.call_args[0][0] @@ -1628,7 +1628,7 @@ def test_flush_on_error_callback_raises_is_swallowed( def bad_callback(exc: Exception) -> None: raise ValueError("callback failed") - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") @log(span_type="llm") def llm_call(query: str) -> str: @@ -1638,7 +1638,7 @@ def llm_call(query: str) -> str: # When/Then: flush does not raise even though the callback raises with patch("galileo.decorator._logger") as mock_logger: - galileo_context.flush(on_error=bad_callback) # must not raise + splunk_ao_context.flush(on_error=bad_callback) # must not raise mock_logger.warning.assert_called_once() assert "on_error callback raised" in mock_logger.warning.call_args[0][0] @@ -1655,7 +1655,7 @@ def test_flush_on_error_logs_at_debug_not_warning( setup_mock_logstreams_client(mock_logstreams_client) mock_traces_client_instance.ingest_traces.side_effect = RuntimeError("network error") - galileo_context.init(project="project-X", log_stream="log-stream-X") + splunk_ao_context.init(project="project-X", log_stream="log-stream-X") @log(span_type="llm") def llm_call(query: str) -> str: @@ -1665,7 +1665,7 @@ def llm_call(query: str) -> str: # When: flush is called with on_error with patch("galileo.decorator._logger") as mock_logger: - galileo_context.flush(on_error=Mock()) + splunk_ao_context.flush(on_error=Mock()) # Then: debug is called, not warning mock_logger.debug.assert_called_once() diff --git a/tests/test_decorator_distributed.py b/tests/test_decorator_distributed.py index a47ee4f6..e57054ff 100644 --- a/tests/test_decorator_distributed.py +++ b/tests/test_decorator_distributed.py @@ -6,7 +6,7 @@ import pytest -from galileo import Message, MessageRole, galileo_context, log +from galileo import Message, MessageRole, splunk_ao_context, log from galileo.constants.tracing import PARENT_ID_HEADER, TRACE_ID_HEADER from galileo.decorator import _parent_id_context, _trace_id_context from galileo.schema.content_blocks import DataContentBlock, TextContentBlock @@ -25,9 +25,9 @@ @pytest.fixture def reset_context(): """Reset the decorator context before each test.""" - galileo_context.reset() + splunk_ao_context.reset() yield - galileo_context.reset() + splunk_ao_context.reset() @pytest.fixture @@ -57,7 +57,7 @@ def test_decorator_get_tracing_headers( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") @log(span_type="workflow") def orchestrator(query: str) -> dict: @@ -72,7 +72,7 @@ def orchestrator(query: str) -> dict: assert PARENT_ID_HEADER in headers # Verify the trace ID matches the logger's trace - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() assert headers[TRACE_ID_HEADER] == str(logger.traces[0].id) @@ -91,7 +91,7 @@ def test_decorator_with_middleware_context( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") # Simulate middleware setting context variables trace_id = "12345678-1234-4678-9abc-123456789abc" @@ -109,7 +109,7 @@ def downstream_service(query: str) -> str: assert result == "processed: test input" # Verify logger was created with distributed tracing context - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() assert logger.mode == "distributed" assert len(logger.traces) == 1 @@ -133,9 +133,9 @@ def test_decorator_updates_trace_with_output_and_duration( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) @log(span_type="workflow") @@ -196,7 +196,7 @@ def test_decorator_server_side_does_not_conclude_trace( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") # Simulate middleware setting context variables (server receiving trace_id/span_id) trace_id = "12345678-1234-4678-9abc-123456789abc" @@ -205,7 +205,7 @@ def test_decorator_server_side_does_not_conclude_trace( _trace_id_context.set(trace_id) _parent_id_context.set(parent_id) - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) @log(span_type="workflow") @@ -244,10 +244,10 @@ def test_decorator_client_and_server_side_behavior( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") # CLIENT SIDE: Start a new trace - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture_client = setup_thread_pool_request_capture(logger) @log(span_type="workflow") @@ -288,8 +288,8 @@ def client_function(query: str) -> str: assert client_trace_request.is_complete, "Trace should be marked complete" # Reset context for server-side test - galileo_context.reset() - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.reset() + splunk_ao_context.init(project="test-project", log_stream="test-stream") # SERVER SIDE: Receive distributed tracing headers trace_id = "aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaaaa" @@ -298,7 +298,7 @@ def client_function(query: str) -> str: _trace_id_context.set(trace_id) _parent_id_context.set(parent_id) - logger_server = galileo_context.get_logger_instance() + logger_server = splunk_ao_context.get_logger_instance() capture_server = setup_thread_pool_request_capture(logger_server) @log(span_type="workflow") @@ -335,9 +335,9 @@ def test_decorator_workflow_span_output_is_set( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) @log(span_type="workflow") @@ -385,9 +385,9 @@ def test_decorator_both_trace_and_workflow_span_have_output( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) # @log without span_type creates a workflow span, and starts a trace if needed @@ -451,9 +451,9 @@ def test_decorator_workflow_span_empty_string_output_is_set( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) @log(span_type="workflow") @@ -495,9 +495,9 @@ def test_decorator_trace_duration_is_set_and_accumulates( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) @log(span_type="workflow") @@ -558,9 +558,9 @@ def test_decorator_distributed_content_blocks_preserved_on_trace( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) blocks = [ @@ -604,9 +604,9 @@ def test_decorator_distributed_messages_serialized_on_trace( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) @log(span_type="workflow") @@ -645,9 +645,9 @@ def test_decorator_distributed_documents_serialized_on_trace( setup_mock_projects_client(mock_projects_client) setup_mock_logstreams_client(mock_logstreams_client) - galileo_context.init(project="test-project", log_stream="test-stream") + splunk_ao_context.init(project="test-project", log_stream="test-stream") - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() capture = setup_thread_pool_request_capture(logger) @log(span_type="workflow") diff --git a/tests/test_experiments.py b/tests/test_experiments.py index bb4af895..ba66eb92 100644 --- a/tests/test_experiments.py +++ b/tests/test_experiments.py @@ -14,7 +14,7 @@ import galileo.experiments import galileo.jobs import galileo.utils.datasets -from galileo import galileo_context +from galileo import splunk_ao_context from galileo.decorator import SPAN_TYPE from galileo.experiments import ( Experiments, @@ -56,7 +56,7 @@ @pytest.fixture def reset_context(auto_use=True) -> None: - galileo_context.reset() + splunk_ao_context.reset() os.environ.pop("GALILEO_PROJECT", None) os.environ.pop("GALILEO_PROJECT_ID", None) @@ -152,7 +152,7 @@ def prompt_run_settings(): def complex_trace_function(input): - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() output = input + " output" logger.add_llm_span(input=input, output=output, model="example") return output @@ -1319,7 +1319,7 @@ def test_run_experiment_on_error_passed_to_flush_in_function_flow( on_error = Mock() # When: run_experiment() is called with a function and on_error (function flow) - with patch("galileo.experiments.galileo_context.flush") as mock_flush: + with patch("galileo.experiments.splunk_ao_context.flush") as mock_flush: run_experiment( experiment_name="test_experiment", project="awesome-new-project", diff --git a/tests/test_galileo_context.py b/tests/test_galileo_context.py index f0166474..4b534f8f 100644 --- a/tests/test_galileo_context.py +++ b/tests/test_galileo_context.py @@ -2,14 +2,14 @@ import pytest -from galileo import galileo_context +from galileo import splunk_ao_context from galileo.decorator import _experiment_id_context, _log_stream_context, _project_context from tests.testutils.setup import setup_mock_logstreams_client, setup_mock_projects_client, setup_mock_traces_client @pytest.fixture def reset_context() -> None: - galileo_context.reset() + splunk_ao_context.reset() @patch("galileo.logger.logger.LogStreams") @@ -19,7 +19,7 @@ def test_nested_context_restoration( mock_traces_client: Mock, mock_projects_client: Mock, mock_logstreams_client: Mock, reset_context ) -> None: """ - Test that nested galileo_context calls correctly restore the previous context. + Test that nested splunk_ao_context calls correctly restore the previous context. This tests the stack-based approach for context nesting. """ setup_mock_traces_client(mock_traces_client) @@ -32,19 +32,19 @@ def test_nested_context_restoration( assert _experiment_id_context.get() is None # First level context - with galileo_context(project="project1", log_stream="log_stream1"): + with splunk_ao_context(project="project1", log_stream="log_stream1"): assert _project_context.get() == "project1" assert _log_stream_context.get() == "log_stream1" assert _experiment_id_context.get() is None # Second level context - with galileo_context(project="project2", log_stream="log_stream2"): + with splunk_ao_context(project="project2", log_stream="log_stream2"): assert _project_context.get() == "project2" assert _log_stream_context.get() == "log_stream2" assert _experiment_id_context.get() is None # Third level context - with galileo_context(project="project3", log_stream="log_stream3"): + with splunk_ao_context(project="project3", log_stream="log_stream3"): assert _project_context.get() == "project3" assert _log_stream_context.get() == "log_stream3" assert _experiment_id_context.get() is None @@ -80,19 +80,19 @@ def test_context_update_with_defaults( setup_mock_logstreams_client(mock_logstreams_client) # First level context with project and log_stream - with galileo_context(project="project1", log_stream="log_stream1"): + with splunk_ao_context(project="project1", log_stream="log_stream1"): assert _project_context.get() == "project1" assert _log_stream_context.get() == "log_stream1" assert _experiment_id_context.get() is None # Second level context with only project updated; log_stream should be default - with galileo_context(project="project2"): + with splunk_ao_context(project="project2"): assert _project_context.get() == "project2" assert _log_stream_context.get() is None # use env default assert _experiment_id_context.get() is None # Third level context with no params: should use defaults - with galileo_context(): + with splunk_ao_context(): assert _project_context.get() is None assert _log_stream_context.get() is None assert _experiment_id_context.get() is None diff --git a/tests/test_langchain.py b/tests/test_langchain.py index 25053992..45e53103 100644 --- a/tests/test_langchain.py +++ b/tests/test_langchain.py @@ -10,7 +10,7 @@ from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage from langchain_core.outputs import ChatGeneration, LLMResult -from galileo import Message, MessageRole, galileo_context +from galileo import Message, MessageRole, splunk_ao_context from galileo.config import GalileoPythonConfig from galileo.handlers.langchain import GalileoAsyncCallback, GalileoCallback from galileo.handlers.langchain.utils import parse_llm_result, update_root_to_agent @@ -1035,7 +1035,7 @@ def logger_mocks(self): [ lambda hook: GalileoCallback(ingestion_hook=hook), lambda hook: GalileoCallback(galileo_logger=GalileoLogger(), ingestion_hook=hook), - lambda hook: GalileoCallback(galileo_logger=galileo_context.get_logger_instance(), ingestion_hook=hook), + lambda hook: GalileoCallback(galileo_logger=splunk_ao_context.get_logger_instance(), ingestion_hook=hook), ], ) def test_on_chain_end_with_ingestion_hook(self, callback_builder): diff --git a/tests/test_langchain_middleware.py b/tests/test_langchain_middleware.py index 971204ac..0462cd0f 100644 --- a/tests/test_langchain_middleware.py +++ b/tests/test_langchain_middleware.py @@ -8,7 +8,7 @@ from langgraph.runtime import Runtime from pydantic import BaseModel -from galileo import galileo_context +from galileo import splunk_ao_context from galileo.handlers.langchain.middleware import GalileoMiddleware from galileo.logger.logger import GalileoLogger from tests.testutils.setup import setup_mock_logstreams_client, setup_mock_projects_client, setup_mock_traces_client @@ -508,7 +508,7 @@ def logger_mocks(self): [ lambda hook: GalileoMiddleware(ingestion_hook=hook), lambda hook: GalileoMiddleware(galileo_logger=GalileoLogger(), ingestion_hook=hook), - lambda hook: GalileoMiddleware(galileo_logger=galileo_context.get_logger_instance(), ingestion_hook=hook), + lambda hook: GalileoMiddleware(galileo_logger=splunk_ao_context.get_logger_instance(), ingestion_hook=hook), ], ) def test_ingestion_hook_called(self, middleware_builder) -> None: diff --git a/tests/test_log_stream.py b/tests/test_log_stream.py index 34d772d6..6e3d2b5d 100644 --- a/tests/test_log_stream.py +++ b/tests/test_log_stream.py @@ -847,25 +847,25 @@ class TestLogStreamContext: """Test suite for LogStream.context() method.""" @patch("galileo.shared.project_resolver.Projects") - @patch("galileo.log_stream.galileo_context") + @patch("galileo.log_stream.splunk_ao_context") @patch("galileo.log_stream.LogStreams") - def test_context_returns_galileo_context( + def test_context_returns_splunk_ao_context( self, mock_logstreams_class: MagicMock, - mock_galileo_context: MagicMock, + mock_splunk_ao_context: MagicMock, mock_projects_class: MagicMock, reset_configuration: None, mock_logstream: MagicMock, mock_project: MagicMock, ) -> None: mock_projects_class.return_value.get_with_env_fallbacks.return_value = mock_project - """Test context() returns a properly configured galileo_context.""" + """Test context() returns a properly configured splunk_ao_context.""" mock_logstream_service = MagicMock() mock_logstreams_class.return_value = mock_logstream_service mock_logstream_service.get.return_value = mock_logstream mock_context = MagicMock() - mock_galileo_context.return_value = mock_context + mock_splunk_ao_context.return_value = mock_context # Mock the project property with patch("galileo.log_stream.Project") as mock_project_class: @@ -876,7 +876,7 @@ def test_context_returns_galileo_context( log_stream = LogStream.get(name="Test Stream", project_id="test-project-id") result = log_stream.context() - mock_galileo_context.assert_called_once_with(project="Test Project", log_stream="Test Stream") + mock_splunk_ao_context.assert_called_once_with(project="Test Project", log_stream="Test Stream") assert result == mock_context diff --git a/tests/test_openai.py b/tests/test_openai.py index 9b5c524a..dfce1b1a 100644 --- a/tests/test_openai.py +++ b/tests/test_openai.py @@ -7,7 +7,7 @@ from openai.types.chat import ChatCompletionChunk from openai.types.responses import ResponseCompletedEvent -from galileo import Message, MessageRole, galileo_context, log +from galileo import Message, MessageRole, splunk_ao_context, log from galileo.openai import OpenAIGalileo, openai from galileo_core.schemas.logging.span import LlmSpan, WorkflowSpan from tests.testutils.setup import setup_mock_logstreams_client, setup_mock_projects_client, setup_mock_traces_client @@ -45,7 +45,7 @@ def test_basic_openai_call( setup_mock_logstreams_client(mock_logstreams_client) openai_create.return_value = create_chat_completion - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() chat_completion = openai.chat.completions.create( @@ -66,7 +66,7 @@ def test_basic_openai_call( response = chat_completion.choices[0].message.content assert response == "The mock is working! ;)" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 @@ -108,7 +108,7 @@ def test_streamed_openai_call( cast_to=ChatCompletionChunk, client=openai.OpenAI(), response=Response(status_code=200, content=EventStream()) ) - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() stream = openai.chat.completions.create( @@ -123,7 +123,7 @@ def test_streamed_openai_call( assert response == "Hello" assert chunk_count == 3 - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 @@ -158,7 +158,7 @@ def test_openai_api_calls_as_parent_span( openai_create.return_value = create_chat_completion # we want reset context and enable tracing for openai plugin - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() @log() @@ -171,7 +171,7 @@ def call_openai(model: str = "gpt-3.5-turbo"): output = call_openai() assert output == "The mock is working! ;)" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] @@ -206,7 +206,7 @@ def test_openai_error_trace( setup_mock_logstreams_client(mock_logstreams_client) # we want reset context and enable tracing for openai plugin - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() def call_openai(model: str = "gpt-3.5-turbo"): @@ -218,7 +218,7 @@ def call_openai(model: str = "gpt-3.5-turbo"): with pytest.raises(RuntimeError): call_openai() - galileo_context.flush() + splunk_ao_context.flush() openai_create.assert_called_once() mock_traces_client_instance.ingest_traces.assert_called() @@ -243,7 +243,7 @@ def test_openai_error_trace_( ) # we want reset context and enable tracing for openai plugin - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() def call_openai(model: str = "gpt-3.5-turbo"): @@ -255,7 +255,7 @@ def call_openai(model: str = "gpt-3.5-turbo"): with pytest.raises(RuntimeError): call_openai() - galileo_context.flush() + splunk_ao_context.flush() openai_create.assert_called_once() mock_traces_client_instance.ingest_traces.assert_called() @@ -283,7 +283,7 @@ def test_client_fails_because_openai_error_trace_no_exp( setup_mock_logstreams_client(mock_logstreams_client) # we want reset context and enable tracing for openai plugin - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() @log @@ -296,7 +296,7 @@ def call_openai(model: str = "gpt-3.5-turbo"): with pytest.raises(RuntimeError): call_openai() - galileo_context.flush() + splunk_ao_context.flush() mock_projects_client.assert_called_once() openai_create.assert_called_once() @@ -328,7 +328,7 @@ def test_galileo_api_client_transport_error_not_blocking_user_code( setup_mock_logstreams_client(mock_logstreams_client) openai_create.return_value = create_chat_completion # we want reset context and enable tracing for openai plugin - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() @log() @@ -339,7 +339,7 @@ def call_openai(model: str = "gpt-3.5-turbo"): return chat_completion.choices[0].message.content assert call_openai() == "The mock is working! ;)" - galileo_context.flush() + splunk_ao_context.flush() # Projects may be called multiple times as different components try to initialize # The key assertion is that user code (openai_create) runs successfully despite SDK errors @@ -363,10 +363,10 @@ def test_openai_calls_in_active_trace( setup_mock_logstreams_client(mock_logstreams_client) openai_create.return_value = create_chat_completion - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() - logger = galileo_context.get_logger_instance() + logger = splunk_ao_context.get_logger_instance() logger.start_trace("test trace") openai.chat.completions.create(messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-4o-mini") @@ -402,7 +402,7 @@ def test_chat_completions_multiple_messages( setup_mock_logstreams_client(mock_logstreams_client) openai_create.return_value = create_chat_completion - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() input_messages = [ @@ -419,7 +419,7 @@ def test_chat_completions_multiple_messages( response_text = response.choices[0].message.content assert response_text == "The mock is working! ;)" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 @@ -462,7 +462,7 @@ def test_basic_responses_api_call( setup_mock_logstreams_client(mock_logstreams_client) openai_create.return_value = create_responses_response - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() response = openai.responses.create(input="Say this is a test", model="gpt-4o") @@ -470,7 +470,7 @@ def test_basic_responses_api_call( response_text = response.output_text assert response_text == "This is a test response" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 @@ -503,7 +503,7 @@ def test_responses_api_with_tools( setup_mock_logstreams_client(mock_logstreams_client) openai_create.return_value = create_responses_response_with_tools - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() openai.responses.create( @@ -523,7 +523,7 @@ def test_responses_api_with_tools( ], ) - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 @@ -567,7 +567,7 @@ def test_responses_api_multiple_messages( setup_mock_logstreams_client(mock_logstreams_client) openai_create.return_value = create_responses_response - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() input_messages = [ @@ -585,7 +585,7 @@ def test_responses_api_multiple_messages( assert len(response.output) == 1 assert response.output[0].role == "assistant" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 @@ -630,7 +630,7 @@ def test_responses_api_streaming( response=Response(status_code=200, content=ResponsesEventStream()), ) - galileo_context.reset() + splunk_ao_context.reset() OpenAIGalileo().register_tracing() stream = openai.responses.create(input="Say hello", model="gpt-4o", stream=True) @@ -646,7 +646,7 @@ def test_responses_api_streaming( assert completed_event is not None assert completed_event.response.status == "completed" - galileo_context.flush() + splunk_ao_context.flush() payload = mock_traces_client_instance.ingest_traces.call_args[0][0] assert len(payload.traces) == 1 diff --git a/tests/test_otel.py b/tests/test_otel.py index 899b63e5..4b7dd050 100644 --- a/tests/test_otel.py +++ b/tests/test_otel.py @@ -14,7 +14,7 @@ _log_stream_context, _project_context, _session_id_context, - galileo_dataset_context, + splunk_ao_dataset_context, ) from galileo.otel import ( _TRACE_PROVIDER_CONTEXT_VAR, @@ -23,12 +23,12 @@ GalileoOTLPExporter, GalileoSpanProcessor, _set_tool_span_attributes, - start_galileo_span, + start_splunk_ao_span, ) from galileo_core.schemas.logging.span import ToolSpan if OTEL_AVAILABLE: - from galileo.otel import _set_workflow_span_attributes, start_galileo_span + from galileo.otel import _set_workflow_span_attributes, start_splunk_ao_span from galileo_core.schemas.logging.llm import Message, MessageRole from galileo_core.schemas.logging.span import WorkflowSpan from galileo_core.schemas.shared.document import Document @@ -570,7 +570,7 @@ def test_tool_span_with_output_no_tool_call_id(self): class TestStartGalileoSpan: - """Test suite for start_galileo_span context manager.""" + """Test suite for start_splunk_ao_span context manager.""" @pytest.fixture(autouse=True) def reset_trace_provider(self): @@ -580,8 +580,8 @@ def reset_trace_provider(self): _TRACE_PROVIDER_CONTEXT_VAR.set(None) @pytest.mark.skipif(not OTEL_AVAILABLE, reason="OpenTelemetry not available") - def test_start_galileo_span_dispatches_tool_span(self): - """Test that start_galileo_span routes a ToolSpan to _set_tool_span_attributes.""" + def test_start_splunk_ao_span_dispatches_tool_span(self): + """Test that start_splunk_ao_span routes a ToolSpan to _set_tool_span_attributes.""" # Given: a ToolSpan with all fields populated and a mock tracer provider tool_span = ToolSpan( name="my-tool", @@ -598,8 +598,8 @@ def test_start_galileo_span_dispatches_tool_span(self): mock_provider.get_tracer.return_value = mock_tracer _TRACE_PROVIDER_CONTEXT_VAR.set(mock_provider) - # When: using start_galileo_span with the ToolSpan - with start_galileo_span(tool_span) as span: + # When: using start_splunk_ao_span with the ToolSpan + with start_splunk_ao_span(tool_span) as span: assert span is mock_otel_span # Then: the span has gen_ai.system set and tool-specific attributes @@ -614,8 +614,8 @@ def test_start_galileo_span_dispatches_tool_span(self): assert calls["gen_ai.tool.call.id"] == "call-789" @pytest.mark.skipif(not OTEL_AVAILABLE, reason="OpenTelemetry not available") - def test_start_galileo_span_tool_span_with_none_output(self): - """Test that start_galileo_span handles a ToolSpan with None output and tool_call_id.""" + def test_start_splunk_ao_span_tool_span_with_none_output(self): + """Test that start_splunk_ao_span handles a ToolSpan with None output and tool_call_id.""" # Given: a ToolSpan with only input populated tool_span = ToolSpan(name="minimal-tool", input="just input", output=None, tool_call_id=None, status_code=200) mock_otel_span = Mock() @@ -626,8 +626,8 @@ def test_start_galileo_span_tool_span_with_none_output(self): mock_provider.get_tracer.return_value = mock_tracer _TRACE_PROVIDER_CONTEXT_VAR.set(mock_provider) - # When: using start_galileo_span with the minimal ToolSpan - with start_galileo_span(tool_span) as span: + # When: using start_splunk_ao_span with the minimal ToolSpan + with start_splunk_ao_span(tool_span) as span: assert span is mock_otel_span # Then: gen_ai.system, operation name, tool name, tool arguments, and input are set (no output or tool_call_id) @@ -737,8 +737,8 @@ def test_workflow_span_with_none_output(self, mock_dependencies): assert input_call[0][0] == "gen_ai.input.messages" @pytest.mark.skipif(not OTEL_AVAILABLE, reason="OpenTelemetry not available") - def test_workflow_span_in_start_galileo_span(self, mock_dependencies): - """Test that WorkflowSpan is handled in start_galileo_span context manager.""" + def test_workflow_span_in_start_splunk_ao_span(self, mock_dependencies): + """Test that WorkflowSpan is handled in start_splunk_ao_span context manager.""" # Given: a WorkflowSpan workflow_span = WorkflowSpan(name="test-workflow", input="input", output="output", status_code=200) mock_span = mock_dependencies["span"] @@ -754,8 +754,8 @@ def test_workflow_span_in_start_galileo_span(self, mock_dependencies): # Patch get_tracer_provider to return our mock with patch("galileo.otel.trace.get_tracer_provider", return_value=mock_trace_provider): - # When: using start_galileo_span with WorkflowSpan - with start_galileo_span(workflow_span): + # When: using start_splunk_ao_span with WorkflowSpan + with start_splunk_ao_span(workflow_span): pass # Then: WorkflowSpan attributes should be set @@ -765,7 +765,7 @@ def test_workflow_span_in_start_galileo_span(self, mock_dependencies): class TestDatasetContext: - """Tests for dataset context variables and galileo_dataset_context manager.""" + """Tests for dataset context variables and splunk_ao_dataset_context manager.""" @pytest.fixture def reset_dataset_context(self): @@ -790,15 +790,15 @@ def mock_processor_deps(self): yield {"exporter": mock_exp, "batch": mock_batch} @pytest.mark.skipif(not OTEL_AVAILABLE, reason="OpenTelemetry not available") - def test_galileo_dataset_context_sets_values(self, reset_dataset_context): - """Test that galileo_dataset_context sets context variables correctly.""" + def test_splunk_ao_dataset_context_sets_values(self, reset_dataset_context): + """Test that splunk_ao_dataset_context sets context variables correctly.""" # Given: dataset context is initially empty assert _dataset_input_context.get(None) is None assert _dataset_output_context.get(None) is None assert _dataset_metadata_context.get(None) is None # When: entering the context manager with values - with galileo_dataset_context( + with splunk_ao_dataset_context( dataset_input="test input", dataset_output="expected output", dataset_metadata={"key": "value"} ): # Then: context variables are set inside the context @@ -812,15 +812,15 @@ def test_galileo_dataset_context_sets_values(self, reset_dataset_context): assert _dataset_metadata_context.get(None) is None @pytest.mark.skipif(not OTEL_AVAILABLE, reason="OpenTelemetry not available") - def test_galileo_dataset_context_nested_contexts(self, reset_dataset_context): - """Test that nested galileo_dataset_context managers work correctly.""" + def test_splunk_ao_dataset_context_nested_contexts(self, reset_dataset_context): + """Test that nested splunk_ao_dataset_context managers work correctly.""" # Given: outer context with initial values - with galileo_dataset_context(dataset_input="outer input", dataset_output="outer output"): + with splunk_ao_dataset_context(dataset_input="outer input", dataset_output="outer output"): assert _dataset_input_context.get() == "outer input" assert _dataset_output_context.get() == "outer output" # When: entering inner context with different values - with galileo_dataset_context(dataset_input="inner input", dataset_output="inner output"): + with splunk_ao_dataset_context(dataset_input="inner input", dataset_output="inner output"): # Then: inner context values are active assert _dataset_input_context.get() == "inner input" assert _dataset_output_context.get() == "inner output" @@ -834,11 +834,11 @@ def test_galileo_dataset_context_nested_contexts(self, reset_dataset_context): assert _dataset_output_context.get(None) is None @pytest.mark.skipif(not OTEL_AVAILABLE, reason="OpenTelemetry not available") - def test_galileo_dataset_context_exception_handling(self, reset_dataset_context): + def test_splunk_ao_dataset_context_exception_handling(self, reset_dataset_context): """Test that context variables are reset even when exception occurs.""" # Given/When: an exception is raised inside the context with pytest.raises(ValueError, match="test error"): - with galileo_dataset_context(dataset_input="test", dataset_output="expected"): + with splunk_ao_dataset_context(dataset_input="test", dataset_output="expected"): assert _dataset_input_context.get() == "test" raise ValueError("test error") @@ -916,10 +916,10 @@ def test_exporter_export_merges_dataset_attributes( assert mock_span._resource == mock_merged_resource @pytest.mark.skipif(not OTEL_AVAILABLE, reason="OpenTelemetry not available") - def test_galileo_dataset_context_partial_values(self, reset_dataset_context): - """Test that galileo_dataset_context works with partial values.""" + def test_splunk_ao_dataset_context_partial_values(self, reset_dataset_context): + """Test that splunk_ao_dataset_context works with partial values.""" # When: only some values are provided - with galileo_dataset_context(dataset_output="expected only"): + with splunk_ao_dataset_context(dataset_output="expected only"): # Then: only provided values are set assert _dataset_input_context.get(None) is None assert _dataset_output_context.get() == "expected only" diff --git a/tests/utils/test_telemetry_toggle.py b/tests/utils/test_telemetry_toggle.py index 230264b5..35a1dcb1 100644 --- a/tests/utils/test_telemetry_toggle.py +++ b/tests/utils/test_telemetry_toggle.py @@ -1,11 +1,11 @@ -from galileo.utils.decorators import galileo_logging_enabled +from galileo.utils.decorators import splunk_ao_logging_enabled -def test_galileo_logging_enabled(monkeypatch) -> None: - assert galileo_logging_enabled() is True +def test_splunk_ao_logging_enabled(monkeypatch) -> None: + assert splunk_ao_logging_enabled() is True monkeypatch.setenv("GALILEO_LOGGING_DISABLED", "true") - assert galileo_logging_enabled() is False + assert splunk_ao_logging_enabled() is False monkeypatch.setenv("GALILEO_LOGGING_DISABLED", "1") - assert galileo_logging_enabled() is False + assert splunk_ao_logging_enabled() is False From bc8ce9d3d6ad21ec7f14ce3d3637b7be87bd509e Mon Sep 17 00:00:00 2001 From: shuningc Date: Tue, 16 Jun 2026 15:42:06 -0700 Subject: [PATCH 2/3] Fixing testing errors --- src/splunk_ao/__init__.py | 4 ++-- src/splunk_ao/experiments.py | 2 +- src/splunk_ao/log_stream.py | 6 +----- src/splunk_ao/openai/__init__.py | 2 +- tests/test_namespace_compatibility.py | 4 ++-- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/splunk_ao/__init__.py b/src/splunk_ao/__init__.py index f49eaba9..977afc6e 100644 --- a/src/splunk_ao/__init__.py +++ b/src/splunk_ao/__init__.py @@ -32,7 +32,7 @@ from splunk_ao.collaborator import Collaborator, CollaboratorRole from splunk_ao.configuration import Configuration from splunk_ao.dataset import Dataset -from splunk_ao.decorator import SplunkAODecorator, galileo_context, log, start_session +from splunk_ao.decorator import SplunkAODecorator, splunk_ao_context, log, start_session from splunk_ao.exceptions import ( AuthenticationError, BadRequestError, @@ -157,7 +157,7 @@ "create_protect_stage", "delete_api_key", "enable_console_logging", - "galileo_context", + "splunk_ao_context", "get_agent_control_target", "get_protect_stage", "get_tracing_headers", diff --git a/src/splunk_ao/experiments.py b/src/splunk_ao/experiments.py index d8919176..b5353107 100644 --- a/src/splunk_ao/experiments.py +++ b/src/splunk_ao/experiments.py @@ -16,7 +16,7 @@ from galileo_core.constants.request_method import RequestMethod from splunk_ao.config import SplunkAOConfig from splunk_ao.datasets import Dataset, convert_dataset_row_to_record -from splunk_ao.decorator import splunk_ao_context, splunk_ao_context_dataset_context, log +from splunk_ao.decorator import splunk_ao_context, splunk_ao_dataset_context, log from splunk_ao.experiment_tags import upsert_experiment_tag from splunk_ao.projects import Project, Projects from splunk_ao.prompts import PromptTemplate diff --git a/src/splunk_ao/log_stream.py b/src/splunk_ao/log_stream.py index 31693a34..7033d736 100644 --- a/src/splunk_ao/log_stream.py +++ b/src/splunk_ao/log_stream.py @@ -6,10 +6,6 @@ from datetime import datetime from typing import TYPE_CHECKING, Any -from galileo.config import GalileoPythonConfig -from galileo.decorator import splunk_ao_context -from galileo.export import ExportClient -from galileo.log_streams import LogStreams from galileo.resources.api.trace import ( sessions_available_columns_projects_project_id_sessions_available_columns_post, spans_available_columns_projects_project_id_spans_available_columns_post, @@ -21,7 +17,7 @@ from galileo.resources.models.log_records_available_columns_response import LogRecordsAvailableColumnsResponse from galileo.resources.types import Unset from splunk_ao.config import SplunkAOConfig -from splunk_ao.decorator import galileo_context +from splunk_ao.decorator import splunk_ao_context from splunk_ao.export import ExportClient from splunk_ao.log_streams import LogStreams from splunk_ao.schema.filters import FilterType diff --git a/src/splunk_ao/openai/__init__.py b/src/splunk_ao/openai/__init__.py index 7fb7b042..8c910c6f 100644 --- a/src/splunk_ao/openai/__init__.py +++ b/src/splunk_ao/openai/__init__.py @@ -26,7 +26,7 @@ print(response.choices[0].message.content) # You can also use it with the splunk_ao_context for more control -from splunk_ao import galileo_context +from splunk_ao import splunk_ao_context with splunk_ao_context(project="my-project", log_stream="my-log-stream"): response = openai.chat.completions.create( diff --git a/tests/test_namespace_compatibility.py b/tests/test_namespace_compatibility.py index 56c725d1..9bb3a94e 100644 --- a/tests/test_namespace_compatibility.py +++ b/tests/test_namespace_compatibility.py @@ -30,7 +30,7 @@ def test_splunk_ao_import_smoke() -> None: module = importlib.import_module("splunk_ao") assert module.__version__ - assert hasattr(module, "galileo_context") + assert hasattr(module, "splunk_ao_context") assert importlib.import_module("splunk_ao.openai") assert importlib.import_module("splunk_ao.logger") @@ -41,7 +41,7 @@ def test_legacy_galileo_import_shim_warns() -> None: with pytest.warns(DeprecationWarning, match="use 'splunk_ao' instead"): module = importlib.import_module("galileo") - assert hasattr(module, "galileo_context") + assert hasattr(module, "splunk_ao_context") def test_protected_generated_resource_imports_remain_available() -> None: From ef06d5bec2f5256f057c07e72f3f7e6599d7f9b9 Mon Sep 17 00:00:00 2001 From: shuningc Date: Tue, 16 Jun 2026 15:52:22 -0700 Subject: [PATCH 3/3] Remove auto-generated AGENTS.md Co-Authored-By: Claude --- AGENTS.md | 466 ------------------------------------------------------ 1 file changed, 466 deletions(-) delete mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index ae1bb8e5..00000000 --- a/AGENTS.md +++ /dev/null @@ -1,466 +0,0 @@ -## Project Overview - -Galileo Python SDK (`galileo` on PyPI) - the official Python client library for the Galileo AI platform. It enables logging and tracing of LLM calls, experiments, datasets, prompt management, and more. - -**Key characteristics:** -- Public SDK published to PyPI (external contributors welcome) -- Depends on `galileo-core` for shared schemas and infrastructure (see Known Issues) -- Uses auto-generated API client from OpenAPI specification -- Supports multiple LLM frameworks: OpenAI, LangChain, CrewAI, OpenAI Agents SDK - -## Build & Development Commands - -```bash -# Install dependencies (requires poetry) -poetry install --all-extras --no-root - -# Full setup (install + pre-commit hooks) -inv setup - -# Run all tests (parallel by default) -poetry run pytest - -# Run single test file -poetry run pytest tests/test_decorator.py - -# Run single test -poetry run pytest tests/test_decorator.py::test_function_name -v - -# Run tests with coverage -inv test - -# Type checking -inv type-check - -# Linting (via pre-commit) -poetry run ruff check --fix src/ -poetry run ruff format src/ -``` - -## Architecture - -### Package Structure - -``` -src/galileo/ -├── __future__/ # New object-centric API (WIP) -│ ├── project.py # Project domain object -│ ├── dataset.py # Dataset domain object -│ ├── experiment.py # Experiment domain object -│ ├── prompt.py # Prompt domain object -│ ├── log_stream.py # LogStream domain object -│ ├── configuration.py # Configuration management -│ └── shared/ # Shared utilities (filters, sorting, base classes) -├── logger/ # Core logging functionality -│ └── logger.py # GalileoLogger - central trace/span management -├── handlers/ # Framework-specific integrations -│ ├── langchain/ # LangChain callback handler (GalileoCallback) -│ ├── crewai/ # CrewAI event listener -│ └── openai_agents/ # OpenAI Agents SDK integration -├── openai/ # Drop-in OpenAI client wrapper (auto-logging) -├── resources/ # Auto-generated API client (DO NOT EDIT) -├── schema/ # Pydantic models for SDK-specific types -├── utils/ # Utility functions and helpers -├── datasets.py # Dataset service (current API) -├── experiments.py # Experiment service (current API) -├── prompts.py # Prompt service (current API) -├── projects.py # Project service (current API) -├── log_streams.py # LogStream service (current API) -├── decorator.py # @log decorator and splunk_ao_context -└── config.py # GalileoPythonConfig configuration -``` - -### Core Components - -**GalileoLogger** (`src/galileo/logger/logger.py`): Central class for uploading traces to Galileo. Supports batch and streaming modes. Manages traces, spans (LLM, retriever, tool, workflow, agent), and sessions. - -**Decorators** (`src/galileo/decorator.py`): The `@log` decorator and `splunk_ao_context` context manager for automatic function tracing. Uses ContextVars for thread-safe nested span tracking. - -**Handlers** (`src/galileo/handlers/`): Framework-specific integrations: -- `langchain/` - LangChain callback handler (`GalileoCallback`) -- `crewai/` - CrewAI handler (uses lazy imports to avoid side effects) -- `openai_agents/` - OpenAI Agents SDK integration - -**OpenAI Wrapper** (`src/galileo/openai/`): Drop-in replacement for OpenAI client that auto-logs calls. - -**`__future__` Package** (`src/galileo/__future__/`): New object-centric API implementing the "Golden Flow" patterns. Provides intuitive, Pythonic interfaces for domain objects (Project, Dataset, Prompt, Experiment, LogStream). Released incrementally as stable. - -### Auto-Generated Code - -**Resources** (`src/galileo/resources/`): Auto-generated API client from OpenAPI spec. **Excluded from linting/type-checking.** Never edit manually. - -```bash -# Regenerate API client -./scripts/import-openapi-yaml.sh https://api.galileo.ai/client -./scripts/auto-generate-api-client.sh -``` - -**Important:** The OpenAPI spec comes from the **Client API** (`/client`), not the main API (`/docs`). The Client API is a curated subset designed specifically for SDK consumption. - -### Dependency on galileo-core - -The SDK depends on `galileo-core` for shared schemas, helpers, and base classes: -- `galileo_core.schemas.logging.*` - Span types (LlmSpan, ToolSpan, etc.), Trace, Session -- `galileo_core.helpers.*` - API key management, execution utilities -- `galileo_core.schemas.protect.*` - Protection/guardrails schemas - -**Note:** There is ongoing work to reduce/eliminate this dependency. See Known Issues section. - -## Key Patterns - -### Object-Centric Design (`__future__` package) - -Domain objects follow consistent patterns: - -```python -from galileo.__future__ import Project, Dataset - -# Factory methods (class-level) -project = Project.get(name="my-project") # Retrieve existing -projects = Project.list() # List all - -# Instance creation with lifecycle -project = Project(name="new-project") # LOCAL_ONLY state -project.create() # → SYNCED state - -# Fluent creation -project = Project(name="new-project").create() # 2-in-1 - -# Relationship methods -log_streams = project.list_log_streams() -dataset = project.create_dataset(name="test-data", content=[...]) - -# Child → Parent navigation -dataset.project # Returns parent Project object -``` - -### State Management - -Objects have explicit sync states: `LOCAL_ONLY`, `SYNCED`, `DIRTY`, `FAILED_SYNC`, `DELETED` - -```python -project = Project(name="test") # LOCAL_ONLY -project.create() # → SYNCED -project.name = "renamed" # → DIRTY -project.save() # → SYNCED -project.delete() # → DELETED -``` - -### Service Layer (Current API) - -Services provide functional interfaces for those who prefer procedural style: - -```python -from galileo.datasets import create_dataset, get_dataset, list_datasets -from galileo.experiments import run_experiment - -dataset = create_dataset(name="test", content=[...]) -results = run_experiment( - experiment_name="eval-1", - dataset=dataset, - prompt_template=get_prompt(name="my-prompt"), - metrics=["correctness"], - project="my-project" -) -``` - -### Logging with Decorators - -```python -from galileo import log, splunk_ao_context - -# Auto-trace function calls -@log -def my_workflow(): - call_llm() - call_llm() - -# Explicit span types -@log(span_type="retriever") -def retrieve_docs(query: str): - return ["doc1", "doc2"] - -# Context manager for explicit control -with splunk_ao_context(project="my-project", log_stream="prod"): - my_workflow() -``` - -### Handler Integrations - -```python -# LangChain -from galileo.handlers.langchain import GalileoCallback -callback = GalileoCallback() -llm = ChatOpenAI(callbacks=[callback]) - -# CrewAI -from galileo.handlers.crewai import CrewAIEventListener -listener = CrewAIEventListener(project="my-project") -# Listener auto-registers; use auto_setup_listeners=False in tests - -# OpenAI (drop-in wrapper) -from galileo.openai import openai -client = openai.OpenAI() # Auto-logs all calls -``` - -## Testing - -Tests use pytest with these key fixtures from `tests/conftest.py`: -- `mock_request` - HTTP request mocking (from `galileo_core[testing]`) -- `mock_healthcheck`, `mock_login_api_key`, `mock_get_current_user` - Common API mocks - -### Test Environment - -Environment variables are set in `conftest.py` for pytest-xdist compatibility: -```python -GALILEO_CONSOLE_URL=http://localtest:8088 -GALILEO_API_KEY=api-1234567890 -GALILEO_PROJECT=test-project -GALILEO_LOG_STREAM=test-log-stream -``` - -Tests run with `--disable-socket` to prevent real network calls. - -### Testing Guidelines - -```python -def test_example(mock_request, mock_healthcheck, mock_login_api_key): - # Mock API responses - mock_request.post("/datasets").respond(json={"id": "123", "name": "test"}) - - # Test SDK functionality - dataset = create_dataset(name="test", content=[...]) - assert dataset.id == "123" -``` - -### Handler Testing (CrewAI) - -CrewAI imports have global side effects. Use lazy imports and `auto_setup_listeners=False`: - -```python -def test_crewai_handler(): - # Import inside test, not at module level - from galileo.handlers.crewai import CrewAIEventListener - - listener = CrewAIEventListener( - project="test", - auto_setup_listeners=False # Prevents import side effects - ) -``` - -### Given/When/Then Testing Style - -Use behavioral testing comments to structure tests clearly. Add inline comments before each section: - -- `# Given: ` - Before setup/arrangement code. Describe the preconditions. -- `# When: ` - Before the action being tested. Describe what action is performed. -- `# Then: ` - Before assertions. Describe the expected outcome. - -**Important rules:** - -- Comments must include a human-readable description after the colon - never leave them empty -- Use sentence case for descriptions (e.g., "a user with admin permissions", not "A User With Admin Permissions") -- Keep descriptions concise but meaningful -- For tests where the action raises an exception, use `# When/Then: ` combined - -```python -def test_create_project_success(mock_request, mock_healthcheck, mock_login_api_key): - # Given: a valid project name and mocked API response - mock_request.post("/projects").respond(json={"id": "123", "name": "test"}) - - # When: creating a new project - project = Project(name="test").create() - - # Then: the project is created with the expected ID - assert project.id == "123" - assert project.name == "test" -``` - -## Code Style & Conventions - -- **Line length:** 120 characters -- **Linting:** ruff (replaces flake8, isort, etc.) -- **Type annotations:** Required for public functions (mypy) -- **Docstrings:** numpy convention -- **Pre-commit hooks:** Run ruff and mypy on commit - -### Required Practices - -- Use standard Python logging: `import logging; logger = logging.getLogger(__name__)` -- Duration variables must be suffixed with units: `timeout_seconds`, `delay_ms` -- Commit messages: `type(scope): description` (conventional commits) -- **Imports at top of file**: Always place imports at the module level, not inside functions -- Exception: Lazy imports for optional dependencies (e.g., crewai) - document why -- Use `from __future__ import annotations` for forward references - -### Error Handling Architecture - -The SDK distinguishes between two types of operations with different error handling needs: - -**Resource Management Operations** (raise exceptions): -- Operations where users explicitly request an action and expect feedback -- Examples: `create_project()`, `get_dataset()`, `delete_log_stream()`, `list_projects()` -- These operations should raise exceptions on failure for clear user feedback - -**Telemetry/Ingestion Operations** (resilient): -- Background operations that observe user code without interfering -- Examples: `ingest_traces()`, `ingest_spans()`, `flush()` -- These operations swallow infrastructure errors gracefully -- Principle: Observability code should observe, not interfere - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ User Application │ -└─────────────────────────────────────────────────────────────────┘ - │ - ┌─────────────────────┴─────────────────────┐ - │ │ - ▼ ▼ -┌───────────────────┐ ┌───────────────────────┐ -│ Resource Mgmt │ │ Telemetry/Ingestion │ -│ (Raises on Error) │ │ (Resilient) │ -├───────────────────┤ ├───────────────────────┤ -│ Projects │ │ Traces.ingest_*() │ -│ Datasets │ │ Traces.update_*() │ -│ LogStreams │ │ Logger streaming │ -│ Stages │ │ @warn_catch_exception │ -└───────────────────┘ └───────────────────────┘ - │ │ - ▼ ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ Generated API Client │ -│ (Always raises HTTP exceptions) │ -└─────────────────────────────────────────────────────────────────┘ -``` - -**HTTP-Specific Exceptions:** - -| Status Code | Exception | Meaning | -|-------------|-----------|---------| -| 400 | `BadRequestError` | Invalid request parameters | -| 401 | `AuthenticationError` | Invalid or expired API key | -| 403 | `ForbiddenError` | Insufficient permissions | -| 404 | `NotFoundError` | Resource doesn't exist | -| 409 | `ConflictError` | Resource already exists | -| 422 | `HTTPValidationError` | Request body/params failed Pydantic validation | -| 429 | `RateLimitError` | Too many requests | -| 5xx | `ServerError` | Server-side error | - -**Infrastructure Exceptions** (caught only in telemetry operations): -```python -INFRASTRUCTURE_EXCEPTIONS = ( - httpx.HTTPError, - httpx.TimeoutException, - httpx.ConnectError, - ConnectionError, - TimeoutError, - OSError, -) -``` - -User errors like `TypeError`, `ValueError`, and `ValidationError` are never caught - they propagate immediately. - -### Logging Convention - -```python -import logging -logger = logging.getLogger(__name__) - -# Log lifecycle events with context -logger.info("Project.create: name=%s – started", name) -logger.info("Project.create: id=%s – completed", project_id) -logger.error("Project.update: id=%s – failed: %s", project_id, error) - -# Never log sensitive data (tokens, API keys, PII) -``` - -**When to Add Logging:** -- Service methods that perform writes (create, update, delete) -- Error conditions with full context when catching exceptions -- Long-running operations (start/completion with duration) - -**What NOT to Log:** -- Sensitive data: Passwords, API keys, tokens, PII -- Large payloads: Don't log entire request/response bodies -- High-frequency loops: Use sampling or aggregate metrics - -## Known Issues and Architectural Decisions - -### 1. galileo-core Dependency - -The SDK has a deep dependency on `galileo-core` (private repository). This creates: -- Contributor friction (requires private repo access) -- Contract split between core and SDK -- Inheritance of internal complexity - -**Mitigation in progress:** Gradual migration to OpenAPI-generated types and SDK-owned abstractions. - -### 2. Configuration State Management - -Configuration exists in three places: -- `Configuration` class attributes (new `__future__` API) -- `os.environ` (synced by Configuration) -- `GalileoPythonConfig._instance` (actual authenticated state from core) - -**Known issue:** `connect()` must be called explicitly; lazy initialization is incomplete. - -### 3. Prompt Version Management - -`Prompt.create_version()` creates a NEW prompt (name with timestamp suffix), not a new version of the same template. True version management requires API alignment. - -### 4. Dataset Version Indexing - -API uses **1-based** version indexing, not 0-based: -```python -# Correct: first version is index 1 -version_content = dataset.get_version_content(index=1) - -# Wrong: index 0 doesn't exist -version_content = dataset.get_version_content(index=0) # Raises ValueError -``` - -### 5. Experiment-Playground Conflation - -The SDK's `Experiment` class conflates two distinct API concepts: -- **Playground**: Interactive workspace for prompt iteration -- **Experiment**: Immutable logged run with recorded results - -Version specification for datasets and prompts is implicit (uses "current" version). - -### 6. Metadata Type Handling - -SDK converts all metadata values to strings. API behavior varies: -- Trace API: Skips `None` values and non-primitives -- Dataset API: Keeps `None` as null, JSON-encodes nested dicts - -## Release Process - -Releases use python-semantic-release with conventional commits: - -```bash -# Patch release triggers -fix:, perf:, chore:, docs:, style:, refactor: - -# Version is managed in: -# - src/galileo/__init__.py:__version__ -# - pyproject.toml:project.version -``` - -## Environment Variables - -| Variable | Required | Description | -|----------|----------|-------------| -| `GALILEO_CONSOLE_URL` | Yes* | Galileo console URL (default: app.galileo.ai) | -| `GALILEO_API_KEY` | Yes | API key for authentication | -| `GALILEO_PROJECT` | No | Default project name | -| `GALILEO_LOG_STREAM` | No | Default log stream name | -| `GALILEO_LOGGING_DISABLED` | No | Disable trace collection | - -*Required for non-production environments - -## References - -- **PyPI:** https://pypi.org/project/galileo/ -- **GitHub:** https://github.com/rungalileo/galileo-python -- **API Docs:** https://docs.galileo.ai -- **OpenAPI Spec:** `openapi.yaml` (generated from Client API)