@lazyingart/agintiflow 0.20.3 → 0.20.5
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/docs/model-selection.md +7 -4
- package/package.json +1 -1
- package/references/model-routing-provider-design.md +2 -2
- package/references/venice-model-reference.md +5 -2
- package/scripts/smoke-cli-chat.js +17 -1
- package/scripts/smoke-model-roles.js +18 -1
- package/src/interactive-cli.js +152 -24
- package/src/model-client.js +55 -1
- package/src/model-routing.js +9 -2
package/docs/model-selection.md
CHANGED
|
@@ -48,14 +48,17 @@ Interactive commands:
|
|
|
48
48
|
|
|
49
49
|
In the interactive CLI, `/provider`, `/route`, `/model`, `/spare`, and `/auxiliary model` without arguments open selectors. Use Up/Down/Left/Right to move through choices, Enter to confirm, and Esc to cancel. Slash-command hints use the same arrow selection behavior: type a prefix such as `/mo`, use arrows to choose `/model` or `/models`, then press Enter or Tab.
|
|
50
50
|
|
|
51
|
-
`/venice`
|
|
51
|
+
`/venice` opens a two-step selector for the Venice route and main models. The current text choices are:
|
|
52
52
|
|
|
53
53
|
```text
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
venice/venice-uncensored-1-2
|
|
55
|
+
venice/e2ee-venice-uncensored-24b-p
|
|
56
|
+
venice/gemma-4-uncensored
|
|
56
57
|
```
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
For scripts or non-interactive terminals, `/venice` uses Venice 1.2 for both roles. You can also set both roles directly with `/venice 1.2`, `/venice 1.1`, or `/venice gemma`. Use two values to set route and main separately, for example `/venice 1.2 gemma`.
|
|
60
|
+
|
|
61
|
+
Use `/venice off` to switch back to the DeepSeek defaults:
|
|
59
62
|
|
|
60
63
|
```text
|
|
61
64
|
/route deepseek/deepseek-v4-flash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a web-first coding agent and CLI with DeepSeek routing, sandboxed tools, model providers, canvas artifacts, and optional wrappers.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -53,7 +53,7 @@ Interactive equivalents:
|
|
|
53
53
|
/auxiliary model grsai/nano-banana-2
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
`/venice`
|
|
56
|
+
`/venice` opens a route/main selector for Venice text models. The selector includes `venice/venice-uncensored-1-2` (Venice 1.2), `venice/e2ee-venice-uncensored-24b-p` (Venice 1.1), and `venice/gemma-4-uncensored` (Gemma 4). In non-interactive shells, `/venice` keeps script compatibility by selecting `venice/venice-uncensored-1-2` for both roles. `/venice 1.2 gemma` sets route to Venice 1.2 and main to Gemma 4; `/venice off` restores `deepseek/deepseek-v4-flash` for route and `deepseek/deepseek-v4-pro` for main.
|
|
57
57
|
|
|
58
58
|
The web UI should expose model names as dropdowns, not free-text fields. The left panel should stay focused on common daily controls, while model-role editing and less-used switches live in an Advanced settings modal. The terminal-like capability panels belong after the runtime log so the left control panel remains short.
|
|
59
59
|
|
|
@@ -74,7 +74,7 @@ Codex wrapper defaults stay separate from native OpenAI API settings: GPT-5.5 me
|
|
|
74
74
|
|
|
75
75
|
| UI bucket | Concrete default | Notes |
|
|
76
76
|
|---|---|---|
|
|
77
|
-
| `venice-uncensored` | `venice-uncensored-1-2` | Venice-native text |
|
|
77
|
+
| `venice-uncensored` | `venice-uncensored-1-2` | Venice-native text; `/venice` also exposes `e2ee-venice-uncensored-24b-p` as Venice 1.1 |
|
|
78
78
|
| `venice-qwen` | `qwen3-6-27b` | Qwen-family text/code |
|
|
79
79
|
| `venice-gpt` | `openai-gpt-55` | OpenAI-family through Venice |
|
|
80
80
|
| `venice-claude` | `claude-sonnet-4-6` | Claude-family through Venice |
|
|
@@ -19,7 +19,7 @@ Store `VENICE_API_KEY` only in ignored local files such as `.aginti/.env` or a s
|
|
|
19
19
|
|
|
20
20
|
| Bucket | Primary model | Other useful models | Use |
|
|
21
21
|
|---|---|---|---|
|
|
22
|
-
| `venice-uncensored` | `venice-uncensored-1-2` | `venice-uncensored`, `venice-uncensored-role-play` | Private/uncensored Venice-native text route |
|
|
22
|
+
| `venice-uncensored` | `venice-uncensored-1-2` | `e2ee-venice-uncensored-24b-p`, `venice-uncensored-role-play`, `venice-uncensored` legacy | Private/uncensored Venice-native text route |
|
|
23
23
|
| `venice-gemma` | `gemma-4-uncensored` | `google-gemma-4-31b-it`, `google-gemma-4-26b-a4b-it` | Low-cost open-model writing and experimentation |
|
|
24
24
|
| `venice-qwen` | `qwen3-6-27b` | `qwen-3-6-plus`, `qwen3-coder-480b-a35b-instruct-turbo`, `qwen3-vl-235b-a22b` | Chinese/open coding and general tasks |
|
|
25
25
|
| `venice-gpt` | `openai-gpt-55` | `openai-gpt-54`, `openai-gpt-54-mini`, `openai-gpt-53-codex`, `openai-gpt-53-codex-spark` | OpenAI-family routes through Venice |
|
|
@@ -44,12 +44,15 @@ Store `VENICE_API_KEY` only in ignored local files such as `.aginti/.env` or a s
|
|
|
44
44
|
| `google-gemma-4-26b-a4b-it` | 256K | Google Gemma 4 26B A4B Instruct |
|
|
45
45
|
| `venice-uncensored-1-2` | 128K | Venice Uncensored 1.2 |
|
|
46
46
|
| `openai-gpt-54-mini` | 400K | GPT-5.4 Mini |
|
|
47
|
+
| `e2ee-venice-uncensored-24b-p` | 32K | Venice Uncensored 1.1 E2EE |
|
|
47
48
|
| `venice-uncensored-role-play` | 128K | Venice Role Play Uncensored |
|
|
48
49
|
| `gemini-3-1-pro-preview` | 1.0M | Gemini 3.1 Pro Preview |
|
|
49
50
|
| `claude-sonnet-4-6` | 1.0M | Claude Sonnet 4.6 |
|
|
50
51
|
| `openai-gpt-53-codex` | 400K | GPT-5.3 Codex |
|
|
51
52
|
| `openai-gpt-52` | 256K | GPT-5.2 |
|
|
52
|
-
| `venice-uncensored` | 32K | Venice Uncensored legacy |
|
|
53
|
+
| `venice-uncensored` | 32K | Venice Uncensored legacy / deprecated |
|
|
54
|
+
|
|
55
|
+
AgInTiFlow exposes the primary Venice text choices through `/venice`: Venice 1.2 (`venice-uncensored-1-2`), Venice 1.1 (`e2ee-venice-uncensored-24b-p`), and Gemma 4 (`gemma-4-uncensored`). The command can select route and main independently, so a fast Venice route can be paired with a larger Gemma main model when useful. The older `venice-uncensored` ID is retained only as a legacy alias.
|
|
53
56
|
|
|
54
57
|
## Image And Edit Models
|
|
55
58
|
|
|
@@ -4,7 +4,14 @@ import fs from "node:fs/promises";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
buildLaunchHeaderLines,
|
|
9
|
+
buildPromptLayout,
|
|
10
|
+
canonicalSlashPromptBuffer,
|
|
11
|
+
classifyEscapeAction,
|
|
12
|
+
formatWorkspaceChange,
|
|
13
|
+
stripMarkdown,
|
|
14
|
+
} from "../src/interactive-cli.js";
|
|
8
15
|
|
|
9
16
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
17
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-cli-chat-"));
|
|
@@ -179,6 +186,14 @@ try {
|
|
|
179
186
|
if (classifyEscapeAction({ active: true, pendingAsap: [] }) !== "abort") {
|
|
180
187
|
throw new Error("active Esc should abort when no ASAP pipe messages are pending");
|
|
181
188
|
}
|
|
189
|
+
if (
|
|
190
|
+
canonicalSlashPromptBuffer("/ve") !== "/venice" ||
|
|
191
|
+
canonicalSlashPromptBuffer("/v") !== "/venice" ||
|
|
192
|
+
canonicalSlashPromptBuffer("/not-a-command") !== "/not-a-command" ||
|
|
193
|
+
canonicalSlashPromptBuffer("/venice off") !== "/venice off"
|
|
194
|
+
) {
|
|
195
|
+
throw new Error("slash command prompt canonicalization did not preserve final submitted command correctly");
|
|
196
|
+
}
|
|
182
197
|
|
|
183
198
|
await runCli(["init"], "");
|
|
184
199
|
const instructions = await fs.readFile(path.join(tempRoot, "AGINTI.md"), "utf8");
|
|
@@ -265,6 +280,7 @@ try {
|
|
|
265
280
|
"auxiliary-command-spelling",
|
|
266
281
|
"skills-command",
|
|
267
282
|
"slash-prefix-autoselect",
|
|
283
|
+
"slash-prefix-canonical-history",
|
|
268
284
|
"instructions-chat-edit",
|
|
269
285
|
"interactive-chat",
|
|
270
286
|
"mock-file-write",
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
modelsForProviderGroup,
|
|
10
10
|
selectModelRoute,
|
|
11
11
|
} from "../src/model-routing.js";
|
|
12
|
+
import { parseTextToolCalls } from "../src/model-client.js";
|
|
12
13
|
|
|
13
14
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
14
15
|
|
|
@@ -108,7 +109,12 @@ assert(fastRoute.model === "deepseek-v4-flash", "fast route did not use route mo
|
|
|
108
109
|
|
|
109
110
|
assert(MODEL_PROVIDER_GROUPS["venice-gpt"].provider === "venice", "venice-gpt group missing");
|
|
110
111
|
assert(modelsForProviderGroup("venice-gemma").some((item) => item.id === "gemma-4-uncensored"), "venice-gemma bucket missing Gemma");
|
|
112
|
+
assert(modelsForProviderGroup("venice-uncensored").some((item) => item.id === "e2ee-venice-uncensored-24b-p"), "venice-uncensored bucket missing Venice 1.1");
|
|
111
113
|
assert(AUXILIARY_MODEL_CATALOG["venice-image"].some((item) => item.id === "gpt-image-2"), "Venice image catalog missing GPT Image 2");
|
|
114
|
+
const parsedTextToolCalls = parseTextToolCalls('[TOOL_CALLS]list_files[ARGS]call_123[ARGS]{"path":".","maxDepth":1}');
|
|
115
|
+
assert(parsedTextToolCalls.length === 1, "Venice text tool-call parser did not detect encoded tool call");
|
|
116
|
+
assert(parsedTextToolCalls[0].function.name === "list_files", "Venice text tool-call parser returned wrong tool name");
|
|
117
|
+
assert(parsedTextToolCalls[0].function.arguments.includes('"maxDepth":1'), "Venice text tool-call parser returned wrong arguments");
|
|
112
118
|
|
|
113
119
|
const output = await runCli(["models"]);
|
|
114
120
|
assert(output.includes("/route") && output.includes("/spare") && output.includes("venice-gpt"), "aginti models output missing role details");
|
|
@@ -117,6 +123,9 @@ const interactiveOutput = await runInteractive("/venice\n");
|
|
|
117
123
|
assert(interactiveOutput.includes("venice=on"), "/venice did not enable Venice roles");
|
|
118
124
|
assert(interactiveOutput.includes("route=venice/venice-uncensored-1-2"), "/venice did not set Venice route role");
|
|
119
125
|
assert(interactiveOutput.includes("main=venice/venice-uncensored-1-2"), "/venice did not set Venice main role");
|
|
126
|
+
const interactiveGemmaOutput = await runInteractive("/venice 1.1 gemma\n");
|
|
127
|
+
assert(interactiveGemmaOutput.includes("route=venice/e2ee-venice-uncensored-24b-p"), "/venice 1.1 did not set Venice 1.1 route role");
|
|
128
|
+
assert(interactiveGemmaOutput.includes("main=venice/gemma-4-uncensored"), "/venice gemma did not set Gemma 4 main role");
|
|
120
129
|
const interactiveOffOutput = await runInteractive("/venice off\n");
|
|
121
130
|
assert(interactiveOffOutput.includes("venice=off"), "/venice off did not restore DeepSeek roles");
|
|
122
131
|
assert(interactiveOffOutput.includes("route=deepseek/deepseek-v4-flash"), "/venice off did not restore DeepSeek route role");
|
|
@@ -125,7 +134,15 @@ console.log(
|
|
|
125
134
|
JSON.stringify(
|
|
126
135
|
{
|
|
127
136
|
ok: true,
|
|
128
|
-
checks: [
|
|
137
|
+
checks: [
|
|
138
|
+
"role-defaults",
|
|
139
|
+
"route-overrides",
|
|
140
|
+
"provider-groups",
|
|
141
|
+
"auxiliary-catalog",
|
|
142
|
+
"venice-text-tool-parser",
|
|
143
|
+
"cli-models-command",
|
|
144
|
+
"venice-shortcut",
|
|
145
|
+
],
|
|
129
146
|
},
|
|
130
147
|
null,
|
|
131
148
|
2
|
package/src/interactive-cli.js
CHANGED
|
@@ -194,6 +194,15 @@ function resolveSlashCommand(command = "") {
|
|
|
194
194
|
return suggestion ? suggestion.slice(1) : raw;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
export function canonicalSlashPromptBuffer(value = "") {
|
|
198
|
+
const text = String(value || "");
|
|
199
|
+
const trimmed = text.trim();
|
|
200
|
+
if (!trimmed.startsWith("/") || /\s/.test(trimmed)) return text;
|
|
201
|
+
const raw = trimmed.slice(1);
|
|
202
|
+
const resolved = resolveSlashCommand(raw);
|
|
203
|
+
return resolved === raw ? text : `/${resolved}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
197
206
|
function clamp(value, min, max) {
|
|
198
207
|
return Math.min(Math.max(value, min), max);
|
|
199
208
|
}
|
|
@@ -584,7 +593,7 @@ function printHelp() {
|
|
|
584
593
|
" /instructions Show AGINTI.md project instructions status.",
|
|
585
594
|
" /memory Alias for /instructions.",
|
|
586
595
|
" /models Show route/main/spare/wrapper/auxiliary model roles.",
|
|
587
|
-
" /venice [
|
|
596
|
+
" /venice [off|model] Pick Venice route/main models, or restore DeepSeek defaults.",
|
|
588
597
|
" /route [mode|provider/model]",
|
|
589
598
|
" Open route selector, or set routing/fast route model.",
|
|
590
599
|
" /model [provider/model] Open main-model selector, or set the active/main model.",
|
|
@@ -944,6 +953,11 @@ function readTtyPrompt(options = {}) {
|
|
|
944
953
|
};
|
|
945
954
|
|
|
946
955
|
const submit = () => {
|
|
956
|
+
const canonical = canonicalSlashPromptBuffer(buffer);
|
|
957
|
+
if (canonical !== buffer) {
|
|
958
|
+
buffer = canonical;
|
|
959
|
+
cursor = buffer.length;
|
|
960
|
+
}
|
|
947
961
|
renderNow();
|
|
948
962
|
moveToPromptBottom(rendered);
|
|
949
963
|
cleanup();
|
|
@@ -1626,23 +1640,59 @@ function useDeepSeekDefaults(state) {
|
|
|
1626
1640
|
state.mainModel = "deepseek-v4-pro";
|
|
1627
1641
|
}
|
|
1628
1642
|
|
|
1629
|
-
function
|
|
1643
|
+
function veniceTextModelChoices() {
|
|
1644
|
+
return [
|
|
1645
|
+
{
|
|
1646
|
+
provider: "venice",
|
|
1647
|
+
model: "venice-uncensored-1-2",
|
|
1648
|
+
label: "Venice 1.2",
|
|
1649
|
+
description: "default Venice text model; 128K context",
|
|
1650
|
+
},
|
|
1651
|
+
{
|
|
1652
|
+
provider: "venice",
|
|
1653
|
+
model: "e2ee-venice-uncensored-24b-p",
|
|
1654
|
+
label: "Venice 1.1",
|
|
1655
|
+
description: "E2EE Venice text model; 32K context",
|
|
1656
|
+
},
|
|
1657
|
+
{
|
|
1658
|
+
provider: "venice",
|
|
1659
|
+
model: "gemma-4-uncensored",
|
|
1660
|
+
label: "Gemma 4",
|
|
1661
|
+
description: "Gemma-family Venice text model; 256K context",
|
|
1662
|
+
},
|
|
1663
|
+
];
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
function resolveVeniceTextModel(value = "") {
|
|
1667
|
+
const normalized = String(value).trim().toLowerCase();
|
|
1668
|
+
if (!normalized || normalized === "on" || normalized === "venice" || normalized === "1.2" || normalized === "venice-1.2") {
|
|
1669
|
+
return "venice-uncensored-1-2";
|
|
1670
|
+
}
|
|
1671
|
+
if (
|
|
1672
|
+
normalized === "1.1" ||
|
|
1673
|
+
normalized === "venice-1.1" ||
|
|
1674
|
+
normalized === "e2ee-venice-uncensored-24b-p"
|
|
1675
|
+
) {
|
|
1676
|
+
return "e2ee-venice-uncensored-24b-p";
|
|
1677
|
+
}
|
|
1678
|
+
if (normalized === "legacy" || normalized === "venice-uncensored") {
|
|
1679
|
+
return "venice-uncensored";
|
|
1680
|
+
}
|
|
1681
|
+
if (normalized === "gemma" || normalized === "gemma4" || normalized === "gemma-4" || normalized === "gemma-4-uncensored") {
|
|
1682
|
+
return "gemma-4-uncensored";
|
|
1683
|
+
}
|
|
1684
|
+
const exact = veniceTextModelChoices().find((item) => item.model.toLowerCase() === normalized);
|
|
1685
|
+
return exact?.model || "";
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
function useVeniceModels(state, routeModel = "venice-uncensored-1-2", mainModel = routeModel) {
|
|
1630
1689
|
state.routingMode = "smart";
|
|
1631
1690
|
state.provider = "deepseek";
|
|
1632
1691
|
state.model = "";
|
|
1633
1692
|
state.routeProvider = "venice";
|
|
1634
|
-
state.routeModel =
|
|
1693
|
+
state.routeModel = routeModel;
|
|
1635
1694
|
state.mainProvider = "venice";
|
|
1636
|
-
state.mainModel =
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
function isVeniceDefaultActive(state) {
|
|
1640
|
-
return (
|
|
1641
|
-
state.routeProvider === "venice" &&
|
|
1642
|
-
state.routeModel === "venice-uncensored-1-2" &&
|
|
1643
|
-
state.mainProvider === "venice" &&
|
|
1644
|
-
state.mainModel === "venice-uncensored-1-2"
|
|
1645
|
-
);
|
|
1695
|
+
state.mainModel = mainModel;
|
|
1646
1696
|
}
|
|
1647
1697
|
|
|
1648
1698
|
function printModelRoles(state) {
|
|
@@ -1723,12 +1773,30 @@ function modelRoleChoices(role = "main") {
|
|
|
1723
1773
|
{
|
|
1724
1774
|
provider: "venice",
|
|
1725
1775
|
model: "venice-uncensored-1-2",
|
|
1726
|
-
label: "Venice
|
|
1776
|
+
label: "Venice 1.2",
|
|
1727
1777
|
description: "Venice text route; use /auth venice if missing",
|
|
1728
1778
|
route: true,
|
|
1729
1779
|
main: true,
|
|
1730
1780
|
spare: true,
|
|
1731
1781
|
},
|
|
1782
|
+
{
|
|
1783
|
+
provider: "venice",
|
|
1784
|
+
model: "e2ee-venice-uncensored-24b-p",
|
|
1785
|
+
label: "Venice 1.1",
|
|
1786
|
+
description: "E2EE Venice text route; use /auth venice if missing",
|
|
1787
|
+
route: true,
|
|
1788
|
+
main: true,
|
|
1789
|
+
spare: true,
|
|
1790
|
+
},
|
|
1791
|
+
{
|
|
1792
|
+
provider: "venice",
|
|
1793
|
+
model: "gemma-4-uncensored",
|
|
1794
|
+
label: "Gemma 4",
|
|
1795
|
+
description: "Gemma-family Venice route; use /auth venice if missing",
|
|
1796
|
+
route: true,
|
|
1797
|
+
main: true,
|
|
1798
|
+
spare: true,
|
|
1799
|
+
},
|
|
1732
1800
|
{
|
|
1733
1801
|
provider: "openai",
|
|
1734
1802
|
model: "gpt-5.5",
|
|
@@ -1835,7 +1903,7 @@ function providerChoices() {
|
|
|
1835
1903
|
provider: "venice",
|
|
1836
1904
|
model: "venice-uncensored-1-2",
|
|
1837
1905
|
label: "Venice",
|
|
1838
|
-
description: "manual Venice route; /venice
|
|
1906
|
+
description: "manual Venice route; /venice picks route+main roles",
|
|
1839
1907
|
},
|
|
1840
1908
|
{
|
|
1841
1909
|
provider: "qwen",
|
|
@@ -1866,10 +1934,12 @@ function renderSelector({ title, subtitle, options, selectedIndex, lineCount = 0
|
|
|
1866
1934
|
if (lineCount > 0) clearSelector(lineCount);
|
|
1867
1935
|
const width = Math.min(Math.max(terminalWidth() - 2, 60), 110);
|
|
1868
1936
|
const bodyWidth = width - 4;
|
|
1937
|
+
const safeTitle = compactLine(title, bodyWidth);
|
|
1938
|
+
const safeSubtitle = compactLine(subtitle, bodyWidth);
|
|
1869
1939
|
const rows = [
|
|
1870
1940
|
`╭${"─".repeat(width - 2)}╮`,
|
|
1871
|
-
`│ ${padVisible(
|
|
1872
|
-
`│ ${padVisible(
|
|
1941
|
+
`│ ${padVisible(safeTitle, bodyWidth)} │`,
|
|
1942
|
+
`│ ${padVisible(safeSubtitle, bodyWidth)} │`,
|
|
1873
1943
|
`├${"─".repeat(width - 2)}┤`,
|
|
1874
1944
|
...options.map((option, index) => {
|
|
1875
1945
|
const marker = index === selectedIndex ? ">" : " ";
|
|
@@ -1998,6 +2068,38 @@ async function pickModelRole(role, state) {
|
|
|
1998
2068
|
return true;
|
|
1999
2069
|
}
|
|
2000
2070
|
|
|
2071
|
+
async function pickVeniceRouteAndMain(state) {
|
|
2072
|
+
const options = veniceTextModelChoices();
|
|
2073
|
+
const routeIndex = Math.max(
|
|
2074
|
+
options.findIndex((item) => item.provider === state.routeProvider && item.model === state.routeModel),
|
|
2075
|
+
0
|
|
2076
|
+
);
|
|
2077
|
+
const route = await selectModelChoice({
|
|
2078
|
+
title: "Select Venice route model",
|
|
2079
|
+
subtitle: "Route handles planning and short turns. Up/Down selects, Enter confirms, Esc cancels.",
|
|
2080
|
+
options,
|
|
2081
|
+
initialIndex: routeIndex,
|
|
2082
|
+
});
|
|
2083
|
+
if (!route) return false;
|
|
2084
|
+
|
|
2085
|
+
const mainIndex = Math.max(
|
|
2086
|
+
options.findIndex((item) => item.provider === state.mainProvider && item.model === state.mainModel),
|
|
2087
|
+
options.findIndex((item) => item.model === route.model),
|
|
2088
|
+
0
|
|
2089
|
+
);
|
|
2090
|
+
const main = await selectModelChoice({
|
|
2091
|
+
title: "Select Venice main model",
|
|
2092
|
+
subtitle: "Main handles complex work. Up/Down selects, Enter confirms, Esc cancels.",
|
|
2093
|
+
options,
|
|
2094
|
+
initialIndex: mainIndex,
|
|
2095
|
+
});
|
|
2096
|
+
if (!main) return false;
|
|
2097
|
+
|
|
2098
|
+
useVeniceModels(state, route.model, main.model);
|
|
2099
|
+
printSystemLine(`venice=on routing=smart route=venice/${state.routeModel} main=venice/${state.mainModel}`);
|
|
2100
|
+
return true;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2001
2103
|
async function pickProvider(state) {
|
|
2002
2104
|
const options = providerChoices();
|
|
2003
2105
|
const currentProvider = state.provider || "deepseek";
|
|
@@ -2252,19 +2354,45 @@ async function handleCommand(line, state, packageDir) {
|
|
|
2252
2354
|
return true;
|
|
2253
2355
|
}
|
|
2254
2356
|
if (command === "venice") {
|
|
2255
|
-
const
|
|
2357
|
+
const canUseSelector = input.isTTY && output.isTTY && typeof input.setRawMode === "function";
|
|
2358
|
+
const action = value || (canUseSelector ? "select" : "on");
|
|
2256
2359
|
if (action === "off" || action === "deepseek" || action === "default") {
|
|
2257
2360
|
useDeepSeekDefaults(state);
|
|
2258
2361
|
printSystemLine("venice=off routing=smart route=deepseek/deepseek-v4-flash main=deepseek/deepseek-v4-pro");
|
|
2259
2362
|
return true;
|
|
2260
2363
|
}
|
|
2261
|
-
if (action
|
|
2262
|
-
|
|
2364
|
+
if (action === "select" && canUseSelector) {
|
|
2365
|
+
const changed = await pickVeniceRouteAndMain(state);
|
|
2366
|
+
if (!changed) {
|
|
2367
|
+
printSystemLine(`route=${state.routeProvider || "deepseek"}/${state.routeModel || "deepseek-v4-flash"} main=${state.mainProvider || "deepseek"}/${state.mainModel || "deepseek-v4-pro"}`);
|
|
2368
|
+
return true;
|
|
2369
|
+
}
|
|
2370
|
+
const keys = providerKeyStatus(process.cwd());
|
|
2371
|
+
if (!keys.venice) {
|
|
2372
|
+
printAgentMessage("Venice model roles are selected, but no Venice key is configured. Run `/auth venice` to save one.");
|
|
2373
|
+
}
|
|
2374
|
+
return true;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
const parts = action.split(/\s+/).filter(Boolean);
|
|
2378
|
+
const routeModel = resolveVeniceTextModel(parts[0] || "on");
|
|
2379
|
+
const mainModel = resolveVeniceTextModel(parts.slice(1).join(" ") || parts[0] || "on");
|
|
2380
|
+
if (!routeModel || !mainModel) {
|
|
2381
|
+
printAgentMessage(
|
|
2382
|
+
[
|
|
2383
|
+
"Usage: /venice [off|1.2|1.1|gemma] [main-model]",
|
|
2384
|
+
"Examples:",
|
|
2385
|
+
" /venice",
|
|
2386
|
+
" /venice gemma",
|
|
2387
|
+
" /venice 1.2 gemma",
|
|
2388
|
+
" /venice off",
|
|
2389
|
+
].join("\n")
|
|
2390
|
+
);
|
|
2263
2391
|
return true;
|
|
2264
2392
|
}
|
|
2265
|
-
|
|
2393
|
+
useVeniceModels(state, routeModel, mainModel);
|
|
2266
2394
|
const keys = providerKeyStatus(process.cwd());
|
|
2267
|
-
printSystemLine(
|
|
2395
|
+
printSystemLine(`venice=on routing=smart route=venice/${state.routeModel} main=venice/${state.mainModel}`);
|
|
2268
2396
|
if (!keys.venice) {
|
|
2269
2397
|
printAgentMessage("Venice model roles are selected, but no Venice key is configured. Run `/auth venice` to save one.");
|
|
2270
2398
|
}
|
|
@@ -2607,7 +2735,7 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
|
|
|
2607
2735
|
}
|
|
2608
2736
|
throw error;
|
|
2609
2737
|
}
|
|
2610
|
-
const line = answer.trim();
|
|
2738
|
+
const line = canonicalSlashPromptBuffer(answer).trim();
|
|
2611
2739
|
if (!line) continue;
|
|
2612
2740
|
if (line.startsWith("/")) {
|
|
2613
2741
|
const keepGoing = await handleCommand(line, state, packageDir);
|
|
@@ -2619,7 +2747,7 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
|
|
|
2619
2747
|
const pendingPrompts = [{ content: line }];
|
|
2620
2748
|
while (pendingPrompts.length > 0) {
|
|
2621
2749
|
const nextPrompt = pendingPrompts.shift();
|
|
2622
|
-
const content = String(nextPrompt.content || "").trim();
|
|
2750
|
+
const content = canonicalSlashPromptBuffer(String(nextPrompt.content || "")).trim();
|
|
2623
2751
|
if (!content) continue;
|
|
2624
2752
|
if (content.startsWith("/")) {
|
|
2625
2753
|
const keepGoing = await handleCommand(content, state, packageDir);
|
package/src/model-client.js
CHANGED
|
@@ -73,6 +73,59 @@ function toolChoiceForProvider(config, messages = []) {
|
|
|
73
73
|
return messages.some((message) => message.role === "tool") ? "auto" : "required";
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
export function parseTextToolCalls(content = "") {
|
|
77
|
+
const text = String(content || "");
|
|
78
|
+
if (!text.includes("[TOOL_CALLS]")) return [];
|
|
79
|
+
|
|
80
|
+
const calls = [];
|
|
81
|
+
const pattern = /\[TOOL_CALLS\]([A-Za-z0-9_-]+)\[ARGS\]([A-Za-z0-9_.:-]+)\[ARGS\]([\s\S]*?)(?=\[TOOL_CALLS\]|$)/g;
|
|
82
|
+
for (const match of text.matchAll(pattern)) {
|
|
83
|
+
const name = match[1]?.trim();
|
|
84
|
+
const id = match[2]?.trim() || `text-tool-${calls.length + 1}`;
|
|
85
|
+
const rawArgs = match[3]?.trim() || "{}";
|
|
86
|
+
if (!name) continue;
|
|
87
|
+
try {
|
|
88
|
+
JSON.parse(rawArgs);
|
|
89
|
+
} catch {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
calls.push({
|
|
93
|
+
id,
|
|
94
|
+
type: "function",
|
|
95
|
+
function: {
|
|
96
|
+
name,
|
|
97
|
+
arguments: rawArgs,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return calls;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizeTextToolCallResponse(response) {
|
|
105
|
+
const message = response?.choices?.[0]?.message;
|
|
106
|
+
if (!message || Array.isArray(message.tool_calls) && message.tool_calls.length > 0) return response;
|
|
107
|
+
|
|
108
|
+
const calls = parseTextToolCalls(message.content || "");
|
|
109
|
+
if (calls.length === 0) return response;
|
|
110
|
+
|
|
111
|
+
const content = String(message.content || "").split("[TOOL_CALLS]")[0].trim();
|
|
112
|
+
return {
|
|
113
|
+
...response,
|
|
114
|
+
choices: response.choices.map((choice, index) =>
|
|
115
|
+
index === 0
|
|
116
|
+
? {
|
|
117
|
+
...choice,
|
|
118
|
+
message: {
|
|
119
|
+
...message,
|
|
120
|
+
content,
|
|
121
|
+
tool_calls: calls,
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
: choice
|
|
125
|
+
),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
76
129
|
function mockCommandForGoal(goal = "") {
|
|
77
130
|
const text = String(goal).toLowerCase();
|
|
78
131
|
if (/\blist\b|folder contents|directory contents|files?/.test(text)) return "ls -la";
|
|
@@ -866,7 +919,7 @@ export async function requestNextStep(client, config, messages) {
|
|
|
866
919
|
]);
|
|
867
920
|
}
|
|
868
921
|
|
|
869
|
-
|
|
922
|
+
const response = await client.chat.completions.create(
|
|
870
923
|
{
|
|
871
924
|
model: config.model,
|
|
872
925
|
temperature: 0,
|
|
@@ -877,4 +930,5 @@ export async function requestNextStep(client, config, messages) {
|
|
|
877
930
|
},
|
|
878
931
|
requestOptions(config)
|
|
879
932
|
);
|
|
933
|
+
return normalizeTextToolCallResponse(response);
|
|
880
934
|
}
|
package/src/model-routing.js
CHANGED
|
@@ -215,12 +215,19 @@ export const PROVIDER_MODEL_CATALOG = {
|
|
|
215
215
|
context: "128K",
|
|
216
216
|
description: "Current Venice uncensored text route.",
|
|
217
217
|
},
|
|
218
|
+
{
|
|
219
|
+
id: "e2ee-venice-uncensored-24b-p",
|
|
220
|
+
label: "Venice Uncensored 1.1",
|
|
221
|
+
bucket: "venice-uncensored",
|
|
222
|
+
context: "32K",
|
|
223
|
+
description: "E2EE Venice uncensored 1.1 text route.",
|
|
224
|
+
},
|
|
218
225
|
{
|
|
219
226
|
id: "venice-uncensored",
|
|
220
|
-
label: "Venice Uncensored",
|
|
227
|
+
label: "Venice Uncensored Legacy",
|
|
221
228
|
bucket: "venice-uncensored",
|
|
222
229
|
context: "32K",
|
|
223
|
-
description: "
|
|
230
|
+
description: "Deprecated legacy Venice uncensored text route.",
|
|
224
231
|
},
|
|
225
232
|
{
|
|
226
233
|
id: "venice-uncensored-role-play",
|