@lazyingart/agintiflow 0.18.0 → 0.19.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.
@@ -69,6 +69,83 @@ const COMPLEX_ROUTE_HINTS = [
69
69
 
70
70
  export const ROUTING_MODES = ["smart", "fast", "complex", "manual"];
71
71
 
72
+ export const MODEL_PROVIDER_GROUPS = {
73
+ deepseek: {
74
+ label: "DeepSeek",
75
+ provider: "deepseek",
76
+ role: "default route/main",
77
+ description: "Primary low-cost coding route. Flash is used for fast planning; Pro is used for complex execution.",
78
+ },
79
+ openai: {
80
+ label: "OpenAI",
81
+ provider: "openai",
82
+ role: "spare/frontier",
83
+ description: "Optional frontier spare or direct manual route, usually with explicit reasoning effort.",
84
+ },
85
+ qwen: {
86
+ label: "Qwen",
87
+ provider: "qwen",
88
+ role: "regional/general",
89
+ description: "DashScope/OpenAI-compatible Qwen route for Chinese and general work.",
90
+ },
91
+ "venice-uncensored": {
92
+ label: "Venice Uncensored",
93
+ provider: "venice",
94
+ role: "alternate text",
95
+ description: "Venice uncensored text models for optional manual routes.",
96
+ },
97
+ "venice-gpt": {
98
+ label: "Venice GPT",
99
+ provider: "venice",
100
+ role: "alternate GPT",
101
+ description: "GPT-family models routed through Venice.",
102
+ },
103
+ "venice-claude": {
104
+ label: "Venice Claude",
105
+ provider: "venice",
106
+ role: "alternate Claude",
107
+ description: "Claude-family models routed through Venice.",
108
+ },
109
+ "venice-gemma": {
110
+ label: "Venice Gemma",
111
+ provider: "venice",
112
+ role: "alternate Gemma",
113
+ description: "Gemma-family Venice models.",
114
+ },
115
+ "venice-qwen": {
116
+ label: "Venice Qwen",
117
+ provider: "venice",
118
+ role: "alternate Qwen",
119
+ description: "Qwen-family Venice models.",
120
+ },
121
+ };
122
+
123
+ export const AUXILIARY_MODEL_CATALOG = {
124
+ grsai: [
125
+ {
126
+ id: "nano-banana-2",
127
+ label: "Nano Banana 2",
128
+ type: "image",
129
+ description: "Default auxiliary image-generation route through GRS AI-compatible APIs.",
130
+ },
131
+ {
132
+ id: "gpt-image-2",
133
+ label: "GPT Image 2",
134
+ type: "image",
135
+ description: "High-quality image generation when available through the configured auxiliary endpoint.",
136
+ },
137
+ ],
138
+ "venice-image": [
139
+ { id: "nano-banana-2", label: "Nano Banana 2", type: "image", price: "$0.10/image" },
140
+ { id: "gpt-image-2", label: "GPT Image 2", type: "image", price: "$0.27/image" },
141
+ { id: "gpt-image-2-edit", label: "GPT Image 2 Edit", type: "inpaint", price: "$0.36/edit" },
142
+ { id: "wan-2-7-pro-edit", label: "Wan 2.7 Pro Edit", type: "inpaint", price: "$0.09/edit" },
143
+ { id: "grok-imagine-image", label: "Grok Imagine", type: "image", price: "$0.03/image" },
144
+ { id: "qwen-image-2-pro", label: "Qwen Image 2 Pro", type: "image", price: "$0.10/image" },
145
+ { id: "bria-bg-remover", label: "Background Remover", type: "image", price: "$0.03/image" },
146
+ ],
147
+ };
148
+
72
149
  export const PROVIDER_MODEL_CATALOG = {
73
150
  deepseek: [
74
151
  {
@@ -250,20 +327,35 @@ export function getProviderDefaults(provider = "deepseek") {
250
327
  };
251
328
  }
252
329
 
253
- export function getModelPresets() {
330
+ function providerDefaultModel(provider, fallback) {
331
+ if (provider === "deepseek" && fallback) return fallback;
332
+ return getProviderDefaults(provider).model || fallback;
333
+ }
334
+
335
+ export function getModelPresets(overrides = {}) {
336
+ const routeProvider = overrides.routeProvider || process.env.AGINTI_ROUTE_PROVIDER || "deepseek";
337
+ const mainProvider = overrides.mainProvider || process.env.AGINTI_MAIN_PROVIDER || "deepseek";
254
338
  return {
255
339
  fast: {
256
340
  id: "fast",
257
341
  label: "Fast base",
258
- provider: "deepseek",
259
- model: process.env.DEEPSEEK_FAST_MODEL || "deepseek-v4-flash",
342
+ provider: routeProvider,
343
+ model:
344
+ overrides.routeModel ||
345
+ process.env.AGINTI_ROUTE_MODEL ||
346
+ process.env.DEEPSEEK_FAST_MODEL ||
347
+ providerDefaultModel(routeProvider, "deepseek-v4-flash"),
260
348
  description: "Default fast route for normal browser, shell, and short coding tasks.",
261
349
  },
262
350
  complex: {
263
351
  id: "complex",
264
352
  label: "Complex reasoning",
265
- provider: "deepseek",
266
- model: process.env.DEEPSEEK_PRO_MODEL || "deepseek-v4-pro",
353
+ provider: mainProvider,
354
+ model:
355
+ overrides.mainModel ||
356
+ process.env.AGINTI_MAIN_MODEL ||
357
+ process.env.DEEPSEEK_PRO_MODEL ||
358
+ providerDefaultModel(mainProvider, "deepseek-v4-pro"),
267
359
  description: "Higher-capacity DeepSeek route for multi-step coding and design tasks.",
268
360
  },
269
361
  mock: {
@@ -367,8 +459,9 @@ export function getModelPresets() {
367
459
  id: "codexPrimary",
368
460
  label: "Codex primary wrapper",
369
461
  provider: "codex-wrapper",
370
- model: process.env.CODEX_PRIMARY_MODEL || "gpt-5.5",
371
- reasoning: process.env.CODEX_PRIMARY_REASONING || "medium",
462
+ model: overrides.wrapperModel || process.env.AGINTI_WRAPPER_MODEL || process.env.CODEX_PRIMARY_MODEL || "gpt-5.5",
463
+ reasoning:
464
+ overrides.wrapperReasoning || process.env.AGINTI_WRAPPER_REASONING || process.env.CODEX_PRIMARY_REASONING || "medium",
372
465
  description: "External Codex wrapper route for coding enhancement tasks.",
373
466
  },
374
467
  codexSpare: {
@@ -382,6 +475,65 @@ export function getModelPresets() {
382
475
  };
383
476
  }
384
477
 
478
+ export function getModelRoleDefaults(overrides = {}) {
479
+ const presets = getModelPresets(overrides);
480
+ const spareProvider = overrides.spareProvider || process.env.AGINTI_SPARE_PROVIDER || "openai";
481
+ const spareModel = overrides.spareModel || process.env.AGINTI_SPARE_MODEL || "gpt-5.4";
482
+ const auxiliaryProvider = overrides.auxiliaryProvider || process.env.AGINTI_AUX_PROVIDER || "grsai";
483
+ const auxiliaryModel = overrides.auxiliaryModel || process.env.AGINTI_AUX_MODEL || process.env.VENICE_IMAGE_MODEL || "nano-banana-2";
484
+ return {
485
+ route: {
486
+ id: "route",
487
+ label: "Route model",
488
+ command: "/route",
489
+ provider: presets.fast.provider,
490
+ model: presets.fast.model,
491
+ description: "Fast planner and triage model. Default: DeepSeek V4 Flash.",
492
+ },
493
+ main: {
494
+ id: "main",
495
+ label: "Main model",
496
+ command: "/model",
497
+ provider: presets.complex.provider,
498
+ model: presets.complex.model,
499
+ description: "Complex executor for coding, debugging, writing, and long tasks. Default: DeepSeek V4 Pro.",
500
+ },
501
+ spare: {
502
+ id: "spare",
503
+ label: "Spare model",
504
+ command: "/spare",
505
+ provider: spareProvider,
506
+ model: spareModel,
507
+ reasoning: overrides.spareReasoning || process.env.AGINTI_SPARE_REASONING || "medium",
508
+ description: "Fallback or cross-check model. Default: OpenAI GPT-5.4 medium reasoning.",
509
+ },
510
+ wrapper: {
511
+ id: "wrapper",
512
+ label: "Wrapper",
513
+ command: "/wrapper",
514
+ provider: "codex",
515
+ model: presets.codexPrimary.model,
516
+ reasoning: presets.codexPrimary.reasoning,
517
+ description: "External coding wrapper when enabled. Default: Codex with GPT-5.5 medium reasoning.",
518
+ },
519
+ auxiliary: {
520
+ id: "auxiliary",
521
+ label: "Auxiliary",
522
+ command: "/auxilliary",
523
+ provider: auxiliaryProvider,
524
+ model: auxiliaryModel,
525
+ description: "Image/media tools. Default: GRS AI/Nano Banana; Venice image is optional.",
526
+ },
527
+ };
528
+ }
529
+
530
+ export function modelsForProviderGroup(groupId) {
531
+ const group = MODEL_PROVIDER_GROUPS[groupId];
532
+ if (!group) return [];
533
+ if (group.provider !== "venice") return PROVIDER_MODEL_CATALOG[group.provider] || [];
534
+ return (PROVIDER_MODEL_CATALOG.venice || []).filter((item) => item.bucket === groupId);
535
+ }
536
+
385
537
  export function scoreTaskComplexity(goal = "", taskProfile = "auto") {
386
538
  const text = String(goal).toLowerCase();
387
539
  let score = text.length > 600 ? 2 : text.length > 240 ? 1 : 0;
@@ -399,10 +551,20 @@ export function normalizeRoutingMode(value) {
399
551
  return ROUTING_MODES.includes(value) ? value : "smart";
400
552
  }
401
553
 
402
- export function selectModelRoute({ routingMode = "smart", provider = "deepseek", model = "", goal = "", taskProfile = "auto" } = {}) {
554
+ export function selectModelRoute({
555
+ routingMode = "smart",
556
+ provider = "deepseek",
557
+ model = "",
558
+ goal = "",
559
+ taskProfile = "auto",
560
+ routeProvider = "",
561
+ routeModel = "",
562
+ mainProvider = "",
563
+ mainModel = "",
564
+ } = {}) {
403
565
  const mode = normalizeRoutingMode(routingMode);
404
566
  const requestedProvider = String(provider || "deepseek").toLowerCase();
405
- const presets = getModelPresets();
567
+ const presets = getModelPresets({ routeProvider, routeModel, mainProvider, mainModel });
406
568
 
407
569
  if (requestedProvider === "mock") {
408
570
  const defaults = getProviderDefaults("mock");
@@ -52,7 +52,10 @@ function codexArgs(prompt, config, preset) {
52
52
  }
53
53
 
54
54
  function wrapperCommand(wrapper, prompt, config, { fallback = false } = {}) {
55
- const presets = getModelPresets();
55
+ const presets = getModelPresets({
56
+ wrapperModel: config.wrapperModel,
57
+ wrapperReasoning: config.wrapperReasoning,
58
+ });
56
59
  switch (wrapper) {
57
60
  case "codex":
58
61
  return {
package/src/web-db.js CHANGED
@@ -1,17 +1,29 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { DatabaseSync } from "node:sqlite";
4
- import { getModelPresets } from "./model-routing.js";
4
+ import { getModelPresets, getModelRoleDefaults } from "./model-routing.js";
5
5
 
6
- const PREFERENCES_SCHEMA_VERSION = 6;
6
+ const PREFERENCES_SCHEMA_VERSION = 7;
7
7
 
8
8
  function defaultPreferences(baseDir) {
9
9
  const presets = getModelPresets();
10
+ const roles = getModelRoleDefaults();
10
11
  return {
11
12
  preferencesSchemaVersion: PREFERENCES_SCHEMA_VERSION,
12
13
  routingMode: "smart",
13
14
  provider: "deepseek",
14
15
  model: presets.fast.model,
16
+ routeProvider: roles.route.provider,
17
+ routeModel: roles.route.model,
18
+ mainProvider: roles.main.provider,
19
+ mainModel: roles.main.model,
20
+ spareProvider: roles.spare.provider,
21
+ spareModel: roles.spare.model,
22
+ spareReasoning: roles.spare.reasoning,
23
+ wrapperModel: roles.wrapper.model,
24
+ wrapperReasoning: roles.wrapper.reasoning,
25
+ auxiliaryProvider: roles.auxiliary.provider,
26
+ auxiliaryModel: roles.auxiliary.model,
15
27
  headless: true,
16
28
  maxSteps: 24,
17
29
  startUrl: "",
@@ -107,6 +119,20 @@ export class WebDatabase {
107
119
  preferences.allowParallelScouts = true;
108
120
  preferences.parallelScoutCount = 3;
109
121
  }
122
+ if ((parsed.preferencesSchemaVersion || 1) < 7) {
123
+ const roles = getModelRoleDefaults();
124
+ preferences.routeProvider = preferences.routeProvider || roles.route.provider;
125
+ preferences.routeModel = preferences.routeModel || roles.route.model;
126
+ preferences.mainProvider = preferences.mainProvider || roles.main.provider;
127
+ preferences.mainModel = preferences.mainModel || roles.main.model;
128
+ preferences.spareProvider = preferences.spareProvider || roles.spare.provider;
129
+ preferences.spareModel = preferences.spareModel || roles.spare.model;
130
+ preferences.spareReasoning = preferences.spareReasoning || roles.spare.reasoning;
131
+ preferences.wrapperModel = preferences.wrapperModel || roles.wrapper.model;
132
+ preferences.wrapperReasoning = preferences.wrapperReasoning || roles.wrapper.reasoning;
133
+ preferences.auxiliaryProvider = preferences.auxiliaryProvider || roles.auxiliary.provider;
134
+ preferences.auxiliaryModel = preferences.auxiliaryModel || roles.auxiliary.model;
135
+ }
110
136
  this.savePreferences(preferences);
111
137
  }
112
138
  return preferences;
package/web.js CHANGED
@@ -6,7 +6,15 @@ import { runAgent } from "./src/agent-runner.js";
6
6
  import { resolveRuntimeConfig } from "./src/config.js";
7
7
  import { WebDatabase } from "./src/web-db.js";
8
8
  import { SessionStore } from "./src/session-store.js";
9
- import { PROVIDER_MODEL_CATALOG, getModelPresets, getProviderDefaults, normalizeRoutingMode } from "./src/model-routing.js";
9
+ import {
10
+ AUXILIARY_MODEL_CATALOG,
11
+ MODEL_PROVIDER_GROUPS,
12
+ PROVIDER_MODEL_CATALOG,
13
+ getModelPresets,
14
+ getModelRoleDefaults,
15
+ getProviderDefaults,
16
+ normalizeRoutingMode,
17
+ } from "./src/model-routing.js";
10
18
  import { listAgentWrappers, normalizeWrapperName } from "./src/tool-wrappers.js";
11
19
  import { getDockerSandboxStatus, getSandboxLogs, runDockerPreflight } from "./src/docker-sandbox.js";
12
20
  import { normalizePackageInstallPolicy, normalizeSandboxMode } from "./src/command-policy.js";
@@ -113,6 +121,7 @@ function serializeRun(run) {
113
121
 
114
122
  function normalizePreferencePayload(body = {}, current = db.getPreferences()) {
115
123
  const modelPresets = getModelPresets();
124
+ const modelRoles = getModelRoleDefaults();
116
125
  const providerCandidate = body.provider || current.provider || "deepseek";
117
126
  const provider = ["openai", "deepseek", "qwen", "venice", "mock"].includes(providerCandidate) ? providerCandidate : "deepseek";
118
127
  const routingMode =
@@ -132,6 +141,50 @@ function normalizePreferencePayload(body = {}, current = db.getPreferences()) {
132
141
  : current.provider !== provider
133
142
  ? providerDefaults.model
134
143
  : current.model || modelPresets.fast.model || providerDefaults.model,
144
+ routeProvider:
145
+ typeof body.routeProvider === "string" && body.routeProvider.trim()
146
+ ? body.routeProvider.trim()
147
+ : current.routeProvider || modelRoles.route.provider,
148
+ routeModel:
149
+ typeof body.routeModel === "string" && body.routeModel.trim()
150
+ ? body.routeModel.trim()
151
+ : current.routeModel || modelRoles.route.model,
152
+ mainProvider:
153
+ typeof body.mainProvider === "string" && body.mainProvider.trim()
154
+ ? body.mainProvider.trim()
155
+ : current.mainProvider || modelRoles.main.provider,
156
+ mainModel:
157
+ typeof body.mainModel === "string" && body.mainModel.trim()
158
+ ? body.mainModel.trim()
159
+ : current.mainModel || modelRoles.main.model,
160
+ spareProvider:
161
+ typeof body.spareProvider === "string" && body.spareProvider.trim()
162
+ ? body.spareProvider.trim()
163
+ : current.spareProvider || modelRoles.spare.provider,
164
+ spareModel:
165
+ typeof body.spareModel === "string" && body.spareModel.trim()
166
+ ? body.spareModel.trim()
167
+ : current.spareModel || modelRoles.spare.model,
168
+ spareReasoning:
169
+ typeof body.spareReasoning === "string" && body.spareReasoning.trim()
170
+ ? body.spareReasoning.trim()
171
+ : current.spareReasoning || modelRoles.spare.reasoning,
172
+ wrapperModel:
173
+ typeof body.wrapperModel === "string" && body.wrapperModel.trim()
174
+ ? body.wrapperModel.trim()
175
+ : current.wrapperModel || modelRoles.wrapper.model,
176
+ wrapperReasoning:
177
+ typeof body.wrapperReasoning === "string" && body.wrapperReasoning.trim()
178
+ ? body.wrapperReasoning.trim()
179
+ : current.wrapperReasoning || modelRoles.wrapper.reasoning,
180
+ auxiliaryProvider:
181
+ typeof body.auxiliaryProvider === "string" && body.auxiliaryProvider.trim()
182
+ ? body.auxiliaryProvider.trim()
183
+ : current.auxiliaryProvider || modelRoles.auxiliary.provider,
184
+ auxiliaryModel:
185
+ typeof body.auxiliaryModel === "string" && body.auxiliaryModel.trim()
186
+ ? body.auxiliaryModel.trim()
187
+ : current.auxiliaryModel || modelRoles.auxiliary.model,
135
188
  headless: typeof body.headless === "boolean" ? body.headless : Boolean(current.headless),
136
189
  maxSteps: Number.isFinite(parsedMaxSteps) && parsedMaxSteps > 0 ? parsedMaxSteps : Number(current.maxSteps) || 24,
137
190
  startUrl: typeof body.startUrl === "string" ? body.startUrl.trim() : current.startUrl || "",
@@ -223,6 +276,17 @@ function buildRunConfig(body, overrides = {}) {
223
276
  provider: merged.provider,
224
277
  model: merged.model || getProviderDefaults(merged.provider).model,
225
278
  routingMode: merged.routingMode,
279
+ routeProvider: merged.routeProvider,
280
+ routeModel: merged.routeModel,
281
+ mainProvider: merged.mainProvider,
282
+ mainModel: merged.mainModel,
283
+ spareProvider: merged.spareProvider,
284
+ spareModel: merged.spareModel,
285
+ spareReasoning: merged.spareReasoning,
286
+ wrapperModel: merged.wrapperModel,
287
+ wrapperReasoning: merged.wrapperReasoning,
288
+ auxiliaryProvider: merged.auxiliaryProvider,
289
+ auxiliaryModel: merged.auxiliaryModel,
226
290
  headless: merged.headless,
227
291
  maxSteps: merged.maxSteps,
228
292
  allowedDomains: String(merged.allowedDomains || "")
@@ -586,6 +650,9 @@ app.get("/api/config", async (_req, res) => {
586
650
  maxSteps: 24,
587
651
  },
588
652
  modelCatalog: PROVIDER_MODEL_CATALOG,
653
+ modelGroups: MODEL_PROVIDER_GROUPS,
654
+ auxiliaryModelCatalog: AUXILIARY_MODEL_CATALOG,
655
+ modelRoles: getModelRoleDefaults(preferences),
589
656
  taskProfiles: listTaskProfiles(),
590
657
  skills: listSkills(),
591
658
  routing: {