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.
@@ -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 (slower, top quality).",
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 },
@@ -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;
@@ -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.0",
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",