--- name: langfuse-local-dev-loop description: 'Set up Langfuse local development workflow with hot reload and debugging. Use when developing LLM applications locally, debugging traces, or setting up a fast iteration loop with Langfuse. Trigger with phrases like "langfuse local dev", "langfuse development", "debug langfuse traces", "langfuse hot reload", "langfuse dev workflow". ' allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(docker:*), Bash(pnpm:*) version: 1.0.0 license: MIT author: Jeremy Longshore tags: - saas - langfuse - llm - debugging - workflow compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw --- # Langfuse Local Dev Loop ## Overview Fast local development workflow with Langfuse tracing, immediate trace visibility, debug logging, and optional self-hosted local instance via Docker. ## Prerequisites - Completed `langfuse-install-auth` setup - Node.js 18+ with `tsx` for hot reload (`npm install -D tsx`) - Docker (optional, for self-hosted local instance) ## Instructions ### Step 1: Development Environment File ```bash # .env.local (git-ignored) LANGFUSE_PUBLIC_KEY=pk-lf-dev-... LANGFUSE_SECRET_KEY=sk-lf-dev-... LANGFUSE_BASE_URL=https://cloud.langfuse.com # Dev-specific settings NODE_ENV=development OPENAI_API_KEY=sk-... ``` ### Step 2: Dev-Optimized Langfuse Setup (v4+) ```typescript // src/lib/langfuse-dev.ts import { LangfuseSpanProcessor } from "@langfuse/otel"; import { NodeSDK } from "@opentelemetry/sdk-node"; import { LangfuseClient } from "@langfuse/client"; const isDev = process.env.NODE_ENV !== "production"; // Configure span processor with dev-friendly settings const processor = new LangfuseSpanProcessor({ // In dev: flush immediately for instant visibility ...(isDev && { exportIntervalMillis: 1000, maxExportBatchSize: 1 }), }); const sdk = new NodeSDK({ spanProcessors: [processor] }); sdk.start(); export const langfuse = new LangfuseClient(); // Print trace URLs in development export function logTrace(traceId: string) { if (isDev) { const host = process.env.LANGFUSE_BASE_URL || "https://cloud.langfuse.com"; console.log(`\n Trace: ${host}/trace/${traceId}\n`); } } // Clean shutdown process.on("SIGINT", async () => { await sdk.shutdown(); process.exit(0); }); ``` ### Step 3: Dev-Optimized Setup (v3 Legacy) ```typescript // src/lib/langfuse-dev.ts import { Langfuse } from "langfuse"; const isDev = process.env.NODE_ENV !== "production"; export const langfuse = new Langfuse({ flushAt: isDev ? 1 : 15, // Immediate flush in dev flushInterval: isDev ? 1000 : 10000, ...(isDev && { debug: true }), // Verbose SDK logging }); export function logTraceUrl(trace: ReturnType) { if (isDev) { console.log(`\n Trace: ${trace.getTraceUrl()}\n`); } } process.on("beforeExit", async () => { await langfuse.shutdownAsync(); }); ``` ### Step 4: Hot Reload Scripts ```json { "scripts": { "dev": "tsx watch --env-file=.env.local src/index.ts", "dev:debug": "DEBUG=langfuse* tsx watch --env-file=.env.local src/index.ts", "dev:trace": "LANGFUSE_DEBUG=true tsx watch --env-file=.env.local src/index.ts" } } ``` ### Step 5: Development Tracing Utilities ```typescript // src/lib/dev-utils.ts import { observe, updateActiveObservation, startActiveObservation } from "@langfuse/tracing"; // Quick traced function wrapper with console output export function devTrace Promise>( name: string, fn: T ): T { return observe({ name }, async (...args: Parameters) => { updateActiveObservation({ input: args, metadata: { env: "dev" } }); const start = Date.now(); const result = await fn(...args); const duration = Date.now() - start; updateActiveObservation({ output: result }); console.log(` [${name}] ${duration}ms`); return result; }) as T; } // Quick debug trace -- fire-and-forget diagnostic trace export async function debugTrace(name: string, data: Record) { await startActiveObservation(`debug/${name}`, async () => { updateActiveObservation({ input: data, metadata: { debug: true, timestamp: new Date().toISOString() }, }); }); } ``` ### Step 6: Example Dev Workflow ```typescript // src/index.ts import "dotenv/config"; import { initTracing, langfuse } from "./lib/langfuse-dev"; import { devTrace } from "./lib/dev-utils"; import OpenAI from "openai"; import { observeOpenAI } from "@langfuse/openai"; initTracing(); const openai = observeOpenAI(new OpenAI()); const askQuestion = devTrace("ask-question", async (question: string) => { const response = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: question }], }); return response.choices[0].message.content; }); // Run on file save (tsx watch restarts automatically) const answer = await askQuestion("What is Langfuse?"); console.log("Answer:", answer); ``` ## Local Self-Hosted Langfuse (Optional) For offline development or data privacy: ```yaml # docker-compose.langfuse.yml services: langfuse: image: langfuse/langfuse:latest ports: - "3000:3000" environment: - DATABASE_URL=postgresql://postgres:postgres@db:5432/langfuse - NEXTAUTH_SECRET=dev-secret-change-in-prod - NEXTAUTH_URL=http://localhost:3000 - SALT=dev-salt-change-in-prod - ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000 depends_on: - db db: image: postgres:16-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: langfuse volumes: - langfuse-db:/var/lib/postgresql/data volumes: langfuse-db: ``` ```bash set -euo pipefail # Start local Langfuse docker compose -f docker-compose.langfuse.yml up -d # Wait for startup, then visit http://localhost:3000 # Create account, project, and API keys in the local UI # Update .env.local echo 'LANGFUSE_BASE_URL=http://localhost:3000' >> .env.local ``` ## Error Handling | Issue | Cause | Solution | |-------|-------|----------| | Traces delayed in dev | Batching still active | Set `flushAt: 1` or `exportIntervalMillis: 1000` | | No debug output | Debug not enabled | Set `LANGFUSE_DEBUG=true` or `DEBUG=langfuse*` | | Hot reload not working | Wrong watch command | Use `tsx watch` (not `ts-node`) | | Local instance 502 | DB not ready | Wait 10s for PostgreSQL startup | | Traces going to cloud | Wrong `LANGFUSE_BASE_URL` | Point to `http://localhost:3000` | ## Resources - [TypeScript SDK Setup](https://langfuse.com/docs/observability/sdk/typescript/setup) - [Self-Hosting Docker Compose](https://langfuse.com/self-hosting/deployment/docker-compose) - [Advanced SDK Configuration](https://langfuse.com/docs/observability/sdk/typescript/advanced-usage) ## Next Steps For SDK patterns and best practices, see `langfuse-sdk-patterns`.