Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Langfuse Bundle Laravel Package

lingoda/langfuse-bundle

Integrates Langfuse observability into your PHP application, letting you trace LLM calls, log prompts and completions, and monitor latency and cost. Provides configurable clients and automatic instrumentation so you can debug AI features in production.

View on GitHub
Deep Wiki
Context7

Getting Started

Treat this as a thin Symfony integration for Langfuse, not a full observability platform. The first hour should be spent confirming what the bundle actually registers, because the package is early-stage and the public API is likely smaller than the README implies.

Minimal setup:

composer require lingoda/langfuse-bundle

Register the bundle if Flex does not do it automatically:

// config/bundles.php
return [
    // ...
    Lingoda\LangfuseBundle\LangfuseBundle::class => ['all' => true],
];

Configure credentials. Verify the exact config root and key names in vendor/lingoda/langfuse-bundle/src/DependencyInjection/Configuration.php; the shape below is the common Langfuse convention:

LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://cloud.langfuse.com
LANGFUSE_ENABLED=1
# config/packages/langfuse.yaml
langfuse:
    public_key: '%env(LANGFUSE_PUBLIC_KEY)%'
    secret_key: '%env(LANGFUSE_SECRET_KEY)%'
    host: '%env(LANGFUSE_HOST)%'
    enabled: '%env(bool:LANGFUSE_ENABLED)%'

Where to look first:

  1. README.md and any docs/ in the vendor package.
  2. src/DependencyInjection/Configuration.php for the real config tree.
  3. src/Resources/config/services.yaml for service IDs, aliases, and autowiring.
  4. bin/console debug:container --show-private | grep -i langfuse to find the injectable client/tracer service.

First use case: trace one LLM call end-to-end. Wrap the bundle client in your own small adapter so the rest of the application does not depend directly on the bundle:

final class LlmTracer
{
    public function __construct(
        private readonly object $langfuse, // replace with the real client type from debug:container
    ) {}

    public function traceChat(string $userId, array $messages): string
    {
        $trace = $this->langfuse->trace([
            'name' => 'support-chat',
            'userId' => $userId,
        ]);

        try {
            $generation = $trace->generation([
                'name' => 'openai.chat',
                'model' => 'gpt-4o-mini',
                'input' => $messages,
            ]);

            $answer = '...'; // call your LLM client here

            $generation->end([
                'output' => $answer,
                'usage' => ['input' => 120, 'output' => 80],
            ]);

            return $answer;
        } finally {
            $trace->end();
        }
    }
}

Then inject LlmTracer into the service or controller that owns the LLM call. For a Laravel developer, the mental shift is: no facade, no config/langfuse.php; you inject a Symfony service and configure through the bundle extension.

Implementation Patterns

Wrap the SDK in an application-owned adapter. Do not scatter bundle/SDK calls through controllers. Create LlmTracer, PromptRepository, or EvaluationRecorder services. This gives you one place to handle flushing, redaction, sampling, and SDK upgrades.

Use one trace per request or job, spans per step, generations per model call. A typical chat endpoint should look like:

  • Trace: user ID, session ID, tenant, feature name.
  • Span: retrieval, prompt compilation, tool call.
  • Generation: model, input messages, output, token usage, latency.
  • Score: user feedback or automated evaluation, attached to the trace.

Propagate trace context through queues and HTTP calls. Symfony Messenger jobs lose the request scope. Put the trace ID in the message envelope or payload, then rebuild a child trace/span in the handler. For outbound HTTP calls, pass the trace ID as metadata so downstream services can correlate logs.

Use the bundle's prompt-management features only behind a fallback. If the bundle exposes Langfuse prompt fetching, treat Langfuse as a remote config source:

$prompt = $this->prompts->get('support-reply', version: 3);
$compiled = $prompt->compile(['customer_name' => $name]);

Always have a local fallback template and a short cache TTL. A Langfuse outage should degrade prompt quality, not take down the endpoint.

Flush at the right lifecycle boundary. Langfuse SDKs usually buffer events and send them asynchronously or in batches. In Symfony, add a kernel.terminate listener that calls the client's flush method if the bundle does not already do it. In long-running Messenger workers, flush after each message or small batch; otherwise traces sit in memory until the worker exits.

Disable tracing in tests and local noise. Set langfuse.enabled: 0 in config/packages/test/langfuse.yaml, or bind a fake client in the test container. Assert on your own adapter's recorded events rather than making network calls to Langfuse.

Record scores where the feedback lives. User thumbs-up/down, eval jobs, and human review should call the Langfuse scoring API with the trace ID. Keep scoring out of the request path if the API call is slow; dispatch a Messenger message instead.

Control cardinality and payload size. Do not create a span for every trivial function. Trace the LLM boundary, retrieval, tool calls, and expensive pre/post-processing. Truncate prompts and completions before sending if they can be large.

Gotchas and Tips

  • This is a Symfony bundle, not a Laravel package. There is no Laravel service provider, facade, or published Laravel config. In a Laravel codebase, you will either port the container wiring or use the underlying Langfuse PHP SDK/API directly. Do not expect config/langfuse.php to exist.
  • Adoption is extremely low. With one star and no dependents, assume documentation gaps and possible API churn. Pin the version in composer.json, read the source before upgrading, and check the bundle's Symfony and PHP version constraints.
  • The service ID may not be autowirable by a friendly alias. Run bin/console debug:container --show-private | grep -i langfuse and inject the exact FQCN or alias. If autowiring fails, define an explicit alias in config/services.yaml.
  • Config cache hides mistakes. After changing bundle config or env vars, run bin/console cache:clear and bin/console debug:config langfuse. Use bin/console lint:container to catch wiring errors before deploy.
  • Silent trace loss is the most common failure. If the process exits before the buffer flushes, traces disappear. Verify the flush listener exists, and test by sending a trace and checking the Langfuse UI. In workers and console commands, call flush explicitly.
  • Network calls can add latency. Langfuse ingestion is usually asynchronous, but prompt fetching and scoring are synchronous. Set HTTP timeouts, fail open when Langfuse is unavailable, and never let observability block the user response.
  • Check authentication and host configuration first when traces do not appear. A wrong host, trailing slash issue, or missing secret key often produces a 401/403 that only shows up in logs. Enable debug logging for the bundle's HTTP client during initial setup.
  • Redact PII before it leaves the application. Prompts and completions frequently contain emails, names, and support ticket contents. Add a redaction layer in your adapter, or disable input/output capture for sensitive features.
  • Use try/finally around generations and spans. Unclosed spans produce incomplete traces. If an LLM call throws, end the generation with error metadata, then rethrow so your application error handling still works.
  • Do not reuse a trace across requests or jobs. Create a fresh trace per unit of work. Reusing trace objects in long-running processes leads to mixed metadata and confusing Langfuse sessions.
  • Extension points are likely limited. If the bundle does not expose the transport or HTTP client, decorate the bundle service in config/services.yaml rather than forking the package. A decorator around your own adapter is usually more stable than decorating the vendor service.
  • Compare against using the SDK directly. If the bundle only registers one or two services, the integration may not be worth the extra dependency. For a small Symfony app, constructing the Langfuse client in services.yaml may be simpler and easier to upgrade.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin