@astrofoundry/pi-astro 0.6.4 → 0.6.5

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,6 +20,7 @@ import {
20
20
  } from "./models.ts";
21
21
  import { parseTweaks } from "./parseTweaks.ts";
22
22
  import { estimateCost, formatUsd, hasPricing } from "./pricing.ts";
23
+ import { formatResolution, resolveResolution } from "./resolution.ts";
23
24
 
24
25
  const DEFAULT_SAVE_SUBDIR = ".gemini-images";
25
26
 
@@ -233,12 +234,16 @@ function saveImages(images: DecodedImage[], dir: string, prompt: string): string
233
234
 
234
235
  function renderPreview(model: ImageModel, p: Params, estimate: { estimatedUsd: number; breakdown: string }): string {
235
236
  const nImages = isImagen(model) ? p.number_of_images ?? 1 : 1;
237
+ const aspect = p.aspect_ratio ?? "1:1";
238
+ const size = p.image_size ?? "1K";
239
+ const resolution = formatResolution(resolveResolution(model, aspect, size));
236
240
  const lines: string[] = [];
237
241
  lines.push(`Model: ${model}`);
238
242
  lines.push(`Prompt: "${p.prompt}"`);
239
- if (p.image_size) lines.push(`Size: ${p.image_size}`);
240
- if (p.aspect_ratio) lines.push(`Aspect ratio: ${p.aspect_ratio}`);
241
- lines.push(`Images: ${nImages}`);
243
+ lines.push(`Aspect: ${aspect}${p.aspect_ratio ? "" : " (default)"}`);
244
+ lines.push(`Size: ${size}${p.image_size ? "" : " (default)"}`);
245
+ lines.push(`Resolution: ${resolution}`);
246
+ lines.push(`Images: ${nImages}${p.number_of_images === undefined && isImagen(model) ? " (default)" : ""}`);
242
247
  if (p.negative_prompt) lines.push(`Negative: "${p.negative_prompt.slice(0, 80)}"`);
243
248
  if (p.seed !== undefined) lines.push(`Seed: ${p.seed}`);
244
249
  if (p.person_generation) lines.push(`People: ${p.person_generation}`);
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { formatResolution, resolveResolution } from "./resolution.ts";
3
+
4
+ describe("resolveResolution", () => {
5
+ it("defaults to 1:1 1K = 1024×1024", () => {
6
+ const r = resolveResolution("gemini-2.5-flash-image", undefined, undefined);
7
+ expect(r.width).toBe(1024);
8
+ expect(r.height).toBe(1024);
9
+ expect(r.approximate).toBe(false);
10
+ });
11
+
12
+ it("scales 2K as 2x", () => {
13
+ const r = resolveResolution("gemini-2.5-flash-image", "1:1", "2K");
14
+ expect(r.width).toBe(2048);
15
+ expect(r.height).toBe(2048);
16
+ });
17
+
18
+ it("scales 4K as 4x", () => {
19
+ const r = resolveResolution("gemini-3-pro-image-preview", "1:1", "4K");
20
+ expect(r.width).toBe(4096);
21
+ expect(r.height).toBe(4096);
22
+ });
23
+
24
+ it("16:9 at 1K → 1344×768", () => {
25
+ const r = resolveResolution("gemini-2.5-flash-image", "16:9", "1K");
26
+ expect(r.width).toBe(1344);
27
+ expect(r.height).toBe(768);
28
+ });
29
+
30
+ it("21:9 at 1K → 1536×672", () => {
31
+ const r = resolveResolution("gemini-2.5-flash-image", "21:9", "1K");
32
+ expect(r.width).toBe(1536);
33
+ expect(r.height).toBe(672);
34
+ });
35
+
36
+ it("Imagen flagged as approximate", () => {
37
+ const r = resolveResolution("imagen-4.0-generate-001", "1:1", "1K");
38
+ expect(r.approximate).toBe(true);
39
+ });
40
+
41
+ it("unknown aspect falls back to 1:1", () => {
42
+ const r = resolveResolution("gemini-2.5-flash-image", "7:3", "1K");
43
+ expect(r.width).toBe(1024);
44
+ expect(r.height).toBe(1024);
45
+ });
46
+ });
47
+
48
+ describe("formatResolution", () => {
49
+ it("exact Gemini is shown without tilde", () => {
50
+ expect(formatResolution({ width: 1024, height: 1024, approximate: false })).toBe("1024×1024");
51
+ });
52
+
53
+ it("approximate Imagen gets tilde prefix", () => {
54
+ expect(formatResolution({ width: 1024, height: 1024, approximate: true })).toBe("~1024×1024");
55
+ });
56
+ });
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Resolve the expected output resolution for a given (model, aspect_ratio, image_size).
3
+ *
4
+ * Base table is the documented Gemini 2.5 Flash Image matrix. 2K/4K scale the
5
+ * base by 2x/4x. Imagen's published resolutions vary per model and aren't in
6
+ * the public docs at the same granularity, so we use the same base and flag
7
+ * the result as approximate.
8
+ */
9
+
10
+ import { isImagen, type ImageModel } from "./models.ts";
11
+
12
+ type AspectRatio =
13
+ | "1:1"
14
+ | "2:3"
15
+ | "3:2"
16
+ | "3:4"
17
+ | "4:3"
18
+ | "4:5"
19
+ | "5:4"
20
+ | "9:16"
21
+ | "16:9"
22
+ | "21:9";
23
+
24
+ // Gemini 2.5 Flash Image 1K dimensions, per ai.google.dev/gemini-api/docs/image-generation.
25
+ const BASE_1K: Record<AspectRatio, [number, number]> = {
26
+ "1:1": [1024, 1024],
27
+ "2:3": [832, 1248],
28
+ "3:2": [1248, 832],
29
+ "3:4": [864, 1184],
30
+ "4:3": [1184, 864],
31
+ "4:5": [896, 1152],
32
+ "5:4": [1152, 896],
33
+ "9:16": [768, 1344],
34
+ "16:9": [1344, 768],
35
+ "21:9": [1536, 672],
36
+ };
37
+
38
+ export interface ResolutionInfo {
39
+ width: number;
40
+ height: number;
41
+ approximate: boolean;
42
+ }
43
+
44
+ export function resolveResolution(
45
+ model: ImageModel,
46
+ aspectRatio: string | undefined,
47
+ imageSize: "1K" | "2K" | "4K" | undefined,
48
+ ): ResolutionInfo {
49
+ const ratio = (aspectRatio ?? "1:1") as AspectRatio;
50
+ const base = BASE_1K[ratio] ?? BASE_1K["1:1"];
51
+ const size = imageSize ?? "1K";
52
+ const multiplier = size === "4K" ? 4 : size === "2K" ? 2 : 1;
53
+ return {
54
+ width: base[0] * multiplier,
55
+ height: base[1] * multiplier,
56
+ approximate: isImagen(model),
57
+ };
58
+ }
59
+
60
+ export function formatResolution(info: ResolutionInfo): string {
61
+ return `${info.approximate ? "~" : ""}${info.width}×${info.height}`;
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrofoundry/pi-astro",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "Personal pi customizations (extensions, skills, prompts, themes) for the pi coding agent.",
5
5
  "keywords": [
6
6
  "pi-package"