@bacnh85/pi-web 0.9.1 → 0.9.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/CHANGELOG.md +17 -0
- package/README.md +3 -0
- package/extensions/index.ts +3 -3
- package/extensions/lib/imageapi.ts +23 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.2 (2026-09-13)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
Review hardening of the `web_image` fallback chain (3 review rounds):
|
|
8
|
+
|
|
9
|
+
- **Cancellation semantics**: an aborted call now surfaces `AbortError`
|
|
10
|
+
immediately — before any provider client construction or fetch — instead of
|
|
11
|
+
walking the chain and reporting "all providers failed". An abort landing
|
|
12
|
+
mid-generation normalizes to `AbortError` with the in-flight provider error
|
|
13
|
+
preserved as `cause`; foreign abort-named errors from upstreams are recorded
|
|
14
|
+
as provider notes and the chain continues.
|
|
15
|
+
- **`n` transparency**: when the gemini web tier returns fewer images than the
|
|
16
|
+
requested `n`, the result states it explicitly (`n` applies to the
|
|
17
|
+
`zai`/`custom` API providers; the gemini web tier returns its own count).
|
|
18
|
+
`out_dir` now resolves against the session cwd, not the process cwd.
|
|
19
|
+
|
|
3
20
|
## 0.9.1 (2026-09-13)
|
|
4
21
|
|
|
5
22
|
### Fixed
|
package/README.md
CHANGED
|
@@ -290,6 +290,9 @@ paths **plus inline image blocks** (multimodal models see the render
|
|
|
290
290
|
immediately). `details` reports the winning provider, model, and fallback
|
|
291
291
|
attempts.
|
|
292
292
|
|
|
293
|
+
`n` (1–4) applies to the API providers (`zai`/`custom`); the Gemini web tier
|
|
294
|
+
returns its own image count (surfaced as a provider note when fewer than `n`).
|
|
295
|
+
|
|
293
296
|
**Guardrails** (soft, in-memory): per-provider `WEB_IMAGE_MIN_INTERVAL_MS`
|
|
294
297
|
(default 5 s) and a `WEB_IMAGE_DAILY_CAP` (default 20/day, applied to the
|
|
295
298
|
Gemini web tier only — keyed APIs are billed upstream and stay uncapped).
|
package/extensions/index.ts
CHANGED
|
@@ -541,7 +541,7 @@ export default function piWebExtension(pi: ExtensionAPI) {
|
|
|
541
541
|
{ default: "auto", description: "auto = gemini → zai (if ZAI_API_KEY) → custom (if WEB_IMAGE_API_BASE_URL); pin one to skip fallback." },
|
|
542
542
|
)),
|
|
543
543
|
model: Type.Optional(Type.String({ description: "Provider-specific model (e.g. glm-image, or a Gemini image-capable model id). Omit for the provider default." })),
|
|
544
|
-
n: Type.Optional(Type.Number({ default: 1, description: "Number of images, 1-4." })),
|
|
544
|
+
n: Type.Optional(Type.Number({ default: 1, description: "Number of images, 1-4 (applies to zai/custom; the gemini web tier returns its own count)." })),
|
|
545
545
|
out_dir: Type.Optional(Type.String({ description: "Directory for saved images (default: fresh temp dir)." })),
|
|
546
546
|
...sharedControlSchema,
|
|
547
547
|
}),
|
|
@@ -552,7 +552,7 @@ export default function piWebExtension(pi: ExtensionAPI) {
|
|
|
552
552
|
const n = Math.min(Math.max(Math.trunc((params.n as number) ?? 1) || 1, 1), 4);
|
|
553
553
|
const timeoutMs = Math.min(Math.max((params.timeout_ms as number) ?? 180_000, 10_000), 600_000);
|
|
554
554
|
const outDir = params.out_dir
|
|
555
|
-
? path.resolve(String(params.out_dir))
|
|
555
|
+
? path.resolve(cwd, String(params.out_dir))
|
|
556
556
|
: await fs.promises.mkdtemp(path.join(os.tmpdir(), "pi-web-image-"));
|
|
557
557
|
const result = await generateImageWithFallback({
|
|
558
558
|
prompt,
|
|
@@ -574,7 +574,7 @@ export default function piWebExtension(pi: ExtensionAPI) {
|
|
|
574
574
|
...(result.urls.length
|
|
575
575
|
? [`Not saved (image host unreachable from this machine — open directly):`, ...result.urls.map((u) => ` ${u}`)]
|
|
576
576
|
: []),
|
|
577
|
-
result.attempts.length ? `
|
|
577
|
+
result.attempts.length ? `Provider notes: ${result.attempts.join(" | ")}` : null,
|
|
578
578
|
].filter(Boolean).join("\n");
|
|
579
579
|
const content: Array<{ type: "text"; text: string } | { type: "image"; data: string; mimeType: string }> = [
|
|
580
580
|
{ type: "text" as const, text },
|
|
@@ -308,6 +308,13 @@ export async function generateImageWithFallback(params: ImageChainParams): Promi
|
|
|
308
308
|
const chain = chainFor(params.provider);
|
|
309
309
|
const attempts: string[] = [];
|
|
310
310
|
for (const provider of chain) {
|
|
311
|
+
// Cancelled calls skip fallback entirely — before any provider client
|
|
312
|
+
// construction or fetch invocation.
|
|
313
|
+
if (params.signal?.aborted) {
|
|
314
|
+
const abortErr = new Error("web_image aborted");
|
|
315
|
+
abortErr.name = "AbortError";
|
|
316
|
+
throw abortErr;
|
|
317
|
+
}
|
|
311
318
|
const configured =
|
|
312
319
|
provider === "gemini" ? true : provider === "zai" ? Boolean(params.apiConfig.zai) : Boolean(params.apiConfig.custom);
|
|
313
320
|
if (!configured) {
|
|
@@ -356,8 +363,24 @@ export async function generateImageWithFallback(params: ImageChainParams): Promi
|
|
|
356
363
|
});
|
|
357
364
|
}
|
|
358
365
|
imageRateRecord(provider);
|
|
366
|
+
if (provider === "gemini" && params.n && params.n > 1 && result.paths.length < params.n) {
|
|
367
|
+
attempts.push(`gemini: n=${params.n} requested — the gemini web tier returns its own image count (${result.paths.length}); n applies to zai/custom`);
|
|
368
|
+
}
|
|
359
369
|
return { provider, model: result.model, paths: result.paths, urls: result.urls ?? [], attempts };
|
|
360
370
|
} catch (err) {
|
|
371
|
+
// Cancellation is not a provider failure: rethrow so aborted tool calls
|
|
372
|
+
// surface as AbortError instead of an "all providers failed" listing —
|
|
373
|
+
// even when a genuine provider error (AuthError, a failed save, …) was
|
|
374
|
+
// the error in flight when the abort landed.
|
|
375
|
+
if (params.signal?.aborted) {
|
|
376
|
+
if ((err as Error)?.name === "AbortError") throw err;
|
|
377
|
+
// cause keeps the in-flight provider error for diagnostics.
|
|
378
|
+
const abortErr = new Error("web_image aborted", { cause: err });
|
|
379
|
+
abortErr.name = "AbortError";
|
|
380
|
+
throw abortErr;
|
|
381
|
+
}
|
|
382
|
+
// A foreign AbortError-named error (not from the caller's signal) is a
|
|
383
|
+
// provider failure like any other — record it and keep the chain going.
|
|
361
384
|
attempts.push(`${provider}: ${provider === "gemini" ? describeGeminiError(err) : describeImageApiError(err)}`);
|
|
362
385
|
}
|
|
363
386
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacnh85/pi-web",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Pi extension for web search, page extraction, Firecrawl scraping/crawling, Crawl4AI headless browser crawling, Gemini web-tier research, and free upstream image generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|