@oh-my-pi/pi-ai 4.4.6 → 4.4.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-ai",
3
- "version": "4.4.6",
3
+ "version": "4.4.9",
4
4
  "description": "Unified LLM API with automatic model discovery and provider configuration",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -200,6 +200,18 @@ const UNSUPPORTED_SCHEMA_FIELDS = new Set([
200
200
  "prefixItems",
201
201
  "unevaluatedProperties",
202
202
  "unevaluatedItems",
203
+ "patternProperties",
204
+ "additionalProperties",
205
+ "minItems",
206
+ "maxItems",
207
+ "minLength",
208
+ "maxLength",
209
+ "minimum",
210
+ "maximum",
211
+ "exclusiveMinimum",
212
+ "exclusiveMaximum",
213
+ "pattern",
214
+ "format",
203
215
  ]);
204
216
 
205
217
  export function sanitizeSchemaForGoogle(value: unknown): unknown {
@@ -211,43 +223,73 @@ export function sanitizeSchemaForGoogle(value: unknown): unknown {
211
223
  return value;
212
224
  }
213
225
 
226
+ const obj = value as Record<string, unknown>;
214
227
  const result: Record<string, unknown> = {};
215
- let constValue: unknown | undefined;
216
228
 
217
- for (const [key, entry] of Object.entries(value)) {
218
- if (UNSUPPORTED_SCHEMA_FIELDS.has(key)) {
219
- continue;
229
+ // Collapse anyOf/oneOf of const values into enum
230
+ for (const combiner of ["anyOf", "oneOf"] as const) {
231
+ if (Array.isArray(obj[combiner])) {
232
+ const variants = obj[combiner] as Record<string, unknown>[];
233
+
234
+ // Check if ALL variants have a const field
235
+ const allHaveConst = variants.every((v) => v && typeof v === "object" && "const" in v);
236
+
237
+ if (allHaveConst && variants.length > 0) {
238
+ // Extract all const values into enum
239
+ result.enum = variants.map((v) => v.const);
240
+
241
+ // Inherit type from first variant if present
242
+ const firstType = variants[0]?.type;
243
+ if (firstType) {
244
+ result.type = firstType;
245
+ }
246
+
247
+ // Copy description and other top-level fields (not the combiner)
248
+ for (const [key, entry] of Object.entries(obj)) {
249
+ if (key !== combiner && !(key in result)) {
250
+ result[key] = sanitizeSchemaForGoogle(entry);
251
+ }
252
+ }
253
+ return result;
254
+ }
220
255
  }
256
+ }
257
+
258
+ // Regular field processing
259
+ let constValue: unknown;
260
+ for (const [key, entry] of Object.entries(obj)) {
261
+ if (UNSUPPORTED_SCHEMA_FIELDS.has(key)) continue;
221
262
  if (key === "const") {
222
263
  constValue = entry;
223
264
  continue;
224
265
  }
225
- if (key === "additionalProperties" && entry === false) {
226
- continue;
227
- }
266
+ if (key === "additionalProperties" && entry === false) continue;
228
267
  result[key] = sanitizeSchemaForGoogle(entry);
229
268
  }
230
269
 
231
270
  if (constValue !== undefined) {
232
- const existingEnum = Array.isArray(result.enum) ? [...result.enum] : undefined;
233
- const enumValues = existingEnum ?? [];
234
- if (!enumValues.some((item) => Object.is(item, constValue))) {
235
- enumValues.push(constValue);
271
+ // Convert const to enum, merging with existing enum if present
272
+ const existingEnum = Array.isArray(result.enum) ? result.enum : [];
273
+ if (!existingEnum.some((item) => Object.is(item, constValue))) {
274
+ existingEnum.push(constValue);
275
+ }
276
+ result.enum = existingEnum;
277
+ if (!result.type) {
278
+ result.type =
279
+ typeof constValue === "string"
280
+ ? "string"
281
+ : typeof constValue === "number"
282
+ ? "number"
283
+ : typeof constValue === "boolean"
284
+ ? "boolean"
285
+ : undefined;
236
286
  }
237
- result.enum = enumValues;
238
287
  }
239
288
 
240
289
  return result;
241
290
  }
242
291
 
243
- function sanitizeToolNoop(tool: Tool): Tool {
244
- return {
245
- name: tool.name,
246
- description: tool.description,
247
- parameters: structuredClone(tool.parameters),
248
- };
249
- }
250
- function sanitizeToolGoogle(tool: Tool): Tool {
292
+ function sanitizeToolForGoogle(tool: Tool): Tool {
251
293
  return {
252
294
  name: tool.name,
253
295
  description: tool.description,
@@ -260,11 +302,10 @@ function sanitizeToolGoogle(tool: Tool): Tool {
260
302
  */
261
303
  export function convertTools(
262
304
  tools: Tool[],
263
- model: Model<"google-generative-ai" | "google-gemini-cli" | "google-vertex">,
305
+ _model: Model<"google-generative-ai" | "google-gemini-cli" | "google-vertex">,
264
306
  ): { functionDeclarations: { name: string; description?: string; parameters: Schema }[] }[] | undefined {
265
307
  if (tools.length === 0) return undefined;
266
- const toolSanitizer = model?.id.startsWith("gemini-") ? sanitizeToolGoogle : sanitizeToolNoop;
267
- return [{ functionDeclarations: tools.map(toolSanitizer) }];
308
+ return [{ functionDeclarations: tools.map(sanitizeToolForGoogle) }];
268
309
  }
269
310
 
270
311
  /**
@@ -11,7 +11,7 @@ import { type TUnsafe, Type } from "@sinclair/typebox";
11
11
  *
12
12
  * type Operation = Static<typeof OperationSchema>; // "add" | "subtract" | "multiply" | "divide"
13
13
  */
14
- export function StringEnum<T extends readonly string[]>(
14
+ export function StringEnum<const T extends readonly string[]>(
15
15
  values: T,
16
16
  options?: { description?: string; default?: T[number] },
17
17
  ): TUnsafe<T[number]> {