← All packages

AI · Convex component

convex-langfuse

Send LLM traces, generations, and scores from your Convex app to Langfuse over OpenTelemetry, with reactive local queries.

npm install convex-langfuse
20downloads last week
669downloads last 12 months
v0.0.5latest, Sep 18, 2026
5releases since Sep 12, 2026

Downloads

Download history

Loading download history…

Build this

Build with convex-langfuse

The install and setup steps from the package documentation.

  1. 1

    Install

    bash
    npm install convex-langfuse

    Requirements: Convex v1.33.1 or later, Node.js 18+, a project (Cloud or self-hosted, OTel-compatible)

  2. 2

    Quick Start

    Three steps to send traces from your Convex app to Langfuse.

    1. Add the component

    In convex/convex.config.ts:

    ts
    import { defineApp } from "convex/server";
    import convexLangfuse from "convex-langfuse/convex.config";
    
    const app = defineApp();
    app.use(convexLangfuse);
    
    export default app;

    2. Set environment variables

    bash
    npx convex env set LANGFUSE_PUBLIC_KEY pk-lf-xxxxxxxxxxxx
    npx convex env set LANGFUSE_SECRET_KEY sk-lf-xxxxxxxxxxxx

    3. Initialize the client

    In convex/observability.ts:

    ts
    import { components } from "./_generated/api";
    import { Langfuse } from "convex-langfuse";
    
    export const langfuse = new Langfuse(components.convexLangfuse, {
      publicKey: process.env.LANGFUSE_PUBLIC_KEY!,
      secretKey: process.env.LANGFUSE_SECRET_KEY!,
      // baseUrl defaults to the EU Langfuse Cloud region — set it for US/JP/self-hosted
    });

    Import langfuse from this file in any Convex action that calls an LLM.

  3. 3

    Usage

    Log a generation

    ts
    export const chat = action({
      args: { userId: v.string(), prompt: v.string() },
      handler: async (ctx, args) => {
        const completion = await callYourModel(args.prompt);
    
        const { traceId } = await langfuse.logGeneration(ctx, {
          name: "chat-completion",
          userId: args.userId,
          model: "gpt-5",
          input: args.prompt,
          output: completion,
          inputTokens: completion.usage.inputTokens,
          outputTokens: completion.usage.outputTokens,
        });
    
        return { completion, traceId };
      },
    });
    // Returns: { traceId, observationId }

    Log a non-LLM step

    ts
    await langfuse.logSpan(ctx, {
      traceId,
      name: "vector-search",
      input: { query },
      output: { matches },
    });

    Record a score

    ts
    export const rate = action({
      args: { traceId: v.string(), value: v.number() },
      handler: async (ctx, args) => {
        await langfuse.recordScore(ctx, {
          traceId: args.traceId,
          name: "user-rating",
          value: args.value, // e.g. 1 for thumbs up, 0 for thumbs down
        });
        return null;
      },
    });

    Read a trace reactively

    ts
    export const getTrace = query({
      args: { traceId: v.string() },
      handler: async (ctx, args) => langfuse.getTrace(ctx, args),
    });
    
    export const getObservations = query({
      args: { traceId: v.string() },
      handler: async (ctx, args) => langfuse.listObservations(ctx, args),
    });