@girardmedia/bootspring 2.0.37 → 2.0.38

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.
@@ -0,0 +1,704 @@
1
+ /**
2
+ * Bootspring Model Context Optimizer
3
+ *
4
+ * Optimizes AI context for different LLM models (Claude, GPT, Gemini, etc.)
5
+ * Handles token limits, formatting preferences, and context prioritization.
6
+ *
7
+ * @package bootspring
8
+ * @module intelligence/model-context-optimizer
9
+ */
10
+
11
+ const path = require('path');
12
+
13
+ /**
14
+ * Model configurations with token limits and formatting preferences
15
+ */
16
+ const MODEL_CONFIGS = {
17
+ claude: {
18
+ name: 'Claude',
19
+ provider: 'Anthropic',
20
+ variants: {
21
+ 'claude-opus-4': { contextWindow: 200000, outputLimit: 32000 },
22
+ 'claude-sonnet-4': { contextWindow: 200000, outputLimit: 64000 },
23
+ 'claude-3.5-sonnet': { contextWindow: 200000, outputLimit: 8192 },
24
+ 'claude-3-opus': { contextWindow: 200000, outputLimit: 4096 },
25
+ 'claude-3-sonnet': { contextWindow: 200000, outputLimit: 4096 },
26
+ 'claude-3-haiku': { contextWindow: 200000, outputLimit: 4096 }
27
+ },
28
+ defaultVariant: 'claude-sonnet-4',
29
+ contextFile: 'CLAUDE.md',
30
+ formatting: {
31
+ preferMarkdown: true,
32
+ xmlTags: true,
33
+ headingStyle: 'atx',
34
+ codeBlocks: 'fenced',
35
+ listStyle: 'dash',
36
+ tableSupport: true
37
+ },
38
+ features: {
39
+ toolUse: true,
40
+ vision: true,
41
+ systemPrompt: true,
42
+ streaming: true,
43
+ artifacts: true
44
+ }
45
+ },
46
+ openai: {
47
+ name: 'GPT',
48
+ provider: 'OpenAI',
49
+ variants: {
50
+ 'gpt-4o': { contextWindow: 128000, outputLimit: 16384 },
51
+ 'gpt-4-turbo': { contextWindow: 128000, outputLimit: 4096 },
52
+ 'gpt-4': { contextWindow: 8192, outputLimit: 4096 },
53
+ 'gpt-3.5-turbo': { contextWindow: 16385, outputLimit: 4096 },
54
+ 'o1': { contextWindow: 200000, outputLimit: 100000 },
55
+ 'o1-mini': { contextWindow: 128000, outputLimit: 65536 }
56
+ },
57
+ defaultVariant: 'gpt-4o',
58
+ contextFile: 'OPENAI.md',
59
+ formatting: {
60
+ preferMarkdown: true,
61
+ xmlTags: false,
62
+ headingStyle: 'atx',
63
+ codeBlocks: 'fenced',
64
+ listStyle: 'dash',
65
+ tableSupport: true
66
+ },
67
+ features: {
68
+ toolUse: true,
69
+ vision: true,
70
+ systemPrompt: true,
71
+ streaming: true,
72
+ jsonMode: true
73
+ }
74
+ },
75
+ gemini: {
76
+ name: 'Gemini',
77
+ provider: 'Google',
78
+ variants: {
79
+ 'gemini-2.0-flash': { contextWindow: 1000000, outputLimit: 8192 },
80
+ 'gemini-1.5-pro': { contextWindow: 2000000, outputLimit: 8192 },
81
+ 'gemini-1.5-flash': { contextWindow: 1000000, outputLimit: 8192 },
82
+ 'gemini-1.0-pro': { contextWindow: 32760, outputLimit: 8192 }
83
+ },
84
+ defaultVariant: 'gemini-1.5-pro',
85
+ contextFile: 'GEMINI.md',
86
+ formatting: {
87
+ preferMarkdown: true,
88
+ xmlTags: false,
89
+ headingStyle: 'atx',
90
+ codeBlocks: 'fenced',
91
+ listStyle: 'dash',
92
+ tableSupport: true
93
+ },
94
+ features: {
95
+ toolUse: true,
96
+ vision: true,
97
+ systemPrompt: true,
98
+ streaming: true,
99
+ multimodal: true
100
+ }
101
+ },
102
+ mistral: {
103
+ name: 'Mistral',
104
+ provider: 'Mistral AI',
105
+ variants: {
106
+ 'mistral-large': { contextWindow: 128000, outputLimit: 8192 },
107
+ 'mistral-medium': { contextWindow: 32000, outputLimit: 8192 },
108
+ 'mistral-small': { contextWindow: 32000, outputLimit: 8192 },
109
+ 'codestral': { contextWindow: 32000, outputLimit: 8192 }
110
+ },
111
+ defaultVariant: 'mistral-large',
112
+ contextFile: 'MISTRAL.md',
113
+ formatting: {
114
+ preferMarkdown: true,
115
+ xmlTags: false,
116
+ headingStyle: 'atx',
117
+ codeBlocks: 'fenced',
118
+ listStyle: 'dash',
119
+ tableSupport: true
120
+ },
121
+ features: {
122
+ toolUse: true,
123
+ vision: false,
124
+ systemPrompt: true,
125
+ streaming: true,
126
+ codeSpecialized: true
127
+ }
128
+ },
129
+ llama: {
130
+ name: 'Llama',
131
+ provider: 'Meta',
132
+ variants: {
133
+ 'llama-3.3-70b': { contextWindow: 128000, outputLimit: 8192 },
134
+ 'llama-3.2-90b': { contextWindow: 128000, outputLimit: 8192 },
135
+ 'llama-3.1-405b': { contextWindow: 128000, outputLimit: 8192 },
136
+ 'llama-3.1-70b': { contextWindow: 128000, outputLimit: 8192 }
137
+ },
138
+ defaultVariant: 'llama-3.3-70b',
139
+ contextFile: 'LLAMA.md',
140
+ formatting: {
141
+ preferMarkdown: true,
142
+ xmlTags: false,
143
+ headingStyle: 'atx',
144
+ codeBlocks: 'fenced',
145
+ listStyle: 'dash',
146
+ tableSupport: true
147
+ },
148
+ features: {
149
+ toolUse: true,
150
+ vision: true,
151
+ systemPrompt: true,
152
+ streaming: true,
153
+ openWeight: true
154
+ }
155
+ }
156
+ };
157
+
158
+ /**
159
+ * Context section priorities (higher = more important)
160
+ */
161
+ const SECTION_PRIORITIES = {
162
+ critical: 100, // Must always include (project name, tech stack)
163
+ high: 80, // Important for most tasks (commands, guidelines)
164
+ medium: 60, // Useful context (plugins, structure)
165
+ low: 40, // Nice to have (learnings, state)
166
+ optional: 20 // Can be dropped first (verbose explanations)
167
+ };
168
+
169
+ /**
170
+ * Default section classifications
171
+ */
172
+ const SECTION_CLASSIFICATIONS = {
173
+ 'Project Overview': SECTION_PRIORITIES.critical,
174
+ 'Tech Stack': SECTION_PRIORITIES.critical,
175
+ 'Development Guidelines': SECTION_PRIORITIES.high,
176
+ 'Bootspring Commands': SECTION_PRIORITIES.high,
177
+ 'Custom Instructions': SECTION_PRIORITIES.high,
178
+ 'Enabled Plugins': SECTION_PRIORITIES.medium,
179
+ 'Project Structure': SECTION_PRIORITIES.medium,
180
+ 'Current State': SECTION_PRIORITIES.medium,
181
+ 'Git Repository': SECTION_PRIORITIES.low,
182
+ 'Recent Learnings': SECTION_PRIORITIES.low,
183
+ 'Generated': SECTION_PRIORITIES.optional
184
+ };
185
+
186
+ /**
187
+ * Token estimation ratios (characters per token, approximate)
188
+ */
189
+ const TOKEN_RATIOS = {
190
+ english: 4.0, // Average English text
191
+ code: 3.5, // Source code
192
+ markdown: 3.8, // Markdown formatted text
193
+ json: 4.5 // JSON data
194
+ };
195
+
196
+ /**
197
+ * Model Context Optimizer
198
+ * Optimizes AI context for different LLM models
199
+ */
200
+ class ModelContextOptimizer {
201
+ /**
202
+ * Create a new ModelContextOptimizer
203
+ * @param {object} options - Configuration options
204
+ * @param {string} options.projectRoot - Project root directory
205
+ * @param {string} options.model - Model family (claude, openai, gemini, etc.)
206
+ * @param {string} options.variant - Specific model variant
207
+ * @param {number} options.reserveTokens - Tokens to reserve for output
208
+ */
209
+ constructor(options = {}) {
210
+ this.projectRoot = options.projectRoot || process.cwd();
211
+ this.modelFamily = options.model || 'claude';
212
+ this.variant = options.variant || null;
213
+ this.reserveTokens = options.reserveTokens || 8000;
214
+ this.customSectionPriorities = options.sectionPriorities || {};
215
+
216
+ // Validate model family
217
+ if (!MODEL_CONFIGS[this.modelFamily]) {
218
+ throw new Error(`Unknown model family: ${this.modelFamily}`);
219
+ }
220
+
221
+ this.config = MODEL_CONFIGS[this.modelFamily];
222
+
223
+ // Set variant (use default if not specified)
224
+ if (!this.variant) {
225
+ this.variant = this.config.defaultVariant;
226
+ }
227
+
228
+ // Validate variant
229
+ if (!this.config.variants[this.variant]) {
230
+ throw new Error(`Unknown variant ${this.variant} for model ${this.modelFamily}`);
231
+ }
232
+
233
+ this.variantConfig = this.config.variants[this.variant];
234
+ }
235
+
236
+ /**
237
+ * Get available context budget in tokens
238
+ * @returns {number} Available tokens for context
239
+ */
240
+ getContextBudget() {
241
+ return this.variantConfig.contextWindow - this.reserveTokens;
242
+ }
243
+
244
+ /**
245
+ * Estimate token count for text
246
+ * @param {string} text - Text to estimate
247
+ * @param {string} contentType - Type of content (english, code, markdown, json)
248
+ * @returns {number} Estimated token count
249
+ */
250
+ estimateTokens(text, contentType = 'markdown') {
251
+ if (!text) return 0;
252
+ const ratio = TOKEN_RATIOS[contentType] || TOKEN_RATIOS.english;
253
+ return Math.ceil(text.length / ratio);
254
+ }
255
+
256
+ /**
257
+ * Parse context document into sections
258
+ * @param {string} content - Markdown content to parse
259
+ * @returns {Array<object>} Parsed sections with metadata
260
+ */
261
+ parseSections(content) {
262
+ const sections = [];
263
+ const lines = content.split('\n');
264
+
265
+ let currentSection = null;
266
+ let currentContent = [];
267
+
268
+ for (const line of lines) {
269
+ // Check for heading
270
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
271
+
272
+ if (headingMatch) {
273
+ // Save previous section
274
+ if (currentSection) {
275
+ currentSection.content = currentContent.join('\n').trim();
276
+ currentSection.tokens = this.estimateTokens(currentSection.content);
277
+ sections.push(currentSection);
278
+ }
279
+
280
+ // Start new section
281
+ const level = headingMatch[1].length;
282
+ const title = headingMatch[2].trim();
283
+
284
+ currentSection = {
285
+ level,
286
+ title,
287
+ priority: this.getSectionPriority(title),
288
+ content: '',
289
+ tokens: 0,
290
+ line: lines.indexOf(line)
291
+ };
292
+ currentContent = [line];
293
+ } else if (currentSection) {
294
+ currentContent.push(line);
295
+ } else {
296
+ // Content before first heading (preamble)
297
+ if (line.trim()) {
298
+ currentSection = {
299
+ level: 0,
300
+ title: '_preamble',
301
+ priority: SECTION_PRIORITIES.critical,
302
+ content: '',
303
+ tokens: 0,
304
+ line: 0
305
+ };
306
+ currentContent = [line];
307
+ }
308
+ }
309
+ }
310
+
311
+ // Save last section
312
+ if (currentSection) {
313
+ currentSection.content = currentContent.join('\n').trim();
314
+ currentSection.tokens = this.estimateTokens(currentSection.content);
315
+ sections.push(currentSection);
316
+ }
317
+
318
+ return sections;
319
+ }
320
+
321
+ /**
322
+ * Get priority for a section based on title
323
+ * @param {string} title - Section title
324
+ * @returns {number} Priority value
325
+ */
326
+ getSectionPriority(title) {
327
+ // Check custom priorities first
328
+ if (this.customSectionPriorities[title]) {
329
+ return this.customSectionPriorities[title];
330
+ }
331
+
332
+ // Check default classifications
333
+ for (const [pattern, priority] of Object.entries(SECTION_CLASSIFICATIONS)) {
334
+ if (title.toLowerCase().includes(pattern.toLowerCase())) {
335
+ return priority;
336
+ }
337
+ }
338
+
339
+ return SECTION_PRIORITIES.medium;
340
+ }
341
+
342
+ /**
343
+ * Optimize context to fit within token budget
344
+ * @param {string} content - Original context content
345
+ * @param {object} options - Optimization options
346
+ * @returns {object} Optimized context with metadata
347
+ */
348
+ optimize(content, options = {}) {
349
+ const budget = options.budget || this.getContextBudget();
350
+ const preserveCritical = options.preserveCritical !== false;
351
+
352
+ const sections = this.parseSections(content);
353
+ const totalTokens = sections.reduce((sum, s) => sum + s.tokens, 0);
354
+
355
+ // If we're under budget, no optimization needed
356
+ if (totalTokens <= budget) {
357
+ return {
358
+ content,
359
+ originalTokens: totalTokens,
360
+ optimizedTokens: totalTokens,
361
+ sectionsIncluded: sections.length,
362
+ sectionsDropped: 0,
363
+ droppedSections: [],
364
+ withinBudget: true
365
+ };
366
+ }
367
+
368
+ // Sort sections by priority (descending)
369
+ const sortedSections = [...sections].sort((a, b) => b.priority - a.priority);
370
+
371
+ // Select sections that fit within budget
372
+ const includedSections = [];
373
+ const droppedSections = [];
374
+ let usedTokens = 0;
375
+
376
+ for (const section of sortedSections) {
377
+ // Critical sections are always included if preserveCritical is true
378
+ const isCritical = section.priority === SECTION_PRIORITIES.critical;
379
+
380
+ if (isCritical && preserveCritical) {
381
+ includedSections.push(section);
382
+ usedTokens += section.tokens;
383
+ } else if (usedTokens + section.tokens <= budget) {
384
+ includedSections.push(section);
385
+ usedTokens += section.tokens;
386
+ } else {
387
+ droppedSections.push(section);
388
+ }
389
+ }
390
+
391
+ // Re-sort by original line order to maintain document flow
392
+ includedSections.sort((a, b) => a.line - b.line);
393
+
394
+ // Reconstruct content
395
+ const optimizedContent = includedSections
396
+ .map(s => s.content)
397
+ .join('\n\n');
398
+
399
+ return {
400
+ content: optimizedContent,
401
+ originalTokens: totalTokens,
402
+ optimizedTokens: usedTokens,
403
+ sectionsIncluded: includedSections.length,
404
+ sectionsDropped: droppedSections.length,
405
+ droppedSections: droppedSections.map(s => ({
406
+ title: s.title,
407
+ tokens: s.tokens,
408
+ priority: s.priority
409
+ })),
410
+ withinBudget: usedTokens <= budget
411
+ };
412
+ }
413
+
414
+ /**
415
+ * Format context for specific model
416
+ * @param {string} content - Context content
417
+ * @returns {string} Formatted content
418
+ */
419
+ formatForModel(content) {
420
+ const formatting = this.config.formatting;
421
+ let formatted = content;
422
+
423
+ // Apply XML tags for Claude
424
+ if (formatting.xmlTags && this.modelFamily === 'claude') {
425
+ formatted = this.wrapWithXmlTags(formatted);
426
+ }
427
+
428
+ // Normalize list style
429
+ if (formatting.listStyle === 'dash') {
430
+ formatted = formatted.replace(/^\*\s+/gm, '- ');
431
+ } else if (formatting.listStyle === 'asterisk') {
432
+ formatted = formatted.replace(/^-\s+/gm, '* ');
433
+ }
434
+
435
+ return formatted;
436
+ }
437
+
438
+ /**
439
+ * Wrap content sections with XML tags (Claude preference)
440
+ * @param {string} content - Content to wrap
441
+ * @returns {string} Wrapped content
442
+ */
443
+ wrapWithXmlTags(content) {
444
+ const sections = this.parseSections(content);
445
+ const wrapped = [];
446
+
447
+ for (const section of sections) {
448
+ if (section.title === '_preamble') {
449
+ wrapped.push(section.content);
450
+ } else {
451
+ const tagName = section.title
452
+ .toLowerCase()
453
+ .replace(/[^a-z0-9]+/g, '-')
454
+ .replace(/^-|-$/g, '');
455
+
456
+ wrapped.push(`<${tagName}>\n${section.content}\n</${tagName}>`);
457
+ }
458
+ }
459
+
460
+ return wrapped.join('\n\n');
461
+ }
462
+
463
+ /**
464
+ * Generate optimized context file for the model
465
+ * @param {string} sourceContent - Source context content
466
+ * @param {object} options - Generation options
467
+ * @returns {object} Generated context with file info
468
+ */
469
+ generateContextFile(sourceContent, options = {}) {
470
+ // Optimize content
471
+ const optimized = this.optimize(sourceContent, options);
472
+
473
+ // Format for model
474
+ const formatted = this.formatForModel(optimized.content);
475
+
476
+ // Add model-specific header
477
+ const header = this.generateHeader();
478
+ const finalContent = `${header}\n\n${formatted}`;
479
+
480
+ return {
481
+ filename: this.config.contextFile,
482
+ filepath: path.join(this.projectRoot, this.config.contextFile),
483
+ content: finalContent,
484
+ model: this.modelFamily,
485
+ variant: this.variant,
486
+ optimization: {
487
+ originalTokens: optimized.originalTokens,
488
+ optimizedTokens: this.estimateTokens(finalContent),
489
+ sectionsIncluded: optimized.sectionsIncluded,
490
+ sectionsDropped: optimized.sectionsDropped,
491
+ droppedSections: optimized.droppedSections
492
+ },
493
+ formatting: this.config.formatting,
494
+ features: this.config.features
495
+ };
496
+ }
497
+
498
+ /**
499
+ * Generate model-specific header
500
+ * @returns {string} Header content
501
+ */
502
+ generateHeader() {
503
+ const lines = [
504
+ `# AI Context - ${this.config.name}`,
505
+ '',
506
+ `**Optimized for**: ${this.config.name} (${this.variant})`,
507
+ `**Provider**: ${this.config.provider}`,
508
+ `**Context Window**: ${this.variantConfig.contextWindow.toLocaleString()} tokens`,
509
+ `**Generated**: ${new Date().toISOString()}`
510
+ ];
511
+
512
+ return lines.join('\n');
513
+ }
514
+
515
+ /**
516
+ * Get model capabilities and recommendations
517
+ * @returns {object} Model capabilities and recommendations
518
+ */
519
+ getModelCapabilities() {
520
+ return {
521
+ model: this.modelFamily,
522
+ variant: this.variant,
523
+ provider: this.config.provider,
524
+ contextWindow: this.variantConfig.contextWindow,
525
+ outputLimit: this.variantConfig.outputLimit,
526
+ availableBudget: this.getContextBudget(),
527
+ features: this.config.features,
528
+ formatting: this.config.formatting,
529
+ recommendations: this.generateRecommendations()
530
+ };
531
+ }
532
+
533
+ /**
534
+ * Generate model-specific recommendations
535
+ * @returns {Array<string>} Recommendations
536
+ */
537
+ generateRecommendations() {
538
+ const recommendations = [];
539
+ const features = this.config.features;
540
+
541
+ // Tool use recommendations
542
+ if (features.toolUse) {
543
+ recommendations.push('Model supports function/tool calling - consider using structured commands');
544
+ }
545
+
546
+ // Vision recommendations
547
+ if (features.vision) {
548
+ recommendations.push('Model supports image analysis - can include diagrams or screenshots');
549
+ }
550
+
551
+ // Context window recommendations
552
+ if (this.variantConfig.contextWindow >= 100000) {
553
+ recommendations.push('Large context window available - include comprehensive documentation');
554
+ } else if (this.variantConfig.contextWindow < 32000) {
555
+ recommendations.push('Limited context window - prioritize essential information only');
556
+ }
557
+
558
+ // Model-specific recommendations
559
+ if (this.modelFamily === 'claude') {
560
+ recommendations.push('Claude excels with XML tags for structured sections');
561
+ recommendations.push('Use <example> tags for code patterns');
562
+ } else if (this.modelFamily === 'openai') {
563
+ recommendations.push('Consider using JSON mode for structured outputs');
564
+ } else if (this.modelFamily === 'gemini') {
565
+ recommendations.push('Very large context - can include full codebase context');
566
+ } else if (this.modelFamily === 'mistral') {
567
+ if (this.variant === 'codestral') {
568
+ recommendations.push('Codestral specialized for code - include more code examples');
569
+ }
570
+ }
571
+
572
+ return recommendations;
573
+ }
574
+
575
+ /**
576
+ * Compare context requirements across models
577
+ * @param {string} content - Context content to compare
578
+ * @returns {Array<object>} Comparison results for all models
579
+ */
580
+ compareAcrossModels(content) {
581
+ const results = [];
582
+ const originalTokens = this.estimateTokens(content);
583
+
584
+ for (const [family, config] of Object.entries(MODEL_CONFIGS)) {
585
+ const optimizer = new ModelContextOptimizer({
586
+ projectRoot: this.projectRoot,
587
+ model: family
588
+ });
589
+
590
+ const budget = optimizer.getContextBudget();
591
+ const fitsWithinBudget = originalTokens <= budget;
592
+
593
+ results.push({
594
+ model: family,
595
+ variant: config.defaultVariant,
596
+ provider: config.provider,
597
+ contextWindow: optimizer.variantConfig.contextWindow,
598
+ availableBudget: budget,
599
+ contentTokens: originalTokens,
600
+ fitsWithinBudget,
601
+ utilizationPercent: Math.round((originalTokens / budget) * 100),
602
+ contextFile: config.contextFile
603
+ });
604
+ }
605
+
606
+ // Sort by context window size (descending)
607
+ return results.sort((a, b) => b.contextWindow - a.contextWindow);
608
+ }
609
+
610
+ /**
611
+ * Generate multi-model context files
612
+ * @param {string} sourceContent - Source context content
613
+ * @param {Array<string>} models - Models to generate for
614
+ * @returns {Array<object>} Generated context files
615
+ */
616
+ generateMultiModelContexts(sourceContent, models = null) {
617
+ const targetModels = models || Object.keys(MODEL_CONFIGS);
618
+ const results = [];
619
+
620
+ for (const model of targetModels) {
621
+ try {
622
+ const optimizer = new ModelContextOptimizer({
623
+ projectRoot: this.projectRoot,
624
+ model
625
+ });
626
+
627
+ const context = optimizer.generateContextFile(sourceContent);
628
+ results.push({
629
+ ...context,
630
+ status: 'success'
631
+ });
632
+ } catch (error) {
633
+ results.push({
634
+ model,
635
+ status: 'error',
636
+ error: error.message
637
+ });
638
+ }
639
+ }
640
+
641
+ return results;
642
+ }
643
+ }
644
+
645
+ /**
646
+ * Get list of supported models
647
+ * @returns {Array<object>} List of supported models
648
+ */
649
+ function listSupportedModels() {
650
+ const models = [];
651
+
652
+ for (const [family, config] of Object.entries(MODEL_CONFIGS)) {
653
+ models.push({
654
+ family,
655
+ name: config.name,
656
+ provider: config.provider,
657
+ variants: Object.keys(config.variants),
658
+ defaultVariant: config.defaultVariant,
659
+ contextFile: config.contextFile,
660
+ features: config.features
661
+ });
662
+ }
663
+
664
+ return models;
665
+ }
666
+
667
+ /**
668
+ * Get model configuration
669
+ * @param {string} family - Model family
670
+ * @param {string} variant - Model variant (optional)
671
+ * @returns {object} Model configuration
672
+ */
673
+ function getModelConfig(family, variant = null) {
674
+ const config = MODEL_CONFIGS[family];
675
+ if (!config) {
676
+ return null;
677
+ }
678
+
679
+ const variantConfig = variant
680
+ ? config.variants[variant]
681
+ : config.variants[config.defaultVariant];
682
+
683
+ return {
684
+ family,
685
+ name: config.name,
686
+ provider: config.provider,
687
+ variant: variant || config.defaultVariant,
688
+ contextWindow: variantConfig.contextWindow,
689
+ outputLimit: variantConfig.outputLimit,
690
+ contextFile: config.contextFile,
691
+ formatting: config.formatting,
692
+ features: config.features
693
+ };
694
+ }
695
+
696
+ module.exports = {
697
+ ModelContextOptimizer,
698
+ listSupportedModels,
699
+ getModelConfig,
700
+ MODEL_CONFIGS,
701
+ SECTION_PRIORITIES,
702
+ SECTION_CLASSIFICATIONS,
703
+ TOKEN_RATIOS
704
+ };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "contractVersion": "v1",
3
3
  "packageName": "@girardmedia/bootspring",
4
- "packageVersion": "2.0.37",
4
+ "packageVersion": "2.0.38",
5
5
  "tools": [
6
6
  {
7
7
  "name": "bootspring_assist",