flex-md 4.3.0 → 4.4.2

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 CHANGED
@@ -523,46 +523,37 @@ if (status === 'validated' || status === 'fixed') {
523
523
  }
524
524
  ```
525
525
 
526
- ## Integrated OFS Transformation (The "Best of Both Worlds")
526
+ Flex-MD allows you to use its native **Output Format Spec (OFS)** as the source of truth for **NX-MD-Parser's** structured extraction.
527
527
 
528
- Flex-MD allows you to use its native **Output Format Spec (OFS)** (the markdown-style description) as the source of truth for **NX-MD-Parser's** structured extraction. This combines the simplicity of describing output with markdown and the power of schema-driven JSON transformation.
528
+ ### Modern "Smart" Transformation
529
529
 
530
- ### Usage:
531
-
532
- ```typescript
533
- import { parseOutputFormatSpec, transformWithOfs } from 'flex-md';
530
+ The `transformWithOfs` function is now smarter. It performs **dual parsing**:
531
+ 1. **Automatic Parsing**: Even if you don't have a spec, it extracts all sections and camel-cases keys.
532
+ 2. **Contract Enforcement**: If you provide a spec, it uses `nx-md-parser` to validate, type-cast (lists/tables), and repair the output.
534
533
 
535
- // 1. Describe your output naturally
536
- const spec = parseOutputFormatSpec(`
537
- ## Output format
538
- - Executive Summary — text (required)
539
- - Key Findings — ordered list (required)
540
- - Technical Specs — table (optional)
541
- Columns: Component, Version, Notes
542
- `);
534
+ #### Usage with LLM Outputs:
543
535
 
544
- // 2. Transfrom Markdown output using the spec
545
- const md = `
546
- ### Summary
547
- The system is fully operational with 99.9% uptime.
536
+ When working with LLMs, pass the **entire response text** directly. Flex-MD handles internal normalization (like escaped `\n` characters) automatically.
548
537
 
549
- ### Findings
550
- 1. Scalability improved by 40%
551
- 2. Memory leak in cache module fixed
552
- `;
538
+ ```typescript
539
+ import { transformWithOfs } from 'flex-md';
553
540
 
554
- const { result, status } = transformWithOfs(md, spec);
541
+ // Pass the RAW content string from your LLM provider
542
+ const {
543
+ parsedOutput, // Always populated (auto-extraction)
544
+ contractOutput, // Populated if spec was provided
545
+ contractStatus, // "ok" | "different" | "skipped"
546
+ status // "validated" | "fixed" | "failed"
547
+ } = transformWithOfs(llmResponseText, spec);
555
548
 
556
- if (status === 'validated' || status === 'fixed') {
557
- console.log(result['Executive Summary']); // "The system is fully operational..."
558
- console.log(result['Key Findings']); // ["Scalability improved...", "Memory leak..."]
559
- }
549
+ console.log(parsedOutput.shortAnswer);
560
550
  ```
561
551
 
562
552
  ### Why use this?
563
- 1. **Fuzzy Matching**: Even if the LLM slightly changes the heading (e.g., "Summary" instead of "Executive Summary"), the adapter will correctly map it based on the spec.
564
- 2. **Type Enforcement**: Lists and Tables are automatically converted to JSON arrays and objects.
565
- 3. **Single Source of Truth**: Use the same spec to guide the LLM AND parse its response.
553
+ 1. **Zero-Config Extraction**: Get structured data without writing a schema first.
554
+ 2. **Dual-Safe**: Compare what the LLM *sent* (`parsedOutput`) with what the contract *required* (`contractOutput`).
555
+ 3. **Internal Normalization**: Handles messy data (escaped newlines, merged code blocks) so you don't have to.
556
+ 4. **Fuzzy Matching**: Even if the LLM slightly changes the heading (e.g., "Summary" vs "Executive Summary"), the contract will correctly map it.
566
557
 
567
558
  ## Advanced AI Features (via NX-MD-Parser 1.4.0)
568
559
 
@@ -622,8 +613,8 @@ const md = `
622
613
  Everything looks correctly formatted based on initial evidence.
623
614
  `;
624
615
 
