Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,14 @@

# ── Secrets (not merged from config.json, env only) ────────────────────
# OPENAI_API_KEY=sk-...
# XAI_API_KEY=xai-...
# GEMINI_API_KEY=AI...
# ATLASCLOUD_API_KEY=apikey-...
# MINIMAX_API_KEY=... # MiniMax image generation (image-01 / image-01-live)

# ── MiniMax image provider ─────────────────────────────────────────────
# IMA2_MINIMAX_REGION=global_en # global_en (default) | cn_zh
# IMA2_MINIMAX_GLOBAL_BASE_URL=https://api.minimax.io/v1
# IMA2_MINIMAX_CN_BASE_URL=https://api.minimaxi.com/v1
# IMA2_MINIMAX_IMAGE_MODEL_DEFAULT=image-01
# IMA2_MINIMAX_GENERATION_TIMEOUT_MS=120000
12 changes: 6 additions & 6 deletions bin/commands/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { join } from "node:path";
import { errInfo } from "../../lib/errInfo.js";
const VALID_MODES = new Set(["auto", "direct"]);
const VALID_MODERATION = new Set(["auto", "low"]);
const VALID_PROVIDERS = new Set(["auto", "oauth", "api", "grok", "grok-api", "agy", "gemini-api", "atlascloud"]);
const KNOWN_IMAGE_MODELS = new Set(["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gpt-5.3-codex-spark", "grok-imagine-image", "grok-imagine-image-quality", "nano-banana-2", "nano-banana-pro"]);
const VALID_PROVIDERS = new Set(["auto", "oauth", "api", "grok", "grok-api", "agy", "gemini-api", "atlascloud", "minimax"]);
const KNOWN_IMAGE_MODELS = new Set(["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gpt-5.3-codex-spark", "grok-imagine-image", "grok-imagine-image-quality", "nano-banana-2", "nano-banana-pro", "image-01", "image-01-live"]);

