From 0d570068875f3813ae1328ebdaeee66608d7a8bb Mon Sep 17 00:00:00 2001 From: phucbm Date: Tue, 9 Jun 2026 10:21:10 +0700 Subject: [PATCH 1/3] feat(chat): migrate Langfuse tracing to OTEL + add user feedback - Add instrumentation.ts: NodeTracerProvider + LangfuseSpanProcessor - Rewrite api/chat/route.ts: observe() wrapper, propagateAttributes(), experimental_telemetry, getActiveTraceId() injected into messageMetadata - Remove manual sendChatTrace() HTTP calls from lib/langfuse.ts - Add postFeedbackScore() using POST /api/public/scores (not batch ingestion) - Add /api/chat/feedback proxy route (secret key server-side) - Add FeedbackAdapter to useChatRuntime, reads traceId from message metadata - Add ActionBarPrimitive.FeedbackPositive/Negative buttons to AssistantActionBar - Install: @opentelemetry/sdk-trace-node, @opentelemetry/api, langfuse Co-Authored-By: Claude Sonnet 4.6 --- app/api/chat/feedback/route.ts | 18 ++ app/api/chat/route.ts | 177 ++++++------ components/assistant-ui/thread.tsx | 12 + components/chat/chat-runtime.tsx | 23 +- instrumentation.ts | 10 + lib/langfuse.ts | 53 +--- package.json | 3 + pnpm-lock.yaml | 443 +++++++++++++++-------------- 8 files changed, 406 insertions(+), 333 deletions(-) create mode 100644 app/api/chat/feedback/route.ts create mode 100644 instrumentation.ts diff --git a/app/api/chat/feedback/route.ts b/app/api/chat/feedback/route.ts new file mode 100644 index 00000000..4d96ef35 --- /dev/null +++ b/app/api/chat/feedback/route.ts @@ -0,0 +1,18 @@ +import { postFeedbackScore } from '@/lib/langfuse'; + +export async function POST(req: Request) { + const body = await req.json() as { traceId?: string; value?: number; comment?: string }; + + if (!body.traceId || typeof body.value !== 'number') { + return new Response(JSON.stringify({ error: 'traceId and value required' }), { + status: 400, + headers: { 'Content-Type': 'application/json' }, + }); + } + + const value = body.value === 1 ? 1 : 0; + + await postFeedbackScore(body.traceId, value, body.comment); + + return new Response(null, { status: 204 }); +} diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index c7d7b961..0f3bdb07 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,10 +1,13 @@ import { createOpenAI } from '@ai-sdk/openai'; import { createMCPClient } from '@ai-sdk/mcp'; import { streamText, stepCountIs, convertToModelMessages, type UIMessage } from 'ai'; +import { after } from 'next/server'; +import { observe, propagateAttributes, setActiveTraceIO, getActiveTraceId } from '@langfuse/tracing'; +import { trace } from '@opentelemetry/api'; import { getSystemPrompt } from '@/lib/chat/system-prompt'; -import { sendChatTrace } from '@/lib/langfuse'; -import { isAllowedModel, getDefaultModel, getModelById } from '@/lib/chat/models'; +import { isAllowedModel, getDefaultModel } from '@/lib/chat/models'; import type { PageContext } from '@/lib/chat/page-context'; +import { langfuseSpanProcessor } from '@/instrumentation'; import fs from 'fs'; import path from 'path'; @@ -43,7 +46,7 @@ const openrouter = createOpenAI({ apiKey: process.env.OPENROUTER_API_KEY, }); -export async function POST(req: Request) { +const handler = async (req: Request) => { const forwarded = req.headers.get('x-forwarded-for'); const ip = forwarded ? forwarded.split(',')[0].trim() : 'unknown'; @@ -64,86 +67,98 @@ export async function POST(req: Request) { const lastUserMessage = uiMessages.findLast((m) => m.role === 'user')?.parts ?.filter((p) => p.type === 'text').map((p) => p.text).join('') ?? ''; - const { text: systemPrompt, version: promptVersion } = await getSystemPrompt(body.pageContext); + const { text: systemPrompt } = await getSystemPrompt(body.pageContext); + + setActiveTraceIO({ input: lastUserMessage }); + + return await propagateAttributes( + { + traceName: 'chat', + sessionId: body.sessionId, + userId: body.userId, + tags: ['web-chat'], + metadata: { model, ip }, + }, + async () => { + let mcpClient: Awaited> | null = null; + + try { + mcpClient = await createMCPClient({ + transport: { + type: 'http', + url: process.env.OPENWALLET_MCP_URL ?? 'http://localhost:8001', + headers: { + 'x-mcp-key': process.env.OPENWALLET_MCP_KEY ?? '', + }, + }, + }); - let mcpClient: Awaited> | null = null; + const tools = await mcpClient.tools(); + const traceId = getActiveTraceId(); + + const result = streamText({ + model: openrouter(model), + system: systemPrompt, + messages, + stopWhen: stepCountIs(5), + tools, + experimental_telemetry: { isEnabled: true }, + onFinish: async ({ usage, text, finishReason, steps }) => { + await mcpClient?.close(); + + setActiveTraceIO({ output: text }); + trace.getActiveSpan()?.end(); + + appendChatLog({ + ts: new Date().toISOString(), + sessionId: body.sessionId, + userId: body.userId, + ip, + model, + messages: uiMessages.slice(-12), + steps: steps?.map((s) => ({ + text: s.text, + toolCalls: s.toolCalls, + toolResults: s.toolResults, + })), + finalText: text, + inputTokens: usage?.inputTokens ?? 0, + outputTokens: usage?.outputTokens ?? 0, + latencyMs: Date.now() - startTime, + finishReason: finishReason ?? 'unknown', + }, body.sessionId); + }, + onError: async ({ error }) => { + console.error('[chat] streamText error:', error); + await mcpClient?.close(); + trace.getActiveSpan()?.end(); + }, + }); - try { - mcpClient = await createMCPClient({ - transport: { - type: 'http', - url: process.env.OPENWALLET_MCP_URL ?? 'http://localhost:8001', - headers: { - 'x-mcp-key': process.env.OPENWALLET_MCP_KEY ?? '', - }, - }, - }); - - const tools = await mcpClient.tools(); - - const result = streamText({ - model: openrouter(model), - system: systemPrompt, - messages, - stopWhen: stepCountIs(5), - tools, - onFinish: async ({ usage, text, finishReason, steps }) => { - await mcpClient?.close(); - appendChatLog({ - ts: new Date().toISOString(), - sessionId: body.sessionId, - userId: body.userId, - ip, - model, - messages: uiMessages.slice(-12), - steps: steps?.map((s) => ({ - text: s.text, - toolCalls: s.toolCalls, - toolResults: s.toolResults, - })), - finalText: text, - inputTokens: usage?.inputTokens ?? 0, - outputTokens: usage?.outputTokens ?? 0, - latencyMs: Date.now() - startTime, - finishReason: finishReason ?? 'unknown', - }, body.sessionId); - await sendChatTrace({ - input: lastUserMessage, - output: text, - model, - tokens: { - input: usage?.inputTokens ?? 0, - output: usage?.outputTokens ?? 0, + result.consumeStream(); // no await - ensures onFinish fires even if client disconnects + + after(async () => await langfuseSpanProcessor.forceFlush()); + + return result.toUIMessageStreamResponse({ + messageMetadata: ({ part }) => { + if (part.type === 'finish') return { custom: { usage: part.totalUsage, traceId } }; + if (part.type === 'finish-step') return { modelId: part.response.modelId }; + return undefined; }, - latencyMs: Date.now() - startTime, - finishReason: finishReason ?? 'unknown', - steps: steps?.length ?? 0, - promptVersion, - userId: body.userId, - sessionId: body.sessionId, }); - }, - onError: async ({ error }) => { - console.error('[chat] streamText error:', error); + } catch (err) { + console.error('[chat] fatal error:', err); await mcpClient?.close(); - }, - }); - - result.consumeStream(); // no await - ensures onFinish fires even if client disconnects - - return result.toUIMessageStreamResponse({ - messageMetadata: ({ part }) => { - if (part.type === 'finish') return { custom: { usage: part.totalUsage } }; - if (part.type === 'finish-step') return { modelId: part.response.modelId }; - return undefined; - }, - }); - } catch (err) { - console.error('[chat] fatal error:', err); - await mcpClient?.close(); - return new Response( - JSON.stringify({ error: String(err) }), - { status: 500, headers: { 'Content-Type': 'application/json' } } - ); - } -} + return new Response( + JSON.stringify({ error: String(err) }), + { status: 500, headers: { 'Content-Type': 'application/json' } } + ); + } + } + ); +}; + +export const POST = observe(handler, { + name: 'handle-chat-message', + endOnExit: false, +}); diff --git a/components/assistant-ui/thread.tsx b/components/assistant-ui/thread.tsx index be7a36ad..34e14de0 100644 --- a/components/assistant-ui/thread.tsx +++ b/components/assistant-ui/thread.tsx @@ -42,6 +42,8 @@ import { PencilIcon, RefreshCwIcon, SquareIcon, + ThumbsDownIcon, + ThumbsUpIcon, } from "lucide-react"; import {type FC, useEffect, useRef, useState} from "react"; import {getContextPlaceholders, type PageContext} from "@/lib/chat/page-context"; @@ -483,6 +485,16 @@ const AssistantActionBar: FC = () => { + + + + + + + + + + ({ +const chatMessageMetadataSchema = jsonSchema<{ usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number; reasoningTokens?: number; cachedInputTokens?: number }; modelId?: string; custom?: { usage?: object; traceId?: string } }>({ type: 'object', properties: { usage: { @@ -24,6 +24,13 @@ const chatMessageMetadataSchema = jsonSchema<{ usage?: { inputTokens?: number; o }, }, modelId: { type: 'string' }, + custom: { + type: 'object', + properties: { + usage: { type: 'object' }, + traceId: { type: 'string' }, + }, + }, }, }); @@ -72,6 +79,20 @@ export function ChatRuntime({ }), messageMetadataSchema: chatMessageMetadataSchema, messages: initialMessages, + adapters: { + feedback: { + submit: async ({ type, message }) => { + const traceId = (message.metadata as { custom?: { traceId?: string } } | undefined) + ?.custom?.traceId; + if (!traceId) return; + await fetch('/api/chat/feedback', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ traceId, value: type === 'positive' ? 1 : 0 }), + }); + }, + }, + }, onFinish: ({ messages }) => { setChatError(null); debouncedSave(messages); diff --git a/instrumentation.ts b/instrumentation.ts new file mode 100644 index 00000000..b10fa16b --- /dev/null +++ b/instrumentation.ts @@ -0,0 +1,10 @@ +import { LangfuseSpanProcessor } from '@langfuse/otel'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; + +export const langfuseSpanProcessor = new LangfuseSpanProcessor(); + +const tracerProvider = new NodeTracerProvider({ + spanProcessors: [langfuseSpanProcessor], +}); + +tracerProvider.register(); diff --git a/lib/langfuse.ts b/lib/langfuse.ts index 0a430f76..3c843ccf 100644 --- a/lib/langfuse.ts +++ b/lib/langfuse.ts @@ -32,63 +32,26 @@ export async function fetchSystemPrompt(): Promise<{ text: string; version: numb promptCache = { text: data.prompt, version: data.version, fetchedAt: Date.now() }; return { text: data.prompt, version: data.version }; } catch { - // fallback: return cached if exists, else null signals caller to use hardcoded if (promptCache) return { text: promptCache.text, version: promptCache.version }; return { text: '', version: 0 }; } } -// ─── Tracing ────────────────────────────────────────────────────────────────── +// ─── User feedback scores ───────────────────────────────────────────────────── -export interface ChatTraceOptions { - input: string; - output: string; - model: string; - tokens: { input: number; output: number }; - latencyMs: number; - finishReason: string; - steps: number; - promptVersion?: number; - userId?: string; - sessionId?: string; -} - -export async function sendChatTrace(opts: ChatTraceOptions): Promise { - await fetch(`${baseUrl()}/api/public/ingestion`, { +export async function postFeedbackScore(traceId: string, value: 0 | 1, comment?: string): Promise { + await fetch(`${baseUrl()}/api/public/scores`, { method: 'POST', headers: { 'Authorization': basicAuth(), 'Content-Type': 'application/json', }, body: JSON.stringify({ - batch: [{ - id: crypto.randomUUID(), - type: 'trace-create', - timestamp: new Date().toISOString(), - body: { - id: crypto.randomUUID(), - name: 'chat', - input: opts.input, - output: opts.output, - usage: { - input: opts.tokens.input, - output: opts.tokens.output, - total: opts.tokens.input + opts.tokens.output, - unit: 'TOKENS', - }, - ...(opts.userId ? { userId: opts.userId } : {}), - ...(opts.sessionId ? { sessionId: opts.sessionId } : {}), - metadata: { - model: opts.model, - tokens: opts.tokens, - latencyMs: opts.latencyMs, - finishReason: opts.finishReason, - steps: opts.steps, - promptVersion: opts.promptVersion, - }, - tags: ['web-chat'], - }, - }], + traceId, + name: 'user-feedback', + value, + dataType: 'NUMERIC', + ...(comment ? { comment } : {}), }), }); } diff --git a/package.json b/package.json index 58e7df18..a7780ede 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,9 @@ "@langfuse/otel": "^5.4.1", "@langfuse/tracing": "^5.4.1", "@next/third-parties": "^16.2.6", + "@opentelemetry/api": "^1.9.1", "@opentelemetry/sdk-node": "^0.218.0", + "@opentelemetry/sdk-trace-node": "^2.7.1", "@scalar/api-reference-react": "^0.8.57", "@scalar/nextjs-api-reference": "^0.9.19", "@tabler/icons-react": "^3.36.1", @@ -50,6 +52,7 @@ "gradflow": "^0.1.0", "gray-matter": "^4.0.3", "gsap": "^3.15.0", + "langfuse": "^3.38.20", "lucide-react": "^0.574.0", "mdast-util-find-and-replace": "^3.0.2", "motion": "^12.40.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5338c451..772689cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,25 +37,31 @@ importers: version: 2.1.2(gsap@3.15.0)(react@19.2.4) '@langfuse/client': specifier: ^5.4.1 - version: 5.4.1(@opentelemetry/api@1.9.0) + version: 5.4.1(@opentelemetry/api@1.9.1) '@langfuse/otel': specifier: ^5.4.1 - version: 5.4.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.0))(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.0)) + version: 5.4.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1)) '@langfuse/tracing': specifier: ^5.4.1 - version: 5.4.1(@opentelemetry/api@1.9.0) + version: 5.4.1(@opentelemetry/api@1.9.1) '@next/third-parties': specifier: ^16.2.6 - version: 16.2.6(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + version: 16.2.6(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + '@opentelemetry/api': + specifier: ^1.9.1 + version: 1.9.1 '@opentelemetry/sdk-node': specifier: ^0.218.0 - version: 0.218.0(@opentelemetry/api@1.9.0) + version: 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-node': + specifier: ^2.7.1 + version: 2.7.1(@opentelemetry/api@1.9.1) '@scalar/api-reference-react': specifier: ^0.8.57 version: 0.8.57(change-case@5.4.4)(react@19.2.4)(tailwindcss@4.1.18)(typescript@5.9.3) '@scalar/nextjs-api-reference': specifier: ^0.9.19 - version: 0.9.19(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + version: 0.9.19(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) '@tabler/icons-react': specifier: ^3.36.1 version: 3.36.1(react@19.2.4) @@ -95,6 +101,9 @@ importers: gsap: specifier: ^3.15.0 version: 3.15.0 + langfuse: + specifier: ^3.38.20 + version: 3.38.20 lucide-react: specifier: ^0.574.0 version: 0.574.0(react@19.2.4) @@ -109,7 +118,7 @@ importers: version: 2.1.1 next: specifier: 16.2.6 - version: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next-mdx-remote: specifier: ^6.0.0 version: 6.0.0(@types/react@19.2.14)(react@19.2.4) @@ -179,7 +188,7 @@ importers: version: 10.4.1(@vitest/browser-playwright@4.1.7)(@vitest/browser@4.1.7)(@vitest/runner@4.1.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vitest@4.1.7) '@storybook/nextjs-vite': specifier: ^10.4.1 - version: 10.4.1(@babel/core@7.29.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.3)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 10.4.1(@babel/core@7.29.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.3)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.1.18 @@ -242,7 +251,7 @@ importers: version: 8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: ^4.1.7 - version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) packages: @@ -1388,6 +1397,10 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@opentelemetry/api@1.9.1': + resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} + engines: {node: '>=8.0.0'} + '@opentelemetry/configuration@0.218.0': resolution: {integrity: sha512-W8wIz7H2R1pufR5jfjb3gU2XkMpm2x/7b1RJcsuzvd70Il/rWWE+g5/Od7hQKrxRTSrTrOWlru101PWXz5I1EQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4734,6 +4747,14 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + langfuse-core@3.38.20: + resolution: {integrity: sha512-zBKVmQN/1oT5VWZUBYlWzvokIlkC/6mnpgr/2atMyTeAm+jR3ia7w2iJMjlrF5/oG8ukO1s8+LDRCzJpF1QeEA==} + engines: {node: '>=18'} + + langfuse@3.38.20: + resolution: {integrity: sha512-MAmBAASSzJtmK1O9HQegA1mFsQhT8Yf+OJRGvE7FXkyv3g/eiBE0glLD0Ohg3pkxhoPdggM5SejK7ue9ctlaMA==} + engines: {node: '>=18'} + leven@4.1.0: resolution: {integrity: sha512-KZ9W9nWDT7rF7Dazg8xyLHGLrmpgq2nVNFUckhqdW3szVP6YhCpp/RAnpmVExA9JvrMynjwSLVrEj3AepHR6ew==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7627,29 +7648,29 @@ snapshots: '@js-sdsl/ordered-map@4.4.2': {} - '@langfuse/client@5.4.1(@opentelemetry/api@1.9.0)': + '@langfuse/client@5.4.1(@opentelemetry/api@1.9.1)': dependencies: - '@langfuse/core': 5.4.1(@opentelemetry/api@1.9.0) - '@langfuse/tracing': 5.4.1(@opentelemetry/api@1.9.0) - '@opentelemetry/api': 1.9.0 + '@langfuse/core': 5.4.1(@opentelemetry/api@1.9.1) + '@langfuse/tracing': 5.4.1(@opentelemetry/api@1.9.1) + '@opentelemetry/api': 1.9.1 mustache: 4.2.0 - '@langfuse/core@5.4.1(@opentelemetry/api@1.9.0)': + '@langfuse/core@5.4.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 - '@langfuse/otel@5.4.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.0))(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.0))': + '@langfuse/otel@5.4.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))': dependencies: - '@langfuse/core': 5.4.1(@opentelemetry/api@1.9.0) - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-http': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) + '@langfuse/core': 5.4.1(@opentelemetry/api@1.9.1) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) - '@langfuse/tracing@5.4.1(@opentelemetry/api@1.9.0)': + '@langfuse/tracing@5.4.1(@opentelemetry/api@1.9.1)': dependencies: - '@langfuse/core': 5.4.1(@opentelemetry/api@1.9.0) - '@opentelemetry/api': 1.9.0 + '@langfuse/core': 5.4.1(@opentelemetry/api@1.9.1) + '@opentelemetry/api': 1.9.1 '@lezer/common@1.5.1': {} @@ -7808,9 +7829,9 @@ snapshots: '@next/swc-win32-x64-msvc@16.2.6': optional: true - '@next/third-parties@16.2.6(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + '@next/third-parties@16.2.6(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': dependencies: - next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 third-party-capital: 1.0.20 @@ -7853,15 +7874,17 @@ snapshots: '@opentelemetry/api@1.9.0': {} - '@opentelemetry/configuration@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/api@1.9.1': {} + + '@opentelemetry/configuration@0.218.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) yaml: 2.8.2 - '@opentelemetry/context-async-hooks@2.7.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/context-async-hooks@2.7.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': dependencies: @@ -7873,20 +7896,20 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/exporter-logs-otlp-grpc@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-logs-otlp-grpc@0.218.0(@opentelemetry/api@1.9.1)': dependencies: '@grpc/grpc-js': 1.14.4 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-grpc-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-grpc-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.0)': dependencies: @@ -7897,105 +7920,105 @@ snapshots: '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-http@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-logs-otlp-http@0.218.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.218.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.1) - '@opentelemetry/exporter-logs-otlp-proto@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-logs-otlp-proto@0.218.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.218.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/exporter-metrics-otlp-grpc@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-metrics-otlp-grpc@0.218.0(@opentelemetry/api@1.9.1)': dependencies: '@grpc/grpc-js': 1.14.4 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-http': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-grpc-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.0) - - '@opentelemetry/exporter-metrics-otlp-http@0.218.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.0) - - '@opentelemetry/exporter-metrics-otlp-proto@0.218.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-http': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.0) - - '@opentelemetry/exporter-prometheus@0.218.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-grpc-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-metrics-otlp-http@0.218.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-metrics-otlp-proto@0.218.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-prometheus@0.218.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/exporter-trace-otlp-grpc@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-trace-otlp-grpc@0.218.0(@opentelemetry/api@1.9.1)': dependencies: '@grpc/grpc-js': 1.14.4 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-grpc-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) - - '@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) - - '@opentelemetry/exporter-trace-otlp-proto@0.218.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) - - '@opentelemetry/exporter-zipkin@2.7.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-grpc-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-trace-otlp-proto@0.218.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-zipkin@2.7.1(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/instrumentation@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation@0.218.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.218.0 import-in-the-middle: 3.0.1 require-in-the-middle: 8.0.1 @@ -8008,19 +8031,19 @@ snapshots: '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/otlp-exporter-base@0.218.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-grpc-exporter-base@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/otlp-grpc-exporter-base@0.218.0(@opentelemetry/api@1.9.1)': dependencies: '@grpc/grpc-js': 1.14.4 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)': dependencies: @@ -8033,25 +8056,25 @@ snapshots: '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) protobufjs: 7.5.4 - '@opentelemetry/otlp-transformer@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/otlp-transformer@0.218.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.218.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/propagator-b3@2.7.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/propagator-b3@2.7.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/propagator-jaeger@2.7.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/propagator-jaeger@2.7.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)': dependencies: @@ -8065,10 +8088,10 @@ snapshots: '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)': @@ -8078,12 +8101,12 @@ snapshots: '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-logs@0.218.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.218.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 '@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0)': @@ -8092,39 +8115,39 @@ snapshots: '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics@2.7.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-metrics@2.7.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-node@0.218.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-node@0.218.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.218.0 - '@opentelemetry/configuration': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/context-async-hooks': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-grpc': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-http': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-logs-otlp-proto': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-grpc': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-http': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-metrics-otlp-proto': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-prometheus': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-grpc': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-http': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-proto': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-zipkin': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/propagator-b3': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/propagator-jaeger': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-node': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/configuration': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/context-async-hooks': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-logs-otlp-grpc': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-logs-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-logs-otlp-proto': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-grpc': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-proto': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-prometheus': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-grpc': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-proto': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-zipkin': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/propagator-b3': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/propagator-jaeger': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-node': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 transitivePeerDependencies: - supports-color @@ -8136,19 +8159,19 @@ snapshots: '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/sdk-trace-node@2.7.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-trace-node@2.7.1(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/context-async-hooks': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions@1.39.0': {} @@ -9417,10 +9440,10 @@ snapshots: '@scalar/helpers': 0.2.12 yaml: 2.8.2 - '@scalar/nextjs-api-reference@0.9.19(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + '@scalar/nextjs-api-reference@0.9.19(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': dependencies: '@scalar/core': 0.3.38 - next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 '@scalar/oas-utils@0.6.42(typescript@5.9.3)': @@ -9621,7 +9644,7 @@ snapshots: '@vitest/browser': 4.1.7(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.7) '@vitest/browser-playwright': 4.1.7(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(playwright@1.60.0)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.7) '@vitest/runner': 4.1.7 - vitest: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - react - react-dom @@ -9662,18 +9685,18 @@ snapshots: - '@tmcp/auth' - typescript - '@storybook/nextjs-vite@10.4.1(@babel/core@7.29.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.3)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': + '@storybook/nextjs-vite@10.4.1(@babel/core@7.29.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.3)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@storybook/builder-vite': 10.4.1(esbuild@0.27.3)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) '@storybook/react': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) '@storybook/react-vite': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) vite: 8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) - vite-plugin-storybook-nextjs: 3.3.0(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vite-plugin-storybook-nextjs: 3.3.0(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) optionalDependencies: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) @@ -10057,7 +10080,7 @@ snapshots: '@vitest/mocker': 4.1.7(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - bufferutil - msw @@ -10073,7 +10096,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) ws: 8.21.0 transitivePeerDependencies: - bufferutil @@ -10093,7 +10116,7 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) optionalDependencies: '@vitest/browser': 4.1.7(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.7) @@ -11560,6 +11583,14 @@ snapshots: kleur@4.1.5: {} + langfuse-core@3.38.20: + dependencies: + mustache: 4.2.0 + + langfuse@3.38.20: + dependencies: + langfuse-core: 3.38.20 + leven@4.1.0: {} lightningcss-android-arm64@1.30.2: @@ -12295,7 +12326,7 @@ snapshots: - '@types/react' - supports-color - next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@next/env': 16.2.6 '@swc/helpers': 0.5.15 @@ -12314,7 +12345,7 @@ snapshots: '@next/swc-linux-x64-musl': 16.2.6 '@next/swc-win32-arm64-msvc': 16.2.6 '@next/swc-win32-x64-msvc': 16.2.6 - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -13703,13 +13734,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-plugin-storybook-nextjs@3.3.0(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): + vite-plugin-storybook-nextjs@3.3.0(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@next/env': 16.0.0 image-size: 2.0.2 magic-string: 0.30.21 module-alias: 2.3.4 - next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) ts-dedent: 2.2.0 vite: 8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) @@ -13744,7 +13775,7 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vitest@4.1.7(@opentelemetry/api@1.9.0)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.33)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.7 '@vitest/mocker': 4.1.7(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -13767,7 +13798,7 @@ snapshots: vite: 8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@types/node': 20.19.33 '@vitest/browser-playwright': 4.1.7(msw@2.12.10(@types/node@20.19.33)(typescript@5.9.3))(playwright@1.60.0)(vite@8.0.14(@types/node@20.19.33)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.7) '@vitest/coverage-v8': 4.1.7(@vitest/browser@4.1.7)(vitest@4.1.7) From 761707d6b06d6c43022536f5c13e581f7dfc9a34 Mon Sep 17 00:00:00 2001 From: phucbm Date: Tue, 9 Jun 2026 11:13:03 +0700 Subject: [PATCH 2/3] feat(chat): add feedback buttons + improve Langfuse tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 👍👎 feedback buttons to assistant action bar (hover to reveal) - Track promptVersion and messageCount in Langfuse trace metadata - Set service.name from VERCEL_ENV for environment-aware traces - Fix action bar visibility: remove -mb-7.5 clip, use autohideFloat="always" - Add @opentelemetry/resources for resourceFromAttributes (v2 API) Co-Authored-By: Claude Sonnet 4.6 --- app/api/chat/route.ts | 5 +++-- components/assistant-ui/thread.tsx | 32 ++++++++++++++++++------------ instrumentation.ts | 4 ++++ package.json | 1 + pnpm-lock.yaml | 23 +++++++-------------- 5 files changed, 34 insertions(+), 31 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 0f3bdb07..58aea90f 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -67,7 +67,8 @@ const handler = async (req: Request) => { const lastUserMessage = uiMessages.findLast((m) => m.role === 'user')?.parts ?.filter((p) => p.type === 'text').map((p) => p.text).join('') ?? ''; - const { text: systemPrompt } = await getSystemPrompt(body.pageContext); + const { text: systemPrompt, version: promptVersion } = await getSystemPrompt(body.pageContext); + const messageCount = uiMessages.length; setActiveTraceIO({ input: lastUserMessage }); @@ -77,7 +78,7 @@ const handler = async (req: Request) => { sessionId: body.sessionId, userId: body.userId, tags: ['web-chat'], - metadata: { model, ip }, + metadata: { model, ip, promptVersion: String(promptVersion), messageCount: String(messageCount) }, }, async () => { let mcpClient: Awaited> | null = null; diff --git a/components/assistant-ui/thread.tsx b/components/assistant-ui/thread.tsx index 34e14de0..f3faa8e3 100644 --- a/components/assistant-ui/thread.tsx +++ b/components/assistant-ui/thread.tsx @@ -389,13 +389,13 @@ const AssistantMessage: FC = () => { // keeps hovered action bar from shifting layout (autohide doesn't support absolute positioning well) // for pt-[n] use -mb-[n + 6] & min-h-[n + 6] to preserve compensation const ACTION_BAR_PT = "pt-1.5"; - const ACTION_BAR_HEIGHT = `-mb-7.5 min-h-7.5 ${ACTION_BAR_PT}`; + const ACTION_BAR_HEIGHT = `min-h-7.5 ${ACTION_BAR_PT}`; return (
{ @@ -485,16 +486,6 @@ const AssistantActionBar: FC = () => { - - - - - - - - - - { align="start" className="aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-2xl border bg-popover p-1 text-popover-foreground shadow-md" > + + + + Helpful + + + + + + Not helpful + + + diff --git a/instrumentation.ts b/instrumentation.ts index b10fa16b..db6a6e82 100644 --- a/instrumentation.ts +++ b/instrumentation.ts @@ -1,9 +1,13 @@ import { LangfuseSpanProcessor } from '@langfuse/otel'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { resourceFromAttributes } from '@opentelemetry/resources'; export const langfuseSpanProcessor = new LangfuseSpanProcessor(); const tracerProvider = new NodeTracerProvider({ + resource: resourceFromAttributes({ + 'service.name': process.env.VERCEL_ENV ?? 'development', + }), spanProcessors: [langfuseSpanProcessor], }); diff --git a/package.json b/package.json index a7780ede..325bdd38 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@langfuse/tracing": "^5.4.1", "@next/third-parties": "^16.2.6", "@opentelemetry/api": "^1.9.1", + "@opentelemetry/resources": "^2.7.1", "@opentelemetry/sdk-node": "^0.218.0", "@opentelemetry/sdk-trace-node": "^2.7.1", "@scalar/api-reference-react": "^0.8.57", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 772689cc..4c08a35d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: '@opentelemetry/api': specifier: ^1.9.1 version: 1.9.1 + '@opentelemetry/resources': + specifier: ^2.7.1 + version: 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-node': specifier: ^0.218.0 version: 0.218.0(@opentelemetry/api@1.9.1) @@ -1419,12 +1422,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.5.1': - resolution: {integrity: sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.7.1': resolution: {integrity: sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -1557,12 +1554,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/resources@2.5.1': - resolution: {integrity: sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/resources@2.7.1': resolution: {integrity: sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -7891,7 +7882,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.39.0 @@ -8082,10 +8073,10 @@ snapshots: '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.39.0 '@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.1)': @@ -12615,7 +12606,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.208.0 '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) '@posthog/core': 1.23.1 '@posthog/types': 1.352.0 From cc5331aa9bc6d732f7027a619397585a9e1580cd Mon Sep 17 00:00:00 2001 From: phucbm Date: Tue, 9 Jun 2026 11:18:40 +0700 Subject: [PATCH 3/3] docs: add Langfuse OTEL tracing learnings, update owie-chat observability section Co-Authored-By: Claude Sonnet 4.6 --- .claude/docs/features/owie-chat.md | 28 +-- .claude/docs/learnings/README.md | 3 +- .../docs/learnings/langfuse-otel-tracing.md | 183 ++++++++++++++++++ 3 files changed, 193 insertions(+), 21 deletions(-) create mode 100644 .claude/docs/learnings/langfuse-otel-tracing.md diff --git a/.claude/docs/features/owie-chat.md b/.claude/docs/features/owie-chat.md index c8641be0..155a3b4a 100644 --- a/.claude/docs/features/owie-chat.md +++ b/.claude/docs/features/owie-chat.md @@ -135,33 +135,21 @@ pnpm chatlog:id # stream one session ## Observability (Langfuse) -All observability uses Langfuse direct HTTP ingestion — no SDK (JS SDK silently fails in CF Workers, GitHub issue -#11984). +Uses OTEL approach: `instrumentation.ts` → `NodeTracerProvider` + `LangfuseSpanProcessor` → `observe()` in route handler → `experimental_telemetry: { isEnabled: true }` on `streamText`. -**File:** `lib/langfuse.ts` +**Full details:** `.claude/docs/learnings/langfuse-otel-tracing.md` -- `fetchSystemPrompt()` — fetches `chat-system-prompt?label=production`, 60s in-memory cache, falls back to hardcoded - `SYSTEM_PROMPT` on error -- `sendChatTrace()` — sends trace with input/output/model/tokens/latency/finishReason/steps/promptVersion +**Files:** +- `instrumentation.ts` — registers OTEL provider at Next.js startup +- `lib/langfuse.ts` — `fetchSystemPrompt()` (prompt management) + `postFeedbackScore()` (user feedback scores) +- `app/api/chat/route.ts` — `observe()` wrapper, `propagateAttributes()`, `getActiveTraceId()` +- `app/api/chat/feedback/route.ts` — server proxy for posting feedback scores (keeps secret key server-side) -**Trace fields:** - -| Field | Source | -|-----------------|--------------------------------------------| -| `input` | Last user message text | -| `output` | Full assistant response | -| `model` | `CHAT_MODEL` / `DEFAULT_MODEL` env var | -| `tokens.input` | `usage.inputTokens` from AI SDK `onFinish` | -| `tokens.output` | `usage.outputTokens` | -| `latencyMs` | `Date.now() - startTime` | -| `finishReason` | From `onFinish` | -| `steps` | `steps.length` (tool call count) | -| `promptVersion` | From Langfuse prompt fetch | +**User feedback:** 👍👎 buttons in assistant action bar → `FeedbackAdapter` in `chat-runtime.tsx` → `/api/chat/feedback` → `POST /api/public/scores` with `name: 'user-feedback'`, value `1` or `0`. **LLM-as-Judge evaluator** configured in Langfuse UI → Evaluators: - Target: Live Traces, filter trace name = `chat` -- Model: setup in Langfuse UI - Scores appear in Evaluation → Scores automatically **Env vars:** diff --git a/.claude/docs/learnings/README.md b/.claude/docs/learnings/README.md index 66251495..800bc7e3 100644 --- a/.claude/docs/learnings/README.md +++ b/.claude/docs/learnings/README.md @@ -10,4 +10,5 @@ Updated as new things are learned. Review anytime to consolidate understanding. | [static-export-constraints.md](static-export-constraints.md) | What `output: 'export'` means, what's not allowed, build-time data fetching pattern | | [seo-jsonld-pattern.md](seo-jsonld-pattern.md) | JSON-LD structured data, OG metadata, per-page builder pattern | | [local-first-wallet.md](local-first-wallet.md) | IndexedDB, Dexie.js, why there's no server/login, client component rule | -| [mdx-blog-pipeline.md](mdx-blog-pipeline.md) | MDX format, frontmatter fields, categories, image conventions, validation | \ No newline at end of file +| [mdx-blog-pipeline.md](mdx-blog-pipeline.md) | MDX format, frontmatter fields, categories, image conventions, validation | +| [langfuse-otel-tracing.md](langfuse-otel-tracing.md) | OTEL vs manual HTTP, instrumentation.ts setup, observe() pattern, traceId→feedback flow, score API | \ No newline at end of file diff --git a/.claude/docs/learnings/langfuse-otel-tracing.md b/.claude/docs/learnings/langfuse-otel-tracing.md new file mode 100644 index 00000000..a7df0663 --- /dev/null +++ b/.claude/docs/learnings/langfuse-otel-tracing.md @@ -0,0 +1,183 @@ +# Langfuse + Vercel AI SDK: OTEL Tracing + +## The two approaches + +### Approach A: OTEL (canonical, current) +`instrumentation.ts` → `NodeTracerProvider` + `LangfuseSpanProcessor` → `observe()` wrapper → `experimental_telemetry: { isEnabled: true }` on `streamText`. + +### Approach B: Manual HTTP (old, removed) +Raw `fetch()` to `/api/public/ingestion`. Was in codebase from early features, added ad-hoc, no consistent pattern. Replaced by Approach A. + +**Always use A.** B looks simple but misses: auto token counts, span hierarchy, tool call spans, step-level tracing — all of which OTEL gives for free via `experimental_telemetry`. + +--- + +## Setup: instrumentation.ts + +Must use `NodeTracerProvider` directly. Do NOT use `@vercel/otel` — Langfuse docs explicitly say to avoid it. + +```ts +import { LangfuseSpanProcessor } from '@langfuse/otel'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { resourceFromAttributes } from '@opentelemetry/resources'; // v2 API — NOT `new Resource({})` + +export const langfuseSpanProcessor = new LangfuseSpanProcessor(); + +const tracerProvider = new NodeTracerProvider({ + resource: resourceFromAttributes({ + 'service.name': process.env.VERCEL_ENV ?? 'development', + }), + spanProcessors: [langfuseSpanProcessor], +}); + +tracerProvider.register(); +``` + +**`@opentelemetry/resources` v2 breaking change:** `new Resource({})` removed, replaced by `resourceFromAttributes({})`. + +**`VERCEL_ENV`** = `production` / `preview` / `development` — use as `service.name` to filter traces by environment in Langfuse UI. No extra env var needed. + +Export `langfuseSpanProcessor` from here so route can call `forceFlush()` via `after()`. + +--- + +## Setup: route handler + +```ts +import { observe, propagateAttributes, setActiveTraceIO, getActiveTraceId } from '@langfuse/tracing'; +import { trace } from '@opentelemetry/api'; +import { langfuseSpanProcessor } from '@/instrumentation'; +import { after } from 'next/server'; + +const handler = async (req: Request) => { + // set input before propagateAttributes + setActiveTraceIO({ input: lastUserMessage }); + + return await propagateAttributes( + { + traceName: 'chat', + sessionId: body.sessionId, + userId: body.userId, + tags: ['web-chat'], + // all metadata values must be string (propagateAttributes type constraint) + metadata: { model, ip, promptVersion: String(promptVersion), messageCount: String(messageCount) }, + }, + async () => { + const traceId = getActiveTraceId(); // capture before streamText + + const result = streamText({ + model: openrouter(model), + messages, + experimental_telemetry: { isEnabled: true }, // auto-captures tokens, steps, tool calls + onFinish: async ({ text }) => { + setActiveTraceIO({ output: text }); + trace.getActiveSpan()?.end(); // must end span manually (endOnExit: false) + }, + onError: async () => { + trace.getActiveSpan()?.end(); + }, + }); + + result.consumeStream(); // no await — ensures onFinish fires if client disconnects + + after(async () => await langfuseSpanProcessor.forceFlush()); // required for serverless + + return result.toUIMessageStreamResponse({ + messageMetadata: ({ part }) => { + if (part.type === 'finish') return { custom: { traceId } }; // expose traceId to frontend + return undefined; + }, + }); + } + ); +}; + +export const POST = observe(handler, { + name: 'handle-chat-message', + endOnExit: false, // keep span open until stream finishes (onFinish calls span.end()) +}); +``` + +**`endOnExit: false`** is critical — without it the span closes before `onFinish` fires and output is never recorded. + +**`after()` + `forceFlush()`** is required in serverless (Vercel functions). Without it, the function exits before spans are flushed to Langfuse. + +**`metadata` values must all be strings** — `propagateAttributes` types `metadata` as `Record`. Pass numbers with `String()`. + +--- + +## traceId → frontend → feedback + +`getActiveTraceId()` captures the active trace ID inside `propagateAttributes()`. Inject into `messageMetadata` on the `finish` part → flows to `message.metadata.custom.traceId` on the client. + +Read in `FeedbackAdapter`: + +```ts +adapters: { + feedback: { + submit: async ({ type, message }) => { + const traceId = (message.metadata as { custom?: { traceId?: string } })?.custom?.traceId; + if (!traceId) return; // silently skip if no traceId (e.g. old messages) + await fetch('/api/chat/feedback', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ traceId, value: type === 'positive' ? 1 : 0 }), + }); + }, + }, +}, +``` + +**Never expose `LANGFUSE_SECRET_KEY` to frontend.** Use a server proxy route (`/api/chat/feedback`) that calls `POST /api/public/scores`. + +--- + +## Posting scores (feedback) + +```ts +// POST /api/public/scores — NOT batch ingestion +// Batch ingestion silently accepts scores but never stores them +await fetch(`${baseUrl}/api/public/scores`, { + method: 'POST', + headers: { Authorization: basicAuth(), 'Content-Type': 'application/json' }, + body: JSON.stringify({ + traceId, + name: 'user-feedback', + value, // 0 or 1 + dataType: 'NUMERIC', + }), +}); +``` + +--- + +## What gets auto-tracked by OTEL + +When `experimental_telemetry: { isEnabled: true }` is set on `streamText`, Langfuse auto-captures: +- Token counts (input/output/total) +- Model name +- Latency +- Tool calls (name + args + result per step) +- Step count +- Finish reason + +Manual additions via `propagateAttributes` + `setActiveTraceIO`: +- `sessionId`, `userId`, `tags` +- `metadata.model`, `metadata.ip`, `metadata.promptVersion`, `metadata.messageCount` +- `input` (last user message text) +- `output` (full assistant response) + +User feedback: +- Score name: `user-feedback`, value `1` (positive) or `0` (negative), dataType `NUMERIC` + +--- + +## Packages + +``` +@langfuse/otel — LangfuseSpanProcessor +@langfuse/tracing — observe(), propagateAttributes(), setActiveTraceIO(), getActiveTraceId() +@opentelemetry/api — trace.getActiveSpan() +@opentelemetry/sdk-trace-node — NodeTracerProvider +@opentelemetry/resources — resourceFromAttributes (v2) +```