Langfuse source
No live Langfuse project was available while writing these docs, so every example below runs against a local fixture (examples/fixtures.py) instead of a real agentstage.from_langfuse(...) pull. The trace shape (observation hierarchy, tool call envelopes, GENERATION.input, metadata) mirrors a real Langfuse trace.get() response, and every example runs for real against it and prints real output.
How Langfuse's raw shape maps to the canonical shape
Langfuse's trace.get() returns one nested object per trace: a trace-level wrapper (carrying metadata) with an observations list inside it. agentstage.sources.langfuse.normalize_trace() turns that raw, camelCase API response into the same canonical shape every source produces (see agentstage/sources/base.py):
- The trace-level
metadata.agent_namebecomes the canonical top-levelagent_name, the grouping key used to separate multiple unrelated systems logged into the same project. - Each observation's
parentObservationIdbecomes canonicalparent_id, andstartTimebecomesstart_time. - An observation's
type == "GENERATION"passes through unchanged; this is the one type distinction reconstruction actually uses (an LLM-call span, whoseinputmay carry a system prompt). See the Braintrust source page for how a structurally different source normalizes into the exact same two-value vocabulary.
The cache on disk still holds the exact, faithful raw API response; normalization happens at read time, so a cached trace can always be re-normalized if the canonical shape ever changes, without re-pulling anything.
Connecting to a Langfuse project
agentstage.from_langfuse is the real entry point. Called with no credentials available, it fails loudly rather than silently defaulting to something:
import agentstage
try:
env = agentstage.from_langfuse(project_id="demo_project")
except Exception as e:
print(f"{type(e).__name__}: {e}")
RuntimeError: missing required Langfuse credentials: public_key, secret_key, base_url (pass explicitly or set LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY / LANGFUSE_BASE_URL)
from_langfuse takes public_key, secret_key, base_url, and project_id explicitly, or reads LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY / LANGFUSE_BASE_URL from the environment. The rest of this page, and every other doc page, uses the local fixture's Environment instead, built the same way from_langfuse builds one internally, just backed by LocalTraceSource instead of LangfuseSource:
from examples.fixtures import build_environment
env = build_environment()
print(env.list_systems())
['invoice-agent', 'support-pipeline', 'support-team', 'multi-hub']
A single Langfuse project can hold traces from several unrelated real systems. list_systems() groups the cached trace batch by the canonical agent_name and reports every group found, minus smoke-test noise (an agent_name of "test" or "test-fix", excluded by default). The fixture project here actually has five groups; "test" is filtered out.
See the core concepts page for what reconstruct() returns from here, how replay matching works, and the three framework adapters. None of it is Langfuse-specific past this point.