@opencode-ai/ai 0.0.0-bootstrap.0 → 0.0.0-next-15617

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.
Files changed (151) hide show
  1. package/README.md +176 -1
  2. package/dist/cache-policy.d.ts +2 -0
  3. package/dist/cache-policy.js +108 -0
  4. package/dist/index.d.ts +14 -0
  5. package/dist/index.js +9 -0
  6. package/dist/llm.d.ts +223 -0
  7. package/dist/llm.js +81 -0
  8. package/dist/protocols/anthropic-messages.d.ts +503 -0
  9. package/dist/protocols/anthropic-messages.js +745 -0
  10. package/dist/protocols/bedrock-converse.d.ts +476 -0
  11. package/dist/protocols/bedrock-converse.js +526 -0
  12. package/dist/protocols/bedrock-event-stream.d.ts +9 -0
  13. package/dist/protocols/bedrock-event-stream.js +64 -0
  14. package/dist/protocols/gemini.d.ts +220 -0
  15. package/dist/protocols/gemini.js +420 -0
  16. package/dist/protocols/google-vertex-anthropic.d.ts +494 -0
  17. package/dist/protocols/google-vertex-anthropic.js +33 -0
  18. package/dist/protocols/google-vertex-gemini.d.ts +59 -0
  19. package/dist/protocols/google-vertex-gemini.js +18 -0
  20. package/dist/protocols/index.d.ts +9 -0
  21. package/dist/protocols/index.js +9 -0
  22. package/dist/protocols/openai-chat.d.ts +391 -0
  23. package/dist/protocols/openai-chat.js +423 -0
  24. package/dist/protocols/openai-compatible-chat.d.ts +73 -0
  25. package/dist/protocols/openai-compatible-chat.js +20 -0
  26. package/dist/protocols/openai-compatible-responses.d.ts +85 -0
  27. package/dist/protocols/openai-compatible-responses.js +18 -0
  28. package/dist/protocols/openai-responses.d.ts +613 -0
  29. package/dist/protocols/openai-responses.js +828 -0
  30. package/dist/protocols/shared.d.ts +192 -0
  31. package/dist/protocols/shared.js +236 -0
  32. package/dist/protocols/utils/bedrock-auth.d.ts +18 -0
  33. package/dist/protocols/utils/bedrock-auth.js +41 -0
  34. package/dist/protocols/utils/bedrock-cache.d.ts +15 -0
  35. package/dist/protocols/utils/bedrock-cache.js +29 -0
  36. package/dist/protocols/utils/bedrock-media.d.ts +49 -0
  37. package/dist/protocols/utils/bedrock-media.js +68 -0
  38. package/dist/protocols/utils/cache.d.ts +6 -0
  39. package/dist/protocols/utils/cache.js +8 -0
  40. package/dist/protocols/utils/gemini-tool-schema.d.ts +2 -0
  41. package/dist/protocols/utils/gemini-tool-schema.js +83 -0
  42. package/dist/protocols/utils/lifecycle.d.ts +19 -0
  43. package/dist/protocols/utils/lifecycle.js +65 -0
  44. package/dist/protocols/utils/openai-options.d.ts +22 -0
  45. package/dist/protocols/utils/openai-options.js +64 -0
  46. package/dist/protocols/utils/tool-schema.d.ts +7 -0
  47. package/dist/protocols/utils/tool-schema.js +79 -0
  48. package/dist/protocols/utils/tool-stream.d.ts +155 -0
  49. package/dist/protocols/utils/tool-stream.js +136 -0
  50. package/dist/protocols.d.ts +1 -0
  51. package/dist/protocols.js +1 -0
  52. package/dist/provider-error.d.ts +13 -0
  53. package/dist/provider-error.js +122 -0
  54. package/dist/provider-package.d.ts +13 -0
  55. package/dist/provider-package.js +1 -0
  56. package/dist/provider.d.ts +23 -0
  57. package/dist/provider.js +2 -0
  58. package/dist/providers/amazon-bedrock.d.ts +150 -0
  59. package/dist/providers/amazon-bedrock.js +41 -0
  60. package/dist/providers/anthropic-compatible.d.ts +183 -0
  61. package/dist/providers/anthropic-compatible.js +44 -0
  62. package/dist/providers/anthropic.d.ts +181 -0
  63. package/dist/providers/anthropic.js +39 -0
  64. package/dist/providers/azure/chat.d.ts +2 -0
  65. package/dist/providers/azure/chat.js +1 -0
  66. package/dist/providers/azure/responses.d.ts +2 -0
  67. package/dist/providers/azure/responses.js +1 -0
  68. package/dist/providers/azure.d.ts +183 -0
  69. package/dist/providers/azure.js +89 -0
  70. package/dist/providers/cloudflare.d.ts +232 -0
  71. package/dist/providers/cloudflare.js +87 -0
  72. package/dist/providers/github-copilot.d.ts +167 -0
  73. package/dist/providers/github-copilot.js +47 -0
  74. package/dist/providers/google-vertex/anthropic.d.ts +2 -0
  75. package/dist/providers/google-vertex/anthropic.js +1 -0
  76. package/dist/providers/google-vertex-anthropic.d.ts +181 -0
  77. package/dist/providers/google-vertex-anthropic.js +46 -0
  78. package/dist/providers/google-vertex-shared.d.ts +22 -0
  79. package/dist/providers/google-vertex-shared.js +61 -0
  80. package/dist/providers/google-vertex.d.ts +93 -0
  81. package/dist/providers/google-vertex.js +49 -0
  82. package/dist/providers/google.d.ts +85 -0
  83. package/dist/providers/google.js +33 -0
  84. package/dist/providers/index.d.ts +15 -0
  85. package/dist/providers/index.js +15 -0
  86. package/dist/providers/openai/chat.d.ts +2 -0
  87. package/dist/providers/openai/chat.js +1 -0
  88. package/dist/providers/openai/responses.d.ts +2 -0
  89. package/dist/providers/openai/responses.js +1 -0
  90. package/dist/providers/openai-compatible/responses.d.ts +1 -0
  91. package/dist/providers/openai-compatible/responses.js +1 -0
  92. package/dist/providers/openai-compatible-profile.d.ts +43 -0
  93. package/dist/providers/openai-compatible-profile.js +12 -0
  94. package/dist/providers/openai-compatible-responses.d.ts +107 -0
  95. package/dist/providers/openai-compatible-responses.js +33 -0
  96. package/dist/providers/openai-compatible.d.ts +130 -0
  97. package/dist/providers/openai-compatible.js +55 -0
  98. package/dist/providers/openai-options.d.ts +30 -0
  99. package/dist/providers/openai-options.js +42 -0
  100. package/dist/providers/openai.d.ts +264 -0
  101. package/dist/providers/openai.js +62 -0
  102. package/dist/providers/openrouter.d.ts +321 -0
  103. package/dist/providers/openrouter.js +65 -0
  104. package/dist/providers/xai.d.ts +169 -0
  105. package/dist/providers/xai.js +43 -0
  106. package/dist/providers.d.ts +1 -0
  107. package/dist/providers.js +1 -0
  108. package/dist/route/auth-options.d.ts +34 -0
  109. package/dist/route/auth-options.js +14 -0
  110. package/dist/route/auth.d.ts +49 -0
  111. package/dist/route/auth.js +89 -0
  112. package/dist/route/client.d.ts +330 -0
  113. package/dist/route/client.js +211 -0
  114. package/dist/route/endpoint.d.ts +28 -0
  115. package/dist/route/endpoint.js +21 -0
  116. package/dist/route/executor.d.ts +12 -0
  117. package/dist/route/executor.js +229 -0
  118. package/dist/route/framing.d.ts +23 -0
  119. package/dist/route/framing.js +4 -0
  120. package/dist/route/index.d.ts +16 -0
  121. package/dist/route/index.js +9 -0
  122. package/dist/route/protocol.d.ts +76 -0
  123. package/dist/route/protocol.js +17 -0
  124. package/dist/route/transport/http.d.ts +33 -0
  125. package/dist/route/transport/http.js +98 -0
  126. package/dist/route/transport/index.d.ts +27 -0
  127. package/dist/route/transport/index.js +4 -0
  128. package/dist/route/transport/websocket.d.ts +56 -0
  129. package/dist/route/transport/websocket.js +181 -0
  130. package/dist/route.d.ts +1 -0
  131. package/dist/route.js +1 -0
  132. package/dist/schema/errors.d.ts +153 -0
  133. package/dist/schema/errors.js +150 -0
  134. package/dist/schema/events.d.ts +4292 -0
  135. package/dist/schema/events.js +482 -0
  136. package/dist/schema/ids.d.ts +30 -0
  137. package/dist/schema/ids.js +18 -0
  138. package/dist/schema/index.d.ts +5 -0
  139. package/dist/schema/index.js +5 -0
  140. package/dist/schema/messages.d.ts +385 -0
  141. package/dist/schema/messages.js +246 -0
  142. package/dist/schema/options.d.ts +143 -0
  143. package/dist/schema/options.js +195 -0
  144. package/dist/tool-runtime.d.ts +15 -0
  145. package/dist/tool-runtime.js +35 -0
  146. package/dist/tool.d.ts +134 -0
  147. package/dist/tool.js +66 -0
  148. package/dist/utils/record.d.ts +2 -0
  149. package/dist/utils/record.js +2 -0
  150. package/package.json +36 -9
  151. package/index.js +0 -1
