@opencode/ai 0.0.0-dev-20076 → 0.0.0-dev-20077
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/protocols/gemini.d.ts +7 -7
- package/dist/protocols/gemini.js +5 -25
- package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
- package/dist/protocols/utils/gemini-json-schema.js +76 -0
- package/dist/providers/google-vertex.d.ts +3 -3
- package/dist/providers/google.d.ts +3 -3
- package/package.json +3 -3
|
@@ -76,7 +76,7 @@ declare const GeminiBody: Schema.Struct<{
|
|
|
76
76
|
readonly functionDeclarations: Schema.$Array<Schema.Struct<{
|
|
77
77
|
readonly name: Schema.String;
|
|
78
78
|
readonly description: Schema.String;
|
|
79
|
-
readonly
|
|
79
|
+
readonly parametersJsonSchema: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
80
80
|
}>>;
|
|
81
81
|
}>>>;
|
|
82
82
|
toolConfig: Schema.optional<Schema.Struct<{
|
|
@@ -148,11 +148,11 @@ export declare const protocol: Protocol<{
|
|
|
148
148
|
}[];
|
|
149
149
|
readonly tools?: readonly {
|
|
150
150
|
readonly functionDeclarations: readonly {
|
|
151
|
-
readonly description: string;
|
|
152
151
|
readonly name: string;
|
|
153
|
-
readonly
|
|
152
|
+
readonly description: string;
|
|
153
|
+
readonly parametersJsonSchema: {
|
|
154
154
|
readonly [x: string]: unknown;
|
|
155
|
-
}
|
|
155
|
+
};
|
|
156
156
|
}[];
|
|
157
157
|
}[] | undefined;
|
|
158
158
|
readonly serviceTier?: string | undefined;
|
|
@@ -262,11 +262,11 @@ export declare const route: Route<{
|
|
|
262
262
|
}[];
|
|
263
263
|
readonly tools?: readonly {
|
|
264
264
|
readonly functionDeclarations: readonly {
|
|
265
|
-
readonly description: string;
|
|
266
265
|
readonly name: string;
|
|
267
|
-
readonly
|
|
266
|
+
readonly description: string;
|
|
267
|
+
readonly parametersJsonSchema: {
|
|
268
268
|
readonly [x: string]: unknown;
|
|
269
|
-
}
|
|
269
|
+
};
|
|
270
270
|
}[];
|
|
271
271
|
}[] | undefined;
|
|
272
272
|
readonly serviceTier?: string | undefined;
|
package/dist/protocols/gemini.js
CHANGED
|
@@ -10,7 +10,7 @@ import { classifyProviderFailure } from "../provider-error.js";
|
|
|
10
10
|
import { Media } from "../media.js";
|
|
11
11
|
import { JsonObject, knownString, lenient, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
12
12
|
import { GeminiGenerateContent } from "./utils/gemini-generate-content.js";
|
|
13
|
-
import {
|
|
13
|
+
import { GeminiJsonSchema } from "./utils/gemini-json-schema.js";
|
|
14
14
|
import { Lifecycle } from "./utils/lifecycle.js";
|
|
15
15
|
import { ToolSchemaProjection } from "./utils/tool-schema.js";
|
|
16
16
|
const ADAPTER = "gemini";
|
|
@@ -103,7 +103,7 @@ const GeminiSystemInstruction = Schema.Struct({
|
|
|
103
103
|
const GeminiFunctionDeclaration = Schema.Struct({
|
|
104
104
|
name: Schema.String,
|
|
105
105
|
description: Schema.String,
|
|
106
|
-
|
|
106
|
+
parametersJsonSchema: JsonObject,
|
|
107
107
|
});
|
|
108
108
|
const GeminiTool = Schema.Struct({
|
|
109
109
|
functionDeclarations: Schema.Array(GeminiFunctionDeclaration),
|
|
@@ -187,34 +187,14 @@ const GeminiEvent = Schema.Struct({
|
|
|
187
187
|
usageMetadata: optionalNull(GeminiUsage),
|
|
188
188
|
});
|
|
189
189
|
// =============================================================================
|
|
190
|
-
// Tool Schema Conversion
|
|
191
|
-
// =============================================================================
|
|
192
|
-
// Tool-schema conversion has two distinct concerns:
|
|
193
|
-
//
|
|
194
|
-
// 1. Sanitize — fix common authoring mistakes Gemini rejects: integer/number
|
|
195
|
-
// enums (must be strings), `required` entries that don't match a property,
|
|
196
|
-
// untyped arrays (`items` must be present), and `properties`/`required`
|
|
197
|
-
// keys on non-object scalars. Mirrors OpenCode's historical Gemini rules.
|
|
198
|
-
//
|
|
199
|
-
// 2. Project — lossy mapping from JSON Schema to Gemini's schema dialect:
|
|
200
|
-
// drop empty root parameter schemas while preserving nested empty objects,
|
|
201
|
-
// expand type arrays into `anyOf`, derive `nullable: true` from null members,
|
|
202
|
-
// coerce `const` to `[const]` enum, recurse properties/items, and propagate
|
|
203
|
-
// only an allowlisted set of keys (description, required, format, type,
|
|
204
|
-
// nullable, enum, properties, items, allOf, anyOf, oneOf, minLength).
|
|
205
|
-
// Anything outside the allowlist (e.g. `additionalProperties`, `$ref`) is
|
|
206
|
-
// silently dropped.
|
|
207
|
-
//
|
|
208
|
-
// Sanitize runs first, then project. The implementation lives in
|
|
209
|
-
// `utils/gemini-tool-schema` so this protocol keeps the same shape as the other
|
|
210
|
-
// provider protocols.
|
|
211
|
-
// =============================================================================
|
|
212
190
|
// Request Lowering
|
|
213
191
|
// =============================================================================
|
|
192
|
+
// Tool schemas go in `parametersJsonSchema`, which accepts standard JSON Schema.
|
|
193
|
+
// `utils/gemini-json-schema` rewrites only the few shapes Gemini still rejects.
|
|
214
194
|
const lowerTool = (tool, inputSchema) => ({
|
|
215
195
|
name: tool.name,
|
|
216
196
|
description: tool.description,
|
|
217
|
-
|
|
197
|
+
parametersJsonSchema: GeminiJsonSchema.normalize(inputSchema),
|
|
218
198
|
});
|
|
219
199
|
const lowerToolConfig = (toolChoice) => ProviderShared.matchToolChoice("Gemini", toolChoice, {
|
|
220
200
|
auto: () => ({ functionCallingConfig: { mode: "AUTO" } }),
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { isRecord } from "../../utils/record.js";
|
|
2
|
+
// Gemini's `parametersJsonSchema` accepts standard JSON Schema, but rejects a few shapes that
|
|
3
|
+
// published tool schemas commonly contain. Rewrite only those and send everything else unchanged.
|
|
4
|
+
const SCHEMA_MAPS = new Set([
|
|
5
|
+
"properties",
|
|
6
|
+
"patternProperties",
|
|
7
|
+
"$defs",
|
|
8
|
+
"definitions",
|
|
9
|
+
"dependentSchemas",
|
|
10
|
+
"dependencies",
|
|
11
|
+
]);
|
|
12
|
+
const VALUES = new Set(["const", "default", "enum", "examples", "dependentRequired"]);
|
|
13
|
+
const mapValues = (record, map) => Object.fromEntries(Object.entries(record).map(([key, value]) => [key, map(value, key)]));
|
|
14
|
+
const normalizeNode = (schema) => {
|
|
15
|
+
if (Array.isArray(schema))
|
|
16
|
+
return schema.map(normalizeNode);
|
|
17
|
+
if (!isRecord(schema))
|
|
18
|
+
return schema;
|
|
19
|
+
const properties = isRecord(schema.properties) ? schema.properties : undefined;
|
|
20
|
+
return Object.fromEntries(Object.entries(schema).flatMap(([key, value]) => {
|
|
21
|
+
if (VALUES.has(key))
|
|
22
|
+
return [[key, value]];
|
|
23
|
+
if (SCHEMA_MAPS.has(key) && isRecord(value))
|
|
24
|
+
return [[key, mapValues(value, normalizeNode)]];
|
|
25
|
+
// `required` may only name declared properties.
|
|
26
|
+
if (key === "required" && properties && Array.isArray(value))
|
|
27
|
+
return [[key, value.filter((name) => typeof name === "string" && Object.hasOwn(properties, name))]];
|
|
28
|
+
// Draft-04 boolean exclusive bounds become the numeric form.
|
|
29
|
+
if (key === "exclusiveMinimum" && typeof value === "boolean")
|
|
30
|
+
return value && typeof schema.minimum === "number" ? [[key, schema.minimum]] : [];
|
|
31
|
+
if (key === "exclusiveMaximum" && typeof value === "boolean")
|
|
32
|
+
return value && typeof schema.maximum === "number" ? [[key, schema.maximum]] : [];
|
|
33
|
+
if (key === "minimum" && schema.exclusiveMinimum === true)
|
|
34
|
+
return [];
|
|
35
|
+
if (key === "maximum" && schema.exclusiveMaximum === true)
|
|
36
|
+
return [];
|
|
37
|
+
// Draft-07 tuples (`items` array plus `additionalItems`) are `prefixItems` plus `items` in 2020-12.
|
|
38
|
+
if (key === "items" && Array.isArray(value))
|
|
39
|
+
return [["prefixItems", value.map(normalizeNode)]];
|
|
40
|
+
if (key === "additionalItems" && Array.isArray(schema.items))
|
|
41
|
+
return [["items", normalizeNode(value)]];
|
|
42
|
+
return [[key, normalizeNode(value)]];
|
|
43
|
+
}));
|
|
44
|
+
};
|
|
45
|
+
// Gemini accepts a recursive `$ref` only when the loop passes through an optional property or
|
|
46
|
+
// potentially empty array `items`. Replace other self-references with an unconstrained schema.
|
|
47
|
+
const cutLoops = (schema, target, safe) => {
|
|
48
|
+
if (Array.isArray(schema))
|
|
49
|
+
return schema.map((item) => cutLoops(item, target, safe));
|
|
50
|
+
if (!isRecord(schema))
|
|
51
|
+
return schema;
|
|
52
|
+
if (schema.$ref === target && !safe)
|
|
53
|
+
return {};
|
|
54
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
55
|
+
return mapValues(schema, (value, key) => {
|
|
56
|
+
if (VALUES.has(key))
|
|
57
|
+
return value;
|
|
58
|
+
if (key === "items")
|
|
59
|
+
return cutLoops(value, target, safe || !(Number(schema.minItems) > 0));
|
|
60
|
+
if (key === "properties" && isRecord(value))
|
|
61
|
+
return mapValues(value, (child, name) => cutLoops(child, target, safe || !required.includes(name)));
|
|
62
|
+
if (SCHEMA_MAPS.has(key) && isRecord(value))
|
|
63
|
+
return mapValues(value, (child) => cutLoops(child, target, safe));
|
|
64
|
+
return cutLoops(value, target, safe);
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
export const normalize = (schema) => {
|
|
68
|
+
const normalized = normalizeNode(schema);
|
|
69
|
+
if (!isRecord(normalized))
|
|
70
|
+
return {};
|
|
71
|
+
const result = cutLoops(mapValues(normalized, (value, key) => (key === "$defs" || key === "definitions") && isRecord(value)
|
|
72
|
+
? mapValues(value, (def, name) => cutLoops(def, `#/${key}/${name}`, false))
|
|
73
|
+
: value), "#", false);
|
|
74
|
+
return isRecord(result) ? result : {};
|
|
75
|
+
};
|
|
76
|
+
export * as GeminiJsonSchema from "./gemini-json-schema.js";
|
|
@@ -67,11 +67,11 @@ export declare const routes: Route<{
|
|
|
67
67
|
}[];
|
|
68
68
|
readonly tools?: readonly {
|
|
69
69
|
readonly functionDeclarations: readonly {
|
|
70
|
-
readonly description: string;
|
|
71
70
|
readonly name: string;
|
|
72
|
-
readonly
|
|
71
|
+
readonly description: string;
|
|
72
|
+
readonly parametersJsonSchema: {
|
|
73
73
|
readonly [x: string]: unknown;
|
|
74
|
-
}
|
|
74
|
+
};
|
|
75
75
|
}[];
|
|
76
76
|
}[] | undefined;
|
|
77
77
|
readonly serviceTier?: string | undefined;
|
|
@@ -52,11 +52,11 @@ export declare const routes: import("../route/client.js").Route<{
|
|
|
52
52
|
}[];
|
|
53
53
|
readonly tools?: readonly {
|
|
54
54
|
readonly functionDeclarations: readonly {
|
|
55
|
-
readonly description: string;
|
|
56
55
|
readonly name: string;
|
|
57
|
-
readonly
|
|
56
|
+
readonly description: string;
|
|
57
|
+
readonly parametersJsonSchema: {
|
|
58
58
|
readonly [x: string]: unknown;
|
|
59
|
-
}
|
|
59
|
+
};
|
|
60
60
|
}[];
|
|
61
61
|
}[] | undefined;
|
|
62
62
|
readonly serviceTier?: string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20077",
|
|
4
4
|
"name": "@opencode/ai",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@clack/prompts": "1.0.0-alpha.1",
|
|
36
36
|
"@effect/platform-node": "4.0.0-rc.112",
|
|
37
|
-
"@opencode/http-recorder": "0.0.0-dev-
|
|
37
|
+
"@opencode/http-recorder": "0.0.0-dev-20077",
|
|
38
38
|
"@tsconfig/bun": "1.0.9",
|
|
39
39
|
"@types/bun": "1.4.0",
|
|
40
40
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@aws-sdk/credential-providers": "3.1057.0",
|
|
45
45
|
"@smithy/eventstream-codec": "4.2.14",
|
|
46
46
|
"@smithy/util-utf8": "4.2.2",
|
|
47
|
-
"@opencode/schema": "0.0.0-dev-
|
|
47
|
+
"@opencode/schema": "0.0.0-dev-20077",
|
|
48
48
|
"aws4fetch": "1.0.20",
|
|
49
49
|
"effect": "4.0.0-rc.112",
|
|
50
50
|
"google-auth-library": "10.5.0"
|