@lazyingart/agintiflow 0.20.184 → 0.20.185
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/auxiliary-image-generation.md +32 -0
- package/package.json +1 -1
- package/references/agintiflow-npm-publication-memory.md +12 -0
- package/scripts/smoke-auxiliary-tools.js +22 -0
- package/scripts/smoke-web-api.js +18 -0
- package/src/agent-runner.js +1 -1
- package/src/auxiliary-tools.js +61 -6
- package/src/model-client.js +7 -2
- package/src/task-profiles.js +1 -1
- package/web.js +23 -0
|
@@ -7,6 +7,10 @@ AgInTiFlow separates **skills** from **tools**:
|
|
|
7
7
|
|
|
8
8
|
The optional image-generation skills are `image_generation` and `venice_image_generation`. Both use the deterministic `generate_image` tool.
|
|
9
9
|
|
|
10
|
+
`generate_image` is a raster-image tool. It does not produce true SVG/vector output. If a caller requests `svg`, the tool records
|
|
11
|
+
`requestedFormat: "svg"`, generates a PNG fallback with `actualFormat: "png"`, and returns a `formatNotice`. If the task truly requires
|
|
12
|
+
editable vectors, use AgInTiFlow file tools to write deterministic SVG/LaTeX/HTML instead of calling `generate_image`.
|
|
13
|
+
|
|
10
14
|
## Setup
|
|
11
15
|
|
|
12
16
|
Store a GRS AI or Venice key project-locally:
|
|
@@ -38,11 +42,39 @@ For image, cover, poster, illustration, photo, or logo-concept requests, the mod
|
|
|
38
42
|
"model": "nano-banana-2",
|
|
39
43
|
"outputDir": "artifacts/images/robot-cover",
|
|
40
44
|
"outputStem": "robot-cover",
|
|
45
|
+
"format": "png",
|
|
41
46
|
"aspectRatio": "1:1",
|
|
42
47
|
"imageSize": "2K"
|
|
43
48
|
}
|
|
44
49
|
```
|
|
45
50
|
|
|
51
|
+
For trusted local apps that need image generation without routing through a language-model run, the web server exposes a deterministic endpoint:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl -sS http://127.0.0.1:3210/api/auxiliary/generate-image \
|
|
55
|
+
-H 'Content-Type: application/json' \
|
|
56
|
+
-d '{
|
|
57
|
+
"prompt": "A polished cyan robot painting a circuit-board river",
|
|
58
|
+
"provider": "venice",
|
|
59
|
+
"format": "svg",
|
|
60
|
+
"outputDir": "artifacts/images/robot-cover",
|
|
61
|
+
"outputStem": "robot-cover"
|
|
62
|
+
}'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This endpoint is intended for trusted local/server-side callers. Browser apps on another origin should use an explicit adapter/proxy
|
|
66
|
+
rather than relying on permissive CORS, because generation can consume API keys and write workspace artifacts.
|
|
67
|
+
|
|
68
|
+
That SVG request returns a PNG fallback contract rather than pretending SVG was generated:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"requestedFormat": "svg",
|
|
73
|
+
"actualFormat": "png",
|
|
74
|
+
"formatNotice": "SVG/vector output is not supported by generate_image. A raster PNG is generated instead."
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
46
78
|
With GRS AI, the tool uses the Nano Banana API:
|
|
47
79
|
|
|
48
80
|
- `POST https://grsaiapi.com/v1/draw/nano-banana`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.185",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
Date: 2026-05-20
|
|
4
4
|
|
|
5
|
+
Updated: 2026-05-31
|
|
6
|
+
|
|
5
7
|
This note records the working publication route for AgInTiFlow so future release work does not get blocked by stale local npm tokens or repeated discussion about why direct `npm publish` failed.
|
|
6
8
|
|
|
7
9
|
## Published Evidence
|
|
8
10
|
|
|
9
11
|
Latest verified release:
|
|
10
12
|
|
|
13
|
+
- Version: `0.20.184`
|
|
14
|
+
- Commit: `9b22f4c`
|
|
15
|
+
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26700154894`
|
|
16
|
+
- Workflow result: success
|
|
17
|
+
- npm registry check: `npm view @lazyingart/agintiflow version dist-tags.latest --registry=https://registry.npmjs.org` returned `0.20.184`.
|
|
18
|
+
- Installed verification: `npm install -g @lazyingart/agintiflow@0.20.184` then `aginti --version` returned `0.20.184`.
|
|
19
|
+
- Webapp verification: `aginti webapp restart --port 3210` then `curl -fsS http://127.0.0.1:3210/health` returned `version":"0.20.184"` from the global npm package path.
|
|
20
|
+
|
|
21
|
+
Previous verified release:
|
|
22
|
+
|
|
11
23
|
- Version: `0.20.182`
|
|
12
24
|
- Commit: `b5492f7`
|
|
13
25
|
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26198123298`
|
|
@@ -60,6 +60,27 @@ try {
|
|
|
60
60
|
}
|
|
61
61
|
);
|
|
62
62
|
assert(veniceDryRun.ok && veniceDryRun.provider === "venice", "venice generate_image dry run failed");
|
|
63
|
+
const svgFallbackDryRun = await generateImage(
|
|
64
|
+
{
|
|
65
|
+
provider: "venice",
|
|
66
|
+
prompt: "A simple geometric logo requested as SVG.",
|
|
67
|
+
outputDir: "artifacts/images/svg-fallback-dry-run",
|
|
68
|
+
outputStem: "logo.svg",
|
|
69
|
+
format: "svg",
|
|
70
|
+
dryRun: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
commandCwd: workspace,
|
|
74
|
+
allowFileTools: true,
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
assert(svgFallbackDryRun.ok && svgFallbackDryRun.requestedFormat === "svg", "SVG fallback did not record requestedFormat");
|
|
78
|
+
assert(svgFallbackDryRun.actualFormat === "png", "SVG fallback did not select PNG output");
|
|
79
|
+
assert(/raster PNG/i.test(svgFallbackDryRun.formatNotice || ""), "SVG fallback did not explain the PNG fallback");
|
|
80
|
+
const svgFallbackManifest = JSON.parse(
|
|
81
|
+
await fs.readFile(path.join(workspace, "artifacts/images/svg-fallback-dry-run/task_manifest.json"), "utf8")
|
|
82
|
+
);
|
|
83
|
+
assert(svgFallbackManifest.requestedFormat === "svg" && svgFallbackManifest.actualFormat === "png", "SVG fallback manifest missing format contract");
|
|
63
84
|
|
|
64
85
|
const blocked = await generateImage(
|
|
65
86
|
{
|
|
@@ -109,6 +130,7 @@ try {
|
|
|
109
130
|
"venice_image_skill_listed",
|
|
110
131
|
"generate_image_dry_run",
|
|
111
132
|
"venice_generate_image_dry_run",
|
|
133
|
+
"svg_request_png_fallback",
|
|
112
134
|
"generate_image_guardrail",
|
|
113
135
|
"mock_agent_image_tool",
|
|
114
136
|
],
|
package/scripts/smoke-web-api.js
CHANGED
|
@@ -178,6 +178,23 @@ try {
|
|
|
178
178
|
}
|
|
179
179
|
const traversalResponse = await fetch(`${baseUrl}/api/workspace/raw?commandCwd=${encodeURIComponent(runtimeDir)}&path=../escape.txt`);
|
|
180
180
|
if (traversalResponse.ok) throw new Error("workspace raw endpoint allowed path traversal");
|
|
181
|
+
const imageApiDryRun = await fetchJson("/api/auxiliary/generate-image", {
|
|
182
|
+
method: "POST",
|
|
183
|
+
headers: { "Content-Type": "application/json" },
|
|
184
|
+
body: JSON.stringify({
|
|
185
|
+
commandCwd: runtimeDir,
|
|
186
|
+
provider: "venice",
|
|
187
|
+
prompt: "A compact logo requested as SVG for API fallback testing.",
|
|
188
|
+
format: "svg",
|
|
189
|
+
outputDir: "artifacts/images/api-svg-fallback",
|
|
190
|
+
outputStem: "api-logo.svg",
|
|
191
|
+
dryRun: true,
|
|
192
|
+
}),
|
|
193
|
+
});
|
|
194
|
+
if (!imageApiDryRun.ok || imageApiDryRun.requestedFormat !== "svg" || imageApiDryRun.actualFormat !== "png") {
|
|
195
|
+
throw new Error(`direct auxiliary image API did not return SVG-to-PNG fallback: ${JSON.stringify(imageApiDryRun)}`);
|
|
196
|
+
}
|
|
197
|
+
await fs.access(path.join(runtimeDir, "artifacts/images/api-svg-fallback/task_manifest.json"));
|
|
181
198
|
if (config.preferences?.sandboxMode !== "docker-workspace") throw new Error("web did not default to docker workspace");
|
|
182
199
|
if (config.preferences?.packageInstallPolicy !== "allow") throw new Error("web did not default to Docker package installs");
|
|
183
200
|
if (config.preferences?.permissionMode !== "normal") throw new Error("web did not default to normal permission mode");
|
|
@@ -572,6 +589,7 @@ try {
|
|
|
572
589
|
"POST /api/workspace/write",
|
|
573
590
|
"POST /api/workspace/rename",
|
|
574
591
|
"POST /api/workspace/delete",
|
|
592
|
+
"POST /api/auxiliary/generate-image",
|
|
575
593
|
"/api/workspace/changes",
|
|
576
594
|
"stale running status reconciliation",
|
|
577
595
|
"/api/sessions/:id/artifacts",
|
package/src/agent-runner.js
CHANGED
|
@@ -629,7 +629,7 @@ async function createInitialState(config, sessionId) {
|
|
|
629
629
|
config.allowAuxiliaryTools
|
|
630
630
|
? `Auxiliary skills are available: ${listAuxiliarySkills()
|
|
631
631
|
.map((skill) => `${skill.id} via ${skill.toolName} (${skill.available ? "key available" : `needs ${skill.keyName}`})`)
|
|
632
|
-
.join(", ")}. Use generate_image for real raster image/photo/illustration/cover/poster/logo requests when appropriate; if image keys are missing, ask the user to run /auxiliary grsai, aginti login grsai, or aginti login venice.`
|
|
632
|
+
.join(", ")}. Use generate_image for real raster image/photo/illustration/cover/poster/logo requests when appropriate. generate_image is raster-only; if SVG/vector is requested, either create true SVG/LaTeX/HTML with file tools or call generate_image with format=png and explicitly report requestedFormat=svg, actualFormat=png. If image keys are missing, ask the user to run /auxiliary grsai, aginti login grsai, or aginti login venice.`
|
|
633
633
|
: "Auxiliary skills are disabled for this run.",
|
|
634
634
|
config.allowWebSearch
|
|
635
635
|
? "web_search is available for lightweight snippets. web_research is available for sourced, persisted research artifacts; use domains for official-source constraints and mode=openai only when hosted OpenAI web research is needed and configured. Prefer these tools over browser search-engine navigation."
|
package/src/auxiliary-tools.js
CHANGED
|
@@ -10,6 +10,7 @@ const DEFAULT_IMAGE_MODEL = "nano-banana-2";
|
|
|
10
10
|
const DEFAULT_VENICE_IMAGE_MODEL = "nano-banana-2";
|
|
11
11
|
const DEFAULT_ASPECT_RATIO = "1:1";
|
|
12
12
|
const DEFAULT_IMAGE_SIZE = "2K";
|
|
13
|
+
const DEFAULT_OUTPUT_FORMAT = "png";
|
|
13
14
|
const TRANSIENT_HTTP_CODES = new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
14
15
|
const MAX_PROMPT_BYTES = 120_000;
|
|
15
16
|
const MAX_REFERENCE_BYTES = 4_000_000;
|
|
@@ -42,11 +43,45 @@ function timestampSlug() {
|
|
|
42
43
|
function safeStem(value = "image") {
|
|
43
44
|
const stem = String(value || "image")
|
|
44
45
|
.trim()
|
|
46
|
+
.replace(/\.(?:svg|png|jpe?g|webp|gif)$/i, "")
|
|
45
47
|
.replace(/[^A-Za-z0-9._-]+/g, "-")
|
|
46
48
|
.replace(/^-+|-+$/g, "");
|
|
47
49
|
return stem || "image";
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
function normalizeOutputFormat(args = {}) {
|
|
53
|
+
const requested = String(args.outputFormat || args.format || DEFAULT_OUTPUT_FORMAT)
|
|
54
|
+
.trim()
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.replace(/^image\//, "");
|
|
57
|
+
if (requested === "svg" || requested === "svg+xml" || requested === "vector") {
|
|
58
|
+
return {
|
|
59
|
+
requestedFormat: requested === "svg+xml" ? "svg" : requested,
|
|
60
|
+
actualFormat: "png",
|
|
61
|
+
notice: "SVG/vector output is not supported by generate_image. A raster PNG is generated instead.",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (requested === "webp") {
|
|
65
|
+
return {
|
|
66
|
+
requestedFormat: "webp",
|
|
67
|
+
actualFormat: "webp",
|
|
68
|
+
notice: "",
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (["jpg", "jpeg", "gif"].includes(requested)) {
|
|
72
|
+
return {
|
|
73
|
+
requestedFormat: requested,
|
|
74
|
+
actualFormat: "png",
|
|
75
|
+
notice: `${requested.toUpperCase()} output is not currently exposed by generate_image. A raster PNG is generated instead.`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
requestedFormat: requested || DEFAULT_OUTPUT_FORMAT,
|
|
80
|
+
actualFormat: DEFAULT_OUTPUT_FORMAT,
|
|
81
|
+
notice: "",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
50
85
|
function normalizeAspectRatio(value) {
|
|
51
86
|
const text = String(value || DEFAULT_ASPECT_RATIO).trim();
|
|
52
87
|
return /^(?:\d+:\d+|1:1|2:3|3:2|3:4|4:3|9:16|16:9)$/.test(text) ? text : DEFAULT_ASPECT_RATIO;
|
|
@@ -305,7 +340,7 @@ function veniceBaseUrl(value = "") {
|
|
|
305
340
|
.replace(/\/+$/, "");
|
|
306
341
|
}
|
|
307
342
|
|
|
308
|
-
async function generateVeniceImages({ prompt, args, target, outputStem, manifest, manifestPath, signal = null }) {
|
|
343
|
+
async function generateVeniceImages({ prompt, args, target, outputStem, manifest, manifestPath, formatInfo, signal = null }) {
|
|
309
344
|
const apiKey = veniceKey();
|
|
310
345
|
if (!apiKey) {
|
|
311
346
|
throw new Error("Missing VENICE_API_KEY. Run `aginti login venice` or `aginti keys set venice --stdin`.");
|
|
@@ -314,7 +349,7 @@ async function generateVeniceImages({ prompt, args, target, outputStem, manifest
|
|
|
314
349
|
const model =
|
|
315
350
|
String(args.model || process.env.AGINTI_AUX_MODEL || process.env.VENICE_IMAGE_MODEL || DEFAULT_VENICE_IMAGE_MODEL).trim() ||
|
|
316
351
|
DEFAULT_VENICE_IMAGE_MODEL;
|
|
317
|
-
const format =
|
|
352
|
+
const format = formatInfo.actualFormat === "webp" ? "webp" : "png";
|
|
318
353
|
const sizing = veniceSizingForModel(model, args);
|
|
319
354
|
const payload = {
|
|
320
355
|
model,
|
|
@@ -356,6 +391,9 @@ async function generateVeniceImages({ prompt, args, target, outputStem, manifest
|
|
|
356
391
|
manifest.provider = "venice";
|
|
357
392
|
manifest.host = base;
|
|
358
393
|
manifest.model = model;
|
|
394
|
+
manifest.requestedFormat = formatInfo.requestedFormat;
|
|
395
|
+
manifest.actualFormat = format;
|
|
396
|
+
if (formatInfo.notice) manifest.formatNotice = formatInfo.notice;
|
|
359
397
|
manifest.status = "succeeded";
|
|
360
398
|
manifest.finishedAt = new Date().toISOString();
|
|
361
399
|
manifest.downloadedFiles = downloads;
|
|
@@ -372,7 +410,10 @@ async function generateVeniceImages({ prompt, args, target, outputStem, manifest
|
|
|
372
410
|
promptPath: path.posix.join(target.relativePath, "prompt.txt"),
|
|
373
411
|
requestPayloadPath: path.posix.join(target.relativePath, "request_payload.redacted.json"),
|
|
374
412
|
taskId: resultPayload.id ? String(resultPayload.id) : "",
|
|
375
|
-
|
|
413
|
+
requestedFormat: formatInfo.requestedFormat,
|
|
414
|
+
actualFormat: format,
|
|
415
|
+
formatNotice: formatInfo.notice,
|
|
416
|
+
summary: `${imagePaths.length} image(s) generated through Venice${formatInfo.notice ? ` (${formatInfo.notice})` : ""}`,
|
|
376
417
|
};
|
|
377
418
|
}
|
|
378
419
|
|
|
@@ -403,6 +444,7 @@ export async function generateImage(args = {}, config = {}) {
|
|
|
403
444
|
}
|
|
404
445
|
|
|
405
446
|
const provider = args.provider ? normalizeImageProvider(args.provider) : defaultImageProvider(config);
|
|
447
|
+
const formatInfo = normalizeOutputFormat(args);
|
|
406
448
|
const host = provider === "venice" ? veniceBaseUrl(args.host) : String(args.host || DEFAULT_GRS_HOST).trim().replace(/\/+$/, "") || DEFAULT_GRS_HOST;
|
|
407
449
|
const model =
|
|
408
450
|
provider === "venice"
|
|
@@ -419,6 +461,7 @@ export async function generateImage(args = {}, config = {}) {
|
|
|
419
461
|
webHook: "-1",
|
|
420
462
|
shutProgress: Boolean(args.shutProgress),
|
|
421
463
|
};
|
|
464
|
+
if (provider === "venice") payload.format = formatInfo.actualFormat;
|
|
422
465
|
|
|
423
466
|
const redactedPayload = redactPayload(payload);
|
|
424
467
|
redactedPayload.urls = references.map((item) => item.redacted);
|
|
@@ -433,6 +476,9 @@ export async function generateImage(args = {}, config = {}) {
|
|
|
433
476
|
provider,
|
|
434
477
|
host,
|
|
435
478
|
model,
|
|
479
|
+
requestedFormat: formatInfo.requestedFormat,
|
|
480
|
+
actualFormat: formatInfo.actualFormat,
|
|
481
|
+
formatNotice: formatInfo.notice,
|
|
436
482
|
outputDir: target.relativePath,
|
|
437
483
|
promptFile: path.posix.join(target.relativePath, "prompt.txt"),
|
|
438
484
|
requestPayloadRedacted: path.posix.join(target.relativePath, "request_payload.redacted.json"),
|
|
@@ -452,12 +498,15 @@ export async function generateImage(args = {}, config = {}) {
|
|
|
452
498
|
promptPath: path.posix.join(target.relativePath, "prompt.txt"),
|
|
453
499
|
requestPayloadPath: path.posix.join(target.relativePath, "request_payload.redacted.json"),
|
|
454
500
|
imagePaths: [],
|
|
455
|
-
|
|
501
|
+
requestedFormat: formatInfo.requestedFormat,
|
|
502
|
+
actualFormat: formatInfo.actualFormat,
|
|
503
|
+
formatNotice: formatInfo.notice,
|
|
504
|
+
summary: `Prepared redacted image-generation payload without calling the provider.${formatInfo.notice ? ` ${formatInfo.notice}` : ""}`,
|
|
456
505
|
};
|
|
457
506
|
}
|
|
458
507
|
|
|
459
508
|
if (provider === "venice") {
|
|
460
|
-
return generateVeniceImages({ prompt, args, target, outputStem, manifest, manifestPath, signal: config.abortSignal });
|
|
509
|
+
return generateVeniceImages({ prompt, args, target, outputStem, manifest, manifestPath, formatInfo, signal: config.abortSignal });
|
|
461
510
|
}
|
|
462
511
|
|
|
463
512
|
const apiKey = grsaiKey();
|
|
@@ -515,6 +564,9 @@ export async function generateImage(args = {}, config = {}) {
|
|
|
515
564
|
}
|
|
516
565
|
|
|
517
566
|
manifest.status = "succeeded";
|
|
567
|
+
manifest.requestedFormat = formatInfo.requestedFormat;
|
|
568
|
+
manifest.actualFormat = path.extname(imagePaths[0] || "").replace(/^\./, "") || formatInfo.actualFormat;
|
|
569
|
+
if (formatInfo.notice) manifest.formatNotice = formatInfo.notice;
|
|
518
570
|
manifest.finishedAt = new Date().toISOString();
|
|
519
571
|
manifest.downloadedFiles = downloads;
|
|
520
572
|
await writeJson(manifestPath, manifest);
|
|
@@ -528,6 +580,9 @@ export async function generateImage(args = {}, config = {}) {
|
|
|
528
580
|
promptPath: path.posix.join(target.relativePath, "prompt.txt"),
|
|
529
581
|
requestPayloadPath: path.posix.join(target.relativePath, "request_payload.redacted.json"),
|
|
530
582
|
taskId: String(taskId),
|
|
531
|
-
|
|
583
|
+
requestedFormat: formatInfo.requestedFormat,
|
|
584
|
+
actualFormat: manifest.actualFormat,
|
|
585
|
+
formatNotice: formatInfo.notice,
|
|
586
|
+
summary: `${imagePaths.length} image(s) generated${formatInfo.notice ? ` (${formatInfo.notice})` : ""}`,
|
|
532
587
|
};
|
|
533
588
|
}
|
package/src/model-client.js
CHANGED
|
@@ -723,7 +723,7 @@ export async function createPlan(client, config, state) {
|
|
|
723
723
|
config.allowAuxiliaryTools
|
|
724
724
|
? `Auxiliary skills are enabled: ${listAuxiliarySkills()
|
|
725
725
|
.map((skill) => `${skill.id} via ${skill.toolName} (${skill.available ? "key available" : `needs ${skill.keyName}`})`)
|
|
726
|
-
.join(", ")}. For raster image generation requests, plan to use generate_image when a GRSAI or Venice image key is available;
|
|
726
|
+
.join(", ")}. For raster image generation requests, plan to use generate_image when a GRSAI or Venice image key is available. generate_image is raster-only; if SVG/vector is requested, either create true SVG/LaTeX/HTML with file tools or use PNG fallback and report requestedFormat=svg, actualFormat=png. Otherwise ask the user to run /auxiliary grsai, aginti login grsai, or aginti login venice.`
|
|
727
727
|
: "Auxiliary skills are disabled for this run.",
|
|
728
728
|
config.allowWebSearch
|
|
729
729
|
? "web_search is available for lightweight snippets. web_research is available for auditable sourced research with persisted artifacts; use mode=snippets by default and mode=openai only when hosted OpenAI web research is needed and configured. Prefer these tools over opening a search engine in the browser."
|
|
@@ -1578,7 +1578,7 @@ export async function requestNextStep(client, config, messages) {
|
|
|
1578
1578
|
function: {
|
|
1579
1579
|
name: "generate_image",
|
|
1580
1580
|
description:
|
|
1581
|
-
"Generate a raster image artifact through optional GRS AI Nano Banana or Venice image-generation skills. Use for user requests that explicitly need a real image/photo/illustration/cover/poster/logo concept rather than SVG/code-native graphics. Saves prompt, redacted payload, manifest, and downloaded images in the workspace. If keys are missing, ask the user to run /auxiliary grsai, aginti login grsai, or aginti login venice.",
|
|
1581
|
+
"Generate a raster image artifact through optional GRS AI Nano Banana or Venice image-generation skills. Use for user requests that explicitly need a real image/photo/illustration/cover/poster/logo concept rather than SVG/code-native graphics. If a caller requests SVG/vector through this tool, request PNG and report the SVG-to-PNG fallback; if true vector is required, write SVG/LaTeX/HTML deterministically with file tools instead. Saves prompt, redacted payload, manifest, and downloaded images in the workspace. If keys are missing, ask the user to run /auxiliary grsai, aginti login grsai, or aginti login venice.",
|
|
1582
1582
|
parameters: {
|
|
1583
1583
|
type: "object",
|
|
1584
1584
|
properties: {
|
|
@@ -1590,6 +1590,11 @@ export async function requestNextStep(client, config, messages) {
|
|
|
1590
1590
|
prompt: { type: "string", description: "Detailed image-generation prompt." },
|
|
1591
1591
|
outputDir: { type: "string", description: "Workspace-relative output directory. Defaults to artifacts/images/<timestamp>." },
|
|
1592
1592
|
outputStem: { type: "string", description: "Filename stem for downloaded images." },
|
|
1593
|
+
format: {
|
|
1594
|
+
type: "string",
|
|
1595
|
+
enum: ["png", "webp", "svg", "jpeg", "jpg", "gif"],
|
|
1596
|
+
description: "Requested output format. SVG/vector is not supported by generate_image and is returned as a PNG fallback with requestedFormat/actualFormat metadata.",
|
|
1597
|
+
},
|
|
1593
1598
|
aspectRatio: { type: "string", description: "Aspect ratio such as 1:1, 16:9, 2:3, or 3:2." },
|
|
1594
1599
|
imageSize: { type: "string", description: "Image size such as 1K, 2K, or 4K." },
|
|
1595
1600
|
model: { type: "string", description: "Image model. Defaults to nano-banana-2; Venice also supports models such as gpt-image-2, wan-2-7-text-to-image, qwen-image-2, and bria-bg-remover." },
|
package/src/task-profiles.js
CHANGED
|
@@ -248,7 +248,7 @@ export const TASK_PROFILES = {
|
|
|
248
248
|
id: "image",
|
|
249
249
|
label: "Image generation",
|
|
250
250
|
prompt:
|
|
251
|
-
"For raster image, cover, poster, illustration, photo, and logo-concept tasks, write a clear visual prompt, use generate_image when the optional GRS AI or Venice image key is available, save outputs under artifacts/images, and send the selected image to the canvas. If keys are missing, ask the user to run /auxiliary grsai, aginti login grsai, or aginti login venice.",
|
|
251
|
+
"For raster image, cover, poster, illustration, photo, and logo-concept tasks, write a clear visual prompt, use generate_image when the optional GRS AI or Venice image key is available, save outputs under artifacts/images, and send the selected image to the canvas. generate_image is raster-only: if SVG/vector is requested, either create deterministic SVG/LaTeX/HTML with file tools or return a PNG fallback with requestedFormat=svg and actualFormat=png. If keys are missing, ask the user to run /auxiliary grsai, aginti login grsai, or aginti login venice.",
|
|
252
252
|
tools: ["auxiliary:image_generation", "files", "canvas"],
|
|
253
253
|
},
|
|
254
254
|
aaps: {
|
package/web.js
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
setProviderKey,
|
|
40
40
|
} from "./src/project.js";
|
|
41
41
|
import { buildCapabilityReport } from "./src/capabilities.js";
|
|
42
|
+
import { generateImage } from "./src/auxiliary-tools.js";
|
|
42
43
|
import { listExternalSkillPacks, listSkills } from "./src/skill-library.js";
|
|
43
44
|
import { platformInfo, platformLabel, platformSetupHints } from "./src/platform.js";
|
|
44
45
|
import { normalizeLanguage } from "./src/i18n.js";
|
|
@@ -1585,6 +1586,28 @@ app.post("/api/mcp", async (req, res) => {
|
|
|
1585
1586
|
}
|
|
1586
1587
|
});
|
|
1587
1588
|
|
|
1589
|
+
app.post("/api/auxiliary/generate-image", async (req, res) => {
|
|
1590
|
+
try {
|
|
1591
|
+
const body = req.body || {};
|
|
1592
|
+
const preferences = normalizePreferencePayload({}, db.getPreferences());
|
|
1593
|
+
const config = buildRunConfig({
|
|
1594
|
+
...preferences,
|
|
1595
|
+
...body,
|
|
1596
|
+
goal: "auxiliary image generation",
|
|
1597
|
+
commandCwd: body.commandCwd || body.cwd || preferences.commandCwd || baseDir,
|
|
1598
|
+
allowFileTools: true,
|
|
1599
|
+
allowAuxiliaryTools: true,
|
|
1600
|
+
auxiliaryProvider: body.provider || body.auxiliaryProvider || preferences.auxiliaryProvider,
|
|
1601
|
+
auxiliaryModel: body.model || body.auxiliaryModel || preferences.auxiliaryModel,
|
|
1602
|
+
});
|
|
1603
|
+
const result = await generateImage(body, config);
|
|
1604
|
+
const status = result.ok ? 200 : result.blocked ? 403 : 400;
|
|
1605
|
+
res.status(status).json(result);
|
|
1606
|
+
} catch (error) {
|
|
1607
|
+
res.status(400).json({ ok: false, toolName: "generate_image", error: error instanceof Error ? error.message : String(error) });
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1588
1611
|
app.post("/api/keys/:provider", async (req, res) => {
|
|
1589
1612
|
try {
|
|
1590
1613
|
const result = await setProviderKey(baseDir, req.params.provider, req.body?.apiKey || req.body?.key || "");
|