"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.bookRoutes = void 0; const express_1 = require("express"); const axios_1 = __importDefault(require("axios")); const genreTemplates_js_1 = require("../prompts/genreTemplates.js"); exports.bookRoutes = (0, express_1.Router)(); // Get all available genre templates exports.bookRoutes.get('/genres', (req, res) => { const genres = Object.entries(genreTemplates_js_1.genreTemplates).map(([key, template]) => ({ id: key, name: template.name, description: template.description, icon: template.icon })); res.json({ genres }); }); // Get a specific genre template exports.bookRoutes.get('/genres/:genreId', (req, res) => { const { genreId } = req.params; const template = genreTemplates_js_1.genreTemplates[genreId]; if (!template) { return res.status(404).json({ error: 'Genre not found' }); } res.json({ template }); }); // Generate book outline based on genre and idea exports.bookRoutes.post('/outline', async (req, res) => { try { const { genre, idea, title, language } = req.body; const targetLang = language && language.startsWith('es') ? 'Spanish' : 'English'; if (!genre || !idea) { return res.status(400).json({ error: 'genre and idea are required' }); } const template = genreTemplates_js_1.genreTemplates[genre]; if (!template) { return res.status(400).json({ error: 'Invalid genre' }); } const prompt = `${template.prompts.outline} Book Title: ${title || 'Untitled'} Core Idea: ${idea} Generate a detailed chapter outline following the structure: ${template.structure.join(' → ')} CRITICAL INSTRUCTIONS: - You MUST respond with VALID JSON only - Do NOT include any text before or after the JSON - Do NOT wrap the JSON in markdown code blocks - All text content (titles, summaries, etc.) MUST be in ${targetLang} - JSON keys MUST remain in English - Ensure the JSON is properly formatted and parseable Required JSON format: { "title": "Book Title in ${targetLang}", "genre": "${genre}", "logline": "One sentence summary in ${targetLang}", "chapters": [ {"number": 1, "title": "Chapter Title in ${targetLang}", "summary": "Brief description in ${targetLang}"}, {"number": 2, "title": "Chapter Title in ${targetLang}", "summary": "Brief description in ${targetLang}"} ] }`; const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', { model: 'nvidia/nemotron-3-nano-30b-a3b:free', // Free model on OpenRouter messages: [{ role: 'user', content: prompt }], temperature: 0.7, max_tokens: 2000 }, { headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://creabook.app', 'X-Title': 'CreaBook' } }); const content = response.data.choices[0].message.content; console.log('=== Outline AI Response ==='); console.log('Raw content:', content); console.log('Target language:', targetLang); // Try to parse JSON from response - improved parsing for multilingual content let outline; try { // First try to find JSON object const jsonMatch = content.match(/\{[\s\S]*\}/); if (jsonMatch) { // Clean the JSON string to handle potential encoding issues let jsonString = jsonMatch[0]; // Remove any markdown formatting that might interfere jsonString = jsonString.replace(/```json\s*/g, '').replace(/```\s*$/g, ''); outline = JSON.parse(jsonString); console.log('Parsed outline:', outline); } else { console.error('No JSON object found in response'); outline = { raw: content, error: 'No JSON found in AI response' }; } } catch (parseError) { console.error('JSON parse error:', parseError); console.error('Failed content:', content); outline = { raw: content, error: 'Failed to parse JSON from AI response' }; } res.json({ outline }); } catch (error) { console.error('Outline generation error:', error); res.status(500).json({ error: 'Failed to generate outline', details: error instanceof Error ? error.message : String(error) }); } }); // Generate a chapter based on outline exports.bookRoutes.post('/chapter', async (req, res) => { try { const { genre, chapterTitle, chapterSummary, previousContent, language } = req.body; const targetLang = language && language.startsWith('es') ? 'Spanish' : 'English'; if (!genre || !chapterTitle || !chapterSummary) { return res.status(400).json({ error: 'genre, chapterTitle, and chapterSummary are required' }); } const template = genreTemplates_js_1.genreTemplates[genre]; let prompt = `${template.prompts.chapter} Chapter: ${chapterTitle} Summary: ${chapterSummary} Tone: ${template.defaults.tone} POV: ${template.defaults.pov} CRITICAL: Write the ENTIRE chapter content in ${targetLang} only. Do NOT include any English text. Write a complete, engaging chapter that matches the summary above. The chapter should be between 1500-3000 words. IMPORTANT: Respond with the chapter content directly, without any introduction, conclusion, or formatting markers. Just the narrative text in ${targetLang}.`; if (previousContent) { prompt += `\n\nPrevious content for context:\n${previousContent.substring(0, 2000)}...`; } const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', { model: 'nvidia/nemotron-3-nano-30b-a3b:free', messages: [{ role: 'user', content: prompt }], temperature: 0.7, max_tokens: 3000 }, { headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://creabook.app', 'X-Title': 'CreaBook' } }); const content = response.data.choices[0].message.content; console.log('=== Chapter AI Response ==='); console.log('Target language:', targetLang); console.log('Chapter title:', chapterTitle); console.log('Content length:', content.length); console.log('Content preview:', content.substring(0, 200) + '...'); res.json({ content, chapterTitle }); } catch (error) { console.error('Chapter generation error:', error); res.status(500).json({ error: 'Failed to generate chapter', details: error instanceof Error ? error.message : String(error) }); } }); // Expand or refine text exports.bookRoutes.post('/expand', async (req, res) => { try { const { text, instruction = 'Expand and improve this text', language } = req.body; const targetLang = language && language.startsWith('es') ? 'Spanish' : 'English'; if (!text) { return res.status(400).json({ error: 'text is required' }); } const prompt = `${instruction}: Original text: ${text} Provide an expanded and improved version. IMPORTANT: Write the expanded text entirely in ${targetLang}:`; const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', { model: 'nvidia/nemotron-3-nano-30b-a3b:free', messages: [{ role: 'user', content: prompt }], temperature: 0.7, max_tokens: 2000 }, { headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://creabook.app', 'X-Title': 'CreaBook' } }); const content = response.data.choices[0].message.content; res.json({ expanded: content }); } catch (error) { console.error('Expand error:', error); res.status(500).json({ error: 'Failed to expand text', details: error instanceof Error ? error.message : String(error) }); } }); // Generate character suggestions exports.bookRoutes.post('/characters', async (req, res) => { try { const { genre, storyIdea, language } = req.body; const targetLang = language && language.startsWith('es') ? 'Spanish' : 'English'; if (!genre || !storyIdea) { return res.status(400).json({ error: 'genre and storyIdea are required' }); } const template = genreTemplates_js_1.genreTemplates[genre]; const prompt = `Based on this ${genre} story idea, suggest 3-5 main characters. Story Idea: ${storyIdea} Genre conventions: ${template.description} IMPORTANT: Write the names, traits, motivation, and backstory values completely in ${targetLang}. Keep the required JSON keys strictly in English. Return ONLY a valid JSON array with this exact format: [ { "name": "Character Name", "role": "protagonist|antagonist|supporting", "traits": ["trait1", "trait2"], "motivation": "What drives them", "backstory": "Brief backstory" } ] Do not include any text outside the JSON array.`; console.log('=== Character Generation Prompt ==='); console.log(prompt); const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', { model: 'nvidia/nemotron-3-nano-30b-a3b:free', messages: [{ role: 'user', content: prompt }], temperature: 0.7, max_tokens: 1500 }, { headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://creabook.app', 'X-Title': 'CreaBook' } }); const content = response.data.choices[0].message.content; console.log('=== Character AI Response ==='); console.log('Raw content:', content); let characters; const jsonMatch = content.match(/\[[\s\S]*\]/); if (jsonMatch) { try { characters = JSON.parse(jsonMatch[0]); console.log('Parsed characters:', characters); } catch (parseError) { console.error('JSON parse error:', parseError); characters = { raw: content, error: 'Failed to parse JSON' }; } } else { console.error('No JSON array found in response'); characters = { raw: content, error: 'No JSON array found' }; } res.json({ characters }); } catch (error) { console.error('Character generation error:', error); res.status(500).json({ error: 'Failed to generate characters', details: error instanceof Error ? error.message : String(error) }); } }); // Generate plot suggestions exports.bookRoutes.post('/plot', async (req, res) => { try { const { genre, currentPlot, issue, language } = req.body; const targetLang = language && language.startsWith('es') ? 'Spanish' : 'English'; if (!genre || !currentPlot) { return res.status(400).json({ error: 'genre and currentPlot are required' }); } const template = genreTemplates_js_1.genreTemplates[genre]; const prompt = `Help develop the plot for this ${genre} story: Current Plot: ${currentPlot} ${issue ? `Specific Issue: ${issue}` : 'Suggest plot developments and twists.'} Consider genre conventions: ${template.description} Structure: ${template.structure.join(' → ')} IMPORTANT: Provide all specific, actionable plot suggestions completely in ${targetLang}:`; const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', { model: 'nvidia/nemotron-3-nano-30b-a3b:free', messages: [{ role: 'user', content: prompt }], temperature: 0.7, max_tokens: 1500 }, { headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://creabook.app', 'X-Title': 'CreaBook' } }); const content = response.data.choices[0].message.content; res.json({ suggestions: content }); } catch (error) { console.error('Plot suggestion error:', error); res.status(500).json({ error: 'Failed to generate plot suggestions', details: error instanceof Error ? error.message : String(error) }); } }); //# sourceMappingURL=books.js.map