AETIUS

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_name becomes the canonical top-level agent_name, the grouping key used to separate multiple unrelated systems logged into the same project.
  • Each observation's parentObservationId becomes canonical parent_id, and startTime becomes start_time.
  • An observation's type == "GENERATION" passes through unchanged; this is the one type distinction reconstruction actually uses (an LLM-call span, whose input may 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:

Python
import agentstage
try:
    env = agentstage.from_langfuse(project_id="demo_project")
except Exception as e:
    print(f"{type(e).__name__}: {e}")
Output
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:

Python
from examples.fixtures import build_environment
env = build_environment()
print(env.list_systems())
Output
['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.