flex-md 4.7.1 → 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 +5 -0
- package/athenices-ai-gateway.md +25 -0
- 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 +45 -0
- package/package.json +12 -8
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ofsToSchema = ofsToSchema;
|
|
4
|
+
exports.transformWithOfs = transformWithOfs;
|
|
5
|
+
const nx_md_parser_1 = require("nx-md-parser");
|
|
6
|
+
const memory_js_1 = require("./memory.cjs");
|
|
7
|
+
const parse_js_1 = require("../md/parse.cjs");
|
|
8
|
+
const normalize_js_1 = require("../md/normalize.cjs");
|
|
9
|
+
const nx_helpers_1 = require("nx-helpers");
|
|
10
|
+
const nx_json_parser_1 = require("nx-json-parser");
|
|
11
|
+
const logger_js_1 = require("../logger.cjs");
|
|
12
|
+
/**
|
|
13
|
+
* Converts a Flex-MD OutputFormatSpec to an nx-md-parser Schema.
|
|
14
|
+
*/
|
|
15
|
+
function ofsToSchema(spec) {
|
|
16
|
+
const properties = {};
|
|
17
|
+
for (const section of spec.sections) {
|
|
18
|
+
let type;
|
|
19
|
+
switch (section.kind) {
|
|
20
|
+
case "list":
|
|
21
|
+
case "ordered_list":
|
|
22
|
+
type = nx_md_parser_1.Schema.array(nx_md_parser_1.Schema.string());
|
|
23
|
+
break;
|
|
24
|
+
case "table":
|
|
25
|
+
case "ordered_table":
|
|
26
|
+
if (section.columns && section.columns.length > 0) {
|
|
27
|
+
const rowProps = {};
|
|
28
|
+
for (const col of section.columns) {
|
|
29
|
+
rowProps[col] = nx_md_parser_1.Schema.string();
|
|
30
|
+
}
|
|
31
|
+
type = nx_md_parser_1.Schema.array(nx_md_parser_1.Schema.object(rowProps));
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
type = nx_md_parser_1.Schema.string();
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
case "text":
|
|
38
|
+
default:
|
|
39
|
+
type = nx_md_parser_1.Schema.string();
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
properties[section.name] = type;
|
|
43
|
+
}
|
|
44
|
+
if (spec.tables && spec.tables.length > 0) {
|
|
45
|
+
for (const table of spec.tables) {
|
|
46
|
+
const tableKey = table.columns.join("_");
|
|
47
|
+
const rowProps = {};
|
|
48
|
+
for (const col of table.columns) {
|
|
49
|
+
rowProps[col] = nx_md_parser_1.Schema.string();
|
|
50
|
+
}
|
|
51
|
+
properties[tableKey] = nx_md_parser_1.Schema.array(nx_md_parser_1.Schema.object(rowProps));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return nx_md_parser_1.Schema.object(properties);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Transforms markdown text using a Flex-MD OutputFormatSpec or a recallId.
|
|
58
|
+
* If no spec is provided, it attempts to infer it from the markdown (autospecs).
|
|
59
|
+
*/
|
|
60
|
+
function transformWithOfs(md, specOrRecallId) {
|
|
61
|
+
logger_js_1.logger.info("Starting Flex-MD transformation", {
|
|
62
|
+
inputLength: md.length,
|
|
63
|
+
hasSpec: !!specOrRecallId,
|
|
64
|
+
specType: specOrRecallId ? typeof specOrRecallId : 'none'
|
|
65
|
+
});
|
|
66
|
+
// 0. Normalize input (handle literal \n common in LLM outputs)
|
|
67
|
+
const normalizedMd = (0, normalize_js_1.normalizeMarkdownInput)(md);
|
|
68
|
+
logger_js_1.logger.debug("Input normalization", {
|
|
69
|
+
originalLength: md.length,
|
|
70
|
+
normalizedLength: normalizedMd.length,
|
|
71
|
+
changed: md !== normalizedMd
|
|
72
|
+
});
|
|
73
|
+
// 1. Automatic parsing (Dual-Response) using nx-json-parser
|
|
74
|
+
logger_js_1.logger.debug("Starting automatic parsing with nx-json-parser");
|
|
75
|
+
const parsedOutput = (0, nx_json_parser_1.markdownToJson)(normalizedMd);
|
|
76
|
+
logger_js_1.logger.debug("Automatic parsing completed", {
|
|
77
|
+
parsedKeys: Object.keys(parsedOutput),
|
|
78
|
+
parsedKeyCount: Object.keys(parsedOutput).length
|
|
79
|
+
});
|
|
80
|
+
if (!specOrRecallId) {
|
|
81
|
+
logger_js_1.logger.info("No spec provided, returning parsed output only");
|
|
82
|
+
return {
|
|
83
|
+
parsedOutput,
|
|
84
|
+
contractOutput: null,
|
|
85
|
+
contractStatus: "skipped",
|
|
86
|
+
status: "validated",
|
|
87
|
+
errors: []
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
let spec;
|
|
91
|
+
if (typeof specOrRecallId === "string") {
|
|
92
|
+
logger_js_1.logger.debug("Recalling spec from memory", { recallId: specOrRecallId });
|
|
93
|
+
const recalled = (0, memory_js_1.recall)(specOrRecallId);
|
|
94
|
+
if (!recalled) {
|
|
95
|
+
logger_js_1.logger.warn("Recall ID not found", { recallId: specOrRecallId });
|
|
96
|
+
return {
|
|
97
|
+
parsedOutput,
|
|
98
|
+
contractOutput: null,
|
|
99
|
+
contractStatus: "skipped",
|
|
100
|
+
status: "failed",
|
|
101
|
+
errors: [`Recall ID "${specOrRecallId}" not found in memory.`]
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
spec = recalled;
|
|
105
|
+
logger_js_1.logger.debug("Spec recalled successfully", {
|
|
106
|
+
sectionCount: spec.sections.length,
|
|
107
|
+
sectionNames: spec.sections.map(s => s.name)
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
spec = specOrRecallId;
|
|
112
|
+
logger_js_1.logger.debug("Using provided spec directly", {
|
|
113
|
+
sectionCount: spec.sections.length,
|
|
114
|
+
sectionNames: spec.sections.map(s => s.name)
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// 2. Parse sections using Flex-MD parser for the contract mapping
|
|
118
|
+
const bulletNames = spec.sections.map(s => s.name);
|
|
119
|
+
logger_js_1.logger.debug("Starting section parsing", {
|
|
120
|
+
expectedSections: bulletNames,
|
|
121
|
+
sectionCount: bulletNames.length
|
|
122
|
+
});
|
|
123
|
+
// Note: We use the local headings parser to find the specific sections defined in the spec
|
|
124
|
+
const parsedSections = (0, parse_js_1.parseHeadingsAndSections)(normalizedMd, { bulletNames });
|
|
125
|
+
logger_js_1.logger.debug("Section parsing completed", {
|
|
126
|
+
foundSections: parsedSections.map(s => s.heading.name),
|
|
127
|
+
foundCount: parsedSections.length
|
|
128
|
+
});
|
|
129
|
+
const parsedObj = {};
|
|
130
|
+
for (const sectionSpec of spec.sections) {
|
|
131
|
+
const normName = (0, parse_js_1.normalizeName)(sectionSpec.name);
|
|
132
|
+
const found = parsedSections.find(s => (0, parse_js_1.normalizeName)(s.heading.name) === normName);
|
|
133
|
+
logger_js_1.logger.verbose(`Processing section "${sectionSpec.name}"`, {
|
|
134
|
+
normalizedName: normName,
|
|
135
|
+
found: !!found,
|
|
136
|
+
kind: sectionSpec.kind,
|
|
137
|
+
hasColumns: !!sectionSpec.columns
|
|
138
|
+
});
|
|
139
|
+
if (found) {
|
|
140
|
+
let value;
|
|
141
|
+
switch (sectionSpec.kind) {
|
|
142
|
+
case "list":
|
|
143
|
+
case "ordered_list":
|
|
144
|
+
value = (0, parse_js_1.extractBullets)(found.body);
|
|
145
|
+
logger_js_1.logger.debug(`Extracted bullets for "${sectionSpec.name}"`, {
|
|
146
|
+
bulletCount: Array.isArray(value) ? value.length : 'unknown'
|
|
147
|
+
});
|
|
148
|
+
break;
|
|
149
|
+
case "table":
|
|
150
|
+
case "ordered_table":
|
|
151
|
+
if (sectionSpec.columns) {
|
|
152
|
+
value = (0, parse_js_1.parseMarkdownTable)(found.body, sectionSpec.columns);
|
|
153
|
+
logger_js_1.logger.debug(`Parsed table for "${sectionSpec.name}"`, {
|
|
154
|
+
columns: sectionSpec.columns,
|
|
155
|
+
rowCount: Array.isArray(value) ? value.length : 'unknown'
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
value = found.body.trim();
|
|
160
|
+
logger_js_1.logger.debug(`Using raw body for table "${sectionSpec.name}" (no columns specified)`);
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
value = found.body.trim();
|
|
165
|
+
logger_js_1.logger.debug(`Using trimmed body for "${sectionSpec.name}"`);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
parsedObj[sectionSpec.name] = value;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
logger_js_1.logger.warn(`Section "${sectionSpec.name}" not found in markdown`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
logger_js_1.logger.debug("Contract parsing completed", {
|
|
175
|
+
parsedKeys: Object.keys(parsedObj),
|
|
176
|
+
parsedKeyCount: Object.keys(parsedObj).length
|
|
177
|
+
});
|
|
178
|
+
// 3. Transform using nx-md-parser (latest v2.2.0) for schema validation and fixing
|
|
179
|
+
const schema = ofsToSchema(spec);
|
|
180
|
+
const transformer = new nx_md_parser_1.JSONTransformer(schema);
|
|
181
|
+
const transformResult = transformer.transform(parsedObj);
|
|
182
|
+
// 4. Compare parsed results with contract results
|
|
183
|
+
const autoKeys = Object.keys(parsedOutput).sort();
|
|
184
|
+
const contractKeys = transformResult.result ? Object.keys(transformResult.result).map(k => (0, nx_helpers_1.toCamelCase)(k)).sort() : [];
|
|
185
|
+
const isSame = autoKeys.length > 0 &&
|
|
186
|
+
autoKeys.every(k => contractKeys.includes(k)) &&
|
|
187
|
+
contractKeys.length === autoKeys.length;
|
|
188
|
+
return {
|
|
189
|
+
parsedOutput,
|
|
190
|
+
contractOutput: transformResult.result,
|
|
191
|
+
contractStatus: isSame ? "ok" : "different",
|
|
192
|
+
status: transformResult.status,
|
|
193
|
+
errors: transformResult.errors || []
|
|
194
|
+
};
|
|
195
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildMarkdownGuidance = buildMarkdownGuidance;
|
|
4
|
+
exports.enrichInstructions = enrichInstructions;
|
|
5
|
+
exports.enrichInstructionsWithFlexMd = enrichInstructionsWithFlexMd;
|
|
6
|
+
/**
|
|
7
|
+
* Generates Markdown guidance instructions for the LLM based on the OFS and contract level.
|
|
8
|
+
* Strictly avoids the word "flex-md" and remains "tax-aware" by only including relevant rules.
|
|
9
|
+
*/
|
|
10
|
+
function buildMarkdownGuidance(spec, strict, opts) {
|
|
11
|
+
const level = strict.level ?? 0;
|
|
12
|
+
const fence = opts?.containerFence === "flexmd" ? "```flexmd" : "```markdown";
|
|
13
|
+
// L0 - Minimal Markdown
|
|
14
|
+
if (level === 0) {
|
|
15
|
+
return "Reply in Markdown.";
|
|
16
|
+
}
|
|
17
|
+
const lines = [];
|
|
18
|
+
// L2+ Container Rule
|
|
19
|
+
if (level >= 2) {
|
|
20
|
+
lines.push(`Return your entire answer inside a single ${fence} fenced block and nothing else.`);
|
|
21
|
+
lines.push("");
|
|
22
|
+
lines.push("Inside the block, include these section headings somewhere (order does not matter):");
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
lines.push("Reply in Markdown.");
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push("Include these section headings somewhere (order does not matter):");
|
|
28
|
+
}
|
|
29
|
+
// Heading List
|
|
30
|
+
for (const s of spec.sections) {
|
|
31
|
+
let suffix = "";
|
|
32
|
+
if (level >= 3) {
|
|
33
|
+
if (s.kind === "list")
|
|
34
|
+
suffix = " (list)";
|
|
35
|
+
else if (s.kind === "ordered_list")
|
|
36
|
+
suffix = " (ordered list)";
|
|
37
|
+
}
|
|
38
|
+
lines.push(`- ${s.name}${suffix}`);
|
|
39
|
+
if (level >= 1) {
|
|
40
|
+
if (s.description) {
|
|
41
|
+
lines.push(` Description: ${s.description}`);
|
|
42
|
+
}
|
|
43
|
+
if (s.instruction) {
|
|
44
|
+
lines.push(` Instruction: ${s.instruction}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
lines.push("");
|
|
49
|
+
// None Rule
|
|
50
|
+
const noneRule = opts?.includeNoneRule ?? "auto";
|
|
51
|
+
const hasRequired = spec.sections.some(s => s.required !== false);
|
|
52
|
+
const includeNone = noneRule === "always" || (noneRule === "auto" && (hasRequired || level >= 3));
|
|
53
|
+
if (includeNone) {
|
|
54
|
+
lines.push(`If a section is empty, write \`None\`.`);
|
|
55
|
+
lines.push("");
|
|
56
|
+
}
|
|
57
|
+
// List Rules
|
|
58
|
+
const listRule = opts?.includeListRules ?? "auto";
|
|
59
|
+
const hasListDecls = spec.sections.some(s => s.kind === "list" || s.kind === "ordered_list");
|
|
60
|
+
const includeList = listRule === "always" || (listRule === "auto" && (hasListDecls || level >= 3));
|
|
61
|
+
if (includeList) {
|
|
62
|
+
lines.push("List rules:");
|
|
63
|
+
lines.push("- Use numbered lists (`1.`, `2.`, …) for ordered lists.");
|
|
64
|
+
lines.push("- Use `-` bullets for unordered lists.");
|
|
65
|
+
lines.push("");
|
|
66
|
+
}
|
|
67
|
+
// Table Rules
|
|
68
|
+
const tableRule = opts?.includeTableRules ?? "auto";
|
|
69
|
+
const hasTableDecls = (spec.tables && spec.tables.length > 0) || spec.sections.some(s => s.kind === "table" || s.kind === "ordered_table");
|
|
70
|
+
const includeTable = tableRule === "always" || (tableRule === "auto" && (hasTableDecls || level >= 3));
|
|
71
|
+
if (includeTable) {
|
|
72
|
+
const hasOrderedTable = spec.tables?.some(t => t.kind === "ordered_table") || spec.sections.some(s => s.kind === "ordered_table");
|
|
73
|
+
lines.push("Tables:");
|
|
74
|
+
lines.push("- If you include a table, use a Markdown pipe table.");
|
|
75
|
+
if (hasOrderedTable || level >= 3) {
|
|
76
|
+
lines.push("- For ordered tables, add a first column named `#` and number rows 1..N.");
|
|
77
|
+
}
|
|
78
|
+
lines.push("");
|
|
79
|
+
}
|
|
80
|
+
if (level >= 3) {
|
|
81
|
+
lines.push("Do not return JSON as the response format.");
|
|
82
|
+
}
|
|
83
|
+
return lines.join("\n").trim();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* @deprecated Use buildMarkdownGuidance
|
|
87
|
+
*/
|
|
88
|
+
function enrichInstructions(spec, strict) {
|
|
89
|
+
return buildMarkdownGuidance(spec, strict);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Enrich instructions with flex-md compliance guidance.
|
|
93
|
+
* Returns detailed information about what was changed.
|
|
94
|
+
*/
|
|
95
|
+
async function enrichInstructionsWithFlexMd(instructions, strictnessLevel, spec // Optional spec, if provided will include section guidance
|
|
96
|
+
) {
|
|
97
|
+
const originalLength = instructions.length;
|
|
98
|
+
const level = parseInt(strictnessLevel.slice(1));
|
|
99
|
+
// Default spec if none provided (minimal)
|
|
100
|
+
const effectiveSpec = spec ?? { sections: [] };
|
|
101
|
+
const guidance = buildMarkdownGuidance(effectiveSpec, { level });
|
|
102
|
+
// Check if guidance is already present (simple check)
|
|
103
|
+
if (instructions.includes(guidance)) {
|
|
104
|
+
return {
|
|
105
|
+
enriched: instructions,
|
|
106
|
+
changes: [],
|
|
107
|
+
originalLength,
|
|
108
|
+
enhancedLength: originalLength,
|
|
109
|
+
metadata: {
|
|
110
|
+
sectionsAdded: 0,
|
|
111
|
+
containerAdded: false,
|
|
112
|
+
guidanceAdded: false
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const separator = instructions.trim().length > 0 ? "\n\n" : "";
|
|
117
|
+
const enriched = instructions.trim() + separator + guidance;
|
|
118
|
+
const enhancedLength = enriched.length;
|
|
119
|
+
const changes = [
|
|
120
|
+
{
|
|
121
|
+
type: "added-guidance",
|
|
122
|
+
description: `Added Flex-MD ${strictnessLevel} compliance guidance.`,
|
|
123
|
+
location: {
|
|
124
|
+
start: instructions.trim().length + separator.length,
|
|
125
|
+
end: enhancedLength
|
|
126
|
+
},
|
|
127
|
+
content: guidance
|
|
128
|
+
}
|
|
129
|
+
];
|
|
130
|
+
if (level >= 2) {
|
|
131
|
+
changes.push({
|
|
132
|
+
type: "added-container",
|
|
133
|
+
description: "Added requirement for a fenced container block.",
|
|
134
|
+
location: {
|
|
135
|
+
start: instructions.trim().length + separator.length,
|
|
136
|
+
end: enhancedLength
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
enriched,
|
|
142
|
+
changes,
|
|
143
|
+
originalLength,
|
|
144
|
+
enhancedLength,
|
|
145
|
+
metadata: {
|
|
146
|
+
sectionsAdded: effectiveSpec.sections.length,
|
|
147
|
+
containerAdded: level >= 2,
|
|
148
|
+
guidanceAdded: true
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inferOfsFromMarkdown = inferOfsFromMarkdown;
|
|
4
|
+
const parse_js_1 = require("../md/parse.cjs");
|
|
5
|
+
/**
|
|
6
|
+
* Infers an OutputFormatSpec from a Markdown string.
|
|
7
|
+
*/
|
|
8
|
+
function inferOfsFromMarkdown(md) {
|
|
9
|
+
// Collect all bullet names that look like headers ("- Name")
|
|
10
|
+
const lines = md.split("\n");
|
|
11
|
+
const bulletNames = [];
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
// Match "- Name" or "- Name\n" or "- Name " but NOT "- Name: more text"
|
|
14
|
+
const m = line.match(/^[-*+]\s+([^—:\n]+)$/);
|
|
15
|
+
if (m) {
|
|
16
|
+
bulletNames.push(m[1].trim());
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const sections = (0, parse_js_1.parseHeadingsAndSections)(md, { bulletNames });
|
|
20
|
+
const specSections = [];
|
|
21
|
+
for (const sec of sections) {
|
|
22
|
+
const name = sec.heading.name;
|
|
23
|
+
const body = sec.body.trim();
|
|
24
|
+
// 1. Detect list
|
|
25
|
+
const bullets = (0, parse_js_1.extractBullets)(body);
|
|
26
|
+
if (bullets.length > 0) {
|
|
27
|
+
specSections.push({
|
|
28
|
+
name,
|
|
29
|
+
kind: "list",
|
|
30
|
+
required: true
|
|
31
|
+
});
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// 2. Detect table (basic check)
|
|
35
|
+
const lines = body.split("\n").map(l => l.trim()).filter(Boolean);
|
|
36
|
+
if (lines.length >= 2 && lines[0].startsWith("|") && /^[|\s-:]+$/.test(lines[1])) {
|
|
37
|
+
// Extract columns
|
|
38
|
+
const cols = lines[0].split("|").map(c => c.trim()).filter(Boolean);
|
|
39
|
+
specSections.push({
|
|
40
|
+
name,
|
|
41
|
+
kind: "table",
|
|
42
|
+
columns: cols,
|
|
43
|
+
required: true
|
|
44
|
+
});
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// Default to text
|
|
48
|
+
specSections.push({
|
|
49
|
+
name,
|
|
50
|
+
kind: "text",
|
|
51
|
+
required: true
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
descriptorType: "output_format_spec",
|
|
56
|
+
format: "markdown",
|
|
57
|
+
sectionOrderMatters: false,
|
|
58
|
+
sections: specSections,
|
|
59
|
+
tablesOptional: true,
|
|
60
|
+
tables: [],
|
|
61
|
+
emptySectionValue: "None"
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseIssuesEnvelope = parseIssuesEnvelope;
|
|
4
|
+
exports.buildIssuesEnvelope = buildIssuesEnvelope;
|
|
5
|
+
exports.buildIssuesEnvelopeAuto = buildIssuesEnvelopeAuto;
|
|
6
|
+
const parse_js_1 = require("../md/parse.cjs");
|
|
7
|
+
function parseIssuesEnvelope(md) {
|
|
8
|
+
return (0, parse_js_1.isIssuesEnvelopeCheck)(md);
|
|
9
|
+
}
|
|
10
|
+
function buildIssuesEnvelope(args) {
|
|
11
|
+
const issues = args.validation.issues
|
|
12
|
+
.filter(i => i.severity === "warn" || i.severity === "error")
|
|
13
|
+
.map(i => `- ${i.message}`);
|
|
14
|
+
const expected = args.expectedSummary.map(x => `- ${x}`);
|
|
15
|
+
const found = args.foundSummary.map(x => `- ${x}`);
|
|
16
|
+
const how = args.howToFix.map(x => `- ${x}`);
|
|
17
|
+
return [
|
|
18
|
+
"## Status",
|
|
19
|
+
"Non-compliant output (cannot be repaired to the required format).",
|
|
20
|
+
"",
|
|
21
|
+
"## Issues",
|
|
22
|
+
issues.length ? issues.join("\n") : "- None",
|
|
23
|
+
"",
|
|
24
|
+
"## Expected",
|
|
25
|
+
expected.length ? expected.join("\n") : "- None",
|
|
26
|
+
"",
|
|
27
|
+
"## Found",
|
|
28
|
+
found.length ? found.join("\n") : "- None",
|
|
29
|
+
"",
|
|
30
|
+
"## How to fix",
|
|
31
|
+
how.length ? how.join("\n") : "- None",
|
|
32
|
+
"",
|
|
33
|
+
].join("\n");
|
|
34
|
+
}
|
|
35
|
+
function buildIssuesEnvelopeAuto(args) {
|
|
36
|
+
const v = args.validation;
|
|
37
|
+
const expected = [];
|
|
38
|
+
const found = [];
|
|
39
|
+
const how = [];
|
|
40
|
+
if (args.level >= 2) {
|
|
41
|
+
expected.push("One single ```markdown fenced block containing the entire answer (no text outside).");
|
|
42
|
+
}
|
|
43
|
+
if (args.level >= 1 && (args.requiredSectionNames?.length ?? 0) > 0) {
|
|
44
|
+
expected.push(`Section headings present (order does not matter): ${args.requiredSectionNames.join(", ")}`);
|
|
45
|
+
}
|
|
46
|
+
if (args.level >= 3 && (args.kindRequirements?.length ?? 0) > 0) {
|
|
47
|
+
for (const k of args.kindRequirements)
|
|
48
|
+
expected.push(k);
|
|
49
|
+
}
|
|
50
|
+
if (v.stats) {
|
|
51
|
+
found.push(`Detected sections: ${v.stats.sectionCount}`);
|
|
52
|
+
if (v.stats.missingRequired.length)
|
|
53
|
+
found.push(`Missing: ${v.stats.missingRequired.join(", ")}`);
|
|
54
|
+
if (v.stats.duplicates.length)
|
|
55
|
+
found.push(`Duplicates: ${v.stats.duplicates.join(", ")}`);
|
|
56
|
+
found.push(`Fences: ${v.stats.container.fenceCount} (langs: ${v.stats.container.fenceLangs.join(", ") || "none"})`);
|
|
57
|
+
found.push(`Outside text: ${v.stats.container.hasOutsideText ? "yes" : "no"}`);
|
|
58
|
+
}
|
|
59
|
+
if (args.level >= 2)
|
|
60
|
+
how.push("Return a single ```markdown fenced block and nothing else.");
|
|
61
|
+
if (args.level >= 1)
|
|
62
|
+
how.push("Include the required headings and put content under each (order does not matter).");
|
|
63
|
+
how.push('If a section is empty, write "None".');
|
|
64
|
+
if (args.level >= 3) {
|
|
65
|
+
how.push('Use numbered lists ("1.", "2.", …) for ordered lists and "-" bullets for unordered lists.');
|
|
66
|
+
how.push("Use Markdown pipe tables for table sections; ordered tables include a first column '#'.");
|
|
67
|
+
how.push("Do not return JSON as the response format.");
|
|
68
|
+
}
|
|
69
|
+
return buildIssuesEnvelope({
|
|
70
|
+
validation: v,
|
|
71
|
+
level: args.level,
|
|
72
|
+
expectedSummary: expected,
|
|
73
|
+
foundSummary: found,
|
|
74
|
+
howToFix: how,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.remember = remember;
|
|
4
|
+
exports.recall = recall;
|
|
5
|
+
const parser_js_1 = require("./parser.cjs");
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const specMemory = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Parses Flex-MD instructions and "remembers" the Output Format Spec.
|
|
10
|
+
* Returns a unique recallId that can be used later.
|
|
11
|
+
*/
|
|
12
|
+
function remember(instructions) {
|
|
13
|
+
const spec = (0, parser_js_1.parseOutputFormatSpec)(instructions);
|
|
14
|
+
if (!spec) {
|
|
15
|
+
throw new Error("No valid Output Format Spec found in instructions.");
|
|
16
|
+
}
|
|
17
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
18
|
+
specMemory.set(id, spec);
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Recalls a previously remembered Output Format Spec.
|
|
23
|
+
*/
|
|
24
|
+
function recall(id) {
|
|
25
|
+
return specMemory.get(id);
|
|
26
|
+
}
|