@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.
- package/cli/analyze.js +312 -0
- package/cli/generate.js +182 -1
- package/cli/visualize.js +171 -1
- package/core/coherence/index.js +15 -1
- package/core/coherence/semantic-analyzer.js +836 -0
- package/generators/visual-doc-generator.js +910 -0
- package/intelligence/index.js +14 -1
- package/intelligence/model-context-optimizer.js +704 -0
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +1 -1
|
@@ -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
|
+
};
|