@@ -0,0 +1,220 @@
1
+ import { Schema } from "effect";
2
+ import { Route } from "../route/client";
3
+ import { Protocol } from "../route/protocol";
4
+ import { Lifecycle } from "./utils/lifecycle";
5
+ export declare const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
6
+ declare const GeminiBody: Schema.Struct<{
7
+ contents: Schema.$Array<Schema.Struct<{
8
+ readonly role: Schema.Literals<readonly ["user", "model"]>;
9
+ readonly parts: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
10
+ readonly text: Schema.String;
11
+ readonly thought: Schema.optional<Schema.Boolean>;
12
+ readonly thoughtSignature: Schema.optional<Schema.String>;
13
+ }>, Schema.Struct<{
14
+ readonly inlineData: Schema.Struct<{
15
+ readonly mimeType: Schema.String;
16
+ readonly data: Schema.String;
17
+ }>;
18
+ }>, Schema.Struct<{
19
+ readonly functionCall: Schema.Struct<{
20
+ readonly name: Schema.String;
21
+ readonly args: Schema.Unknown;
22
+ }>;
23
+ readonly thoughtSignature: Schema.optional<Schema.String>;
24
+ }>, Schema.Struct<{
25
+ readonly functionResponse: Schema.Struct<{
26
+ readonly name: Schema.String;
27
+ readonly response: Schema.Unknown;
28
+ }>;
29
+ }>]>>;
30
+ }>>;
31
+ systemInstruction: Schema.optional<Schema.Struct<{
32
+ readonly parts: Schema.$Array<Schema.Struct<{
33
+ readonly text: Schema.String;
34
+ }>>;
35
+ }>>;
36
+ tools: Schema.optional<Schema.$Array<Schema.Struct<{
37
+ readonly functionDeclarations: Schema.$Array<Schema.Struct<{
38
+ readonly name: Schema.String;
39
+ readonly description: Schema.String;
40
+ readonly parameters: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
41
+ }>>;
42
+ }>>>;
43
+ toolConfig: Schema.optional<Schema.Struct<{
44
+ readonly functionCallingConfig: Schema.Struct<{
45
+ readonly mode: Schema.Literals<readonly ["AUTO", "NONE", "ANY"]>;
46
+ readonly allowedFunctionNames: Schema.optional<Schema.$Array<Schema.String>>;
47
+ }>;
48
+ }>>;
49
+ generationConfig: Schema.optional<Schema.Struct<{
50
+ readonly maxOutputTokens: Schema.optional<Schema.Number>;
51
+ readonly temperature: Schema.optional<Schema.Number>;
52
+ readonly topP: Schema.optional<Schema.Number>;
53
+ readonly topK: Schema.optional<Schema.Number>;
54
+ readonly stopSequences: Schema.optional<Schema.$Array<Schema.String>>;
55
+ readonly thinkingConfig: Schema.optional<Schema.Struct<{
56
+ readonly thinkingBudget: Schema.optional<Schema.Number>;
57
+ readonly includeThoughts: Schema.optional<Schema.Boolean>;
58
+ }>>;
59
+ }>>;
60
+ }>;
61
+ export type GeminiBody = Schema.Schema.Type<typeof GeminiBody>;
62
+ /**
63
+ * The Gemini protocol — request body construction, body schema, and the
64
+ * streaming-event state machine. Used by Google AI Studio Gemini and (once
65
+ * registered) Vertex Gemini.
66
+ */
67
+ export declare const protocol: Protocol<{
68
+ readonly contents: readonly {
69
+ readonly role: "user" | "model";
70
+ readonly parts: readonly ({
71
+ readonly text: string;
72
+ readonly thought?: boolean | undefined;
73
+ readonly thoughtSignature?: string | undefined;
74
+ } | {
75
+ readonly inlineData: {
76
+ readonly mimeType: string;
77
+ readonly data: string;
78
+ };
79
+ } | {
80
+ readonly functionCall: {
81
+ readonly name: string;
82
+ readonly args: unknown;
83
+ };
84
+ readonly thoughtSignature?: string | undefined;
85
+ } | {
86
+ readonly functionResponse: {
87
+ readonly name: string;
88
+ readonly response: unknown;
89
+ };
90
+ })[];
91
+ }[];
92
+ readonly systemInstruction?: {
93
+ readonly parts: readonly {
94
+ readonly text: string;
95
+ }[];
96
+ } | undefined;
97
+ readonly tools?: readonly {
98
+ readonly functionDeclarations: readonly {
99
+ readonly name: string;
100
+ readonly description: string;
101
+ readonly parameters?: {
102
+ readonly [x: string]: unknown;
103
+ } | undefined;
104
+ }[];
105
+ }[] | undefined;
106
+ readonly toolConfig?: {
107
+ readonly functionCallingConfig: {
108
+ readonly mode: "AUTO" | "NONE" | "ANY";
109
+ readonly allowedFunctionNames?: readonly string[] | undefined;
110
+ };
111
+ } | undefined;
112
+ readonly generationConfig?: {
113
+ readonly maxOutputTokens?: number | undefined;
114
+ readonly temperature?: number | undefined;
115
+ readonly topP?: number | undefined;
116
+ readonly topK?: number | undefined;
117
+ readonly stopSequences?: readonly string[] | undefined;
118
+ readonly thinkingConfig?: {
119
+ readonly thinkingBudget?: number | undefined;
120
+ readonly includeThoughts?: boolean | undefined;
121
+ } | undefined;
122
+ } | undefined;
123
+ }, string, {
124
+ readonly candidates?: readonly {
125
+ readonly content?: {
126
+ readonly role: "user" | "model";
127
+ readonly parts: readonly ({
128
+ readonly text: string;
129
+ readonly thought?: boolean | undefined;
130
+ readonly thoughtSignature?: string | undefined;
131
+ } | {
132
+ readonly inlineData: {
133
+ readonly mimeType: string;
134
+ readonly data: string;
135
+ };
136
+ } | {
137
+ readonly functionCall: {
138
+ readonly name: string;
139
+ readonly args: unknown;
140
+ };
141
+ readonly thoughtSignature?: string | undefined;
142
+ } | {
143
+ readonly functionResponse: {
144
+ readonly name: string;
145
+ readonly response: unknown;
146
+ };
147
+ })[];
148
+ } | undefined;
149
+ readonly finishReason?: string | undefined;
150
+ }[] | undefined;
151
+ readonly usageMetadata?: {
152
+ readonly cachedContentTokenCount?: number | undefined;
153
+ readonly thoughtsTokenCount?: number | undefined;
154
+ readonly promptTokenCount?: number | undefined;
155
+ readonly candidatesTokenCount?: number | undefined;
156
+ readonly totalTokenCount?: number | undefined;
157
+ } | undefined;
158
+ }, {
159
+ hasToolCalls: boolean;
160
+ nextToolCallId: number;
161
+ lifecycle: Lifecycle.State;
162
+ }>;
163
+ export declare const route: Route<{
164
+ readonly contents: readonly {
165
+ readonly role: "user" | "model";
166
+ readonly parts: readonly ({
167
+ readonly text: string;
168
+ readonly thought?: boolean | undefined;
169
+ readonly thoughtSignature?: string | undefined;
170
+ } | {
171
+ readonly inlineData: {
172
+ readonly mimeType: string;
173
+ readonly data: string;
174
+ };
175
+ } | {
176
+ readonly functionCall: {
177
+ readonly name: string;
178
+ readonly args: unknown;
179
+ };
180
+ readonly thoughtSignature?: string | undefined;
181
+ } | {
182
+ readonly functionResponse: {
183
+ readonly name: string;
184
+ readonly response: unknown;
185
+ };
186
+ })[];
187
+ }[];
188
+ readonly systemInstruction?: {
189
+ readonly parts: readonly {
190
+ readonly text: string;
191
+ }[];
192
+ } | undefined;
193
+ readonly tools?: readonly {
194
+ readonly functionDeclarations: readonly {
195
+ readonly name: string;
196
+ readonly description: string;
197
+ readonly parameters?: {
198
+ readonly [x: string]: unknown;
199
+ } | undefined;
200
+ }[];
201
+ }[] | undefined;
202
+ readonly toolConfig?: {
203
+ readonly functionCallingConfig: {
204
+ readonly mode: "AUTO" | "NONE" | "ANY";
205
+ readonly allowedFunctionNames?: readonly string[] | undefined;
206
+ };
207
+ } | undefined;
208
+ readonly generationConfig?: {
209
+ readonly maxOutputTokens?: number | undefined;
210
+ readonly temperature?: number | undefined;
211
+ readonly topP?: number | undefined;
212
+ readonly topK?: number | undefined;
213
+ readonly stopSequences?: readonly string[] | undefined;
214
+ readonly thinkingConfig?: {
215
+ readonly thinkingBudget?: number | undefined;
216
+ readonly includeThoughts?: boolean | undefined;
217
+ } | undefined;
218
+ } | undefined;
219
+ }, import("../route/transport/http").HttpPrepared<string>>;
220
+ export * as Gemini from "./gemini";
@@ -0,0 +1,420 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Route } from "../route/client";
3
+ import { Auth } from "../route/auth";
4
+ import { Endpoint } from "../route/endpoint";
5
+ import { Framing } from "../route/framing";
6
+ import { Protocol } from "../route/protocol";
7
+ import { LLMEvent, Usage, } from "../schema";
8
+ import { JsonObject, optionalArray, ProviderShared } from "./shared";
9
+ import { GeminiToolSchema } from "./utils/gemini-tool-schema";
10
+ import { Lifecycle } from "./utils/lifecycle";
11
+ import { ToolSchemaProjection } from "./utils/tool-schema";
12
+ const ADAPTER = "gemini";
13
+ const MEDIA_MIMES = new Set(ProviderShared.MEDIA_MIMES);
14
+ export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
15
+ // =============================================================================
16
+ // Request Body Schema
17
+ // =============================================================================
18
+ const GeminiTextPart = Schema.Struct({
19
+ text: Schema.String,
20
+ thought: Schema.optional(Schema.Boolean),
21
+ thoughtSignature: Schema.optional(Schema.String),
22
+ });
23
+ const GeminiInlineDataPart = Schema.Struct({
24
+ inlineData: Schema.Struct({
25
+ mimeType: Schema.String,
26
+ data: Schema.String,
27
+ }),
28
+ });
29
+ const GeminiFunctionCallPart = Schema.Struct({
30
+ functionCall: Schema.Struct({
31
+ name: Schema.String,
32
+ args: Schema.Unknown,
33
+ }),
34
+ thoughtSignature: Schema.optional(Schema.String),
35
+ });
36
+ const GeminiFunctionResponsePart = Schema.Struct({
37
+ functionResponse: Schema.Struct({
38
+ name: Schema.String,
39
+ response: Schema.Unknown,
40
+ }),
41
+ });
42
+ const GeminiContentPart = Schema.Union([
43
+ GeminiTextPart,
44
+ GeminiInlineDataPart,
45
+ GeminiFunctionCallPart,
46
+ GeminiFunctionResponsePart,
47
+ ]);
48
+ const GeminiContent = Schema.Struct({
49
+ role: Schema.Literals(["user", "model"]),
50
+ parts: Schema.Array(GeminiContentPart),
51
+ });
52
+ const GeminiSystemInstruction = Schema.Struct({
53
+ parts: Schema.Array(Schema.Struct({ text: Schema.String })),
54
+ });
55
+ const GeminiFunctionDeclaration = Schema.Struct({
56
+ name: Schema.String,
57
+ description: Schema.String,
58
+ parameters: Schema.optional(JsonObject),
59
+ });
60
+ const GeminiTool = Schema.Struct({
61
+ functionDeclarations: Schema.Array(GeminiFunctionDeclaration),
62
+ });
63
+ const GeminiToolConfig = Schema.Struct({
64
+ functionCallingConfig: Schema.Struct({
65
+ mode: Schema.Literals(["AUTO", "NONE", "ANY"]),
66
+ allowedFunctionNames: optionalArray(Schema.String),
67
+ }),
68
+ });
69
+ const GeminiThinkingConfig = Schema.Struct({
70
+ thinkingBudget: Schema.optional(Schema.Number),
71
+ includeThoughts: Schema.optional(Schema.Boolean),
72
+ });
73
+ const GeminiGenerationConfig = Schema.Struct({
74
+ maxOutputTokens: Schema.optional(Schema.Number),
75
+ temperature: Schema.optional(Schema.Number),
76
+ topP: Schema.optional(Schema.Number),
77
+ topK: Schema.optional(Schema.Number),
78
+ stopSequences: optionalArray(Schema.String),
79
+ thinkingConfig: Schema.optional(GeminiThinkingConfig),
80
+ });
81
+ const GeminiBodyFields = {
82
+ contents: Schema.Array(GeminiContent),
83
+ systemInstruction: Schema.optional(GeminiSystemInstruction),
84
+ tools: optionalArray(GeminiTool),
85
+ toolConfig: Schema.optional(GeminiToolConfig),
86
+ generationConfig: Schema.optional(GeminiGenerationConfig),
87
+ };
88
+ const GeminiBody = Schema.Struct(GeminiBodyFields);
89
+ const GeminiUsage = Schema.Struct({
90
+ cachedContentTokenCount: Schema.optional(Schema.Number),
91
+ thoughtsTokenCount: Schema.optional(Schema.Number),
92
+ promptTokenCount: Schema.optional(Schema.Number),
93
+ candidatesTokenCount: Schema.optional(Schema.Number),
94
+ totalTokenCount: Schema.optional(Schema.Number),
95
+ });
96
+ const GeminiCandidate = Schema.Struct({
97
+ content: Schema.optional(GeminiContent),
98
+ finishReason: Schema.optional(Schema.String),
99
+ });
100
+ const GeminiEvent = Schema.Struct({
101
+ candidates: optionalArray(GeminiCandidate),
102
+ usageMetadata: Schema.optional(GeminiUsage),
103
+ });
104
+ // =============================================================================
105
+ // Tool Schema Conversion
106
+ // =============================================================================
107
+ // Tool-schema conversion has two distinct concerns:
108
+ //
109
+ // 1. Sanitize — fix common authoring mistakes Gemini rejects: integer/number
110
+ // enums (must be strings), `required` entries that don't match a property,
111
+ // untyped arrays (`items` must be present), and `properties`/`required`
112
+ // keys on non-object scalars. Mirrors OpenCode's historical Gemini rules.
113
+ //
114
+ // 2. Project — lossy mapping from JSON Schema to Gemini's schema dialect:
115
+ // drop empty objects, derive `nullable: true` from `type: [..., "null"]`,
116
+ // coerce `const` to `[const]` enum, recurse properties/items, propagate
117
+ // only an allowlisted set of keys (description, required, format, type,
118
+ // properties, items, allOf, anyOf, oneOf, minLength). Anything outside the
119
+ // allowlist (e.g. `additionalProperties`, `$ref`) is silently dropped.
120
+ //
121
+ // Sanitize runs first, then project. The implementation lives in
122
+ // `utils/gemini-tool-schema` so this protocol keeps the same shape as the other
123
+ // provider protocols.
124
+ // =============================================================================
125
+ // Request Lowering
126
+ // =============================================================================
127
+ const lowerTool = (tool, inputSchema) => ({
128
+ name: tool.name,
129
+ description: tool.description,
130
+ parameters: GeminiToolSchema.convert(inputSchema),
131
+ });
132
+ const lowerToolConfig = (toolChoice) => ProviderShared.matchToolChoice("Gemini", toolChoice, {
133
+ auto: () => ({ functionCallingConfig: { mode: "AUTO" } }),
134
+ none: () => ({ functionCallingConfig: { mode: "NONE" } }),
135
+ required: () => ({ functionCallingConfig: { mode: "ANY" } }),
136
+ tool: (name) => ({ functionCallingConfig: { mode: "ANY", allowedFunctionNames: [name] } }),
137
+ });
138
+ const lowerUserPart = Effect.fn("Gemini.lowerUserPart")(function* (part) {
139
+ if (part.type === "text")
140
+ return { text: part.text };
141
+ const media = yield* ProviderShared.validateMedia("Gemini", part, MEDIA_MIMES);
142
+ return { inlineData: { mimeType: media.mime, data: media.base64 } };
143
+ });
144
+ const googleMetadata = (metadata) => ({ google: metadata });
145
+ const thoughtSignature = (providerMetadata) => {
146
+ const google = providerMetadata?.google;
147
+ return ProviderShared.isRecord(google) && typeof google.thoughtSignature === "string"
148
+ ? google.thoughtSignature
149
+ : undefined;
150
+ };
151
+ const lowerToolCall = (part) => ({
152
+ functionCall: { name: part.name, args: part.input },
153
+ thoughtSignature: thoughtSignature(part.providerMetadata),
154
+ });
155
+ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
156
+ const contents = [];
157
+ for (const message of request.messages) {
158
+ if (message.role === "system") {
159
+ const part = yield* ProviderShared.wrappedSystemUpdate("Gemini", message);
160
+ const previous = contents.at(-1);
161
+ if (previous?.role === "user")
162
+ contents[contents.length - 1] = { role: "user", parts: [...previous.parts, { text: part.text }] };
163
+ else
164
+ contents.push({ role: "user", parts: [{ text: part.text }] });
165
+ continue;
166
+ }
167
+ if (message.role === "user") {
168
+ const parts = [];
169
+ for (const part of message.content) {
170
+ if (!ProviderShared.supportsContent(part, ["text", "media"]))
171
+ return yield* ProviderShared.unsupportedContent("Gemini", "user", ["text", "media"]);
172
+ parts.push(yield* lowerUserPart(part));
173
+ }
174
+ contents.push({ role: "user", parts });
175
+ continue;
176
+ }
177
+ if (message.role === "assistant") {
178
+ const parts = [];
179
+ for (const part of message.content) {
180
+ if (!ProviderShared.supportsContent(part, ["text", "reasoning", "tool-call"]))
181
+ return yield* ProviderShared.unsupportedContent("Gemini", "assistant", ["text", "reasoning", "tool-call"]);
182
+ if (part.type === "text") {
183
+ parts.push({ text: part.text });
184
+ continue;
185
+ }
186
+ if (part.type === "reasoning") {
187
+ parts.push({ text: part.text, thought: true, thoughtSignature: thoughtSignature(part.providerMetadata) });
188
+ continue;
189
+ }
190
+ if (part.type === "tool-call") {
191
+ parts.push(lowerToolCall(part));
192
+ continue;
193
+ }
194
+ }
195
+ contents.push({ role: "model", parts });
196
+ continue;
197
+ }
198
+ const parts = [];
199
+ for (const part of message.content) {
200
+ if (!ProviderShared.supportsContent(part, ["tool-result"]))
201
+ return yield* ProviderShared.unsupportedContent("Gemini", "tool", ["tool-result"]);
202
+ if (part.result.type !== "content") {
203
+ parts.push({
204
+ functionResponse: {
205
+ name: part.name,
206
+ response: {
207
+ name: part.name,
208
+ content: ProviderShared.toolResultText(part),
209
+ },
210
+ },
211
+ });
212
+ continue;
213
+ }
214
+ const content = part.result.value;
215
+ const text = content.filter((item) => item.type === "text").map((item) => item.text);
216
+ parts.push({
217
+ functionResponse: {
218
+ name: part.name,
219
+ response: {
220
+ name: part.name,
221
+ content: text.join("\n"),
222
+ },
223
+ },
224
+ });
225
+ for (const item of content) {
226
+ if (item.type === "text")
227
+ continue;
228
+ const media = yield* ProviderShared.validateToolFile("Gemini", item, MEDIA_MIMES);
229
+ parts.push({ inlineData: { mimeType: media.mime, data: media.base64 } });
230
+ }
231
+ }
232
+ contents.push({ role: "user", parts });
233
+ }
234
+ return contents;
235
+ });
236
+ const geminiOptions = (request) => request.providerOptions?.gemini;
237
+ const thinkingConfig = (request) => {
238
+ const value = geminiOptions(request)?.thinkingConfig;
239
+ if (!ProviderShared.isRecord(value))
240
+ return undefined;
241
+ const result = {
242
+ thinkingBudget: typeof value.thinkingBudget === "number" ? value.thinkingBudget : undefined,
243
+ includeThoughts: typeof value.includeThoughts === "boolean" ? value.includeThoughts : undefined,
244
+ };
245
+ return Object.values(result).some((item) => item !== undefined) ? result : undefined;
246
+ };
247
+ const fromRequest = Effect.fn("Gemini.fromRequest")(function* (request) {
248
+ const toolsEnabled = request.tools.length > 0 && request.toolChoice?.type !== "none";
249
+ const generation = request.generation;
250
+ const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
251
+ const generationConfig = {
252
+ maxOutputTokens: generation?.maxTokens,
253
+ temperature: generation?.temperature,
254
+ topP: generation?.topP,
255
+ topK: generation?.topK,
256
+ stopSequences: generation?.stop,
257
+ thinkingConfig: thinkingConfig(request),
258
+ };
259
+ return {
260
+ contents: yield* lowerMessages(request),
261
+ systemInstruction: request.system.length === 0 ? undefined : { parts: [{ text: ProviderShared.joinText(request.system) }] },
262
+ tools: toolsEnabled
263
+ ? [
264
+ {
265
+ functionDeclarations: request.tools.map((tool) => lowerTool(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility))),
266
+ },
267
+ ]
268
+ : undefined,
269
+ toolConfig: toolsEnabled && request.toolChoice ? yield* lowerToolConfig(request.toolChoice) : undefined,
270
+ generationConfig: Object.values(generationConfig).some((value) => value !== undefined)
271
+ ? generationConfig
272
+ : undefined,
273
+ };
274
+ });
275
+ // =============================================================================
276
+ // Stream Parsing
277
+ // =============================================================================
278
+ // Gemini reports `promptTokenCount` (inclusive total) with a
279
+ // `cachedContentTokenCount` subset. `candidatesTokenCount` is *exclusive*
280
+ // of `thoughtsTokenCount` — visible-only, not a total — so we sum the two
281
+ // to produce the inclusive `outputTokens` the rest of the contract expects.
282
+ const mapUsage = (usage) => {
283
+ if (!usage)
284
+ return undefined;
285
+ const cached = usage.cachedContentTokenCount;
286
+ const nonCached = ProviderShared.subtractTokens(usage.promptTokenCount, cached);
287
+ // `candidatesTokenCount` is visible-only; sum with thoughts to produce the
288
+ // inclusive `outputTokens` the contract expects. Only compute the total
289
+ // when the visible component is reported — otherwise we'd fabricate an
290
+ // inclusive number from a partial breakdown.
291
+ const outputTokens = usage.candidatesTokenCount !== undefined ? usage.candidatesTokenCount + (usage.thoughtsTokenCount ?? 0) : undefined;
292
+ return new Usage({
293
+ inputTokens: usage.promptTokenCount,
294
+ outputTokens,
295
+ nonCachedInputTokens: nonCached,
296
+ cacheReadInputTokens: cached,
297
+ reasoningTokens: usage.thoughtsTokenCount,
298
+ totalTokens: ProviderShared.totalTokens(usage.promptTokenCount, outputTokens, usage.totalTokenCount),
299
+ providerMetadata: { google: usage },
300
+ });
301
+ };
302
+ const mapFinishReason = (finishReason, hasToolCalls) => {
303
+ if (finishReason === "STOP")
304
+ return hasToolCalls ? "tool-calls" : "stop";
305
+ if (finishReason === "MAX_TOKENS")
306
+ return "length";
307
+ if (finishReason === "IMAGE_SAFETY" ||
308
+ finishReason === "RECITATION" ||
309
+ finishReason === "SAFETY" ||
310
+ finishReason === "BLOCKLIST" ||
311
+ finishReason === "PROHIBITED_CONTENT" ||
312
+ finishReason === "SPII")
313
+ return "content-filter";
314
+ if (finishReason === "MALFORMED_FUNCTION_CALL")
315
+ return "error";
316
+ return "unknown";
317
+ };
318
+ const finish = (state) => state.finishReason || state.usage
319
+ ? (() => {
320
+ const events = [];
321
+ const lifecycle = state.reasoningSignature
322
+ ? Lifecycle.reasoningEnd(state.lifecycle, events, "reasoning-0", googleMetadata({ thoughtSignature: state.reasoningSignature }))
323
+ : state.lifecycle;
324
+ Lifecycle.finish(lifecycle, events, {
325
+ reason: mapFinishReason(state.finishReason, state.hasToolCalls),
326
+ usage: state.usage,
327
+ });
328
+ return events;
329
+ })()
330
+ : [];
331
+ const step = (state, event) => {
332
+ const nextState = {
333
+ ...state,
334
+ usage: event.usageMetadata ? (mapUsage(event.usageMetadata) ?? state.usage) : state.usage,
335
+ };
336
+ const candidate = event.candidates?.[0];
337
+ if (!candidate?.content)
338
+ return Effect.succeed([
339
+ { ...nextState, finishReason: candidate?.finishReason ?? nextState.finishReason },
340
+ [],
341
+ ]);
342
+ const events = [];
343
+ let hasToolCalls = nextState.hasToolCalls;
344
+ let lifecycle = nextState.lifecycle;
345
+ let nextToolCallId = nextState.nextToolCallId;
346
+ let reasoningSignature = nextState.reasoningSignature;
347
+ for (const part of candidate.content.parts) {
348
+ if ("thoughtSignature" in part && part.thoughtSignature && "thought" in part && part.thought)
349
+ reasoningSignature = part.thoughtSignature;
350
+ if ("text" in part && part.text.length > 0) {
351
+ if (part.thought) {
352
+ lifecycle = Lifecycle.reasoningDelta(lifecycle, events, "reasoning-0", part.text, part.thoughtSignature ? googleMetadata({ thoughtSignature: part.thoughtSignature }) : undefined);
353
+ continue;
354
+ }
355
+ lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningSignature ? googleMetadata({ thoughtSignature: reasoningSignature }) : undefined);
356
+ lifecycle = Lifecycle.textDelta(lifecycle, events, "text-0", part.text);
357
+ continue;
358
+ }
359
+ if ("functionCall" in part) {
360
+ const input = part.functionCall.args;
361
+ const id = `tool_${nextToolCallId++}`;
362
+ lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningSignature ? googleMetadata({ thoughtSignature: reasoningSignature }) : undefined);
363
+ lifecycle = Lifecycle.stepStart(lifecycle, events);
364
+ events.push(LLMEvent.toolCall({
365
+ id,
366
+ name: part.functionCall.name,
367
+ input,
368
+ providerMetadata: part.thoughtSignature
369
+ ? googleMetadata({ thoughtSignature: part.thoughtSignature })
370
+ : undefined,
371
+ }));
372
+ hasToolCalls = true;
373
+ }
374
+ }
375
+ return Effect.succeed([
376
+ {
377
+ ...nextState,
378
+ hasToolCalls,
379
+ lifecycle,
380
+ nextToolCallId,
381
+ reasoningSignature,
382
+ finishReason: candidate.finishReason ?? nextState.finishReason,
383
+ },
384
+ events,
385
+ ]);
386
+ };
387
+ // =============================================================================
388
+ // Protocol And Gemini Route
389
+ // =============================================================================
390
+ /**
391
+ * The Gemini protocol — request body construction, body schema, and the
392
+ * streaming-event state machine. Used by Google AI Studio Gemini and (once
393
+ * registered) Vertex Gemini.
394
+ */
395
+ export const protocol = Protocol.make({
396
+ id: ADAPTER,
397
+ body: {
398
+ schema: GeminiBody,
399
+ from: fromRequest,
400
+ },
401
+ stream: {
402
+ event: Protocol.jsonEvent(GeminiEvent),
403
+ initial: () => ({ hasToolCalls: false, nextToolCallId: 0, lifecycle: Lifecycle.initial() }),
404
+ step,
405
+ onHalt: finish,
406
+ },
407
+ });
408
+ export const route = Route.make({
409
+ id: ADAPTER,
410
+ provider: "google",
411
+ providerMetadataKey: "google",
412
+ protocol,
413
+ // Gemini's path embeds the model id and pins SSE framing at the URL level.
414
+ endpoint: Endpoint.path(({ request }) => `/models/${request.model.id}:streamGenerateContent?alt=sse`, {
415
+ baseURL: DEFAULT_BASE_URL,
416
+ }),
417
+ auth: Auth.none,
418
+ framing: Framing.sse,
419
+ });
420
+ export * as Gemini from "./gemini";