@lazyingart/agintiflow 0.17.6 → 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.
- package/README.md +8 -3
- package/docs/auxiliary-image-generation.md +15 -4
- package/docs/model-selection.md +70 -0
- package/package.json +10 -3
- package/public/app.js +170 -6
- package/public/index.html +80 -3
- package/public/styles.css +144 -0
- package/references/agintiflow-logo-prompt.md +57 -0
- package/references/backend-frontend-artifact-tunnel.md +214 -0
- package/references/codex-style-agent-command-execution.md +112 -0
- package/references/design-philosophy-review.md +234 -0
- package/references/model-routing-provider-design.md +90 -0
- package/references/run_design_scan.sh +91 -0
- package/references/venice-model-reference.md +119 -0
- package/scripts/smoke-auth.js +14 -3
- package/scripts/smoke-auxiliary-tools.js +27 -1
- package/scripts/smoke-capabilities.js +1 -0
- package/scripts/smoke-model-roles.js +88 -0
- package/scripts/smoke-web-api.js +24 -0
- package/src/agent-runner.js +1 -1
- package/src/auth-onboarding.js +13 -4
- package/src/auxiliary-tools.js +153 -4
- package/src/capabilities.js +5 -1
- package/src/cli.js +127 -7
- package/src/command-policy.js +3 -0
- package/src/config.js +39 -2
- package/src/interactive-cli.js +225 -17
- package/src/model-client.js +8 -3
- package/src/model-routing.js +420 -11
- package/src/project.js +31 -5
- package/src/redaction.js +1 -1
- package/src/task-profiles.js +1 -1
- package/src/tool-wrappers.js +4 -1
- package/src/web-db.js +28 -2
- package/web.js +72 -2
package/README.md
CHANGED
|
@@ -46,15 +46,17 @@ aginti --list-profiles
|
|
|
46
46
|
aginti --sandbox-status
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
On first interactive use, if no main model key is detected, `aginti` opens an auth wizard. Use Up/Down to choose DeepSeek, OpenAI, or
|
|
49
|
+
On first interactive use, if no main model key is detected, `aginti` opens an auth wizard. Use Up/Down to choose DeepSeek, OpenAI, Qwen, or Venice, paste the key, and press Enter to save it to the project-local ignored file `.aginti/.env` with `0600` permissions. The wizard points to DeepSeek keys at `https://platform.deepseek.com/api_keys`, OpenAI keys at `https://platform.openai.com/api-keys`, and Venice at `https://venice.ai`. It then offers the optional auxiliary image key; press Esc to skip. You can rerun it even when keys already exist:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
52
|
aginti auth
|
|
53
53
|
aginti auth openai
|
|
54
|
+
aginti auth venice
|
|
54
55
|
# inside chat, use /login or /auth for the same wizard
|
|
55
56
|
# or non-interactively:
|
|
56
57
|
printf '%s' "$DEEPSEEK_API_KEY" | aginti keys set deepseek --stdin
|
|
57
58
|
printf '%s' "$QWEN_API_KEY" | aginti keys set qwen --stdin
|
|
59
|
+
printf '%s' "$VENICE_API_KEY" | aginti keys set venice --stdin
|
|
58
60
|
|
|
59
61
|
# optional image-generation auxiliary skill:
|
|
60
62
|
aginti login grsai
|
|
@@ -75,6 +77,8 @@ Inside chat, type normal requests such as `write a small Python CLI app with tes
|
|
|
75
77
|
|
|
76
78
|
For code edits, AgInTiFlow routes patch/refactor/database-style tasks to DeepSeek v4 pro by default and exposes `apply_patch` as a deterministic workspace tool. It supports exact replacements, Codex-style patch envelopes, and unified diffs, with preflight checks, path guardrails, hashes, and compact per-file diffs. See [docs/patch-tools.md](docs/patch-tools.md).
|
|
77
79
|
|
|
80
|
+
Model choice is role-based: `/route` defaults to DeepSeek V4 Flash, `/model` or `/main` defaults to DeepSeek V4 Pro, `/spare` defaults to OpenAI GPT-5.4 medium, `/wrapper` defaults to Codex GPT-5.5 medium when enabled, and `/auxilliary` defaults to GRS AI/Nano Banana for image tools. See [docs/model-selection.md](docs/model-selection.md) and run `aginti models`.
|
|
81
|
+
|
|
78
82
|
For larger repositories, use `--profile large-codebase` or choose **Large codebase engineering** in the web UI. The web default stays **Auto**, and Auto now escalates codebase/system/debugging prompts to the same engineering loop when needed. Complex work routes to DeepSeek v4 pro, starts with `inspect_project`, then uses search/read/patch/check loops inspired by Codex, Copilot SDK, Claude Code, Gemini CLI, Qwen, and Claw Code. See [docs/large-codebase-engineering.md](docs/large-codebase-engineering.md).
|
|
79
83
|
|
|
80
84
|
AgInTiFlow can also spend cheap DeepSeek calls on parallel scout notes before the main executor starts a complicated task. It first writes a bounded project map to `.aginti/codebase-map.json`, then runs scouts for architecture, implementation, review, research, context mapping, tests, git workflow, integration, symbol tracing, and dependency risks. A coordinator Swarm Board is injected for the main agent and saved as `artifacts/scout-blackboard.json` in the session. The executor still does the real file/shell/browser work itself. Disable with `--no-parallel-scouts` or set `--scout-count 1..10`.
|
|
@@ -83,7 +87,7 @@ The next productive-agent roadmap is tracked in [docs/productive-agent-roadmap.m
|
|
|
83
87
|
|
|
84
88
|
For current docs, install errors, package/toolchain setup, and source discovery, the agent has a guarded `web_search` tool. It returns compact search results without browser search-engine loops and respects configured domain allowlists. Disable with `--no-web-search`.
|
|
85
89
|
|
|
86
|
-
For raster image work, AgInTiFlow has
|
|
90
|
+
For raster image work, AgInTiFlow has optional `image_generation` and `venice_image_generation` skills backed by the `generate_image` tool and local `GRSAI` or `VENICE_API_KEY` credentials. The skill tells the model when image generation is appropriate; the tool saves manifests/images under `artifacts/images` and sends the result to the canvas. See [docs/auxiliary-image-generation.md](docs/auxiliary-image-generation.md) and [references/venice-model-reference.md](references/venice-model-reference.md).
|
|
87
91
|
|
|
88
92
|
For long-running shell work, AgInTiFlow exposes host-side tmux tools when the shell tool is enabled: `tmux_list_sessions`, `tmux_capture_pane`, `tmux_send_keys`, and `tmux_start_session`. Use normal prompts such as `start this test server in tmux and monitor it` or `check my tmux session`. The agent captures panes before interacting, redacts outputs, blocks secret-like sends, and avoids sending sudo passwords or destructive commands. Tmux is intentionally host-side: Docker `run_command` containers are short-lived, so tmux servers started there cannot persist.
|
|
89
93
|
|
|
@@ -273,7 +277,7 @@ Defaults:
|
|
|
273
277
|
| `smart` | DeepSeek | Fast for normal tasks, pro for complex tasks | `AGENT_ROUTING_MODE=smart` |
|
|
274
278
|
| `fast` | DeepSeek | `deepseek-v4-flash` | `DEEPSEEK_FAST_MODEL` |
|
|
275
279
|
| `complex` | DeepSeek | `deepseek-v4-pro` | `DEEPSEEK_PRO_MODEL` |
|
|
276
|
-
| `manual` | DeepSeek/OpenAI/Qwen | user supplied | `AGENT_PROVIDER`, `LLM_MODEL` |
|
|
280
|
+
| `manual` | DeepSeek/OpenAI/Qwen/Venice | user supplied | `AGENT_PROVIDER`, `LLM_MODEL` |
|
|
277
281
|
|
|
278
282
|
Provider credentials:
|
|
279
283
|
|
|
@@ -282,6 +286,7 @@ Provider credentials:
|
|
|
282
286
|
| OpenAI | `OPENAI_API_KEY` | `https://api.openai.com/v1` |
|
|
283
287
|
| DeepSeek | `DEEPSEEK_API_KEY` | `https://api.deepseek.com/v1` |
|
|
284
288
|
| Qwen | `QWEN_API_KEY` | `QWEN_BASE_URL` or DashScope compatible mode |
|
|
289
|
+
| Venice | `VENICE_API_KEY` | `VENICE_API_BASE` or `https://api.venice.ai/api/v1` |
|
|
285
290
|
|
|
286
291
|
Project-local credentials can be stored without committing secrets:
|
|
287
292
|
|
|
@@ -5,16 +5,18 @@ AgInTiFlow separates **skills** from **tools**:
|
|
|
5
5
|
- A skill is instruction and routing context that teaches the agent when a capability is useful.
|
|
6
6
|
- A tool is the deterministic callable function that performs the action and writes auditable artifacts.
|
|
7
7
|
|
|
8
|
-
The optional image-generation
|
|
8
|
+
The optional image-generation skills are `image_generation` and `venice_image_generation`. Both use the deterministic `generate_image` tool.
|
|
9
9
|
|
|
10
10
|
## Setup
|
|
11
11
|
|
|
12
|
-
Store
|
|
12
|
+
Store a GRS AI or Venice key project-locally:
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
aginti login grsai
|
|
16
|
+
aginti login venice
|
|
16
17
|
# or
|
|
17
18
|
printf '%s' "$GRSAI" | aginti keys set grsai --stdin
|
|
19
|
+
printf '%s' "$VENICE_API_KEY" | aginti keys set venice --stdin
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
Inside interactive chat, use either spelling:
|
|
@@ -24,7 +26,7 @@ Inside interactive chat, use either spelling:
|
|
|
24
26
|
/auxiliary grsai
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
Keys are saved in `.aginti/.env` as `GRSAI` or `VENICE_API_KEY` with `0600` permissions. The CLI and web app only report whether a key exists; they never return raw values.
|
|
28
30
|
|
|
29
31
|
## Runtime Flow
|
|
30
32
|
|
|
@@ -33,6 +35,8 @@ For image, cover, poster, illustration, photo, or logo-concept requests, the mod
|
|
|
33
35
|
```json
|
|
34
36
|
{
|
|
35
37
|
"prompt": "A polished cyan robot painting a circuit-board river, high-end product illustration",
|
|
38
|
+
"provider": "venice",
|
|
39
|
+
"model": "nano-banana-2",
|
|
36
40
|
"outputDir": "artifacts/images/robot-cover",
|
|
37
41
|
"outputStem": "robot-cover",
|
|
38
42
|
"aspectRatio": "1:1",
|
|
@@ -40,18 +44,25 @@ For image, cover, poster, illustration, photo, or logo-concept requests, the mod
|
|
|
40
44
|
}
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
With GRS AI, the tool uses the Nano Banana API:
|
|
44
48
|
|
|
45
49
|
- `POST https://grsaiapi.com/v1/draw/nano-banana`
|
|
46
50
|
- `POST https://grsaiapi.com/v1/draw/result`
|
|
47
51
|
- `Authorization: Bearer <GRSAI>`
|
|
48
52
|
|
|
53
|
+
With Venice, the tool uses the image-generation API:
|
|
54
|
+
|
|
55
|
+
- `POST https://api.venice.ai/api/v1/image/generate`
|
|
56
|
+
- `Authorization: Bearer <VENICE_API_KEY>`
|
|
57
|
+
- image models such as `nano-banana-2`, `gpt-image-2`, `qwen-image-2`, `wan-2-7-text-to-image`, `bria-bg-remover`, and `venice-sd35`
|
|
58
|
+
|
|
49
59
|
Saved workspace artifacts:
|
|
50
60
|
|
|
51
61
|
- `prompt.txt`
|
|
52
62
|
- `request_payload.redacted.json`
|
|
53
63
|
- `submit_response.json`
|
|
54
64
|
- `result_response.json`
|
|
65
|
+
- `venice_result_response.json` for Venice image calls
|
|
55
66
|
- `task_manifest.json`
|
|
56
67
|
- generated image files, for example `image.png`
|
|
57
68
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Model Selection
|
|
2
|
+
|
|
3
|
+
AgInTiFlow treats model choice as a role-based control plane. A provider supplies models; a role decides how the agent uses one.
|
|
4
|
+
|
|
5
|
+
## Default Roles
|
|
6
|
+
|
|
7
|
+
| Role | CLI command | Default | Purpose |
|
|
8
|
+
| --- | --- | --- | --- |
|
|
9
|
+
| Route | `/route` | `deepseek/deepseek-v4-flash` | Fast planner, triage, short tasks, and routing decisions. |
|
|
10
|
+
| Main | `/model` or `/main` | `deepseek/deepseek-v4-pro` | Complex executor for coding, debugging, writing, and long tasks. |
|
|
11
|
+
| Spare | `/spare` | `openai/gpt-5.4` with `medium` reasoning | Optional fallback or cross-check model. |
|
|
12
|
+
| Wrapper | `/wrapper` | `codex gpt-5.5 medium` | External coding assistant when wrapper tools are enabled. |
|
|
13
|
+
| Auxiliary | `/auxilliary` | `grsai/nano-banana-2` | Image/media tools; Venice image models are optional. |
|
|
14
|
+
|
|
15
|
+
Smart routing still works as before: normal work goes to the route model, and complex work goes to the main model. Manual provider/model selection remains available for one-off runs.
|
|
16
|
+
|
|
17
|
+
## CLI Commands
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
aginti models
|
|
21
|
+
aginti --list-models
|
|
22
|
+
aginti --list-routes
|
|
23
|
+
|
|
24
|
+
# one-shot overrides
|
|
25
|
+
aginti --route-model deepseek-v4-flash --main-model deepseek-v4-pro "fix this project"
|
|
26
|
+
aginti --provider venice --model venice-uncensored-1-2 --routing manual "draft a note"
|
|
27
|
+
aginti --spare-provider openai --spare-model gpt-5.4 --spare-reasoning medium "review this design"
|
|
28
|
+
aginti --allow-wrappers --wrapper codex --wrapper-model gpt-5.5 "patch this bug"
|
|
29
|
+
aginti --image --aux-provider venice --aux-model gpt-image-2 "generate a logo"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Interactive commands:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
/models
|
|
36
|
+
/route deepseek/deepseek-v4-flash
|
|
37
|
+
/model deepseek/deepseek-v4-pro
|
|
38
|
+
/spare openai/gpt-5.4 medium
|
|
39
|
+
/wrapper codex gpt-5.5 medium
|
|
40
|
+
/auxilliary model grsai/nano-banana-2
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Provider Buckets
|
|
44
|
+
|
|
45
|
+
| Bucket | Provider | Typical use |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| `deepseek` | DeepSeek | Default route/main because V4 Flash and V4 Pro are cheap and strong. |
|
|
48
|
+
| `openai` | OpenAI | Spare/frontier checks, Codex-family work, and explicit manual routes. |
|
|
49
|
+
| `qwen` | Qwen | Chinese and general-purpose OpenAI-compatible tasks. |
|
|
50
|
+
| `venice-uncensored` | Venice | Venice-native uncensored text models. |
|
|
51
|
+
| `venice-gpt` | Venice | GPT-family models through Venice. |
|
|
52
|
+
| `venice-claude` | Venice | Claude-family models through Venice. |
|
|
53
|
+
| `venice-gemma` | Venice | Gemma-family models through Venice. |
|
|
54
|
+
| `venice-qwen` | Venice | Qwen-family models through Venice. |
|
|
55
|
+
| `venice-image` | Venice | Image generation/editing such as Nano Banana, GPT Image, Wan, Qwen Image. |
|
|
56
|
+
| `grsai` | GRS AI | Auxiliary image generation only. |
|
|
57
|
+
|
|
58
|
+
## Keys
|
|
59
|
+
|
|
60
|
+
Keys are set per project and stored only in ignored `.aginti/.env`:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
aginti auth
|
|
64
|
+
aginti auth venice
|
|
65
|
+
aginti keys status
|
|
66
|
+
printf '%s' "$VENICE_API_KEY" | aginti keys set venice --stdin
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The web UI mirrors the same roles in its **Model roles** panel. Use the top provider/model fields for manual runs; use the role fields to change how smart routing chooses route, main, spare, wrapper, and auxiliary models.
|
|
70
|
+
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "AgInTiFlow is a
|
|
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",
|
|
7
7
|
"homepage": "https://flow.lazying.art",
|
|
8
8
|
"repository": {
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
"browser",
|
|
19
19
|
"playwright",
|
|
20
20
|
"deepseek",
|
|
21
|
+
"venice",
|
|
22
|
+
"openai",
|
|
23
|
+
"qwen",
|
|
21
24
|
"sandbox",
|
|
22
25
|
"cli"
|
|
23
26
|
],
|
|
@@ -38,6 +41,8 @@
|
|
|
38
41
|
"logos/banner-opaque.png",
|
|
39
42
|
"logos/logo.png",
|
|
40
43
|
"public/",
|
|
44
|
+
"references/*.md",
|
|
45
|
+
"references/run_design_scan.sh",
|
|
41
46
|
"scripts/install-docker-ubuntu.sh",
|
|
42
47
|
"scripts/setup-agent-toolchain-docker.sh",
|
|
43
48
|
"scripts/real-deepseek-capabilities.js",
|
|
@@ -47,6 +52,7 @@
|
|
|
47
52
|
"scripts/smoke-coding-tools.js",
|
|
48
53
|
"scripts/smoke-capabilities.js",
|
|
49
54
|
"scripts/smoke-inbox.js",
|
|
55
|
+
"scripts/smoke-model-roles.js",
|
|
50
56
|
"scripts/smoke-platform.js",
|
|
51
57
|
"scripts/smoke-skills.js",
|
|
52
58
|
"scripts/smoke-tmux-tools.js",
|
|
@@ -73,11 +79,12 @@
|
|
|
73
79
|
"smoke:skills": "node scripts/smoke-skills.js",
|
|
74
80
|
"smoke:toolchain-docker": "node scripts/smoke-toolchain-docker.js",
|
|
75
81
|
"smoke:inbox": "node scripts/smoke-inbox.js",
|
|
82
|
+
"smoke:model-roles": "node scripts/smoke-model-roles.js",
|
|
76
83
|
"smoke:platform": "node scripts/smoke-platform.js",
|
|
77
84
|
"smoke:tmux-tools": "node scripts/smoke-tmux-tools.js",
|
|
78
85
|
"smoke:web-api": "node scripts/smoke-web-api.js",
|
|
79
86
|
"real:deepseek": "node scripts/real-deepseek-capabilities.js",
|
|
80
|
-
"test": "npm run check && npm run smoke:web-api && npm run smoke:coding-tools && npm run smoke:auxiliary-tools && npm run smoke:auth && npm run smoke:capabilities && npm run smoke:platform && npm run smoke:skills && npm run smoke:tmux-tools && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
87
|
+
"test": "npm run check && npm run smoke:web-api && npm run smoke:coding-tools && npm run smoke:auxiliary-tools && npm run smoke:auth && npm run smoke:capabilities && npm run smoke:model-roles && npm run smoke:platform && npm run smoke:skills && npm run smoke:tmux-tools && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
81
88
|
"pack:dry-run": "npm pack --dry-run",
|
|
82
89
|
"smoke:capabilities": "node scripts/smoke-capabilities.js"
|
|
83
90
|
},
|
package/public/app.js
CHANGED
|
@@ -22,9 +22,9 @@ const translations = {
|
|
|
22
22
|
projectStatusTitle: "Project folder",
|
|
23
23
|
setupTitle: "Provider setup",
|
|
24
24
|
setupHelp:
|
|
25
|
-
"DeepSeek/OpenAI/Qwen keys are missing. Use mock mode, export an env var, or save a project-local model key.",
|
|
25
|
+
"DeepSeek/OpenAI/Qwen/Venice keys are missing. Use mock mode, export an env var, or save a project-local model key.",
|
|
26
26
|
setupEnvHelp:
|
|
27
|
-
"Env vars: DEEPSEEK_API_KEY, OPENAI_API_KEY, QWEN_API_KEY, LLM_API_KEY, and optional GRSAI for image generation. Mock mode remains available for local tests.",
|
|
27
|
+
"Env vars: DEEPSEEK_API_KEY, OPENAI_API_KEY, QWEN_API_KEY, VENICE_API_KEY, LLM_API_KEY, and optional GRSAI for image generation. Mock mode remains available for local tests.",
|
|
28
28
|
setupKeyLinksLabel: "Get keys:",
|
|
29
29
|
deepseekKeyLink: "DeepSeek API keys",
|
|
30
30
|
openaiKeyLink: "OpenAI API keys",
|
|
@@ -649,6 +649,21 @@ const languageField = document.querySelector("#language");
|
|
|
649
649
|
const routingModeField = document.querySelector("#routingMode");
|
|
650
650
|
const providerField = document.querySelector("#provider");
|
|
651
651
|
const modelField = document.querySelector("#model");
|
|
652
|
+
const modelOptionsEl = document.querySelector("#model-options");
|
|
653
|
+
const modelCatalogEl = document.querySelector("#model-catalog");
|
|
654
|
+
const modelRoleGridEl = document.querySelector("#model-role-grid");
|
|
655
|
+
const modelRoutePillEl = document.querySelector("#model-route-pill");
|
|
656
|
+
const routeProviderField = document.querySelector("#routeProvider");
|
|
657
|
+
const routeModelField = document.querySelector("#routeModel");
|
|
658
|
+
const mainProviderField = document.querySelector("#mainProvider");
|
|
659
|
+
const mainModelField = document.querySelector("#mainModel");
|
|
660
|
+
const spareProviderField = document.querySelector("#spareProvider");
|
|
661
|
+
const spareModelField = document.querySelector("#spareModel");
|
|
662
|
+
const spareReasoningField = document.querySelector("#spareReasoning");
|
|
663
|
+
const wrapperModelField = document.querySelector("#wrapperModel");
|
|
664
|
+
const wrapperReasoningField = document.querySelector("#wrapperReasoning");
|
|
665
|
+
const auxiliaryProviderField = document.querySelector("#auxiliaryProvider");
|
|
666
|
+
const auxiliaryModelField = document.querySelector("#auxiliaryModel");
|
|
652
667
|
const routingHintEl = document.querySelector("#routing-hint");
|
|
653
668
|
const modelRouteStatusEl = document.querySelector("#model-route-status");
|
|
654
669
|
const projectStatusEl = document.querySelector("#project-status");
|
|
@@ -718,11 +733,16 @@ const defaults = {
|
|
|
718
733
|
openai: "gpt-5.4-mini",
|
|
719
734
|
deepseek: "deepseek-v4-flash",
|
|
720
735
|
qwen: "qwen-plus",
|
|
736
|
+
venice: "venice-uncensored-1-2",
|
|
721
737
|
mock: "mock-agent",
|
|
722
738
|
};
|
|
723
739
|
|
|
724
740
|
let currentLanguage = "en";
|
|
725
741
|
let routingPresets = {};
|
|
742
|
+
let modelCatalog = {};
|
|
743
|
+
let modelRoles = {};
|
|
744
|
+
let modelGroups = {};
|
|
745
|
+
let auxiliaryModelCatalog = {};
|
|
726
746
|
let taskProfiles = [];
|
|
727
747
|
let projectInfo = null;
|
|
728
748
|
let currentSessionId = "";
|
|
@@ -789,12 +809,14 @@ function renderKeyStatus(status = lastKeyStatus) {
|
|
|
789
809
|
status.openai ? t("availableLabel") : t("missingLabel")
|
|
790
810
|
} · DeepSeek ${status.deepseek ? t("availableLabel") : t("missingLabel")} · Qwen ${
|
|
791
811
|
status.qwen ? t("availableLabel") : t("missingLabel")
|
|
812
|
+
} · Venice ${
|
|
813
|
+
status.venice ? t("availableLabel") : t("missingLabel")
|
|
792
814
|
} · GRS AI ${
|
|
793
815
|
status.grsai ? t("availableLabel") : t("missingLabel")
|
|
794
816
|
} · ${t("mockLabel")} ${
|
|
795
817
|
status.mock ? t("availableLabel") : t("missingLabel")
|
|
796
818
|
}`;
|
|
797
|
-
if (setupCardEl) setupCardEl.hidden = Boolean(status.openai || status.deepseek || status.qwen);
|
|
819
|
+
if (setupCardEl) setupCardEl.hidden = Boolean(status.openai || status.deepseek || status.qwen || status.venice);
|
|
798
820
|
}
|
|
799
821
|
|
|
800
822
|
function renderProjectStatus(info = projectInfo) {
|
|
@@ -959,6 +981,90 @@ function renderSandboxLogs(logs) {
|
|
|
959
981
|
.join("\n\n");
|
|
960
982
|
}
|
|
961
983
|
|
|
984
|
+
function providerModelOptions(provider = providerField.value) {
|
|
985
|
+
return modelCatalog[provider] || [];
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
function renderModelRoles() {
|
|
989
|
+
if (!modelRoleGridEl) return;
|
|
990
|
+
const roles = {
|
|
991
|
+
route: {
|
|
992
|
+
...(modelRoles.route || {}),
|
|
993
|
+
provider: routeProviderField?.value || modelRoles.route?.provider || "deepseek",
|
|
994
|
+
model: routeModelField?.value || modelRoles.route?.model || "deepseek-v4-flash",
|
|
995
|
+
},
|
|
996
|
+
main: {
|
|
997
|
+
...(modelRoles.main || {}),
|
|
998
|
+
provider: mainProviderField?.value || modelRoles.main?.provider || "deepseek",
|
|
999
|
+
model: mainModelField?.value || modelRoles.main?.model || "deepseek-v4-pro",
|
|
1000
|
+
},
|
|
1001
|
+
spare: {
|
|
1002
|
+
...(modelRoles.spare || {}),
|
|
1003
|
+
provider: spareProviderField?.value || modelRoles.spare?.provider || "openai",
|
|
1004
|
+
model: spareModelField?.value || modelRoles.spare?.model || "gpt-5.4",
|
|
1005
|
+
reasoning: spareReasoningField?.value || modelRoles.spare?.reasoning || "medium",
|
|
1006
|
+
},
|
|
1007
|
+
wrapper: {
|
|
1008
|
+
...(modelRoles.wrapper || {}),
|
|
1009
|
+
provider: preferredWrapperField?.value || modelRoles.wrapper?.provider || "codex",
|
|
1010
|
+
model: wrapperModelField?.value || modelRoles.wrapper?.model || "gpt-5.5",
|
|
1011
|
+
reasoning: wrapperReasoningField?.value || modelRoles.wrapper?.reasoning || "medium",
|
|
1012
|
+
},
|
|
1013
|
+
auxiliary: {
|
|
1014
|
+
...(modelRoles.auxiliary || {}),
|
|
1015
|
+
provider: auxiliaryProviderField?.value || modelRoles.auxiliary?.provider || "grsai",
|
|
1016
|
+
model: auxiliaryModelField?.value || modelRoles.auxiliary?.model || "nano-banana-2",
|
|
1017
|
+
},
|
|
1018
|
+
};
|
|
1019
|
+
modelRoleGridEl.innerHTML = Object.values(roles)
|
|
1020
|
+
.map((role) => {
|
|
1021
|
+
const reasoning = role.reasoning ? ` · ${role.reasoning}` : "";
|
|
1022
|
+
return `
|
|
1023
|
+
<div class="model-role-card">
|
|
1024
|
+
<strong>${escapeHtml(role.label || role.id)}</strong>
|
|
1025
|
+
<span>${escapeHtml(`${role.provider}/${role.model}${reasoning}`)}</span>
|
|
1026
|
+
</div>
|
|
1027
|
+
`;
|
|
1028
|
+
})
|
|
1029
|
+
.join("");
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function renderModelOptions() {
|
|
1033
|
+
const provider = providerField.value || "deepseek";
|
|
1034
|
+
const options = providerModelOptions(provider);
|
|
1035
|
+
if (modelOptionsEl) {
|
|
1036
|
+
modelOptionsEl.innerHTML = options
|
|
1037
|
+
.map((item) => `<option value="${escapeHtml(item.id)}">${escapeHtml(item.label || item.id)}</option>`)
|
|
1038
|
+
.join("");
|
|
1039
|
+
}
|
|
1040
|
+
if (modelRoutePillEl) {
|
|
1041
|
+
const mode = routingModeField.value || "smart";
|
|
1042
|
+
const primary = modelField.value.trim() || defaults[provider] || "";
|
|
1043
|
+
const routeLabel = `${routeProviderField?.value || "deepseek"}/${routeModelField?.value || "deepseek-v4-flash"}`;
|
|
1044
|
+
const mainLabel = `${mainProviderField?.value || "deepseek"}/${mainModelField?.value || "deepseek-v4-pro"}`;
|
|
1045
|
+
modelRoutePillEl.textContent =
|
|
1046
|
+
mode === "manual"
|
|
1047
|
+
? `${provider} · ${primary}`
|
|
1048
|
+
: `${mode} · route ${routeLabel} · main ${mainLabel}`;
|
|
1049
|
+
}
|
|
1050
|
+
if (!modelCatalogEl) return;
|
|
1051
|
+
if (options.length === 0) {
|
|
1052
|
+
modelCatalogEl.innerHTML = "";
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
modelCatalogEl.innerHTML = options
|
|
1056
|
+
.map(
|
|
1057
|
+
(item) => `
|
|
1058
|
+
<button class="model-chip" type="button" data-model-id="${escapeHtml(item.id)}" data-active="${item.id === modelField.value.trim()}">
|
|
1059
|
+
<strong>${escapeHtml(item.label || item.id)}</strong>
|
|
1060
|
+
<span>${escapeHtml([item.bucket || item.role, item.context, item.reasoning?.length ? `reasoning ${item.reasoning.join("/")}` : ""].filter(Boolean).join(" · "))}</span>
|
|
1061
|
+
</button>
|
|
1062
|
+
`
|
|
1063
|
+
)
|
|
1064
|
+
.join("");
|
|
1065
|
+
renderModelRoles();
|
|
1066
|
+
}
|
|
1067
|
+
|
|
962
1068
|
function updateRoutingHint() {
|
|
963
1069
|
const mode = routingModeField.value || "smart";
|
|
964
1070
|
const hintKey = {
|
|
@@ -969,8 +1075,17 @@ function updateRoutingHint() {
|
|
|
969
1075
|
}[mode];
|
|
970
1076
|
routingHintEl.textContent = t(hintKey);
|
|
971
1077
|
|
|
972
|
-
if (mode !== "manual") {
|
|
973
|
-
const preset =
|
|
1078
|
+
if (mode !== "manual" && providerField.value === "deepseek") {
|
|
1079
|
+
const preset =
|
|
1080
|
+
mode === "complex"
|
|
1081
|
+
? {
|
|
1082
|
+
provider: mainProviderField?.value || routingPresets.complex?.provider,
|
|
1083
|
+
model: mainModelField?.value || routingPresets.complex?.model,
|
|
1084
|
+
}
|
|
1085
|
+
: {
|
|
1086
|
+
provider: routeProviderField?.value || routingPresets.fast?.provider,
|
|
1087
|
+
model: routeModelField?.value || routingPresets.fast?.model,
|
|
1088
|
+
};
|
|
974
1089
|
if (preset) {
|
|
975
1090
|
providerField.value = preset.provider === "deepseek" ? "deepseek" : providerField.value;
|
|
976
1091
|
modelField.value = preset.model || modelField.value;
|
|
@@ -980,6 +1095,7 @@ function updateRoutingHint() {
|
|
|
980
1095
|
modelRouteStatusEl.textContent = `${t("modelRouteStatus")}: ${providerField.value} / ${
|
|
981
1096
|
modelField.value.trim() || defaults[providerField.value] || ""
|
|
982
1097
|
}`;
|
|
1098
|
+
renderModelOptions();
|
|
983
1099
|
}
|
|
984
1100
|
|
|
985
1101
|
function updatePackageWarning() {
|
|
@@ -1037,6 +1153,17 @@ function formPayload() {
|
|
|
1037
1153
|
routingMode: routingModeField.value,
|
|
1038
1154
|
provider: providerField.value,
|
|
1039
1155
|
model: modelField.value.trim(),
|
|
1156
|
+
routeProvider: routeProviderField?.value || "deepseek",
|
|
1157
|
+
routeModel: routeModelField?.value.trim() || "deepseek-v4-flash",
|
|
1158
|
+
mainProvider: mainProviderField?.value || "deepseek",
|
|
1159
|
+
mainModel: mainModelField?.value.trim() || "deepseek-v4-pro",
|
|
1160
|
+
spareProvider: spareProviderField?.value || "openai",
|
|
1161
|
+
spareModel: spareModelField?.value.trim() || "gpt-5.4",
|
|
1162
|
+
spareReasoning: spareReasoningField?.value || "medium",
|
|
1163
|
+
wrapperModel: wrapperModelField?.value.trim() || "gpt-5.5",
|
|
1164
|
+
wrapperReasoning: wrapperReasoningField?.value || "medium",
|
|
1165
|
+
auxiliaryProvider: auxiliaryProviderField?.value || "grsai",
|
|
1166
|
+
auxiliaryModel: auxiliaryModelField?.value.trim() || "nano-banana-2",
|
|
1040
1167
|
startUrl: document.querySelector("#startUrl").value.trim(),
|
|
1041
1168
|
allowedDomains: document.querySelector("#allowedDomains").value.trim(),
|
|
1042
1169
|
commandCwd: document.querySelector("#commandCwd").value.trim(),
|
|
@@ -2293,7 +2420,7 @@ deleteSessionButton.addEventListener("click", () => {
|
|
|
2293
2420
|
});
|
|
2294
2421
|
|
|
2295
2422
|
providerField.addEventListener("change", () => {
|
|
2296
|
-
if (providerField.value === "mock") {
|
|
2423
|
+
if (providerField.value === "mock" || providerField.value === "venice" || providerField.value === "openai" || providerField.value === "qwen") {
|
|
2297
2424
|
routingModeField.value = "manual";
|
|
2298
2425
|
}
|
|
2299
2426
|
|
|
@@ -2302,6 +2429,7 @@ providerField.addEventListener("change", () => {
|
|
|
2302
2429
|
modelField.value === defaults.openai ||
|
|
2303
2430
|
modelField.value === defaults.deepseek ||
|
|
2304
2431
|
modelField.value === defaults.qwen ||
|
|
2432
|
+
modelField.value === defaults.venice ||
|
|
2305
2433
|
modelField.value === defaults.mock
|
|
2306
2434
|
) {
|
|
2307
2435
|
modelField.value = defaults[providerField.value] || "";
|
|
@@ -2310,7 +2438,27 @@ providerField.addEventListener("change", () => {
|
|
|
2310
2438
|
schedulePreferenceSave();
|
|
2311
2439
|
});
|
|
2312
2440
|
|
|
2441
|
+
modelCatalogEl?.addEventListener("click", (event) => {
|
|
2442
|
+
const button = event.target.closest("[data-model-id]");
|
|
2443
|
+
if (!button) return;
|
|
2444
|
+
modelField.value = button.dataset.modelId || modelField.value;
|
|
2445
|
+
updateRoutingHint();
|
|
2446
|
+
schedulePreferenceSave();
|
|
2447
|
+
});
|
|
2448
|
+
|
|
2313
2449
|
modelField.addEventListener("input", updateRoutingHint);
|
|
2450
|
+
[routeProviderField, routeModelField, mainProviderField, mainModelField, spareProviderField, spareModelField, spareReasoningField, wrapperModelField, wrapperReasoningField, auxiliaryProviderField, auxiliaryModelField]
|
|
2451
|
+
.filter(Boolean)
|
|
2452
|
+
.forEach((field) => {
|
|
2453
|
+
field.addEventListener("input", () => {
|
|
2454
|
+
renderModelRoles();
|
|
2455
|
+
schedulePreferenceSave();
|
|
2456
|
+
});
|
|
2457
|
+
field.addEventListener("change", () => {
|
|
2458
|
+
renderModelRoles();
|
|
2459
|
+
schedulePreferenceSave();
|
|
2460
|
+
});
|
|
2461
|
+
});
|
|
2314
2462
|
sandboxModeField.addEventListener("change", updatePackageWarning);
|
|
2315
2463
|
packageInstallPolicyField.addEventListener("change", updatePackageWarning);
|
|
2316
2464
|
allowWrapperToolsField.addEventListener("change", () => renderWrapperStatus());
|
|
@@ -2534,10 +2682,15 @@ async function loadConfig() {
|
|
|
2534
2682
|
const data = await response.json();
|
|
2535
2683
|
const prefs = data.preferences || {};
|
|
2536
2684
|
routingPresets = data.routing?.presets || {};
|
|
2685
|
+
modelCatalog = data.modelCatalog || {};
|
|
2686
|
+
modelRoles = data.modelRoles || {};
|
|
2687
|
+
modelGroups = data.modelGroups || {};
|
|
2688
|
+
auxiliaryModelCatalog = data.auxiliaryModelCatalog || {};
|
|
2537
2689
|
taskProfiles = data.taskProfiles || [];
|
|
2538
2690
|
projectInfo = data.project || null;
|
|
2539
2691
|
defaults.openai = data.defaults?.openai?.model || defaults.openai;
|
|
2540
2692
|
defaults.qwen = data.defaults?.qwen?.model || defaults.qwen;
|
|
2693
|
+
defaults.venice = data.defaults?.venice?.model || defaults.venice;
|
|
2541
2694
|
defaults.deepseek = routingPresets.fast?.model || data.defaults?.deepseek?.model || defaults.deepseek;
|
|
2542
2695
|
defaults.mock = data.defaults?.mock?.model || defaults.mock;
|
|
2543
2696
|
|
|
@@ -2546,6 +2699,17 @@ async function loadConfig() {
|
|
|
2546
2699
|
routingModeField.value = prefs.routingMode || "smart";
|
|
2547
2700
|
providerField.value = prefs.provider || "deepseek";
|
|
2548
2701
|
modelField.value = prefs.model || defaults[providerField.value] || "deepseek-v4-flash";
|
|
2702
|
+
if (routeProviderField) routeProviderField.value = prefs.routeProvider || modelRoles.route?.provider || "deepseek";
|
|
2703
|
+
if (routeModelField) routeModelField.value = prefs.routeModel || modelRoles.route?.model || "deepseek-v4-flash";
|
|
2704
|
+
if (mainProviderField) mainProviderField.value = prefs.mainProvider || modelRoles.main?.provider || "deepseek";
|
|
2705
|
+
if (mainModelField) mainModelField.value = prefs.mainModel || modelRoles.main?.model || "deepseek-v4-pro";
|
|
2706
|
+
if (spareProviderField) spareProviderField.value = prefs.spareProvider || modelRoles.spare?.provider || "openai";
|
|
2707
|
+
if (spareModelField) spareModelField.value = prefs.spareModel || modelRoles.spare?.model || "gpt-5.4";
|
|
2708
|
+
if (spareReasoningField) spareReasoningField.value = prefs.spareReasoning || modelRoles.spare?.reasoning || "medium";
|
|
2709
|
+
if (wrapperModelField) wrapperModelField.value = prefs.wrapperModel || modelRoles.wrapper?.model || "gpt-5.5";
|
|
2710
|
+
if (wrapperReasoningField) wrapperReasoningField.value = prefs.wrapperReasoning || modelRoles.wrapper?.reasoning || "medium";
|
|
2711
|
+
if (auxiliaryProviderField) auxiliaryProviderField.value = prefs.auxiliaryProvider || modelRoles.auxiliary?.provider || "grsai";
|
|
2712
|
+
if (auxiliaryModelField) auxiliaryModelField.value = prefs.auxiliaryModel || modelRoles.auxiliary?.model || "nano-banana-2";
|
|
2549
2713
|
renderTaskProfiles(prefs.taskProfile || "auto");
|
|
2550
2714
|
document.querySelector("#startUrl").value = prefs.startUrl || "";
|
|
2551
2715
|
document.querySelector("#allowedDomains").value = prefs.allowedDomains || "";
|
package/public/index.html
CHANGED
|
@@ -45,16 +45,18 @@
|
|
|
45
45
|
<div>
|
|
46
46
|
<strong data-i18n="setupTitle">Provider setup</strong>
|
|
47
47
|
<p class="subtle" data-i18n="setupHelp">
|
|
48
|
-
DeepSeek/OpenAI keys are missing. Use mock mode, export an env var, or save a project-local
|
|
48
|
+
DeepSeek/OpenAI/Qwen/Venice keys are missing. Use mock mode, export an env var, or save a project-local key.
|
|
49
49
|
</p>
|
|
50
50
|
<p class="subtle" data-i18n="setupEnvHelp">
|
|
51
|
-
Env vars: DEEPSEEK_API_KEY, OPENAI_API_KEY, QWEN_API_KEY, or LLM_API_KEY. Mock mode remains available for local tests.
|
|
51
|
+
Env vars: DEEPSEEK_API_KEY, OPENAI_API_KEY, QWEN_API_KEY, VENICE_API_KEY, or LLM_API_KEY. Mock mode remains available for local tests.
|
|
52
52
|
</p>
|
|
53
53
|
<p class="subtle key-links">
|
|
54
54
|
<span data-i18n="setupKeyLinksLabel">Get keys:</span>
|
|
55
55
|
<a href="https://platform.deepseek.com/api_keys" target="_blank" rel="noreferrer" data-i18n="deepseekKeyLink">DeepSeek API keys</a>
|
|
56
56
|
<span aria-hidden="true">·</span>
|
|
57
57
|
<a href="https://platform.openai.com/api-keys" target="_blank" rel="noreferrer" data-i18n="openaiKeyLink">OpenAI API keys</a>
|
|
58
|
+
<span aria-hidden="true">·</span>
|
|
59
|
+
<a href="https://venice.ai" target="_blank" rel="noreferrer">Venice API</a>
|
|
58
60
|
</p>
|
|
59
61
|
</div>
|
|
60
62
|
<div class="grid">
|
|
@@ -64,6 +66,7 @@
|
|
|
64
66
|
<option value="deepseek">DeepSeek</option>
|
|
65
67
|
<option value="openai">OpenAI</option>
|
|
66
68
|
<option value="qwen">Qwen</option>
|
|
69
|
+
<option value="venice">Venice</option>
|
|
67
70
|
<option value="grsai">GRS AI image</option>
|
|
68
71
|
</select>
|
|
69
72
|
</label>
|
|
@@ -96,18 +99,92 @@
|
|
|
96
99
|
<option value="deepseek">DeepSeek</option>
|
|
97
100
|
<option value="openai">OpenAI</option>
|
|
98
101
|
<option value="qwen">Qwen</option>
|
|
102
|
+
<option value="venice">Venice</option>
|
|
99
103
|
<option value="mock" data-i18n="mockProviderOption">Mock local</option>
|
|
100
104
|
</select>
|
|
101
105
|
</label>
|
|
102
106
|
|
|
103
107
|
<label>
|
|
104
108
|
<span data-i18n="modelLabel">Model</span>
|
|
105
|
-
<input id="model" name="model" type="text" value="deepseek-v4-flash" />
|
|
109
|
+
<input id="model" name="model" type="text" list="model-options" value="deepseek-v4-flash" />
|
|
110
|
+
<datalist id="model-options"></datalist>
|
|
106
111
|
</label>
|
|
107
112
|
</div>
|
|
108
113
|
|
|
109
114
|
<p id="routing-hint" class="subtle route-hint"></p>
|
|
110
115
|
<p id="model-route-status" class="subtle route-hint"></p>
|
|
116
|
+
<section class="model-catalog-panel" aria-label="Model catalog">
|
|
117
|
+
<div class="model-catalog-head">
|
|
118
|
+
<span>Route</span>
|
|
119
|
+
<strong id="model-route-pill">DeepSeek flash/pro</strong>
|
|
120
|
+
</div>
|
|
121
|
+
<div id="model-role-grid" class="model-role-grid"></div>
|
|
122
|
+
<div id="model-catalog" class="model-catalog"></div>
|
|
123
|
+
</section>
|
|
124
|
+
|
|
125
|
+
<section class="model-role-editor" aria-label="Model role settings">
|
|
126
|
+
<div class="model-role-editor-head">
|
|
127
|
+
<strong>Model roles</strong>
|
|
128
|
+
<span>Route, main, spare, wrapper, auxiliary</span>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="role-editor-grid">
|
|
131
|
+
<label>
|
|
132
|
+
<span>Route</span>
|
|
133
|
+
<select id="routeProvider" name="routeProvider">
|
|
134
|
+
<option value="deepseek">DeepSeek</option>
|
|
135
|
+
<option value="openai">OpenAI</option>
|
|
136
|
+
<option value="qwen">Qwen</option>
|
|
137
|
+
<option value="venice">Venice</option>
|
|
138
|
+
<option value="mock">Mock</option>
|
|
139
|
+
</select>
|
|
140
|
+
<input id="routeModel" name="routeModel" type="text" placeholder="deepseek-v4-flash" />
|
|
141
|
+
</label>
|
|
142
|
+
<label>
|
|
143
|
+
<span>Main</span>
|
|
144
|
+
<select id="mainProvider" name="mainProvider">
|
|
145
|
+
<option value="deepseek">DeepSeek</option>
|
|
146
|
+
<option value="openai">OpenAI</option>
|
|
147
|
+
<option value="qwen">Qwen</option>
|
|
148
|
+
<option value="venice">Venice</option>
|
|
149
|
+
</select>
|
|
150
|
+
<input id="mainModel" name="mainModel" type="text" placeholder="deepseek-v4-pro" />
|
|
151
|
+
</label>
|
|
152
|
+
<label>
|
|
153
|
+
<span>Spare</span>
|
|
154
|
+
<select id="spareProvider" name="spareProvider">
|
|
155
|
+
<option value="openai">OpenAI</option>
|
|
156
|
+
<option value="deepseek">DeepSeek</option>
|
|
157
|
+
<option value="qwen">Qwen</option>
|
|
158
|
+
<option value="venice">Venice</option>
|
|
159
|
+
</select>
|
|
160
|
+
<input id="spareModel" name="spareModel" type="text" placeholder="gpt-5.4" />
|
|
161
|
+
<select id="spareReasoning" name="spareReasoning">
|
|
162
|
+
<option value="low">low</option>
|
|
163
|
+
<option value="medium">medium</option>
|
|
164
|
+
<option value="high">high</option>
|
|
165
|
+
<option value="xhigh">xhigh</option>
|
|
166
|
+
</select>
|
|
167
|
+
</label>
|
|
168
|
+
<label>
|
|
169
|
+
<span>Wrapper</span>
|
|
170
|
+
<input id="wrapperModel" name="wrapperModel" type="text" placeholder="gpt-5.5" />
|
|
171
|
+
<select id="wrapperReasoning" name="wrapperReasoning">
|
|
172
|
+
<option value="low">low</option>
|
|
173
|
+
<option value="medium">medium</option>
|
|
174
|
+
<option value="high">high</option>
|
|
175
|
+
<option value="xhigh">xhigh</option>
|
|
176
|
+
</select>
|
|
177
|
+
</label>
|
|
178
|
+
<label>
|
|
179
|
+
<span>Auxiliary</span>
|
|
180
|
+
<select id="auxiliaryProvider" name="auxiliaryProvider">
|
|
181
|
+
<option value="grsai">GRS AI</option>
|
|
182
|
+
<option value="venice">Venice image</option>
|
|
183
|
+
</select>
|
|
184
|
+
<input id="auxiliaryModel" name="auxiliaryModel" type="text" placeholder="nano-banana-2" />
|
|
185
|
+
</label>
|
|
186
|
+
</div>
|
|
187
|
+
</section>
|
|
111
188
|
|
|
112
189
|
<label>
|
|
113
190
|
<span data-i18n="taskProfileLabel">Task profile</span>
|