flex-md 4.6.0 → 4.7.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 +124 -4
- package/dist/index.cjs +3 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/ofs/parser.d.ts +11 -1
- package/dist/ofs/parser.js +125 -0
- package/dist/tokens/estimator.d.ts +6 -0
- package/dist/tokens/estimator.js +15 -1
- package/dist/tokens/index.d.ts +21 -1
- package/dist/tokens/index.js +27 -1
- package/dist/tokens/runtime-estimator.d.ts +92 -0
- package/dist/tokens/runtime-estimator.js +74 -0
- package/dist/tokens/spec-estimator.d.ts +19 -1
- package/dist/tokens/spec-estimator.js +54 -0
- package/dist/types.d.ts +16 -0
- package/dist/validate/compliance.d.ts +1 -0
- package/dist/validate/compliance.js +9 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -170,14 +170,72 @@ Compliance levels measure how much detail you provide in system parts:
|
|
|
170
170
|
"Lines: ~50. Provide a complete working example."
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
+
## Input vs Output Format Specs
|
|
174
|
+
|
|
175
|
+
Flex-MD supports both **Input Format Specs** and **Output Format Specs**, but they serve different purposes:
|
|
176
|
+
|
|
177
|
+
### Input Format Specs (Planning & Design)
|
|
178
|
+
|
|
179
|
+
Input Format Specs (defined with `## Input format`) are **design-time tools** for planning and documentation:
|
|
180
|
+
|
|
181
|
+
- ✅ **Documentation**: Clearly specify what input structure your LLM expects
|
|
182
|
+
- ✅ **Planning**: Help design your prompt structure and data flow
|
|
183
|
+
- ✅ **Coding**: Guide developers on how to structure inputs
|
|
184
|
+
- ✅ **Validation**: Can be used to validate input data (optional)
|
|
185
|
+
|
|
186
|
+
**Input format specs are NOT used for runtime token estimation** — they're for human understanding and system design.
|
|
187
|
+
|
|
188
|
+
### Output Format Specs (Runtime)
|
|
189
|
+
|
|
190
|
+
Output Format Specs (defined with `## Output format`) are **runtime tools**:
|
|
191
|
+
|
|
192
|
+
- ✅ **Token Estimation**: Used to calculate `max_tokens` for API calls
|
|
193
|
+
- ✅ **Validation**: Enforce structure on LLM responses
|
|
194
|
+
- ✅ **Extraction**: Parse structured data from responses
|
|
195
|
+
- ✅ **Guidance**: Tell the LLM exactly what format to return
|
|
196
|
+
|
|
197
|
+
**Output format specs ARE used for runtime token estimation** — they directly impact API call parameters.
|
|
198
|
+
|
|
199
|
+
### Example: Using Both
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const instructions = `
|
|
203
|
+
## Input format
|
|
204
|
+
- User Query — text (required)
|
|
205
|
+
The question or request from the user.
|
|
206
|
+
- Context — list (optional)
|
|
207
|
+
Items: 1-5. Additional context items.
|
|
208
|
+
|
|
209
|
+
## Output format
|
|
210
|
+
- Answer — text (required)
|
|
211
|
+
Length: 2-3 paragraphs. Provide a comprehensive answer.
|
|
212
|
+
- Sources — list (required)
|
|
213
|
+
Items: 3-7. List information sources used.
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
// Parse both formats
|
|
217
|
+
import { parseFormatSpecs } from 'flex-md';
|
|
218
|
+
const { input, output } = parseFormatSpecs(instructions);
|
|
219
|
+
|
|
220
|
+
// Input format: Use for documentation/planning
|
|
221
|
+
console.log('Expected input structure:', input);
|
|
222
|
+
|
|
223
|
+
// Output format: Use for runtime token estimation
|
|
224
|
+
const maxTokens = getMaxTokens(output);
|
|
225
|
+
```
|
|
226
|
+
|
|
173
227
|
## Smart Toolbox
|
|
174
228
|
|
|
175
229
|
### Token Estimation
|
|
176
230
|
|
|
231
|
+
#### Planning-Time Estimation
|
|
232
|
+
|
|
233
|
+
For estimating tokens from format specs directly (useful during development):
|
|
234
|
+
|
|
177
235
|
```typescript
|
|
178
236
|
import { getMaxTokens, estimateSpecTokens } from 'flex-md';
|
|
179
237
|
|
|
180
|
-
// Quick estimate
|
|
238
|
+
// Quick estimate from output format spec
|
|
181
239
|
const maxTokens = getMaxTokens(spec);
|
|
182
240
|
|
|
183
241
|
// Detailed estimate with options
|
|
@@ -195,6 +253,64 @@ console.log(estimate);
|
|
|
195
253
|
// }
|
|
196
254
|
```
|
|
197
255
|
|
|
256
|
+
#### Runtime Estimation
|
|
257
|
+
|
|
258
|
+
For estimating tokens at runtime with actual prompt, context, and instructions:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { runtimeEstimateTokens } from 'flex-md';
|
|
262
|
+
|
|
263
|
+
const estimate = runtimeEstimateTokens({
|
|
264
|
+
prompt: 'Analyze this code and explain what it does.',
|
|
265
|
+
context: 'Previous conversation about TypeScript...',
|
|
266
|
+
instructions: `
|
|
267
|
+
You are a helpful coding assistant.
|
|
268
|
+
|
|
269
|
+
## Output format
|
|
270
|
+
- Analysis — text (required)
|
|
271
|
+
Length: 2-3 paragraphs. Explain the code functionality.
|
|
272
|
+
- Code Quality — list (required)
|
|
273
|
+
Items: 3-5. List quality observations.
|
|
274
|
+
`,
|
|
275
|
+
options: {
|
|
276
|
+
safetyMultiplier: 1.2,
|
|
277
|
+
strategy: 'average',
|
|
278
|
+
additionalOverhead: 50 // For system messages, formatting, etc.
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
console.log(estimate.maxTokens); // Recommended max_tokens for API call (output tokens only)
|
|
283
|
+
console.log(estimate.breakdown);
|
|
284
|
+
// {
|
|
285
|
+
// prompt: 12, // Input tokens (for budgeting)
|
|
286
|
+
// context: 8, // Input tokens (for budgeting)
|
|
287
|
+
// instructions: 45, // Input tokens (for budgeting)
|
|
288
|
+
// output: { total: { estimated: 450, ... }, ... }, // Output token estimate
|
|
289
|
+
// additionalOverhead: 50,
|
|
290
|
+
// total: 565 // Total tokens (input + output) for budgeting
|
|
291
|
+
// }
|
|
292
|
+
|
|
293
|
+
// Use in API call
|
|
294
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
295
|
+
method: 'POST',
|
|
296
|
+
body: JSON.stringify({
|
|
297
|
+
model: 'claude-sonnet-4-20250514',
|
|
298
|
+
max_tokens: estimate.maxTokens, // Output tokens only (extracted from output format spec)
|
|
299
|
+
messages: [{
|
|
300
|
+
role: 'user',
|
|
301
|
+
content: prompt + '\n\n' + instructions
|
|
302
|
+
}]
|
|
303
|
+
})
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The `runtimeEstimateTokens` function:
|
|
308
|
+
- **Extracts output format specs from instructions automatically** (input format specs are ignored)
|
|
309
|
+
- Estimates tokens for prompt, context, and instructions text (for budgeting/planning)
|
|
310
|
+
- **Estimates output tokens based on the output format spec** (this becomes `max_tokens`)
|
|
311
|
+
- Provides a breakdown showing input tokens (for budgeting) and output tokens (for API parameter)
|
|
312
|
+
- Returns `maxTokens` which is the value to use for the `max_tokens` API parameter (output only)
|
|
313
|
+
|
|
198
314
|
### Compliance Checking
|
|
199
315
|
|
|
200
316
|
```typescript
|
|
@@ -410,14 +526,18 @@ const v3 = `
|
|
|
410
526
|
## API Reference
|
|
411
527
|
|
|
412
528
|
### Core Functions
|
|
413
|
-
- `parseOutputFormatSpec(markdown)` - Parse spec from markdown
|
|
529
|
+
- `parseOutputFormatSpec(markdown)` - Parse output format spec from markdown
|
|
530
|
+
- `parseInputFormatSpec(markdown)` - Parse input format spec from markdown
|
|
531
|
+
- `parseFormatSpecs(instructions)` - Extract both input and output format specs from instructions
|
|
414
532
|
- `stringifyOutputFormatSpec(spec)` - Convert spec to markdown
|
|
415
533
|
- `buildMarkdownGuidance(spec, options)` - Generate LLM instructions
|
|
416
534
|
- `enforceFlexMd(text, spec, options)` - Validate and repair LLM output
|
|
417
535
|
|
|
418
536
|
### Token Estimation (v4.0)
|
|
419
|
-
- `getMaxTokens(spec, options?)` - Get estimated max_tokens
|
|
420
|
-
- `
|
|
537
|
+
- `getMaxTokens(spec, options?)` - Get estimated max_tokens from spec
|
|
538
|
+
- `getMaxTokensFromInstructions(instructions, options?)` - Extract format specs and estimate tokens
|
|
539
|
+
- `estimateSpecTokens(spec, options?)` - Detailed token estimate from spec
|
|
540
|
+
- `runtimeEstimateTokens(params)` - Runtime estimation with prompt, context, and instructions
|
|
421
541
|
- `parseSystemPart(instruction, kind)` - Parse system part from instruction
|
|
422
542
|
- `estimateTokens(systemPart)` - Estimate tokens for system part
|
|
423
543
|
|
package/dist/index.cjs
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.jsonToMarkdown = exports.Schema = exports.MarkdownParser = exports.JSONTransformer = exports.enforceFlexMd = exports.repairToMarkdownLevel = exports.detectResponseKind = exports.buildIssuesEnvelopeAuto = exports.buildIssuesEnvelope = exports.parseIssuesEnvelope = exports.processResponseMarkdown = exports.extractFromMarkdown = exports.checkConnection = exports.hasFlexMdContract = exports.checkCompliance = exports.validateMarkdownAgainstOfs = exports.enrichInstructionsWithFlexMd = exports.enrichInstructions = exports.buildMarkdownGuidance = exports.stringifyOutputFormatSpec = exports.recall = exports.remember = exports.transformWithOfs = exports.ofsToSchema = exports.validateFormat = exports.parseOutputFormatSpec = exports.buildOutline = exports.logger = void 0;
|
|
17
|
+
exports.jsonToMarkdown = exports.Schema = exports.MarkdownParser = exports.JSONTransformer = exports.enforceFlexMd = exports.repairToMarkdownLevel = exports.detectResponseKind = exports.buildIssuesEnvelopeAuto = exports.buildIssuesEnvelope = exports.parseIssuesEnvelope = exports.processResponseMarkdown = exports.extractFromMarkdown = exports.checkConnection = exports.hasFlexMdContract = exports.checkCompliance = exports.validateMarkdownAgainstOfs = exports.enrichInstructionsWithFlexMd = exports.enrichInstructions = exports.buildMarkdownGuidance = exports.stringifyOutputFormatSpec = exports.recall = exports.remember = exports.transformWithOfs = exports.ofsToSchema = exports.validateFormat = exports.parseFormatSpecs = exports.parseInputFormatSpec = exports.parseOutputFormatSpec = exports.buildOutline = exports.logger = void 0;
|
|
18
18
|
// Core SFMD Types
|
|
19
19
|
__exportStar(require("./types.js"), exports);
|
|
20
20
|
__exportStar(require("./strictness/types.js"), exports);
|
|
@@ -28,6 +28,8 @@ Object.defineProperty(exports, "buildOutline", { enumerable: true, get: function
|
|
|
28
28
|
// Output Format Spec (OFS)
|
|
29
29
|
var parser_js_1 = require("./ofs/parser.js");
|
|
30
30
|
Object.defineProperty(exports, "parseOutputFormatSpec", { enumerable: true, get: function () { return parser_js_1.parseOutputFormatSpec; } });
|
|
31
|
+
Object.defineProperty(exports, "parseInputFormatSpec", { enumerable: true, get: function () { return parser_js_1.parseInputFormatSpec; } });
|
|
32
|
+
Object.defineProperty(exports, "parseFormatSpecs", { enumerable: true, get: function () { return parser_js_1.parseFormatSpecs; } });
|
|
31
33
|
Object.defineProperty(exports, "validateFormat", { enumerable: true, get: function () { return parser_js_1.validateFormat; } });
|
|
32
34
|
var adapter_js_1 = require("./ofs/adapter.js");
|
|
33
35
|
Object.defineProperty(exports, "ofsToSchema", { enumerable: true, get: function () { return adapter_js_1.ofsToSchema; } });
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export * from "./strictness/types.js";
|
|
|
3
3
|
export { logger } from "./logger.js";
|
|
4
4
|
export * from "./md/parse.js";
|
|
5
5
|
export { buildOutline } from "./md/outline.js";
|
|
6
|
-
export { parseOutputFormatSpec, validateFormat } from "./ofs/parser.js";
|
|
6
|
+
export { parseOutputFormatSpec, parseInputFormatSpec, parseFormatSpecs, validateFormat } from "./ofs/parser.js";
|
|
7
7
|
export { ofsToSchema, transformWithOfs } from "./ofs/adapter.js";
|
|
8
8
|
export { remember, recall } from "./ofs/memory.js";
|
|
9
9
|
export { stringifyOutputFormatSpec } from "./ofs/stringify.js";
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.jsonToMarkdown = exports.Schema = exports.MarkdownParser = exports.JSONTransformer = exports.enforceFlexMd = exports.repairToMarkdownLevel = exports.detectResponseKind = exports.buildIssuesEnvelopeAuto = exports.buildIssuesEnvelope = exports.parseIssuesEnvelope = exports.processResponseMarkdown = exports.extractFromMarkdown = exports.checkConnection = exports.hasFlexMdContract = exports.checkCompliance = exports.validateMarkdownAgainstOfs = exports.enrichInstructionsWithFlexMd = exports.enrichInstructions = exports.buildMarkdownGuidance = exports.stringifyOutputFormatSpec = exports.recall = exports.remember = exports.transformWithOfs = exports.ofsToSchema = exports.validateFormat = exports.parseOutputFormatSpec = exports.buildOutline = exports.logger = void 0;
|
|
17
|
+
exports.jsonToMarkdown = exports.Schema = exports.MarkdownParser = exports.JSONTransformer = exports.enforceFlexMd = exports.repairToMarkdownLevel = exports.detectResponseKind = exports.buildIssuesEnvelopeAuto = exports.buildIssuesEnvelope = exports.parseIssuesEnvelope = exports.processResponseMarkdown = exports.extractFromMarkdown = exports.checkConnection = exports.hasFlexMdContract = exports.checkCompliance = exports.validateMarkdownAgainstOfs = exports.enrichInstructionsWithFlexMd = exports.enrichInstructions = exports.buildMarkdownGuidance = exports.stringifyOutputFormatSpec = exports.recall = exports.remember = exports.transformWithOfs = exports.ofsToSchema = exports.validateFormat = exports.parseFormatSpecs = exports.parseInputFormatSpec = exports.parseOutputFormatSpec = exports.buildOutline = exports.logger = void 0;
|
|
18
18
|
// Core SFMD Types
|
|
19
19
|
__exportStar(require("./types.js"), exports);
|
|
20
20
|
__exportStar(require("./strictness/types.js"), exports);
|
|
@@ -28,6 +28,8 @@ Object.defineProperty(exports, "buildOutline", { enumerable: true, get: function
|
|
|
28
28
|
// Output Format Spec (OFS)
|
|
29
29
|
var parser_js_1 = require("./ofs/parser.js");
|
|
30
30
|
Object.defineProperty(exports, "parseOutputFormatSpec", { enumerable: true, get: function () { return parser_js_1.parseOutputFormatSpec; } });
|
|
31
|
+
Object.defineProperty(exports, "parseInputFormatSpec", { enumerable: true, get: function () { return parser_js_1.parseInputFormatSpec; } });
|
|
32
|
+
Object.defineProperty(exports, "parseFormatSpecs", { enumerable: true, get: function () { return parser_js_1.parseFormatSpecs; } });
|
|
31
33
|
Object.defineProperty(exports, "validateFormat", { enumerable: true, get: function () { return parser_js_1.validateFormat; } });
|
|
32
34
|
var adapter_js_1 = require("./ofs/adapter.js");
|
|
33
35
|
Object.defineProperty(exports, "ofsToSchema", { enumerable: true, get: function () { return adapter_js_1.ofsToSchema; } });
|
package/dist/ofs/parser.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OutputFormatSpec, FormatValidationResult } from "../types.js";
|
|
1
|
+
import type { OutputFormatSpec, InputFormatSpec, FormatSpecs, FormatValidationResult } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Validate a format specification.
|
|
4
4
|
* Returns detailed validation results.
|
|
@@ -12,3 +12,13 @@ export interface ParseOfsOptions {
|
|
|
12
12
|
allowDelimiterFallbacks?: boolean;
|
|
13
13
|
}
|
|
14
14
|
export declare function parseOutputFormatSpec(md: string, opts?: ParseOfsOptions): OutputFormatSpec | null;
|
|
15
|
+
/**
|
|
16
|
+
* Parse an Input Format Spec block from Markdown.
|
|
17
|
+
* Uses the same parsing logic as Output Format Spec.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseInputFormatSpec(md: string, opts?: ParseOfsOptions): InputFormatSpec | null;
|
|
20
|
+
/**
|
|
21
|
+
* Extract both Input and Output Format Specs from instructions text.
|
|
22
|
+
* This function searches for both "## Input format" and "## Output format" sections.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseFormatSpecs(instructions: string, opts?: ParseOfsOptions): FormatSpecs;
|
package/dist/ofs/parser.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validateFormat = validateFormat;
|
|
4
4
|
exports.parseOutputFormatSpec = parseOutputFormatSpec;
|
|
5
|
+
exports.parseInputFormatSpec = parseInputFormatSpec;
|
|
6
|
+
exports.parseFormatSpecs = parseFormatSpecs;
|
|
5
7
|
const parse_js_1 = require("../md/parse.js");
|
|
6
8
|
/**
|
|
7
9
|
* Validate a format specification.
|
|
@@ -246,3 +248,126 @@ function parseRequiredOptional(s) {
|
|
|
246
248
|
return false;
|
|
247
249
|
return undefined;
|
|
248
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Parse an Input Format Spec block from Markdown.
|
|
253
|
+
* Uses the same parsing logic as Output Format Spec.
|
|
254
|
+
*/
|
|
255
|
+
function parseInputFormatSpec(md, opts = {}) {
|
|
256
|
+
const headingRx = opts.headingRegex ?? /^##\s*Input format\b/i;
|
|
257
|
+
const lines = md.split("\n");
|
|
258
|
+
// find Input Format Spec start
|
|
259
|
+
let start = -1;
|
|
260
|
+
for (let i = 0; i < lines.length; i++) {
|
|
261
|
+
const line = lines[i] ?? "";
|
|
262
|
+
if (headingRx.test(line)) {
|
|
263
|
+
start = i;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (start === -1)
|
|
268
|
+
return null;
|
|
269
|
+
// capture block until next H2 (##) or end
|
|
270
|
+
let end = lines.length;
|
|
271
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
272
|
+
const line = lines[i] ?? "";
|
|
273
|
+
if (/^##\s+/.test(line)) {
|
|
274
|
+
end = i;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const block = lines.slice(start, end).join("\n");
|
|
279
|
+
const sections = [];
|
|
280
|
+
const tables = [];
|
|
281
|
+
let emptySectionValue;
|
|
282
|
+
let inTables = false;
|
|
283
|
+
let currentSection = null;
|
|
284
|
+
for (const rawLine of block.split("\n")) {
|
|
285
|
+
const line = rawLine.trim();
|
|
286
|
+
if (/^tables\b/i.test(line)) {
|
|
287
|
+
inTables = true;
|
|
288
|
+
currentSection = null;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (/^empty sections\b/i.test(line)) {
|
|
292
|
+
inTables = false;
|
|
293
|
+
currentSection = null;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
// Empty section rule
|
|
297
|
+
const mNone = line.match(/write\s+`([^`]+)`/i);
|
|
298
|
+
if (/empty/i.test(line) && mNone) {
|
|
299
|
+
emptySectionValue = mNone[1];
|
|
300
|
+
currentSection = null;
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
// bullet items
|
|
304
|
+
const bullet = line.match(/^- (.+)$/);
|
|
305
|
+
if (bullet) {
|
|
306
|
+
const item = bullet[1];
|
|
307
|
+
if (inTables) {
|
|
308
|
+
const t = parseTableDecl(item);
|
|
309
|
+
if (t)
|
|
310
|
+
tables.push(t);
|
|
311
|
+
currentSection = null;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
const s = parseSectionDecl(item, !!opts.allowDelimiterFallbacks);
|
|
315
|
+
if (s) {
|
|
316
|
+
sections.push(s);
|
|
317
|
+
currentSection = s;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
currentSection = null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
// heading items (e.g. ### Short Answer)
|
|
326
|
+
const headingMatch = line.match(/^#{1,6}\s+(.+)$/);
|
|
327
|
+
if (headingMatch) {
|
|
328
|
+
const name = headingMatch[1].trim();
|
|
329
|
+
// Don't re-parse "Input format" itself if it somehow gets in here
|
|
330
|
+
if ((0, parse_js_1.normalizeName)(name) !== "input format") {
|
|
331
|
+
const s = { name, kind: "text" };
|
|
332
|
+
sections.push(s);
|
|
333
|
+
currentSection = s;
|
|
334
|
+
}
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
// If not a bullet and we have a current section, it's an instruction
|
|
338
|
+
if (currentSection && line.length > 0) {
|
|
339
|
+
// Support "Columns: A, B, C" in instructions for tables
|
|
340
|
+
const colMatch = line.match(/^Columns:\s*(.+)$/i);
|
|
341
|
+
if (colMatch && (currentSection.kind === "table" || currentSection.kind === "ordered_table")) {
|
|
342
|
+
currentSection.columns = colMatch[1].split(",").map(c => c.trim()).filter(Boolean);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
const existing = currentSection.instruction || "";
|
|
346
|
+
currentSection.instruction = existing ? `${existing} ${line}` : line;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (!sections.length)
|
|
351
|
+
return null;
|
|
352
|
+
return {
|
|
353
|
+
descriptorType: "input_format_spec",
|
|
354
|
+
format: "markdown",
|
|
355
|
+
sectionOrderMatters: false,
|
|
356
|
+
sections,
|
|
357
|
+
tablesOptional: true,
|
|
358
|
+
tables,
|
|
359
|
+
emptySectionValue: emptySectionValue ?? "None"
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Extract both Input and Output Format Specs from instructions text.
|
|
364
|
+
* This function searches for both "## Input format" and "## Output format" sections.
|
|
365
|
+
*/
|
|
366
|
+
function parseFormatSpecs(instructions, opts = {}) {
|
|
367
|
+
const input = parseInputFormatSpec(instructions, opts);
|
|
368
|
+
const output = parseOutputFormatSpec(instructions, opts);
|
|
369
|
+
return {
|
|
370
|
+
input: input || undefined,
|
|
371
|
+
output: output || undefined
|
|
372
|
+
};
|
|
373
|
+
}
|
|
@@ -7,6 +7,12 @@ export declare const TOKEN_CONSTANTS: {
|
|
|
7
7
|
readonly perCodeLine: 5;
|
|
8
8
|
readonly headingOverhead: 10;
|
|
9
9
|
readonly baseOverhead: 50;
|
|
10
|
+
readonly charsPerToken: 4;
|
|
10
11
|
};
|
|
11
12
|
export declare function estimateTokens(systemPart: SystemPart): TokenEstimate;
|
|
12
13
|
export declare function getFallbackEstimate(kind: SectionKind, required: boolean): TokenEstimate;
|
|
14
|
+
/**
|
|
15
|
+
* Estimate tokens from plain text using character-based approximation.
|
|
16
|
+
* Uses a rule of thumb: ~4 characters per token (varies by model, but good approximation).
|
|
17
|
+
*/
|
|
18
|
+
export declare function estimateTextTokens(text: string): number;
|
package/dist/tokens/estimator.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.TOKEN_CONSTANTS = void 0;
|
|
4
4
|
exports.estimateTokens = estimateTokens;
|
|
5
5
|
exports.getFallbackEstimate = getFallbackEstimate;
|
|
6
|
+
exports.estimateTextTokens = estimateTextTokens;
|
|
6
7
|
// Calibrated token constants
|
|
7
8
|
exports.TOKEN_CONSTANTS = {
|
|
8
9
|
// Text lengths (enumerated)
|
|
@@ -22,7 +23,9 @@ exports.TOKEN_CONSTANTS = {
|
|
|
22
23
|
perCodeLine: 5,
|
|
23
24
|
// Overhead
|
|
24
25
|
headingOverhead: 10,
|
|
25
|
-
baseOverhead: 50
|
|
26
|
+
baseOverhead: 50,
|
|
27
|
+
// Text token estimation (rough approximation: 1 token ≈ 4 characters)
|
|
28
|
+
charsPerToken: 4
|
|
26
29
|
};
|
|
27
30
|
function estimateTokens(systemPart) {
|
|
28
31
|
const { parsed } = systemPart;
|
|
@@ -141,3 +144,14 @@ function getFallbackEstimate(kind, required) {
|
|
|
141
144
|
confidence: 'low'
|
|
142
145
|
};
|
|
143
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Estimate tokens from plain text using character-based approximation.
|
|
149
|
+
* Uses a rule of thumb: ~4 characters per token (varies by model, but good approximation).
|
|
150
|
+
*/
|
|
151
|
+
function estimateTextTokens(text) {
|
|
152
|
+
if (!text || text.length === 0)
|
|
153
|
+
return 0;
|
|
154
|
+
// Rough approximation: 1 token ≈ 4 characters for English text
|
|
155
|
+
// This is a common rule of thumb and works reasonably well for most cases
|
|
156
|
+
return Math.ceil(text.length / exports.TOKEN_CONSTANTS.charsPerToken);
|
|
157
|
+
}
|
package/dist/tokens/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './types.js';
|
|
|
3
3
|
export * from './parser.js';
|
|
4
4
|
export * from './estimator.js';
|
|
5
5
|
export * from './spec-estimator.js';
|
|
6
|
+
export * from './runtime-estimator.js';
|
|
6
7
|
export * from './validator.js';
|
|
7
8
|
export * from './compliance.js';
|
|
8
9
|
export * from './cognitive-cost.js';
|
|
@@ -13,7 +14,7 @@ export * from './auto-fix.js';
|
|
|
13
14
|
/**
|
|
14
15
|
* Convenience function: estimate max_tokens from spec
|
|
15
16
|
*
|
|
16
|
-
* @param spec - OutputFormatSpec object or
|
|
17
|
+
* @param spec - OutputFormatSpec object, markdown string, or full instructions text
|
|
17
18
|
* @param options - Estimation options
|
|
18
19
|
* @returns Estimated max_tokens value
|
|
19
20
|
*/
|
|
@@ -21,4 +22,23 @@ export declare function getMaxTokens(spec: OutputFormatSpec | string | null | un
|
|
|
21
22
|
includeOptional?: boolean;
|
|
22
23
|
safetyMultiplier?: number;
|
|
23
24
|
strategy?: 'conservative' | 'average' | 'generous';
|
|
25
|
+
/**
|
|
26
|
+
* If true, treats the input as full instructions and extracts both
|
|
27
|
+
* input and output format specs. If false, only looks for output format.
|
|
28
|
+
* @default false (backwards compatible)
|
|
29
|
+
*/
|
|
30
|
+
extractFromInstructions?: boolean;
|
|
31
|
+
}): number;
|
|
32
|
+
/**
|
|
33
|
+
* Estimate max tokens from entire instructions text.
|
|
34
|
+
* Automatically extracts both input and output format specs if present.
|
|
35
|
+
*
|
|
36
|
+
* @param instructions - Full instructions text that may contain format specs
|
|
37
|
+
* @param options - Estimation options
|
|
38
|
+
* @returns Estimated max_tokens value based on both input and output formats
|
|
39
|
+
*/
|
|
40
|
+
export declare function getMaxTokensFromInstructions(instructions: string, options?: {
|
|
41
|
+
includeOptional?: boolean;
|
|
42
|
+
safetyMultiplier?: number;
|
|
43
|
+
strategy?: 'conservative' | 'average' | 'generous';
|
|
24
44
|
}): number;
|
package/dist/tokens/index.js
CHANGED
|
@@ -15,12 +15,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.getMaxTokens = getMaxTokens;
|
|
18
|
+
exports.getMaxTokensFromInstructions = getMaxTokensFromInstructions;
|
|
18
19
|
const parser_js_1 = require("../ofs/parser.js");
|
|
19
20
|
const spec_estimator_js_1 = require("./spec-estimator.js");
|
|
20
21
|
__exportStar(require("./types.js"), exports);
|
|
21
22
|
__exportStar(require("./parser.js"), exports);
|
|
22
23
|
__exportStar(require("./estimator.js"), exports);
|
|
23
24
|
__exportStar(require("./spec-estimator.js"), exports);
|
|
25
|
+
__exportStar(require("./runtime-estimator.js"), exports);
|
|
24
26
|
__exportStar(require("./validator.js"), exports);
|
|
25
27
|
__exportStar(require("./compliance.js"), exports);
|
|
26
28
|
__exportStar(require("./cognitive-cost.js"), exports);
|
|
@@ -31,13 +33,24 @@ __exportStar(require("./auto-fix.js"), exports);
|
|
|
31
33
|
/**
|
|
32
34
|
* Convenience function: estimate max_tokens from spec
|
|
33
35
|
*
|
|
34
|
-
* @param spec - OutputFormatSpec object or
|
|
36
|
+
* @param spec - OutputFormatSpec object, markdown string, or full instructions text
|
|
35
37
|
* @param options - Estimation options
|
|
36
38
|
* @returns Estimated max_tokens value
|
|
37
39
|
*/
|
|
38
40
|
function getMaxTokens(spec, options) {
|
|
39
41
|
if (!spec)
|
|
40
42
|
return 0;
|
|
43
|
+
const extractFromInstructions = options?.extractFromInstructions ?? false;
|
|
44
|
+
// If extractFromInstructions is true and spec is a string, try to extract both formats
|
|
45
|
+
if (extractFromInstructions && typeof spec === 'string') {
|
|
46
|
+
const formatSpecs = (0, parser_js_1.parseFormatSpecs)(spec);
|
|
47
|
+
if (formatSpecs.input || formatSpecs.output) {
|
|
48
|
+
const estimate = (0, spec_estimator_js_1.estimateFormatSpecsTokens)(formatSpecs.input, formatSpecs.output, options);
|
|
49
|
+
return estimate.total.estimated;
|
|
50
|
+
}
|
|
51
|
+
// Fall through to output-only parsing if no formats found
|
|
52
|
+
}
|
|
53
|
+
// Original behavior: parse as output format spec
|
|
41
54
|
const parsedSpec = typeof spec === 'string'
|
|
42
55
|
? (0, parser_js_1.parseOutputFormatSpec)(spec)
|
|
43
56
|
: spec;
|
|
@@ -46,3 +59,16 @@ function getMaxTokens(spec, options) {
|
|
|
46
59
|
const estimate = (0, spec_estimator_js_1.estimateSpecTokens)(parsedSpec, options);
|
|
47
60
|
return estimate.total.estimated;
|
|
48
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Estimate max tokens from entire instructions text.
|
|
64
|
+
* Automatically extracts both input and output format specs if present.
|
|
65
|
+
*
|
|
66
|
+
* @param instructions - Full instructions text that may contain format specs
|
|
67
|
+
* @param options - Estimation options
|
|
68
|
+
* @returns Estimated max_tokens value based on both input and output formats
|
|
69
|
+
*/
|
|
70
|
+
function getMaxTokensFromInstructions(instructions, options) {
|
|
71
|
+
const formatSpecs = (0, parser_js_1.parseFormatSpecs)(instructions);
|
|
72
|
+
const estimate = (0, spec_estimator_js_1.estimateFormatSpecsTokens)(formatSpecs.input, formatSpecs.output, options);
|
|
73
|
+
return estimate.total.estimated;
|
|
74
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { SpecTokenEstimate } from './types.js';
|
|
2
|
+
export interface RuntimeEstimateOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Whether to include optional sections in output format estimation
|
|
5
|
+
* @default true
|
|
6
|
+
*/
|
|
7
|
+
includeOptional?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Safety multiplier to add headroom (e.g., 1.2 = 20% extra)
|
|
10
|
+
* @default 1.2
|
|
11
|
+
*/
|
|
12
|
+
safetyMultiplier?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Estimation strategy
|
|
15
|
+
* @default 'average'
|
|
16
|
+
*/
|
|
17
|
+
strategy?: 'conservative' | 'average' | 'generous';
|
|
18
|
+
/**
|
|
19
|
+
* Whether to estimate tokens for the prompt text
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
estimatePrompt?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Whether to estimate tokens for the context text
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
estimateContext?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Whether to estimate tokens for the instructions text
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
estimateInstructions?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Additional overhead tokens to add (for system messages, formatting, etc.)
|
|
35
|
+
* @default 0
|
|
36
|
+
*/
|
|
37
|
+
additionalOverhead?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface RuntimeTokenEstimate {
|
|
40
|
+
/**
|
|
41
|
+
* Estimated max_tokens needed for the LLM response
|
|
42
|
+
*/
|
|
43
|
+
maxTokens: number;
|
|
44
|
+
/**
|
|
45
|
+
* Breakdown of token estimates
|
|
46
|
+
*/
|
|
47
|
+
breakdown: {
|
|
48
|
+
prompt: number;
|
|
49
|
+
context: number;
|
|
50
|
+
instructions: number;
|
|
51
|
+
output: SpecTokenEstimate;
|
|
52
|
+
additionalOverhead: number;
|
|
53
|
+
total: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Confidence level of the estimate
|
|
57
|
+
*/
|
|
58
|
+
confidence: 'high' | 'medium' | 'low';
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Estimate max_tokens for an LLM API call at runtime.
|
|
62
|
+
*
|
|
63
|
+
* This function estimates the total tokens needed by considering:
|
|
64
|
+
* - The prompt text (user input)
|
|
65
|
+
* - Context (previous messages, system messages, etc.)
|
|
66
|
+
* - Instructions (which may contain output format specs)
|
|
67
|
+
* - Expected output format (extracted from instructions)
|
|
68
|
+
*
|
|
69
|
+
* Note: Input format specs in instructions are ignored (they're for planning/design only).
|
|
70
|
+
* Only output format specs are used for runtime token estimation.
|
|
71
|
+
*
|
|
72
|
+
* @param params - Runtime estimation parameters
|
|
73
|
+
* @returns Token estimate with breakdown
|
|
74
|
+
*/
|
|
75
|
+
export declare function runtimeEstimateTokens(params: {
|
|
76
|
+
/**
|
|
77
|
+
* The user prompt/message text
|
|
78
|
+
*/
|
|
79
|
+
prompt?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Context text (system messages, previous conversation, etc.)
|
|
82
|
+
*/
|
|
83
|
+
context?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Instructions text (may contain format specs)
|
|
86
|
+
*/
|
|
87
|
+
instructions?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Estimation options
|
|
90
|
+
*/
|
|
91
|
+
options?: RuntimeEstimateOptions;
|
|
92
|
+
}): RuntimeTokenEstimate;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runtimeEstimateTokens = runtimeEstimateTokens;
|
|
4
|
+
const parser_js_1 = require("../ofs/parser.js");
|
|
5
|
+
const spec_estimator_js_1 = require("./spec-estimator.js");
|
|
6
|
+
const estimator_js_1 = require("./estimator.js");
|
|
7
|
+
/**
|
|
8
|
+
* Estimate max_tokens for an LLM API call at runtime.
|
|
9
|
+
*
|
|
10
|
+
* This function estimates the total tokens needed by considering:
|
|
11
|
+
* - The prompt text (user input)
|
|
12
|
+
* - Context (previous messages, system messages, etc.)
|
|
13
|
+
* - Instructions (which may contain output format specs)
|
|
14
|
+
* - Expected output format (extracted from instructions)
|
|
15
|
+
*
|
|
16
|
+
* Note: Input format specs in instructions are ignored (they're for planning/design only).
|
|
17
|
+
* Only output format specs are used for runtime token estimation.
|
|
18
|
+
*
|
|
19
|
+
* @param params - Runtime estimation parameters
|
|
20
|
+
* @returns Token estimate with breakdown
|
|
21
|
+
*/
|
|
22
|
+
function runtimeEstimateTokens(params) {
|
|
23
|
+
const { prompt = '', context = '', instructions = '' } = params;
|
|
24
|
+
const opts = {
|
|
25
|
+
includeOptional: params.options?.includeOptional ?? true,
|
|
26
|
+
safetyMultiplier: params.options?.safetyMultiplier ?? 1.2,
|
|
27
|
+
strategy: params.options?.strategy ?? 'average',
|
|
28
|
+
estimatePrompt: params.options?.estimatePrompt ?? true,
|
|
29
|
+
estimateContext: params.options?.estimateContext ?? true,
|
|
30
|
+
estimateInstructions: params.options?.estimateInstructions ?? true,
|
|
31
|
+
additionalOverhead: params.options?.additionalOverhead ?? 0
|
|
32
|
+
};
|
|
33
|
+
// Extract format specs from instructions (only output format is used at runtime)
|
|
34
|
+
const formatSpecs = (0, parser_js_1.parseFormatSpecs)(instructions);
|
|
35
|
+
// Estimate tokens for each component
|
|
36
|
+
const promptTokens = opts.estimatePrompt ? (0, estimator_js_1.estimateTextTokens)(prompt) : 0;
|
|
37
|
+
const contextTokens = opts.estimateContext ? (0, estimator_js_1.estimateTextTokens)(context) : 0;
|
|
38
|
+
const instructionsTokens = opts.estimateInstructions ? (0, estimator_js_1.estimateTextTokens)(instructions) : 0;
|
|
39
|
+
// Estimate output tokens from output format spec (input format is for planning only)
|
|
40
|
+
const outputEstimate = formatSpecs.output
|
|
41
|
+
? (0, spec_estimator_js_1.estimateFormatSpecsTokens)(undefined, formatSpecs.output, {
|
|
42
|
+
includeOptional: opts.includeOptional,
|
|
43
|
+
safetyMultiplier: 1.0, // Don't apply safety multiplier here, apply at the end
|
|
44
|
+
strategy: opts.strategy
|
|
45
|
+
})
|
|
46
|
+
: {
|
|
47
|
+
total: { estimated: 0, min: 0, max: 0, confidence: 'low' },
|
|
48
|
+
bySectionName: {},
|
|
49
|
+
overhead: 0
|
|
50
|
+
};
|
|
51
|
+
// Calculate total input tokens (prompt + context + instructions)
|
|
52
|
+
const inputTokens = promptTokens + contextTokens + instructionsTokens;
|
|
53
|
+
// Calculate total output tokens (from format spec)
|
|
54
|
+
// Note: max_tokens parameter in LLM APIs refers to OUTPUT tokens only
|
|
55
|
+
const outputTokens = outputEstimate.total.estimated;
|
|
56
|
+
// Apply safety multiplier to output tokens only (this is what max_tokens represents)
|
|
57
|
+
const maxTokens = Math.ceil(outputTokens * opts.safetyMultiplier) + opts.additionalOverhead;
|
|
58
|
+
// Total tokens for budgeting/planning (input + output)
|
|
59
|
+
const totalTokens = inputTokens + outputTokens + opts.additionalOverhead;
|
|
60
|
+
// Determine confidence based on output spec confidence
|
|
61
|
+
const confidence = outputEstimate.total.confidence;
|
|
62
|
+
return {
|
|
63
|
+
maxTokens, // This is the value to use for max_tokens API parameter (output only)
|
|
64
|
+
breakdown: {
|
|
65
|
+
prompt: promptTokens,
|
|
66
|
+
context: contextTokens,
|
|
67
|
+
instructions: instructionsTokens,
|
|
68
|
+
output: outputEstimate,
|
|
69
|
+
additionalOverhead: opts.additionalOverhead,
|
|
70
|
+
total: totalTokens // Total for budgeting (input + output)
|
|
71
|
+
},
|
|
72
|
+
confidence
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import type { SpecTokenEstimate } from './types.js';
|
|
2
|
-
import type { OutputFormatSpec } from '../types.js';
|
|
2
|
+
import type { OutputFormatSpec, InputFormatSpec } from '../types.js';
|
|
3
3
|
export declare function estimateSpecTokens(spec: OutputFormatSpec, options?: {
|
|
4
4
|
includeOptional?: boolean;
|
|
5
5
|
safetyMultiplier?: number;
|
|
6
6
|
strategy?: 'conservative' | 'average' | 'generous';
|
|
7
7
|
}): SpecTokenEstimate;
|
|
8
|
+
/**
|
|
9
|
+
* Estimate tokens for an Input Format Spec.
|
|
10
|
+
* Uses the same logic as output format estimation.
|
|
11
|
+
*/
|
|
12
|
+
export declare function estimateInputSpecTokens(spec: InputFormatSpec, options?: {
|
|
13
|
+
includeOptional?: boolean;
|
|
14
|
+
safetyMultiplier?: number;
|
|
15
|
+
strategy?: 'conservative' | 'average' | 'generous';
|
|
16
|
+
}): SpecTokenEstimate;
|
|
17
|
+
/**
|
|
18
|
+
* Estimate tokens for both input and output format specs.
|
|
19
|
+
* Combines estimates from both if present.
|
|
20
|
+
*/
|
|
21
|
+
export declare function estimateFormatSpecsTokens(inputSpec: InputFormatSpec | undefined, outputSpec: OutputFormatSpec | undefined, options?: {
|
|
22
|
+
includeOptional?: boolean;
|
|
23
|
+
safetyMultiplier?: number;
|
|
24
|
+
strategy?: 'conservative' | 'average' | 'generous';
|
|
25
|
+
}): SpecTokenEstimate;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.estimateSpecTokens = estimateSpecTokens;
|
|
4
|
+
exports.estimateInputSpecTokens = estimateInputSpecTokens;
|
|
5
|
+
exports.estimateFormatSpecsTokens = estimateFormatSpecsTokens;
|
|
4
6
|
const parser_js_1 = require("./parser.js");
|
|
5
7
|
const estimator_js_1 = require("./estimator.js");
|
|
6
8
|
function estimateSpecTokens(spec, options = {}) {
|
|
@@ -69,3 +71,55 @@ function estimateSpecTokens(spec, options = {}) {
|
|
|
69
71
|
overhead
|
|
70
72
|
};
|
|
71
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Estimate tokens for an Input Format Spec.
|
|
76
|
+
* Uses the same logic as output format estimation.
|
|
77
|
+
*/
|
|
78
|
+
function estimateInputSpecTokens(spec, options = {}) {
|
|
79
|
+
// Input format uses the same structure as output format, so reuse the same logic
|
|
80
|
+
const outputSpec = {
|
|
81
|
+
...spec,
|
|
82
|
+
descriptorType: "output_format_spec"
|
|
83
|
+
};
|
|
84
|
+
return estimateSpecTokens(outputSpec, options);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Estimate tokens for both input and output format specs.
|
|
88
|
+
* Combines estimates from both if present.
|
|
89
|
+
*/
|
|
90
|
+
function estimateFormatSpecsTokens(inputSpec, outputSpec, options = {}) {
|
|
91
|
+
const inputEstimate = inputSpec ? estimateInputSpecTokens(inputSpec, options) : null;
|
|
92
|
+
const outputEstimate = outputSpec ? estimateSpecTokens(outputSpec, options) : null;
|
|
93
|
+
// If only one is present, return it
|
|
94
|
+
if (!inputEstimate && outputEstimate)
|
|
95
|
+
return outputEstimate;
|
|
96
|
+
if (inputEstimate && !outputEstimate)
|
|
97
|
+
return inputEstimate;
|
|
98
|
+
if (!inputEstimate && !outputEstimate) {
|
|
99
|
+
// Return empty estimate
|
|
100
|
+
return {
|
|
101
|
+
total: { estimated: 0, min: 0, max: 0, confidence: 'low' },
|
|
102
|
+
bySectionName: {},
|
|
103
|
+
overhead: 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Combine both estimates
|
|
107
|
+
const combined = {
|
|
108
|
+
total: {
|
|
109
|
+
estimated: inputEstimate.total.estimated + outputEstimate.total.estimated,
|
|
110
|
+
min: inputEstimate.total.min + outputEstimate.total.min,
|
|
111
|
+
max: inputEstimate.total.max + outputEstimate.total.max,
|
|
112
|
+
confidence: inputEstimate.total.confidence === 'high' && outputEstimate.total.confidence === 'high'
|
|
113
|
+
? 'high'
|
|
114
|
+
: inputEstimate.total.confidence === 'low' || outputEstimate.total.confidence === 'low'
|
|
115
|
+
? 'low'
|
|
116
|
+
: 'medium'
|
|
117
|
+
},
|
|
118
|
+
bySectionName: {
|
|
119
|
+
...Object.fromEntries(Object.entries(inputEstimate.bySectionName).map(([k, v]) => [`input:${k}`, v])),
|
|
120
|
+
...Object.fromEntries(Object.entries(outputEstimate.bySectionName).map(([k, v]) => [`output:${k}`, v]))
|
|
121
|
+
},
|
|
122
|
+
overhead: inputEstimate.overhead + outputEstimate.overhead
|
|
123
|
+
};
|
|
124
|
+
return combined;
|
|
125
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -112,6 +112,20 @@ export interface OutputFormatSpec {
|
|
|
112
112
|
tablesOptional?: boolean;
|
|
113
113
|
tables?: OfsTable[];
|
|
114
114
|
}
|
|
115
|
+
export interface InputFormatSpec {
|
|
116
|
+
description?: string;
|
|
117
|
+
sections: OutputSectionSpec[];
|
|
118
|
+
emptySectionValue?: string;
|
|
119
|
+
descriptorType?: "input_format_spec";
|
|
120
|
+
format?: "markdown";
|
|
121
|
+
sectionOrderMatters?: boolean;
|
|
122
|
+
tablesOptional?: boolean;
|
|
123
|
+
tables?: OfsTable[];
|
|
124
|
+
}
|
|
125
|
+
export interface FormatSpecs {
|
|
126
|
+
input?: InputFormatSpec;
|
|
127
|
+
output?: OutputFormatSpec;
|
|
128
|
+
}
|
|
115
129
|
export interface MdNode {
|
|
116
130
|
title: string;
|
|
117
131
|
level: number;
|
|
@@ -199,6 +213,8 @@ export interface ComplianceCheckResult {
|
|
|
199
213
|
hasRequiredSections?: boolean;
|
|
200
214
|
sectionCount?: number;
|
|
201
215
|
containerType?: "fenced-block" | "none";
|
|
216
|
+
hasInputFormat?: boolean;
|
|
217
|
+
hasOutputFormat?: boolean;
|
|
202
218
|
};
|
|
203
219
|
}
|
|
204
220
|
export interface EnhancementChange {
|
|
@@ -2,6 +2,7 @@ import { ComplianceCheckResult } from "../types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Check if instructions meet the required flex-md compliance level.
|
|
4
4
|
* Returns detailed results including what's missing or wrong.
|
|
5
|
+
* Now also checks for Input Format Spec if present.
|
|
5
6
|
*/
|
|
6
7
|
export declare function checkCompliance(instructions: string, complianceLevel: "L0" | "L1" | "L2" | "L3"): Promise<ComplianceCheckResult>;
|
|
7
8
|
/**
|
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkCompliance = checkCompliance;
|
|
4
4
|
exports.hasFlexMdContract = hasFlexMdContract;
|
|
5
|
+
const parser_js_1 = require("../ofs/parser.js");
|
|
5
6
|
/**
|
|
6
7
|
* Check if instructions meet the required flex-md compliance level.
|
|
7
8
|
* Returns detailed results including what's missing or wrong.
|
|
9
|
+
* Now also checks for Input Format Spec if present.
|
|
8
10
|
*/
|
|
9
11
|
async function checkCompliance(instructions, complianceLevel) {
|
|
10
12
|
const issues = [];
|
|
11
13
|
const suggestions = [];
|
|
12
14
|
const lower = instructions.toLowerCase();
|
|
15
|
+
// Extract both input and output format specs
|
|
16
|
+
const formatSpecs = (0, parser_js_1.parseFormatSpecs)(instructions);
|
|
17
|
+
const hasInputFormat = !!formatSpecs.input;
|
|
18
|
+
const hasOutputFormat = !!formatSpecs.output;
|
|
13
19
|
const hasMarkdownMention = lower.includes("markdown");
|
|
14
20
|
const hasSectionMention = lower.includes("section") || lower.includes("heading");
|
|
15
21
|
const hasContainerMention = (lower.includes("fenced block") || lower.includes("```")) && (lower.includes("inside") || lower.includes("wrapped"));
|
|
@@ -81,7 +87,9 @@ async function checkCompliance(instructions, complianceLevel) {
|
|
|
81
87
|
metadata: {
|
|
82
88
|
hasContainer: hasContainerMention,
|
|
83
89
|
hasRequiredSections: hasSectionMention,
|
|
84
|
-
containerType: hasContainerMention ? "fenced-block" : "none"
|
|
90
|
+
containerType: hasContainerMention ? "fenced-block" : "none",
|
|
91
|
+
hasInputFormat,
|
|
92
|
+
hasOutputFormat
|
|
85
93
|
}
|
|
86
94
|
};
|
|
87
95
|
}
|
package/package.json
CHANGED