Major Features:
- Enhanced Mode v2: Smart Adaptive section selection strategy
* AI automatically selects ONE optimal section per iteration
* Intelligent selection based on quality assessment and strategic importance
* Section tracking to avoid duplicate modifications
* Fixed header level preservation (## stays ##, ### stays ###)
* Updated document code block format (document)
- Mermaid Auto-Fix System
* New POST /api/ai/fix-mermaid endpoint for automatic diagram correction
* Automatic error detection in preview and presentation modes
* Inter-window communication for seamless editor updates
* AI-powered syntax error resolution
- Comprehensive Logging System
* Centralized logger utility (utils/logger.js)
* API request/response middleware logging
* AI operations: rephrase, check-inconsistencies, check-duplications, give-advice, liberty-mode, fix-mermaid
* Storage operations: create, read, update, delete journals
* Export operations: PDF and Web ZIP generation
* Structured logging with timestamps, levels, categories, and metadata
Improvements:
- Table of Contents: Clean display with markdown formatting stripped from titles
- Section badges: Fixed visibility with hardcoded blue background (#2563eb)
- Internationalization: Complete English translation (lang, UI text, placeholders, comments)
- Code cleanup: Removed all emojis from codebase
Technical Changes:
- routes/ai.js: Enhanced mode implementation, Mermaid fix endpoint, comprehensive logging
- routes/api.js: Storage operation logging
- routes/export.js: Export operation logging
- routes/index.js: Presentation mode Mermaid auto-fix
- assets/js/app.js: TOC markdown stripping, Mermaid error handling, message listeners
- assets/css/style.css: Dark mode comment, English placeholders, header icon
- views/page.js: English lang attribute, favicon, scroll-to-top button
- views/header.js: Theme toggle icon
- utils/logger.js: New centralized logging system
- app.js: API request/response logging middleware
669 lines
22 KiB
JavaScript
669 lines
22 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const https = require('https');
|
|
const { logger } = require('../utils/logger');
|
|
require('dotenv').config({ path: './config/.env' });
|
|
|
|
// Mistral AI Configuration
|
|
const MISTRAL_API_KEY = process.env.MISTRAL_API_KEY;
|
|
const MISTRAL_BASE_URL = process.env.MISTRAL_BASE_URL || 'https://api.mistral.ai/v1';
|
|
const MISTRAL_MODEL = process.env.MISTRAL_MODEL || 'mistral-large-latest';
|
|
const AI_ENABLED = process.env.AI_ENABLED === 'true';
|
|
|
|
// Verification middleware
|
|
function checkAIEnabled(req, res, next) {
|
|
if (!AI_ENABLED) {
|
|
return res.status(503).json({
|
|
success: false,
|
|
error: 'AI features are disabled'
|
|
});
|
|
}
|
|
|
|
if (!MISTRAL_API_KEY) {
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: 'Mistral API key not configured'
|
|
});
|
|
}
|
|
|
|
next();
|
|
}
|
|
|
|
// Create a custom HTTPS agent with longer timeouts
|
|
const httpsAgent = new https.Agent({
|
|
keepAlive: true,
|
|
timeout: 120000, // 120 seconds
|
|
maxSockets: 5
|
|
});
|
|
|
|
// Function to call Mistral API with timeout handling
|
|
async function callMistralAPI(messages, temperature = null) {
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), 120000); // 120 seconds timeout
|
|
|
|
try {
|
|
const response = await fetch(`${MISTRAL_BASE_URL}/chat/completions`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${MISTRAL_API_KEY}`
|
|
},
|
|
body: JSON.stringify({
|
|
model: MISTRAL_MODEL,
|
|
messages: messages,
|
|
temperature: temperature !== null ? temperature : parseFloat(process.env.AI_TEMPERATURE) || 0.3,
|
|
max_tokens: parseInt(process.env.AI_MAX_TOKENS) || 35000,
|
|
top_p: parseFloat(process.env.AI_TOP_P) || 0.85
|
|
}),
|
|
signal: controller.signal,
|
|
agent: httpsAgent
|
|
});
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
throw new Error(`Mistral API Error: ${response.status} - ${error}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
return data.choices[0].message.content;
|
|
} catch (error) {
|
|
clearTimeout(timeoutId);
|
|
console.error('Mistral API Error:', error);
|
|
|
|
// Better error messages
|
|
if (error.name === 'AbortError') {
|
|
throw new Error('Request timeout: The API took too long to respond (>120s)');
|
|
}
|
|
if (error.cause && error.cause.code === 'UND_ERR_HEADERS_TIMEOUT') {
|
|
throw new Error('Connection timeout: Unable to connect to Mistral API. Check your network or API endpoint.');
|
|
}
|
|
if (error.cause && error.cause.code === 'ECONNREFUSED') {
|
|
throw new Error('Connection refused: Cannot reach Mistral API endpoint.');
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// POST /api/ai/rephrase - Rephrase text
|
|
router.post('/rephrase', checkAIEnabled, async (req, res) => {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
const { text, context = '' } = req.body;
|
|
|
|
logger.aiRequest('rephrase', MISTRAL_MODEL, text.length);
|
|
|
|
if (!text || text.trim().length === 0) {
|
|
logger.aiError('rephrase', new Error('Text to rephrase is required'));
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'Text to rephrase is required'
|
|
});
|
|
}
|
|
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: `You are an assistant specialized in rephrasing technical and design texts.
|
|
|
|
STRICT RULES:
|
|
1. Rephrase the text to improve clarity, style, and fluency
|
|
2. Preserve the technical level and all important details
|
|
3. Respond ONLY with the final rephrased text
|
|
4. No introduction, conclusion, explanation, or commentary
|
|
5. Do not start with "Here is", "The rephrased text" or other preamble
|
|
6. Start directly with the rephrased content
|
|
|
|
${context ? `Document context: ${context}` : ''}`
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: `Rephrase this text: "${text}"`
|
|
}
|
|
];
|
|
|
|
const result = await callMistralAPI(messages, 0.2); // Precise rephrasing
|
|
|
|
logger.aiResponse('rephrase', true, Date.now() - startTime);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
original: text,
|
|
rephrased: result
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
logger.aiError('rephrase', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Error during rephrasing: ' + error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// POST /api/ai/check-inconsistencies - Check for inconsistencies
|
|
router.post('/check-inconsistencies', checkAIEnabled, async (req, res) => {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
const { content } = req.body;
|
|
|
|
logger.aiRequest('check-inconsistencies', MISTRAL_MODEL, content.length);
|
|
|
|
if (!content || content.trim().length === 0) {
|
|
logger.aiError('check-inconsistencies', new Error('Content to analyze is required'));
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'Content to analyze is required'
|
|
});
|
|
}
|
|
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: `You are an expert in technical design document analysis.
|
|
|
|
Analyze the following document and identify all potential inconsistencies:
|
|
- Contradictions in information
|
|
- Conflicting decisions
|
|
- Inconsistencies in terminology
|
|
- Logical issues in architecture or choices
|
|
|
|
Respond directly with your detailed analysis of the inconsistencies found.`
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: `Analyze this document to detect inconsistencies:\n\n${content}`
|
|
}
|
|
];
|
|
|
|
const result = await callMistralAPI(messages, 0.1); // Precise and factual analysis
|
|
|
|
logger.aiResponse('check-inconsistencies', true, Date.now() - startTime);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
analysis: result
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
logger.aiError('check-inconsistencies', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Error during analysis: ' + error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// POST /api/ai/check-duplications - Check for duplications
|
|
router.post('/check-duplications', checkAIEnabled, async (req, res) => {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
const { content } = req.body;
|
|
|
|
logger.aiRequest('check-duplications', MISTRAL_MODEL, content.length);
|
|
|
|
if (!content || content.trim().length === 0) {
|
|
logger.aiError('check-duplications', new Error('Content to analyze is required'));
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'Content to analyze is required'
|
|
});
|
|
}
|
|
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: `You are a content analysis expert.
|
|
|
|
Analyze the following document to identify:
|
|
- Repeated or redundant information
|
|
- Sections that cover the same topic
|
|
- Duplicated explanations
|
|
- Concepts presented multiple times
|
|
|
|
Suggest ways to eliminate these duplications while preserving important information.
|
|
Respond directly with your analysis and suggestions.`
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: `Analyze this document to detect duplications:\n\n${content}`
|
|
}
|
|
];
|
|
|
|
const result = await callMistralAPI(messages, 0.1); // Precise and factual analysis
|
|
|
|
logger.aiResponse('check-duplications', true, Date.now() - startTime);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
analysis: result
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
logger.aiError('check-duplications', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Error during analysis: ' + error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// POST /api/ai/give-advice - Give advice
|
|
router.post('/give-advice', checkAIEnabled, async (req, res) => {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
const { content, domain = 'general' } = req.body;
|
|
|
|
logger.aiRequest('give-advice', MISTRAL_MODEL, content.length);
|
|
|
|
if (!content || content.trim().length === 0) {
|
|
logger.aiError('give-advice', new Error('Content to analyze is required'));
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'Content to analyze is required'
|
|
});
|
|
}
|
|
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: `You are an expert consultant in design and technical architecture in the domain: ${domain}.
|
|
|
|
Analyze the provided design document and give constructive advice to improve it.
|
|
|
|
Focus on:
|
|
- Documentation completeness
|
|
- Clarity of explanations
|
|
- Content organization
|
|
- Domain best practices
|
|
- Important missing points
|
|
- Concrete improvement suggestions
|
|
|
|
Be constructive and practical in your recommendations.
|
|
Respond directly with your advice and improvement suggestions.`
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: `Analyze this design document and provide advice to improve it:\n\n${content}`
|
|
}
|
|
];
|
|
|
|
const result = await callMistralAPI(messages, 0.4); // Balanced advice
|
|
|
|
logger.aiResponse('give-advice', true, Date.now() - startTime);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
advice: result
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
logger.aiError('give-advice', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Error during analysis: ' + error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// POST /api/ai/liberty-mode - Enhanced mode (iterative generation)
|
|
router.post('/liberty-mode', checkAIEnabled, async (req, res) => {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
const { content, iterations = 1, precision = 70, focus = 'design' } = req.body;
|
|
|
|
logger.aiRequest('liberty-mode', MISTRAL_MODEL, content.length);
|
|
|
|
if (!content || content.trim().length === 0) {
|
|
logger.aiError('liberty-mode', new Error('Base content is required'));
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'Base content is required'
|
|
});
|
|
}
|
|
|
|
const maxIterations = Math.min(parseInt(iterations), 10); // Limit to 10 iterations
|
|
const precisionPercent = Math.min(Math.max(parseInt(precision), 10), 100); // Between 10% and 100%
|
|
|
|
logger.info('AI', `Liberty mode started: ${maxIterations} iterations at ${precisionPercent}% precision`);
|
|
|
|
// Configure streaming for real-time responses
|
|
res.writeHead(200, {
|
|
'Content-Type': 'text/event-stream',
|
|
'Cache-Control': 'no-cache',
|
|
'Connection': 'keep-alive',
|
|
'Access-Control-Allow-Origin': '*'
|
|
});
|
|
|
|
let currentContent = content;
|
|
let modifiedSections = []; // Track sections that have been modified
|
|
|
|
for (let i = 0; i < maxIterations; i++) {
|
|
try {
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: `You are a technical design expert with "Enhanced Mode - Smart Adaptive Strategy".
|
|
|
|
MISSION: Analyze the entire document and choose ONE section to improve in this iteration.
|
|
|
|
SMART SECTION SELECTION CRITERIA:
|
|
1. **Quality Assessment**: Identify sections that need improvement (incomplete, vague, lacking details)
|
|
2. **Strategic Importance**: Prioritize critical sections (Introduction, Architecture, Core APIs)
|
|
3. **Coverage**: Avoid sections recently modified: ${modifiedSections.length > 0 ? modifiedSections.join(', ') : 'None yet'}
|
|
4. **Balance**: Ensure progressive document enhancement
|
|
|
|
PRECISION RULES:
|
|
- At ${precisionPercent}%: You can deduce and add content up to ${precisionPercent}% based on existing information
|
|
- At ${100 - precisionPercent}%: You can create logical and relevant content even without explicit info in the text
|
|
|
|
SECTION BOUNDARIES - CRITICAL UNDERSTANDING:
|
|
A section is defined by its header and extends until:
|
|
- The next header of EQUAL or HIGHER level appears
|
|
- The end of the document is reached
|
|
|
|
Example structure:
|
|
```
|
|
# Main Title (level 1)
|
|
content...
|
|
|
|
## Section A (level 2) ← Section A starts here
|
|
content A...
|
|
|
|
### Subsection A1 (level 3) ← Part of Section A
|
|
content A1...
|
|
|
|
### Subsection A2 (level 3) ← Part of Section A
|
|
content A2...
|
|
|
|
## Section B (level 2) ← Section A ends, Section B starts
|
|
content B...
|
|
```
|
|
|
|
If you choose to modify "### Subsection A1":
|
|
- Include ONLY the content from "### Subsection A1" until "### Subsection A2"
|
|
- DO NOT include "### Subsection A2" or anything after it
|
|
- DO NOT include "## Section B" or anything after it
|
|
- The rest of the document (before and after) must remain EXACTLY as it was
|
|
|
|
INSTRUCTIONS:
|
|
1. **ANALYZE** all sections in the document
|
|
2. **CHOOSE** exactly ONE section to improve (based on criteria above)
|
|
3. **IDENTIFY BOUNDARIES**: Determine where the chosen section starts and ends:
|
|
- Starts: At the section's header (e.g., "### API Documentation")
|
|
- Ends: Right BEFORE the next header of equal or higher level, OR at document end
|
|
4. **ENHANCE** only that chosen section with:
|
|
- Additional technical details
|
|
- Better explanations
|
|
- Code examples if relevant
|
|
- Mermaid diagrams if relevant (using \`\`\`mermaid blocks)
|
|
- Best practices
|
|
5. **PRESERVE** everything else:
|
|
- ALL content BEFORE the chosen section must remain unchanged
|
|
- ALL content AFTER the chosen section must remain unchanged
|
|
- ALL other sections at any level must remain unchanged
|
|
6. **MAINTAIN** document structure and formatting
|
|
7. **CRITICAL**: Keep the EXACT same header level for the modified section
|
|
- If the original section uses ## (level 2), the modified section MUST use ##
|
|
- If the original section uses ### (level 3), the modified section MUST use ###
|
|
- DO NOT change header levels (## to ###, or ### to ##, etc.)
|
|
8. **VERIFY**: Before returning, check that the document contains ALL original sections
|
|
|
|
MANDATORY RESPONSE FORMAT - Use code blocks with triple backticks:
|
|
|
|
First, write your analysis and decision inside a code block labeled "comment${i + 1}":
|
|
- **Analyzed Sections**: List all major sections found
|
|
- **Selected Section**: Which section you chose to improve (exact header name)
|
|
- **Selection Reason**: Why this section needs improvement most
|
|
- **Improvements Made**: Detailed list of enhancements
|
|
- **Next Recommendations**: Sections to consider for future iterations
|
|
|
|
Then, write the complete markdown document inside a code block labeled "document".
|
|
The document must include ALL sections, with ONLY the selected section modified.
|
|
|
|
Example format:
|
|
\`\`\`comment${i + 1}
|
|
**Analyzed Sections**: Introduction, Architecture, API Documentation, Conclusion
|
|
**Selected Section**: ### API Documentation
|
|
**Selection Reason**: This section lacks implementation details and error handling
|
|
**Improvements Made**:
|
|
- Added authentication flow
|
|
- Included error handling scenarios
|
|
- Added code examples
|
|
**Next Recommendations**: Consider enhancing Architecture section
|
|
\`\`\`
|
|
|
|
\`\`\`document
|
|
[Full document with only API Documentation section improved]
|
|
\`\`\`document
|
|
|
|
Focus: ${focus}
|
|
Precision: ${precisionPercent}%
|
|
Iteration: ${i + 1}/${maxIterations}
|
|
Previously Modified: ${modifiedSections.length > 0 ? modifiedSections.join(', ') : 'None'}`
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: `Document to analyze and improve (Iteration ${i + 1}):\n\n${currentContent}`
|
|
}
|
|
];
|
|
|
|
// Temperature based on precision (more creative = higher temperature)
|
|
const temperature = (100 - precisionPercent) / 100 * 0.8 + 0.1; // Between 0.1 and 0.9
|
|
|
|
const result = await callMistralAPI(messages, temperature);
|
|
|
|
// Extract code blocks
|
|
const commentRegex = /```comment\d+\s*([\s\S]*?)```/;
|
|
const documentRegex = /```document\s*([\s\S]*?)```document/;
|
|
|
|
const commentMatch = result.match(commentRegex);
|
|
let documentMatch = result.match(documentRegex);
|
|
|
|
// Fallback: if new format not found, try old format
|
|
if (!documentMatch) {
|
|
const oldDocumentRegex = /```document\s*([\s\S]*?)```/;
|
|
documentMatch = result.match(oldDocumentRegex);
|
|
}
|
|
|
|
let explanation = '';
|
|
let newMarkdown = currentContent; // Default, keep old content
|
|
let selectedSection = '';
|
|
|
|
if (commentMatch && commentMatch[1]) {
|
|
explanation = commentMatch[1].trim();
|
|
|
|
// Extract selected section name from comment
|
|
const sectionMatch = explanation.match(/\*\*Selected Section\*\*:?\s*(.+?)(?:\n|\*\*|$)/i);
|
|
if (sectionMatch && sectionMatch[1]) {
|
|
selectedSection = sectionMatch[1].trim();
|
|
// Add to modified sections tracker
|
|
if (!modifiedSections.includes(selectedSection)) {
|
|
modifiedSections.push(selectedSection);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (documentMatch && documentMatch[1]) {
|
|
newMarkdown = documentMatch[1].trim();
|
|
// Update for next iteration
|
|
currentContent = newMarkdown;
|
|
}
|
|
|
|
// Fallback: if no code blocks found, try old format for compatibility
|
|
if (!commentMatch && !documentMatch) {
|
|
const parts = result.split('---SPLIT---');
|
|
if (parts.length >= 2) {
|
|
explanation = parts[0].trim();
|
|
newMarkdown = parts[1].trim();
|
|
currentContent = newMarkdown;
|
|
} else {
|
|
explanation = result;
|
|
}
|
|
}
|
|
|
|
// Send this iteration's response
|
|
const iterationData = {
|
|
iteration: i + 1,
|
|
explanation: explanation,
|
|
selectedSection: selectedSection || 'Unknown',
|
|
markdown: newMarkdown,
|
|
completed: false
|
|
};
|
|
|
|
res.write(`data: ${JSON.stringify(iterationData)}\n\n`);
|
|
|
|
// Small delay to allow client-side display
|
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
|
} catch (iterationError) {
|
|
console.error(`Iteration ${i + 1} error:`, iterationError);
|
|
|
|
const errorData = {
|
|
iteration: i + 1,
|
|
error: `Iteration ${i + 1} error: ${iterationError.message}`,
|
|
completed: true
|
|
};
|
|
|
|
res.write(`data: ${JSON.stringify(errorData)}\n\n`);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// End signal
|
|
const finalData = {
|
|
completed: true,
|
|
totalIterations: maxIterations,
|
|
finalMarkdown: currentContent
|
|
};
|
|
|
|
res.write(`data: ${JSON.stringify(finalData)}\n\n`);
|
|
res.end();
|
|
|
|
logger.aiResponse('liberty-mode', true, Date.now() - startTime);
|
|
|
|
} catch (error) {
|
|
logger.aiError('liberty-mode', error);
|
|
|
|
const errorData = {
|
|
error: 'Error during generation: ' + error.message,
|
|
completed: true
|
|
};
|
|
|
|
res.write(`data: ${JSON.stringify(errorData)}\n\n`);
|
|
res.end();
|
|
}
|
|
});
|
|
|
|
// POST /api/ai/fix-mermaid - Fix Mermaid diagram errors
|
|
router.post('/fix-mermaid', checkAIEnabled, async (req, res) => {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
const { mermaidCode, error } = req.body;
|
|
|
|
logger.aiRequest('fix-mermaid', MISTRAL_MODEL, mermaidCode ? mermaidCode.length : 0);
|
|
|
|
if (!mermaidCode || mermaidCode.trim().length === 0) {
|
|
logger.aiError('fix-mermaid', new Error('Mermaid code is required'));
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'Mermaid code is required'
|
|
});
|
|
}
|
|
|
|
if (!error || error.trim().length === 0) {
|
|
logger.aiError('fix-mermaid', new Error('Error message is required'));
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'Error message is required'
|
|
});
|
|
}
|
|
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: `You are a Mermaid diagram syntax expert.
|
|
|
|
MISSION: Fix the Mermaid diagram syntax error.
|
|
|
|
STRICT RULES:
|
|
1. Analyze the error message carefully
|
|
2. Identify the syntax issue in the Mermaid code
|
|
3. Fix ONLY the syntax error while preserving the diagram's intent and content
|
|
4. Return ONLY the corrected Mermaid code
|
|
5. Do NOT add explanations, comments, or any text outside the code block
|
|
6. Do NOT wrap in \`\`\`mermaid - respond with raw Mermaid code only
|
|
|
|
Common Mermaid syntax issues to watch for:
|
|
- Invalid character sequences in node labels
|
|
- Missing quotes around special characters
|
|
- Incorrect bracket/parenthesis syntax for node shapes
|
|
- Invalid keywords or operators
|
|
- Line break issues (\n should be used carefully)
|
|
- Special characters that need escaping: (, ), [, ], {, }, <, >, ", '`
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: `Fix this Mermaid diagram that has a syntax error.
|
|
|
|
ERROR:
|
|
${error}
|
|
|
|
MERMAID CODE:
|
|
${mermaidCode}
|
|
|
|
Return ONLY the corrected Mermaid code without any explanation or markdown formatting.`
|
|
}
|
|
];
|
|
|
|
const result = await callMistralAPI(messages, 0.1); // Very precise
|
|
|
|
logger.aiResponse('fix-mermaid', true, Date.now() - startTime);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
originalCode: mermaidCode,
|
|
fixedCode: result.trim(),
|
|
error: error
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
logger.aiError('fix-mermaid', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Error fixing Mermaid diagram: ' + error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// GET /api/ai/status - AI status
|
|
router.get('/status', (req, res) => {
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
enabled: AI_ENABLED,
|
|
model: MISTRAL_MODEL,
|
|
configured: !!MISTRAL_API_KEY
|
|
}
|
|
});
|
|
});
|
|
|
|
module.exports = router; |