sigma-agents 0.1.7 → 0.1.9

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/src/router.ts CHANGED
@@ -1,138 +1,277 @@
1
- import { RoutingConfig, TaskCategory } from './types.js';
2
- import { readFile } from 'node:fs/promises';
1
+ import { readFile } from "node:fs/promises";
2
+ import type { RoutingConfig, TaskCategory } from "./types.js";
3
3
 
4
4
  export class SmartRouter {
5
- private config: RoutingConfig;
6
-
7
- constructor(config: RoutingConfig) {
8
- this.config = config;
9
- }
10
-
11
- /**
12
- * Analyse le prompt et retourne la catégorie de tâche
13
- * Priorité : debug > code > plan > review > test > explore > general
14
- */
15
- classifyTask(prompt: string): TaskCategory {
16
- const lowerPrompt = prompt.toLowerCase();
17
- const categories: TaskCategory[] = [];
18
-
19
- // Check each category
20
- for (const [category, route] of Object.entries(this.config.routes)) {
21
- const hasKeyword = route.keywords.some(keyword =>
22
- lowerPrompt.includes(keyword.toLowerCase())
23
- );
24
-
25
- if (hasKeyword) {
26
- categories.push(category as TaskCategory);
27
- }
28
- }
29
-
30
- if (categories.length === 0) {
31
- return 'general';
32
- }
33
-
34
- // Apply priorities
35
- const priorityOrder: TaskCategory[] = ['debug', 'code', 'plan', 'review', 'test', 'explore', 'general'];
36
-
37
- for (const priority of priorityOrder) {
38
- if (categories.includes(priority)) {
39
- return priority;
40
- }
41
- }
42
-
43
- return categories[0];
44
- }
45
-
46
- /**
47
- * Retourne le modèle et l'agent recommandés pour un prompt
48
- */
49
- getRecommendation(prompt: string): { model: string; agent: string | null; category: TaskCategory } {
50
- const category = this.classifyTask(prompt);
51
- const route = this.config.routes[category];
52
-
53
- if (!route) {
54
- return {
55
- model: this.config.default.model,
56
- agent: this.config.default.agent,
57
- category
58
- };
59
- }
60
-
61
- return {
62
- model: route.preferredModel,
63
- agent: route.agent,
64
- category
65
- };
66
- }
67
-
68
- /**
69
- * Charge la configuration depuis un fichier JSON
70
- */
71
- static async loadConfig(configPath: string): Promise<RoutingConfig> {
72
- try {
73
- const content = await readFile(configPath, 'utf8');
74
- return JSON.parse(content);
75
- } catch {
76
- // File doesn't exist yet — use defaults silently
77
- return SmartRouter.defaultConfig();
78
- }
79
- }
80
-
81
- /**
82
- * Default configuration with provider-agnostic model names.
83
- * Uses 'default' as model placeholder — the actual model is determined
84
- * at runtime by /phi-init or the user's routing.json.
85
- */
86
- static defaultConfig(): RoutingConfig {
87
- return {
88
- routes: {
89
- code: {
90
- preferredModel: 'default',
91
- fallback: 'default',
92
- agent: null,
93
- keywords: ['code', 'implement', 'write', 'create', 'build', 'développer', 'coder', 'programmer', 'function', 'class', 'method']
94
- },
95
- debug: {
96
- preferredModel: 'default',
97
- fallback: 'default',
98
- agent: null,
99
- keywords: ['debug', 'fix', 'error', 'bug', 'broken', 'issue', 'problem', 'repair', 'correct', 'erreur', 'problème', 'réparer']
100
- },
101
- explore: {
102
- preferredModel: 'default',
103
- fallback: 'default',
104
- agent: null,
105
- keywords: ['explore', 'understand', 'analyze', 'examine', 'investigate', 'study', 'review', 'explorer', 'analyser', 'comprendre']
106
- },
107
- plan: {
108
- preferredModel: 'default',
109
- fallback: 'default',
110
- agent: null,
111
- keywords: ['plan', 'design', 'architecture', 'strategy', 'approach', 'structure', 'organize', 'concevoir', 'planifier', 'architecture']
112
- },
113
- test: {
114
- preferredModel: 'default',
115
- fallback: 'default',
116
- agent: null,
117
- keywords: ['test', 'testing', 'unit', 'integration', 'verify', 'validate', 'check', 'tester', 'vérifier', 'valider']
118
- },
119
- review: {
120
- preferredModel: 'default',
121
- fallback: 'default',
122
- agent: null,
123
- keywords: ['review', 'audit', 'check', 'validate', 'quality', 'improve', 'optimize', 'réviser', 'améliorer', 'optimiser']
124
- },
125
- general: {
126
- preferredModel: 'default',
127
- fallback: 'default',
128
- agent: null,
129
- keywords: ['help', 'explain', 'what', 'how', 'why', 'question', 'aide', 'expliquer', 'comment', 'pourquoi']
130
- }
131
- },
132
- default: {
133
- model: 'default',
134
- agent: null
135
- }
136
- };
137
- }
138
- }
5
+ private config: RoutingConfig;
6
+
7
+ constructor(config: RoutingConfig) {
8
+ this.config = config;
9
+ }
10
+
11
+ /**
12
+ * Analyse le prompt et retourne la catégorie de tâche
13
+ * Priorité : debug > code > plan > review > test > explore > general
14
+ */
15
+ classifyTask(prompt: string): TaskCategory {
16
+ const lowerPrompt = prompt.toLowerCase();
17
+ const categories: TaskCategory[] = [];
18
+
19
+ if (!this.config?.routes) {
20
+ return "general";
21
+ }
22
+
23
+ // Check each category
24
+ for (const [category, route] of Object.entries(this.config.routes)) {
25
+ if (!Array.isArray(route?.keywords)) continue;
26
+ const hasKeyword = route.keywords.some((keyword) => lowerPrompt.includes(keyword.toLowerCase()));
27
+
28
+ if (hasKeyword) {
29
+ categories.push(category as TaskCategory);
30
+ }
31
+ }
32
+
33
+ if (categories.length === 0) {
34
+ return "general";
35
+ }
36
+
37
+ // Apply priorities
38
+ const priorityOrder: TaskCategory[] = ["debug", "code", "plan", "review", "test", "explore", "general"];
39
+
40
+ for (const priority of priorityOrder) {
41
+ if (categories.includes(priority)) {
42
+ return priority;
43
+ }
44
+ }
45
+
46
+ return categories[0];
47
+ }
48
+
49
+ /**
50
+ * Retourne le modèle et l'agent recommandés pour un prompt
51
+ */
52
+ getRecommendation(prompt: string): { model: string; agent: string | null; category: TaskCategory } {
53
+ const category = this.classifyTask(prompt);
54
+ const route = this.config.routes[category];
55
+
56
+ if (!route) {
57
+ return {
58
+ model: this.config.default.model,
59
+ agent: this.config.default.agent,
60
+ category,
61
+ };
62
+ }
63
+
64
+ return {
65
+ model: route.preferredModel,
66
+ agent: route.agent,
67
+ category,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Charge la configuration depuis un fichier JSON
73
+ */
74
+ static async loadConfig(configPath: string): Promise<RoutingConfig> {
75
+ try {
76
+ const content = await readFile(configPath, "utf8");
77
+ const parsed: unknown = JSON.parse(content);
78
+
79
+ if (!SmartRouter.validateRoutingConfig(parsed)) {
80
+ console.warn(
81
+ `Invalid routing config structure in ${configPath}; falling back to defaults`,
82
+ );
83
+ return SmartRouter.defaultConfig();
84
+ }
85
+
86
+ return parsed;
87
+ } catch {
88
+ // File doesn't exist yet — use defaults silently
89
+ return SmartRouter.defaultConfig();
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Valide la structure d'une RoutingConfig chargée depuis un fichier non fiable.
95
+ * Fail-safe : retourne false sur toute incohérence structurelle.
96
+ */
97
+ static validateRoutingConfig(config: unknown): config is RoutingConfig {
98
+ if (typeof config !== "object" || config === null) {
99
+ return false;
100
+ }
101
+
102
+ const candidate = config as Record<string, unknown>;
103
+
104
+ const routes = candidate.routes;
105
+ if (typeof routes !== "object" || routes === null) {
106
+ return false;
107
+ }
108
+
109
+ for (const route of Object.values(routes as Record<string, unknown>)) {
110
+ if (typeof route !== "object" || route === null) {
111
+ return false;
112
+ }
113
+ const r = route as Record<string, unknown>;
114
+ if (typeof r.preferredModel !== "string" || typeof r.fallback !== "string") {
115
+ return false;
116
+ }
117
+ if (!(r.agent === null || typeof r.agent === "string")) {
118
+ return false;
119
+ }
120
+ if (!Array.isArray(r.keywords) || !r.keywords.every((k) => typeof k === "string")) {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ const defaults = candidate.default;
126
+ if (typeof defaults !== "object" || defaults === null) {
127
+ return false;
128
+ }
129
+ const d = defaults as Record<string, unknown>;
130
+ if (typeof d.model !== "string") {
131
+ return false;
132
+ }
133
+ if (!(d.agent === null || typeof d.agent === "string")) {
134
+ return false;
135
+ }
136
+
137
+ return true;
138
+ }
139
+
140
+ /**
141
+ * Default configuration with provider-agnostic model names.
142
+ * Uses 'default' as model placeholder — the actual model is determined
143
+ * at runtime by /phi-init or the user's routing.json.
144
+ */
145
+ static defaultConfig(): RoutingConfig {
146
+ return {
147
+ routes: {
148
+ code: {
149
+ preferredModel: "default",
150
+ fallback: "default",
151
+ agent: null,
152
+ keywords: [
153
+ "code",
154
+ "implement",
155
+ "write",
156
+ "create",
157
+ "build",
158
+ "développer",
159
+ "coder",
160
+ "programmer",
161
+ "function",
162
+ "class",
163
+ "method",
164
+ ],
165
+ },
166
+ debug: {
167
+ preferredModel: "default",
168
+ fallback: "default",
169
+ agent: null,
170
+ keywords: [
171
+ "debug",
172
+ "fix",
173
+ "error",
174
+ "bug",
175
+ "broken",
176
+ "issue",
177
+ "problem",
178
+ "repair",
179
+ "correct",
180
+ "erreur",
181
+ "problème",
182
+ "réparer",
183
+ ],
184
+ },
185
+ explore: {
186
+ preferredModel: "default",
187
+ fallback: "default",
188
+ agent: null,
189
+ keywords: [
190
+ "explore",
191
+ "understand",
192
+ "analyze",
193
+ "examine",
194
+ "investigate",
195
+ "study",
196
+ "review",
197
+ "explorer",
198
+ "analyser",
199
+ "comprendre",
200
+ ],
201
+ },
202
+ plan: {
203
+ preferredModel: "default",
204
+ fallback: "default",
205
+ agent: null,
206
+ keywords: [
207
+ "plan",
208
+ "design",
209
+ "architecture",
210
+ "strategy",
211
+ "approach",
212
+ "structure",
213
+ "organize",
214
+ "concevoir",
215
+ "planifier",
216
+ "architecture",
217
+ ],
218
+ },
219
+ test: {
220
+ preferredModel: "default",
221
+ fallback: "default",
222
+ agent: null,
223
+ keywords: [
224
+ "test",
225
+ "testing",
226
+ "unit",
227
+ "integration",
228
+ "verify",
229
+ "validate",
230
+ "check",
231
+ "tester",
232
+ "vérifier",
233
+ "valider",
234
+ ],
235
+ },
236
+ review: {
237
+ preferredModel: "default",
238
+ fallback: "default",
239
+ agent: null,
240
+ keywords: [
241
+ "review",
242
+ "audit",
243
+ "check",
244
+ "validate",
245
+ "quality",
246
+ "improve",
247
+ "optimize",
248
+ "réviser",
249
+ "améliorer",
250
+ "optimiser",
251
+ ],
252
+ },
253
+ general: {
254
+ preferredModel: "default",
255
+ fallback: "default",
256
+ agent: null,
257
+ keywords: [
258
+ "help",
259
+ "explain",
260
+ "what",
261
+ "how",
262
+ "why",
263
+ "question",
264
+ "aide",
265
+ "expliquer",
266
+ "comment",
267
+ "pourquoi",
268
+ ],
269
+ },
270
+ },
271
+ default: {
272
+ model: "default",
273
+ agent: null,
274
+ },
275
+ };
276
+ }
277
+ }