pi-banana 2.5.0 → 2.5.2
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/extensions/index.ts +29 -5
- package/extensions/providers.ts +1 -1
- package/extensions/wizard.ts +48 -0
- package/package.json +1 -1
package/extensions/index.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
22
22
|
import { resolveProvider, openaiGenerateImage, openaiEditImage, openaiVision, sizeFor } from "./providers.ts";
|
|
23
|
-
import { runBananaSetup } from "./wizard.ts";
|
|
23
|
+
import { runBananaSetup, configureBananaProvider } from "./wizard.ts";
|
|
24
24
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
25
25
|
import {
|
|
26
26
|
Box,
|
|
@@ -53,7 +53,7 @@ const ASPECT_RATIOS = [
|
|
|
53
53
|
"21:9",
|
|
54
54
|
] as const;
|
|
55
55
|
const IMAGE_SIZES = ["1K", "2K", "4K"] as const;
|
|
56
|
-
const IMAGE_QUALITY = ["lite", "fast", "high"] as const;
|
|
56
|
+
const IMAGE_QUALITY = ["lite", "fast", "high", "max"] as const;
|
|
57
57
|
const VISION_QUALITY = ["fast", "high"] as const;
|
|
58
58
|
type ImageQuality = (typeof IMAGE_QUALITY)[number];
|
|
59
59
|
|
|
@@ -67,6 +67,7 @@ const MODEL_FOR_QUALITY: Record<ImageQuality, string> = {
|
|
|
67
67
|
lite: "gemini-3.1-flash-lite-image", // Nano Banana 2 Lite (ultra-low latency, cheapest)
|
|
68
68
|
fast: "gemini-3.1-flash-image", // Nano Banana 2
|
|
69
69
|
high: "gemini-3-pro-image", // Nano Banana Pro
|
|
70
|
+
max: "gemini-3-pro-image", // Google fallback; configured provider maps max to GPT Image 2
|
|
70
71
|
};
|
|
71
72
|
|
|
72
73
|
const DEFAULT_OUTPUT_DIR =
|
|
@@ -250,6 +251,29 @@ export default function (pi: ExtensionAPI) {
|
|
|
250
251
|
},
|
|
251
252
|
});
|
|
252
253
|
|
|
254
|
+
pi.registerTool({
|
|
255
|
+
name: "banana_configure",
|
|
256
|
+
label: "Banana Configure",
|
|
257
|
+
description:
|
|
258
|
+
"Configure banana to generate images through an OpenAI-compatible provider (mantice) instead of Google. " +
|
|
259
|
+
"Probes the endpoint, auto-selects image models per quality tier, and saves the configuration. " +
|
|
260
|
+
"Call this when the user asks to set up or switch the image provider.",
|
|
261
|
+
promptSnippet: "Configure the banana image provider (mantice) non-interactively.",
|
|
262
|
+
parameters: Type.Object({
|
|
263
|
+
baseUrl: Type.String({ description: "OpenAI-compatible base URL, e.g. https://llm.fornace.net/v1" }),
|
|
264
|
+
apiKey: Type.Optional(Type.String({
|
|
265
|
+
description: "API key. Omit to reuse the mantice/google key already stored in pi credentials.",
|
|
266
|
+
})),
|
|
267
|
+
visionModel: Type.Optional(Type.String({
|
|
268
|
+
description: "Model for image analysis. Omit to auto-detect (e.g. fornace-vision).",
|
|
269
|
+
})),
|
|
270
|
+
}),
|
|
271
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
272
|
+
const report = await configureBananaProvider(ctx, params);
|
|
273
|
+
return { content: [{ type: "text", text: report }], details: {} };
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
253
277
|
pi.registerTool({
|
|
254
278
|
name: "banana_image",
|
|
255
279
|
label: "Banana Image",
|
|
@@ -288,7 +312,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
288
312
|
quality: Type.Optional(
|
|
289
313
|
StringEnum(IMAGE_QUALITY, {
|
|
290
314
|
description:
|
|
291
|
-
"'lite' = Nano Banana 2 Lite (ultra-low latency, cheapest). 'fast' = Nano Banana 2 (default, ~3s, cheap). 'high' = Nano Banana Pro (
|
|
315
|
+
"'lite' = Nano Banana 2 Lite (ultra-low latency, cheapest). 'fast' = Nano Banana 2 (default, ~3s, cheap). 'high' = Nano Banana Pro (strong editing). 'max' = premium benchmark tier, GPT Image 2 when the configured provider supports it.",
|
|
292
316
|
default: DEFAULT_QUALITY,
|
|
293
317
|
}),
|
|
294
318
|
),
|
|
@@ -339,7 +363,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
339
363
|
const provider = await resolveProvider(ctx);
|
|
340
364
|
if (provider.kind === "openai-compat") {
|
|
341
365
|
const { config } = provider;
|
|
342
|
-
const oaiModel = config.models[quality] ?? config.models.fast;
|
|
366
|
+
const oaiModel = config.models[quality] ?? config.models.high ?? config.models.fast;
|
|
343
367
|
if (!oaiModel) {
|
|
344
368
|
throw new Error(`banana: no model mapped for quality "${quality}". Run /banana-setup.`);
|
|
345
369
|
}
|
|
@@ -392,7 +416,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
392
416
|
content: [
|
|
393
417
|
{
|
|
394
418
|
type: "text",
|
|
395
|
-
text: `🎨 ${verbLabel} ${aspectRatio} ${imageSize} image with ${quality === "lite" ? "Nano Banana 2 Lite" : quality === "fast" ? "Nano Banana 2" : "Nano Banana Pro"}…`,
|
|
419
|
+
text: `🎨 ${verbLabel} ${aspectRatio} ${imageSize} image with ${quality === "lite" ? "Nano Banana 2 Lite" : quality === "fast" ? "Nano Banana 2" : quality === "high" ? "Nano Banana Pro" : "GPT Image 2"}…`,
|
|
396
420
|
},
|
|
397
421
|
],
|
|
398
422
|
details: { model, aspectRatio, imageSize, quality, editing },
|
package/extensions/providers.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { resolve } from "path";
|
|
|
18
18
|
export interface OpenAiCompatConfig {
|
|
19
19
|
baseUrl: string;
|
|
20
20
|
apiKey: string;
|
|
21
|
-
models: { lite?: string; fast?: string; high?: string };
|
|
21
|
+
models: { lite?: string; fast?: string; high?: string; max?: string };
|
|
22
22
|
visionModel?: string;
|
|
23
23
|
/** Optional provider id whose key lives in pi's auth.json (e.g. "mantice"). */
|
|
24
24
|
providerName?: string;
|
package/extensions/wizard.ts
CHANGED
|
@@ -20,6 +20,54 @@ function pickDefault(candidates: string[], hint: RegExp): string | undefined {
|
|
|
20
20
|
return candidates.find((m) => hint.test(m)) ?? candidates[0];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export interface ConfigureOptions {
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
apiKey?: string;
|
|
26
|
+
visionModel?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Non-interactive provider configuration: resolves the key, probes the
|
|
30
|
+
* endpoint, auto-assigns quality tiers, and persists to settings.json.
|
|
31
|
+
* Returns a human-readable report. Used by banana_configure and the wizard. */
|
|
32
|
+
export async function configureBananaProvider(ctx: any, opts: ConfigureOptions): Promise<string> {
|
|
33
|
+
const baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
34
|
+
let apiKey = opts.apiKey;
|
|
35
|
+
if (!apiKey && ctx?.modelRegistry) {
|
|
36
|
+
for (const p of ["mantice", "google"]) {
|
|
37
|
+
try {
|
|
38
|
+
apiKey = await ctx.modelRegistry.getApiKeyForProvider(p);
|
|
39
|
+
if (apiKey) break;
|
|
40
|
+
} catch { /* next */ }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!apiKey) apiKey = process.env.MANTICE_API_KEY;
|
|
44
|
+
if (!apiKey) throw new Error("No API key found: pass apiKey, log in via /login, or set MANTICE_API_KEY.");
|
|
45
|
+
|
|
46
|
+
const probe = await probeCapabilities(baseUrl, apiKey);
|
|
47
|
+
if (!probe.supportsImageGen || probe.imageModels.length === 0) {
|
|
48
|
+
throw new Error("Endpoint has no image generation models. Nothing was saved.");
|
|
49
|
+
}
|
|
50
|
+
const pick = (hint: RegExp, fallback?: string) =>
|
|
51
|
+
probe.imageModels.find((m) => hint.test(m)) ?? fallback ?? probe.imageModels[0];
|
|
52
|
+
const models = {
|
|
53
|
+
lite: pick(/lite|fast|turbo|mini/i),
|
|
54
|
+
fast: pick(/^(?!.*(lite|high|max|pro|ultra)).*$/i),
|
|
55
|
+
high: pick(/high|pro/i),
|
|
56
|
+
max: pick(/max|ultra/i),
|
|
57
|
+
};
|
|
58
|
+
const visionModel = opts.visionModel ?? probe.visionModels[0];
|
|
59
|
+
persistBananaConfig({ baseUrl, apiKey, models, ...(visionModel ? { visionModel } : {}) });
|
|
60
|
+
return [
|
|
61
|
+
`Configured banana on ${baseUrl}:`,
|
|
62
|
+
` lite = ${models.lite}`,
|
|
63
|
+
` fast = ${models.fast}`,
|
|
64
|
+
` high = ${models.high}`,
|
|
65
|
+
visionModel ? ` vision = ${visionModel}` : " vision = (google default kept)",
|
|
66
|
+
`Image edit endpoint: ${probe.supportsImageEdit ? "available" : "not available"}.`,
|
|
67
|
+
"banana_image and banana_vision now use this provider.",
|
|
68
|
+
].join("\n");
|
|
69
|
+
}
|
|
70
|
+
|
|
23
71
|
export async function runBananaSetup(ctx: any): Promise<void> {
|
|
24
72
|
const ui = ctx.ui;
|
|
25
73
|
ui.notify("Banana setup: configure an OpenAI-compatible image provider (mantice works).", "info");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-banana",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"description": "Generate, edit, and analyze images in pi using Google Nano Banana (image gen) and Gemini Vision (analysis). Inline terminal preview, reference-image editing, auto-save.",
|
|
5
5
|
"author": "Francesco Frapporti <effedue@gmail.com>",
|
|
6
6
|
"license": "MIT",
|