flex-md 3.5.0 โ 4.1.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 +423 -39
- package/dist/index.cjs +62 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/md/parse.d.ts +1 -0
- package/dist/md/parse.js +12 -0
- package/dist/ofs/parser.js +31 -10
- package/dist/tokens/auto-fix.d.ts +10 -0
- package/dist/tokens/auto-fix.js +56 -0
- package/dist/tokens/cognitive-cost.d.ts +10 -0
- package/dist/tokens/cognitive-cost.js +205 -0
- package/dist/tokens/compliance.d.ts +10 -0
- package/dist/tokens/compliance.js +70 -0
- package/dist/tokens/confidence.d.ts +6 -0
- package/dist/tokens/confidence.js +332 -0
- package/dist/tokens/estimator.d.ts +12 -0
- package/dist/tokens/estimator.js +138 -0
- package/dist/tokens/improvements.d.ts +10 -0
- package/dist/tokens/improvements.js +697 -0
- package/dist/tokens/index.d.ts +24 -0
- package/dist/tokens/index.js +31 -0
- package/dist/tokens/parser.d.ts +3 -0
- package/dist/tokens/parser.js +97 -0
- package/dist/tokens/patterns.d.ts +9 -0
- package/dist/tokens/patterns.js +20 -0
- package/dist/tokens/smart-report.d.ts +10 -0
- package/dist/tokens/smart-report.js +187 -0
- package/dist/tokens/spec-estimator.d.ts +7 -0
- package/dist/tokens/spec-estimator.js +68 -0
- package/dist/tokens/types.d.ts +185 -0
- package/dist/tokens/validator.d.ts +16 -0
- package/dist/tokens/validator.js +59 -0
- package/docs/Recommended New Strategies for AI Request Builder.md +691 -0
- package/package.json +7 -3
- package/dist/detection/detector.d.ts +0 -6
- package/dist/detection/detector.js +0 -104
- package/dist/detection/extractor.d.ts +0 -10
- package/dist/detection/extractor.js +0 -54
- package/dist/issues/build.d.ts +0 -26
- package/dist/issues/build.js +0 -62
- package/dist/md/lists.d.ts +0 -14
- package/dist/md/lists.js +0 -33
- package/dist/md/tables.d.ts +0 -25
- package/dist/md/tables.js +0 -72
- package/dist/ofs/extractor.d.ts +0 -9
- package/dist/ofs/extractor.js +0 -75
- package/dist/ofs/issues.d.ts +0 -14
- package/dist/ofs/issues.js +0 -92
- package/dist/ofs/validator.d.ts +0 -10
- package/dist/ofs/validator.js +0 -91
- package/dist/outline/builder.d.ts +0 -10
- package/dist/outline/builder.js +0 -85
- package/dist/outline/renderer.d.ts +0 -6
- package/dist/outline/renderer.js +0 -23
- package/dist/parser.d.ts +0 -2
- package/dist/parser.js +0 -199
- package/dist/parsers/lists.d.ts +0 -6
- package/dist/parsers/lists.js +0 -36
- package/dist/parsers/tables.d.ts +0 -10
- package/dist/parsers/tables.js +0 -58
- package/dist/stringify.d.ts +0 -2
- package/dist/stringify.js +0 -110
- package/dist/test-pipeline.js +0 -53
- package/dist/test-runner.d.ts +0 -1
- package/dist/test-runner.js +0 -331
- package/dist/test-strictness.d.ts +0 -1
- package/dist/test-strictness.js +0 -213
- package/dist/util.d.ts +0 -5
- package/dist/util.js +0 -64
- package/dist/validate/policy.d.ts +0 -10
- package/dist/validate/policy.js +0 -17
- package/dist/validator.d.ts +0 -2
- package/dist/validator.js +0 -80
- /package/dist/{test-pipeline.d.ts โ tokens/types.js} +0 -0
package/dist/test-pipeline.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { enforceFlexMd, parseOutputFormatSpec, detectResponseKind } from "./index.js";
|
|
2
|
-
function assert(condition, message) {
|
|
3
|
-
if (!condition) {
|
|
4
|
-
throw new Error(`Assertion failed: ${message}`);
|
|
5
|
-
}
|
|
6
|
-
}
|
|
7
|
-
console.log("๐งช Running Pipeline & JSON Detection Tests\n");
|
|
8
|
-
const spec = parseOutputFormatSpec(`
|
|
9
|
-
## Output format
|
|
10
|
-
- Answer โ prose (required)
|
|
11
|
-
- Reasoning โ ordered list (optional)
|
|
12
|
-
`);
|
|
13
|
-
// 1. Detection Kind
|
|
14
|
-
console.log("--- Detection Kind ---");
|
|
15
|
-
const mdResp = "## Answer\nThis is an answer.";
|
|
16
|
-
const kind1 = detectResponseKind(mdResp, spec);
|
|
17
|
-
console.log("FlexMD Kind:", kind1.kind);
|
|
18
|
-
assert(kind1.kind === "flexmd", "Should detect flexmd");
|
|
19
|
-
const issuesResp = "## Status\n- status: error\n- code: fail\n- message: fail\n\n## Issues\n- issue: x";
|
|
20
|
-
const kind2 = detectResponseKind(issuesResp, spec);
|
|
21
|
-
console.log("Issues Kind:", kind2.kind);
|
|
22
|
-
assert(kind2.kind === "issues", "Should detect issues");
|
|
23
|
-
const plainResp = "Just some text.";
|
|
24
|
-
const kind3 = detectResponseKind(plainResp, spec);
|
|
25
|
-
console.log("Markdown Kind:", kind3.kind);
|
|
26
|
-
assert(kind3.kind === "markdown", "Should detect markdown");
|
|
27
|
-
// 2. JSON fixing
|
|
28
|
-
console.log("\n--- JSON Fixing ---");
|
|
29
|
-
const jsonResp = "```json\n{\n \"Answer\": \"Parsed from JSON\",\n \"Reasoning\": [\"Step 1\", \"Step 2\"]\n}\n```";
|
|
30
|
-
const resultJson = enforceFlexMd(jsonResp, spec, { level: 1 });
|
|
31
|
-
console.log("JSON Fixed OK:", resultJson.ok);
|
|
32
|
-
console.log("JSON Fixed Content:\n", resultJson.extracted?.sectionsByName["Answer"]?.md);
|
|
33
|
-
assert(resultJson.ok === true, "Enforce should succeed with fixed JSON");
|
|
34
|
-
assert(!!resultJson.extracted?.sectionsByName["Answer"]?.md.includes("Parsed from JSON"), "Should extract from fixed JSON");
|
|
35
|
-
assert(resultJson.extracted?.sectionsByName["Reasoning"]?.list?.items.length === 2, "Should extract list from fixed JSON");
|
|
36
|
-
// 3. Raw JSON object
|
|
37
|
-
console.log("\n--- Raw JSON Object ---");
|
|
38
|
-
const rawJson = "{\"Answer\": \"Raw JSON\"}";
|
|
39
|
-
const resultRaw = enforceFlexMd(rawJson, spec, { level: 1 });
|
|
40
|
-
console.log("Raw JSON Fixed OK:", resultRaw.ok);
|
|
41
|
-
assert(resultRaw.ok === true, "Should fix raw JSON");
|
|
42
|
-
// 4. Missing sections fixing
|
|
43
|
-
console.log("\n--- Missing Sections ---");
|
|
44
|
-
// The fixer (repairToMarkdownLevel) SHOULD add missing required sections.
|
|
45
|
-
const failingResp = "## SomeOtherSection\nContent";
|
|
46
|
-
const resultFixed = enforceFlexMd(failingResp, spec, { level: 1 });
|
|
47
|
-
console.log("Auto-Repaired OK:", resultFixed.ok);
|
|
48
|
-
console.log("Repaired Content Contains 'Answer':", resultFixed.outputText.includes("## Answer"));
|
|
49
|
-
console.log("Repaired Content Contains 'None':", resultFixed.outputText.includes("None"));
|
|
50
|
-
assert(resultFixed.ok === true, "Should be ok if required sections are auto-repaired");
|
|
51
|
-
assert(resultFixed.outputText.includes("## Answer"), "Should have added 'Answer' heading");
|
|
52
|
-
assert(resultFixed.outputText.includes("None"), "Should have filled with 'None'");
|
|
53
|
-
console.log("\nโ
Pipeline Tests Passed");
|
package/dist/test-runner.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/test-runner.js
DELETED
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Layer A
|
|
3
|
-
parseFlexMd, stringifyFlexMd, validateFlexMd,
|
|
4
|
-
// Layer B
|
|
5
|
-
parseOutputFormatSpec, stringifyOutputFormatSpec, enrichInstructions, validateMarkdownAgainstOfs as validateOutput, extractFromMarkdown as extractOutput,
|
|
6
|
-
// Outline
|
|
7
|
-
buildOutline, renderOutline,
|
|
8
|
-
// Parsers
|
|
9
|
-
parseNestedList as parseList, parsePipeTable, extractPipeTables as extractAllTables, enforceOrderedTable,
|
|
10
|
-
// Strictness
|
|
11
|
-
strictnessDefaults,
|
|
12
|
-
// Layer C
|
|
13
|
-
detectObjects, parseAny } from "./index.js";
|
|
14
|
-
let passed = 0;
|
|
15
|
-
let failed = 0;
|
|
16
|
-
function test(name, fn) {
|
|
17
|
-
try {
|
|
18
|
-
fn();
|
|
19
|
-
console.log(`โ
${name}`);
|
|
20
|
-
passed++;
|
|
21
|
-
}
|
|
22
|
-
catch (e) {
|
|
23
|
-
console.error(`โ ${name}`);
|
|
24
|
-
console.error(e);
|
|
25
|
-
failed++;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function assert(condition, message) {
|
|
29
|
-
if (!condition) {
|
|
30
|
-
throw new Error(`Assertion failed: ${message}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
console.log("๐งช Running FlexMD v1.1 Tests\n");
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// Layer A Tests
|
|
36
|
-
// ============================================================================
|
|
37
|
-
console.log("๐ฆ Layer A - FlexMD Frames\n");
|
|
38
|
-
test("Parse basic FlexMD frame", () => {
|
|
39
|
-
const md = `[[message role=user id=m1]]
|
|
40
|
-
@tags: auth, login
|
|
41
|
-
Hello world
|
|
42
|
-
`;
|
|
43
|
-
const doc = parseFlexMd(md);
|
|
44
|
-
assert(doc.frames.length === 1, "Should have 1 frame");
|
|
45
|
-
assert(doc.frames[0].type === "message", "Type should be message");
|
|
46
|
-
assert(doc.frames[0].role === "user", "Role should be user");
|
|
47
|
-
assert(Array.isArray(doc.frames[0].meta?.tags), "Tags should be array");
|
|
48
|
-
});
|
|
49
|
-
test("Stringify FlexMD frame", () => {
|
|
50
|
-
const doc = {
|
|
51
|
-
frames: [{
|
|
52
|
-
type: "message",
|
|
53
|
-
role: "user",
|
|
54
|
-
id: "m1",
|
|
55
|
-
meta: { tags: ["auth", "login"] },
|
|
56
|
-
body_md: "Hello world\n"
|
|
57
|
-
}]
|
|
58
|
-
};
|
|
59
|
-
const md = stringifyFlexMd(doc);
|
|
60
|
-
assert(md.includes("[[message"), "Should contain frame header");
|
|
61
|
-
assert(md.includes("@tags:"), "Should contain meta");
|
|
62
|
-
});
|
|
63
|
-
test("Parse with type inference", () => {
|
|
64
|
-
const md = `[[message]]
|
|
65
|
-
@priority: 5
|
|
66
|
-
@enabled: true
|
|
67
|
-
@value: null
|
|
68
|
-
Body text
|
|
69
|
-
`;
|
|
70
|
-
const doc = parseFlexMd(md, { metaTypeMode: "infer" });
|
|
71
|
-
assert(doc.frames[0].meta?.priority === 5, "Should infer number");
|
|
72
|
-
assert(doc.frames[0].meta?.enabled === true, "Should infer boolean");
|
|
73
|
-
assert(doc.frames[0].meta?.value === null, "Should infer null");
|
|
74
|
-
});
|
|
75
|
-
test("Validate FlexMD", () => {
|
|
76
|
-
const valid = `[[message role=user]]
|
|
77
|
-
Hello
|
|
78
|
-
`;
|
|
79
|
-
const result = validateFlexMd(valid);
|
|
80
|
-
assert(result.valid === true, "Should be valid");
|
|
81
|
-
});
|
|
82
|
-
// ============================================================================
|
|
83
|
-
// Layer B Tests
|
|
84
|
-
// ============================================================================
|
|
85
|
-
console.log("\n๐ Layer B - Output Format Spec\n");
|
|
86
|
-
test("Parse OFS", () => {
|
|
87
|
-
const md = `## Output format (Markdown)
|
|
88
|
-
Include these sections somewhere (order does not matter):
|
|
89
|
-
|
|
90
|
-
- Short answer โ prose
|
|
91
|
-
- Reasoning โ ordered list
|
|
92
|
-
- Assumptions โ list
|
|
93
|
-
`;
|
|
94
|
-
const spec = parseOutputFormatSpec(md);
|
|
95
|
-
assert(spec !== null, "Spec should not be null");
|
|
96
|
-
assert(spec.sections.length === 3, "Should have 3 sections");
|
|
97
|
-
assert(spec.sections[0].name === "Short answer", "First section name");
|
|
98
|
-
assert(spec.sections[0].kind === "prose", "First section kind");
|
|
99
|
-
assert(spec.sections[1].kind === "ordered_list", "Second section kind");
|
|
100
|
-
});
|
|
101
|
-
test("Stringify OFS", () => {
|
|
102
|
-
const spec = {
|
|
103
|
-
descriptorType: "output_format_spec",
|
|
104
|
-
format: "markdown",
|
|
105
|
-
sectionOrderMatters: false,
|
|
106
|
-
sections: [
|
|
107
|
-
{ name: "Answer", kind: "prose" }
|
|
108
|
-
],
|
|
109
|
-
tablesOptional: true,
|
|
110
|
-
tables: [],
|
|
111
|
-
emptySectionValue: "None"
|
|
112
|
-
};
|
|
113
|
-
const md = stringifyOutputFormatSpec(spec);
|
|
114
|
-
assert(md.includes("Answer โ prose"), "Should contain section");
|
|
115
|
-
assert(md.includes("None"), "Should contain empty value");
|
|
116
|
-
});
|
|
117
|
-
test("Enrich instructions", () => {
|
|
118
|
-
const spec = {
|
|
119
|
-
descriptorType: "output_format_spec",
|
|
120
|
-
format: "markdown",
|
|
121
|
-
sectionOrderMatters: false,
|
|
122
|
-
sections: [
|
|
123
|
-
{ name: "Steps", kind: "ordered_list" },
|
|
124
|
-
{ name: "Notes", kind: "list" }
|
|
125
|
-
],
|
|
126
|
-
tablesOptional: true,
|
|
127
|
-
tables: [],
|
|
128
|
-
emptySectionValue: "None"
|
|
129
|
-
};
|
|
130
|
-
const instructions = enrichInstructions(spec, strictnessDefaults(1));
|
|
131
|
-
assert(instructions.includes("numbered items"), "Should mention ordered lists");
|
|
132
|
-
assert(instructions.includes("bullets"), "Should mention lists");
|
|
133
|
-
assert(instructions.includes("None"), "Should mention empty value");
|
|
134
|
-
});
|
|
135
|
-
test("Validate output against OFS", () => {
|
|
136
|
-
const spec = {
|
|
137
|
-
descriptorType: "output_format_spec",
|
|
138
|
-
format: "markdown",
|
|
139
|
-
sectionOrderMatters: false,
|
|
140
|
-
sections: [
|
|
141
|
-
{ name: "Answer", kind: "prose" }
|
|
142
|
-
],
|
|
143
|
-
tablesOptional: true,
|
|
144
|
-
tables: []
|
|
145
|
-
};
|
|
146
|
-
const md = `## Answer\nThe answer is 42.`;
|
|
147
|
-
const result = validateOutput(md, spec, strictnessDefaults(1));
|
|
148
|
-
assert(result.ok === true, "Should validate successfully");
|
|
149
|
-
});
|
|
150
|
-
test("Extract output from OFS", () => {
|
|
151
|
-
const spec = {
|
|
152
|
-
descriptorType: "output_format_spec",
|
|
153
|
-
format: "markdown",
|
|
154
|
-
sectionOrderMatters: false,
|
|
155
|
-
sections: [
|
|
156
|
-
{ name: "Answer", kind: "prose" },
|
|
157
|
-
{ name: "Steps", kind: "ordered_list" }
|
|
158
|
-
],
|
|
159
|
-
tablesOptional: true,
|
|
160
|
-
tables: []
|
|
161
|
-
};
|
|
162
|
-
const md = `## Answer
|
|
163
|
-
The answer is 42.
|
|
164
|
-
|
|
165
|
-
## Steps
|
|
166
|
-
1. First step
|
|
167
|
-
2. Second step
|
|
168
|
-
`;
|
|
169
|
-
const extracted = extractOutput(md, spec, strictnessDefaults(1));
|
|
170
|
-
assert(extracted.sectionsByName["Answer"].md.includes("42"), "Should extract answer");
|
|
171
|
-
assert(extracted.sectionsByName["Steps"].list?.ordered === true, "Should parse ordered list");
|
|
172
|
-
assert(extracted.sectionsByName["Steps"].list?.items.length === 2, "Should have 2 items");
|
|
173
|
-
});
|
|
174
|
-
// ============================================================================
|
|
175
|
-
// Outline Tests
|
|
176
|
-
// ============================================================================
|
|
177
|
-
console.log("\n๐ณ Outline & Tree Building\n");
|
|
178
|
-
test("Build outline from headings", () => {
|
|
179
|
-
const md = `# Title
|
|
180
|
-
Content
|
|
181
|
-
|
|
182
|
-
## Section 1
|
|
183
|
-
More content
|
|
184
|
-
|
|
185
|
-
### Subsection 1.1
|
|
186
|
-
Details
|
|
187
|
-
|
|
188
|
-
## Section 2
|
|
189
|
-
Other content
|
|
190
|
-
`;
|
|
191
|
-
const outline = buildOutline(md);
|
|
192
|
-
assert(outline.nodes.length === 1, "Should have 1 root node");
|
|
193
|
-
assert(outline.nodes[0].title === "Title", "Root title");
|
|
194
|
-
assert(outline.nodes[0].children.length === 2, "Should have 2 children");
|
|
195
|
-
assert(outline.nodes[0].children[0].children.length === 1, "First child has 1 child");
|
|
196
|
-
});
|
|
197
|
-
test("Render outline back to Markdown", () => {
|
|
198
|
-
const outline = {
|
|
199
|
-
type: "md_outline",
|
|
200
|
-
nodes: [{
|
|
201
|
-
title: "Title",
|
|
202
|
-
level: 1,
|
|
203
|
-
key: "title",
|
|
204
|
-
content_md: "Content\n",
|
|
205
|
-
children: [{
|
|
206
|
-
title: "Section",
|
|
207
|
-
level: 2,
|
|
208
|
-
key: "section",
|
|
209
|
-
content_md: "More\n",
|
|
210
|
-
children: []
|
|
211
|
-
}]
|
|
212
|
-
}]
|
|
213
|
-
};
|
|
214
|
-
const md = renderOutline(outline);
|
|
215
|
-
assert(md.includes("# Title"), "Should have h1");
|
|
216
|
-
assert(md.includes("## Section"), "Should have h2");
|
|
217
|
-
assert(!md.includes("key"), "Should not include internal keys");
|
|
218
|
-
});
|
|
219
|
-
// ============================================================================
|
|
220
|
-
// Parser Tests
|
|
221
|
-
// ============================================================================
|
|
222
|
-
console.log("\n๐ List & Table Parsers\n");
|
|
223
|
-
test("Parse unordered list", () => {
|
|
224
|
-
const md = `- Item 1
|
|
225
|
-
- Nested 1.1
|
|
226
|
-
- Item 2
|
|
227
|
-
`;
|
|
228
|
-
const list = parseList(md);
|
|
229
|
-
assert(list !== null, "Should parse list");
|
|
230
|
-
assert(list.ordered === false, "Should be unordered");
|
|
231
|
-
assert(list.items.length === 2, "Should have 2 items");
|
|
232
|
-
assert(list.items[0].children.length === 1, "First item has 1 child");
|
|
233
|
-
});
|
|
234
|
-
test("Parse ordered list", () => {
|
|
235
|
-
const md = `1. First
|
|
236
|
-
1. Sub-first
|
|
237
|
-
2. Second
|
|
238
|
-
`;
|
|
239
|
-
const list = parseList(md);
|
|
240
|
-
assert(list !== null, "Should parse list");
|
|
241
|
-
assert(list.ordered === true, "Should be ordered");
|
|
242
|
-
assert(list.items.length === 2, "Should have 2 items");
|
|
243
|
-
assert(list.items[0].index === 1, "First item index");
|
|
244
|
-
});
|
|
245
|
-
test("Parse pipe table", () => {
|
|
246
|
-
const md = `| Name | Age |
|
|
247
|
-
|------|-----|
|
|
248
|
-
| Alice | 30 |
|
|
249
|
-
| Bob | 25 |
|
|
250
|
-
`;
|
|
251
|
-
const table = parsePipeTable(md);
|
|
252
|
-
assert(table !== null, "Should parse table");
|
|
253
|
-
assert(table.columns.length === 2, "Should have 2 columns");
|
|
254
|
-
assert(table.rows.length === 2, "Should have 2 rows");
|
|
255
|
-
});
|
|
256
|
-
test("Parse ordered table", () => {
|
|
257
|
-
const md = `| # | Task | Status |
|
|
258
|
-
|---|------|--------|
|
|
259
|
-
| 1 | Do it | Done |
|
|
260
|
-
| 2 | Check | Pending |
|
|
261
|
-
`;
|
|
262
|
-
const table = parsePipeTable(md);
|
|
263
|
-
assert(table !== null, "Should parse table");
|
|
264
|
-
const issues = enforceOrderedTable(table);
|
|
265
|
-
assert(issues.length === 0, "Should have no ordered table issues");
|
|
266
|
-
assert(table.columns[0] === "#", "First column should be #");
|
|
267
|
-
});
|
|
268
|
-
test("Extract all tables", () => {
|
|
269
|
-
const md = `Some text
|
|
270
|
-
|
|
271
|
-
| A | B |
|
|
272
|
-
|---|---|
|
|
273
|
-
| 1 | 2 |
|
|
274
|
-
|
|
275
|
-
More text
|
|
276
|
-
|
|
277
|
-
| C | D |
|
|
278
|
-
|---|---|
|
|
279
|
-
| 3 | 4 |
|
|
280
|
-
`;
|
|
281
|
-
const tables = extractAllTables(md);
|
|
282
|
-
assert(tables.length === 2, "Should find 2 tables");
|
|
283
|
-
});
|
|
284
|
-
// ============================================================================
|
|
285
|
-
// Layer C Tests
|
|
286
|
-
// ============================================================================
|
|
287
|
-
console.log("\n๐ Layer C - Detection & Extraction\n");
|
|
288
|
-
test("Detect flexmd fence", () => {
|
|
289
|
-
const text = `Some text
|
|
290
|
-
\`\`\`flexmd
|
|
291
|
-
[[message]]
|
|
292
|
-
Hello
|
|
293
|
-
\`\`\`
|
|
294
|
-
More text
|
|
295
|
-
`;
|
|
296
|
-
const detected = detectObjects(text);
|
|
297
|
-
assert(detected.length > 0, "Should detect object");
|
|
298
|
-
assert(detected[0].kind === "flexmd_fence", "Should be flexmd fence");
|
|
299
|
-
assert(detected[0].confidence === 1.0, "Should have high confidence");
|
|
300
|
-
});
|
|
301
|
-
test("Detect JSON FlexDocument", () => {
|
|
302
|
-
const text = `\`\`\`json
|
|
303
|
-
{"frames": [{"type": "message", "body_md": "Hello"}]}
|
|
304
|
-
\`\`\`
|
|
305
|
-
`;
|
|
306
|
-
const detected = detectObjects(text);
|
|
307
|
-
assert(detected.length > 0, "Should detect object");
|
|
308
|
-
assert(detected[0].kind === "flexdoc_json_fence", "Should be JSON FlexDoc");
|
|
309
|
-
});
|
|
310
|
-
test("Parse any text", () => {
|
|
311
|
-
const text = `Random text
|
|
312
|
-
\`\`\`flexmd
|
|
313
|
-
[[message role=user]]
|
|
314
|
-
Hello
|
|
315
|
-
\`\`\`
|
|
316
|
-
More text
|
|
317
|
-
`;
|
|
318
|
-
const result = parseAny(text);
|
|
319
|
-
assert(result.flexDocs.length === 1, "Should extract 1 FlexDoc");
|
|
320
|
-
assert(result.flexDocs[0].frames.length === 1, "Should have 1 frame");
|
|
321
|
-
});
|
|
322
|
-
// ============================================================================
|
|
323
|
-
// Summary
|
|
324
|
-
// ============================================================================
|
|
325
|
-
console.log("\n" + "=".repeat(50));
|
|
326
|
-
console.log(`โ
Passed: ${passed}`);
|
|
327
|
-
console.log(`โ Failed: ${failed}`);
|
|
328
|
-
console.log("=" + "=".repeat(50));
|
|
329
|
-
if (failed > 0) {
|
|
330
|
-
throw new Error("Tests failed");
|
|
331
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/test-strictness.js
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { parseOutputFormatSpec, processResponseMarkdown, strictnessDefaults } from "../src/index.js";
|
|
2
|
-
const specText = `
|
|
3
|
-
## Output format (Markdown)
|
|
4
|
-
Include these sections somewhere (order does not matter):
|
|
5
|
-
|
|
6
|
-
- Short answer โ prose
|
|
7
|
-
- Long answer โ prose
|
|
8
|
-
- Reasoning โ ordered list
|
|
9
|
-
- Assumptions โ list
|
|
10
|
-
- Unknowns โ list
|
|
11
|
-
|
|
12
|
-
tables:
|
|
13
|
-
- (property, value โ table)
|
|
14
|
-
|
|
15
|
-
empty sections:
|
|
16
|
-
- If a section is empty, write \`None\`.
|
|
17
|
-
`;
|
|
18
|
-
const spec = parseOutputFormatSpec(specText);
|
|
19
|
-
// --- L0 Test ---
|
|
20
|
-
console.log("--- Level 0 Test ---");
|
|
21
|
-
const l0Response = `
|
|
22
|
-
I thought about it.
|
|
23
|
-
## Short answer
|
|
24
|
-
Yes, it works.
|
|
25
|
-
|
|
26
|
-
## Unknowns
|
|
27
|
-
- None so far.
|
|
28
|
-
`;
|
|
29
|
-
const l0Result = processResponseMarkdown(l0Response, spec, strictnessDefaults(0));
|
|
30
|
-
console.log("L0 OK:", l0Result.ok);
|
|
31
|
-
console.log("L0 Issues:", l0Result.issues);
|
|
32
|
-
// --- L1 Test ---
|
|
33
|
-
console.log("\n--- Level 1 Test ---");
|
|
34
|
-
const l1Response = `
|
|
35
|
-
## Short answer
|
|
36
|
-
Yes.
|
|
37
|
-
|
|
38
|
-
## Long answer
|
|
39
|
-
Full details.
|
|
40
|
-
|
|
41
|
-
## Reasoning
|
|
42
|
-
1. Step one.
|
|
43
|
-
2. Step two.
|
|
44
|
-
|
|
45
|
-
## Assumptions
|
|
46
|
-
- Assumption A.
|
|
47
|
-
|
|
48
|
-
## Unknowns
|
|
49
|
-
None
|
|
50
|
-
`;
|
|
51
|
-
const l1Result = processResponseMarkdown(l1Response, spec, strictnessDefaults(1));
|
|
52
|
-
console.log("L1 OK:", l1Result.ok);
|
|
53
|
-
console.log("L1 Issues:", l1Result.issues);
|
|
54
|
-
// --- L2 Test ---
|
|
55
|
-
console.log("\n--- Level 2 Test ---");
|
|
56
|
-
const l2Response = `
|
|
57
|
-
Some chatter before.
|
|
58
|
-
\`\`\`markdown
|
|
59
|
-
## Short answer
|
|
60
|
-
Yes.
|
|
61
|
-
## Long answer
|
|
62
|
-
None
|
|
63
|
-
## Reasoning
|
|
64
|
-
1. A
|
|
65
|
-
## Assumptions
|
|
66
|
-
None
|
|
67
|
-
## Unknowns
|
|
68
|
-
None
|
|
69
|
-
\`\`\`
|
|
70
|
-
Some chatter after.
|
|
71
|
-
`;
|
|
72
|
-
const l2Result = processResponseMarkdown(l2Response, spec, strictnessDefaults(2));
|
|
73
|
-
console.log("L2 OK (should be false if chatter exists):", l2Result.ok);
|
|
74
|
-
console.log("L2 Issues (should include 'content_outside_container'):", l2Result.issues);
|
|
75
|
-
const l2ResponseClean = `\`\`\`markdown
|
|
76
|
-
## Short answer
|
|
77
|
-
Yes.
|
|
78
|
-
## Long answer
|
|
79
|
-
None
|
|
80
|
-
## Reasoning
|
|
81
|
-
1. A
|
|
82
|
-
## Assumptions
|
|
83
|
-
None
|
|
84
|
-
## Unknowns
|
|
85
|
-
None
|
|
86
|
-
\`\`\``;
|
|
87
|
-
const l2ResultClean = processResponseMarkdown(l2ResponseClean, spec, strictnessDefaults(2));
|
|
88
|
-
console.log("L2 Clean OK:", l2ResultClean.ok);
|
|
89
|
-
// --- L3 Test ---
|
|
90
|
-
console.log("\n--- Level 3 Test ---");
|
|
91
|
-
const l3Response = `\`\`\`markdown
|
|
92
|
-
## Short answer
|
|
93
|
-
Data processed.
|
|
94
|
-
## Long answer
|
|
95
|
-
None
|
|
96
|
-
## Reasoning
|
|
97
|
-
1. Step
|
|
98
|
-
## Assumptions
|
|
99
|
-
None
|
|
100
|
-
## Unknowns
|
|
101
|
-
None
|
|
102
|
-
|
|
103
|
-
\`\`\`json
|
|
104
|
-
{ "status": "success", "val": 42 }
|
|
105
|
-
\`\`\`
|
|
106
|
-
\`\`\``;
|
|
107
|
-
// The regex in processor.ts is: /```json\s*\n([\s\S]*?)\n```/i
|
|
108
|
-
// Let's see why it failed. Ah, the innerMarkdown was extracted but maybe the nested blocks were messed up.
|
|
109
|
-
// Actually, l3Result returned issues: [ 'content_outside_container', 'json_payload_missing' ]
|
|
110
|
-
// content_outside_container is due to the trailing \`\`\` outside if the first one was matched greedily?
|
|
111
|
-
// No, regex is non-greedy in container.ts: /\`\`\`" + fenceLang + "\\s*\\n([\\s\\S]*?)\\n\`\`\`/i
|
|
112
|
-
// Wait, the container extractor in container.ts uses: /\`\`\`" + fenceLang + "\\s*\\n([\\s\\S]*?)\\n\`\`\`/i
|
|
113
|
-
// In my L3 test:
|
|
114
|
-
// \`\`\`markdown
|
|
115
|
-
// ...
|
|
116
|
-
// \`\`\`json
|
|
117
|
-
// ...
|
|
118
|
-
// \`\`\`
|
|
119
|
-
// \`\`\`
|
|
120
|
-
// The second to last \`\`\` matches the end of the markdown block. The last \`\`\` is "outside".
|
|
121
|
-
const l3ResponseFixed = `\`\`\`markdown
|
|
122
|
-
## Short answer
|
|
123
|
-
Data processed.
|
|
124
|
-
## Long answer
|
|
125
|
-
None
|
|
126
|
-
## Reasoning
|
|
127
|
-
1. Step
|
|
128
|
-
## Assumptions
|
|
129
|
-
None
|
|
130
|
-
## Unknowns
|
|
131
|
-
None
|
|
132
|
-
|
|
133
|
-
\`\`\`json
|
|
134
|
-
{ "status": "success", "val": 42 }
|
|
135
|
-
\`\`\`
|
|
136
|
-
\`\`\``;
|
|
137
|
-
// In this one there are two closing fences.
|
|
138
|
-
const l3ResponseSimple = `\`\`\`markdown
|
|
139
|
-
## Short answer
|
|
140
|
-
Data processed.
|
|
141
|
-
## Long answer
|
|
142
|
-
None
|
|
143
|
-
## Reasoning
|
|
144
|
-
1. Step
|
|
145
|
-
## Assumptions
|
|
146
|
-
None
|
|
147
|
-
## Unknowns
|
|
148
|
-
None
|
|
149
|
-
|
|
150
|
-
\`\`\`json
|
|
151
|
-
{ "status": "success", "val": 42 }
|
|
152
|
-
\`\`\`
|
|
153
|
-
\`\`\``;
|
|
154
|
-
// Let's try again with a simpler L3 mock
|
|
155
|
-
const l3ResponseSuccess = `\`\`\`markdown
|
|
156
|
-
## Short answer
|
|
157
|
-
Done.
|
|
158
|
-
## Long answer
|
|
159
|
-
None
|
|
160
|
-
## Reasoning
|
|
161
|
-
1. A
|
|
162
|
-
## Assumptions
|
|
163
|
-
None
|
|
164
|
-
## Unknowns
|
|
165
|
-
None
|
|
166
|
-
|
|
167
|
-
\`\`\`json
|
|
168
|
-
{"ok": true}
|
|
169
|
-
\`\`\`
|
|
170
|
-
\`\`\``;
|
|
171
|
-
const lResult3 = processResponseMarkdown(l3ResponseSuccess, spec, strictnessDefaults(3));
|
|
172
|
-
console.log("L3 Success OK:", lResult3.ok);
|
|
173
|
-
console.log("L3 Issues:", lResult3.issues);
|
|
174
|
-
// --- Semi-Strict Test ---
|
|
175
|
-
console.log("\n--- Semi-Strict Test ---");
|
|
176
|
-
const semiStrictSpecText = `
|
|
177
|
-
## Output format
|
|
178
|
-
- Answer โ prose (required)
|
|
179
|
-
- Extra โ list (optional)
|
|
180
|
-
`;
|
|
181
|
-
const semiStrictSpec = parseOutputFormatSpec(semiStrictSpecText);
|
|
182
|
-
const semiStrictResponse = `
|
|
183
|
-
## Answer
|
|
184
|
-
This is the answer.
|
|
185
|
-
`;
|
|
186
|
-
// Level 1 defaults to requireAllSections: true.
|
|
187
|
-
// But "Extra" is marked (optional).
|
|
188
|
-
const semiStrictResult = processResponseMarkdown(semiStrictResponse, semiStrictSpec, { level: 1 });
|
|
189
|
-
console.log("Semi-Strict Missing Optional OK:", semiStrictResult.ok); // Should be true
|
|
190
|
-
console.log("Semi-Strict Issues:", semiStrictResult.issues);
|
|
191
|
-
const semiStrictResponseError = `
|
|
192
|
-
## Extra
|
|
193
|
-
- Something
|
|
194
|
-
`;
|
|
195
|
-
const semiStrictResultError = processResponseMarkdown(semiStrictResponseError, semiStrictSpec, { level: 1 });
|
|
196
|
-
console.log("Semi-Strict Missing Required OK:", semiStrictResultError.ok); // Should be false
|
|
197
|
-
console.log("Semi-Strict Issues (should have missing_section:Answer):", semiStrictResultError.issues.map((i) => i.code));
|
|
198
|
-
// --- Mode B (Issues Envelope) Test ---
|
|
199
|
-
console.log("\n--- Mode B Test ---");
|
|
200
|
-
const modeBResponse = `
|
|
201
|
-
## Status
|
|
202
|
-
- status: error
|
|
203
|
-
- code: missing_input
|
|
204
|
-
- message: The user forgot the org_id.
|
|
205
|
-
|
|
206
|
-
## Issues
|
|
207
|
-
- issue: missing_org_id
|
|
208
|
-
hint: please provide org_id
|
|
209
|
-
`;
|
|
210
|
-
const modeBResult = processResponseMarkdown(modeBResponse, semiStrictSpec, { level: 1 });
|
|
211
|
-
console.log("Mode B Detected:", !!modeBResult.issuesEnvelope);
|
|
212
|
-
console.log("Mode B Status Code:", modeBResult.issuesEnvelope?.status.code);
|
|
213
|
-
console.log("Mode B OK (should be false for error status):", modeBResult.ok);
|
package/dist/util.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export declare function isBlank(s: unknown): boolean;
|
|
2
|
-
export declare function isEmptyObject(o: unknown): boolean;
|
|
3
|
-
export declare function splitTokensPreservingQuotes(input: string): string[];
|
|
4
|
-
export declare function unquote(s: string): string;
|
|
5
|
-
export declare function quoteIfNeeded(value: string): string;
|
package/dist/util.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
export function isBlank(s) {
|
|
2
|
-
return typeof s !== "string" || s.trim().length === 0;
|
|
3
|
-
}
|
|
4
|
-
export function isEmptyObject(o) {
|
|
5
|
-
if (!o || typeof o !== "object")
|
|
6
|
-
return true;
|
|
7
|
-
return Object.keys(o).length === 0;
|
|
8
|
-
}
|
|
9
|
-
export function splitTokensPreservingQuotes(input) {
|
|
10
|
-
// Splits by whitespace, but keeps "quoted strings" and 'quoted strings'
|
|
11
|
-
// Example: key="hello world" -> single token
|
|
12
|
-
const tokens = [];
|
|
13
|
-
let i = 0;
|
|
14
|
-
while (i < input.length) {
|
|
15
|
-
while (i < input.length && /\s/.test(input[i]))
|
|
16
|
-
i++;
|
|
17
|
-
if (i >= input.length)
|
|
18
|
-
break;
|
|
19
|
-
const ch = input[i];
|
|
20
|
-
if (ch === '"' || ch === "'") {
|
|
21
|
-
const quote = ch;
|
|
22
|
-
i++;
|
|
23
|
-
let buf = "";
|
|
24
|
-
while (i < input.length) {
|
|
25
|
-
const c = input[i];
|
|
26
|
-
if (c === "\\" && i + 1 < input.length) {
|
|
27
|
-
// minimal escape support
|
|
28
|
-
buf += input[i + 1];
|
|
29
|
-
i += 2;
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (c === quote) {
|
|
33
|
-
i++;
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
buf += c;
|
|
37
|
-
i++;
|
|
38
|
-
}
|
|
39
|
-
tokens.push(quote + buf + quote);
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
// unquoted token
|
|
43
|
-
let start = i;
|
|
44
|
-
while (i < input.length && !/\s/.test(input[i]))
|
|
45
|
-
i++;
|
|
46
|
-
tokens.push(input.slice(start, i));
|
|
47
|
-
}
|
|
48
|
-
return tokens;
|
|
49
|
-
}
|
|
50
|
-
export function unquote(s) {
|
|
51
|
-
const t = s.trim();
|
|
52
|
-
if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
|
|
53
|
-
return t.slice(1, -1);
|
|
54
|
-
}
|
|
55
|
-
return t;
|
|
56
|
-
}
|
|
57
|
-
export function quoteIfNeeded(value) {
|
|
58
|
-
// Only quote if whitespace or special chars likely to break header tokenization
|
|
59
|
-
if (/[\s\]]/.test(value) || value.includes('"') || value.includes("'")) {
|
|
60
|
-
// Use JSON-style quoting for safety
|
|
61
|
-
return JSON.stringify(value);
|
|
62
|
-
}
|
|
63
|
-
return value;
|
|
64
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Enforcement, SemiStrictPolicy } from "../strictness/types.js";
|
|
2
|
-
import { ValidationIssue } from "./types.js";
|
|
3
|
-
export declare function enforcementToSeverity(e: Enforcement): "error" | "warning";
|
|
4
|
-
/**
|
|
5
|
-
* Decide whether to emit an issue:
|
|
6
|
-
* - "ignore" => return null
|
|
7
|
-
* - "warn"/"error" => return ValidationIssue with chosen severity
|
|
8
|
-
*/
|
|
9
|
-
export declare function makeIssue(policy: SemiStrictPolicy | undefined, key: keyof SemiStrictPolicy, fallback: Enforcement, issue: Omit<ValidationIssue, "severity">): ValidationIssue | null;
|
|
10
|
-
export declare function hasErrors(issues: ValidationIssue[]): boolean;
|