flex-md 3.0.0 → 3.2.0
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 +30 -1
- package/dist/__tests__/diagnostics.test.d.ts +1 -0
- package/dist/__tests__/diagnostics.test.js +59 -0
- package/dist/__tests__/ofs.test.d.ts +1 -0
- package/dist/__tests__/ofs.test.js +51 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/ofs/enricher.d.ts +6 -1
- package/dist/ofs/enricher.js +69 -0
- package/dist/ofs/parser.d.ts +6 -1
- package/dist/ofs/parser.js +70 -0
- package/dist/ofs/stringify.d.ts +1 -1
- package/dist/ofs/stringify.js +11 -1
- package/dist/types.d.ts +69 -0
- package/dist/validate/compliance.d.ts +11 -0
- package/dist/validate/compliance.js +91 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,8 +9,8 @@ Version 3.0 introduces the **Detect-Repair-Enforce** pipeline, ensuring LLM resp
|
|
|
9
9
|
- **Standard Markdown**: No proprietary tags. Pure headings, lists, and tables.
|
|
10
10
|
- **Strictness Levels (L0–L3)**: From loose guidance to rigid structural enforcement.
|
|
11
11
|
- **Deterministic Repair**: Auto-fixes misformatted LLM output (merged fences, missing headings, format conversion).
|
|
12
|
+
- **Instructions Output Format Guidance**: Generate formal "Instructions Blocks" for LLM prompts directly from spec objects.
|
|
12
13
|
- **Issues Envelope**: A structured failure format for when repairs fail, allowing safe fallbacks.
|
|
13
|
-
- **Tax-Aware Prompts**: Generates minimal, relevant instructions to save tokens.
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
@@ -61,6 +61,35 @@ if (result.ok) {
|
|
|
61
61
|
}
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
### 4. Dynamic Instructions Output Format Blocks
|
|
65
|
+
|
|
66
|
+
You can also define your spec as a plain JavaScript object and generate the formal "Instructions Block" for your LLM prompts.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { stringifyOutputFormatSpec } from 'flex-md';
|
|
70
|
+
|
|
71
|
+
const spec = {
|
|
72
|
+
description: "Standard report format for technical analysis.",
|
|
73
|
+
sections: [
|
|
74
|
+
{
|
|
75
|
+
name: "Summary",
|
|
76
|
+
kind: "text",
|
|
77
|
+
required: true,
|
|
78
|
+
description: "A brief summary of the topic."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "Key Points",
|
|
82
|
+
kind: "list",
|
|
83
|
+
required: false,
|
|
84
|
+
instruction: "Include at least 3 bullet points."
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const instructionsBlock = stringifyOutputFormatSpec(spec);
|
|
90
|
+
// Output will include the ## Output format (Markdown) header, descriptions, and instructions.
|
|
91
|
+
```
|
|
92
|
+
|
|
64
93
|
## Strictness Levels
|
|
65
94
|
|
|
66
95
|
| Level | Goal | Guidance | Enforcement |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { checkCompliance, hasFlexMdContract, enrichInstructionsWithFlexMd, validateFormat } from "../index.js";
|
|
3
|
+
describe("Diagnostics", () => {
|
|
4
|
+
describe("checkCompliance", () => {
|
|
5
|
+
it("should return meetsCompliance: true for L0 if Markdown is mentioned", async () => {
|
|
6
|
+
const res = await checkCompliance("Reply in Markdown.", "L0");
|
|
7
|
+
expect(res.meetsCompliance).toBe(true);
|
|
8
|
+
expect(res.issues).toHaveLength(0);
|
|
9
|
+
});
|
|
10
|
+
it("should return issues for L2 if container is missing", async () => {
|
|
11
|
+
const res = await checkCompliance("Reply in Markdown. Include headings.", "L2");
|
|
12
|
+
expect(res.meetsCompliance).toBe(false);
|
|
13
|
+
expect(res.issues.some(i => i.type === "missing-container")).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it("should return false for hasFlexMdContract if requirements not met", async () => {
|
|
16
|
+
const ok = await hasFlexMdContract("Hello", "L0");
|
|
17
|
+
expect(ok).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe("enrichInstructionsWithFlexMd", () => {
|
|
21
|
+
it("should append guidance to existing instructions", async () => {
|
|
22
|
+
const res = await enrichInstructionsWithFlexMd("Do a good job.", "L0");
|
|
23
|
+
expect(res.enriched).toContain("Do a good job.");
|
|
24
|
+
expect(res.enriched).toContain("Reply in Markdown.");
|
|
25
|
+
expect(res.changes).toHaveLength(1);
|
|
26
|
+
expect(res.metadata?.guidanceAdded).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it("should track container addition for L2", async () => {
|
|
29
|
+
const res = await enrichInstructionsWithFlexMd("Do a good job.", "L2");
|
|
30
|
+
expect(res.changes.some(c => c.type === "added-container")).toBe(true);
|
|
31
|
+
expect(res.metadata?.containerAdded).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("validateFormat", () => {
|
|
35
|
+
it("should validate a correct flex-md OFS", async () => {
|
|
36
|
+
const ofs = `## Output format\n- Summary — prose\n- Actions — list`;
|
|
37
|
+
const res = await validateFormat(ofs);
|
|
38
|
+
expect(res.valid).toBe(true);
|
|
39
|
+
expect(res.metadata?.sectionCount).toBe(2);
|
|
40
|
+
});
|
|
41
|
+
it("should return errors for missing heading", async () => {
|
|
42
|
+
const ofs = `- Summary — prose`;
|
|
43
|
+
const res = await validateFormat(ofs);
|
|
44
|
+
expect(res.valid).toBe(false);
|
|
45
|
+
expect(res.issues.some(i => i.type === "missing-heading")).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
it("should validate JSON schema", async () => {
|
|
48
|
+
const schema = `{"type": "object"}`;
|
|
49
|
+
const res = await validateFormat(schema, "json-schema");
|
|
50
|
+
expect(res.valid).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
it("should fail on invalid JSON schema", async () => {
|
|
53
|
+
const schema = `{"type": "object"`;
|
|
54
|
+
const res = await validateFormat(schema, "json-schema");
|
|
55
|
+
expect(res.valid).toBe(false);
|
|
56
|
+
expect(res.issues[0].type).toBe("syntax-error");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { stringifyOutputFormatSpec } from "../ofs/stringify.js";
|
|
3
|
+
import { buildMarkdownGuidance } from "../ofs/enricher.js";
|
|
4
|
+
describe("Instructions Output Format Block Generation", () => {
|
|
5
|
+
const spec = {
|
|
6
|
+
description: "Standard report format for technical analysis.",
|
|
7
|
+
sections: [
|
|
8
|
+
{
|
|
9
|
+
name: "Summary",
|
|
10
|
+
kind: "text",
|
|
11
|
+
required: true,
|
|
12
|
+
description: "A brief summary of the topic.",
|
|
13
|
+
instruction: "Keep it under 3 sentences."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "Key Points",
|
|
17
|
+
kind: "list",
|
|
18
|
+
required: false,
|
|
19
|
+
description: "Important takeaways."
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
emptySectionValue: "N/A"
|
|
23
|
+
};
|
|
24
|
+
it("should stringify an OFS object correctly", () => {
|
|
25
|
+
const md = stringifyOutputFormatSpec(spec);
|
|
26
|
+
expect(md).toContain("## Output format (Markdown)");
|
|
27
|
+
expect(md).toContain("Standard report format for technical analysis.");
|
|
28
|
+
expect(md).toContain("- Summary — text (required)");
|
|
29
|
+
expect(md).toContain("Description: A brief summary of the topic.");
|
|
30
|
+
expect(md).toContain("Instruction: Keep it under 3 sentences.");
|
|
31
|
+
expect(md).toContain("- Key Points — list (optional)");
|
|
32
|
+
expect(md).toContain("Description: Important takeaways.");
|
|
33
|
+
expect(md).toContain("If a section is empty, write `N/A`.");
|
|
34
|
+
});
|
|
35
|
+
it("should build markdown guidance (L1) correctly", () => {
|
|
36
|
+
const guidance = buildMarkdownGuidance(spec, { level: 1 });
|
|
37
|
+
expect(guidance).toContain("Include these section headings somewhere");
|
|
38
|
+
expect(guidance).toContain("- Summary");
|
|
39
|
+
expect(guidance).toContain("Description: A brief summary of the topic.");
|
|
40
|
+
expect(guidance).toContain("Instruction: Keep it under 3 sentences.");
|
|
41
|
+
expect(guidance).toContain("- Key Points");
|
|
42
|
+
expect(guidance).toContain("Description: Important takeaways.");
|
|
43
|
+
});
|
|
44
|
+
it("should build markdown guidance (L3) correctly", () => {
|
|
45
|
+
const guidance = buildMarkdownGuidance(spec, { level: 3 });
|
|
46
|
+
expect(guidance).toContain("Return your entire answer inside a single ```markdown fenced block");
|
|
47
|
+
expect(guidance).toContain("- Summary");
|
|
48
|
+
expect(guidance).toContain("- Key Points (list)");
|
|
49
|
+
expect(guidance).toContain("Do not return JSON");
|
|
50
|
+
});
|
|
51
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export * from "./types.js";
|
|
2
2
|
export * from "./strictness/types.js";
|
|
3
3
|
export * from "./md/parse.js";
|
|
4
|
-
export { parseOutputFormatSpec } from "./ofs/parser.js";
|
|
4
|
+
export { parseOutputFormatSpec, validateFormat } from "./ofs/parser.js";
|
|
5
5
|
export { stringifyOutputFormatSpec } from "./ofs/stringify.js";
|
|
6
|
-
export { buildMarkdownGuidance, enrichInstructions } from "./ofs/enricher.js";
|
|
6
|
+
export { buildMarkdownGuidance, enrichInstructions, enrichInstructionsWithFlexMd } from "./ofs/enricher.js";
|
|
7
7
|
export { validateMarkdownAgainstOfs } from "./validate/validate.js";
|
|
8
|
+
export { checkCompliance, hasFlexMdContract } from "./validate/compliance.js";
|
|
8
9
|
export { extractFromMarkdown } from "./extract/extract.js";
|
|
9
10
|
export { processResponseMarkdown } from "./strictness/processor.js";
|
|
10
11
|
export { parseIssuesEnvelope, buildIssuesEnvelope, buildIssuesEnvelopeAuto } from "./ofs/issuesEnvelope.js";
|
package/dist/index.js
CHANGED
|
@@ -4,11 +4,12 @@ export * from "./strictness/types.js";
|
|
|
4
4
|
// Shared MD Parsing
|
|
5
5
|
export * from "./md/parse.js";
|
|
6
6
|
// Output Format Spec (OFS)
|
|
7
|
-
export { parseOutputFormatSpec } from "./ofs/parser.js";
|
|
7
|
+
export { parseOutputFormatSpec, validateFormat } from "./ofs/parser.js";
|
|
8
8
|
export { stringifyOutputFormatSpec } from "./ofs/stringify.js";
|
|
9
|
-
export { buildMarkdownGuidance, enrichInstructions } from "./ofs/enricher.js";
|
|
9
|
+
export { buildMarkdownGuidance, enrichInstructions, enrichInstructionsWithFlexMd } from "./ofs/enricher.js";
|
|
10
10
|
// Validation & Extraction
|
|
11
11
|
export { validateMarkdownAgainstOfs } from "./validate/validate.js";
|
|
12
|
+
export { checkCompliance, hasFlexMdContract } from "./validate/compliance.js";
|
|
12
13
|
export { extractFromMarkdown } from "./extract/extract.js";
|
|
13
14
|
// Processor & Fallback
|
|
14
15
|
export { processResponseMarkdown } from "./strictness/processor.js";
|
package/dist/ofs/enricher.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OutputFormatSpec } from "../types.js";
|
|
1
|
+
import { OutputFormatSpec, EnhancementResult } from "../types.js";
|
|
2
2
|
import { StrictnessOptions } from "../strictness/types.js";
|
|
3
3
|
/**
|
|
4
4
|
* Generates Markdown guidance instructions for the LLM based on the OFS and contract level.
|
|
@@ -14,3 +14,8 @@ export declare function buildMarkdownGuidance(spec: OutputFormatSpec, strict: St
|
|
|
14
14
|
* @deprecated Use buildMarkdownGuidance
|
|
15
15
|
*/
|
|
16
16
|
export declare function enrichInstructions(spec: OutputFormatSpec, strict: StrictnessOptions): string;
|
|
17
|
+
/**
|
|
18
|
+
* Enrich instructions with flex-md compliance guidance.
|
|
19
|
+
* Returns detailed information about what was changed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function enrichInstructionsWithFlexMd(instructions: string, strictnessLevel: "L0" | "L1" | "L2" | "L3", spec?: OutputFormatSpec): Promise<EnhancementResult>;
|
package/dist/ofs/enricher.js
CHANGED
|
@@ -31,6 +31,14 @@ export function buildMarkdownGuidance(spec, strict, opts) {
|
|
|
31
31
|
suffix = " (ordered list)";
|
|
32
32
|
}
|
|
33
33
|
lines.push(`- ${s.name}${suffix}`);
|
|
34
|
+
if (level >= 1) {
|
|
35
|
+
if (s.description) {
|
|
36
|
+
lines.push(` Description: ${s.description}`);
|
|
37
|
+
}
|
|
38
|
+
if (s.instruction) {
|
|
39
|
+
lines.push(` Instruction: ${s.instruction}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
34
42
|
}
|
|
35
43
|
lines.push("");
|
|
36
44
|
// None Rule
|
|
@@ -75,3 +83,64 @@ export function buildMarkdownGuidance(spec, strict, opts) {
|
|
|
75
83
|
export function enrichInstructions(spec, strict) {
|
|
76
84
|
return buildMarkdownGuidance(spec, strict);
|
|
77
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Enrich instructions with flex-md compliance guidance.
|
|
88
|
+
* Returns detailed information about what was changed.
|
|
89
|
+
*/
|
|
90
|
+
export async function enrichInstructionsWithFlexMd(instructions, strictnessLevel, spec // Optional spec, if provided will include section guidance
|
|
91
|
+
) {
|
|
92
|
+
const originalLength = instructions.length;
|
|
93
|
+
const level = parseInt(strictnessLevel.slice(1));
|
|
94
|
+
// Default spec if none provided (minimal)
|
|
95
|
+
const effectiveSpec = spec ?? { sections: [] };
|
|
96
|
+
const guidance = buildMarkdownGuidance(effectiveSpec, { level });
|
|
97
|
+
// Check if guidance is already present (simple check)
|
|
98
|
+
if (instructions.includes(guidance)) {
|
|
99
|
+
return {
|
|
100
|
+
enriched: instructions,
|
|
101
|
+
changes: [],
|
|
102
|
+
originalLength,
|
|
103
|
+
enhancedLength: originalLength,
|
|
104
|
+
metadata: {
|
|
105
|
+
sectionsAdded: 0,
|
|
106
|
+
containerAdded: false,
|
|
107
|
+
guidanceAdded: false
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const separator = instructions.trim().length > 0 ? "\n\n" : "";
|
|
112
|
+
const enriched = instructions.trim() + separator + guidance;
|
|
113
|
+
const enhancedLength = enriched.length;
|
|
114
|
+
const changes = [
|
|
115
|
+
{
|
|
116
|
+
type: "added-guidance",
|
|
117
|
+
description: `Added Flex-MD ${strictnessLevel} compliance guidance.`,
|
|
118
|
+
location: {
|
|
119
|
+
start: instructions.trim().length + separator.length,
|
|
120
|
+
end: enhancedLength
|
|
121
|
+
},
|
|
122
|
+
content: guidance
|
|
123
|
+
}
|
|
124
|
+
];
|
|
125
|
+
if (level >= 2) {
|
|
126
|
+
changes.push({
|
|
127
|
+
type: "added-container",
|
|
128
|
+
description: "Added requirement for a fenced container block.",
|
|
129
|
+
location: {
|
|
130
|
+
start: instructions.trim().length + separator.length,
|
|
131
|
+
end: enhancedLength
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
enriched,
|
|
137
|
+
changes,
|
|
138
|
+
originalLength,
|
|
139
|
+
enhancedLength,
|
|
140
|
+
metadata: {
|
|
141
|
+
sectionsAdded: effectiveSpec.sections.length,
|
|
142
|
+
containerAdded: level >= 2,
|
|
143
|
+
guidanceAdded: true
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
package/dist/ofs/parser.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type { OutputFormatSpec } from "../types.js";
|
|
1
|
+
import type { OutputFormatSpec, FormatValidationResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validate a format specification.
|
|
4
|
+
* Returns detailed validation results.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateFormat(formatSpec: string, formatType?: "flex-md" | "json-schema"): Promise<FormatValidationResult>;
|
|
2
7
|
/**
|
|
3
8
|
* Parse an Output Format Spec block from Markdown.
|
|
4
9
|
*/
|
package/dist/ofs/parser.js
CHANGED
|
@@ -1,3 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a format specification.
|
|
3
|
+
* Returns detailed validation results.
|
|
4
|
+
*/
|
|
5
|
+
export async function validateFormat(formatSpec, formatType = "flex-md") {
|
|
6
|
+
const issues = [];
|
|
7
|
+
const suggestions = [];
|
|
8
|
+
if (formatType === "json-schema") {
|
|
9
|
+
try {
|
|
10
|
+
JSON.parse(formatSpec);
|
|
11
|
+
return {
|
|
12
|
+
valid: true,
|
|
13
|
+
formatType: "json-schema",
|
|
14
|
+
issues: [],
|
|
15
|
+
suggestions: [],
|
|
16
|
+
metadata: {
|
|
17
|
+
structureType: "json"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return {
|
|
23
|
+
valid: false,
|
|
24
|
+
formatType: "json-schema",
|
|
25
|
+
issues: [{
|
|
26
|
+
type: "syntax-error",
|
|
27
|
+
description: `Invalid JSON: ${e.message}`,
|
|
28
|
+
severity: "error"
|
|
29
|
+
}],
|
|
30
|
+
suggestions: ["Ensure the format specification is valid JSON."],
|
|
31
|
+
metadata: {
|
|
32
|
+
structureType: "json"
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// flex-md validation
|
|
38
|
+
const spec = parseOutputFormatSpec(formatSpec);
|
|
39
|
+
if (!formatSpec.toLowerCase().includes("output format")) {
|
|
40
|
+
issues.push({
|
|
41
|
+
type: "missing-heading",
|
|
42
|
+
description: "Missing '## Output format' heading.",
|
|
43
|
+
severity: "error",
|
|
44
|
+
suggestion: "Add '## Output format' to start the specification block."
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (!spec || spec.sections.length === 0) {
|
|
48
|
+
issues.push({
|
|
49
|
+
type: "missing-section",
|
|
50
|
+
description: "No sections detected in the specification.",
|
|
51
|
+
severity: "error",
|
|
52
|
+
suggestion: "Add at least one section using the '- Name — Kind' format."
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const valid = !issues.some(i => i.severity === "error");
|
|
56
|
+
if (!valid) {
|
|
57
|
+
suggestions.push(...issues.map(i => i.suggestion).filter((s) => !!s));
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
valid,
|
|
61
|
+
formatType: "flex-md",
|
|
62
|
+
issues,
|
|
63
|
+
suggestions,
|
|
64
|
+
metadata: {
|
|
65
|
+
sectionCount: spec?.sections.length ?? 0,
|
|
66
|
+
hasContainer: formatSpec.includes("```"),
|
|
67
|
+
structureType: "markdown"
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
1
71
|
export function parseOutputFormatSpec(md, opts = {}) {
|
|
2
72
|
const headingRx = opts.headingRegex ?? /^##\s*Output format\b/i;
|
|
3
73
|
const lines = md.split("\n");
|
package/dist/ofs/stringify.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OutputFormatSpec } from "../types.js";
|
|
2
2
|
/**
|
|
3
|
-
* Convert an OutputFormatSpec to
|
|
3
|
+
* Convert an OutputFormatSpec to a formal "Instructions Output Format Block".
|
|
4
4
|
*/
|
|
5
5
|
export declare function stringifyOutputFormatSpec(spec: OutputFormatSpec): string;
|
package/dist/ofs/stringify.js
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Convert an OutputFormatSpec to
|
|
2
|
+
* Convert an OutputFormatSpec to a formal "Instructions Output Format Block".
|
|
3
3
|
*/
|
|
4
4
|
export function stringifyOutputFormatSpec(spec) {
|
|
5
5
|
const lines = [];
|
|
6
6
|
lines.push(`## Output format (Markdown)`);
|
|
7
|
+
if (spec.description) {
|
|
8
|
+
lines.push(spec.description);
|
|
9
|
+
lines.push("");
|
|
10
|
+
}
|
|
7
11
|
lines.push(`Include these sections somewhere (order does not matter):`);
|
|
8
12
|
lines.push("");
|
|
9
13
|
for (const s of spec.sections) {
|
|
10
14
|
const k = s.kind === "ordered_list" ? "ordered list" : (s.kind || "text");
|
|
11
15
|
const required = s.required === true ? " (required)" : (s.required === false ? " (optional)" : "");
|
|
12
16
|
lines.push(`- ${s.name} — ${k}${required}`);
|
|
17
|
+
if (s.description) {
|
|
18
|
+
lines.push(` Description: ${s.description}`);
|
|
19
|
+
}
|
|
20
|
+
if (s.instruction) {
|
|
21
|
+
lines.push(` Instruction: ${s.instruction}`);
|
|
22
|
+
}
|
|
13
23
|
}
|
|
14
24
|
const tables = spec.tables || [];
|
|
15
25
|
if (tables.length) {
|
package/dist/types.d.ts
CHANGED
|
@@ -86,6 +86,8 @@ export interface OutputSectionSpec {
|
|
|
86
86
|
kind?: SectionKind;
|
|
87
87
|
required?: boolean;
|
|
88
88
|
columns?: string[];
|
|
89
|
+
description?: string;
|
|
90
|
+
instruction?: string;
|
|
89
91
|
}
|
|
90
92
|
/**
|
|
91
93
|
* @deprecated Use OutputSectionSpec
|
|
@@ -101,6 +103,7 @@ export interface OfsTable {
|
|
|
101
103
|
required?: boolean;
|
|
102
104
|
}
|
|
103
105
|
export interface OutputFormatSpec {
|
|
106
|
+
description?: string;
|
|
104
107
|
sections: OutputSectionSpec[];
|
|
105
108
|
emptySectionValue?: string;
|
|
106
109
|
descriptorType?: "output_format_spec";
|
|
@@ -174,3 +177,69 @@ export interface IssuesEnvelope {
|
|
|
174
177
|
bullets: string[];
|
|
175
178
|
}>;
|
|
176
179
|
}
|
|
180
|
+
export interface ComplianceIssue {
|
|
181
|
+
type: "missing-section" | "missing-container" | "invalid-format" | "missing-heading" | "other";
|
|
182
|
+
description: string;
|
|
183
|
+
severity: "error" | "warning";
|
|
184
|
+
location?: {
|
|
185
|
+
start: number;
|
|
186
|
+
end: number;
|
|
187
|
+
line?: number;
|
|
188
|
+
column?: number;
|
|
189
|
+
};
|
|
190
|
+
suggestion?: string;
|
|
191
|
+
}
|
|
192
|
+
export interface ComplianceCheckResult {
|
|
193
|
+
meetsCompliance: boolean;
|
|
194
|
+
complianceLevel: "L0" | "L1" | "L2" | "L3";
|
|
195
|
+
issues: ComplianceIssue[];
|
|
196
|
+
suggestions: string[];
|
|
197
|
+
metadata?: {
|
|
198
|
+
hasContainer?: boolean;
|
|
199
|
+
hasRequiredSections?: boolean;
|
|
200
|
+
sectionCount?: number;
|
|
201
|
+
containerType?: "fenced-block" | "none";
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
export interface EnhancementChange {
|
|
205
|
+
type: "added-section" | "added-container" | "modified-section" | "added-guidance" | "other";
|
|
206
|
+
description: string;
|
|
207
|
+
location: {
|
|
208
|
+
start: number;
|
|
209
|
+
end: number;
|
|
210
|
+
};
|
|
211
|
+
content?: string;
|
|
212
|
+
}
|
|
213
|
+
export interface EnhancementResult {
|
|
214
|
+
enriched: string;
|
|
215
|
+
changes: EnhancementChange[];
|
|
216
|
+
originalLength: number;
|
|
217
|
+
enhancedLength: number;
|
|
218
|
+
metadata?: {
|
|
219
|
+
sectionsAdded?: number;
|
|
220
|
+
containerAdded?: boolean;
|
|
221
|
+
guidanceAdded?: boolean;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
export interface FormatValidationIssue {
|
|
225
|
+
type: "syntax-error" | "missing-section" | "invalid-structure" | "invalid-markdown" | "missing-heading" | "other";
|
|
226
|
+
description: string;
|
|
227
|
+
severity: "error" | "warning";
|
|
228
|
+
location?: {
|
|
229
|
+
start: number;
|
|
230
|
+
end: number;
|
|
231
|
+
line?: number;
|
|
232
|
+
};
|
|
233
|
+
suggestion?: string;
|
|
234
|
+
}
|
|
235
|
+
export interface FormatValidationResult {
|
|
236
|
+
valid: boolean;
|
|
237
|
+
formatType: "flex-md" | "json-schema" | "other" | "unknown";
|
|
238
|
+
issues: FormatValidationIssue[];
|
|
239
|
+
suggestions: string[];
|
|
240
|
+
metadata?: {
|
|
241
|
+
sectionCount?: number;
|
|
242
|
+
hasContainer?: boolean;
|
|
243
|
+
structureType?: string;
|
|
244
|
+
};
|
|
245
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ComplianceCheckResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Check if instructions meet the required flex-md compliance level.
|
|
4
|
+
* Returns detailed results including what's missing or wrong.
|
|
5
|
+
*/
|
|
6
|
+
export declare function checkCompliance(instructions: string, complianceLevel: "L0" | "L1" | "L2" | "L3"): Promise<ComplianceCheckResult>;
|
|
7
|
+
/**
|
|
8
|
+
* Check if instructions meet the required flex-md compliance level.
|
|
9
|
+
* @deprecated Use checkCompliance for detailed results.
|
|
10
|
+
*/
|
|
11
|
+
export declare function hasFlexMdContract(instructions: string, complianceLevel: "L0" | "L1" | "L2" | "L3"): Promise<boolean>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if instructions meet the required flex-md compliance level.
|
|
3
|
+
* Returns detailed results including what's missing or wrong.
|
|
4
|
+
*/
|
|
5
|
+
export async function checkCompliance(instructions, complianceLevel) {
|
|
6
|
+
const issues = [];
|
|
7
|
+
const suggestions = [];
|
|
8
|
+
const lower = instructions.toLowerCase();
|
|
9
|
+
const hasMarkdownMention = lower.includes("markdown");
|
|
10
|
+
const hasSectionMention = lower.includes("section") || lower.includes("heading");
|
|
11
|
+
const hasContainerMention = (lower.includes("fenced block") || lower.includes("```")) && (lower.includes("inside") || lower.includes("wrapped"));
|
|
12
|
+
const hasNoneRule = lower.includes("none") && lower.includes("empty");
|
|
13
|
+
const hasListRules = lower.includes("list rules") || (lower.includes("ordered list") && lower.includes("unordered list"));
|
|
14
|
+
const hasTableRules = lower.includes("table") && lower.includes("pipe");
|
|
15
|
+
const levelNum = parseInt(complianceLevel.slice(1));
|
|
16
|
+
// L0 requirements
|
|
17
|
+
if (levelNum >= 0) {
|
|
18
|
+
if (!hasMarkdownMention) {
|
|
19
|
+
issues.push({
|
|
20
|
+
type: "other",
|
|
21
|
+
description: "Instructions do not mention 'Markdown'.",
|
|
22
|
+
severity: "error",
|
|
23
|
+
suggestion: "Add 'Reply in Markdown.' to your instructions."
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// L1 requirements
|
|
28
|
+
if (levelNum >= 1) {
|
|
29
|
+
if (!hasSectionMention) {
|
|
30
|
+
issues.push({
|
|
31
|
+
type: "missing-section",
|
|
32
|
+
description: "Instructions do not specify required sections or headings.",
|
|
33
|
+
severity: "error",
|
|
34
|
+
suggestion: "Include a list of required section headings."
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// L2 requirements
|
|
39
|
+
if (levelNum >= 2) {
|
|
40
|
+
if (!hasContainerMention) {
|
|
41
|
+
issues.push({
|
|
42
|
+
type: "missing-container",
|
|
43
|
+
description: "Instructions do not require a fenced container block (L2+ requirement).",
|
|
44
|
+
severity: "error",
|
|
45
|
+
suggestion: "Add 'Return your entire answer inside a single ```markdown fenced block and nothing else.' to your instructions."
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// L3 requirements
|
|
50
|
+
if (levelNum >= 3) {
|
|
51
|
+
if (!hasNoneRule) {
|
|
52
|
+
issues.push({
|
|
53
|
+
type: "other",
|
|
54
|
+
description: "Instructions are missing the 'None' rule for empty sections (L3 requirement).",
|
|
55
|
+
severity: "warning",
|
|
56
|
+
suggestion: "Add 'If a section is empty, write `None`.' to your instructions."
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (!hasListRules) {
|
|
60
|
+
issues.push({
|
|
61
|
+
type: "invalid-format",
|
|
62
|
+
description: "Instructions are missing specific list formatting rules (L3 requirement).",
|
|
63
|
+
severity: "warning",
|
|
64
|
+
suggestion: "Add bullet and numbered list formatting instructions."
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const meetsCompliance = !issues.some(i => i.severity === "error");
|
|
69
|
+
if (!meetsCompliance) {
|
|
70
|
+
suggestions.push(...issues.map(i => i.suggestion).filter((s) => !!s));
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
meetsCompliance,
|
|
74
|
+
complianceLevel,
|
|
75
|
+
issues,
|
|
76
|
+
suggestions,
|
|
77
|
+
metadata: {
|
|
78
|
+
hasContainer: hasContainerMention,
|
|
79
|
+
hasRequiredSections: hasSectionMention,
|
|
80
|
+
containerType: hasContainerMention ? "fenced-block" : "none"
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if instructions meet the required flex-md compliance level.
|
|
86
|
+
* @deprecated Use checkCompliance for detailed results.
|
|
87
|
+
*/
|
|
88
|
+
export async function hasFlexMdContract(instructions, complianceLevel) {
|
|
89
|
+
const result = await checkCompliance(instructions, complianceLevel);
|
|
90
|
+
return result.meetsCompliance;
|
|
91
|
+
}
|
package/package.json
CHANGED