diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 03cd939..7916d36 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,7 +1,6 @@ import { streamText, convertToModelMessages, createUIMessageStream, createUIMessageStreamResponse } from 'ai'; import { getAIModel } from '@/lib/ai-providers'; import { findCachedResponse } from '@/lib/cached-responses'; -import { setTraceInput, setTraceOutput, getTelemetryConfig, wrapWithObserve } from '@/lib/langfuse'; import { getSystemPrompt } from '@/lib/system-prompts'; import { z } from "zod"; @@ -32,27 +31,7 @@ function createCachedStreamResponse(xml: string): Response { // Inner handler function async function handleChatRequest(req: Request): Promise { - 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; - - // Extract user input text for Langfuse trace - const currentMessage = messages[messages.length - 1]; - const userInputText = currentMessage?.parts?.find((p: any) => p.type === 'text')?.text || ''; - - // Update Langfuse trace with input, session, and user - setTraceInput({ - input: userInputText, - sessionId: validSessionId, - userId: userId, - }); + const { messages, xml } = await req.json(); // === CACHE CHECK START === const isFirstMessage = messages.length === 1; @@ -175,19 +154,9 @@ ${lastMessageText} messages: allMessages, ...(providerOptions && { providerOptions }), ...(headers && { headers }), - // Langfuse telemetry config (returns undefined if not configured) - ...(getTelemetryConfig({ sessionId: validSessionId, userId }) && { - experimental_telemetry: getTelemetryConfig({ sessionId: validSessionId, userId }), - }), - onFinish: ({ text, usage, providerMetadata }) => { + onFinish: ({ usage, providerMetadata }) => { console.log('[Cache] Full providerMetadata:', JSON.stringify(providerMetadata, null, 2)); console.log('[Cache] Usage:', JSON.stringify(usage, null, 2)); - // Pass usage to Langfuse (Bedrock streaming doesn't auto-report tokens to telemetry) - // AI SDK uses inputTokens/outputTokens, Langfuse expects promptTokens/completionTokens - setTraceOutput(text, { - promptTokens: usage?.inputTokens, - completionTokens: usage?.outputTokens, - }); }, tools: { // Client-side tool that will be executed on the client @@ -278,8 +247,7 @@ IMPORTANT: Keep edits concise: }); } -// Wrap handler with error handling -async function safeHandler(req: Request): Promise { +export async function POST(req: Request) { try { return await handleChatRequest(req); } catch (error) { @@ -287,10 +255,3 @@ async function safeHandler(req: Request): Promise { return Response.json({ error: 'Internal server error' }, { status: 500 }); } } - -// Wrap with Langfuse observe (if configured) -const observedHandler = wrapWithObserve(safeHandler); - -export async function POST(req: Request) { - return observedHandler(req); -} diff --git a/app/api/log-feedback/route.ts b/app/api/log-feedback/route.ts deleted file mode 100644 index a0dbb09..0000000 --- a/app/api/log-feedback/route.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { getLangfuseClient } from '@/lib/langfuse'; -import { randomUUID } from 'crypto'; -import { z } from 'zod'; - -const feedbackSchema = z.object({ - messageId: z.string().min(1).max(200), - feedback: z.enum(['good', 'bad']), - sessionId: z.string().min(1).max(200).optional(), -}); - -export async function POST(req: Request) { - const langfuse = getLangfuseClient(); - if (!langfuse) { - return Response.json({ success: true, logged: false }); - } - - // Validate input - let data; - try { - data = feedbackSchema.parse(await req.json()); - } catch { - return Response.json({ success: false, error: 'Invalid input' }, { status: 400 }); - } - - const { messageId, feedback, sessionId } = data; - - // Get user IP for tracking - const forwardedFor = req.headers.get('x-forwarded-for'); - const userId = forwardedFor?.split(',')[0]?.trim() || 'anonymous'; - - try { - // Find the most recent chat trace for this session to attach the score to - const tracesResponse = await langfuse.api.trace.list({ - sessionId, - limit: 1, - }); - - const traces = tracesResponse.data || []; - const latestTrace = traces[0]; - - if (!latestTrace) { - // No trace found for this session - create a standalone feedback trace - const traceId = randomUUID(); - const timestamp = new Date().toISOString(); - - await langfuse.api.ingestion.batch({ - batch: [ - { - type: 'trace-create', - id: randomUUID(), - timestamp, - body: { - id: traceId, - name: 'user-feedback', - sessionId, - userId, - input: { messageId, feedback }, - metadata: { source: 'feedback-button', note: 'standalone - no chat trace found' }, - timestamp, - }, - }, - { - type: 'score-create', - id: randomUUID(), - timestamp, - body: { - id: randomUUID(), - traceId, - name: 'user-feedback', - value: feedback === 'good' ? 1 : 0, - comment: `User gave ${feedback} feedback`, - }, - }, - ], - }); - } else { - // Attach score to the existing chat trace - const timestamp = new Date().toISOString(); - - await langfuse.api.ingestion.batch({ - batch: [ - { - type: 'score-create', - id: randomUUID(), - timestamp, - body: { - id: randomUUID(), - traceId: latestTrace.id, - name: 'user-feedback', - value: feedback === 'good' ? 1 : 0, - comment: `User gave ${feedback} feedback`, - }, - }, - ], - }); - } - - return Response.json({ success: true, logged: true }); - } catch (error) { - console.error('Langfuse feedback error:', error); - return Response.json({ success: false, error: 'Failed to log feedback' }, { status: 500 }); - } -} diff --git a/app/api/log-save/route.ts b/app/api/log-save/route.ts deleted file mode 100644 index e4862c9..0000000 --- a/app/api/log-save/route.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { getLangfuseClient } from '@/lib/langfuse'; -import { randomUUID } from 'crypto'; -import { z } from 'zod'; - -const saveSchema = z.object({ - filename: z.string().min(1).max(255), - format: z.enum(['drawio', 'png', 'svg']), - sessionId: z.string().min(1).max(200).optional(), -}); - -export async function POST(req: Request) { - const langfuse = getLangfuseClient(); - if (!langfuse) { - return Response.json({ success: true, logged: false }); - } - - // Validate input - let data; - try { - data = saveSchema.parse(await req.json()); - } catch { - return Response.json({ success: false, error: 'Invalid input' }, { status: 400 }); - } - - const { filename, format, sessionId } = data; - - try { - const timestamp = new Date().toISOString(); - - // Find the most recent chat trace for this session to attach the save flag - const tracesResponse = await langfuse.api.trace.list({ - sessionId, - limit: 1, - }); - - const traces = tracesResponse.data || []; - const latestTrace = traces[0]; - - if (latestTrace) { - // Add a score to the existing trace to flag that user saved - await langfuse.api.ingestion.batch({ - batch: [ - { - type: 'score-create', - id: randomUUID(), - timestamp, - body: { - id: randomUUID(), - traceId: latestTrace.id, - name: 'diagram-saved', - value: 1, - comment: `User saved diagram as ${filename}.${format}`, - }, - }, - ], - }); - } - // If no trace found, skip logging (user hasn't chatted yet) - - return Response.json({ success: true, logged: !!latestTrace }); - } catch (error) { - console.error('Langfuse save error:', error); - return Response.json({ success: false, error: 'Failed to log save' }, { status: 500 }); - } -} diff --git a/components/chat-input.tsx b/components/chat-input.tsx index 9f5350f..5d2502d 100644 --- a/components/chat-input.tsx +++ b/components/chat-input.tsx @@ -29,7 +29,6 @@ interface ChatInputProps { onFileChange?: (files: File[]) => void; showHistory?: boolean; onToggleHistory?: (show: boolean) => void; - sessionId?: string; } export function ChatInput({ @@ -42,7 +41,6 @@ export function ChatInput({ onFileChange = () => {}, showHistory = false, onToggleHistory = () => {}, - sessionId, }: ChatInputProps) { const { diagramHistory, saveDiagramToFile } = useDiagram(); const textareaRef = useRef(null); @@ -251,7 +249,7 @@ export function ChatInput({ saveDiagramToFile(filename, format, sessionId)} + onSave={(filename, format) => saveDiagramToFile(filename, format)} defaultFilename={`diagram-${new Date().toISOString().slice(0, 10)}`} /> diff --git a/components/chat-message-display.tsx b/components/chat-message-display.tsx index 089b80b..0011fdd 100644 --- a/components/chat-message-display.tsx +++ b/components/chat-message-display.tsx @@ -6,7 +6,7 @@ import { ScrollArea } from "@/components/ui/scroll-area"; import ExamplePanel from "./chat-example-panel"; import { UIMessage } from "ai"; import { convertToLegalXml, replaceNodes, validateMxCellStructure } from "@/lib/utils"; -import { Copy, Check, X, ChevronDown, ChevronUp, Cpu, Minus, Plus, ThumbsUp, ThumbsDown, RotateCcw, Pencil } from "lucide-react"; +import { Copy, Check, X, ChevronDown, ChevronUp, Cpu, Minus, Plus, RotateCcw, Pencil } from "lucide-react"; import { CodeBlock } from "./code-block"; interface EditPair { @@ -67,7 +67,6 @@ interface ChatMessageDisplayProps { error?: Error | null; setInput: (input: string) => void; setFiles: (files: File[]) => void; - sessionId?: string; onRegenerate?: (messageIndex: number) => void; onEditMessage?: (messageIndex: number, newText: string) => void; } @@ -77,7 +76,6 @@ export function ChatMessageDisplay({ error, setInput, setFiles, - sessionId, onRegenerate, onEditMessage, }: ChatMessageDisplayProps) { @@ -90,7 +88,6 @@ export function ChatMessageDisplay({ ); const [copiedMessageId, setCopiedMessageId] = useState(null); const [copyFailedMessageId, setCopyFailedMessageId] = useState(null); - const [feedback, setFeedback] = useState>({}); const [editingMessageId, setEditingMessageId] = useState(null); const [editText, setEditText] = useState(""); @@ -106,34 +103,6 @@ export function ChatMessageDisplay({ } }; - const submitFeedback = async (messageId: string, value: "good" | "bad") => { - // Toggle off if already selected - if (feedback[messageId] === value) { - setFeedback((prev) => { - const next = { ...prev }; - delete next[messageId]; - return next; - }); - return; - } - - setFeedback((prev) => ({ ...prev, [messageId]: value })); - - try { - await fetch("/api/log-feedback", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - messageId, - feedback: value, - sessionId, - }), - }); - } catch (error) { - console.warn("Failed to log feedback:", error); - } - }; - const handleDisplayChart = useCallback( (xml: string) => { const currentXml = xml || ""; @@ -467,32 +436,6 @@ export function ChatMessageDisplay({ )} - {/* Divider */} -
- {/* Thumbs up */} - - {/* Thumbs down */} -
)} diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index 0fcd694..3edd973 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -62,9 +62,6 @@ 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)}`); - // Store XML snapshots for each user message (keyed by message index) const xmlSnapshotsRef = useRef>(new Map()); @@ -208,7 +205,6 @@ Please retry with an adjusted search pattern or use display_diagram if retries a { body: { xml: chartXml, - sessionId, }, } ); @@ -283,7 +279,6 @@ Please retry with an adjusted search pattern or use display_diagram if retries a { body: { xml: savedXml, - sessionId, }, } ); @@ -337,7 +332,6 @@ Please retry with an adjusted search pattern or use display_diagram if retries a { body: { xml: savedXml, - sessionId, }, } ); @@ -424,7 +418,6 @@ Please retry with an adjusted search pattern or use display_diagram if retries a error={error} setInput={setInput} setFiles={handleFileChange} - sessionId={sessionId} onRegenerate={handleRegenerate} onEditMessage={handleEditMessage} /> @@ -440,14 +433,12 @@ 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)}`); xmlSnapshotsRef.current.clear(); }} files={files} onFileChange={handleFileChange} showHistory={showHistory} onToggleHistory={setShowHistory} - sessionId={sessionId} /> diff --git a/contexts/diagram-context.tsx b/contexts/diagram-context.tsx index 1995f71..a9d5a4f 100644 --- a/contexts/diagram-context.tsx +++ b/contexts/diagram-context.tsx @@ -16,7 +16,7 @@ interface DiagramContextType { drawioRef: React.Ref; handleDiagramExport: (data: any) => void; clearDiagram: () => void; - saveDiagramToFile: (filename: string, format: ExportFormat, sessionId?: string) => void; + saveDiagramToFile: (filename: string, format: ExportFormat) => void; } const DiagramContext = createContext(undefined); @@ -107,7 +107,7 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) { setDiagramHistory([]); }; - const saveDiagramToFile = (filename: string, format: ExportFormat, sessionId?: string) => { + const saveDiagramToFile = (filename: string, format: ExportFormat) => { if (!drawioRef.current) { console.warn("Draw.io editor not ready"); return; @@ -145,9 +145,6 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) { extension = ".svg"; } - // Log save event to Langfuse (flags the trace) - logSaveToLangfuse(filename, format, sessionId); - // Handle download let url: string; if (typeof fileContent === "string" && fileContent.startsWith("data:")) { @@ -177,19 +174,6 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) { drawioRef.current.exportDiagram({ format: drawioFormat }); }; - // Log save event to Langfuse (just flags the trace, doesn't send content) - const logSaveToLangfuse = async (filename: string, format: string, sessionId?: string) => { - try { - await fetch("/api/log-save", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ filename, format, sessionId }), - }); - } catch (error) { - console.warn("Failed to log save to Langfuse:", error); - } - }; - return ( { - const spanName = otelSpan.name; - // Skip Next.js HTTP infrastructure spans - if (spanName.startsWith('POST /') || - spanName.startsWith('GET /') || - spanName.includes('BaseServer') || - spanName.includes('handleRequest')) { - return false; - } - return true; - }, - }); - - const tracerProvider = new NodeTracerProvider({ - spanProcessors: [langfuseSpanProcessor], - }); - - // Register globally so AI SDK's telemetry also uses this processor - tracerProvider.register(); -} diff --git a/lib/langfuse.ts b/lib/langfuse.ts deleted file mode 100644 index a966059..0000000 --- a/lib/langfuse.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { observe, updateActiveTrace } from '@langfuse/tracing'; -import { LangfuseClient } from '@langfuse/client'; -import * as api from '@opentelemetry/api'; - -// Singleton LangfuseClient instance for direct API calls -let langfuseClient: LangfuseClient | null = null; - -export function getLangfuseClient(): LangfuseClient | null { - if (!process.env.LANGFUSE_PUBLIC_KEY || !process.env.LANGFUSE_SECRET_KEY) { - return null; - } - - if (!langfuseClient) { - langfuseClient = new LangfuseClient({ - publicKey: process.env.LANGFUSE_PUBLIC_KEY, - secretKey: process.env.LANGFUSE_SECRET_KEY, - baseUrl: process.env.LANGFUSE_BASEURL, - }); - } - - return langfuseClient; -} - -// Check if Langfuse is configured -export function isLangfuseEnabled(): boolean { - return !!process.env.LANGFUSE_PUBLIC_KEY; -} - -// Update trace with input data at the start of request -export function setTraceInput(params: { - input: string; - sessionId?: string; - userId?: string; -}) { - if (!isLangfuseEnabled()) return; - - updateActiveTrace({ - name: 'chat', - input: params.input, - sessionId: params.sessionId, - userId: params.userId, - }); -} - -// Update trace with output and end the span -export function setTraceOutput(output: string, usage?: { promptTokens?: number; completionTokens?: number }) { - if (!isLangfuseEnabled()) return; - - updateActiveTrace({ output }); - - const activeSpan = api.trace.getActiveSpan(); - if (activeSpan) { - // Manually set usage attributes since AI SDK Bedrock streaming doesn't provide them - if (usage?.promptTokens) { - activeSpan.setAttribute('ai.usage.promptTokens', usage.promptTokens); - activeSpan.setAttribute('gen_ai.usage.input_tokens', usage.promptTokens); - } - if (usage?.completionTokens) { - activeSpan.setAttribute('ai.usage.completionTokens', usage.completionTokens); - activeSpan.setAttribute('gen_ai.usage.output_tokens', usage.completionTokens); - } - activeSpan.end(); - } -} - -// Get telemetry config for streamText -export function getTelemetryConfig(params: { - sessionId?: string; - userId?: string; -}) { - if (!isLangfuseEnabled()) return undefined; - - return { - isEnabled: true, - // Disable automatic input recording to avoid uploading large base64 images to Langfuse media - // User text input is recorded manually via setTraceInput - recordInputs: false, - recordOutputs: true, - metadata: { - sessionId: params.sessionId, - userId: params.userId, - }, - }; -} - -// Wrap a handler with Langfuse observe -export function wrapWithObserve( - handler: (req: Request) => Promise -): (req: Request) => Promise { - if (!isLangfuseEnabled()) { - return handler; - } - - return observe(handler, { name: 'chat', endOnExit: false }); -} diff --git a/package-lock.json b/package-lock.json index 6a2d169..557d606 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,12 +16,8 @@ "@ai-sdk/google": "^2.0.0", "@ai-sdk/openai": "^2.0.19", "@ai-sdk/react": "^2.0.22", - "@langfuse/client": "^4.4.9", - "@langfuse/otel": "^4.4.4", - "@langfuse/tracing": "^4.4.9", "@next/third-parties": "^16.0.6", "@openrouter/ai-sdk-provider": "^1.2.3", - "@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-select": "^2.2.6", @@ -1703,62 +1699,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@langfuse/client": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/@langfuse/client/-/client-4.4.9.tgz", - "integrity": "sha512-Y7bU70tMx/lYOU/A7NGvXXVZoL3AiFigbf7EwS5PVFc0xd34eRUmvwdLwEtuK7CnYTyxIZTzVVP2KEaicWCYZg==", - "license": "MIT", - "dependencies": { - "@langfuse/core": "^4.4.9", - "@langfuse/tracing": "^4.4.9", - "mustache": "^4.2.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0" - } - }, - "node_modules/@langfuse/core": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/@langfuse/core/-/core-4.4.9.tgz", - "integrity": "sha512-9Hz/eH6dkOP8E/FLt1fsAQR8RE/TF8Ag/39GmY8JjN1o/Tl/MFJfK2QvqRGrkjDkIkMJGOSD+iQmV2pYm4upDA==", - "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/@langfuse/tracing": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/@langfuse/tracing/-/tracing-4.4.9.tgz", - "integrity": "sha512-if+G/v9NsyTKj40KKX96bRSdMXwyDbVL4GJQvmwQ9SxvGYF+d99pGFB7L6QOeCd1KBHMdmDe733ncmvCnSHJ9w==", - "license": "MIT", - "dependencies": { - "@langfuse/core": "^4.4.9" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0" - } - }, "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", @@ -2027,273 +1967,6 @@ "node": ">=8.0.0" } }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.208.0", - "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" - }, - "engines": { - "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", - "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.208.0", - "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", - "@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" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", - "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.2.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.208.0", - "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", - "@opentelemetry/resources": "2.2.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz", - "integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/core": "2.2.0", - "@opentelemetry/resources": "2.2.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.9.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", - "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.2.0", - "@opentelemetry/resources": "2.2.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@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", - "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", - "license": "Apache-2.0", - "engines": { - "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", @@ -3790,6 +3463,7 @@ "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" @@ -7471,13 +7145,6 @@ "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", @@ -8361,15 +8028,6 @@ "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", @@ -8879,31 +8537,6 @@ "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", @@ -10106,6 +9739,7 @@ "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 e1e49f5..dfd2863 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,8 @@ "@ai-sdk/google": "^2.0.0", "@ai-sdk/openai": "^2.0.19", "@ai-sdk/react": "^2.0.22", - "@langfuse/client": "^4.4.9", - "@langfuse/otel": "^4.4.4", - "@langfuse/tracing": "^4.4.9", "@next/third-parties": "^16.0.6", "@openrouter/ai-sdk-provider": "^1.2.3", - "@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-select": "^2.2.6",