import OpenAI from 'openai' import type { QuestPlan, GeneratedPoint, PointContent } from '../types.js' import { getSystemPrompt } from '../prompts/system.js' import { getStepGenerationPrompt } from '../prompts/step-generation.js' import { getPointContentPrompt } from '../prompts/point-content.js' import { getReactionPrompt } from '../prompts/reaction.js' const client = new OpenAI({ baseURL: process.env.LLM_BASE_URL || 'https://api.deepseek.com', apiKey: process.env.LLM_API_KEY || '', }) const MODEL = process.env.LLM_MODEL || 'deepseek-chat' class AIService { async generateQuestPlan(params: { city: string days: number companions: string pace: string userComment?: string lang: string }): Promise { const systemPrompt = getSystemPrompt(params.lang) const userPrompt = `Create a quest plan for a traveler: - City: ${params.city} - Duration: ${params.days} day(s) - Companions: ${params.companions} - Pace: ${params.pace} ${params.userComment ? `- Special wishes: ${params.userComment}` : ''} Return a JSON object with: { "title": "Creative quest title (in ${params.lang === 'ru' ? 'Russian' : 'English'})", "description": "1-2 sentence quest description that sets the mood (in ${params.lang === 'ru' ? 'Russian' : 'English'})", "dayThemes": ["Theme for day 1", "Theme for day 2", ...] } IMPORTANT: Return ONLY valid JSON, no markdown, no extra text.` const response = await client.chat.completions.create({ model: MODEL, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt }, ], temperature: 0.8, max_tokens: 1000, }) const text = response.choices[0]?.message?.content?.trim() || '{}' try { // Try to extract JSON from potential markdown code blocks const jsonMatch = text.match(/\{[\s\S]*\}/) const parsed = JSON.parse(jsonMatch ? jsonMatch[0] : text) return { title: parsed.title || `Adventure in ${params.city}`, description: parsed.description || 'Your personal city quest awaits!', dayThemes: parsed.dayThemes || Array(params.days).fill('Exploration'), } } catch { console.error('Failed to parse quest plan:', text) return { title: params.lang === 'ru' ? `Приключение в городе ${params.city}` : `Adventure in ${params.city}`, description: params.lang === 'ru' ? 'Твой персональный городской квест ждёт!' : 'Your personal city quest awaits!', dayThemes: Array(params.days).fill( params.lang === 'ru' ? 'Исследование' : 'Exploration' ), } } } async generatePoint(params: { city: string dayNumber: number totalDays: number dayTheme: string pace: string companions: string userComment?: string previousPoints: string[] lang: string }): Promise { const systemPrompt = getSystemPrompt(params.lang) const userPrompt = getStepGenerationPrompt(params) const response = await client.chat.completions.create({ model: MODEL, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt }, ], temperature: 0.8, max_tokens: 500, }) const text = response.choices[0]?.message?.content?.trim() || '{}' try { const jsonMatch = text.match(/\{[\s\S]*\}/) const parsed = JSON.parse(jsonMatch ? jsonMatch[0] : text) return { title: parsed.title || 'Unknown Location', teaserText: parsed.teaserText || 'A mysterious place awaits...', locationLat: parsed.locationLat || 0, locationLon: parsed.locationLon || 0, isLastPointOfDay: parsed.isLastPointOfDay || false, } } catch { console.error('Failed to parse generated point:', text) return { title: 'Mystery Spot', teaserText: params.lang === 'ru' ? 'Загадочное место ждёт тебя...' : 'A mysterious place awaits...', locationLat: 0, locationLon: 0, isLastPointOfDay: false, } } } async generatePointContent(params: { city: string pointTitle: string dayNumber: number companions: string pace: string lang: string }): Promise { const systemPrompt = getSystemPrompt(params.lang) const userPrompt = getPointContentPrompt(params) const response = await client.chat.completions.create({ model: MODEL, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt }, ], temperature: 0.7, max_tokens: 2000, }) const text = response.choices[0]?.message?.content?.trim() || '' return { contentText: text } } async generateReaction(params: { eventType: string pointTitle?: string city: string lang: string companions: string pace: string }): Promise { const systemPrompt = getSystemPrompt(params.lang) const userPrompt = getReactionPrompt(params) const response = await client.chat.completions.create({ model: MODEL, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt }, ], temperature: 0.8, max_tokens: 300, }) return response.choices[0]?.message?.content?.trim() || '' } } export const aiService = new AIService()