@probelabs/probe 0.6.0-rc109 → 0.6.0-rc113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/agent/ProbeAgent.js +5 -2
- package/build/agent/index.js +26378 -4808
- package/build/agent/schemaUtils.js +276 -794
- package/cjs/agent/ProbeAgent.cjs +26084 -4514
- package/cjs/index.cjs +26084 -4514
- package/package.json +2 -1
- package/src/agent/ProbeAgent.js +5 -2
- package/src/agent/schemaUtils.js +276 -794
package/src/agent/schemaUtils.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { createMessagePreview } from '../tools/common.js';
|
|
7
|
+
import { validate, fixText, extractMermaidBlocks } from '@probelabs/maid';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* HTML entity decoder map for common entities that might appear in mermaid diagrams
|
|
@@ -482,8 +483,9 @@ export function extractMermaidFromMarkdown(response) {
|
|
|
482
483
|
|
|
483
484
|
while ((match = mermaidBlockRegex.exec(response)) !== null) {
|
|
484
485
|
const attributes = match[1] ? match[1].trim() : '';
|
|
485
|
-
|
|
486
|
-
|
|
486
|
+
// Don't trim the content - maid 0.0.6 requires trailing newlines for sequence diagrams
|
|
487
|
+
const fullContent = match[2];
|
|
488
|
+
|
|
487
489
|
// If attributes exist, they were captured separately, so fullContent is just the diagram
|
|
488
490
|
// If no attributes, the first line of fullContent might be diagram type or actual content
|
|
489
491
|
diagrams.push({
|
|
@@ -544,135 +546,47 @@ export async function validateMermaidDiagram(diagram) {
|
|
|
544
546
|
}
|
|
545
547
|
|
|
546
548
|
try {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
+
// Don't trim the diagram - maid 0.0.6 requires trailing newlines for sequence diagrams
|
|
550
|
+
// and handles leading/trailing whitespace correctly
|
|
551
|
+
|
|
549
552
|
// Check for markdown code block markers
|
|
550
|
-
if (
|
|
551
|
-
return {
|
|
552
|
-
isValid: false,
|
|
553
|
+
if (diagram.includes('```')) {
|
|
554
|
+
return {
|
|
555
|
+
isValid: false,
|
|
553
556
|
error: 'Diagram contains markdown code block markers',
|
|
554
557
|
detailedError: 'Mermaid diagram should not contain ``` markers when extracted from markdown'
|
|
555
558
|
};
|
|
556
559
|
}
|
|
557
560
|
|
|
558
|
-
//
|
|
559
|
-
const
|
|
560
|
-
{ pattern: /^(graph|flowchart)/i, type: 'flowchart' },
|
|
561
|
-
{ pattern: /^sequenceDiagram/i, type: 'sequence' },
|
|
562
|
-
{ pattern: /^gantt/i, type: 'gantt' },
|
|
563
|
-
{ pattern: /^pie/i, type: 'pie' },
|
|
564
|
-
{ pattern: /^stateDiagram/i, type: 'state' },
|
|
565
|
-
{ pattern: /^classDiagram/i, type: 'class' },
|
|
566
|
-
{ pattern: /^erDiagram/i, type: 'er' },
|
|
567
|
-
{ pattern: /^journey/i, type: 'journey' },
|
|
568
|
-
{ pattern: /^gitgraph/i, type: 'gitgraph' },
|
|
569
|
-
{ pattern: /^requirementDiagram/i, type: 'requirement' },
|
|
570
|
-
{ pattern: /^C4Context/i, type: 'c4' },
|
|
571
|
-
];
|
|
561
|
+
// Use maid to validate the diagram
|
|
562
|
+
const result = validate(diagram);
|
|
572
563
|
|
|
573
|
-
//
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
if (!diagramType) {
|
|
583
|
-
return {
|
|
584
|
-
isValid: false,
|
|
585
|
-
error: 'Diagram does not match any known Mermaid diagram pattern',
|
|
586
|
-
detailedError: 'The diagram must start with a valid Mermaid diagram type (graph, sequenceDiagram, gantt, pie, etc.)'
|
|
564
|
+
// Maid returns { type: string, errors: array }
|
|
565
|
+
// Valid if errors array is empty
|
|
566
|
+
if (result.errors && result.errors.length === 0) {
|
|
567
|
+
return {
|
|
568
|
+
isValid: true,
|
|
569
|
+
diagramType: result.type || 'unknown'
|
|
587
570
|
};
|
|
588
|
-
}
|
|
571
|
+
} else {
|
|
572
|
+
// Format maid errors into a readable error message
|
|
573
|
+
const errorMessages = (result.errors || []).map(err => {
|
|
574
|
+
const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ''}` : '';
|
|
575
|
+
return location ? `${location} - ${err.message}` : err.message;
|
|
576
|
+
});
|
|
589
577
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
// Check for GitHub-incompatible patterns that cause "got 'PS'" errors
|
|
598
|
-
if (diagramType === 'flowchart') {
|
|
599
|
-
// Check for unbalanced brackets in node labels
|
|
600
|
-
const brackets = line.match(/\[[^\]]*$/); // Unclosed bracket
|
|
601
|
-
if (brackets) {
|
|
602
|
-
return {
|
|
603
|
-
isValid: false,
|
|
604
|
-
error: `Unclosed bracket on line ${i + 1}`,
|
|
605
|
-
detailedError: `Line "${line}" contains an unclosed bracket`
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// GitHub-strict: Check for parentheses inside node labels (causes PS token error)
|
|
610
|
-
// But allow parentheses inside double-quoted strings
|
|
611
|
-
const nodeWithParens = line.match(/\[[^"\[\]]*\([^"\[\]]*\]/);
|
|
612
|
-
if (nodeWithParens) {
|
|
613
|
-
return {
|
|
614
|
-
isValid: false,
|
|
615
|
-
error: `Parentheses in node label on line ${i + 1} (GitHub incompatible)`,
|
|
616
|
-
detailedError: `Line "${line}" contains parentheses inside node label brackets. GitHub mermaid renderer fails with 'got PS' error. Use quotes or escape characters instead.`
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// GitHub-strict: Check for single quotes and backticks inside node labels (causes PS token error)
|
|
621
|
-
const nodeWithQuotes = line.match(/\{[^{}]*['`][^{}]*\}|\[[^[\]]*['`][^[\]]*\]/);
|
|
622
|
-
if (nodeWithQuotes) {
|
|
623
|
-
const hasBacktick = line.includes('`');
|
|
624
|
-
const quoteType = hasBacktick ? 'backticks' : 'single quotes';
|
|
625
|
-
return {
|
|
626
|
-
isValid: false,
|
|
627
|
-
error: `${hasBacktick ? 'Backticks' : 'Single quotes'} in node label on line ${i + 1} (GitHub incompatible)`,
|
|
628
|
-
detailedError: `Line "${line}" contains ${quoteType} inside node label. GitHub mermaid renderer fails with 'got PS' error. Use double quotes or escape characters instead.`
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// GitHub-strict: Check for complex expressions inside diamond nodes
|
|
633
|
-
// Allow double-quoted strings in diamond nodes, but catch problematic single quotes and complex expressions
|
|
634
|
-
// Allow HTML breaks (<br/>, <br>, etc.) but catch other problematic patterns
|
|
635
|
-
const diamondWithComplexContent = line.match(/\{[^"{}]*[()'"<>&][^"{}]*\}/);
|
|
636
|
-
const hasHtmlBreak = line.match(/\{[^{}]*<br\s*\/?>.*\}/);
|
|
637
|
-
if (diamondWithComplexContent && !line.match(/\{\"[^\"]*\"\}/) && !hasHtmlBreak) {
|
|
638
|
-
return {
|
|
639
|
-
isValid: false,
|
|
640
|
-
error: `Complex expression in diamond node on line ${i + 1} (GitHub incompatible)`,
|
|
641
|
-
detailedError: `Line "${line}" contains special characters in diamond node that may cause GitHub parsing errors. Use simpler text or escape characters.`
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
// GitHub-strict: Check for parentheses in subgraph labels (causes PS token error)
|
|
646
|
-
if (line.startsWith('subgraph ') && line.match(/subgraph\s+[^"]*\([^"]*\)/)) {
|
|
647
|
-
return {
|
|
648
|
-
isValid: false,
|
|
649
|
-
error: `Parentheses in subgraph label on line ${i + 1} (GitHub incompatible)`,
|
|
650
|
-
detailedError: `Line "${line}" contains parentheses in subgraph label. GitHub mermaid renderer fails with 'got PS' error. Use quotes around the label or avoid parentheses.`
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
if (diagramType === 'sequence') {
|
|
656
|
-
// Check for missing colon in sequence messages
|
|
657
|
-
if (line.includes('->>') && !line.includes(':')) {
|
|
658
|
-
return {
|
|
659
|
-
isValid: false,
|
|
660
|
-
error: `Missing colon in sequence message on line ${i + 1}`,
|
|
661
|
-
detailedError: `Line "${line}" appears to be a sequence message but is missing a colon`
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
|
-
}
|
|
578
|
+
return {
|
|
579
|
+
isValid: false,
|
|
580
|
+
diagramType: result.type || 'unknown',
|
|
581
|
+
error: errorMessages[0] || 'Validation failed',
|
|
582
|
+
detailedError: errorMessages.join('\n'),
|
|
583
|
+
errors: result.errors || [] // Include raw maid errors for AI fixing
|
|
584
|
+
};
|
|
665
585
|
}
|
|
666
586
|
|
|
667
|
-
// If we get here, basic validation passed
|
|
668
|
-
return {
|
|
669
|
-
isValid: true,
|
|
670
|
-
diagramType
|
|
671
|
-
};
|
|
672
|
-
|
|
673
587
|
} catch (error) {
|
|
674
|
-
return {
|
|
675
|
-
isValid: false,
|
|
588
|
+
return {
|
|
589
|
+
isValid: false,
|
|
676
590
|
error: error.message || 'Unknown mermaid parsing error',
|
|
677
591
|
detailedError: error.stack || error.toString()
|
|
678
592
|
};
|
|
@@ -758,6 +672,61 @@ Ensure all Mermaid diagrams are properly formatted within \`\`\`mermaid code blo
|
|
|
758
672
|
return prompt;
|
|
759
673
|
}
|
|
760
674
|
|
|
675
|
+
/**
|
|
676
|
+
* Use maid to attempt auto-fixing of mermaid diagrams
|
|
677
|
+
* @param {string} diagramContent - The diagram content to fix
|
|
678
|
+
* @param {Object} options - Fix options
|
|
679
|
+
* @returns {Object} - {fixed: string, wasFixed: boolean, errors: Array}
|
|
680
|
+
*/
|
|
681
|
+
export async function tryMaidAutoFix(diagramContent, options = {}) {
|
|
682
|
+
const { debug = false } = options;
|
|
683
|
+
|
|
684
|
+
try {
|
|
685
|
+
// Always use 'all' level fixes (most aggressive)
|
|
686
|
+
if (debug) {
|
|
687
|
+
console.log(`[DEBUG] Mermaid maid: Trying 'all' level auto-fixes...`);
|
|
688
|
+
}
|
|
689
|
+
const result = fixText(diagramContent, { level: 'all' });
|
|
690
|
+
const validation = validate(result.fixed);
|
|
691
|
+
|
|
692
|
+
// Maid validation returns { type, errors }
|
|
693
|
+
// Valid if errors array is empty
|
|
694
|
+
if (validation.errors && validation.errors.length === 0) {
|
|
695
|
+
if (debug) {
|
|
696
|
+
console.log(`[DEBUG] Mermaid maid: 'All' level fixes succeeded`);
|
|
697
|
+
}
|
|
698
|
+
return {
|
|
699
|
+
fixed: result.fixed,
|
|
700
|
+
wasFixed: result.fixed !== diagramContent,
|
|
701
|
+
errors: [],
|
|
702
|
+
fixLevel: 'all'
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Maid couldn't fix it completely, return the best attempt with remaining errors
|
|
707
|
+
if (debug) {
|
|
708
|
+
console.log(`[DEBUG] Mermaid maid: Auto-fixes couldn't resolve all issues, ${validation.errors?.length || 0} errors remain`);
|
|
709
|
+
}
|
|
710
|
+
return {
|
|
711
|
+
fixed: result.fixed,
|
|
712
|
+
wasFixed: result.fixed !== diagramContent,
|
|
713
|
+
errors: validation.errors || [], // Pass maid's structured errors for AI fixing
|
|
714
|
+
fixLevel: 'all'
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
} catch (error) {
|
|
718
|
+
if (debug) {
|
|
719
|
+
console.error(`[DEBUG] Mermaid maid: Auto-fix error: ${error.message}`);
|
|
720
|
+
}
|
|
721
|
+
return {
|
|
722
|
+
fixed: diagramContent,
|
|
723
|
+
wasFixed: false,
|
|
724
|
+
errors: [{ message: error.message }],
|
|
725
|
+
fixLevel: null
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
761
730
|
/**
|
|
762
731
|
* Specialized Mermaid diagram fixing agent
|
|
763
732
|
* Uses a separate ProbeAgent instance optimized for Mermaid syntax correction
|
|
@@ -842,7 +811,8 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
842
811
|
model: this.options.model,
|
|
843
812
|
debug: this.options.debug,
|
|
844
813
|
tracer: this.options.tracer,
|
|
845
|
-
allowEdit: this.options.allowEdit
|
|
814
|
+
allowEdit: this.options.allowEdit,
|
|
815
|
+
maxIterations: 2 // Limit mermaid fixing to 2 iterations to prevent long loops
|
|
846
816
|
});
|
|
847
817
|
}
|
|
848
818
|
|
|
@@ -881,17 +851,30 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
881
851
|
|
|
882
852
|
await this.initializeAgent();
|
|
883
853
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
854
|
+
// Format error context - handle both simple strings and maid's structured errors
|
|
855
|
+
let errorContext = '';
|
|
856
|
+
if (originalErrors.length > 0) {
|
|
857
|
+
const formattedErrors = originalErrors.map(err => {
|
|
858
|
+
// Check if this is a maid structured error object
|
|
859
|
+
if (typeof err === 'object' && err.message) {
|
|
860
|
+
const location = err.line ? `line ${err.line}${err.column ? `:${err.column}` : ''}` : '';
|
|
861
|
+
const hint = err.hint ? `\n Hint: ${err.hint}` : '';
|
|
862
|
+
return location ? `- ${location}: ${err.message}${hint}` : `- ${err.message}${hint}`;
|
|
863
|
+
}
|
|
864
|
+
// Handle simple string errors
|
|
865
|
+
return `- ${err}`;
|
|
866
|
+
}).join('\n');
|
|
887
867
|
|
|
888
|
-
|
|
889
|
-
|
|
868
|
+
errorContext = `\n\nDetected errors:\n${formattedErrors}`;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const diagramTypeHint = diagramInfo.diagramType
|
|
872
|
+
? `\n\nExpected diagram type: ${diagramInfo.diagramType}`
|
|
890
873
|
: '';
|
|
891
874
|
|
|
892
875
|
// Use decoded content for AI fixing to ensure HTML entities are handled
|
|
893
876
|
const contentToFix = decodedContent !== diagramContent ? decodedContent : diagramContent;
|
|
894
|
-
|
|
877
|
+
|
|
895
878
|
const prompt = `Analyze and fix the following Mermaid diagram.${errorContext}${diagramTypeHint}
|
|
896
879
|
|
|
897
880
|
Broken Mermaid diagram:
|
|
@@ -902,10 +885,11 @@ ${contentToFix}
|
|
|
902
885
|
Provide only the corrected Mermaid diagram within a mermaid code block. Do not add any explanations or additional text.`;
|
|
903
886
|
|
|
904
887
|
try {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
888
|
+
// Don't pass schema to avoid infinite loop where AI returns raw mermaid code
|
|
889
|
+
// instead of using attempt_completion tool. The custom prompt already instructs
|
|
890
|
+
// to return only mermaid code blocks.
|
|
891
|
+
const result = await this.agent.answer(prompt, []);
|
|
892
|
+
|
|
909
893
|
// Extract the mermaid code from the response
|
|
910
894
|
const extractedDiagram = this.extractCorrectedDiagram(result);
|
|
911
895
|
return extractedDiagram || result;
|
|
@@ -958,54 +942,35 @@ Provide only the corrected Mermaid diagram within a mermaid code block. Do not a
|
|
|
958
942
|
}
|
|
959
943
|
|
|
960
944
|
/**
|
|
961
|
-
*
|
|
962
|
-
*
|
|
963
|
-
* @param {
|
|
964
|
-
* @
|
|
945
|
+
* Validate and fix Mermaid diagrams using maid
|
|
946
|
+
* Replaces old manual fix logic with maid's auto-fix capabilities
|
|
947
|
+
* @param {string} response - Response containing mermaid diagrams
|
|
948
|
+
* @param {Object} options - Validation options
|
|
949
|
+
* @returns {Promise<Object>} - Validation and fixing results
|
|
965
950
|
*/
|
|
966
951
|
export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
967
952
|
const { schema, debug, path, provider, model, tracer } = options;
|
|
968
953
|
const startTime = Date.now();
|
|
969
|
-
|
|
954
|
+
|
|
970
955
|
if (debug) {
|
|
971
|
-
console.log(`[DEBUG] Mermaid validation: Starting
|
|
972
|
-
console.log(`[DEBUG] Mermaid validation: Options - path: ${path}, provider: ${provider}, model: ${model}`);
|
|
956
|
+
console.log(`[DEBUG] Mermaid validation: Starting maid-based validation for response (${response.length} chars)`);
|
|
973
957
|
}
|
|
974
958
|
|
|
975
|
-
/**
|
|
976
|
-
* Helper function to determine if node content needs quoting due to problematic characters
|
|
977
|
-
* @param {string} content - The node content to check
|
|
978
|
-
* @returns {boolean} - True if content needs to be quoted
|
|
979
|
-
*/
|
|
980
|
-
const needsQuoting = (content) => {
|
|
981
|
-
return /[()'"<>&`]/.test(content) || // Core problematic characters
|
|
982
|
-
content.includes('e.g.') ||
|
|
983
|
-
content.includes('i.e.') ||
|
|
984
|
-
content.includes('src/') ||
|
|
985
|
-
content.includes('defaults/') ||
|
|
986
|
-
content.includes('.ts') ||
|
|
987
|
-
content.includes('.js') ||
|
|
988
|
-
content.includes('.yaml') ||
|
|
989
|
-
content.includes('.json') ||
|
|
990
|
-
content.includes('.md') ||
|
|
991
|
-
content.includes('.html') ||
|
|
992
|
-
content.includes('.css');
|
|
993
|
-
};
|
|
994
|
-
|
|
995
959
|
// Record mermaid validation start in telemetry
|
|
996
960
|
if (tracer) {
|
|
997
961
|
tracer.recordMermaidValidationEvent('started', {
|
|
998
962
|
'mermaid_validation.response_length': response.length,
|
|
999
963
|
'mermaid_validation.provider': provider,
|
|
1000
|
-
'mermaid_validation.model': model
|
|
964
|
+
'mermaid_validation.model': model,
|
|
965
|
+
'mermaid_validation.method': 'maid'
|
|
1001
966
|
});
|
|
1002
967
|
}
|
|
1003
|
-
|
|
968
|
+
|
|
1004
969
|
// First, run standard validation
|
|
1005
970
|
const validationStart = Date.now();
|
|
1006
971
|
const validation = await validateMermaidResponse(response);
|
|
1007
972
|
const validationTime = Date.now() - validationStart;
|
|
1008
|
-
|
|
973
|
+
|
|
1009
974
|
if (debug) {
|
|
1010
975
|
console.log(`[DEBUG] Mermaid validation: Initial validation completed in ${validationTime}ms`);
|
|
1011
976
|
console.log(`[DEBUG] Mermaid validation: Found ${validation.diagrams?.length || 0} diagrams, valid: ${validation.isValid}`);
|
|
@@ -1018,24 +983,13 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1018
983
|
});
|
|
1019
984
|
}
|
|
1020
985
|
}
|
|
1021
|
-
|
|
1022
|
-
// Always check for HTML entities, even if diagrams are technically valid
|
|
1023
|
-
let needsHtmlEntityCheck = false;
|
|
1024
|
-
if (validation.diagrams && validation.diagrams.length > 0) {
|
|
1025
|
-
for (const diagram of validation.diagrams) {
|
|
1026
|
-
if (diagram.content && (diagram.content.includes('<') || diagram.content.includes('>') || diagram.content.includes('&') || diagram.content.includes('"') || diagram.content.includes('''))) {
|
|
1027
|
-
needsHtmlEntityCheck = true;
|
|
1028
|
-
break;
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
986
|
|
|
1033
|
-
|
|
987
|
+
// If all valid, return early
|
|
988
|
+
if (validation.isValid) {
|
|
1034
989
|
if (debug) {
|
|
1035
|
-
console.log(`[DEBUG] Mermaid validation: All diagrams valid
|
|
990
|
+
console.log(`[DEBUG] Mermaid validation: All diagrams valid, no fixing needed`);
|
|
1036
991
|
}
|
|
1037
|
-
|
|
1038
|
-
// Record successful validation in telemetry
|
|
992
|
+
|
|
1039
993
|
if (tracer) {
|
|
1040
994
|
tracer.recordMermaidValidationEvent('completed', {
|
|
1041
995
|
'mermaid_validation.success': true,
|
|
@@ -1044,8 +998,7 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1044
998
|
'mermaid_validation.duration_ms': Date.now() - startTime
|
|
1045
999
|
});
|
|
1046
1000
|
}
|
|
1047
|
-
|
|
1048
|
-
// All diagrams are valid and no HTML entities found, no fixing needed
|
|
1001
|
+
|
|
1049
1002
|
return {
|
|
1050
1003
|
...validation,
|
|
1051
1004
|
wasFixed: false,
|
|
@@ -1054,10 +1007,10 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1054
1007
|
};
|
|
1055
1008
|
}
|
|
1056
1009
|
|
|
1057
|
-
// If no diagrams found
|
|
1010
|
+
// If no diagrams found, return without fixing
|
|
1058
1011
|
if (!validation.diagrams || validation.diagrams.length === 0) {
|
|
1059
1012
|
if (debug) {
|
|
1060
|
-
console.log(`[DEBUG] Mermaid validation: No mermaid diagrams found in response
|
|
1013
|
+
console.log(`[DEBUG] Mermaid validation: No mermaid diagrams found in response`);
|
|
1061
1014
|
}
|
|
1062
1015
|
return {
|
|
1063
1016
|
...validation,
|
|
@@ -1067,596 +1020,147 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1067
1020
|
};
|
|
1068
1021
|
}
|
|
1069
1022
|
|
|
1070
|
-
// Try
|
|
1023
|
+
// Try maid auto-fix for invalid diagrams
|
|
1071
1024
|
const invalidCount = validation.diagrams.filter(d => !d.isValid).length;
|
|
1072
1025
|
if (debug) {
|
|
1073
|
-
|
|
1074
|
-
console.log(`[DEBUG] Mermaid validation: ${invalidCount} invalid diagrams detected, trying HTML entity auto-fix first...`);
|
|
1075
|
-
} else {
|
|
1076
|
-
console.log(`[DEBUG] Mermaid validation: Diagrams are valid but HTML entities detected, applying HTML entity auto-fix...`);
|
|
1077
|
-
}
|
|
1026
|
+
console.log(`[DEBUG] Mermaid validation: ${invalidCount} invalid diagrams, trying maid auto-fix...`);
|
|
1078
1027
|
}
|
|
1079
1028
|
|
|
1080
1029
|
try {
|
|
1081
1030
|
let fixedResponse = response;
|
|
1082
1031
|
const fixingResults = [];
|
|
1083
|
-
let
|
|
1084
|
-
|
|
1085
|
-
// Extract diagrams with position information
|
|
1032
|
+
let maidFixesApplied = false;
|
|
1033
|
+
|
|
1034
|
+
// Extract diagrams with position information
|
|
1086
1035
|
const { diagrams } = extractMermaidFromMarkdown(response);
|
|
1087
|
-
|
|
1088
|
-
//
|
|
1089
|
-
|
|
1090
|
-
const allDiagrams = validation.diagrams
|
|
1036
|
+
|
|
1037
|
+
// Process invalid diagrams in reverse to maintain indices
|
|
1038
|
+
const invalidDiagrams = validation.diagrams
|
|
1091
1039
|
.map((result, index) => ({ ...result, originalIndex: index }))
|
|
1040
|
+
.filter(result => !result.isValid)
|
|
1092
1041
|
.reverse();
|
|
1093
1042
|
|
|
1094
|
-
for (const
|
|
1095
|
-
const originalContent =
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
fixedResponse.slice(originalDiagram.endIndex);
|
|
1111
|
-
|
|
1112
|
-
fixingResults.push({
|
|
1113
|
-
diagramIndex: diagram.originalIndex,
|
|
1114
|
-
wasFixed: true,
|
|
1115
|
-
originalContent: originalContent,
|
|
1116
|
-
fixedContent: decodedContent,
|
|
1117
|
-
originalError: diagram.error || 'HTML entity cleanup',
|
|
1118
|
-
fixedWithHtmlDecoding: true
|
|
1119
|
-
});
|
|
1120
|
-
|
|
1121
|
-
htmlEntityFixesApplied = true;
|
|
1122
|
-
|
|
1123
|
-
if (debug) {
|
|
1124
|
-
console.log(`[DEBUG] Mermaid validation: Fixed diagram ${diagram.originalIndex + 1} with HTML entity decoding`);
|
|
1125
|
-
console.log(`[DEBUG] Mermaid validation: Original status: ${diagram.isValid ? 'valid' : 'invalid'} - ${diagram.error || 'no error'}`);
|
|
1126
|
-
console.log(`[DEBUG] Mermaid validation: Decoded HTML entities`);
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
} catch (error) {
|
|
1130
|
-
if (debug) {
|
|
1131
|
-
console.log(`[DEBUG] Mermaid validation: HTML entity decoding didn't improve diagram ${diagram.originalIndex + 1}: ${error.message}`);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
// If HTML entity fixes were applied, re-validate the entire response
|
|
1138
|
-
if (htmlEntityFixesApplied) {
|
|
1139
|
-
const revalidation = await validateMermaidResponse(fixedResponse);
|
|
1140
|
-
if (revalidation.isValid) {
|
|
1141
|
-
// All diagrams are now valid, return without AI fixing
|
|
1142
|
-
const totalTime = Date.now() - startTime;
|
|
1143
|
-
if (debug) {
|
|
1144
|
-
console.log(`[DEBUG] Mermaid validation: All diagrams fixed with HTML entity decoding in ${totalTime}ms, no AI needed`);
|
|
1145
|
-
console.log(`[DEBUG] Mermaid validation: Applied ${fixingResults.length} HTML entity fixes`);
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// Record HTML entity fix success in telemetry
|
|
1149
|
-
if (tracer) {
|
|
1150
|
-
tracer.recordMermaidValidationEvent('html_fix_completed', {
|
|
1151
|
-
'mermaid_validation.success': true,
|
|
1152
|
-
'mermaid_validation.fix_method': 'html_entity_decoding',
|
|
1153
|
-
'mermaid_validation.diagrams_fixed': fixingResults.length,
|
|
1154
|
-
'mermaid_validation.duration_ms': totalTime
|
|
1155
|
-
});
|
|
1156
|
-
}
|
|
1157
|
-
return {
|
|
1158
|
-
...revalidation,
|
|
1159
|
-
wasFixed: true,
|
|
1160
|
-
originalResponse: response,
|
|
1161
|
-
fixedResponse: fixedResponse,
|
|
1162
|
-
fixingResults: fixingResults
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
// Proactive pass: Fix common node label issues in ALL diagrams (not just invalid ones)
|
|
1168
|
-
let proactiveFixesApplied = false;
|
|
1169
|
-
|
|
1170
|
-
// Re-extract diagrams after HTML entity fixes
|
|
1171
|
-
const { diagrams: currentDiagrams } = extractMermaidFromMarkdown(fixedResponse);
|
|
1172
|
-
|
|
1173
|
-
for (let diagramIndex = currentDiagrams.length - 1; diagramIndex >= 0; diagramIndex--) {
|
|
1174
|
-
const diagram = currentDiagrams[diagramIndex];
|
|
1175
|
-
const originalContent = diagram.content;
|
|
1176
|
-
const lines = originalContent.split('\n');
|
|
1177
|
-
let wasFixed = false;
|
|
1178
|
-
|
|
1179
|
-
// Proactively fix node labels that contain special characters
|
|
1180
|
-
const fixedLines = lines.map(line => {
|
|
1181
|
-
const trimmedLine = line.trim();
|
|
1182
|
-
let modifiedLine = line;
|
|
1183
|
-
|
|
1184
|
-
// Enhanced auto-fixing for square bracket nodes [...]
|
|
1185
|
-
if (trimmedLine.match(/\[[^\]]*\]/)) {
|
|
1186
|
-
modifiedLine = modifiedLine.replace(/\[([^\]]*)\]/g, (match, content) => {
|
|
1187
|
-
// Check if already properly quoted with outer quotes
|
|
1188
|
-
if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
|
|
1189
|
-
// Extract the inner content (between the outer quotes)
|
|
1190
|
-
const innerContent = content.trim().slice(1, -1);
|
|
1191
|
-
// Check if inner content has unescaped quotes that need escaping
|
|
1192
|
-
if (innerContent.includes('"') || innerContent.includes("'")) {
|
|
1193
|
-
wasFixed = true;
|
|
1194
|
-
// Decode any existing HTML entities first, then re-encode ALL quotes
|
|
1195
|
-
const decodedContent = decodeHtmlEntities(innerContent);
|
|
1196
|
-
const safeContent = decodedContent
|
|
1197
|
-
.replace(/"/g, '"') // Replace ALL double quotes with HTML entity
|
|
1198
|
-
.replace(/'/g, '''); // Replace ALL single quotes with HTML entity
|
|
1199
|
-
return `["${safeContent}"]`;
|
|
1200
|
-
}
|
|
1201
|
-
return match;
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
// Check if content needs quoting (contains problematic patterns)
|
|
1205
|
-
if (needsQuoting(content)) {
|
|
1206
|
-
wasFixed = true;
|
|
1207
|
-
// Use HTML entities for quotes as per Mermaid best practices:
|
|
1208
|
-
// - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
|
|
1209
|
-
// - HTML entities are the official way to escape quotes in Mermaid
|
|
1210
|
-
// - Always use double quotes with square brackets ["..."] for node labels
|
|
1211
|
-
// IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
|
|
1212
|
-
const decodedContent = decodeHtmlEntities(content);
|
|
1213
|
-
const safeContent = decodedContent
|
|
1214
|
-
.replace(/"/g, '"') // Replace double quotes with HTML entity
|
|
1215
|
-
.replace(/'/g, '''); // Replace single quotes with HTML entity
|
|
1216
|
-
return `["${safeContent}"]`;
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
return match;
|
|
1220
|
-
});
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
// Enhanced auto-fixing for diamond nodes {...}
|
|
1224
|
-
if (trimmedLine.match(/\{[^{}]*\}/)) {
|
|
1225
|
-
modifiedLine = modifiedLine.replace(/\{([^{}]*)\}/g, (match, content) => {
|
|
1226
|
-
// Check if already properly quoted with outer quotes
|
|
1227
|
-
if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
|
|
1228
|
-
// Extract the inner content (between the outer quotes)
|
|
1229
|
-
const innerContent = content.trim().slice(1, -1);
|
|
1230
|
-
// Check if inner content has unescaped quotes that need escaping
|
|
1231
|
-
if (innerContent.includes('"') || innerContent.includes("'")) {
|
|
1232
|
-
wasFixed = true;
|
|
1233
|
-
// Decode any existing HTML entities first, then re-encode ALL quotes
|
|
1234
|
-
const decodedContent = decodeHtmlEntities(innerContent);
|
|
1235
|
-
const safeContent = decodedContent
|
|
1236
|
-
.replace(/"/g, '"') // Replace ALL double quotes with HTML entity
|
|
1237
|
-
.replace(/'/g, '''); // Replace ALL single quotes with HTML entity
|
|
1238
|
-
return `{"${safeContent}"}`;
|
|
1239
|
-
}
|
|
1240
|
-
return match;
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
// Check if content needs quoting (contains problematic patterns)
|
|
1244
|
-
if (needsQuoting(content)) {
|
|
1245
|
-
wasFixed = true;
|
|
1246
|
-
// Use HTML entities for quotes as per Mermaid best practices:
|
|
1247
|
-
// - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
|
|
1248
|
-
// - HTML entities are the official way to escape quotes in Mermaid
|
|
1249
|
-
// - Always use double quotes with curly brackets {"..."} for diamond nodes
|
|
1250
|
-
// IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
|
|
1251
|
-
const decodedContent = decodeHtmlEntities(content);
|
|
1252
|
-
const safeContent = decodedContent
|
|
1253
|
-
.replace(/"/g, '"') // Replace double quotes with HTML entity
|
|
1254
|
-
.replace(/'/g, '''); // Replace single quotes with HTML entity
|
|
1255
|
-
return `{"${safeContent}"}`;
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
return match;
|
|
1259
|
-
});
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
return modifiedLine;
|
|
1263
|
-
});
|
|
1264
|
-
|
|
1265
|
-
if (wasFixed) {
|
|
1266
|
-
const fixedContent = fixedLines.join('\n');
|
|
1267
|
-
|
|
1268
|
-
// Replace the diagram in the response
|
|
1269
|
-
const attributesStr = diagram.attributes ? ` ${diagram.attributes}` : '';
|
|
1270
|
-
const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
|
|
1271
|
-
|
|
1272
|
-
fixedResponse = fixedResponse.slice(0, diagram.startIndex) +
|
|
1273
|
-
newCodeBlock +
|
|
1274
|
-
fixedResponse.slice(diagram.endIndex);
|
|
1275
|
-
|
|
1043
|
+
for (const invalidDiagram of invalidDiagrams) {
|
|
1044
|
+
const originalContent = invalidDiagram.content;
|
|
1045
|
+
|
|
1046
|
+
// Try maid auto-fix
|
|
1047
|
+
const maidResult = await tryMaidAutoFix(originalContent, { debug });
|
|
1048
|
+
|
|
1049
|
+
if (maidResult.errors.length === 0) {
|
|
1050
|
+
// Maid fixed it completely
|
|
1051
|
+
const originalDiagram = diagrams[invalidDiagram.originalIndex];
|
|
1052
|
+
const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
|
|
1053
|
+
const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${maidResult.fixed}\n\`\`\``;
|
|
1054
|
+
|
|
1055
|
+
fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
|
|
1056
|
+
newCodeBlock +
|
|
1057
|
+
fixedResponse.slice(originalDiagram.endIndex);
|
|
1058
|
+
|
|
1276
1059
|
fixingResults.push({
|
|
1277
|
-
diagramIndex:
|
|
1060
|
+
diagramIndex: invalidDiagram.originalIndex,
|
|
1278
1061
|
wasFixed: true,
|
|
1279
1062
|
originalContent: originalContent,
|
|
1280
|
-
fixedContent:
|
|
1281
|
-
originalError:
|
|
1282
|
-
|
|
1283
|
-
|
|
1063
|
+
fixedContent: maidResult.fixed,
|
|
1064
|
+
originalError: invalidDiagram.error,
|
|
1065
|
+
fixedWithMaid: true,
|
|
1066
|
+
fixLevel: maidResult.fixLevel
|
|
1284
1067
|
});
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
if (debug) {
|
|
1289
|
-
console.log(`[DEBUG] Mermaid validation: Proactively fixed diagram ${diagramIndex + 1} with node label quoting`);
|
|
1290
|
-
console.log(`[DEBUG] Mermaid validation: Applied automatic quoting to special characters`);
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
// If proactive fixes were applied, re-validate the entire response
|
|
1296
|
-
if (proactiveFixesApplied) {
|
|
1297
|
-
const revalidation = await validateMermaidResponse(fixedResponse);
|
|
1298
|
-
if (revalidation.isValid) {
|
|
1299
|
-
// All diagrams are now valid, return without AI fixing
|
|
1300
|
-
const totalTime = Date.now() - startTime;
|
|
1068
|
+
|
|
1069
|
+
maidFixesApplied = true;
|
|
1070
|
+
|
|
1301
1071
|
if (debug) {
|
|
1302
|
-
console.log(`[DEBUG] Mermaid validation:
|
|
1303
|
-
console.log(`[DEBUG] Mermaid validation: Applied ${fixingResults.length} proactive fixes`);
|
|
1072
|
+
console.log(`[DEBUG] Mermaid validation: Maid fixed diagram ${invalidDiagram.originalIndex + 1}`);
|
|
1304
1073
|
}
|
|
1305
|
-
|
|
1306
|
-
//
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
'mermaid_validation.fix_method': 'node_label_quote_wrapping',
|
|
1311
|
-
'mermaid_validation.diagrams_fixed': fixingResults.length,
|
|
1312
|
-
'mermaid_validation.duration_ms': totalTime
|
|
1313
|
-
});
|
|
1314
|
-
}
|
|
1315
|
-
return {
|
|
1316
|
-
...revalidation,
|
|
1317
|
-
wasFixed: true,
|
|
1318
|
-
originalResponse: response,
|
|
1319
|
-
fixedResponse: fixedResponse,
|
|
1320
|
-
fixingResults: fixingResults,
|
|
1321
|
-
performanceMetrics: {
|
|
1322
|
-
totalTimeMs: totalTime,
|
|
1323
|
-
aiFixingTimeMs: 0,
|
|
1324
|
-
finalValidationTimeMs: 0,
|
|
1325
|
-
diagramsProcessed: fixingResults.length,
|
|
1326
|
-
diagramsFixed: fixingResults.length
|
|
1327
|
-
}
|
|
1328
|
-
};
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
// Second pass: Try auto-fixing unquoted subgraph names with parentheses
|
|
1333
|
-
let subgraphFixesApplied = false;
|
|
1334
|
-
|
|
1335
|
-
// Re-extract diagrams and re-validate after HTML entity fixes
|
|
1336
|
-
const { diagrams: postHtmlDiagrams } = extractMermaidFromMarkdown(fixedResponse);
|
|
1337
|
-
const postHtmlValidation = await validateMermaidResponse(fixedResponse);
|
|
1338
|
-
|
|
1339
|
-
const stillInvalidAfterHtml = postHtmlValidation.diagrams
|
|
1340
|
-
.map((result, index) => ({ ...result, originalIndex: index }))
|
|
1341
|
-
.filter(result => !result.isValid)
|
|
1342
|
-
.reverse();
|
|
1074
|
+
} else if (maidResult.wasFixed) {
|
|
1075
|
+
// Maid improved it but didn't fix everything - update content for AI fixing
|
|
1076
|
+
const originalDiagram = diagrams[invalidDiagram.originalIndex];
|
|
1077
|
+
const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
|
|
1078
|
+
const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${maidResult.fixed}\n\`\`\``;
|
|
1343
1079
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
// Extract the subgraph name part
|
|
1359
|
-
const match = trimmedLine.match(/^(\s*subgraph\s+)(.+)$/);
|
|
1360
|
-
if (match) {
|
|
1361
|
-
const prefix = match[1];
|
|
1362
|
-
const name = match[2];
|
|
1363
|
-
const fixedLine = line.replace(trimmedLine, `${prefix.trim()} "${name}"`);
|
|
1364
|
-
wasFixed = true;
|
|
1365
|
-
return fixedLine;
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
return line;
|
|
1080
|
+
fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
|
|
1081
|
+
newCodeBlock +
|
|
1082
|
+
fixedResponse.slice(originalDiagram.endIndex);
|
|
1083
|
+
|
|
1084
|
+
fixingResults.push({
|
|
1085
|
+
diagramIndex: invalidDiagram.originalIndex,
|
|
1086
|
+
wasFixed: false,
|
|
1087
|
+
originalContent: originalContent,
|
|
1088
|
+
partiallyFixedContent: maidResult.fixed,
|
|
1089
|
+
originalError: invalidDiagram.error,
|
|
1090
|
+
remainingErrors: maidResult.errors,
|
|
1091
|
+
fixedWithMaid: 'partial',
|
|
1092
|
+
fixLevel: maidResult.fixLevel
|
|
1369
1093
|
});
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
// Validate the fixed content
|
|
1375
|
-
try {
|
|
1376
|
-
const quickValidation = await validateMermaidDiagram(fixedContent);
|
|
1377
|
-
if (quickValidation.isValid) {
|
|
1378
|
-
// Subgraph auto-fix worked!
|
|
1379
|
-
const originalDiagram = postHtmlDiagrams[invalidDiagram.originalIndex];
|
|
1380
|
-
const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
|
|
1381
|
-
const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
|
|
1382
|
-
|
|
1383
|
-
fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
|
|
1384
|
-
newCodeBlock +
|
|
1385
|
-
fixedResponse.slice(originalDiagram.endIndex);
|
|
1386
|
-
|
|
1387
|
-
fixingResults.push({
|
|
1388
|
-
originalIndex: invalidDiagram.originalIndex,
|
|
1389
|
-
wasFixed: true,
|
|
1390
|
-
originalError: invalidDiagram.error,
|
|
1391
|
-
fixMethod: 'subgraph_quote_wrapping',
|
|
1392
|
-
fixedWithSubgraphQuoting: true
|
|
1393
|
-
});
|
|
1394
|
-
|
|
1395
|
-
subgraphFixesApplied = true;
|
|
1396
|
-
|
|
1397
|
-
if (debug) {
|
|
1398
|
-
console.log(`[DEBUG] Mermaid validation: Fixed diagram ${invalidDiagram.originalIndex + 1} with subgraph quote wrapping`);
|
|
1399
|
-
console.log(`[DEBUG] Mermaid validation: Original error: ${invalidDiagram.error}`);
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
} catch (error) {
|
|
1403
|
-
if (debug) {
|
|
1404
|
-
console.log(`[DEBUG] Mermaid validation: Subgraph auto-fix didn't work for diagram ${invalidDiagram.originalIndex + 1}: ${error.message}`);
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
// If subgraph fixes were applied, re-validate the entire response
|
|
1412
|
-
if (subgraphFixesApplied) {
|
|
1413
|
-
const revalidation = await validateMermaidResponse(fixedResponse);
|
|
1414
|
-
if (revalidation.isValid) {
|
|
1415
|
-
// All diagrams are now valid, return without AI fixing
|
|
1416
|
-
const totalTime = Date.now() - startTime;
|
|
1094
|
+
|
|
1095
|
+
maidFixesApplied = true;
|
|
1096
|
+
|
|
1417
1097
|
if (debug) {
|
|
1418
|
-
console.log(`[DEBUG] Mermaid validation:
|
|
1419
|
-
console.log(`[DEBUG] Mermaid validation: Applied ${fixingResults.length} auto-fixes (HTML entities + subgraph quotes)`);
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
// Record auto-fix success in telemetry
|
|
1423
|
-
if (tracer) {
|
|
1424
|
-
tracer.recordMermaidValidationEvent('auto_fix_completed', {
|
|
1425
|
-
'mermaid_validation.success': true,
|
|
1426
|
-
'mermaid_validation.fix_method': 'auto_fixes',
|
|
1427
|
-
'mermaid_validation.diagrams_fixed': fixingResults.length,
|
|
1428
|
-
'mermaid_validation.duration_ms': totalTime
|
|
1429
|
-
});
|
|
1098
|
+
console.log(`[DEBUG] Mermaid validation: Maid partially fixed diagram ${invalidDiagram.originalIndex + 1}, ${maidResult.errors.length} errors remain`);
|
|
1430
1099
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
diagramsProcessed: fixingResults.length,
|
|
1442
|
-
diagramsFixed: fixingResults.length
|
|
1443
|
-
}
|
|
1444
|
-
};
|
|
1100
|
+
} else {
|
|
1101
|
+
// Maid couldn't fix it, keep track for AI fixing
|
|
1102
|
+
fixingResults.push({
|
|
1103
|
+
diagramIndex: invalidDiagram.originalIndex,
|
|
1104
|
+
wasFixed: false,
|
|
1105
|
+
originalContent: originalContent,
|
|
1106
|
+
originalError: invalidDiagram.error,
|
|
1107
|
+
maidErrors: maidResult.errors,
|
|
1108
|
+
fixedWithMaid: false
|
|
1109
|
+
});
|
|
1445
1110
|
}
|
|
1446
1111
|
}
|
|
1447
|
-
|
|
1448
|
-
// Third pass: Try auto-fixing node labels with parentheses or single quotes
|
|
1449
|
-
let nodeLabelFixesApplied = false;
|
|
1450
|
-
|
|
1451
|
-
// Re-extract diagrams and re-validate after previous fixes
|
|
1452
|
-
const { diagrams: postSubgraphDiagrams } = extractMermaidFromMarkdown(fixedResponse);
|
|
1453
|
-
const postSubgraphValidation = await validateMermaidResponse(fixedResponse);
|
|
1454
|
-
|
|
1455
|
-
const stillInvalidAfterSubgraph = postSubgraphValidation.diagrams
|
|
1456
|
-
.map((result, index) => ({ ...result, originalIndex: index }))
|
|
1457
|
-
.filter(result => !result.isValid)
|
|
1458
|
-
.reverse();
|
|
1459
1112
|
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
// Enhanced auto-fixing for square bracket nodes [...]
|
|
1477
|
-
// Look for any node labels that contain special characters and aren't already quoted
|
|
1478
|
-
if (trimmedLine.match(/\[[^\]]*\]/)) {
|
|
1479
|
-
modifiedLine = modifiedLine.replace(/\[([^\]]*)\]/g, (match, content) => {
|
|
1480
|
-
// Check if already properly quoted with outer quotes
|
|
1481
|
-
if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
|
|
1482
|
-
// Extract the inner content (between the outer quotes)
|
|
1483
|
-
const innerContent = content.trim().slice(1, -1);
|
|
1484
|
-
// Check if inner content has unescaped quotes that need escaping
|
|
1485
|
-
if (innerContent.includes('"') || innerContent.includes("'")) {
|
|
1486
|
-
wasFixed = true;
|
|
1487
|
-
// Decode any existing HTML entities first, then re-encode ALL quotes
|
|
1488
|
-
const decodedContent = decodeHtmlEntities(innerContent);
|
|
1489
|
-
const safeContent = decodedContent
|
|
1490
|
-
.replace(/"/g, '"') // Replace ALL double quotes with HTML entity
|
|
1491
|
-
.replace(/'/g, '''); // Replace ALL single quotes with HTML entity
|
|
1492
|
-
return `["${safeContent}"]`;
|
|
1493
|
-
}
|
|
1494
|
-
return match;
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
// Check if content needs quoting (contains problematic patterns)
|
|
1498
|
-
if (needsQuoting(content)) {
|
|
1499
|
-
wasFixed = true;
|
|
1500
|
-
// Use HTML entities for quotes as per Mermaid best practices:
|
|
1501
|
-
// - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
|
|
1502
|
-
// - HTML entities are the official way to escape quotes in Mermaid
|
|
1503
|
-
// - Always use double quotes with square brackets ["..."] for node labels
|
|
1504
|
-
// IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
|
|
1505
|
-
const decodedContent = decodeHtmlEntities(content);
|
|
1506
|
-
const safeContent = decodedContent
|
|
1507
|
-
.replace(/"/g, '"') // Replace double quotes with HTML entity
|
|
1508
|
-
.replace(/'/g, '''); // Replace single quotes with HTML entity
|
|
1509
|
-
return `["${safeContent}"]`;
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
return match;
|
|
1513
|
-
});
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
// Enhanced auto-fixing for diamond nodes {...}
|
|
1517
|
-
if (trimmedLine.match(/\{[^{}]*\}/)) {
|
|
1518
|
-
modifiedLine = modifiedLine.replace(/\{([^{}]*)\}/g, (match, content) => {
|
|
1519
|
-
// Check if already properly quoted with outer quotes
|
|
1520
|
-
if (content.trim().startsWith('"') && content.trim().endsWith('"')) {
|
|
1521
|
-
// Extract the inner content (between the outer quotes)
|
|
1522
|
-
const innerContent = content.trim().slice(1, -1);
|
|
1523
|
-
// Check if inner content has unescaped quotes that need escaping
|
|
1524
|
-
if (innerContent.includes('"') || innerContent.includes("'")) {
|
|
1525
|
-
wasFixed = true;
|
|
1526
|
-
// Decode any existing HTML entities first, then re-encode ALL quotes
|
|
1527
|
-
const decodedContent = decodeHtmlEntities(innerContent);
|
|
1528
|
-
const safeContent = decodedContent
|
|
1529
|
-
.replace(/"/g, '"') // Replace ALL double quotes with HTML entity
|
|
1530
|
-
.replace(/'/g, '''); // Replace ALL single quotes with HTML entity
|
|
1531
|
-
return `{"${safeContent}"}`;
|
|
1532
|
-
}
|
|
1533
|
-
return match;
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
// Check if content needs quoting (contains problematic patterns)
|
|
1537
|
-
if (needsQuoting(content)) {
|
|
1538
|
-
wasFixed = true;
|
|
1539
|
-
// Use HTML entities for quotes as per Mermaid best practices:
|
|
1540
|
-
// - GitHub doesn't support single quotes in node labels (causes 'got PS' error)
|
|
1541
|
-
// - HTML entities are the official way to escape quotes in Mermaid
|
|
1542
|
-
// - Always use double quotes with curly brackets {"..."} for diamond nodes
|
|
1543
|
-
// IMPORTANT: Decode any existing HTML entities first to avoid double-encoding
|
|
1544
|
-
const decodedContent = decodeHtmlEntities(content);
|
|
1545
|
-
const safeContent = decodedContent
|
|
1546
|
-
.replace(/"/g, '"') // Replace double quotes with HTML entity
|
|
1547
|
-
.replace(/'/g, '''); // Replace single quotes with HTML entity
|
|
1548
|
-
return `{"${safeContent}"}`;
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
return match;
|
|
1552
|
-
});
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
return modifiedLine;
|
|
1113
|
+
// Re-validate after maid fixes
|
|
1114
|
+
const revalidation = await validateMermaidResponse(fixedResponse);
|
|
1115
|
+
if (revalidation.isValid) {
|
|
1116
|
+
// All diagrams fixed with maid
|
|
1117
|
+
const totalTime = Date.now() - startTime;
|
|
1118
|
+
if (debug) {
|
|
1119
|
+
console.log(`[DEBUG] Mermaid validation: All diagrams fixed with maid in ${totalTime}ms, no AI needed`);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (tracer) {
|
|
1123
|
+
tracer.recordMermaidValidationEvent('maid_fix_completed', {
|
|
1124
|
+
'mermaid_validation.success': true,
|
|
1125
|
+
'mermaid_validation.fix_method': 'maid',
|
|
1126
|
+
'mermaid_validation.diagrams_fixed': fixingResults.filter(r => r.wasFixed).length,
|
|
1127
|
+
'mermaid_validation.duration_ms': totalTime
|
|
1556
1128
|
});
|
|
1557
|
-
|
|
1558
|
-
if (wasFixed) {
|
|
1559
|
-
const fixedContent = fixedLines.join('\n');
|
|
1560
|
-
|
|
1561
|
-
// Validate the fixed content
|
|
1562
|
-
try {
|
|
1563
|
-
const quickValidation = await validateMermaidDiagram(fixedContent);
|
|
1564
|
-
if (quickValidation.isValid) {
|
|
1565
|
-
// Node label auto-fix worked!
|
|
1566
|
-
const originalDiagram = postSubgraphDiagrams[invalidDiagram.originalIndex];
|
|
1567
|
-
const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
|
|
1568
|
-
const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
|
|
1569
|
-
|
|
1570
|
-
fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
|
|
1571
|
-
newCodeBlock +
|
|
1572
|
-
fixedResponse.slice(originalDiagram.endIndex);
|
|
1573
|
-
|
|
1574
|
-
fixingResults.push({
|
|
1575
|
-
originalIndex: invalidDiagram.originalIndex,
|
|
1576
|
-
wasFixed: true,
|
|
1577
|
-
originalError: invalidDiagram.error,
|
|
1578
|
-
fixMethod: 'node_label_quote_wrapping',
|
|
1579
|
-
fixedWithNodeLabelQuoting: true
|
|
1580
|
-
});
|
|
1581
|
-
|
|
1582
|
-
nodeLabelFixesApplied = true;
|
|
1583
|
-
|
|
1584
|
-
if (debug) {
|
|
1585
|
-
console.log(`[DEBUG] Mermaid validation: Fixed diagram ${invalidDiagram.originalIndex + 1} with node label quote wrapping`);
|
|
1586
|
-
console.log(`[DEBUG] Mermaid validation: Original error: ${invalidDiagram.error}`);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
} catch (error) {
|
|
1590
|
-
if (debug) {
|
|
1591
|
-
console.log(`[DEBUG] Mermaid validation: Node label auto-fix didn't work for diagram ${invalidDiagram.originalIndex + 1}: ${error.message}`);
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
1129
|
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
// Record auto-fix success in telemetry
|
|
1610
|
-
if (tracer) {
|
|
1611
|
-
tracer.recordMermaidValidationEvent('auto_fix_completed', {
|
|
1612
|
-
'mermaid_validation.success': true,
|
|
1613
|
-
'mermaid_validation.fix_method': 'auto_fixes',
|
|
1614
|
-
'mermaid_validation.diagrams_fixed': fixingResults.length,
|
|
1615
|
-
'mermaid_validation.duration_ms': totalTime
|
|
1616
|
-
});
|
|
1130
|
+
|
|
1131
|
+
return {
|
|
1132
|
+
...revalidation,
|
|
1133
|
+
wasFixed: true,
|
|
1134
|
+
originalResponse: response,
|
|
1135
|
+
fixedResponse: fixedResponse,
|
|
1136
|
+
fixingResults: fixingResults,
|
|
1137
|
+
performanceMetrics: {
|
|
1138
|
+
totalTimeMs: totalTime,
|
|
1139
|
+
aiFixingTimeMs: 0,
|
|
1140
|
+
diagramsProcessed: fixingResults.length,
|
|
1141
|
+
diagramsFixed: fixingResults.filter(r => r.wasFixed).length
|
|
1617
1142
|
}
|
|
1618
|
-
|
|
1619
|
-
...revalidation,
|
|
1620
|
-
wasFixed: true,
|
|
1621
|
-
originalResponse: response,
|
|
1622
|
-
fixedResponse: fixedResponse,
|
|
1623
|
-
fixingResults: fixingResults,
|
|
1624
|
-
performanceMetrics: {
|
|
1625
|
-
totalTimeMs: totalTime,
|
|
1626
|
-
aiFixingTimeMs: 0,
|
|
1627
|
-
finalValidationTimeMs: 0,
|
|
1628
|
-
diagramsProcessed: fixingResults.length,
|
|
1629
|
-
diagramsFixed: fixingResults.length
|
|
1630
|
-
}
|
|
1631
|
-
};
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
// Re-extract diagrams and re-validate after HTML entity fixes
|
|
1636
|
-
const { diagrams: updatedDiagrams } = extractMermaidFromMarkdown(fixedResponse);
|
|
1637
|
-
const updatedValidation = await validateMermaidResponse(fixedResponse);
|
|
1638
|
-
|
|
1639
|
-
// Still have invalid diagrams after all auto-fixes, proceed with AI fixing
|
|
1640
|
-
if (debug) {
|
|
1641
|
-
const stillInvalidAfterHtml = updatedValidation?.diagrams?.filter(d => !d.isValid)?.length || invalidCount;
|
|
1642
|
-
console.log(`[DEBUG] Mermaid validation: ${stillInvalidAfterHtml} diagrams still invalid after HTML entity decoding, starting AI fixing...`);
|
|
1643
|
-
console.log(`[DEBUG] Mermaid validation: HTML entity fixes applied: ${fixingResults.length}`);
|
|
1143
|
+
};
|
|
1644
1144
|
}
|
|
1645
|
-
|
|
1646
|
-
//
|
|
1145
|
+
|
|
1146
|
+
// Still have invalid diagrams, proceed with AI fixing
|
|
1647
1147
|
if (debug) {
|
|
1648
|
-
|
|
1148
|
+
const stillInvalid = revalidation.diagrams.filter(d => !d.isValid).length;
|
|
1149
|
+
console.log(`[DEBUG] Mermaid validation: ${stillInvalid} diagrams still invalid after maid, starting AI fixing...`);
|
|
1649
1150
|
}
|
|
1151
|
+
|
|
1650
1152
|
const aiFixingStart = Date.now();
|
|
1651
1153
|
const mermaidFixer = new MermaidFixingAgent({
|
|
1652
1154
|
path, provider, model, debug, tracer
|
|
1653
1155
|
});
|
|
1654
|
-
|
|
1655
|
-
|
|
1156
|
+
|
|
1157
|
+
// Extract updated diagrams and validation
|
|
1158
|
+
const { diagrams: updatedDiagrams } = extractMermaidFromMarkdown(fixedResponse);
|
|
1159
|
+
const stillInvalidDiagrams = revalidation.diagrams
|
|
1656
1160
|
.map((result, index) => ({ ...result, originalIndex: index }))
|
|
1657
1161
|
.filter(result => !result.isValid)
|
|
1658
1162
|
.reverse();
|
|
1659
|
-
|
|
1163
|
+
|
|
1660
1164
|
if (debug) {
|
|
1661
1165
|
console.log(`[DEBUG] Mermaid validation: Found ${stillInvalidDiagrams.length} diagrams requiring AI fixing`);
|
|
1662
1166
|
}
|
|
@@ -1664,15 +1168,18 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1664
1168
|
for (const invalidDiagram of stillInvalidDiagrams) {
|
|
1665
1169
|
if (debug) {
|
|
1666
1170
|
console.log(`[DEBUG] Mermaid validation: Attempting AI fix for diagram ${invalidDiagram.originalIndex + 1}`);
|
|
1667
|
-
console.log(`[DEBUG] Mermaid validation: Diagram type: ${invalidDiagram.diagramType || 'unknown'}`);
|
|
1668
|
-
console.log(`[DEBUG] Mermaid validation: Error to fix: ${invalidDiagram.error}`);
|
|
1669
1171
|
}
|
|
1670
|
-
|
|
1172
|
+
|
|
1671
1173
|
const diagramFixStart = Date.now();
|
|
1672
1174
|
try {
|
|
1175
|
+
// Pass maid's structured errors if available
|
|
1176
|
+
const errorsToPass = invalidDiagram.errors && invalidDiagram.errors.length > 0
|
|
1177
|
+
? invalidDiagram.errors
|
|
1178
|
+
: [invalidDiagram.error];
|
|
1179
|
+
|
|
1673
1180
|
const fixedContent = await mermaidFixer.fixMermaidDiagram(
|
|
1674
1181
|
invalidDiagram.content,
|
|
1675
|
-
|
|
1182
|
+
errorsToPass,
|
|
1676
1183
|
{ diagramType: invalidDiagram.diagramType }
|
|
1677
1184
|
);
|
|
1678
1185
|
const diagramFixTime = Date.now() - diagramFixStart;
|
|
@@ -1682,91 +1189,68 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1682
1189
|
const originalDiagram = updatedDiagrams[invalidDiagram.originalIndex];
|
|
1683
1190
|
const attributesStr = originalDiagram.attributes ? ` ${originalDiagram.attributes}` : '';
|
|
1684
1191
|
const newCodeBlock = `\`\`\`mermaid${attributesStr}\n${fixedContent}\n\`\`\``;
|
|
1685
|
-
|
|
1686
|
-
fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
|
|
1687
|
-
newCodeBlock +
|
|
1192
|
+
|
|
1193
|
+
fixedResponse = fixedResponse.slice(0, originalDiagram.startIndex) +
|
|
1194
|
+
newCodeBlock +
|
|
1688
1195
|
fixedResponse.slice(originalDiagram.endIndex);
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1196
|
+
|
|
1197
|
+
// Find existing result or create new one
|
|
1198
|
+
const existingResultIndex = fixingResults.findIndex(r => r.diagramIndex === invalidDiagram.originalIndex);
|
|
1199
|
+
if (existingResultIndex >= 0) {
|
|
1200
|
+
fixingResults[existingResultIndex] = {
|
|
1201
|
+
...fixingResults[existingResultIndex],
|
|
1202
|
+
wasFixed: true,
|
|
1203
|
+
fixedContent: fixedContent,
|
|
1204
|
+
aiFixingTimeMs: diagramFixTime
|
|
1205
|
+
};
|
|
1206
|
+
} else {
|
|
1207
|
+
fixingResults.push({
|
|
1208
|
+
diagramIndex: invalidDiagram.originalIndex,
|
|
1209
|
+
wasFixed: true,
|
|
1210
|
+
originalContent: invalidDiagram.content,
|
|
1211
|
+
fixedContent: fixedContent,
|
|
1212
|
+
originalError: invalidDiagram.error,
|
|
1213
|
+
aiFixingTimeMs: diagramFixTime
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1698
1216
|
|
|
1699
1217
|
if (debug) {
|
|
1700
|
-
console.log(`[DEBUG] Mermaid validation: Successfully fixed diagram ${invalidDiagram.originalIndex + 1} in ${diagramFixTime}ms`);
|
|
1701
|
-
console.log(`[DEBUG] Mermaid validation: Original error: ${invalidDiagram.error}`);
|
|
1702
|
-
console.log(`[DEBUG] Mermaid validation: Content changes: ${invalidDiagram.content.length} -> ${fixedContent.length} chars`);
|
|
1218
|
+
console.log(`[DEBUG] Mermaid validation: Successfully fixed diagram ${invalidDiagram.originalIndex + 1} with AI in ${diagramFixTime}ms`);
|
|
1703
1219
|
}
|
|
1704
1220
|
} else {
|
|
1705
|
-
fixingResults.push({
|
|
1706
|
-
diagramIndex: invalidDiagram.originalIndex,
|
|
1707
|
-
wasFixed: false,
|
|
1708
|
-
originalContent: invalidDiagram.content,
|
|
1709
|
-
originalError: invalidDiagram.error,
|
|
1710
|
-
fixingError: 'No valid fix generated',
|
|
1711
|
-
aiFixingTimeMs: diagramFixTime
|
|
1712
|
-
});
|
|
1713
|
-
|
|
1714
1221
|
if (debug) {
|
|
1715
1222
|
console.log(`[DEBUG] Mermaid validation: AI fix failed for diagram ${invalidDiagram.originalIndex + 1} - no valid fix generated`);
|
|
1716
1223
|
}
|
|
1717
1224
|
}
|
|
1718
1225
|
} catch (error) {
|
|
1719
|
-
const diagramFixTime = Date.now() - diagramFixStart;
|
|
1720
|
-
fixingResults.push({
|
|
1721
|
-
diagramIndex: invalidDiagram.originalIndex,
|
|
1722
|
-
wasFixed: false,
|
|
1723
|
-
originalContent: invalidDiagram.content,
|
|
1724
|
-
originalError: invalidDiagram.error,
|
|
1725
|
-
fixingError: error.message,
|
|
1726
|
-
aiFixingTimeMs: diagramFixTime
|
|
1727
|
-
});
|
|
1728
|
-
|
|
1729
1226
|
if (debug) {
|
|
1730
|
-
console.log(`[DEBUG] Mermaid validation: AI fix failed for diagram ${invalidDiagram.originalIndex + 1}
|
|
1227
|
+
console.log(`[DEBUG] Mermaid validation: AI fix failed for diagram ${invalidDiagram.originalIndex + 1}: ${error.message}`);
|
|
1731
1228
|
}
|
|
1732
1229
|
}
|
|
1733
1230
|
}
|
|
1734
1231
|
|
|
1735
|
-
//
|
|
1736
|
-
const finalValidationStart = Date.now();
|
|
1232
|
+
// Final validation
|
|
1737
1233
|
const finalValidation = await validateMermaidResponse(fixedResponse);
|
|
1738
|
-
const finalValidationTime = Date.now() - finalValidationStart;
|
|
1739
1234
|
const totalTime = Date.now() - startTime;
|
|
1740
1235
|
const aiFixingTime = Date.now() - aiFixingStart;
|
|
1741
1236
|
|
|
1742
|
-
// Check if any diagrams were actually fixed
|
|
1743
1237
|
const wasActuallyFixed = fixingResults.some(result => result.wasFixed);
|
|
1744
1238
|
const fixedCount = fixingResults.filter(result => result.wasFixed).length;
|
|
1745
|
-
const totalAttempts = fixingResults.length;
|
|
1746
1239
|
|
|
1747
1240
|
if (debug) {
|
|
1748
|
-
console.log(`[DEBUG] Mermaid validation: Final validation completed in ${finalValidationTime}ms`);
|
|
1749
1241
|
console.log(`[DEBUG] Mermaid validation: Total process time: ${totalTime}ms (AI fixing: ${aiFixingTime}ms)`);
|
|
1750
|
-
console.log(`[DEBUG] Mermaid validation: Fixed ${fixedCount}/${
|
|
1242
|
+
console.log(`[DEBUG] Mermaid validation: Fixed ${fixedCount}/${fixingResults.length} diagrams`);
|
|
1751
1243
|
console.log(`[DEBUG] Mermaid validation: Final result - all valid: ${finalValidation.isValid}`);
|
|
1752
|
-
|
|
1753
|
-
if (mermaidFixer.getTokenUsage) {
|
|
1754
|
-
const tokenUsage = mermaidFixer.getTokenUsage();
|
|
1755
|
-
console.log(`[DEBUG] Mermaid validation: AI token usage - prompt: ${tokenUsage?.promptTokens || 0}, completion: ${tokenUsage?.completionTokens || 0}`);
|
|
1756
|
-
}
|
|
1757
1244
|
}
|
|
1758
|
-
|
|
1759
|
-
// Record final completion in telemetry
|
|
1245
|
+
|
|
1760
1246
|
if (tracer) {
|
|
1761
1247
|
tracer.recordMermaidValidationEvent('completed', {
|
|
1762
1248
|
'mermaid_validation.success': finalValidation.isValid,
|
|
1763
1249
|
'mermaid_validation.was_fixed': wasActuallyFixed,
|
|
1764
|
-
'mermaid_validation.diagrams_processed':
|
|
1250
|
+
'mermaid_validation.diagrams_processed': fixingResults.length,
|
|
1765
1251
|
'mermaid_validation.diagrams_fixed': fixedCount,
|
|
1766
1252
|
'mermaid_validation.total_duration_ms': totalTime,
|
|
1767
|
-
'mermaid_validation.ai_fixing_duration_ms': aiFixingTime
|
|
1768
|
-
'mermaid_validation.final_validation_duration_ms': finalValidationTime,
|
|
1769
|
-
'mermaid_validation.token_usage': mermaidFixer.getTokenUsage ? mermaidFixer.getTokenUsage() : null
|
|
1253
|
+
'mermaid_validation.ai_fixing_duration_ms': aiFixingTime
|
|
1770
1254
|
});
|
|
1771
1255
|
}
|
|
1772
1256
|
|
|
@@ -1779,8 +1263,7 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1779
1263
|
performanceMetrics: {
|
|
1780
1264
|
totalTimeMs: totalTime,
|
|
1781
1265
|
aiFixingTimeMs: aiFixingTime,
|
|
1782
|
-
|
|
1783
|
-
diagramsProcessed: totalAttempts,
|
|
1266
|
+
diagramsProcessed: fixingResults.length,
|
|
1784
1267
|
diagramsFixed: fixedCount
|
|
1785
1268
|
},
|
|
1786
1269
|
tokenUsage: mermaidFixer.getTokenUsage()
|
|
@@ -1788,10 +1271,9 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1788
1271
|
|
|
1789
1272
|
} catch (error) {
|
|
1790
1273
|
if (debug) {
|
|
1791
|
-
console.error(`[DEBUG] Mermaid fixing
|
|
1274
|
+
console.error(`[DEBUG] Mermaid fixing failed: ${error.message}`);
|
|
1792
1275
|
}
|
|
1793
1276
|
|
|
1794
|
-
// Return original validation with fixing error
|
|
1795
1277
|
return {
|
|
1796
1278
|
...validation,
|
|
1797
1279
|
wasFixed: false,
|
|
@@ -1800,4 +1282,4 @@ export async function validateAndFixMermaidResponse(response, options = {}) {
|
|
|
1800
1282
|
fixingError: error.message
|
|
1801
1283
|
};
|
|
1802
1284
|
}
|
|
1803
|
-
}
|
|
1285
|
+
}
|