@benjamolina/pi-antigravity-guard 0.4.5 → 0.4.6

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/dist/catalog.d.ts CHANGED
@@ -436,7 +436,7 @@ export declare function toPiModelDescriptor(entry: CatalogEntry): {
436
436
  medium: string | null;
437
437
  minimal: string | null;
438
438
  };
439
- input: "text"[];
439
+ input: ("text" | "image")[];
440
440
  cost: {
441
441
  input: number;
442
442
  output: number;
package/dist/catalog.js CHANGED
@@ -179,12 +179,13 @@ export function resolveToolCapability(capability, publicModelId, reasoning, wire
179
179
  return capability;
180
180
  }
181
181
  export function toPiModelDescriptor(entry) {
182
+ const isMultimodal = entry.response.family === "gemini" || entry.response.family === "claude";
182
183
  return {
183
184
  id: entry.publicId,
184
- name: entry.descriptor.name,
185
+ name: entry.descriptor.name.replace(", text only", ""),
185
186
  reasoning: true,
186
187
  thinkingLevelMap: { ...entry.descriptor.thinkingLevelMap },
187
- input: ["text"],
188
+ input: (isMultimodal ? ["text", "image"] : ["text"]),
188
189
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
189
190
  contextWindow: entry.descriptor.contextWindow,
190
191
  maxTokens: entry.descriptor.maxTokens,
package/dist/context.d.ts CHANGED
@@ -2,9 +2,13 @@ import type { Context, Model, SimpleStreamOptions } from "@earendil-works/pi-ai"
2
2
  import { type GenerationSelection } from "./catalog.ts";
3
3
  import { type ToolReplayDiagnostics } from "./tool-context.ts";
4
4
  interface Part {
5
- text: string;
5
+ text?: string;
6
6
  thought?: true;
7
7
  thoughtSignature?: string;
8
+ inlineData?: {
9
+ mimeType: string;
10
+ data: string;
11
+ };
8
12
  }
9
13
  interface Content {
10
14
  role: "user" | "model";
package/dist/context.js CHANGED
@@ -77,7 +77,7 @@ export function serializeTextContext(input) {
77
77
  fail("Unsupported context role for this text-only provider.");
78
78
  const assistant = role === "assistant";
79
79
  const parts = messageParts(field(message, "content", true), assistant, assistant && entry.replay.kind === "same-public-model" && isSameProviderAndModel(message, entry.publicId));
80
- textBytes += parts.reduce((total, part) => total + byteLength(part.text), 0);
80
+ textBytes += parts.reduce((total, part) => total + (part.text ? byteLength(part.text) : (part.inlineData ? byteLength(part.inlineData.data) : 0)), 0);
81
81
  if (textBytes > MAX_TEXT_BYTES)
82
82
  fail("Text context is too large.");
83
83
  contents.push({ role: role === "assistant" ? "model" : "user", parts });
@@ -159,6 +159,14 @@ function messagePart(part, assistant, sameProviderAndModel) {
159
159
  fail("Only text context is supported by this provider.");
160
160
  return { text };
161
161
  }
162
+ if (type === "image") {
163
+ const rawData = field(part, "data") ?? (isRecord(field(part, "source")) ? field(field(part, "source"), "data") : undefined);
164
+ if (typeof rawData !== "string" || !rawData)
165
+ fail("Invalid image data.");
166
+ const rawMime = field(part, "mimeType") ?? (isRecord(field(part, "source")) ? field(field(part, "source"), "media_type") : undefined);
167
+ const mimeType = typeof rawMime === "string" && rawMime ? rawMime : "image/jpeg";
168
+ return { inlineData: { mimeType, data: rawData } };
169
+ }
162
170
  fail("Only text context is supported by this provider.");
163
171
  }
164
172
  function isSameProviderAndModel(message, publicId) {
@@ -58,13 +58,17 @@ export function replayToolHistory(messages, serializePart, onDiagnostics, policy
58
58
  const finalize = () => {
59
59
  if (!pending)
60
60
  return;
61
- appendTurn("user", pending.calls.map((call) => {
61
+ const turnParts = pending.calls.map((call) => {
62
62
  const actual = pending.results.get(call.id);
63
63
  if (actual)
64
64
  return actual;
65
65
  recoveryCount.value += 1;
66
66
  return pending.observations ? observation(call, missingResultText()) : missingResult(call);
67
- }));
67
+ });
68
+ if (pending.extraParts?.length) {
69
+ turnParts.push(...pending.extraParts);
70
+ }
71
+ appendTurn("user", turnParts);
68
72
  pending = undefined;
69
73
  };
70
74
  for (const message of messages) {
@@ -83,14 +87,34 @@ export function replayToolHistory(messages, serializePart, onDiagnostics, policy
83
87
  history("PI_TOOL_RESULT_NAME_MISMATCH");
84
88
  if (value(current, "addedToolNames") !== undefined && dense(value(current, "addedToolNames")).length)
85
89
  history("PI_TOOL_CALL_INVALID");
86
- const text = dense(value(current, "content")).map((item) => {
90
+ const textParts = [];
91
+ const imageParts = [];
92
+ for (const item of dense(value(current, "content"))) {
87
93
  const part = record(item);
88
- if (value(part, "type") !== "text" || typeof value(part, "text") !== "string")
94
+ if (value(part, "type") === "text" && typeof value(part, "text") === "string") {
95
+ textParts.push(value(part, "text"));
96
+ }
97
+ else if (value(part, "type") === "image") {
98
+ const rawData = value(part, "data") ?? (isRecord(value(part, "source")) ? value(record(value(part, "source")), "data") : undefined);
99
+ if (typeof rawData === "string" && rawData) {
100
+ const rawMime = value(part, "mimeType") ?? (isRecord(value(part, "source")) ? value(record(value(part, "source")), "media_type") : undefined);
101
+ const mimeType = typeof rawMime === "string" && rawMime ? rawMime : "image/jpeg";
102
+ imageParts.push({ inlineData: { mimeType, data: rawData } });
103
+ }
104
+ }
105
+ else {
89
106
  history("PI_TOOL_RESULT_MEDIA_UNSUPPORTED");
90
- return value(part, "text");
91
- }).join("\n\n");
92
- const response = value(current, "isError") === true ? { error: text } : { output: text };
93
- pending.results.set(id, pending.observations ? observation(call, text) : { functionResponse: { name: call.name, response } });
107
+ }
108
+ }
109
+ const text = textParts.join("\n\n");
110
+ const outputText = text || (imageParts.length > 0 ? "[Image content]" : "");
111
+ const response = value(current, "isError") === true ? { error: outputText } : { output: outputText };
112
+ pending.results.set(id, pending.observations ? observation(call, outputText) : { functionResponse: { name: call.name, response } });
113
+ if (imageParts.length > 0) {
114
+ if (!pending.extraParts)
115
+ pending.extraParts = [];
116
+ pending.extraParts.push(...imageParts);
117
+ }
94
118
  results.add(id);
95
119
  continue;
96
120
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benjamolina/pi-antigravity-guard",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Pi adapter for Antigravity Guard",
5
5
  "type": "module",
6
6
  "license": "MIT",