@dmsdc-ai/aigentry-deliberation 0.0.19 → 0.0.20
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/model-router.js +224 -0
- package/package.json +2 -1
package/model-router.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// model-router.js
|
|
2
|
+
// Dynamic model selection based on deliberation prompt context
|
|
3
|
+
|
|
4
|
+
const CATEGORY_KEYWORDS = {
|
|
5
|
+
reasoning: [
|
|
6
|
+
'analyze', 'analysis', 'debug', 'debugging', 'complex', 'logic', 'reasoning',
|
|
7
|
+
'problem', 'solve', 'evaluate', 'assess', 'critique', 'argument', 'inference',
|
|
8
|
+
'contradiction', 'flaw', 'why', 'explain', 'cause', 'effect', 'implication',
|
|
9
|
+
'consequence', 'tradeoff', 'compare', 'pros', 'cons', 'decision', 'strategy',
|
|
10
|
+
'architecture', 'design pattern', 'refactor', 'optimize', 'performance',
|
|
11
|
+
],
|
|
12
|
+
coding: [
|
|
13
|
+
'code', 'implement', 'function', 'class', 'module', 'api', 'library',
|
|
14
|
+
'algorithm', 'data structure', 'typescript', 'javascript', 'python', 'rust',
|
|
15
|
+
'build', 'deploy', 'test', 'unit test', 'integration', 'bug', 'fix', 'error',
|
|
16
|
+
'syntax', 'runtime', 'compile', 'script', 'program', 'software', 'feature',
|
|
17
|
+
'endpoint', 'schema', 'database', 'query', 'sql', 'migration', 'lint',
|
|
18
|
+
],
|
|
19
|
+
creative: [
|
|
20
|
+
'write', 'writing', 'design', 'brainstorm', 'idea', 'creative', 'story',
|
|
21
|
+
'narrative', 'style', 'tone', 'voice', 'draft', 'outline', 'concept',
|
|
22
|
+
'vision', 'imagine', 'suggest', 'propose', 'generate', 'name', 'brand',
|
|
23
|
+
'ui', 'ux', 'interface', 'experience', 'aesthetic', 'layout', 'visual',
|
|
24
|
+
],
|
|
25
|
+
research: [
|
|
26
|
+
'research', 'find', 'search', 'fact', 'data', 'statistic', 'source',
|
|
27
|
+
'reference', 'study', 'survey', 'report', 'paper', 'documentation', 'docs',
|
|
28
|
+
'compare', 'benchmark', 'review', 'overview', 'summary', 'what is', 'who is',
|
|
29
|
+
'history', 'background', 'context', 'information', 'knowledge',
|
|
30
|
+
],
|
|
31
|
+
simple: [
|
|
32
|
+
'yes', 'no', 'confirm', 'ok', 'okay', 'agree', 'disagree', 'correct',
|
|
33
|
+
'hello', 'hi', 'thanks', 'thank you', 'sure', 'got it', 'understood',
|
|
34
|
+
'clarify', 'quick', 'brief', 'simple', 'basic',
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const COMPLEXITY_KEYWORDS = {
|
|
39
|
+
high: [
|
|
40
|
+
'complex', 'complicated', 'difficult', 'challenging', 'sophisticated',
|
|
41
|
+
'advanced', 'deep', 'thorough', 'comprehensive', 'exhaustive', 'detailed',
|
|
42
|
+
'multi-step', 'multi-faceted', 'nuanced', 'ambiguous', 'architecture',
|
|
43
|
+
'system', 'scalable', 'distributed', 'concurrent', 'critical', 'production',
|
|
44
|
+
],
|
|
45
|
+
low: [
|
|
46
|
+
'simple', 'basic', 'quick', 'brief', 'short', 'easy', 'trivial',
|
|
47
|
+
'straightforward', 'obvious', 'clear', 'confirm', 'yes', 'no', 'agree',
|
|
48
|
+
'ok', 'sure', 'hello', 'hi',
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const ROLE_KEYWORDS = {
|
|
53
|
+
critic: 'reasoning',
|
|
54
|
+
implementer: 'coding',
|
|
55
|
+
mediator: 'creative',
|
|
56
|
+
researcher: 'research',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Count keyword matches in text for a given keyword list.
|
|
61
|
+
*/
|
|
62
|
+
function countMatches(text, keywords) {
|
|
63
|
+
const lower = text.toLowerCase();
|
|
64
|
+
return keywords.reduce((count, kw) => {
|
|
65
|
+
return count + (lower.includes(kw.toLowerCase()) ? 1 : 0);
|
|
66
|
+
}, 0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Analyze deliberation context to determine category and complexity.
|
|
71
|
+
* @param {string} topic - The deliberation topic
|
|
72
|
+
* @param {string} recentLog - Recent log text from deliberation
|
|
73
|
+
* @param {string} role - The speaker's role
|
|
74
|
+
* @returns {{ category: string, complexity: string }}
|
|
75
|
+
*/
|
|
76
|
+
export function analyzePromptContext(topic, recentLog, role) {
|
|
77
|
+
const text = `${topic} ${recentLog}`;
|
|
78
|
+
|
|
79
|
+
// Role-based category hint
|
|
80
|
+
let roleCategory = null;
|
|
81
|
+
if (role) {
|
|
82
|
+
const lowerRole = role.toLowerCase();
|
|
83
|
+
for (const [keyword, category] of Object.entries(ROLE_KEYWORDS)) {
|
|
84
|
+
if (lowerRole.includes(keyword)) {
|
|
85
|
+
roleCategory = category;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Keyword-based category scoring
|
|
92
|
+
const scores = {};
|
|
93
|
+
for (const [category, keywords] of Object.entries(CATEGORY_KEYWORDS)) {
|
|
94
|
+
scores[category] = countMatches(text, keywords);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Find top category by keyword score
|
|
98
|
+
let topCategory = 'simple';
|
|
99
|
+
let topScore = 0;
|
|
100
|
+
for (const [category, score] of Object.entries(scores)) {
|
|
101
|
+
if (score > topScore) {
|
|
102
|
+
topScore = score;
|
|
103
|
+
topCategory = category;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Role hint takes precedence if no strong keyword signal
|
|
108
|
+
const category = topScore >= 2 ? topCategory : (roleCategory || topCategory);
|
|
109
|
+
|
|
110
|
+
// Complexity detection
|
|
111
|
+
const highCount = countMatches(text, COMPLEXITY_KEYWORDS.high);
|
|
112
|
+
const lowCount = countMatches(text, COMPLEXITY_KEYWORDS.low);
|
|
113
|
+
|
|
114
|
+
let complexity;
|
|
115
|
+
if (highCount > lowCount && highCount >= 1) {
|
|
116
|
+
complexity = 'high';
|
|
117
|
+
} else if (lowCount > 0 && highCount === 0) {
|
|
118
|
+
complexity = 'low';
|
|
119
|
+
} else {
|
|
120
|
+
complexity = 'medium';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { category, complexity };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Select the optimal model for a given provider based on category and complexity.
|
|
128
|
+
* @param {string} provider - The AI provider identifier
|
|
129
|
+
* @param {string} category - Prompt category
|
|
130
|
+
* @param {string} complexity - Prompt complexity
|
|
131
|
+
* @returns {{ model: string, reason: string }}
|
|
132
|
+
*/
|
|
133
|
+
export function selectModelForProvider(provider, category, complexity) {
|
|
134
|
+
const isHighReasoning = category === 'reasoning' || complexity === 'high';
|
|
135
|
+
const isSimple = complexity === 'low' || category === 'simple';
|
|
136
|
+
const isCoding = category === 'coding';
|
|
137
|
+
|
|
138
|
+
switch (provider) {
|
|
139
|
+
case 'chatgpt': {
|
|
140
|
+
if (isHighReasoning) return { model: 'o3', reason: 'High-complexity reasoning task' };
|
|
141
|
+
if (isCoding) return { model: 'o4-mini', reason: 'Coding/implementation task' };
|
|
142
|
+
if (isSimple) return { model: 'gpt-4o-mini', reason: 'Simple task, cost-efficient model' };
|
|
143
|
+
return { model: 'gpt-4o', reason: 'Creative or medium-complexity task' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
case 'claude': {
|
|
147
|
+
if (isHighReasoning) return { model: 'opus', reason: 'High-complexity reasoning task' };
|
|
148
|
+
if (isSimple) return { model: 'haiku', reason: 'Simple task, fast and cost-efficient' };
|
|
149
|
+
return { model: 'sonnet', reason: 'Coding or medium-complexity task' };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case 'gemini': {
|
|
153
|
+
if (isHighReasoning || complexity === 'high') return { model: '2.5 Pro', reason: 'High-complexity or reasoning task' };
|
|
154
|
+
if (isSimple) return { model: '2.0 Flash', reason: 'Simple task, fast response' };
|
|
155
|
+
return { model: '2.5 Flash', reason: 'Medium-complexity task' };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
case 'deepseek': {
|
|
159
|
+
if (category === 'reasoning') return { model: 'DeepSeek-R1', reason: 'Reasoning-focused task' };
|
|
160
|
+
return { model: 'DeepSeek-V3', reason: 'General task' };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
case 'grok': {
|
|
164
|
+
if (complexity === 'high' || isHighReasoning) return { model: 'grok-3', reason: 'High-complexity task' };
|
|
165
|
+
return { model: 'grok-3-mini', reason: 'Simple task' };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case 'mistral': {
|
|
169
|
+
if (complexity === 'high' || isHighReasoning) return { model: 'Mistral Large', reason: 'High-complexity task' };
|
|
170
|
+
return { model: 'Mistral Small', reason: 'Simple task' };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
case 'poe': {
|
|
174
|
+
if (complexity === 'high' || isHighReasoning) return { model: 'Claude-3.5-Sonnet', reason: 'High-complexity task' };
|
|
175
|
+
if (isSimple) return { model: 'Claude-3-Haiku', reason: 'Simple task' };
|
|
176
|
+
return { model: 'GPT-4o', reason: 'Medium-complexity task' };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
case 'qwen': {
|
|
180
|
+
if (complexity === 'high' || isHighReasoning) return { model: 'Qwen-Max', reason: 'High-complexity task' };
|
|
181
|
+
return { model: 'Qwen-Plus', reason: 'Simple task' };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
case 'huggingchat': {
|
|
185
|
+
if (complexity === 'high' || isHighReasoning) return { model: 'Qwen/QwQ-32B', reason: 'High-complexity task' };
|
|
186
|
+
return { model: 'meta-llama/Llama-3.3-70B', reason: 'Simple task' };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case 'copilot': {
|
|
190
|
+
return { model: 'GPT-4o', reason: 'Copilot uses GPT-4o (no model selection)' };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case 'perplexity': {
|
|
194
|
+
return { model: 'default', reason: 'Perplexity uses default model (no model selection)' };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
default: {
|
|
198
|
+
return { model: 'default', reason: `Unknown provider: ${provider}` };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* High-level function: analyze state and return optimal model selection for a turn.
|
|
205
|
+
* @param {object} state - Deliberation state with topic, log, speaker_roles
|
|
206
|
+
* @param {string} speaker - The current speaker identifier
|
|
207
|
+
* @param {string} provider - The AI provider to use
|
|
208
|
+
* @returns {{ model: string, category: string, complexity: string, reason: string }}
|
|
209
|
+
*/
|
|
210
|
+
export function getModelSelectionForTurn(state, speaker, provider) {
|
|
211
|
+
const topic = state.topic || '';
|
|
212
|
+
const role = (state.speaker_roles && state.speaker_roles[speaker]) || '';
|
|
213
|
+
|
|
214
|
+
// Use recent log entries (last 5) for context analysis
|
|
215
|
+
const recentEntries = Array.isArray(state.log) ? state.log.slice(-5) : [];
|
|
216
|
+
const recentLog = recentEntries
|
|
217
|
+
.map(entry => (typeof entry === 'string' ? entry : JSON.stringify(entry)))
|
|
218
|
+
.join(' ');
|
|
219
|
+
|
|
220
|
+
const { category, complexity } = analyzePromptContext(topic, recentLog, role);
|
|
221
|
+
const { model, reason } = selectModelForProvider(provider, category, complexity);
|
|
222
|
+
|
|
223
|
+
return { model, category, complexity, reason };
|
|
224
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dmsdc-ai/aigentry-deliberation",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "MCP Deliberation Server — Multi-session AI deliberation with smart speaker ordering and persona roles",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"index.js",
|
|
32
|
+
"model-router.js",
|
|
32
33
|
"install.js",
|
|
33
34
|
"doctor.js",
|
|
34
35
|
"observer.js",
|