Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/benbo-se_seoanalyze.json
T

1 line
5.5 KiB
JSON

{"repo": "benbo-se/seoanalyze", "files": {"frontend/src/app/api/chatbot/ask/route.js": {"c7e981acde256c89e1739602711d9c8940c39094": {"t": 1785745543.7660472, "n": 5195, "h": "8921ddedea71c83d", "c": "import { NextResponse } from 'next/server';\nimport { prisma } from '@/core/prisma';\n\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/v1/chat/completions';\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request) {\n const startTime = Date.now();\n\n try {\n const { message, sessionId, context } = await request.json();\n\n // Validate input\n if (!message?.trim()) {\n return NextResponse.json({ error: 'Message required' }, { status: 400 });\n }\n\n if (message.length > 500) {\n return NextResponse.json({ error: 'Message too long (max 500 characters)' }, { status: 400 });\n }\n\n if (!sessionId) {\n return NextResponse.json({ error: 'Session ID required' }, { status: 400 });\n }\n\n // Build system prompt based on context\n const systemPrompt = buildSystemPrompt(context);\n\n // Call DeepSeek API\n const response = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`\n },\n body: JSON.stringify({\n model: 'deepseek-chat',\n messages: [\n { role: 'system', content: systemPrompt },\n ...buildConversationHistory(context.conversationHistory),\n { role: 'user', content: message }\n ],\n temperature: 0.7,\n max_tokens: 500,\n top_p: 0.9\n })\n });\n\n if (!response.ok) {\n const errorData = await response.text();\n console.error('DeepSeek API error:', response.status, errorData);\n throw new Error(`DeepSeek API error: ${response.status}`);\n }\n\n const data = await response.json();\n const answer = data.choices[0].message.content;\n const tokensUsed = data.usage?.total_tokens || 0;\n const cost = 0.02; // Approximate cost per request\n const responseTime = Date.now() - startTime;\n\n // Log to database\n const chatLog = await prisma.chatLog.create({\n data: {\n sessionId,\n userMessage: message,\n botResponse: answer,\n currentPage: context?.currentPage || null,\n analysisId: context?.analysisId || null,\n responseTime,\n tokensUsed,\n cost\n }\n });\n\n return NextResponse.json({\n answer,\n logId: chatLog.id,\n responseTime\n });\n\n } catch (error) {\n console.error('Chatbot error:', error);\n return NextResponse.json({\n error: 'Ett fel uppstod. Försök igen eller kontakta reda@benbo.se',\n details: error.message\n }, { status: 500 });\n }\n}\n\nfunction buildSystemPrompt(context) {\n let prompt = `Du är en hjälpsam kundservice-bot för SEO Analyzer (seoanalyze.se).\n\nVÅR TJÄNST:\n- SEO-analys: Analyserar en sidas SEO-kvalitet (30-60s). Kollar title, meta, headings, bilder, LIX-läsbarhet.\n- Crawl-analys: Crawlar hela webbplatsen, hittar 404, duplicates, tekniska problem (2-5 min).\n- Lighthouse: Mäter prestanda och Core Web Vitals (LCP, FCP, TBT, CLS) (1-3 min).\n- AI-analys: Kombinerar allt ovan + AI-rekommendationer + konkurrentjämförelse (60-90s).\n\nVIKTIGT:\n- Allt är GRATIS under beta\n- Ingen registrering krävs\n- Vi stödjer svenska webbplatser (LIX-analys)\n- DeepSeek-powered AI för smarta rekommendationer\n\nVANLIGA TERMER:\n- LCP (Largest Contentful Paint) = Hur lång tid innan största innehållet syns. Bör vara <2.5s.\n- FCP (First Contentful Paint) = Tid tills första innehållet syns. Bör vara <1.8s.\n- TBT (Total Blocking Time) = Hur lång tid JavaScript blockerar sidan. Bör vara <200ms.\n- CLS (Cumulative Layout Shift) = Hur mycket sidan \"hoppar\" när den laddas. Bör vara <0.1.\n- LIX = Läsbarhetsindex för svenska texter. 30-40 = lätt, 40-50 = medel, 50+ = svår.\n- Score 0 i Crawl = INTE att sajten är trasig, utan att det finns förbättringspotential (404, duplicates, etc).\n\nSUPPORT:\n- Om analys fastnar >5 min, be användare uppdatera sidan eller testa igen\n- Om error 429, säg att de träffat rate limit och ska vänta 1 minut\n- Om error 500, be dem kontakta reda@benbo.se\n\nUPPGIFT:\n- Svara på SVENSKA\n- Var vänlig, hjälpsam och uppmuntrande\n- Förklara tekniska termer i klartext\n- Max 120 ord per svar\n- Använd emojis sparsamt (max 1-2 per svar)\n- Ge konkreta, actionable råd\n- Om användare frågar om konkurrenter (Screaming Frog, etc), var ärlig om vad vi är bättre/sämre på`;\n\n // Add context if user is on a results page\n if (context?.currentPage) {\n if (context.currentPage.includes('/analyses/') || context.currentPage.includes('/ai-analys/')) {\n prompt += `\\n\\nKONTEXT: Användaren tittar just nu på en analysresultat-sida. De har kört en analys och ser resultaten.`;\n }\n\n if (context.currentPage.includes('/ai-analys/')) {\n prompt += `\\nDe tittar specifikt på en AI-analys med konkurrentjämförelse.`;\n }\n }\n\n if (context?.analysisId) {\n prompt += `\\nAnalysis ID: ${context.analysisId}`;\n }\n\n return prompt;\n}\n\nfunction buildConversationHistory(history) {\n if (!history || history.length === 0) return [];\n\n // Only take last 6 messages (3 exchanges) to avoid token limit\n const recentHistory = history.slice(-6);\n\n return recentHistory.map(msg => ({\n role: msg.role === 'user' ? 'user' : 'assistant',\n content: msg.content\n }));\n}\n"}}}}