625
- const { result } = transformWithOfs(md, recallId);
626
- console.log(result.Confidence); // 0.95
616
+ const { contractOutput } = transformWithOfs(md, recallId);
617
+ console.log(contractOutput.Confidence); // 0.95
627
618
  ```
628
619
 
629
620
  ### Why use this?
@@ -1,5 +1,15 @@
1
- import { type SchemaType, type TransformResult } from "nx-md-parser";
1
+ import { type SchemaType } from "nx-md-parser";
2
2
  import { type OutputFormatSpec } from "../types.js";
3
+ /**
4
+ * Result of a Flex-MD transformation.
5
+ */
6
+ export interface FlexTransformResult<T = any> {
7
+ parsedOutput: Record<string, any>;
8
+ contractOutput: T | null;
9
+ contractStatus: "ok" | "different" | "skipped";
10
+ status: "validated" | "fixed" | "failed";
11
+ errors: string[];
12
+ }
3
13
  /**
4
14
  * Converts a Flex-MD OutputFormatSpec to an nx-md-parser Schema.
5
15
  */
@@ -8,4 +18,4 @@ export declare function ofsToSchema(spec: OutputFormatSpec): SchemaType;
8
18
  * Transforms markdown text using a Flex-MD OutputFormatSpec or a recallId.
9
19
  * If no spec is provided, it attempts to infer it from the markdown (autospecs).
10
20
  */
11
- export declare function transformWithOfs<T = any>(md: string, specOrRecallId?: OutputFormatSpec | string): TransformResult<T>;
21
+ export declare function transformWithOfs<T = any>(md: string, specOrRecallId?: OutputFormatSpec | string): FlexTransformResult<T>;
@@ -1,7 +1,9 @@
1
1
  import { JSONTransformer, Schema } from "nx-md-parser";
2
2
  import { recall } from "./memory.js";
3
- import { parseHeadingsAndSections, extractBullets, parseMarkdownTable, normalizeName, markdownToJson } from "../md/parse.js";
3
+ import { parseHeadingsAndSections, extractBullets, parseMarkdownTable, normalizeName } from "../md/parse.js";
4
4
  import { normalizeMarkdownInput } from "../md/normalize.js";
5
+ import { toCamelCase } from "nx-helpers";
6
+ import { markdownToJson as autoMarkdownToJson } from "nx-json-parser";
5
7
  /**
6
8
  * Converts a Flex-MD OutputFormatSpec to an nx-md-parser Schema.
7
9
  */
@@ -53,13 +55,14 @@ export function ofsToSchema(spec) {
53
55
  export function transformWithOfs(md, specOrRecallId) {
54
56
  // 0. Normalize input (handle literal \n common in LLM outputs)
55
57
  const normalizedMd = normalizeMarkdownInput(md);
58
+ // 1. Automatic parsing (Dual-Response) using nx-json-parser
59
+ const parsedOutput = autoMarkdownToJson(normalizedMd);
56
60
  if (!specOrRecallId) {
57
- // AUTOSPECS: If no spec is provided, use internal logic ONLY.
58
- // This avoids complex schema generation and keeps it robust for unknown structures.
59
- const result = markdownToJson(normalizedMd);
60
61
  return {
62
+ parsedOutput,
63
+ contractOutput: null,
64
+ contractStatus: "skipped",
61
65
  status: "validated",
62
- result: result,
63
66
  errors: []
64
67
  };
65
68
  }
@@ -68,8 +71,10 @@ export function transformWithOfs(md, specOrRecallId) {
68
71
  const recalled = recall(specOrRecallId);
69
72
  if (!recalled) {
70
73
  return {
74
+ parsedOutput,
75
+ contractOutput: null,
76
+ contractStatus: "skipped",
71
77
  status: "failed",
72
- result: null,
73
78
  errors: [`Recall ID "${specOrRecallId}" not found in memory.`]
74
79
  };
75
80
  }
@@ -78,14 +83,13 @@ export function transformWithOfs(md, specOrRecallId) {
78
83
  else {
79
84
  spec = specOrRecallId;
80
85
  }
81
- // 1. Parse sections using Flex-MD parser
86
+ // 2. Parse sections using Flex-MD parser for the contract mapping
82
87
  const bulletNames = spec.sections.map(s => s.name);
88
+ // Note: We use the local headings parser to find the specific sections defined in the spec
83
89
  const parsedSections = parseHeadingsAndSections(normalizedMd, { bulletNames });
84
90
  const parsedObj = {};
85
- // 2. Map sections to OFS and apply complex parsing (tables/lists)
86
91
  for (const sectionSpec of spec.sections) {
87
92
  const normName = normalizeName(sectionSpec.name);
88
- // Find section with similar name
89
93
  const found = parsedSections.find(s => normalizeName(s.heading.name) === normName);
90
94
  if (found) {
91
95
  let value;
@@ -110,8 +114,21 @@ export function transformWithOfs(md, specOrRecallId) {
110
114
  parsedObj[sectionSpec.name] = value;
111
115
  }
112
116
  }
113
- // 3. Transform using nx-md-parser for schema validation and auto-fixing
117
+ // 3. Transform using nx-md-parser (latest v2.2.0) for schema validation and fixing
114
118
  const schema = ofsToSchema(spec);
115
119
  const transformer = new JSONTransformer(schema);
116
- return transformer.transform(parsedObj);
120
+ const transformResult = transformer.transform(parsedObj);
121
+ // 4. Compare parsed results with contract results
122
+ const autoKeys = Object.keys(parsedOutput).sort();
123
+ const contractKeys = transformResult.result ? Object.keys(transformResult.result).map(k => toCamelCase(k)).sort() : [];
124
+ const isSame = autoKeys.length > 0 &&
125
+ autoKeys.every(k => contractKeys.includes(k)) &&
126
+ contractKeys.length === autoKeys.length;
127
+ return {
128
+ parsedOutput,
129
+ contractOutput: transformResult.result,
130
+ contractStatus: isSame ? "ok" : "different",
131
+ status: transformResult.status,
132
+ errors: transformResult.errors || []
133
+ };
117
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flex-md",
3
- "version": "4.3.0",
3
+ "version": "4.4.2",
4
4
  "description": "Parse and stringify FlexMD: semi-structured Markdown with three powerful layers - Frames, Output Format Spec (OFS), and Detection/Extraction.",
5
5
  "license": "MIT",
6
6
  "author": "",
@@ -16,6 +16,9 @@
16
16
  "detection",
17
17
  "extraction"
18
18
  ],
19
+ "ts-node": {
20
+ "esm": true
21
+ },
19
22
  "type": "module",
20
23
  "main": "./dist/index.cjs",
21
24
  "module": "./dist/index.js",
@@ -44,12 +47,14 @@
44
47
  },
45
48
  "devDependencies": {
46
49
  "@types/node": "^25.0.3",
50
+ "tsx": "^4.21.0",
47
51
  "typescript": "^5.6.3",
48
52
  "vitest": "^4.0.16"
49
53
  },
50
54
  "dependencies": {
51
55
  "nd": "^1.2.0",
52
56
  "nx-helpers": "^1.5.0",
53
- "nx-md-parser": "^2.0.2"
57
+ "nx-json-parser": "^1.1.0",
58
+ "nx-md-parser": "^2.2.0"
54
59
  }
55
- }
60
+ }