Skip to content

feat(instrument): Langfuse adapter closure — sample + doc + LayerLens alias (lifecycle_preview → mature)#110

Closed
mmercuri wants to merge 1 commit into
feat/instrument-base-foundationfrom
feat/instrument-frameworks-langfuse-closure
Closed

feat(instrument): Langfuse adapter closure — sample + doc + LayerLens alias (lifecycle_preview → mature)#110
mmercuri wants to merge 1 commit into
feat/instrument-base-foundationfrom
feat/instrument-frameworks-langfuse-closure

Conversation

@mmercuri

Copy link
Copy Markdown
Contributor

Summary

Closes the Langfuse framework adapter to commercial-grade mature
status. It was the closest of the 13 lifecycle_preview adapters at
audit time — the source itself was already structurally complete
(1,795 LOC across 7 files, 12-test unit suite green) — so this PR
ships only the closure deltas:

  • Runnable mocked samplesamples/instrument/langfuse/
  • Reference docdocs/adapters/frameworks-langfuse.md
  • STRATIX → LayerLens deprecation alias on the package
  • Manifest relabellangfusemature in scripts/emit_adapter_manifest.py
  • Docstring sweep — ~20 STRATIX references rewritten to LayerLens, with the legacy name preserved exactly once per concept (back-compat alias mention + loop-prevention tag)

