pi-openai-toolkit 0.17.3 → 0.19.0

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.
@@ -15,13 +15,13 @@ import {
15
15
  type Component,
16
16
  } from "@earendil-works/pi-tui";
17
17
  import { getGeneratedImagesRoot } from "./artifacts";
18
- import { isValidPng, readPngDimensions } from "./protocol";
18
+ import { readImageMetadata } from "./protocol";
19
19
  import {
20
- IMAGE_GENERATION_MIME_TYPE,
21
20
  MAX_GENERATED_IMAGE_BYTES,
22
21
  isImageGenerationDetails,
23
22
  type GenerateImageParams,
24
- type ImageGenerationDetails,
23
+ type ImageGenerationMimeType,
24
+ type ImageGenerationToolDetails,
25
25
  } from "./types";
26
26
 
27
27
  export type ImageGenerationRenderState = Record<string, never>;
@@ -54,7 +54,7 @@ function isPathInsideOrEqual(root: string, candidate: string): boolean {
54
54
  return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
55
55
  }
56
56
 
57
- function resultText(result: AgentToolResult<ImageGenerationDetails>): string {
57
+ function resultText(result: AgentToolResult<ImageGenerationToolDetails>): string {
58
58
  return result.content
59
59
  .filter((block): block is { type: "text"; text: string } => block.type === "text")
60
60
  .map((block) => block.text)
@@ -66,6 +66,7 @@ class DiskBackedImageResult extends Container {
66
66
  readonly artifactPath?: string;
67
67
  readonly fileSize?: number;
68
68
  readonly mtimeMs?: number;
69
+ readonly mimeType?: ImageGenerationMimeType;
69
70
  readonly showImages: boolean;
70
71
  readonly textValue: string;
71
72
 
@@ -74,6 +75,7 @@ class DiskBackedImageResult extends Container {
74
75
  artifactPath?: string;
75
76
  fileSize?: number;
76
77
  mtimeMs?: number;
78
+ mimeType?: ImageGenerationMimeType;
77
79
  showImages: boolean;
78
80
  base64?: string;
79
81
  theme: Theme;
@@ -83,6 +85,7 @@ class DiskBackedImageResult extends Container {
83
85
  this.artifactPath = args.artifactPath;
84
86
  this.fileSize = args.fileSize;
85
87
  this.mtimeMs = args.mtimeMs;
88
+ this.mimeType = args.mimeType;
86
89
  this.showImages = args.showImages;
87
90
  this.textValue = `${args.text}\n${args.previewWarning ?? ""}`;
88
91
  this.addChild(new Text(args.theme.fg("toolOutput", args.text), 0, 0));
@@ -94,7 +97,7 @@ class DiskBackedImageResult extends Container {
94
97
  this.addChild(
95
98
  new Image(
96
99
  args.base64,
97
- IMAGE_GENERATION_MIME_TYPE,
100
+ args.mimeType ?? "image/png",
98
101
  { fallbackColor: (text) => args.theme.fg("toolOutput", text) },
99
102
  { maxWidthCells: 60, filename: args.artifactPath },
100
103
  ),
@@ -107,6 +110,7 @@ class DiskBackedImageResult extends Container {
107
110
  artifactPath?: string;
108
111
  fileSize?: number;
109
112
  mtimeMs?: number;
113
+ mimeType?: ImageGenerationMimeType;
110
114
  showImages: boolean;
111
115
  previewWarning?: string;
112
116
  }): boolean {
@@ -115,6 +119,7 @@ class DiskBackedImageResult extends Container {
115
119
  this.artifactPath === args.artifactPath &&
116
120
  this.fileSize === args.fileSize &&
117
121
  this.mtimeMs === args.mtimeMs &&
122
+ this.mimeType === args.mimeType &&
118
123
  this.showImages === args.showImages
119
124
  );
120
125
  }
@@ -148,7 +153,7 @@ function textOnly(args: {
148
153
  export function createImageGenerationResultRenderer(
149
154
  deps: ImageRendererDependencies = DEFAULT_RENDER_DEPS,
150
155
  ): (
151
- result: AgentToolResult<ImageGenerationDetails>,
156
+ result: AgentToolResult<ImageGenerationToolDetails>,
152
157
  options: ToolRenderResultOptions,
153
158
  theme: Theme,
154
159
  context: ImageGenerationRenderContext,
@@ -204,6 +209,7 @@ export function createImageGenerationResultRenderer(
204
209
  artifactPath: realArtifact,
205
210
  fileSize: info.size,
206
211
  mtimeMs: info.mtimeMs,
212
+ mimeType: result.details.mimeType,
207
213
  showImages: true,
208
214
  })
209
215
  ) {
@@ -221,13 +227,14 @@ export function createImageGenerationResultRenderer(
221
227
  previewWarning: "Image preview unavailable: the artifact could not be read.",
222
228
  });
223
229
  }
224
- if (bytes.length !== info.size || !isValidPng(bytes) || !readPngDimensions(bytes)) {
230
+ const metadata = readImageMetadata(bytes);
231
+ if (bytes.length !== info.size || !metadata || metadata.mimeType !== result.details.mimeType) {
225
232
  bytes.fill(0);
226
233
  return textOnly({
227
234
  text,
228
235
  theme,
229
236
  context,
230
- previewWarning: "Image preview unavailable: the artifact is not a valid PNG.",
237
+ previewWarning: "Image preview unavailable: the artifact is not a valid supported image.",
231
238
  });
232
239
  }
233
240
  const base64 = bytes.toString("base64");
@@ -237,6 +244,7 @@ export function createImageGenerationResultRenderer(
237
244
  artifactPath: realArtifact,
238
245
  fileSize: info.size,
239
246
  mtimeMs: info.mtimeMs,
247
+ mimeType: metadata.mimeType,
240
248
  showImages: true,
241
249
  base64,
242
250
  theme,