flex-md 4.7.2 → 4.7.3
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/README.md +1 -1
- package/dist/__tests__/diagnostics.test.js +45 -47
- package/dist/__tests__/ofs.test.js +28 -30
- package/dist/__tests__/structural.test.js +19 -21
- package/dist/__tests__/validate.test.js +27 -29
- package/dist/cli/index.js +13 -15
- package/dist/detect/json/detectIntent.js +1 -4
- package/dist/detect/json/detectMarkdown.js +29 -35
- package/dist/detect/json/detectPresence.js +2 -6
- package/dist/detect/json/index.js +10 -31
- package/dist/detect/json/types.js +1 -2
- package/dist/extract/extract.js +11 -14
- package/dist/extract/types.js +1 -2
- package/dist/index.js +22 -69
- package/dist/logger.js +3 -6
- package/dist/md/match.js +1 -4
- package/dist/md/normalize.js +1 -4
- package/dist/md/outline.js +4 -8
- package/dist/md/parse.js +11 -20
- package/dist/ofs/adapter.js +45 -49
- package/dist/ofs/enricher.js +3 -8
- package/dist/ofs/infer.js +4 -7
- package/dist/ofs/issuesEnvelope.js +5 -10
- package/dist/ofs/memory.js +6 -10
- package/dist/ofs/parser.js +7 -13
- package/dist/ofs/stringify.js +1 -4
- package/dist/pipeline/enforce.js +12 -15
- package/dist/pipeline/kind.js +3 -6
- package/dist/pipeline/repair.js +8 -11
- package/dist/strictness/container.js +1 -4
- package/dist/strictness/processor.js +7 -10
- package/dist/strictness/types.js +1 -4
- package/dist/tokens/auto-fix.js +1 -4
- package/dist/tokens/cognitive-cost.js +6 -10
- package/dist/tokens/compliance.js +4 -8
- package/dist/tokens/confidence.js +6 -9
- package/dist/tokens/estimator.js +20 -26
- package/dist/tokens/improvements.js +12 -16
- package/dist/tokens/index.js +22 -40
- package/dist/tokens/parser.js +4 -7
- package/dist/tokens/patterns.js +2 -5
- package/dist/tokens/runtime-estimator.js +9 -12
- package/dist/tokens/smart-report.js +10 -14
- package/dist/tokens/spec-estimator.js +10 -15
- package/dist/tokens/types.js +1 -2
- package/dist/tokens/validator.js +3 -6
- package/dist/types.js +1 -2
- package/dist/validate/compliance.js +4 -8
- package/dist/validate/connection.js +5 -8
- package/dist/validate/types.js +1 -2
- package/dist/validate/validate.js +16 -19
- package/dist-cjs/__tests__/diagnostics.test.cjs +61 -0
- package/dist-cjs/__tests__/ofs.test.cjs +53 -0
- package/dist-cjs/__tests__/structural.test.cjs +30 -0
- package/dist-cjs/__tests__/validate.test.cjs +110 -0
- package/dist-cjs/cli/index.cjs +110 -0
- package/dist-cjs/detect/json/detectIntent.cjs +82 -0
- package/dist-cjs/detect/json/detectMarkdown.cjs +304 -0
- package/dist-cjs/detect/json/detectPresence.cjs +195 -0
- package/dist-cjs/detect/json/index.cjs +34 -0
- package/dist-cjs/detect/json/types.cjs +2 -0
- package/dist-cjs/extract/extract.cjs +72 -0
- package/dist-cjs/extract/types.cjs +2 -0
- package/dist-cjs/flex-md-loader.cjs +102 -0
- package/dist-cjs/index.cjs +79 -0
- package/dist-cjs/logger.cjs +22 -0
- package/dist-cjs/md/match.cjs +47 -0
- package/dist-cjs/md/normalize.cjs +13 -0
- package/dist-cjs/md/outline.cjs +49 -0
- package/dist-cjs/md/parse.cjs +199 -0
- package/dist-cjs/ofs/adapter.cjs +195 -0
- package/dist-cjs/ofs/enricher.cjs +151 -0
- package/dist-cjs/ofs/infer.cjs +63 -0
- package/dist-cjs/ofs/issuesEnvelope.cjs +76 -0
- package/dist-cjs/ofs/memory.cjs +26 -0
- package/dist-cjs/ofs/parser.cjs +373 -0
- package/dist-cjs/ofs/stringify.cjs +45 -0
- package/dist-cjs/pipeline/enforce.cjs +49 -0
- package/dist-cjs/pipeline/kind.cjs +30 -0
- package/dist-cjs/pipeline/repair.cjs +115 -0
- package/dist-cjs/strictness/container.cjs +49 -0
- package/dist-cjs/strictness/processor.cjs +32 -0
- package/dist-cjs/strictness/types.cjs +109 -0
- package/dist-cjs/tokens/auto-fix.cjs +59 -0
- package/dist-cjs/tokens/cognitive-cost.cjs +209 -0
- package/dist-cjs/tokens/compliance.cjs +74 -0
- package/dist-cjs/tokens/confidence.cjs +335 -0
- package/dist-cjs/tokens/estimator.cjs +157 -0
- package/dist-cjs/tokens/improvements.cjs +701 -0
- package/dist-cjs/tokens/index.cjs +74 -0
- package/dist-cjs/tokens/parser.cjs +100 -0
- package/dist-cjs/tokens/patterns.cjs +23 -0
- package/dist-cjs/tokens/runtime-estimator.cjs +74 -0
- package/dist-cjs/tokens/smart-report.cjs +191 -0
- package/dist-cjs/tokens/spec-estimator.cjs +125 -0
- package/dist-cjs/tokens/types.cjs +2 -0
- package/dist-cjs/tokens/validator.cjs +62 -0
- package/dist-cjs/types.cjs +2 -0
- package/dist-cjs/validate/compliance.cjs +103 -0
- package/dist-cjs/validate/connection.cjs +47 -0
- package/dist-cjs/validate/types.cjs +2 -0
- package/dist-cjs/validate/validate.cjs +319 -0
- package/docs/consumption.md +1 -1
- package/package.json +14 -8
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.getMaxTokens = getMaxTokens;
|
|
18
|
+
exports.getMaxTokensFromInstructions = getMaxTokensFromInstructions;
|
|
19
|
+
const parser_js_1 = require("../ofs/parser.cjs");
|
|
20
|
+
const spec_estimator_js_1 = require("./spec-estimator.cjs");
|
|
21
|
+
__exportStar(require("./types.cjs"), exports);
|
|
22
|
+
__exportStar(require("./parser.cjs"), exports);
|
|
23
|
+
__exportStar(require("./estimator.cjs"), exports);
|
|
24
|
+
__exportStar(require("./spec-estimator.cjs"), exports);
|
|
25
|
+
__exportStar(require("./runtime-estimator.cjs"), exports);
|
|
26
|
+
__exportStar(require("./validator.cjs"), exports);
|
|
27
|
+
__exportStar(require("./compliance.cjs"), exports);
|
|
28
|
+
__exportStar(require("./cognitive-cost.cjs"), exports);
|
|
29
|
+
__exportStar(require("./confidence.cjs"), exports);
|
|
30
|
+
__exportStar(require("./smart-report.cjs"), exports);
|
|
31
|
+
__exportStar(require("./improvements.cjs"), exports);
|
|
32
|
+
__exportStar(require("./auto-fix.cjs"), exports);
|
|
33
|
+
/**
|
|
34
|
+
* Convenience function: estimate max_tokens from spec
|
|
35
|
+
*
|
|
36
|
+
* @param spec - OutputFormatSpec object, markdown string, or full instructions text
|
|
37
|
+
* @param options - Estimation options
|
|
38
|
+
* @returns Estimated max_tokens value
|
|
39
|
+
*/
|
|
40
|
+
function getMaxTokens(spec, options) {
|
|
41
|
+
if (!spec)
|
|
42
|
+
return 0;
|
|
43
|
+
const extractFromInstructions = options?.extractFromInstructions ?? false;
|
|
44
|
+
// If extractFromInstructions is true and spec is a string, try to extract both formats
|
|
45
|
+
if (extractFromInstructions && typeof spec === 'string') {
|
|
46
|
+
const formatSpecs = (0, parser_js_1.parseFormatSpecs)(spec);
|
|
47
|
+
if (formatSpecs.input || formatSpecs.output) {
|
|
48
|
+
const estimate = (0, spec_estimator_js_1.estimateFormatSpecsTokens)(formatSpecs.input, formatSpecs.output, options);
|
|
49
|
+
return estimate.total.estimated;
|
|
50
|
+
}
|
|
51
|
+
// Fall through to output-only parsing if no formats found
|
|
52
|
+
}
|
|
53
|
+
// Original behavior: parse as output format spec
|
|
54
|
+
const parsedSpec = typeof spec === 'string'
|
|
55
|
+
? (0, parser_js_1.parseOutputFormatSpec)(spec)
|
|
56
|
+
: spec;
|
|
57
|
+
if (!parsedSpec)
|
|
58
|
+
return 0;
|
|
59
|
+
const estimate = (0, spec_estimator_js_1.estimateSpecTokens)(parsedSpec, options);
|
|
60
|
+
return estimate.total.estimated;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Estimate max tokens from entire instructions text.
|
|
64
|
+
* Automatically extracts both input and output format specs if present.
|
|
65
|
+
*
|
|
66
|
+
* @param instructions - Full instructions text that may contain format specs
|
|
67
|
+
* @param options - Estimation options
|
|
68
|
+
* @returns Estimated max_tokens value based on both input and output formats
|
|
69
|
+
*/
|
|
70
|
+
function getMaxTokensFromInstructions(instructions, options) {
|
|
71
|
+
const formatSpecs = (0, parser_js_1.parseFormatSpecs)(instructions);
|
|
72
|
+
const estimate = (0, spec_estimator_js_1.estimateFormatSpecsTokens)(formatSpecs.input, formatSpecs.output, options);
|
|
73
|
+
return estimate.total.estimated;
|
|
74
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseSystemPart = parseSystemPart;
|
|
4
|
+
const patterns_js_1 = require("./patterns.cjs");
|
|
5
|
+
function parseSystemPart(instruction, kind // Allow 'code' as a special case if needed
|
|
6
|
+
) {
|
|
7
|
+
if (!instruction)
|
|
8
|
+
return null;
|
|
9
|
+
const pattern = patterns_js_1.SYSTEM_PART_PATTERNS[kind];
|
|
10
|
+
if (!pattern)
|
|
11
|
+
return null;
|
|
12
|
+
const match = instruction.match(pattern);
|
|
13
|
+
if (!match)
|
|
14
|
+
return null;
|
|
15
|
+
// Split into system and guidance parts
|
|
16
|
+
const parts = instruction.split(patterns_js_1.INSTRUCTION_SPLIT);
|
|
17
|
+
const raw = parts[0];
|
|
18
|
+
const guidance = parts.slice(1).join('. ') || null;
|
|
19
|
+
const parsed = parseMatchByKind(kind, match);
|
|
20
|
+
if (!parsed)
|
|
21
|
+
return null;
|
|
22
|
+
return { raw, guidance, parsed };
|
|
23
|
+
}
|
|
24
|
+
function parseMatchByKind(kind, match) {
|
|
25
|
+
switch (kind) {
|
|
26
|
+
case 'text':
|
|
27
|
+
return parseTextMatch(match);
|
|
28
|
+
case 'list':
|
|
29
|
+
case 'ordered_list':
|
|
30
|
+
return parseItemsMatch(match);
|
|
31
|
+
case 'table':
|
|
32
|
+
case 'ordered_table':
|
|
33
|
+
return parseTableMatch(match);
|
|
34
|
+
case 'code':
|
|
35
|
+
return parseCodeMatch(match);
|
|
36
|
+
default:
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function parseTextMatch(match) {
|
|
41
|
+
const raw = match[1].toLowerCase().replace(/\s+/g, ' ');
|
|
42
|
+
// Map to canonical LengthValue
|
|
43
|
+
const lengthMap = {
|
|
44
|
+
'1 sentence': '1 sentence',
|
|
45
|
+
'1-2 sentences': '1-2 sentences',
|
|
46
|
+
'1 paragraph': '1 paragraph',
|
|
47
|
+
'2-3 paragraphs': '2-3 paragraphs',
|
|
48
|
+
'brief': 'brief',
|
|
49
|
+
'moderate': 'moderate',
|
|
50
|
+
'detailed': 'detailed',
|
|
51
|
+
'extensive': 'extensive'
|
|
52
|
+
};
|
|
53
|
+
const value = lengthMap[raw] || 'moderate';
|
|
54
|
+
return { type: 'length', value };
|
|
55
|
+
}
|
|
56
|
+
function parseItemsMatch(match) {
|
|
57
|
+
// match[1] = "at least" (optional)
|
|
58
|
+
// match[2] = min number
|
|
59
|
+
// match[3] = max number (optional)
|
|
60
|
+
return {
|
|
61
|
+
type: 'items',
|
|
62
|
+
min: parseInt(match[2]),
|
|
63
|
+
max: match[3] ? parseInt(match[3]) : null,
|
|
64
|
+
atLeast: !!match[1]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function parseTableMatch(match) {
|
|
68
|
+
// match[1] = rows "at least" (optional)
|
|
69
|
+
// match[2] = rows min
|
|
70
|
+
// match[3] = rows max (optional)
|
|
71
|
+
// match[4] = columns "at least" (optional)
|
|
72
|
+
// match[5] = columns min
|
|
73
|
+
// match[6] = columns max (optional)
|
|
74
|
+
return {
|
|
75
|
+
type: 'table',
|
|
76
|
+
rows: {
|
|
77
|
+
min: parseInt(match[2]),
|
|
78
|
+
max: match[3] ? parseInt(match[3]) : null,
|
|
79
|
+
atLeast: !!match[1],
|
|
80
|
+
approximate: false
|
|
81
|
+
},
|
|
82
|
+
columns: {
|
|
83
|
+
min: parseInt(match[5]),
|
|
84
|
+
max: match[6] ? parseInt(match[6]) : null,
|
|
85
|
+
atLeast: !!match[4],
|
|
86
|
+
approximate: false
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function parseCodeMatch(match) {
|
|
91
|
+
// match[1] = "~" (optional)
|
|
92
|
+
// match[2] = min number
|
|
93
|
+
// match[3] = max number (optional)
|
|
94
|
+
return {
|
|
95
|
+
type: 'lines',
|
|
96
|
+
min: parseInt(match[2]),
|
|
97
|
+
max: match[3] ? parseInt(match[3]) : null,
|
|
98
|
+
approximate: !!match[1]
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// patterns.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.INSTRUCTION_SPLIT = exports.SYSTEM_PART_PATTERNS = void 0;
|
|
5
|
+
exports.SYSTEM_PART_PATTERNS = {
|
|
6
|
+
// TEXT: Enumerated literal strings only
|
|
7
|
+
text: /^Length:\s*(1\s+sentence|1-2\s+sentences|1\s+paragraph|2-3\s+paragraphs|brief|moderate|detailed|extensive)/i,
|
|
8
|
+
// LIST/ORDERED-LIST: [optional "at least"] + number + [optional range]
|
|
9
|
+
// Matches: "3" | "3-5" | "at least 3"
|
|
10
|
+
list: /^Items:\s*(?:(at least)\s+)?(\d+)(?:-(\d+))?/i,
|
|
11
|
+
ordered_list: /^Items:\s*(?:(at least)\s+)?(\d+)(?:-(\d+))?/i,
|
|
12
|
+
// TABLE: Two numeric patterns (rows and columns)
|
|
13
|
+
// Matches: "Rows: 3, Columns: 5" | "Rows: 3-5, Columns: at least 2"
|
|
14
|
+
table: /^Rows:\s*(?:(at least)\s+)?(\d+)(?:-(\d+))?,\s*Columns:\s*(?:(at least)\s+)?(\d+)(?:-(\d+))?/i,
|
|
15
|
+
ordered_table: /^Rows:\s*(?:(at least)\s+)?(\d+)(?:-(\d+))?,\s*Columns:\s*(?:(at least)\s+)?(\d+)(?:-(\d+))?/i,
|
|
16
|
+
// CODE: [optional "~"] + number + [optional range]
|
|
17
|
+
// Matches: "10" | "10-20" | "~50"
|
|
18
|
+
// Note: SectionKind doesn't include 'code' currently, but we might handle it if it's added.
|
|
19
|
+
// For now, let's keep it but expect SectionKind to match.
|
|
20
|
+
code: /^Lines:\s*(~)?(\d+)(?:-(\d+))?/i
|
|
21
|
+
};
|
|
22
|
+
// Separator for splitting system part from guidance
|
|
23
|
+
exports.INSTRUCTION_SPLIT = /\.\s+/;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runtimeEstimateTokens = runtimeEstimateTokens;
|
|
4
|
+
const parser_js_1 = require("../ofs/parser.cjs");
|
|
5
|
+
const spec_estimator_js_1 = require("./spec-estimator.cjs");
|
|
6
|
+
const estimator_js_1 = require("./estimator.cjs");
|
|
7
|
+
/**
|
|
8
|
+
* Estimate max_tokens for an LLM API call at runtime.
|
|
9
|
+
*
|
|
10
|
+
* This function estimates the total tokens needed by considering:
|
|
11
|
+
* - The prompt text (user input)
|
|
12
|
+
* - Context (previous messages, system messages, etc.)
|
|
13
|
+
* - Instructions (which may contain output format specs)
|
|
14
|
+
* - Expected output format (extracted from instructions)
|
|
15
|
+
*
|
|
16
|
+
* Note: Input format specs in instructions are ignored (they're for planning/design only).
|
|
17
|
+
* Only output format specs are used for runtime token estimation.
|
|
18
|
+
*
|
|
19
|
+
* @param params - Runtime estimation parameters
|
|
20
|
+
* @returns Token estimate with breakdown
|
|
21
|
+
*/
|
|
22
|
+
function runtimeEstimateTokens(params) {
|
|
23
|
+
const { prompt = '', context = '', instructions = '' } = params;
|
|
24
|
+
const opts = {
|
|
25
|
+
includeOptional: params.options?.includeOptional ?? true,
|
|
26
|
+
safetyMultiplier: params.options?.safetyMultiplier ?? 1.2,
|
|
27
|
+
strategy: params.options?.strategy ?? 'average',
|
|
28
|
+
estimatePrompt: params.options?.estimatePrompt ?? true,
|
|
29
|
+
estimateContext: params.options?.estimateContext ?? true,
|
|
30
|
+
estimateInstructions: params.options?.estimateInstructions ?? true,
|
|
31
|
+
additionalOverhead: params.options?.additionalOverhead ?? 0
|
|
32
|
+
};
|
|
33
|
+
// Extract format specs from instructions (only output format is used at runtime)
|
|
34
|
+
const formatSpecs = (0, parser_js_1.parseFormatSpecs)(instructions);
|
|
35
|
+
// Estimate tokens for each component
|
|
36
|
+
const promptTokens = opts.estimatePrompt ? (0, estimator_js_1.estimateTextTokens)(prompt) : 0;
|
|
37
|
+
const contextTokens = opts.estimateContext ? (0, estimator_js_1.estimateTextTokens)(context) : 0;
|
|
38
|
+
const instructionsTokens = opts.estimateInstructions ? (0, estimator_js_1.estimateTextTokens)(instructions) : 0;
|
|
39
|
+
// Estimate output tokens from output format spec (input format is for planning only)
|
|
40
|
+
const outputEstimate = formatSpecs.output
|
|
41
|
+
? (0, spec_estimator_js_1.estimateFormatSpecsTokens)(undefined, formatSpecs.output, {
|
|
42
|
+
includeOptional: opts.includeOptional,
|
|
43
|
+
safetyMultiplier: 1.0, // Don't apply safety multiplier here, apply at the end
|
|
44
|
+
strategy: opts.strategy
|
|
45
|
+
})
|
|
46
|
+
: {
|
|
47
|
+
total: { estimated: 0, min: 0, max: 0, confidence: 'low' },
|
|
48
|
+
bySectionName: {},
|
|
49
|
+
overhead: 0
|
|
50
|
+
};
|
|
51
|
+
// Calculate total input tokens (prompt + context + instructions)
|
|
52
|
+
const inputTokens = promptTokens + contextTokens + instructionsTokens;
|
|
53
|
+
// Calculate total output tokens (from format spec)
|
|
54
|
+
// Note: max_tokens parameter in LLM APIs refers to OUTPUT tokens only
|
|
55
|
+
const outputTokens = outputEstimate.total.estimated;
|
|
56
|
+
// Apply safety multiplier to output tokens only (this is what max_tokens represents)
|
|
57
|
+
const maxTokens = Math.ceil(outputTokens * opts.safetyMultiplier) + opts.additionalOverhead;
|
|
58
|
+
// Total tokens for budgeting/planning (input + output)
|
|
59
|
+
const totalTokens = inputTokens + outputTokens + opts.additionalOverhead;
|
|
60
|
+
// Determine confidence based on output spec confidence
|
|
61
|
+
const confidence = outputEstimate.total.confidence;
|
|
62
|
+
return {
|
|
63
|
+
maxTokens, // This is the value to use for max_tokens API parameter (output only)
|
|
64
|
+
breakdown: {
|
|
65
|
+
prompt: promptTokens,
|
|
66
|
+
context: contextTokens,
|
|
67
|
+
instructions: instructionsTokens,
|
|
68
|
+
output: outputEstimate,
|
|
69
|
+
additionalOverhead: opts.additionalOverhead,
|
|
70
|
+
total: totalTokens // Total for budgeting (input + output)
|
|
71
|
+
},
|
|
72
|
+
confidence
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeSpec = analyzeSpec;
|
|
4
|
+
exports.formatSmartReport = formatSmartReport;
|
|
5
|
+
const compliance_js_1 = require("./compliance.cjs");
|
|
6
|
+
const cognitive_cost_js_1 = require("./cognitive-cost.cjs");
|
|
7
|
+
const confidence_js_1 = require("./confidence.cjs");
|
|
8
|
+
const spec_estimator_js_1 = require("./spec-estimator.cjs");
|
|
9
|
+
/**
|
|
10
|
+
* Comprehensive smart analysis of spec
|
|
11
|
+
*/
|
|
12
|
+
function analyzeSpec(spec, targetLevel = 2) {
|
|
13
|
+
const compliance = (0, compliance_js_1.checkSpecCompliance)(spec, targetLevel);
|
|
14
|
+
const cognitiveCost = (0, cognitive_cost_js_1.calculateCognitiveCost)(spec);
|
|
15
|
+
const confidence = (0, confidence_js_1.calculateConfidence)(spec);
|
|
16
|
+
const tokenEstimate = (0, spec_estimator_js_1.estimateSpecTokens)(spec);
|
|
17
|
+
const recommendations = generateSmartRecommendations(compliance, cognitiveCost, confidence, tokenEstimate);
|
|
18
|
+
return {
|
|
19
|
+
spec,
|
|
20
|
+
compliance,
|
|
21
|
+
cognitiveCost,
|
|
22
|
+
confidence,
|
|
23
|
+
tokenEstimate,
|
|
24
|
+
recommendations
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function generateSmartRecommendations(compliance, cost, confidence, estimate) {
|
|
28
|
+
const recs = [];
|
|
29
|
+
// High priority: Compliance issues
|
|
30
|
+
if (!compliance.compliant && compliance.violations.length > 0) {
|
|
31
|
+
recs.push({
|
|
32
|
+
priority: 'high',
|
|
33
|
+
category: 'quality',
|
|
34
|
+
message: `${compliance.violations.length} sections don't meet L${compliance.level} compliance`,
|
|
35
|
+
action: 'Add or upgrade system parts in flagged sections'
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// High priority: Low confidence
|
|
39
|
+
if (confidence.overall < 70) {
|
|
40
|
+
recs.push({
|
|
41
|
+
priority: 'high',
|
|
42
|
+
category: 'confidence',
|
|
43
|
+
message: `Token estimation confidence is ${confidence.grade} (${confidence.overall.toFixed(0)}%)`,
|
|
44
|
+
action: confidence.recommendations[0]
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// Medium priority: High cognitive cost
|
|
48
|
+
if (cost.totalCost > 60) {
|
|
49
|
+
recs.push({
|
|
50
|
+
priority: 'medium',
|
|
51
|
+
category: 'cost',
|
|
52
|
+
message: `Cognitive cost is high (${cost.totalCost.toFixed(0)}/100)`,
|
|
53
|
+
action: 'Consider reducing some sections to L2 or L1'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Medium priority: Unreasonable token estimate
|
|
57
|
+
if (estimate.total.estimated > 4000) {
|
|
58
|
+
recs.push({
|
|
59
|
+
priority: 'medium',
|
|
60
|
+
category: 'quality',
|
|
61
|
+
message: `Estimated ${estimate.total.estimated} tokens - may be too large`,
|
|
62
|
+
action: 'Consider reducing scope or splitting into multiple prompts'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Low priority: Optimization opportunities
|
|
66
|
+
if (confidence.overall >= 70 && confidence.overall < 90) {
|
|
67
|
+
recs.push({
|
|
68
|
+
priority: 'low',
|
|
69
|
+
category: 'confidence',
|
|
70
|
+
message: 'Good confidence, but can be improved',
|
|
71
|
+
action: 'Upgrade a few sections to L3 for better precision'
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
// Low priority: Cost optimization
|
|
75
|
+
if (cost.totalCost < 30 && confidence.overall > 80) {
|
|
76
|
+
recs.push({
|
|
77
|
+
priority: 'low',
|
|
78
|
+
category: 'cost',
|
|
79
|
+
message: 'Excellent balance of cost and confidence',
|
|
80
|
+
action: 'No changes needed'
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return recs;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Format comprehensive smart report
|
|
87
|
+
*/
|
|
88
|
+
function formatSmartReport(analysis) {
|
|
89
|
+
const lines = [];
|
|
90
|
+
lines.push('╔═══════════════════════════════════════════════════╗');
|
|
91
|
+
lines.push('║ FLEX-MD SMART ANALYSIS REPORT ║');
|
|
92
|
+
lines.push('╚═══════════════════════════════════════════════════╝');
|
|
93
|
+
lines.push('');
|
|
94
|
+
// Summary Dashboard
|
|
95
|
+
lines.push('📊 SUMMARY DASHBOARD');
|
|
96
|
+
lines.push('─'.repeat(50));
|
|
97
|
+
lines.push(`Compliance: ${analysis.compliance.compliant ? '✓' : '✗'} L${analysis.compliance.level} ${analysis.compliance.compliant ? 'PASS' : 'FAIL'}`);
|
|
98
|
+
lines.push(`Confidence: ${analysis.confidence.grade} (${analysis.confidence.overall.toFixed(0)}%)`);
|
|
99
|
+
lines.push(`Cognitive Cost: ${analysis.cognitiveCost.totalCost.toFixed(0)}/100`);
|
|
100
|
+
lines.push(`Token Estimate: ${analysis.tokenEstimate.total.estimated} tokens`);
|
|
101
|
+
lines.push('');
|
|
102
|
+
// Key Metrics
|
|
103
|
+
lines.push('📈 KEY METRICS');
|
|
104
|
+
lines.push('─'.repeat(50));
|
|
105
|
+
lines.push(`Total Sections: ${analysis.spec.sections.length}`);
|
|
106
|
+
lines.push(`With System Parts: ${analysis.compliance.summary.compliantSections}`);
|
|
107
|
+
lines.push(`Coverage: ${analysis.confidence.bySection.filter(s => s.hasSystemPart).length}/${analysis.spec.sections.length} (${((analysis.confidence.bySection.filter(s => s.hasSystemPart).length / (analysis.spec.sections.length || 1)) * 100).toFixed(0)}%)`);
|
|
108
|
+
lines.push(`Average Compliance Level: L${(analysis.compliance.summary.averageLevel || 0).toFixed(1)}`);
|
|
109
|
+
lines.push('');
|
|
110
|
+
// Recommendations
|
|
111
|
+
if (analysis.recommendations.length > 0) {
|
|
112
|
+
lines.push('💡 RECOMMENDATIONS');
|
|
113
|
+
lines.push('─'.repeat(50));
|
|
114
|
+
const highPriority = analysis.recommendations.filter(r => r.priority === 'high');
|
|
115
|
+
const mediumPriority = analysis.recommendations.filter(r => r.priority === 'medium');
|
|
116
|
+
const lowPriority = analysis.recommendations.filter(r => r.priority === 'low');
|
|
117
|
+
if (highPriority.length > 0) {
|
|
118
|
+
lines.push('🔴 High Priority:');
|
|
119
|
+
highPriority.forEach(r => {
|
|
120
|
+
lines.push(` • ${r.message}`);
|
|
121
|
+
if (r.action)
|
|
122
|
+
lines.push(` → ${r.action}`);
|
|
123
|
+
});
|
|
124
|
+
lines.push('');
|
|
125
|
+
}
|
|
126
|
+
if (mediumPriority.length > 0) {
|
|
127
|
+
lines.push('🟡 Medium Priority:');
|
|
128
|
+
mediumPriority.forEach(r => {
|
|
129
|
+
lines.push(` • ${r.message}`);
|
|
130
|
+
if (r.action)
|
|
131
|
+
lines.push(` → ${r.action}`);
|
|
132
|
+
});
|
|
133
|
+
lines.push('');
|
|
134
|
+
}
|
|
135
|
+
if (lowPriority.length > 0) {
|
|
136
|
+
lines.push('🟢 Low Priority:');
|
|
137
|
+
lowPriority.forEach(r => {
|
|
138
|
+
lines.push(` • ${r.message}`);
|
|
139
|
+
if (r.action)
|
|
140
|
+
lines.push(` → ${r.action}`);
|
|
141
|
+
});
|
|
142
|
+
lines.push('');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Section Breakdown
|
|
146
|
+
lines.push('📋 SECTION BREAKDOWN');
|
|
147
|
+
lines.push('─'.repeat(50));
|
|
148
|
+
lines.push('Section Level Confidence Cost Tokens');
|
|
149
|
+
lines.push('─'.repeat(50));
|
|
150
|
+
analysis.confidence.bySection.forEach((conf, i) => {
|
|
151
|
+
const cost = analysis.cognitiveCost.perSection[i];
|
|
152
|
+
const tokens = analysis.tokenEstimate.bySectionName[conf.sectionName];
|
|
153
|
+
const level = conf.level !== null ? `L${conf.level}` : 'L0';
|
|
154
|
+
const confStr = `${conf.confidence.toFixed(0)}%`;
|
|
155
|
+
const costStr = cost ? `${cost.cost.toFixed(1)}` : '0.0';
|
|
156
|
+
const tokenStr = tokens ? `~${tokens.estimated}` : 'N/A';
|
|
157
|
+
const name = conf.sectionName.length > 24 ? conf.sectionName.slice(0, 21) + '...' : conf.sectionName.padEnd(24);
|
|
158
|
+
lines.push(`${name} ${level.padEnd(5)} ${confStr.padEnd(10)} ${costStr.padEnd(4)} ${tokenStr}`);
|
|
159
|
+
});
|
|
160
|
+
lines.push('');
|
|
161
|
+
// Confidence Factors
|
|
162
|
+
if (analysis.confidence.factors.length > 0) {
|
|
163
|
+
lines.push('🎯 CONFIDENCE FACTORS');
|
|
164
|
+
lines.push('─'.repeat(50));
|
|
165
|
+
analysis.confidence.factors.forEach(factor => {
|
|
166
|
+
const icon = factor.impact === 'positive' ? '✓' : factor.impact === 'negative' ? '✗' : '○';
|
|
167
|
+
const sign = factor.score >= 0 ? '+' : '';
|
|
168
|
+
lines.push(`${icon} ${factor.factor}: ${sign}${factor.score}`);
|
|
169
|
+
lines.push(` ${factor.description}`);
|
|
170
|
+
});
|
|
171
|
+
lines.push('');
|
|
172
|
+
}
|
|
173
|
+
// Cost Breakdown
|
|
174
|
+
lines.push('💰 COGNITIVE COST BREAKDOWN');
|
|
175
|
+
lines.push('─'.repeat(50));
|
|
176
|
+
lines.push(`Base Cost: ${analysis.cognitiveCost.breakdown.baseCost.toFixed(1)}`);
|
|
177
|
+
lines.push(`Complexity Cost: ${analysis.cognitiveCost.breakdown.complexityCost.toFixed(1)}`);
|
|
178
|
+
lines.push(`Decision Cost: ${analysis.cognitiveCost.breakdown.decisionCost.toFixed(1)}`);
|
|
179
|
+
lines.push(`Total Cost: ${analysis.cognitiveCost.totalCost.toFixed(1)}/100`);
|
|
180
|
+
lines.push(`Assessment: ${analysis.cognitiveCost.recommendation}`);
|
|
181
|
+
lines.push('');
|
|
182
|
+
// Token Estimate
|
|
183
|
+
lines.push('🔢 TOKEN ESTIMATE');
|
|
184
|
+
lines.push('─'.repeat(50));
|
|
185
|
+
lines.push(`Estimated: ${analysis.tokenEstimate.total.estimated} tokens`);
|
|
186
|
+
lines.push(`Range: ${analysis.tokenEstimate.total.min} - ${analysis.tokenEstimate.total.max} tokens`);
|
|
187
|
+
lines.push(`Overhead: ${analysis.tokenEstimate.overhead} tokens`);
|
|
188
|
+
lines.push(`Confidence: ${analysis.confidence.grade}`);
|
|
189
|
+
lines.push('');
|
|
190
|
+
return lines.join('\n');
|
|
191
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.estimateSpecTokens = estimateSpecTokens;
|
|
4
|
+
exports.estimateInputSpecTokens = estimateInputSpecTokens;
|
|
5
|
+
exports.estimateFormatSpecsTokens = estimateFormatSpecsTokens;
|
|
6
|
+
const parser_js_1 = require("./parser.cjs");
|
|
7
|
+
const estimator_js_1 = require("./estimator.cjs");
|
|
8
|
+
function estimateSpecTokens(spec, options = {}) {
|
|
9
|
+
const { includeOptional = true, safetyMultiplier = 1.2, strategy = 'average' } = options;
|
|
10
|
+
const bySectionName = {};
|
|
11
|
+
let totalEstimated = 0;
|
|
12
|
+
let totalMin = 0;
|
|
13
|
+
let totalMax = 0;
|
|
14
|
+
for (const section of spec.sections) {
|
|
15
|
+
// Skip optional sections if requested
|
|
16
|
+
const isRequired = section.required !== false; // Default to required if not explicitly false
|
|
17
|
+
if (!includeOptional && !isRequired)
|
|
18
|
+
continue;
|
|
19
|
+
const kind = section.kind || 'text';
|
|
20
|
+
const systemPart = (0, parser_js_1.parseSystemPart)(section.instruction, kind);
|
|
21
|
+
let estimate;
|
|
22
|
+
if (systemPart) {
|
|
23
|
+
estimate = (0, estimator_js_1.estimateTokens)(systemPart);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
estimate = (0, estimator_js_1.getFallbackEstimate)(kind, isRequired);
|
|
27
|
+
}
|
|
28
|
+
// Apply optional section discount
|
|
29
|
+
if (!isRequired) {
|
|
30
|
+
estimate = {
|
|
31
|
+
estimated: Math.round(estimate.estimated * 0.6),
|
|
32
|
+
min: Math.round(estimate.min * 0.5),
|
|
33
|
+
max: Math.round(estimate.max * 0.7),
|
|
34
|
+
confidence: 'low'
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
bySectionName[section.name] = estimate;
|
|
38
|
+
totalEstimated += estimate.estimated;
|
|
39
|
+
totalMin += estimate.min;
|
|
40
|
+
totalMax += estimate.max;
|
|
41
|
+
}
|
|
42
|
+
// Add overhead
|
|
43
|
+
const overhead = estimator_js_1.TOKEN_CONSTANTS.baseOverhead +
|
|
44
|
+
(spec.sections.length * estimator_js_1.TOKEN_CONSTANTS.headingOverhead);
|
|
45
|
+
totalEstimated += overhead;
|
|
46
|
+
totalMin += overhead;
|
|
47
|
+
totalMax += overhead;
|
|
48
|
+
// Apply strategy
|
|
49
|
+
let finalEstimate;
|
|
50
|
+
switch (strategy) {
|
|
51
|
+
case 'conservative':
|
|
52
|
+
finalEstimate = totalMin;
|
|
53
|
+
break;
|
|
54
|
+
case 'generous':
|
|
55
|
+
finalEstimate = totalMax;
|
|
56
|
+
break;
|
|
57
|
+
case 'average':
|
|
58
|
+
default:
|
|
59
|
+
finalEstimate = totalEstimated;
|
|
60
|
+
}
|
|
61
|
+
// Apply safety multiplier
|
|
62
|
+
finalEstimate = Math.ceil(finalEstimate * safetyMultiplier);
|
|
63
|
+
return {
|
|
64
|
+
total: {
|
|
65
|
+
estimated: finalEstimate,
|
|
66
|
+
min: Math.ceil(totalMin * safetyMultiplier),
|
|
67
|
+
max: Math.ceil(totalMax * safetyMultiplier),
|
|
68
|
+
confidence: 'medium'
|
|
69
|
+
},
|
|
70
|
+
bySectionName,
|
|
71
|
+
overhead
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Estimate tokens for an Input Format Spec.
|
|
76
|
+
* Uses the same logic as output format estimation.
|
|
77
|
+
*/
|
|
78
|
+
function estimateInputSpecTokens(spec, options = {}) {
|
|
79
|
+
// Input format uses the same structure as output format, so reuse the same logic
|
|
80
|
+
const outputSpec = {
|
|
81
|
+
...spec,
|
|
82
|
+
descriptorType: "output_format_spec"
|
|
83
|
+
};
|
|
84
|
+
return estimateSpecTokens(outputSpec, options);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Estimate tokens for both input and output format specs.
|
|
88
|
+
* Combines estimates from both if present.
|
|
89
|
+
*/
|
|
90
|
+
function estimateFormatSpecsTokens(inputSpec, outputSpec, options = {}) {
|
|
91
|
+
const inputEstimate = inputSpec ? estimateInputSpecTokens(inputSpec, options) : null;
|
|
92
|
+
const outputEstimate = outputSpec ? estimateSpecTokens(outputSpec, options) : null;
|
|
93
|
+
// If only one is present, return it
|
|
94
|
+
if (!inputEstimate && outputEstimate)
|
|
95
|
+
return outputEstimate;
|
|
96
|
+
if (inputEstimate && !outputEstimate)
|
|
97
|
+
return inputEstimate;
|
|
98
|
+
if (!inputEstimate && !outputEstimate) {
|
|
99
|
+
// Return empty estimate
|
|
100
|
+
return {
|
|
101
|
+
total: { estimated: 0, min: 0, max: 0, confidence: 'low' },
|
|
102
|
+
bySectionName: {},
|
|
103
|
+
overhead: 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Combine both estimates
|
|
107
|
+
const combined = {
|
|
108
|
+
total: {
|
|
109
|
+
estimated: inputEstimate.total.estimated + outputEstimate.total.estimated,
|
|
110
|
+
min: inputEstimate.total.min + outputEstimate.total.min,
|
|
111
|
+
max: inputEstimate.total.max + outputEstimate.total.max,
|
|
112
|
+
confidence: inputEstimate.total.confidence === 'high' && outputEstimate.total.confidence === 'high'
|
|
113
|
+
? 'high'
|
|
114
|
+
: inputEstimate.total.confidence === 'low' || outputEstimate.total.confidence === 'low'
|
|
115
|
+
? 'low'
|
|
116
|
+
: 'medium'
|
|
117
|
+
},
|
|
118
|
+
bySectionName: {
|
|
119
|
+
...Object.fromEntries(Object.entries(inputEstimate.bySectionName).map(([k, v]) => [`input:${k}`, v])),
|
|
120
|
+
...Object.fromEntries(Object.entries(outputEstimate.bySectionName).map(([k, v]) => [`output:${k}`, v]))
|
|
121
|
+
},
|
|
122
|
+
overhead: inputEstimate.overhead + outputEstimate.overhead
|
|
123
|
+
};
|
|
124
|
+
return combined;
|
|
125
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateSystemParts = validateSystemParts;
|
|
4
|
+
const parser_js_1 = require("./parser.cjs");
|
|
5
|
+
function validateSystemParts(spec) {
|
|
6
|
+
const errors = [];
|
|
7
|
+
const warnings = [];
|
|
8
|
+
for (const section of spec.sections) {
|
|
9
|
+
const isRequired = section.required !== false;
|
|
10
|
+
const kind = section.kind || 'text';
|
|
11
|
+
if (!section.instruction) {
|
|
12
|
+
if (isRequired) {
|
|
13
|
+
warnings.push({
|
|
14
|
+
sectionName: section.name,
|
|
15
|
+
message: 'Required section has no instruction. Token estimation will use fallback.'
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const systemPart = (0, parser_js_1.parseSystemPart)(section.instruction, kind);
|
|
21
|
+
if (!systemPart) {
|
|
22
|
+
warnings.push({
|
|
23
|
+
sectionName: section.name,
|
|
24
|
+
message: `No valid system part found. Expected pattern for '${kind}' kind.`
|
|
25
|
+
});
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
// Validate ranges
|
|
29
|
+
if (systemPart.parsed.type === 'items' || systemPart.parsed.type === 'lines') {
|
|
30
|
+
const { min, max } = systemPart.parsed;
|
|
31
|
+
if (max !== null && max < min) {
|
|
32
|
+
errors.push({
|
|
33
|
+
sectionName: section.name,
|
|
34
|
+
message: `Invalid range: max (${max}) is less than min (${min})`,
|
|
35
|
+
instruction: section.instruction
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (systemPart.parsed.type === 'table') {
|
|
40
|
+
const { rows, columns } = systemPart.parsed;
|
|
41
|
+
if (rows.max !== null && rows.max < rows.min) {
|
|
42
|
+
errors.push({
|
|
43
|
+
sectionName: section.name,
|
|
44
|
+
message: `Invalid rows range: max (${rows.max}) < min (${rows.min})`,
|
|
45
|
+
instruction: section.instruction
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (columns.max !== null && columns.max < columns.min) {
|
|
49
|
+
errors.push({
|
|
50
|
+
sectionName: section.name,
|
|
51
|
+
message: `Invalid columns range: max (${columns.max}) < min (${columns.min})`,
|
|
52
|
+
instruction: section.instruction
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
valid: errors.length === 0,
|
|
59
|
+
errors,
|
|
60
|
+
warnings
|
|
61
|
+
};
|
|
62
|
+
}
|