[OPIK-6268] [PY-SDK] feat: add environment support#6622
[OPIK-6268] [PY-SDK] feat: add environment support#6622petrotiurin wants to merge 6 commits intoboryst/OPIK-6265-be-add-environments-to-spans-traces-threadsfrom
Conversation
Adds environment to the Python SDK so traces/spans can be tagged with a lifecycle stage (e.g. development/staging/production) and the per-workspace set of environments can be managed programmatically. - Opik(environment=...) constructor; falls back to OPIK_ENVIRONMENT env var. Resolution priority: per-call > client > env var > None. - Opik.trace(...), Opik.span(...), @track(environment=...) flow through TraceData/SpanData into CreateTraceMessage/CreateSpanMessage payloads. - search_traces, search_spans, search_threads accept environment=... and inject an OQL filter on the environment field. - CRUD: create_environment, get_environment, get_environments, update_environment, delete_environment on Opik. Public opik.Environment re-export. - TS SDK and `opik configure` CLI changes are out of scope here. Tests: 10 new unit tests for resolution priority, payload propagation, @track, and back-compat. 5 new e2e tests for CRUD round-trip and search-by-environment against a local backend. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📋 PR Linter Failed❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the |
ID is not user-facing for environments — drop get_environment(id), and identify environments by name in update/delete: - update_environment(name, description=None, color=None) - delete_environment(name) — no-op when name doesn't exist create_environment still returns the created Environment (looked up internally by id after create). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📋 PR Linter Failed❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the |
…ests - Extend verify_trace with optional environment kwarg, checked only when the caller supplies it (mirrors the project_name pattern). - Add a created_envs fixture that records env names per test and deletes them on teardown — environments are workspace-capped (default 20), so leaked envs from earlier runs caused conflict errors. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📋 PR Linter Failed❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the |
verify_trace and verify_span now both check the persisted environment, which makes the search-by-environment assertions in the e2e tests redundant — those filter behaviors are exercised by other suites and unit-level coverage. Restructure the env e2e file accordingly: - Add environment kwarg to verify_span (mirrors verify_trace). - Replace the inline created_envs fixture with an environment_name fixture in e2e/conftest.py, mirroring temporary_project_name (yields a unique name, best-effort delete on teardown). - Tests now log a trace/span with environment=, then rely on verify_trace/verify_span to confirm persistence — no second round-trip through search_traces / search_spans. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📋 PR Linter Failed❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the |
📋 PR Linter Failed❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the |
…verride Once a trace has been opened with an environment, an inner @track or context-attached span can no longer override that environment. The parent trace's (or parent span's) environment wins, and the inner span's per-call environment kwarg is ignored. - span_creation_handler.create_span_respecting_context: when a parent span or trace is in context with environment set, copy that env onto start_span_arguments before building the child span. - Three new unit tests in test_span_creation_handler covering both parent-trace and parent-span branches plus the no-trace-env passthrough. - e2e test_span__... renamed/rewritten to exercise this rule via @track outer/inner pattern (replaces the prior plain-persistence test, which was strictly weaker). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📋 PR Linter Failed❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the ❌ Missing Section. The description is missing the |
Summary
environmentto the Opik Python SDK so traces/spans can be tagged with a lifecycle stage (e.g.development/staging/production).Opik(environment=...)constructor >OPIK_ENVIRONMENTenv var >None. No new field inOpikConfig, no~/.opik.configpersistence.Opik.trace,Opik.span,@track, plus search helpers (search_traces/search_spans/search_threads) all acceptenvironment=....Opik:create_environment,get_environment,get_environments,update_environment,delete_environment. Publicopik.Environmentre-export.opik configureCLI changes are intentionally out of scope.Jira: OPIK-6268
Test plan
tests/unit/api_objects/test_environment.py(10 tests) — resolution priority, payload propagation,@track(environment=...)flow, backwards-compat defaults.tests/e2e/test_environment.py(5 tests) — CRUD round-trip,search_traces(environment=...),search_spans(environment=...), constructor-set env, no-env back-compat. All pass against the local backend.🤖 Generated with Claude Code