sdks/python/examples/agent_config_demo.ipynb
This notebook walks through the Opik Agent Config API:
get_or_create_config — fetch from the backend, auto-creating from a fallback when nothing exists yetget_or_create_config with fallback only (returns fallback when backend is unreachable or empty)create_config — unconditionally write a new config versionset_config_env — tag a version with an environment name (e.g. "prod", "staging")env and by explicit version name%pip install opik --quiet
import uuid
from typing import Optional
import opik
from opik.api_objects.agent_config.cache import get_global_registry
# Configure once — reads OPIK_API_KEY and OPIK_URL_OVERRIDE from env if set.
# opik.configure(use_local=True) # swap for opik.configure() when using Opik Cloud
client = opik.Opik()
# Give each demo run its own project so configs don't bleed between runs.
PROJECT = f"agent-config-demo-{uuid.uuid4().hex[:8]}"
print(f"Project: {PROJECT}")
opik.Config is a Pydantic-based model. Subclass it to declare the fields your agent needs.
class AgentConfig(opik.Config):
temperature: float
model: str
system_prompt: Optional[str] = None
get_or_create_config — first call auto-creates from fallbackThe project has no config yet. get_or_create_config detects this and writes the
fallback values as the first version. The backend automatically tags the first
version as "prod".
FALLBACK_V1 = AgentConfig(
temperature=0.5,
model="gpt-3.5-turbo",
system_prompt="You are a helpful assistant.",
)
# get_or_create_config must be called from inside an @opik.track function.
@opik.track(project_name=PROJECT)
def run_agent(user_message: str):
cfg = client.get_or_create_config(
fallback=AgentConfig(
temperature=0.54,
model="gpt-3.5aaaa-turbo",
system_prompt="You are a helpful assistant.",
), # optional, but preferred
project_name=PROJECT,
)
# cfg is an AgentConfig instance because we passed a typed fallback.
print(f" is_fallback : {cfg.is_fallback}")
print(f" temperature : {cfg.temperature}")
print(f" model : {cfg.model}")
print(f" system_prompt: {cfg.system_prompt}")
return cfg
print("=== First call — auto-creates from fallback ===")
cfg_v1 = run_agent("Hello!")
assert cfg_v1.is_fallback is False, (
"auto-created config should NOT be marked as fallback"
)
get_or_create_config — returns fallback when backend unreachableIf the backend times out or is unreachable and a fallback is provided,
get_or_create_config returns the fallback with is_fallback=True instead of
raising an error. We simulate this by passing an unreachable URL.
# Point a second client at a non-existent host to force a timeout.
unreachable_client = opik.Opik(
host="http://127.0.0.1:19999", # nothing listening here
api_key="demo",
)
@opik.track(project_name=PROJECT)
def run_agent_offline(user_message: str):
cfg = unreachable_client.get_or_create_config(
fallback=FALLBACK_V1,
project_name=PROJECT,
timeout_in_seconds=2,
)
print(f" is_fallback : {cfg.is_fallback}")
print(f" temperature : {cfg.temperature} (from local fallback)")
print(f" model : {cfg.model}")
return cfg
print("=== Call against unreachable backend — returns fallback ===")
offline_cfg = run_agent_offline("Hello offline!")
assert offline_cfg.is_fallback is True, (
"should be marked as fallback when backend is unreachable"
)
create_config — write a new version unconditionallycreate_config does not require a @opik.track context and always creates a new
version. It returns the version name (a string) that you can use later with
set_config_env or to fetch by explicit version.
v2 = AgentConfig(
temperature=0.8,
model="gpt-4o",
system_prompt="You are an expert assistant. Think step by step.",
)
v2_name = client.create_config(
v2,
project_name=PROJECT,
description="Upgraded to gpt-4o with chain-of-thought prompt",
)
print(f"Created version: {v2_name!r}")
assert isinstance(v2_name, str) and v2_name != ""
set_config_env — tag a version with an environmentRight now "prod" still points to the v1 values (auto-tagged by the backend on first
write). We promote v2 to "prod" with set_config_env.
client.set_config_env(
project_name=PROJECT,
version=v2_name,
env="prod",
)
print(f"Version {v2_name!r} is now tagged as 'prod'")
# Also tag the same version as 'staging' to show multi-env support.
client.set_config_env(
project_name=PROJECT,
version=v2_name,
env="staging",
)
print(f"Version {v2_name!r} is now also tagged as 'staging'")
env — confirm prod now returns v2 values# Clear cache so we hit the backend, not a locally cached copy.
# get_global_registry().clear()
@opik.track(project_name=PROJECT)
def fetch_prod():
return client.get_or_create_config(
fallback=FALLBACK_V1,
project_name=PROJECT,
env="prod",
)
print("=== Fetch env='prod' (should return v2 after set_config_env) ===")
prod_cfg = fetch_prod()
print(f" temperature : {prod_cfg.temperature}")
print(f" model : {prod_cfg.model}")
print(f" system_prompt: {prod_cfg.system_prompt}")
assert prod_cfg.temperature == 0.8
assert prod_cfg.model == "gpt-4o"
version name@opik.track(project_name=PROJECT)
def fetch_by_version(version_name: str):
return client.get_or_create_config(
fallback=FALLBACK_V1,
project_name=PROJECT,
version=version_name,
)
print(f"=== Fetch by explicit version {v2_name!r} ===")
by_name_cfg = fetch_by_version(v2_name)
print(f" temperature : {by_name_cfg.temperature}")
print(f" model : {by_name_cfg.model}")
assert by_name_cfg.temperature == 0.8
assert by_name_cfg.model == "gpt-4o"
Config return type)Omitting fallback returns a base opik.Config instance. Typed field access still
works through attribute lookup, but you lose static type-checking of the subclass.
get_global_registry().clear()
@opik.track(project_name=PROJECT)
def fetch_no_fallback():
# No fallback — returns opik.Config, raises ConfigNotFound if project is empty.
return client.get_or_create_config(project_name=PROJECT)
print("=== Fetch without fallback ===")
no_fallback_cfg = fetch_no_fallback()
print(f" type : {type(no_fallback_cfg).__name__}")
print(f" is_fallback : {no_fallback_cfg.is_fallback}")
print(f" temperature : {no_fallback_cfg.temperature}")
print(f" model : {no_fallback_cfg.model}")
assert type(no_fallback_cfg) is opik.Config
assert no_fallback_cfg.is_fallback is False
# Values come from the prod version (v2).
assert no_fallback_cfg.temperature == by_name_cfg.temperature
assert no_fallback_cfg.model == by_name_cfg.model
Delete the demo project so it doesn't clutter the workspace.
from opik.rest_api import core as rest_api_core
try:
project_id = client.rest_client.projects.retrieve_project(name=PROJECT).id
client.rest_client.projects.delete_project_by_id(project_id)
print(f"Deleted project {PROJECT!r}")
except rest_api_core.ApiError as e:
print(f"Could not delete project: {e}")