The adapter source files are copied unchanged from the orchestration
batch (feat/instrument-frameworks-orchestration → PR #96) and rebased
onto feat/instrument-base-foundation, identical to how the other M2
closure PRs (#101, #104, #108, etc.) are structured.

Sample

samples/instrument/langfuse/main.py is a fully-mocked end-to-end demo.
A small in-process _MockLangfuseClient returns two synthetic Langfuse
traces (one with a GENERATION observation, one with a TOOL SPAN)
and records every batch the export path ingests. The sample exercises
three labeled flows and exits 0:

[import] imported=2 skipped=0 failed=0 duration_ms=0.0
[import] events emitted by type: {'agent.input': 2, 'agent.output': 2, 'cost.record': 1, 'environment.config': 1, 'model.invoke': 1, 'tool.call': 1}
[export] exported=1 skipped=0 failed=0 duration_ms=0.0
[export] mock recorded 1 ingestion batch(es)
[export] batch event types: ['generation-create', 'trace-create']
[export] loop-prevention tags: ['layerlens-exported', 'stratix-exported']
[bidirectional] direction=bidirectional dry_run=True imported=0 exported=1 skipped=2 failed=0
[summary] sink recorded 8 events; sync_state imported=2 exported=1 quarantined=0

The optional LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY env vars
additionally trigger a single live connect() smoke (just the health
check) when present. Defaults to fully offline.

Doc

docs/adapters/frameworks-langfuse.md (321 lines) covers:

  • Install + quick-start (import, export, bidirectional sync)
  • Pipeline architecture diagrams for both directions, including the cursor / dedup / loop-prevention machinery
  • Bidirectional event mapping table (Langfuse GENERATIONmodel.invoke + cost.record, SPAN with metadata.type=TOOLtool.call, etc.)
  • Capability matrix
  • Version compatibility (Pydantic v2-only, Langfuse public-API forward-compat, no Langfuse Python SDK dependency — pure urllib)
  • CaptureConfig patterns
  • BYOK posture (the adapter never owns model keys — only Langfuse public/secret)
  • Replay semantics (serialize_for_replay())
  • Backward-compatibility section documenting the deprecation alias
  • Operational notes (rate limits, cursor persistence, quarantine clearance)

STRATIX → LayerLens deprecation alias

The package now exposes STRATIXLangfuseAdapter via a PEP 562
module-level __getattr__:

from layerlens.instrument.adapters.frameworks.langfuse import STRATIXLangfuseAdapter
# DeprecationWarning: STRATIXLangfuseAdapter is a deprecated alias for LangfuseAdapter
# and will be removed in v2.0. Import LangfuseAdapter from
# layerlens.instrument.adapters.frameworks.langfuse instead.

The alias resolves to LangfuseAdapter and is listed in __all__.
Unknown attribute access still raises AttributeError as expected.

Loop-prevention tags are now emitted as both layerlens-exported
(canonical) and stratix-exported (legacy alias). The importer
recognises both, so a deployment upgrade does not double-import any
trace previously exported by an older release.

Manifest

 _MATURE: set = {
     "openai",
     ...
     "smolagents",
+    # Frameworks promoted from ``lifecycle_preview`` once they ship a
+    # dedicated unit-test suite, a reference doc in ``docs/adapters/``,
+    # and a runnable mocked sample under ``samples/instrument/``.
+    "langfuse",
 }

python scripts/emit_adapter_manifest.py --stdout now emits, for the
langfuse entry:

{
  "key": "langfuse",
  "framework": "langfuse",
  "category": "framework",
  "class_name": "LangfuseAdapter",
  "version": "0.1.0",
  "extras": ["langfuse-importer"],
  "maturity": "mature",
  "requires_pydantic": "v2_only",
  "capabilities": ["trace_tools", "trace_models", "replay"],
  "description": "Bidirectional trace sync between LayerLens and Langfuse"
}

Acceptance

  • uv run pytest tests/instrument/adapters/frameworks/test_langfuse_adapter.py -x12 passed
  • uv run mypy --strict src/layerlens/instrument/adapters/frameworks/langfuseSuccess: no issues found in 8 source files
  • uv run ruff check src/.../langfuse tests/.../test_langfuse_adapter.pyAll checks passed
  • python -m samples.instrument.langfuse.main → exit 0, all three labeled flows + summary
  • uv run pytest tests/instrument/test_default_install.py tests/instrument/test_lazy_imports.py6 passed (default-install + lazy-import guards intact; [langfuse-importer] extra is empty so the default pip install layerlens set is unchanged)
  • uv run mypy --strict src (whole src tree) → Success: no issues found in 99 source files

Stacks on

feat/instrument-base-foundation (M1.A, PR #93) — required for the
BaseAdapter surface this adapter consumes.

Test plan

  • CI: mypy --strict clean on the new package
  • CI: ruff check clean on the new package + test
  • CI: pytest green on the 12-test langfuse adapter suite
  • CI: default-install guard green — confirms the langfuse-importer extra adds zero deps
  • Reviewer: read docs/adapters/frameworks-langfuse.md end-to-end against the Langfuse public API docs
  • Reviewer: confirm the PEP 562 __getattr__ deprecation pattern matches the rename convention adopted elsewhere in the M2 batch (cf. to_stratix_policy in agentforce)
  • Reviewer: spot-check that the loop-prevention tag round-trip recognises both layerlens-exported (new) and stratix-exported (legacy)

… alias (lifecycle_preview → mature)

Closes the Langfuse framework adapter to commercial-grade ``mature``
status. The adapter source itself was already structurally complete
(1,795 LOC across 7 files, 12-test unit suite green). This PR adds the
three deliverables required to graduate it from ``lifecycle_preview``:

  1. Runnable mocked sample (``samples/instrument/langfuse/``)
  2. Reference doc (``docs/adapters/frameworks-langfuse.md``)
  3. STRATIX → LayerLens deprecation alias on the adapter package

…and relabels the manifest entry accordingly.

Sample
------

``samples/instrument/langfuse/main.py`` is a fully-mocked end-to-end
demo of ``import_traces``, ``export_traces``, and ``sync(direction=
BIDIRECTIONAL)``. It stands in for ``LangfuseAPIClient`` with an
in-process fixture that returns two synthetic Langfuse traces (one
with a ``GENERATION`` observation, one with a ``TOOL`` ``SPAN``) and
records every batch ingested by the export path. The sample never
touches the network and exits 0 in CI; an optional ``LANGFUSE_*``
env-var path additionally exercises a single live ``connect()`` smoke
when credentials are present.

Doc
---

``docs/adapters/frameworks-langfuse.md`` covers install, quick-start,
the import / export pipeline architecture, the bidirectional event
mapping (Langfuse ↔ LayerLens canonical events), the capability
matrix, version compatibility (Pydantic v2-only, Langfuse public-API
forward-compat, no Langfuse Python SDK dependency), capture-config
patterns, BYOK posture, replay semantics, the backward-compatibility
aliases, and operational notes (rate limits, cursor persistence,
quarantine).

LayerLens rename + back-compat alias
------------------------------------

* ``STRATIXLangfuseAdapter`` is now a deprecated module alias for
  ``LangfuseAdapter``, resolved via PEP 562 ``__getattr__`` with a
  ``DeprecationWarning`` and slated for removal in v2.0.
* All ``STRATIX`` mentions in adapter docstrings have been swept to
  ``LayerLens``; the legacy name is preserved in one place per file
  where it documents the back-compat alias.
* The adapter ``AdapterInfo`` now reports ``author='LayerLens Team'``
  and ``description='Bidirectional trace sync between LayerLens and
  Langfuse'``.
* Exported traces are tagged with **both** ``layerlens-exported``
  (canonical) and ``stratix-exported`` (legacy alias) so a deployment
  upgrade does not double-import previously-exported traces. The
  importer recognises both tags for loop prevention.

Manifest
--------

``scripts/emit_adapter_manifest.py`` now lists ``langfuse`` in
``_MATURE`` so the atlas-app catalog labels the adapter ``mature``
instead of ``smoke_only``. The full manifest entry resolves with
``class_name='LangfuseAdapter'``, ``capabilities=['trace_tools',
'trace_models', 'replay']``, ``requires_pydantic='v2_only'``,
``extras=['langfuse-importer']``.

Acceptance
----------

* ``uv run pytest tests/instrument/adapters/frameworks/test_langfuse_adapter.py -x`` → **12 passed**
* ``uv run mypy --strict src/layerlens/instrument/adapters/frameworks/langfuse`` → **Success: no issues found in 8 source files**
* ``uv run ruff check src/.../langfuse tests/.../test_langfuse_adapter.py`` → **All checks passed**
* ``python -m samples.instrument.langfuse.main`` → exit 0, prints all three labeled flows + summary
* ``uv run pytest tests/instrument/test_default_install.py tests/instrument/test_lazy_imports.py`` → **6 passed** (default-install + lazy-import guards intact; the ``[langfuse-importer]`` extra is empty so the default install set is unchanged)

Stacks on
---------

``feat/instrument-base-foundation`` (M1.A) — required for the
``BaseAdapter`` surface this adapter consumes.

Closes the langfuse leg of the lifecycle_preview → mature audit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants