@steve31415/baselib 3.33.0 → 3.33.1

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,21 +15,47 @@ function classifyFinish(finishReason) {
15
15
  return 'complete';
16
16
  return 'other';
17
17
  }
18
- /** Recursively strip fields that Gemini's API doesn't support (e.g. additionalProperties). */
19
- function stripUnsupportedSchemaFields(schema) {
20
- if (Array.isArray(schema)) {
21
- return schema.map(stripUnsupportedSchemaFields);
22
- }
23
- if (schema !== null && typeof schema === 'object') {
24
- const result = {};
25
- for (const [key, value] of Object.entries(schema)) {
26
- if (key === 'additionalProperties')
27
- continue;
28
- result[key] = stripUnsupportedSchemaFields(value);
18
+ /**
19
+ * Transform a portable JSON schema into the dialect Gemini's `responseSchema`
20
+ * accepts — an OpenAPI-subset proto, not full JSON Schema:
21
+ * - `additionalProperties` is dropped (Gemini rejects it).
22
+ * - A `type` array (JSON Schema's union; usually the nullable union
23
+ * `['string', 'null']`) becomes one `type`, or an `anyOf` of single types
24
+ * for a multi-type union, plus `nullable: true` when `'null'` was a member.
25
+ * The proto's `type` holds a single value, so an array is rejected with a
26
+ * 400 before the model runs ("Proto field is not repeating, cannot start
27
+ * list"; verified live on 3.5 Flash-Lite, 3.6 Flash and 3.1 Pro, 2026-10-09).
28
+ * Returns a deep copy — the caller's schema object is reused across failover
29
+ * attempts.
30
+ */
31
+ function toGeminiSchema(schema) {
32
+ if (Array.isArray(schema))
33
+ return schema.map(toGeminiSchema);
34
+ if (schema === null || typeof schema !== 'object')
35
+ return schema;
36
+ const result = {};
37
+ for (const [key, value] of Object.entries(schema)) {
38
+ if (key === 'additionalProperties')
39
+ continue;
40
+ // Only a keyword `type` is ever an array: a property named `type` (an
41
+ // entry of a `properties` map) has a schema object as its value.
42
+ if (key === 'type' && Array.isArray(value)) {
43
+ Object.assign(result, singleGeminiType(value));
44
+ continue;
29
45
  }
30
- return result;
46
+ result[key] = toGeminiSchema(value);
31
47
  }
32
- return schema;
48
+ return result;
49
+ }
50
+ /** A JSON Schema type array as Gemini's single `type` / `anyOf` plus `nullable`. */
51
+ function singleGeminiType(types) {
52
+ const nonNull = types.filter((type) => type !== 'null');
53
+ if (nonNull.length === 0)
54
+ return { type: 'null' };
55
+ const nullable = nonNull.length < types.length ? { nullable: true } : {};
56
+ if (nonNull.length === 1)
57
+ return { type: nonNull[0], ...nullable };
58
+ return { anyOf: nonNull.map((type) => ({ type })), ...nullable };
33
59
  }
34
60
  export const geminiProvider = {
35
61
  name: 'gemini',
@@ -50,7 +76,7 @@ export const geminiProvider = {
50
76
  }
51
77
  if (request.jsonSchema) {
52
78
  generationConfig.responseMimeType = 'application/json';
53
- generationConfig.responseSchema = stripUnsupportedSchemaFields(request.jsonSchema.schema);
79
+ generationConfig.responseSchema = toGeminiSchema(request.jsonSchema.schema);
54
80
  }
55
81
  // Gemini thinking models reason before answering; the reasoning tokens
56
82
  // count against maxOutputTokens. `effort` (pre-gated and clamped in
@@ -38,7 +38,7 @@ function makeNullable(propSchema) {
38
38
  * Outputs mode requires: every object must set `additionalProperties: false`
39
39
  * and list ALL of its properties in `required`. Callers author one portable
40
40
  * schema; this keeps the OpenAI-specific dialect local to this adapter,
41
- * mirroring the Gemini adapter's `stripUnsupportedSchemaFields` and the
41
+ * mirroring the Gemini adapter's `toGeminiSchema` and the
42
42
  * Anthropic adapter's `toAnthropicStrictSchema` (which closes objects but keeps
43
43
  * optional fields optional). Returns a deep copy — the
44
44
  * caller's schema object is never mutated (it is reused across failover attempts).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steve31415/baselib",
3
- "version": "3.33.0",
3
+ "version": "3.33.1",
4
4
  "description": "Plasticine new-world shared platform library: logging, auth, service-to-service auth, db, HTTP, sync, app updates",
5
5
  "type": "module",
6
6
  "license": "MIT",