const SPEC = {
flags: {
Expand Down Expand Up @@ -45,9 +45,9 @@ const HELP = `
-s, --size <WxH>
-o, --out <file>
--json
--model <gpt-5.5|gpt-5.4|gpt-5.4-mini|gpt-5.6-sol|gpt-5.6-terra|gpt-5.6-luna|gpt-5.3-codex-spark|grok-imagine-image|grok-imagine-image-quality|nano-banana-2|nano-banana-pro> Default: gpt-5.6-luna
--model <gpt-5.5|gpt-5.4|gpt-5.4-mini|gpt-5.6-sol|gpt-5.6-terra|gpt-5.6-luna|gpt-5.3-codex-spark|grok-imagine-image|grok-imagine-image-quality|nano-banana-2|nano-banana-pro|image-01|image-01-live> Default: gpt-5.6-luna
Aliases: luna, sol, terra, spark
--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud>
--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud|minimax>
Provider (oauth = GPT OAuth; grok = xAI Grok; agy/gemini-api = Gemini)
--mode <auto|direct> Prompt handling mode. Default: auto
--moderation <auto|low> Default: low
Expand All @@ -67,11 +67,11 @@ export default async function editCmd(argv: string[]) {
if (!VALID_MODES.has(String(args.mode))) die(2, "--mode must be one of: auto, direct");
if (!VALID_MODERATION.has(String(args.moderation))) die(2, "--moderation must be one of: auto, low");
if (args.provider && !VALID_PROVIDERS.has(String(args.provider))) {
die(2, "--provider must be one of: auto, oauth, api, grok, grok-api, agy, gemini-api, atlascloud");
die(2, "--provider must be one of: auto, oauth, api, grok, grok-api, agy, gemini-api, atlascloud, minimax");
}
const model = canonicalizeImageModel(args.model);
if (model && !KNOWN_IMAGE_MODELS.has(model)) {
die(2, "--model must be one of: gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.3-codex-spark, grok-imagine-image, grok-imagine-image-quality, nano-banana-2, nano-banana-pro");
die(2, "--model must be one of: gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.3-codex-spark, grok-imagine-image, grok-imagine-image-quality, nano-banana-2, nano-banana-pro, image-01, image-01-live");
}
const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh", "max"]);
if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
Expand Down
2 changes: 1 addition & 1 deletion bin/commands/gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const HELP = `
--server <url> Override server URL
--model <model|lane/model> Bare IDs must be unique across lanes
Core aliases: luna, sol, terra, spark
--provider <oauth|api|grok|grok-api|agy|gemini-api|atlascloud|runway|higgsfield>
--provider <oauth|api|grok|grok-api|agy|gemini-api|atlascloud|minimax|runway|higgsfield>
'auto' was removed; choose a lane explicitly
--mode <auto|direct> Core lanes only. Default: auto
--moderation <auto|low> Core lanes only. Default: low
Expand Down
6 changes: 3 additions & 3 deletions bin/commands/multimode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const HELP = `
--json
--model <gpt-5.5|gpt-5.4|gpt-5.4-mini|gpt-5.6-sol|gpt-5.6-terra|gpt-5.6-luna|gpt-5.3-codex-spark|grok-imagine-image|grok-imagine-image-quality|nano-banana-2|nano-banana-pro> Default: gpt-5.6-luna
Aliases: luna, sol, terra, spark
--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud>
--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud|minimax>
Provider (oauth = GPT OAuth; grok = xAI Grok; agy/gemini-api = Gemini)
--mode <auto|direct> Prompt handling mode. Default: auto
--ref <file> Attach reference image (repeatable, max ${MAX_REFERENCE_COUNT})
Expand All @@ -72,11 +72,11 @@ export default async function multimodeCmd(argv: string[]) {
const prompt = args.positional.join(" ");
if (!prompt) die(2, "prompt required");

const VALID_PROVIDERS = new Set(["auto", "oauth", "api", "grok", "grok-api", "agy", "gemini-api", "atlascloud"]);
const VALID_PROVIDERS = new Set(["auto", "oauth", "api", "grok", "grok-api", "agy", "gemini-api", "atlascloud", "minimax"]);
const VALID_MODES = new Set(["auto", "direct"]);
const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh", "max"]);
if (args.provider && !VALID_PROVIDERS.has(String(args.provider))) {
die(2, "--provider must be one of: auto, oauth, api, grok, grok-api, agy, gemini-api, atlascloud");
die(2, "--provider must be one of: auto, oauth, api, grok, grok-api, agy, gemini-api, atlascloud, minimax");
}
if (!VALID_MODES.has(String(args.mode))) die(2, "--mode must be one of: auto, direct");
if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
Expand Down
8 changes: 4 additions & 4 deletions bin/commands/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ const HELP = `
ima2 node <subcommand> [options]

Subcommands:
generate <prompt...> [--parent <nodeId>] [--ref <file>...] [--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud>] [--no-stream] [...gen-style flags]
generate <prompt...> [--parent <nodeId>] [--ref <file>...] [--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud|minimax>] [--no-stream] [...gen-style flags]
show <nodeId> [--json]

Generate options:
--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud> Provider for this request
--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud|minimax> Provider for this request
`;

const GEN_FLAGS = {
Expand Down Expand Up @@ -57,10 +57,10 @@ async function generateSub(argv: string[]) {
const prompt = args.positional.join(" ");
if (!prompt) die(2, "prompt required");
const refs = (Array.isArray(args.ref) ? args.ref : []) as string[];
const VALID_PROVIDERS = new Set(["auto", "oauth", "api", "grok", "grok-api", "agy", "gemini-api", "atlascloud"]);
const VALID_PROVIDERS = new Set(["auto", "oauth", "api", "grok", "grok-api", "agy", "gemini-api", "atlascloud", "minimax"]);
const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh", "max"]);
if (args.provider && !VALID_PROVIDERS.has(String(args.provider))) {
die(2, "--provider must be one of: auto, oauth, api, grok, grok-api, agy, gemini-api, atlascloud");
die(2, "--provider must be one of: auto, oauth, api, grok, grok-api, agy, gemini-api, atlascloud, minimax");
}
if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
die(2, "--reasoning-effort must be one of: none, low, medium, high, xhigh, max");
Expand Down
4 changes: 2 additions & 2 deletions bin/lib/modelResolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { canonicalizeImageModel } from "./model-aliases.js";

export type Lane = "oauth" | "api" | "grok" | "grok-api" | "agy" | "gemini-api" | "atlascloud" | "runway" | "higgsfield";
export type Lane = "oauth" | "api" | "grok" | "grok-api" | "agy" | "gemini-api" | "atlascloud" | "minimax" | "runway" | "higgsfield";
export type LaneStatus = "ready" | "locked" | "disconnected" | "key-missing";

export interface ModelEntry {
Expand All @@ -23,7 +23,7 @@ export type ResolveResult =
| { ok: false; code: string; message: string; extra?: Record<string, unknown> };

const LANES: readonly Lane[] = [
"oauth", "api", "grok", "grok-api", "agy", "gemini-api", "atlascloud", "runway", "higgsfield",
"oauth", "api", "grok", "grok-api", "agy", "gemini-api", "atlascloud", "minimax", "runway", "higgsfield",
];

function failure(code: string, message: string, extra?: Record<string, unknown>): ResolveResult {
Expand Down
10 changes: 10 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ export const config = {
videoTimeoutMs: pickInt(env.IMA2_GROK_VIDEO_TIMEOUT_MS, fileCfg.grokProvider?.videoTimeoutMs, 900_000),
videoDownloadTimeoutMs: pickInt(env.IMA2_GROK_VIDEO_DOWNLOAD_TIMEOUT_MS, fileCfg.grokProvider?.videoDownloadTimeoutMs, 120_000),
},
// Direct MiniMax image-generation provider (text-to-image / image-to-image).
// Region selects the global (.io) or China (.minimaxi.com) OpenAI-compatible
// base URL; the regional fields are shared with the MiniMax text endpoints.
minimaxProvider: {
defaultImageModel: pickStr(env.IMA2_MINIMAX_IMAGE_MODEL_DEFAULT, fileCfg.minimaxProvider?.defaultImageModel, "image-01"),
region: pickStr(env.IMA2_MINIMAX_REGION, fileCfg.minimaxProvider?.region, "global_en"),
globalBaseUrl: pickStr(env.IMA2_MINIMAX_GLOBAL_BASE_URL, fileCfg.minimaxProvider?.globalBaseUrl, "https://api.minimax.io/v1"),
cnBaseUrl: pickStr(env.IMA2_MINIMAX_CN_BASE_URL, fileCfg.minimaxProvider?.cnBaseUrl, "https://api.minimaxi.com/v1"),
generationTimeoutMs: pickInt(env.IMA2_MINIMAX_GENERATION_TIMEOUT_MS, fileCfg.minimaxProvider?.generationTimeoutMs, 120_000),
},
log: {
level: pickStr(env.IMA2_LOG_LEVEL, fileCfg.log?.level, defaultLogLevelForEnv(env)),
pretty: env.NODE_ENV !== "production",
Expand Down
10 changes: 10 additions & 0 deletions config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,16 @@ export const config = {
videoTimeoutMs: pickInt(env.IMA2_GROK_VIDEO_TIMEOUT_MS, fileCfg.grokProvider?.videoTimeoutMs, 900_000),
videoDownloadTimeoutMs: pickInt(env.IMA2_GROK_VIDEO_DOWNLOAD_TIMEOUT_MS, fileCfg.grokProvider?.videoDownloadTimeoutMs, 120_000),
},
// Direct MiniMax image-generation provider (text-to-image / image-to-image).
// Region selects the global (.io) or China (.minimaxi.com) OpenAI-compatible
// base URL; the regional fields are shared with the MiniMax text endpoints.
minimaxProvider: {
defaultImageModel: pickStr(env.IMA2_MINIMAX_IMAGE_MODEL_DEFAULT, fileCfg.minimaxProvider?.defaultImageModel, "image-01"),
region: pickStr(env.IMA2_MINIMAX_REGION, fileCfg.minimaxProvider?.region, "global_en"),
globalBaseUrl: pickStr(env.IMA2_MINIMAX_GLOBAL_BASE_URL, fileCfg.minimaxProvider?.globalBaseUrl, "https://api.minimax.io/v1"),
cnBaseUrl: pickStr(env.IMA2_MINIMAX_CN_BASE_URL, fileCfg.minimaxProvider?.cnBaseUrl, "https://api.minimaxi.com/v1"),
generationTimeoutMs: pickInt(env.IMA2_MINIMAX_GENERATION_TIMEOUT_MS, fileCfg.minimaxProvider?.generationTimeoutMs, 120_000),
},
log: {
level: pickStr(env.IMA2_LOG_LEVEL, fileCfg.log?.level, defaultLogLevelForEnv(env)),
pretty: env.NODE_ENV !== "production",
Expand Down
7 changes: 4 additions & 3 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ Since 3.0.0, `ima2 gen` and generate-mode `ima2 video` are **fail-closed**: they
target through the lane catalog (`GET /api/models`) and exit 2 with `NO_DEFAULT_MODEL` when no
`--model <lane>/<model>`, `--provider <lane>`, or persisted `ima2 defaults set image|video`
target applies. Their `--provider` accepts explicit lanes only
(`oauth|api|grok|grok-api|agy|gemini-api|atlascloud|runway|higgsfield`); `--provider auto` exits 2 with
(`oauth|api|grok|grok-api|agy|gemini-api|atlascloud|minimax|runway|higgsfield`); `--provider auto` exits 2 with
`PROVIDER_AUTO_REMOVED`. Inspect lanes and models with `ima2 models [--kind image|video] [--lane <lane>] [--json]`.

`edit`, `multimode`, and `node generate` keep the legacy surface for now: `--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud>`, `--reasoning-effort {none\|low\|medium\|high\|xhigh\|max}`, `--web-search` / `--no-web-search`, `--model`, `--mode`, `--moderation`, `--ref <file>` (repeatable, up to 5 where supported), `-q low|medium|high`, `-n <count>`, `-o <file>`.
`edit`, `multimode`, and `node generate` keep the legacy surface for now: `--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud|minimax>`, `--reasoning-effort {none\|low\|medium\|high\|xhigh\|max}`, `--web-search` / `--no-web-search`, `--model`, `--mode`, `--moderation`, `--ref <file>` (repeatable, up to 5 where supported), `-q low|medium|high`, `-n <count>`, `-o <file>`.

Provider override semantics:

Expand All @@ -86,6 +86,7 @@ Provider override semantics:
- `agy` spawns the Antigravity CLI to generate via Google Gemini (`nano-banana-2`). Fixed 1024×1024 JPEG output, max 3 refs. No web search, quality, size, or mask controls. If `agy` is not on the server process PATH, ima2 also checks common user-local installs such as `~/.local/bin/agy`; set `IMA2_AGY_BIN=/absolute/path/to/agy` to force a specific binary.
- `gemini-api` calls the Google Generative Language API directly. Models: `nano-banana-2` (Gemini 3.1 Flash Image) and `nano-banana-pro` (Gemini 3 Pro Image). Use `--model nano-banana-2` or `--model nano-banana-pro` to select. Supports `--size` for aspect ratio and resolution (512px–4K) on the direct API path; Vertex AI ignores aspect/size. Requires `GEMINI_API_KEY` or a Vertex AI service account (`VERTEX_SERVICE_ACCOUNT_JSON`). Switching from `agy` or `gemini-api` provider auto-selects the corresponding Gemini model; switching away resets to the GPT default.
- `atlascloud` calls Atlas Cloud's Media API directly. Models: `openai/gpt-image-2/text-to-image` for text-to-image and `openai/gpt-image-2/edit` when references are attached. Requires `ATLASCLOUD_API_KEY`; web search, reasoning, mask, and video controls are ignored.
- `minimax` calls the MiniMax image-generation API directly at `POST /v1/image_generation`. Models: `image-01` for text-to-image and `image-01-live` when a reference image is attached (mapped to the `subject_reference` field). Region selects the global (`https://api.minimax.io/v1`, default) or China (`https://api.minimaxi.com/v1`, `IMA2_MINIMAX_REGION=cn_zh`) base URL. `--size` maps to the closest supported `aspect_ratio`; responses are returned as URLs or base64. Requires `MINIMAX_API_KEY`; web search, reasoning, mask, and video controls are ignored, and image-to-image supports at most one subject reference.
- `runway` / `higgsfield` (gen/video only) route through the MCP async pipeline (`POST /api/mcp/generate` + SSE wait). Runway requires an MCP connection; Higgsfield stays catalog-only (`locked`) until a paid plan. MCP lanes accept `-n 1` only, gallery filenames for `--ref`, and reject core-only flags with `FLAG_NOT_SUPPORTED`.
- `auto` preserves route default behavior and currently resolves to GPT OAuth unless server routing changes (edit/multimode/node only; removed from gen/video in 3.0.0).

Expand Down Expand Up @@ -135,7 +136,7 @@ mockup`.
For dense or critical text, keep the text large and explicit. Exact placement,
small text, and pixel-perfect typography can still need iteration or post-editing.

Multimode-specific flags include `--max-images <1..24>` by default (configurable through `IMA2_MAX_GENERATED_IMAGES`), `--ref <file>` (repeatable, max 5), `--mode <auto|direct>`, `--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud>`, and `--show-partial`. `ima2 edit --mask` remains intentionally deferred to #31 because current mask plumbing is guided edit rather than guaranteed true masked/inpaint semantics.
Multimode-specific flags include `--max-images <1..24>` by default (configurable through `IMA2_MAX_GENERATED_IMAGES`), `--ref <file>` (repeatable, max 5), `--mode <auto|direct>`, `--provider <auto|oauth|api|grok|grok-api|agy|gemini-api|atlascloud|minimax>`, and `--show-partial`. `ima2 edit --mask` remains intentionally deferred to #31 because current mask plumbing is guided edit rather than guaranteed true masked/inpaint semantics.

## Video

Expand Down
6 changes: 5 additions & 1 deletion docs/migration/runtime-test-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Generated by `npm run test:inventory` (script: `scripts/classify-tests.mjs`).

_Tests considered "runtime-importing" if they import from `../lib/`, `../routes/`, `../bin/`, `../server`, or `../config`._

Total: 336 (runtime: 142, contract: 194)
Total: 340 (runtime: 144, contract: 196)

## Runtime-importing tests
- `tests/agent-mode-auto-planner-contract.test.ts`
Expand Down Expand Up @@ -94,6 +94,8 @@ Total: 336 (runtime: 142, contract: 194)
- `tests/mcp-temp-references.test.ts`
- `tests/mcp-token-store.test.ts`
- `tests/mcp-upscale-params.test.ts`
- `tests/minimax-key-validation-route.test.ts`
- `tests/minimax-provider-contract.test.ts`
- `tests/model-default-projection-contract.test.ts`
- `tests/models-endpoint-contract.test.ts`
- `tests/node-parent-source-contract.test.ts`
Expand Down Expand Up @@ -271,6 +273,7 @@ Total: 336 (runtime: 142, contract: 194)
- `tests/mcp-schema-spike-contract.test.ts`
- `tests/mcp-selection-helpers.test.ts`
- `tests/mcp-settings-states-contract.test.ts`
- `tests/minimax-ui-registration-contract.test.ts`
- `tests/mobile-compose-sheet-accessibility-contract.test.js`
- `tests/mobile-composer-tray-contract.test.js`
- `tests/mobile-generate-entry-contract.test.js`
Expand Down Expand Up @@ -302,6 +305,7 @@ Total: 336 (runtime: 142, contract: 194)
- `tests/oauth-proxy-edit-mask-contract.test.js`
- `tests/package-smoke.test.js`
- `tests/png-info-contract.test.js`
- `tests/portal-dropdown-scroll-dismiss-contract.test.ts`
- `tests/prompt-curated-search-contract.test.js`
- `tests/prompt-discovery-ui-contract.test.js`
- `tests/prompt-import-dialog-ui-contract.test.js`
Expand Down
11 changes: 10 additions & 1 deletion lib/agentImageVideoGen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { generateViaResponses } from "./responsesImageAdapter.js";
import { generateViaGrok, type GrokReferenceImage } from "./grokImageAdapter.js";
import { generateViaAgy } from "./agyImageAdapter.js";
import { generateViaAtlasCloud } from "./atlasCloudImageAdapter.js";
import { generateViaMinimax } from "./minimaxImageAdapter.js";
import { DEFAULT_GROK_PLANNER_MODEL } from "../config.js";
import { generateVideoViaGrok, type GrokVideoGenerateResult } from "./grokVideoAdapter.js";
import { GROK_VIDEO_MODEL_15, GROK_VIDEO_MODEL_BASE } from "./imageModels.js";
Expand Down Expand Up @@ -106,6 +107,14 @@ async function generateAgentImage(
signal: options.signal ?? undefined,
references: await loadAgentCurrentImageReferences(ctx, sessionId, options.sourceImagePolicy ?? "none"),
})
: activeProvider === "minimax"
? await generateViaMinimax(`${manifest}\n\nUser request:\n${prompt}`, ctx, {
model: effectiveModel,
size: providerOptions.size,
requestId,
signal: options.signal ?? undefined,
references: await loadAgentCurrentImageReferences(ctx, sessionId, options.sourceImagePolicy ?? "none"),
})
: activeProvider === "grok"
? await generateViaGrok(`${manifest}\n\nUser request:\n${prompt}`, ctx, {
model: effectiveModel,
Expand All @@ -132,7 +141,7 @@ async function generateAgentImage(
signal: options.signal,
},
);
const format = activeProvider === "grok" || activeProvider === "agy" || activeProvider === "atlascloud"
const format = activeProvider === "grok" || activeProvider === "agy" || activeProvider === "atlascloud" || activeProvider === "minimax"
? imageFormatFromMime(("mime" in response ? response.mime : undefined) || detectImageMimeFromB64(response.b64) || "image/jpeg")
: options.format ?? "png";
const image = await persistAgentImage(ctx, sessionId, prompt, format, providerOptions.size, requestId, response, {
Expand Down
Loading
Loading