From 30b598d960156f5fb93c7b88ebd4c58de082fcb6 Mon Sep 17 00:00:00 2001 From: "dayuan.jiang" Date: Thu, 4 Dec 2025 00:13:42 +0900 Subject: [PATCH] feat: add optional Langfuse observability integration - Add session tracking with unique sessionId per conversation - Add user tracking via IP address (x-forwarded-for header) - Make telemetry conditional - only enabled if LANGFUSE_PUBLIC_KEY is set - Add environment variable validation in instrumentation.ts - Add sessionId validation (type check + 200 char limit) - Update env.example with Langfuse configuration docs - Remove unused langfuse-vercel and @vercel/otel packages --- app/api/chat/route.ts | 22 ++- components/chat-panel.tsx | 5 + env.example | 6 + instrumentation.ts | 23 ++- package-lock.json | 340 ++++++++++++++++++++++++-------------- package.json | 7 +- 6 files changed, 265 insertions(+), 138 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 2672128..2afa072 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -30,7 +30,16 @@ function createCachedStreamResponse(xml: string): Response { export async function POST(req: Request) { try { - const { messages, xml } = await req.json(); + const { messages, xml, sessionId } = await req.json(); + + // Get user IP for Langfuse tracking + const forwardedFor = req.headers.get('x-forwarded-for'); + const userId = forwardedFor?.split(',')[0]?.trim() || 'anonymous'; + + // Validate sessionId for Langfuse (must be string, max 200 chars) + const validSessionId = sessionId && typeof sessionId === 'string' && sessionId.length <= 200 + ? sessionId + : undefined; // === CACHE CHECK START === const isFirstMessage = messages.length === 1; @@ -257,7 +266,16 @@ ${lastMessageText} messages: [systemMessageWithCache, ...enhancedMessages], ...(providerOptions && { providerOptions }), ...(headers && { headers }), - experimental_telemetry: { isEnabled: true }, + // Only enable telemetry if Langfuse is configured + ...(process.env.LANGFUSE_PUBLIC_KEY && { + experimental_telemetry: { + isEnabled: true, + metadata: { + sessionId: validSessionId, + userId: userId, + }, + }, + }), onFinish: ({ usage, providerMetadata }) => { console.log('[Cache] Usage:', JSON.stringify({ inputTokens: usage?.inputTokens, diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index 386e80e..6c9d7b5 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -61,6 +61,9 @@ export default function ChatPanel({ const [showHistory, setShowHistory] = useState(false); const [input, setInput] = useState(""); + // Generate a unique session ID for Langfuse tracing + const [sessionId, setSessionId] = useState(() => `session-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`); + const { messages, sendMessage, addToolResult, status, error, setMessages } = useChat({ transport: new DefaultChatTransport({ @@ -177,6 +180,7 @@ Please retry with an adjusted search pattern or use display_diagram if retries a { body: { xml: chartXml, + sessionId, }, } ); @@ -293,6 +297,7 @@ Please retry with an adjusted search pattern or use display_diagram if retries a onClearChat={() => { setMessages([]); clearDiagram(); + setSessionId(`session-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`); }} files={files} onFileChange={handleFileChange} diff --git a/env.example b/env.example index 1921f1b..e654d91 100644 --- a/env.example +++ b/env.example @@ -41,3 +41,9 @@ AI_MODEL=global.anthropic.claude-sonnet-4-5-20250929-v1:0 # DeepSeek Configuration # DEEPSEEK_API_KEY=sk-... # DEEPSEEK_BASE_URL=https://api.deepseek.com/v1 # Optional: Custom endpoint + +# Langfuse Observability (Optional) +# Enable LLM tracing and analytics - https://langfuse.com +# LANGFUSE_PUBLIC_KEY=pk-lf-... +# LANGFUSE_SECRET_KEY=sk-lf-... +# LANGFUSE_BASEURL=https://cloud.langfuse.com # EU region, use https://us.cloud.langfuse.com for US diff --git a/instrumentation.ts b/instrumentation.ts index 6f11fc5..be21a71 100644 --- a/instrumentation.ts +++ b/instrumentation.ts @@ -1,9 +1,22 @@ -import { registerOTel } from '@vercel/otel'; -import { LangfuseExporter } from 'langfuse-vercel'; +import { LangfuseSpanProcessor } from '@langfuse/otel'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; export function register() { - registerOTel({ - serviceName: 'next-ai-draw-io', - traceExporter: new LangfuseExporter(), + // Skip telemetry if Langfuse env vars are not configured + if (!process.env.LANGFUSE_PUBLIC_KEY || !process.env.LANGFUSE_SECRET_KEY) { + console.warn('[Langfuse] Environment variables not configured - telemetry disabled'); + return; + } + + const langfuseSpanProcessor = new LangfuseSpanProcessor({ + publicKey: process.env.LANGFUSE_PUBLIC_KEY, + secretKey: process.env.LANGFUSE_SECRET_KEY, + baseUrl: process.env.LANGFUSE_BASEURL, }); + + const tracerProvider = new NodeTracerProvider({ + spanProcessors: [langfuseSpanProcessor], + }); + + tracerProvider.register(); } diff --git a/package-lock.json b/package-lock.json index a0c1ea2..85dff6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,24 +15,21 @@ "@ai-sdk/google": "^2.0.0", "@ai-sdk/openai": "^2.0.19", "@ai-sdk/react": "^2.0.22", + "@langfuse/otel": "^4.4.4", "@next/third-parties": "^16.0.6", "@openrouter/ai-sdk-provider": "^1.2.3", - "@opentelemetry/api-logs": "^0.208.0", - "@opentelemetry/instrumentation": "^0.208.0", - "@opentelemetry/sdk-logs": "^0.208.0", + "@opentelemetry/sdk-trace-node": "^2.2.0", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-scroll-area": "^1.2.3", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", "@vercel/analytics": "^1.5.0", - "@vercel/otel": "^2.1.0", "@xmldom/xmldom": "^0.9.8", "ai": "^5.0.89", "base-64": "^1.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "jsdom": "^26.0.0", - "langfuse-vercel": "^3.38.6", "lucide-react": "^0.483.0", "next": "15.2.3", "ollama-ai-provider-v2": "^1.5.4", @@ -1597,6 +1594,33 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@langfuse/core": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@langfuse/core/-/core-4.4.4.tgz", + "integrity": "sha512-hmtMNAOIsvDwT/xld0CJPXrIsakETbelSmAOGEY07faKtKdJy/BGjxexBbfAWLPgAC6wqC2fK2ByaYCGgC7MBw==", + "license": "MIT", + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + } + }, + "node_modules/@langfuse/otel": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@langfuse/otel/-/otel-4.4.4.tgz", + "integrity": "sha512-b6VfVZUf5U1HrbPicUNZ0jYuR2QmnGhJpOV85Kbonzkslu60XggzjKi1UzTAVCA0sVzABK7WvqWvojpk02bfAw==", + "license": "MIT", + "dependencies": { + "@langfuse/core": "^4.4.4" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^2.0.1", + "@opentelemetry/exporter-trace-otlp-http": ">=0.202.0 <1.0.0", + "@opentelemetry/sdk-trace-base": "^2.0.1" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -1870,6 +1894,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz", "integrity": "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api": "^1.3.0" }, @@ -1877,6 +1902,18 @@ "node": ">=8.0.0" } }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", + "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/core": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", @@ -1892,15 +1929,57 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/instrumentation": { + "node_modules/@opentelemetry/exporter-trace-otlp-http": { "version": "0.208.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz", - "integrity": "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.208.0.tgz", + "integrity": "sha512-jbzDw1q+BkwKFq9yxhjAJ9rjKldbt5AgIy1gmEIJjEV/WRxQ3B6HcLVkwbjJ3RcMif86BDNKR846KJ0tY0aOJA==", "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.208.0.tgz", + "integrity": "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-transformer": "0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.208.0.tgz", + "integrity": "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==", + "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.208.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-logs": "0.208.0", + "@opentelemetry/sdk-metrics": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0", + "protobufjs": "^7.3.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1930,6 +2009,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.208.0.tgz", "integrity": "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", @@ -1964,7 +2044,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", @@ -1977,6 +2056,23 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.2.0.tgz", + "integrity": "sha512-+OaRja3f0IqGG2kptVeYsrZQK9nKRSpfFrKtRBq4uh6nIB8bTBgaGvYQrQoRrQWQMA5dK5yLhDMDc0dvYvCOIQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.2.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/semantic-conventions": { "version": "1.38.0", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", @@ -1986,6 +2082,80 @@ "node": ">=14" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause", + "peer": true + }, "node_modules/@radix-ui/number": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", @@ -2906,7 +3076,6 @@ "version": "20.17.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -3524,24 +3693,6 @@ "node": ">= 20" } }, - "node_modules/@vercel/otel": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vercel/otel/-/otel-2.1.0.tgz", - "integrity": "sha512-Zwu2Cu4t46DzBnY1DQSTxZ4MBLVfYsOjnlWuZuLRWnmVPX+SNrVHbs3ssiJ6uvY1J1JJswor4zSn8mHYxzYeBA==", - "license": "MIT", - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.9.0 <2.0.0", - "@opentelemetry/api-logs": ">=0.200.0 <0.300.0", - "@opentelemetry/instrumentation": ">=0.200.0 <0.300.0", - "@opentelemetry/resources": ">=2.0.0 <3.0.0", - "@opentelemetry/sdk-logs": ">=0.200.0 <0.300.0", - "@opentelemetry/sdk-metrics": ">=2.0.0 <3.0.0", - "@opentelemetry/sdk-trace-base": ">=2.0.0 <3.0.0" - } - }, "node_modules/@xmldom/xmldom": { "version": "0.9.8", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", @@ -3555,6 +3706,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3563,15 +3715,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -4114,12 +4257,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "license": "MIT" - }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -5734,18 +5871,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-in-the-middle": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.0.tgz", - "integrity": "sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.14.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^1.2.2", - "module-details-from-path": "^1.0.3" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6379,46 +6504,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/langfuse": { - "version": "3.38.6", - "resolved": "https://registry.npmjs.org/langfuse/-/langfuse-3.38.6.tgz", - "integrity": "sha512-mtwfsNGIYvObRh+NYNGlJQJDiBN+Wr3Hnr++wN25mxuOpSTdXX+JQqVCyAqGL5GD2TAXRZ7COsN42Vmp9krYmg==", - "license": "MIT", - "dependencies": { - "langfuse-core": "^3.38.6" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/langfuse-core": { - "version": "3.38.6", - "resolved": "https://registry.npmjs.org/langfuse-core/-/langfuse-core-3.38.6.tgz", - "integrity": "sha512-EcZXa+DK9FJdi1I30+u19eKjuBJ04du6j2Nybk19KKCuraLczg/ppkTQcGvc4QOk//OAi3qUHrajUuV74RXsBQ==", - "license": "MIT", - "dependencies": { - "mustache": "^4.2.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/langfuse-vercel": { - "version": "3.38.6", - "resolved": "https://registry.npmjs.org/langfuse-vercel/-/langfuse-vercel-3.38.6.tgz", - "integrity": "sha512-QlKZC1RhZRUlI0zNvxP5B6P2Xtt1+1ADTQfC/3hlnXFT3BMJZMynI/eUN3VX7WbNRGK0yDpqA9PyylXQskr92Q==", - "license": "MIT", - "dependencies": { - "langfuse": "^3.38.6", - "langfuse-core": "^3.38.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "ai": ">=3.2.44" - } - }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -6715,6 +6800,13 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -7592,27 +7684,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/module-details-from-path": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", - "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "license": "MIT" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "license": "MIT", - "bin": { - "mustache": "bin/mustache" - } - }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -8124,6 +8201,31 @@ "react-is": "^16.13.1" } }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -8362,19 +8464,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -9352,7 +9441,6 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, "license": "MIT" }, "node_modules/unified": { diff --git a/package.json b/package.json index edc7b04..956c38c 100644 --- a/package.json +++ b/package.json @@ -16,24 +16,21 @@ "@ai-sdk/google": "^2.0.0", "@ai-sdk/openai": "^2.0.19", "@ai-sdk/react": "^2.0.22", + "@langfuse/otel": "^4.4.4", "@next/third-parties": "^16.0.6", "@openrouter/ai-sdk-provider": "^1.2.3", - "@opentelemetry/api-logs": "^0.208.0", - "@opentelemetry/instrumentation": "^0.208.0", - "@opentelemetry/sdk-logs": "^0.208.0", + "@opentelemetry/sdk-trace-node": "^2.2.0", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-scroll-area": "^1.2.3", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", "@vercel/analytics": "^1.5.0", - "@vercel/otel": "^2.1.0", "@xmldom/xmldom": "^0.9.8", "ai": "^5.0.89", "base-64": "^1.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "jsdom": "^26.0.0", - "langfuse-vercel": "^3.38.6", "lucide-react": "^0.483.0", "next": "15.2.3", "ollama-ai-provider-v2": "^1.5.4",