@tt-a1i/openpi 0.5.0 → 0.6.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.
- package/README.md +18 -10
- package/SETUP.md +8 -2
- package/THIRD_PARTY_NOTICES.md +16 -0
- package/bin/openpi.js +25 -15
- package/extensions/ai-providers/LICENSE.upstream +23 -0
- package/extensions/ai-providers/README.md +59 -0
- package/extensions/ai-providers/antigravity/credentials.ts +52 -0
- package/extensions/ai-providers/antigravity/discovery.ts +130 -0
- package/extensions/ai-providers/antigravity/google-conversion.ts +455 -0
- package/extensions/ai-providers/antigravity/models.ts +84 -0
- package/extensions/ai-providers/antigravity/oauth.ts +700 -0
- package/extensions/ai-providers/antigravity/provider.ts +1116 -0
- package/extensions/ai-providers/antigravity/routing.ts +340 -0
- package/extensions/ai-providers/antigravity/with-resolvers.d.ts +19 -0
- package/extensions/ai-providers/cursor/constants.ts +5 -0
- package/extensions/ai-providers/cursor/credentials.ts +14 -0
- package/extensions/ai-providers/cursor/discovery.ts +291 -0
- package/extensions/ai-providers/cursor/input-images.ts +106 -0
- package/extensions/ai-providers/cursor/models.ts +45 -0
- package/extensions/ai-providers/cursor/oauth.ts +263 -0
- package/extensions/ai-providers/cursor/proto.ts +1064 -0
- package/extensions/ai-providers/cursor/protobuf.ts +1171 -0
- package/extensions/ai-providers/cursor/provider.ts +1175 -0
- package/extensions/ai-providers/cursor/proxy.ts +213 -0
- package/extensions/ai-providers/cursor/with-resolvers.d.ts +12 -0
- package/extensions/ai-providers/index.ts +86 -0
- package/extensions/ai-providers/oauth-adapter.ts +81 -0
- package/extensions/ai-providers/usage.ts +10 -0
- package/extensions/background-terminals/index.ts +8 -1
- package/extensions/background-terminals/src/manager.ts +3 -5
- package/extensions/background-terminals/src/result-delivery.ts +43 -23
- package/extensions/cron/index.ts +68 -27
- package/extensions/cron/schedule.ts +5 -1
- package/extensions/model-info/cache-diagnostics.ts +220 -0
- package/extensions/model-info/index.ts +45 -1
- package/extensions/plan-mode/index.ts +75 -4
- package/extensions/setup/index.ts +15 -3
- package/extensions/shared/child-session.ts +25 -5
- package/extensions/shared/completion-inbox.ts +193 -0
- package/extensions/shared/setup-config.ts +10 -1
- package/extensions/shared/structured-output.ts +154 -0
- package/extensions/subagents/index.ts +44 -4
- package/extensions/subagents/src/backends/pi.ts +76 -5
- package/extensions/subagents/src/domain.ts +16 -1
- package/extensions/subagents/src/manager.ts +5 -0
- package/extensions/subagents/src/prompt.ts +17 -3
- package/extensions/subagents/src/result-artifact.ts +32 -0
- package/extensions/subagents/src/result-delivery.ts +33 -14
- package/extensions/ui-customization/footer.ts +16 -5
- package/extensions/user-input-fold/index.ts +42 -6
- package/extensions/web/index.ts +25 -2
- package/extensions/workflows/acceptance.ts +43 -19
- package/extensions/workflows/completion-projection.ts +3 -1
- package/extensions/workflows/dashboard.ts +8 -0
- package/extensions/workflows/index.ts +13 -0
- package/extensions/workflows/model.ts +5 -1
- package/extensions/workflows/prompt.ts +4 -10
- package/extensions/workflows/result-delivery.ts +96 -22
- package/extensions/workflows/retention.ts +6 -0
- package/extensions/workflows/runner.ts +6 -71
- package/package.json +7 -7
- package/skills/subagents/REFERENCE.md +3 -2
- package/skills/subagents/SKILL.md +1 -0
- package/skills/workflows/REFERENCE.md +3 -3
- package/skills/workflows/SKILL.md +1 -1
- package/web/adapter/pi-adapter.ts +3 -0
- package/web/host/pi-coding-agent-entry.ts +162 -0
- package/web/host/web-host.ts +330 -50
- package/web/protocol/types.ts +5 -0
- package/web/runtime/pi-runtime.ts +240 -25
- package/web/runtime/types.ts +32 -1
- package/web/ui/app.js +343 -41
- package/web/ui/index.html +3 -0
- package/web/ui/styles.css +119 -37
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google/Cloud Code Assist message conversion used by the Antigravity adapter.
|
|
3
|
+
*
|
|
4
|
+
* Kept local because Pi only exposes its documented runtime modules to loaded
|
|
5
|
+
* extensions; `@earendil-works/pi-ai/api/google-shared` is not one of them.
|
|
6
|
+
* Behavior is adapted from pi-ai 0.84.1's MIT-licensed google-shared converter.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
Api,
|
|
11
|
+
AssistantMessage,
|
|
12
|
+
Context,
|
|
13
|
+
ImageContent,
|
|
14
|
+
Message,
|
|
15
|
+
Model,
|
|
16
|
+
StopReason,
|
|
17
|
+
Tool,
|
|
18
|
+
ToolCall,
|
|
19
|
+
} from "@earendil-works/pi-ai/compat";
|
|
20
|
+
|
|
21
|
+
const NON_VISION_USER_IMAGE_PLACEHOLDER =
|
|
22
|
+
"(image omitted: model does not support images)";
|
|
23
|
+
const NON_VISION_TOOL_IMAGE_PLACEHOLDER =
|
|
24
|
+
"(tool image omitted: model does not support images)";
|
|
25
|
+
const BASE64_SIGNATURE_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
26
|
+
const JSON_SCHEMA_META_DECLARATIONS = new Set([
|
|
27
|
+
"$schema",
|
|
28
|
+
"$id",
|
|
29
|
+
"$anchor",
|
|
30
|
+
"$dynamicAnchor",
|
|
31
|
+
"$vocabulary",
|
|
32
|
+
"$comment",
|
|
33
|
+
"$defs",
|
|
34
|
+
"definitions",
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
interface GoogleFunctionCall {
|
|
38
|
+
id?: string;
|
|
39
|
+
name: string;
|
|
40
|
+
args: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface GoogleFunctionResponse {
|
|
44
|
+
id?: string;
|
|
45
|
+
name: string;
|
|
46
|
+
response: { output: string } | { error: string };
|
|
47
|
+
parts?: GooglePart[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface GooglePart {
|
|
51
|
+
text?: string;
|
|
52
|
+
thought?: boolean;
|
|
53
|
+
thoughtSignature?: string;
|
|
54
|
+
inlineData?: { mimeType: string; data: string };
|
|
55
|
+
functionCall?: GoogleFunctionCall;
|
|
56
|
+
functionResponse?: GoogleFunctionResponse;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface GoogleContent {
|
|
60
|
+
role: "user" | "model";
|
|
61
|
+
parts: GooglePart[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function sanitizeSurrogates(text: string): string {
|
|
65
|
+
return text.replace(
|
|
66
|
+
/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,
|
|
67
|
+
"",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function replaceContentImages(
|
|
72
|
+
content: ({ type: "text"; text: string } | ImageContent)[],
|
|
73
|
+
placeholder: string,
|
|
74
|
+
): ({ type: "text"; text: string } | ImageContent)[] {
|
|
75
|
+
const result: ({ type: "text"; text: string } | ImageContent)[] = [];
|
|
76
|
+
let previousWasPlaceholder = false;
|
|
77
|
+
for (const block of content) {
|
|
78
|
+
if (block.type === "image") {
|
|
79
|
+
if (!previousWasPlaceholder) {
|
|
80
|
+
result.push({ type: "text", text: placeholder });
|
|
81
|
+
}
|
|
82
|
+
previousWasPlaceholder = true;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
result.push(block);
|
|
86
|
+
previousWasPlaceholder = block.text === placeholder;
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function downgradeUnsupportedImages(
|
|
92
|
+
messages: Message[],
|
|
93
|
+
model: Model<Api>,
|
|
94
|
+
): Message[] {
|
|
95
|
+
if (model.input.includes("image")) return messages;
|
|
96
|
+
return messages.map((message) => {
|
|
97
|
+
if (message.role === "user" && Array.isArray(message.content)) {
|
|
98
|
+
return {
|
|
99
|
+
...message,
|
|
100
|
+
content: replaceContentImages(
|
|
101
|
+
message.content,
|
|
102
|
+
NON_VISION_USER_IMAGE_PLACEHOLDER,
|
|
103
|
+
),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (message.role === "toolResult") {
|
|
107
|
+
return {
|
|
108
|
+
...message,
|
|
109
|
+
content: replaceContentImages(
|
|
110
|
+
message.content,
|
|
111
|
+
NON_VISION_TOOL_IMAGE_PLACEHOLDER,
|
|
112
|
+
),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return message;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function normalizeMessageContent(message: Message): Message {
|
|
120
|
+
if (message.content !== null && message.content !== undefined) return message;
|
|
121
|
+
return { ...message, content: [] };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function transformMessages(
|
|
125
|
+
messages: Message[],
|
|
126
|
+
model: Model<Api>,
|
|
127
|
+
normalizeToolCallId: (id: string) => string,
|
|
128
|
+
): Message[] {
|
|
129
|
+
const toolCallIdMap = new Map<string, string>();
|
|
130
|
+
const normalizedMessages = messages.map(normalizeMessageContent);
|
|
131
|
+
const imageAwareMessages = downgradeUnsupportedImages(
|
|
132
|
+
normalizedMessages,
|
|
133
|
+
model,
|
|
134
|
+
);
|
|
135
|
+
const transformed = imageAwareMessages.map((message): Message => {
|
|
136
|
+
if (message.role === "user") return message;
|
|
137
|
+
if (message.role === "toolResult") {
|
|
138
|
+
const normalizedId = toolCallIdMap.get(message.toolCallId);
|
|
139
|
+
return normalizedId && normalizedId !== message.toolCallId
|
|
140
|
+
? { ...message, toolCallId: normalizedId }
|
|
141
|
+
: message;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const isSameModel =
|
|
145
|
+
message.provider === model.provider &&
|
|
146
|
+
message.api === model.api &&
|
|
147
|
+
message.model === model.id;
|
|
148
|
+
const content: AssistantMessage["content"] = [];
|
|
149
|
+
for (const block of message.content) {
|
|
150
|
+
if (block.type === "thinking") {
|
|
151
|
+
if (block.redacted) {
|
|
152
|
+
if (isSameModel) content.push(block);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (isSameModel && block.thinkingSignature) {
|
|
156
|
+
content.push(block);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (!block.thinking.trim()) continue;
|
|
160
|
+
content.push(
|
|
161
|
+
isSameModel ? block : { type: "text", text: block.thinking },
|
|
162
|
+
);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (block.type === "text") {
|
|
166
|
+
content.push(isSameModel ? block : { type: "text", text: block.text });
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let normalizedToolCall: ToolCall = block;
|
|
171
|
+
if (!isSameModel && block.thoughtSignature) {
|
|
172
|
+
const { thoughtSignature: _thoughtSignature, ...unsignedToolCall } =
|
|
173
|
+
normalizedToolCall;
|
|
174
|
+
normalizedToolCall = unsignedToolCall;
|
|
175
|
+
}
|
|
176
|
+
if (!isSameModel) {
|
|
177
|
+
const normalizedId = normalizeToolCallId(block.id);
|
|
178
|
+
if (normalizedId !== block.id) {
|
|
179
|
+
toolCallIdMap.set(block.id, normalizedId);
|
|
180
|
+
normalizedToolCall = { ...normalizedToolCall, id: normalizedId };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
content.push(normalizedToolCall);
|
|
184
|
+
}
|
|
185
|
+
return { ...message, content };
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const result: Message[] = [];
|
|
189
|
+
let pendingToolCalls: ToolCall[] = [];
|
|
190
|
+
let existingToolResultIds = new Set<string>();
|
|
191
|
+
const insertSyntheticToolResults = () => {
|
|
192
|
+
for (const toolCall of pendingToolCalls) {
|
|
193
|
+
if (existingToolResultIds.has(toolCall.id)) continue;
|
|
194
|
+
result.push({
|
|
195
|
+
role: "toolResult",
|
|
196
|
+
toolCallId: toolCall.id,
|
|
197
|
+
toolName: toolCall.name,
|
|
198
|
+
content: [{ type: "text", text: "No result provided" }],
|
|
199
|
+
isError: true,
|
|
200
|
+
timestamp: Date.now(),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
pendingToolCalls = [];
|
|
204
|
+
existingToolResultIds = new Set<string>();
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
for (const message of transformed) {
|
|
208
|
+
if (message.role === "assistant") {
|
|
209
|
+
insertSyntheticToolResults();
|
|
210
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
pendingToolCalls = message.content.filter(
|
|
214
|
+
(block): block is ToolCall => block.type === "toolCall",
|
|
215
|
+
);
|
|
216
|
+
result.push(message);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (message.role === "toolResult") {
|
|
220
|
+
existingToolResultIds.add(message.toolCallId);
|
|
221
|
+
result.push(message);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
insertSyntheticToolResults();
|
|
225
|
+
result.push(message);
|
|
226
|
+
}
|
|
227
|
+
insertSyntheticToolResults();
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function getGeminiMajorVersion(modelId: string): number | undefined {
|
|
232
|
+
const match = modelId.toLowerCase().match(/^gemini(?:-live)?-(\d+)/);
|
|
233
|
+
return match ? Number.parseInt(match[1], 10) : undefined;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function requiresToolCallId(modelId: string): boolean {
|
|
237
|
+
const geminiMajorVersion = getGeminiMajorVersion(modelId);
|
|
238
|
+
return (
|
|
239
|
+
modelId.startsWith("claude-") ||
|
|
240
|
+
modelId.startsWith("gpt-oss-") ||
|
|
241
|
+
(geminiMajorVersion !== undefined && geminiMajorVersion >= 3)
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function supportsMultimodalFunctionResponse(modelId: string): boolean {
|
|
246
|
+
const geminiMajorVersion = getGeminiMajorVersion(modelId);
|
|
247
|
+
return geminiMajorVersion === undefined || geminiMajorVersion >= 3;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function resolveThoughtSignature(
|
|
251
|
+
isSameProviderAndModel: boolean,
|
|
252
|
+
signature: string | undefined,
|
|
253
|
+
): string | undefined {
|
|
254
|
+
if (!isSameProviderAndModel || !signature || signature.length % 4 !== 0) {
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
return BASE64_SIGNATURE_PATTERN.test(signature) ? signature : undefined;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function convertMessages(
|
|
261
|
+
model: Model<Api>,
|
|
262
|
+
context: Context,
|
|
263
|
+
): GoogleContent[] {
|
|
264
|
+
const contents: GoogleContent[] = [];
|
|
265
|
+
const normalizeToolCallId = (id: string) =>
|
|
266
|
+
requiresToolCallId(model.id)
|
|
267
|
+
? id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64)
|
|
268
|
+
: id;
|
|
269
|
+
const messages = transformMessages(
|
|
270
|
+
context.messages,
|
|
271
|
+
model,
|
|
272
|
+
normalizeToolCallId,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
for (const message of messages) {
|
|
276
|
+
if (message.role === "user") {
|
|
277
|
+
const parts =
|
|
278
|
+
typeof message.content === "string"
|
|
279
|
+
? [{ text: sanitizeSurrogates(message.content) }]
|
|
280
|
+
: message.content.map(
|
|
281
|
+
(item): GooglePart =>
|
|
282
|
+
item.type === "text"
|
|
283
|
+
? { text: sanitizeSurrogates(item.text) }
|
|
284
|
+
: {
|
|
285
|
+
inlineData: {
|
|
286
|
+
mimeType: item.mimeType,
|
|
287
|
+
data: item.data,
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
);
|
|
291
|
+
if (parts.length > 0) contents.push({ role: "user", parts });
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (message.role === "assistant") {
|
|
296
|
+
const parts: GooglePart[] = [];
|
|
297
|
+
const isSameProviderAndModel =
|
|
298
|
+
message.provider === model.provider && message.model === model.id;
|
|
299
|
+
for (const block of message.content) {
|
|
300
|
+
if (block.type === "text") {
|
|
301
|
+
const thoughtSignature = resolveThoughtSignature(
|
|
302
|
+
isSameProviderAndModel,
|
|
303
|
+
block.textSignature,
|
|
304
|
+
);
|
|
305
|
+
if (!block.text.trim() && !thoughtSignature) continue;
|
|
306
|
+
parts.push({
|
|
307
|
+
text: sanitizeSurrogates(block.text),
|
|
308
|
+
...(thoughtSignature ? { thoughtSignature } : {}),
|
|
309
|
+
});
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (block.type === "thinking") {
|
|
313
|
+
if (!isSameProviderAndModel) {
|
|
314
|
+
if (block.thinking.trim()) {
|
|
315
|
+
parts.push({ text: sanitizeSurrogates(block.thinking) });
|
|
316
|
+
}
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const thoughtSignature = resolveThoughtSignature(
|
|
320
|
+
true,
|
|
321
|
+
block.thinkingSignature,
|
|
322
|
+
);
|
|
323
|
+
if (!block.thinking.trim() && !thoughtSignature) continue;
|
|
324
|
+
parts.push({
|
|
325
|
+
thought: true,
|
|
326
|
+
text: sanitizeSurrogates(block.thinking),
|
|
327
|
+
...(thoughtSignature ? { thoughtSignature } : {}),
|
|
328
|
+
});
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const thoughtSignature = resolveThoughtSignature(
|
|
333
|
+
isSameProviderAndModel,
|
|
334
|
+
block.thoughtSignature,
|
|
335
|
+
);
|
|
336
|
+
parts.push({
|
|
337
|
+
functionCall: {
|
|
338
|
+
name: block.name,
|
|
339
|
+
args: block.arguments ?? {},
|
|
340
|
+
...(requiresToolCallId(model.id) ? { id: block.id } : {}),
|
|
341
|
+
},
|
|
342
|
+
...(thoughtSignature ? { thoughtSignature } : {}),
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
if (parts.length > 0) contents.push({ role: "model", parts });
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const textResult = message.content
|
|
350
|
+
.filter((part) => part.type === "text")
|
|
351
|
+
.map((part) => part.text)
|
|
352
|
+
.join("\n");
|
|
353
|
+
const imageContent = model.input.includes("image")
|
|
354
|
+
? message.content.filter(
|
|
355
|
+
(part): part is ImageContent => part.type === "image",
|
|
356
|
+
)
|
|
357
|
+
: [];
|
|
358
|
+
const hasImages = imageContent.length > 0;
|
|
359
|
+
const responseValue = textResult
|
|
360
|
+
? sanitizeSurrogates(textResult)
|
|
361
|
+
: hasImages
|
|
362
|
+
? "(see attached image)"
|
|
363
|
+
: "";
|
|
364
|
+
const imageParts: GooglePart[] = imageContent.map((image) => ({
|
|
365
|
+
inlineData: { mimeType: image.mimeType, data: image.data },
|
|
366
|
+
}));
|
|
367
|
+
const supportsMultimodal = supportsMultimodalFunctionResponse(model.id);
|
|
368
|
+
const functionResponsePart: GooglePart = {
|
|
369
|
+
functionResponse: {
|
|
370
|
+
name: message.toolName,
|
|
371
|
+
response: message.isError
|
|
372
|
+
? { error: responseValue }
|
|
373
|
+
: { output: responseValue },
|
|
374
|
+
...(hasImages && supportsMultimodal ? { parts: imageParts } : {}),
|
|
375
|
+
...(requiresToolCallId(model.id) ? { id: message.toolCallId } : {}),
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
const lastContent = contents.at(-1);
|
|
379
|
+
if (
|
|
380
|
+
lastContent?.role === "user" &&
|
|
381
|
+
lastContent.parts.some((part) => part.functionResponse)
|
|
382
|
+
) {
|
|
383
|
+
lastContent.parts.push(functionResponsePart);
|
|
384
|
+
} else {
|
|
385
|
+
contents.push({ role: "user", parts: [functionResponsePart] });
|
|
386
|
+
}
|
|
387
|
+
if (hasImages && !supportsMultimodal) {
|
|
388
|
+
contents.push({
|
|
389
|
+
role: "user",
|
|
390
|
+
parts: [{ text: "Tool result image:" }, ...imageParts],
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return contents;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function sanitizeForOpenApi(
|
|
398
|
+
schema: unknown,
|
|
399
|
+
insidePropertiesMap = false,
|
|
400
|
+
): unknown {
|
|
401
|
+
if (typeof schema !== "object" || schema === null || Array.isArray(schema)) {
|
|
402
|
+
return schema;
|
|
403
|
+
}
|
|
404
|
+
const result: Record<string, unknown> = {};
|
|
405
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
406
|
+
if (insidePropertiesMap) {
|
|
407
|
+
result[key] = sanitizeForOpenApi(value);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (!JSON_SCHEMA_META_DECLARATIONS.has(key)) {
|
|
411
|
+
result[key] = sanitizeForOpenApi(value, key === "properties");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function convertTools(
|
|
418
|
+
tools: Tool[],
|
|
419
|
+
useParameters = false,
|
|
420
|
+
): { functionDeclarations: Record<string, unknown>[] }[] | undefined {
|
|
421
|
+
if (tools.length === 0) return undefined;
|
|
422
|
+
return [
|
|
423
|
+
{
|
|
424
|
+
functionDeclarations: tools.map((tool) => ({
|
|
425
|
+
name: tool.name,
|
|
426
|
+
description: tool.description,
|
|
427
|
+
...(useParameters
|
|
428
|
+
? { parameters: sanitizeForOpenApi(tool.parameters) }
|
|
429
|
+
: { parametersJsonSchema: tool.parameters }),
|
|
430
|
+
})),
|
|
431
|
+
},
|
|
432
|
+
];
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function isThinkingPart(part: {
|
|
436
|
+
thought?: boolean;
|
|
437
|
+
thoughtSignature?: string;
|
|
438
|
+
}): boolean {
|
|
439
|
+
return part.thought === true;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export function retainThoughtSignature(
|
|
443
|
+
existing: string | undefined,
|
|
444
|
+
incoming: string | undefined,
|
|
445
|
+
): string | undefined {
|
|
446
|
+
return typeof incoming === "string" && incoming.length > 0
|
|
447
|
+
? incoming
|
|
448
|
+
: existing;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export function mapStopReasonString(reason: string): StopReason {
|
|
452
|
+
if (reason === "STOP") return "stop";
|
|
453
|
+
if (reason === "MAX_TOKENS") return "length";
|
|
454
|
+
return "error";
|
|
455
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static Antigravity model table.
|
|
3
|
+
*
|
|
4
|
+
* This is the offline fallback so the provider is usable before (or without)
|
|
5
|
+
* discovery. The live source of truth is the Cloud Code Assist
|
|
6
|
+
* `fetchAvailableModels` endpoint, wired via `createProvider.fetchModels` (see
|
|
7
|
+
* oh-my-pi packages/catalog/src/discovery/antigravity.ts for the reference
|
|
8
|
+
* implementation and its denylist). Wire ids follow the real client:
|
|
9
|
+
* `gemini-*` for Gemini routes, `claude-*[-thinking]` for Claude routes.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
13
|
+
|
|
14
|
+
export const ANTIGRAVITY_API_URL = "https://daily-cloudcode-pa.googleapis.com";
|
|
15
|
+
|
|
16
|
+
export type AntigravityProviderModel = Model<"antigravity-cloudcode"> & {
|
|
17
|
+
requestModelId?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const ZERO_COST: AntigravityProviderModel["cost"] = {
|
|
21
|
+
input: 0,
|
|
22
|
+
output: 0,
|
|
23
|
+
cacheRead: 0,
|
|
24
|
+
cacheWrite: 0,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Defaults mirror omp's discovery normalization (200k context, 64k output).
|
|
28
|
+
const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
29
|
+
const DEFAULT_MAX_TOKENS = 64_000;
|
|
30
|
+
|
|
31
|
+
function define(
|
|
32
|
+
id: string,
|
|
33
|
+
name: string,
|
|
34
|
+
reasoning: boolean,
|
|
35
|
+
requestModelId?: string,
|
|
36
|
+
capabilities?: Pick<
|
|
37
|
+
AntigravityProviderModel,
|
|
38
|
+
"input" | "contextWindow" | "maxTokens"
|
|
39
|
+
>,
|
|
40
|
+
): AntigravityProviderModel {
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
name,
|
|
44
|
+
api: "antigravity-cloudcode",
|
|
45
|
+
provider: "google-antigravity",
|
|
46
|
+
baseUrl: ANTIGRAVITY_API_URL,
|
|
47
|
+
reasoning,
|
|
48
|
+
input: capabilities?.input ?? ["text", "image"],
|
|
49
|
+
cost: ZERO_COST,
|
|
50
|
+
contextWindow: capabilities?.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
|
51
|
+
maxTokens: capabilities?.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
52
|
+
...(requestModelId ? { requestModelId } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const ANTIGRAVITY_MODELS: AntigravityProviderModel[] = [
|
|
57
|
+
define(
|
|
58
|
+
"gemini-3.7-flash",
|
|
59
|
+
"Gemini 3.7 Flash (Antigravity)",
|
|
60
|
+
true,
|
|
61
|
+
"gemini-3.7-flash-low",
|
|
62
|
+
),
|
|
63
|
+
define(
|
|
64
|
+
"gemini-3.5-flash",
|
|
65
|
+
"Gemini 3.5 Flash (Antigravity)",
|
|
66
|
+
true,
|
|
67
|
+
"gemini-3.5-flash-extra-low",
|
|
68
|
+
),
|
|
69
|
+
define(
|
|
70
|
+
"gemini-3.1-pro",
|
|
71
|
+
"Gemini 3.1 Pro (Antigravity)",
|
|
72
|
+
true,
|
|
73
|
+
"gemini-3.1-pro-low",
|
|
74
|
+
),
|
|
75
|
+
define("claude-sonnet-4-6", "Claude Sonnet 4.6 (Antigravity)", true),
|
|
76
|
+
define("claude-opus-4-6", "Claude Opus 4.6 (Antigravity)", true),
|
|
77
|
+
define(
|
|
78
|
+
"gpt-oss-120b",
|
|
79
|
+
"GPT OSS 120B (Antigravity)",
|
|
80
|
+
true,
|
|
81
|
+
"gpt-oss-120b-medium",
|
|
82
|
+
{ input: ["text"], contextWindow: 131_072, maxTokens: 32_768 },
|
|
83
|
+
),
|
|
84
|
+
];
|