@effect-uai/google 0.15.0 → 0.17.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.
Files changed (44) hide show
  1. package/README.md +11 -2
  2. package/dist/Gemini.d.mts +1 -1
  3. package/dist/GeminiEmbedding.d.mts +1 -1
  4. package/dist/GeminiImageGenerator.d.mts +1 -1
  5. package/dist/GeminiLiveSession.d.mts +17 -0
  6. package/dist/GeminiLiveSession.d.mts.map +1 -0
  7. package/dist/GeminiLiveSession.mjs +26 -0
  8. package/dist/GeminiLiveSession.mjs.map +1 -0
  9. package/dist/GeminiSynthesizer.d.mts +1 -1
  10. package/dist/GeminiSynthesizer.test.mjs +1 -1
  11. package/dist/GoogleDeepResearch.d.mts +1 -1
  12. package/dist/GoogleDeepResearch.test.mjs +1 -1
  13. package/dist/LyriaGenerator.d.mts +1 -1
  14. package/dist/LyriaGenerator.test.mjs +1 -1
  15. package/dist/codec.d.mts +5 -2
  16. package/dist/codec.d.mts.map +1 -1
  17. package/dist/codec.mjs +4 -1
  18. package/dist/codec.mjs.map +1 -1
  19. package/dist/geminiSpeechCodec.test.mjs +1 -1
  20. package/dist/geminiTools.test.mjs +1 -1
  21. package/dist/index.d.mts +3 -2
  22. package/dist/index.mjs +2 -1
  23. package/dist/models-BVSKJwCx.d.mts +87 -0
  24. package/dist/models-BVSKJwCx.d.mts.map +1 -0
  25. package/dist/models.d.mts +2 -75
  26. package/dist/realtimeSession-7HSIjWh4.d.mts +62 -0
  27. package/dist/realtimeSession-7HSIjWh4.d.mts.map +1 -0
  28. package/dist/realtimeSession.d.mts +2 -0
  29. package/dist/realtimeSession.mjs +406 -0
  30. package/dist/realtimeSession.mjs.map +1 -0
  31. package/dist/realtimeSession.test.d.mts +1 -0
  32. package/dist/realtimeSession.test.mjs +505 -0
  33. package/dist/realtimeSession.test.mjs.map +1 -0
  34. package/dist/{test.DNmyFkvJ-N3QqMRwZ.mjs → test.DNmyFkvJ-BPz3G1HS.mjs} +531 -22
  35. package/dist/test.DNmyFkvJ-BPz3G1HS.mjs.map +1 -0
  36. package/package.json +6 -2
  37. package/src/GeminiLiveSession.ts +45 -0
  38. package/src/codec.ts +3 -2
  39. package/src/index.ts +1 -0
  40. package/src/models.ts +19 -0
  41. package/src/realtimeSession.test.ts +415 -0
  42. package/src/realtimeSession.ts +807 -0
  43. package/dist/models.d.mts.map +0 -1
  44. package/dist/test.DNmyFkvJ-N3QqMRwZ.mjs.map +0 -1
package/README.md CHANGED
@@ -2,8 +2,17 @@
2
2
 
3
3
  Google Gemini provider for [`@effect-uai/core`](https://www.npmjs.com/package/@effect-uai/core).
4
4
 
5
- Implements the `LanguageModel` contract against Google's Gemini API
6
- with SSE streaming.
5
+ One key reaches the whole Gemini family, each at its own subpath:
6
+
7
+ - **`@effect-uai/google`** (`Gemini`): the `LanguageModel` contract over
8
+ Gemini's API with SSE streaming.
9
+ - **`@effect-uai/google/GeminiLiveSession`**: `RealtimeSession` over
10
+ Gemini Live, a live voice conversation that also takes camera frames.
11
+ - **`@effect-uai/google/GeminiEmbedding`**, **`GeminiSynthesizer`**,
12
+ **`GeminiImageGenerator`**, **`LyriaGenerator`**: embeddings, speech,
13
+ Nano Banana images and Lyria music.
14
+ - **`@effect-uai/google/GoogleDeepResearch`**: long-running research to
15
+ a cited report.
7
16
 
8
17
  ## Install
9
18
 
package/dist/Gemini.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ImageConfig, WireChunk } from "./codec.mjs";
2
2
  import { codeExecutionTool, googleSearchTool, urlContextTool } from "./GeminiTools.mjs";
3
- import { GoogleModel } from "./models.mjs";
3
+ import { s as GoogleModel } from "./models-BVSKJwCx.mjs";
4
4
  import { Context, Effect, Layer, Redacted, Schema, Stream } from "effect";
5
5
  import { HttpClient } from "effect/unstable/http";
6
6
  import * as AiError from "@effect-uai/core/AiError";
@@ -1,4 +1,4 @@
1
- import { GoogleEmbeddingModel } from "./models.mjs";
1
+ import { o as GoogleEmbeddingModel } from "./models-BVSKJwCx.mjs";
2
2
  import { Context, Effect, Layer, Redacted } from "effect";
3
3
  import { HttpClient } from "effect/unstable/http";
4
4
  import * as AiError from "@effect-uai/core/AiError";
@@ -1,4 +1,4 @@
1
- import { GeminiImageModel } from "./models.mjs";
1
+ import { t as GeminiImageModel } from "./models-BVSKJwCx.mjs";
2
2
  import { Context, Effect, Layer, Redacted, Schema } from "effect";
3
3
  import { HttpClient } from "effect/unstable/http";
4
4
  import * as AiError from "@effect-uai/core/AiError";
@@ -0,0 +1,17 @@
1
+ import { n as GeminiLiveRequest, t as Config } from "./realtimeSession-7HSIjWh4.mjs";
2
+ import { Context, Effect, Layer, Scope } from "effect";
3
+ import * as AiError from "@effect-uai/core/AiError";
4
+ import { RealtimeSession, RealtimeSessionHandle, RealtimeVideoInput } from "@effect-uai/core/RealtimeSession";
5
+ declare namespace GeminiLiveSession_d_exports {
6
+ export { Config, GeminiLiveRequest, GeminiLiveSession, GeminiLiveSessionService, layer, make };
7
+ }
8
+ type GeminiLiveSessionService = {
9
+ readonly open: (request: GeminiLiveRequest) => Effect.Effect<RealtimeSessionHandle, AiError.AiError, Scope.Scope>;
10
+ };
11
+ declare const GeminiLiveSession_base: Context.ServiceClass<GeminiLiveSession, "@betalyra/effect-uai/providers/google/GeminiLiveSession", GeminiLiveSessionService>;
12
+ declare class GeminiLiveSession extends GeminiLiveSession_base {}
13
+ declare const make: (cfg: Config) => GeminiLiveSessionService;
14
+ declare const layer: (cfg: Config) => Layer.Layer<GeminiLiveSession | RealtimeSession | RealtimeVideoInput>;
15
+ //#endregion
16
+ export { type Config, type GeminiLiveRequest, GeminiLiveSession, GeminiLiveSessionService, layer, make, GeminiLiveSession_d_exports as t };
17
+ //# sourceMappingURL=GeminiLiveSession.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeminiLiveSession.d.mts","names":[],"sources":["../src/GeminiLiveSession.ts"],"mappings":";;;;;;;KAsBY;WACD,OACP,SAAS,sBACN,OAAO,OAAO,uBAAuB,QAAQ,SAAS,MAAM;;;cAGtD,0BAA0B;cAK1B,OAAI,KAAS,WAAS;cAEtB,QAAK,KACX,WACJ,MAAM,MAAM,oBAAoB,kBAAkB"}
@@ -0,0 +1,26 @@
1
+ import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
2
+ import { openSession } from "./realtimeSession.mjs";
3
+ import { Context, Layer } from "effect";
4
+ import { RealtimeSession, RealtimeVideoInput } from "@effect-uai/core/RealtimeSession";
5
+ //#region src/GeminiLiveSession.ts
6
+ /**
7
+ * Gemini Live speech-to-speech Layer. Registers the typed `GeminiLiveSession`
8
+ * tag, the generic `RealtimeSession` tag, and the `RealtimeVideoInput` marker,
9
+ * which makes `sendVideoFrame` compile against this provider and not against
10
+ * one without video.
11
+ *
12
+ * Auth rides on the URL, so this runs anywhere a `WebSocket` global exists:
13
+ * Node, Bun, Deno and the browser, with no peer dependency.
14
+ */
15
+ var GeminiLiveSession_exports = /* @__PURE__ */ __exportAll({
16
+ GeminiLiveSession: () => GeminiLiveSession,
17
+ layer: () => layer,
18
+ make: () => make
19
+ });
20
+ var GeminiLiveSession = class extends Context.Service()("@betalyra/effect-uai/providers/google/GeminiLiveSession") {};
21
+ const make = (cfg) => ({ open: openSession(cfg) });
22
+ const layer = (cfg) => Layer.mergeAll(Layer.succeed(GeminiLiveSession, make(cfg)), Layer.succeed(RealtimeSession, { open: (request) => make(cfg).open(request) }), Layer.succeed(RealtimeVideoInput, void 0));
23
+ //#endregion
24
+ export { GeminiLiveSession, layer, make, GeminiLiveSession_exports as t };
25
+
26
+ //# sourceMappingURL=GeminiLiveSession.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeminiLiveSession.mjs","names":[],"sources":["../src/GeminiLiveSession.ts"],"sourcesContent":["/**\n * Gemini Live speech-to-speech Layer. Registers the typed `GeminiLiveSession`\n * tag, the generic `RealtimeSession` tag, and the `RealtimeVideoInput` marker,\n * which makes `sendVideoFrame` compile against this provider and not against\n * one without video.\n *\n * Auth rides on the URL, so this runs anywhere a `WebSocket` global exists:\n * Node, Bun, Deno and the browser, with no peer dependency.\n */\nimport { Context, Effect, Layer, type Scope } from \"effect\"\nimport type * as AiError from \"@effect-uai/core/AiError\"\nimport type { CommonSessionRequest } from \"@effect-uai/core/Realtime\"\nimport {\n RealtimeSession,\n type RealtimeSessionHandle,\n type RealtimeSessionService,\n RealtimeVideoInput,\n} from \"@effect-uai/core/RealtimeSession\"\nimport { type Config, type GeminiLiveRequest, openSession } from \"./realtimeSession.js\"\n\nexport type { Config, GeminiLiveRequest } from \"./realtimeSession.js\"\n\nexport type GeminiLiveSessionService = {\n readonly open: (\n request: GeminiLiveRequest,\n ) => Effect.Effect<RealtimeSessionHandle, AiError.AiError, Scope.Scope>\n}\n\nexport class GeminiLiveSession extends Context.Service<\n GeminiLiveSession,\n GeminiLiveSessionService\n>()(\"@betalyra/effect-uai/providers/google/GeminiLiveSession\") {}\n\nexport const make = (cfg: Config): GeminiLiveSessionService => ({ open: openSession(cfg) })\n\nexport const layer = (\n cfg: Config,\n): Layer.Layer<GeminiLiveSession | RealtimeSession | RealtimeVideoInput> =>\n Layer.mergeAll(\n Layer.succeed(GeminiLiveSession, make(cfg)),\n Layer.succeed(RealtimeSession, {\n open: (request: CommonSessionRequest) => make(cfg).open(request as GeminiLiveRequest),\n } satisfies RealtimeSessionService),\n Layer.succeed(RealtimeVideoInput, undefined),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA4BA,IAAa,oBAAb,cAAuC,QAAQ,QAG7C,CAAC,CAAC,yDAAyD,CAAC,CAAC,CAAC;AAEhE,MAAa,QAAQ,SAA2C,EAAE,MAAM,YAAY,GAAG,EAAE;AAEzF,MAAa,SACX,QAEA,MAAM,SACJ,MAAM,QAAQ,mBAAmB,KAAK,GAAG,CAAC,GAC1C,MAAM,QAAQ,iBAAiB,EAC7B,OAAO,YAAkC,KAAK,GAAG,CAAC,CAAC,KAAK,OAA4B,EACtF,CAAkC,GAClC,MAAM,QAAQ,oBAAoB,KAAA,CAAS,CAC7C"}
@@ -1,4 +1,4 @@
1
- import { GeminiTtsModel, GeminiVoiceName } from "./models.mjs";
1
+ import { i as GeminiVoiceName, r as GeminiTtsModel } from "./models-BVSKJwCx.mjs";
2
2
  import { Context, Effect, Layer, Redacted, Result, Stream } from "effect";
3
3
  import { HttpClient } from "effect/unstable/http";
4
4
  import * as AiError from "@effect-uai/core/AiError";
@@ -1,4 +1,4 @@
1
- import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
1
+ import { n as globalExpect, o as describe, s as it } from "./test.DNmyFkvJ-BPz3G1HS.mjs";
2
2
  import { realizeOutput } from "./GeminiSynthesizer.mjs";
3
3
  import { Result } from "effect";
4
4
  //#region src/GeminiSynthesizer.test.ts
@@ -1,4 +1,4 @@
1
- import { GoogleDeepResearchModel } from "./models.mjs";
1
+ import { a as GoogleDeepResearchModel } from "./models-BVSKJwCx.mjs";
2
2
  import { Context, Effect, Layer, Option, Redacted, Schema } from "effect";
3
3
  import { HttpClient } from "effect/unstable/http";
4
4
  import { DeepResearch, DeepResearchServiceShape } from "@effect-uai/core/DeepResearch";
@@ -1,4 +1,4 @@
1
- import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
1
+ import { n as globalExpect, o as describe, s as it } from "./test.DNmyFkvJ-BPz3G1HS.mjs";
2
2
  import { WireInteraction, jobStateOf } from "./GoogleDeepResearch.mjs";
3
3
  import { Schema } from "effect";
4
4
  import * as Turn$1 from "@effect-uai/core/Turn";
@@ -1,4 +1,4 @@
1
- import { LyriaModel } from "./models.mjs";
1
+ import { c as LyriaModel } from "./models-BVSKJwCx.mjs";
2
2
  import { Context, Effect, Layer, Redacted, Stream } from "effect";
3
3
  import { HttpClient } from "effect/unstable/http";
4
4
  import * as AiError from "@effect-uai/core/AiError";
@@ -1,5 +1,5 @@
1
1
  import { t as __commonJSMin } from "./rolldown-runtime-EAsCxrDo.mjs";
2
- import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
2
+ import { n as globalExpect, o as describe, s as it } from "./test.DNmyFkvJ-BPz3G1HS.mjs";
3
3
  import { LyriaGenerator, containerToMimeType, layer, realizedFormat } from "./LyriaGenerator.mjs";
4
4
  import { Duration, Effect, Layer, Redacted, Stream } from "effect";
5
5
  import { FetchHttpClient } from "effect/unstable/http";
package/dist/codec.d.mts CHANGED
@@ -6,7 +6,7 @@ import { Turn } from "@effect-uai/core/Turn";
6
6
  import { ImageSource } from "@effect-uai/core/Image";
7
7
  import { Annotation, HistoryItem } from "@effect-uai/core/Items";
8
8
  declare namespace codec_d_exports {
9
- export { AccumulatedFunctionCall, Accumulator, ChunkPart, ChunkResult, GenerationConfig, GroundingMetadata, ImageConfig, RequestBody, RequestTool, ThinkingConfig, WireChunk, accumulatorToTurn, buildRequestBody, emptyAccumulator, groundingToAnnotations, hasUrlImageSource, httpStatusError, ingestChunk, transportFailure };
9
+ export { AccumulatedFunctionCall, Accumulator, ChunkPart, ChunkResult, GenerationConfig, GroundingMetadata, ImageConfig, RequestBody, RequestTool, ThinkingConfig, WireChunk, accumulatorToTurn, buildRequestBody, emptyAccumulator, groundingToAnnotations, hasUrlImageSource, httpStatusError, ingestChunk, parsedResponse, toolDescriptorsToTools, transportFailure };
10
10
  }
11
11
  declare const GroundingMetadata: Schema.Struct<{
12
12
  readonly groundingChunks: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
@@ -148,6 +148,9 @@ type RequestBody = {
148
148
  readonly tools?: ReadonlyArray<RequestTool>;
149
149
  readonly toolConfig?: RequestToolConfig;
150
150
  };
151
+ /** A `ToolCallOutput.output` string as the object `functionResponse` wants. */
152
+ declare const parsedResponse: (encoded: string) => Record<string, unknown>;
153
+ declare const toolDescriptorsToTools: (tools: ReadonlyArray<ToolDescriptor>) => ReadonlyArray<RequestTool>;
151
154
  /** `provider` names the surface that failed, so errors read `gemini` or `lyria`. */
152
155
  declare const transportFailure: (provider: string) => (cause: unknown) => AiError.AiError;
153
156
  /** 413 is the inline-payload cap, which is a context limit on every surface. */
@@ -222,5 +225,5 @@ type ChunkResult = {
222
225
  declare const ingestChunk: (acc: Accumulator, chunk: WireChunk) => ChunkResult;
223
226
  declare const accumulatorToTurn: (acc: Accumulator) => Turn;
224
227
  //#endregion
225
- export { AccumulatedFunctionCall, Accumulator, ChunkPart, ChunkResult, GenerationConfig, GroundingMetadata, ImageConfig, RequestBody, RequestTool, ThinkingConfig, WireChunk, accumulatorToTurn, buildRequestBody, emptyAccumulator, groundingToAnnotations, hasUrlImageSource, httpStatusError, ingestChunk, codec_d_exports as t, transportFailure };
228
+ export { AccumulatedFunctionCall, Accumulator, ChunkPart, ChunkResult, GenerationConfig, GroundingMetadata, ImageConfig, RequestBody, RequestTool, ThinkingConfig, WireChunk, accumulatorToTurn, buildRequestBody, emptyAccumulator, groundingToAnnotations, hasUrlImageSource, httpStatusError, ingestChunk, parsedResponse, codec_d_exports as t, toolDescriptorsToTools, transportFailure };
226
229
  //# sourceMappingURL=codec.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;;;;;cAsFa,mBAAiB,OAAA;;;;;;;;KAGlB,2BAA2B,kBAAkB;cAU5C,4BAAsB,OACvB,cAAc,0CACvB,cAAc;cA+BJ,WAAS,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAIV,mBAAmB,UAAU;KAMpC;WACU;;WACA;aAAuB;aAA2B;;;WAElD;aACE;aACA;aACA;;WAEF;;WAGA;aACE;aACA;aACA,UAAU;;;KAItB;WACM;WACA,OAAO,cAAc;;KAG3B;WACM,OAAO;aAAyB;;;;;;;;;;KAU/B;WAEG,sBAAsB;aACpB;aACA;aACA,YAAY;;;WAGd;;WACA;;WACA;;KAEV;WACM;aACE;aACA,uBAAuB;;;KAIxB;WACD;;KAGC;WACD;WACA;;KAGC;WACD;WACA;WACA;WACA,iBAAiB;;WAEjB,qBAAqB;WACrB,cAAc;;WAEd;;WAEA,qBAAqB;;KAGpB;WACD,UAAU,cAAc;WACxB,oBAAoB;WACpB,mBAAmB;WACnB,QAAQ,cAAc;WACtB,aAAa;;;cAiRX,mBAAgB,sBACV,mBACC,QAAQ;;cAIf,kBAAe,sBACT,gBACF,iBAAiB,QAAQ;;;;;;;cAkC7B,oBAAiB,SAAa,cAAc;cAO5C,mBAAgB,SAClB,cAAc,cAAY,kBACjB,OAAO,OAAO,mBAAiB,QAC1C,cAAc,iBAAe,sBACf,cAAc,cAAY,aACnC,gCACX;KAoCS;;WAED;WACA;;WAEA,YAAY,OAAO;;WAEnB,WAAW,OAAO;;WAElB;;KAGC;WACD;WACA;;WAEA,QAAQ,cAAc;WACtB,eAAe,cAAc;;WAE7B,aAAa,cAAc;WAC3B,cAAc,OAAO;WACrB;aACE;aACA;aACA;aACA;eAAkC;;aAClC;eAAmC;;;;cAInC,kBAAkB;;;;;;KAenB;WACG;WAAuB;;WACvB;WAA4B;;WAC5B;WAAwB,QAAQ;;WAEhC;WACA,IAAI,OAAO;WACX,WAAW,OAAO;WAClB;WACA;;KAGH;WACD,aAAa;WACb,OAAO,cAAc;WACrB;;cA6HE,cAAW,KAAS,aAAW,OAAS,cAAY;cA8DpD,oBAAiB,KAAS,gBAAc"}
1
+ {"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;;;;;cAsFa,mBAAiB,OAAA;;;;;;;;KAGlB,2BAA2B,kBAAkB;cAU5C,4BAAsB,OACvB,cAAc,0CACvB,cAAc;cA+BJ,WAAS,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAIV,mBAAmB,UAAU;KAMpC;WACU;;WACA;aAAuB;aAA2B;;;WAElD;aACE;aACA;aACA;;WAEF;;WAGA;aACE;aACA;aACA,UAAU;;;KAItB;WACM;WACA,OAAO,cAAc;;KAG3B;WACM,OAAO;aAAyB;;;;;;;;;;KAU/B;WAEG,sBAAsB;aACpB;aACA;aACA,YAAY;;;WAGd;;WACA;;WACA;;KAEV;WACM;aACE;aACA,uBAAuB;;;KAIxB;WACD;;KAGC;WACD;WACA;;KAGC;WACD;WACA;WACA;WACA,iBAAiB;;WAEjB,qBAAqB;WACrB,cAAc;;WAEd;;WAEA,qBAAqB;;KAGpB;WACD,UAAU,cAAc;WACxB,oBAAoB;WACpB,mBAAmB;WACnB,QAAQ,cAAc;WACtB,aAAa;;;cAgGX,iBAAc,oBAXN;cA0JR,yBAAsB,OAC1B,cAAc,oBACpB,cAAc;;cAiCJ,mBAAgB,sBACV,mBACC,QAAQ;;cAIf,kBAAe,sBACT,gBACF,iBAAiB,QAAQ;;;;;;;cAkC7B,oBAAiB,SAAa,cAAc;cAO5C,mBAAgB,SAClB,cAAc,cAAY,kBACjB,OAAO,OAAO,mBAAiB,QAC1C,cAAc,iBAAe,sBACf,cAAc,cAAY,aACnC,gCACX;KAoCS;;WAED;WACA;;WAEA,YAAY,OAAO;;WAEnB,WAAW,OAAO;;WAElB;;KAGC;WACD;WACA;;WAEA,QAAQ,cAAc;WACtB,eAAe,cAAc;;WAE7B,aAAa,cAAc;WAC3B,cAAc,OAAO;WACrB;aACE;aACA;aACA;aACA;eAAkC;;aAClC;eAAmC;;;;cAInC,kBAAkB;;;;;;KAenB;WACG;WAAuB;;WACvB;WAA4B;;WAC5B;WAAwB,QAAQ;;WAEhC;WACA,IAAI,OAAO;WACX,WAAW,OAAO;WAClB;WACA;;KAGH;WACD,aAAa;WACb,OAAO,cAAc;WACrB;;cA6HE,cAAW,KAAS,aAAW,OAAS,cAAY;cA8DpD,oBAAiB,KAAS,gBAAc"}
package/dist/codec.mjs CHANGED
@@ -13,6 +13,8 @@ var codec_exports = /* @__PURE__ */ __exportAll({
13
13
  hasUrlImageSource: () => hasUrlImageSource,
14
14
  httpStatusError: () => httpStatusError,
15
15
  ingestChunk: () => ingestChunk,
16
+ parsedResponse: () => parsedResponse,
17
+ toolDescriptorsToTools: () => toolDescriptorsToTools,
16
18
  transportFailure: () => transportFailure
17
19
  });
18
20
  const TextPart = Schema.Struct({
@@ -140,6 +142,7 @@ const parsedJsonObject = (fallback) => (encoded) => pipe(decodeJsonObject(encode
140
142
  onFailure: () => fallback(encoded)
141
143
  }));
142
144
  const parsedArgs = parsedJsonObject(() => ({}));
145
+ /** A `ToolCallOutput.output` string as the object `functionResponse` wants. */
143
146
  const parsedResponse = parsedJsonObject((raw) => ({ output: raw }));
144
147
  /**
145
148
  * `ToolCallOutput` only carries `call_id`; Gemini's `functionResponse`
@@ -408,6 +411,6 @@ const accumulatorToTurn = (acc) => ({
408
411
  ]
409
412
  });
410
413
  //#endregion
411
- export { GroundingMetadata, WireChunk, accumulatorToTurn, buildRequestBody, emptyAccumulator, groundingToAnnotations, hasUrlImageSource, httpStatusError, ingestChunk, codec_exports as t, transportFailure };
414
+ export { GroundingMetadata, WireChunk, accumulatorToTurn, buildRequestBody, emptyAccumulator, groundingToAnnotations, hasUrlImageSource, httpStatusError, ingestChunk, parsedResponse, codec_exports as t, toolDescriptorsToTools, transportFailure };
412
415
 
413
416
  //# sourceMappingURL=codec.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Encoding, Match, Option, Result, Schema, pipe } from \"effect\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { ImageSource } from \"@effect-uai/core/Image\"\nimport { imageBase64 } from \"@effect-uai/core/Image\"\nimport type {\n ContentBlock,\n ToolCall,\n InputImage,\n HistoryItem,\n Message,\n Annotation,\n} from \"@effect-uai/core/Items\"\nimport type { ToolDescriptor } from \"@effect-uai/core/Tool\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\nimport type { CommonRequest } from \"@effect-uai/core/LanguageModel\"\n\n// ---------------------------------------------------------------------------\n// Wire schemas - the subset of Gemini's streamGenerateContent payload we\n// consume. Reference: https://ai.google.dev/api/generate-content\n// ---------------------------------------------------------------------------\n\nconst TextPart = Schema.Struct({\n text: Schema.String,\n /**\n * Gemini's flag for chain-of-thought parts. When `true`, the part is\n * reasoning trace, not the model's user-facing answer. Maps onto\n * `reasoning_delta { kind: \"trace\" }` in the canonical view.\n */\n thought: Schema.optional(Schema.Boolean),\n})\n\n/**\n * Gemini function-call part. Args are delivered as a whole JSON *object*\n * in a single chunk (Gemini does not stream tool-call args). On Gemini 3\n * the response also carries an `id` per call, which we echo back on the\n * corresponding `functionResponse`.\n */\nconst FunctionCallPart = Schema.Struct({\n functionCall: Schema.Struct({\n id: Schema.optional(Schema.String),\n name: Schema.String,\n args: Schema.optional(Schema.Unknown),\n }),\n // Gemini 3 thinking models attach an opaque signature to each function-call\n // part that must be replayed on the next request, or the call is rejected.\n thoughtSignature: Schema.optional(Schema.String),\n})\n\n/**\n * An image the model drew, as a part of its own turn. Only image models\n * emit these, and only when the request asked for the IMAGE modality.\n * `thought: true` marks an interim image drawn while planning, which is\n * not the answer and is dropped the same way thought text is.\n */\nconst InlineDataPart = Schema.Struct({\n inlineData: Schema.Struct({\n mimeType: Schema.optional(Schema.String),\n data: Schema.String,\n }),\n thought: Schema.optional(Schema.Boolean),\n})\n\nconst Part = Schema.Union([TextPart, FunctionCallPart, InlineDataPart])\ntype WireFunctionCallPart = typeof FunctionCallPart.Type\ntype WireInlineDataPart = typeof InlineDataPart.Type\n\nconst Content = Schema.Struct({\n role: Schema.optional(Schema.String),\n parts: Schema.optional(Schema.Array(Part)),\n})\n\n// Google grounding metadata. On a grounded response the candidate carries\n// `groundingChunks[].web.{uri,title}` - the structured form of the inline `[n]`\n// / trailing `**Sources:**` list the model renders into the answer text.\n// Decoded defensively (every field optional) as it is absent on ungrounded\n// turns. Shared with the Interactions deep-research surface via `codec.ts`.\nconst GroundingChunk = Schema.Struct({\n web: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n uri: Schema.optional(Schema.NullOr(Schema.String)),\n title: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n})\nexport const GroundingMetadata = Schema.Struct({\n groundingChunks: Schema.optional(Schema.NullOr(Schema.Array(GroundingChunk))),\n})\nexport type GroundingMetadata = typeof GroundingMetadata.Type\n\n// Two citations are the same source when they point at the same url.\nconst sameUrlCitation = (a: Annotation, b: Annotation): boolean =>\n a.type === \"url_citation\" && b.type === \"url_citation\" && a.url === b.url\n\n// Grounding chunks → `url_citation` annotations, de-duped by url across every\n// metadata block passed. `web.uri` is a `grounding-api-redirect` URL and\n// `web.title` the bare domain; both are kept as-is (the redirect resolves to\n// the real page).\nexport const groundingToAnnotations = (\n ...metas: ReadonlyArray<GroundingMetadata | null | undefined>\n): ReadonlyArray<Annotation> =>\n pipe(\n metas,\n Arr.flatMap((meta) => meta?.groundingChunks ?? []),\n Arr.filterMap((chunk) =>\n chunk.web?.uri == null\n ? Result.failVoid\n : Result.succeed<Annotation>({\n type: \"url_citation\",\n url: chunk.web.uri,\n title: chunk.web.title ?? chunk.web.uri,\n }),\n ),\n Arr.dedupeWith(sameUrlCitation),\n )\n\nconst Candidate = Schema.Struct({\n content: Schema.optional(Content),\n groundingMetadata: Schema.optional(GroundingMetadata),\n finishReason: Schema.optional(Schema.String),\n index: Schema.optional(Schema.Number),\n})\n\nconst UsageMetadata = Schema.Struct({\n promptTokenCount: Schema.optional(Schema.Number),\n candidatesTokenCount: Schema.optional(Schema.Number),\n totalTokenCount: Schema.optional(Schema.Number),\n cachedContentTokenCount: Schema.optional(Schema.Number),\n thoughtsTokenCount: Schema.optional(Schema.Number),\n})\n\nexport const WireChunk = Schema.Struct({\n candidates: Schema.optional(Schema.Array(Candidate)),\n usageMetadata: Schema.optional(UsageMetadata),\n})\nexport type WireChunk = typeof WireChunk.Type\n\n// ---------------------------------------------------------------------------\n// History → request body\n// ---------------------------------------------------------------------------\n\ntype RequestPart =\n | { readonly text: string }\n | { readonly inlineData: { readonly mimeType: string; readonly data: string } }\n | {\n readonly functionCall: {\n readonly id?: string\n readonly name: string\n readonly args: unknown\n }\n readonly thoughtSignature?: string\n }\n | {\n readonly functionResponse: {\n readonly id?: string\n readonly name: string\n readonly response: Record<string, unknown>\n }\n }\n\ntype RequestContent = {\n readonly role: \"user\" | \"model\"\n readonly parts: ReadonlyArray<RequestPart>\n}\n\ntype RequestSystemInstruction = {\n readonly parts: ReadonlyArray<{ readonly text: string }>\n}\n\n/**\n * Gemini's tool declaration. A `tools[]` entry is either a function-declaration\n * block (translated from `ToolDescriptor`s) or one of the native hosted-tool\n * entries. The native entries are empty objects keyed camelCase per the REST\n * v1beta `Tool` object, and sit as siblings of the declarations in the same\n * `tools` array.\n */\nexport type RequestTool =\n | {\n readonly functionDeclarations: ReadonlyArray<{\n readonly name: string\n readonly description: string\n readonly parameters: Record<string, unknown>\n }>\n }\n | { readonly googleSearch: {} }\n | { readonly urlContext: {} }\n | { readonly codeExecution: {} }\n\ntype RequestToolConfig = {\n readonly functionCallingConfig: {\n readonly mode: \"AUTO\" | \"ANY\" | \"NONE\"\n readonly allowedFunctionNames?: ReadonlyArray<string>\n }\n}\n\nexport type ThinkingConfig = {\n readonly thinkingBudget: number\n}\n\nexport type ImageConfig = {\n readonly aspectRatio?: string\n readonly imageSize?: string\n}\n\nexport type GenerationConfig = {\n readonly temperature?: number\n readonly maxOutputTokens?: number\n readonly topP?: number\n readonly thinkingConfig?: ThinkingConfig\n /** Image models need `[\"TEXT\", \"IMAGE\"]` or they answer in prose only. */\n readonly responseModalities?: ReadonlyArray<\"TEXT\" | \"IMAGE\">\n readonly imageConfig?: ImageConfig\n /** Set together with `responseJsonSchema` to constrain output to JSON. */\n readonly responseMimeType?: string\n /** JSON Schema constraint on the output. */\n readonly responseJsonSchema?: Record<string, unknown>\n}\n\nexport type RequestBody = {\n readonly contents: ReadonlyArray<RequestContent>\n readonly systemInstruction?: RequestSystemInstruction\n readonly generationConfig?: GenerationConfig\n readonly tools?: ReadonlyArray<RequestTool>\n readonly toolConfig?: RequestToolConfig\n}\n\nconst blockText = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => b.text,\n input_image: () => \"\",\n output_text: (b) => b.text,\n output_image: () => \"\",\n refusal: (b) => b.text,\n }),\n)\n\nconst messageText = (message: Message): string => message.content.map(blockText).join(\"\")\n\n/**\n * Gemini's `inlineData` form expects a base64 payload. URL-form images\n * would need to go through Gemini's Files API (upload then `fileData`\n * with the returned URI); pre-uploading isn't free, so we skip those for\n * now and document as a follow-up.\n */\nconst imageSourceToParts = Match.type<InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (): ReadonlyArray<RequestPart> => []),\n Match.tag(\"base64\", (s): ReadonlyArray<RequestPart> => [\n { inlineData: { mimeType: s.mimeType, data: s.base64 } },\n ]),\n Match.tag(\"bytes\", (s): ReadonlyArray<RequestPart> => [\n { inlineData: { mimeType: s.mimeType, data: Encoding.encodeBase64(s.bytes) } },\n ]),\n Match.exhaustive,\n)\n\nconst blockToParts = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b): ReadonlyArray<RequestPart> => (b.text.length === 0 ? [] : [{ text: b.text }]),\n input_image: (b): ReadonlyArray<RequestPart> => imageSourceToParts(b.source),\n output_text: (b): ReadonlyArray<RequestPart> => (b.text.length === 0 ? [] : [{ text: b.text }]),\n // An image the model drew goes back as a model-role `inlineData` part,\n // which is how a multi-turn edit keeps its subject.\n output_image: (b): ReadonlyArray<RequestPart> => imageSourceToParts(b.source),\n // Refusals are assistant-side content; they don't round-trip into Gemini's\n // request body as parts. Skip.\n refusal: (): ReadonlyArray<RequestPart> => [],\n }),\n)\n\nconst messageToContent = (message: Message): Result.Result<RequestContent, void> => {\n const parts = pipe(message.content, Arr.flatMap(blockToParts))\n if (parts.length === 0) return Result.failVoid\n return Match.value(message.role).pipe(\n Match.when(\"user\", () => Result.succeed({ role: \"user\" as const, parts })),\n Match.when(\"assistant\", () => Result.succeed({ role: \"model\" as const, parts })),\n Match.when(\"system\", () => Result.failVoid),\n Match.exhaustive,\n )\n}\n\nconst systemMessageText = (message: Message): Result.Result<string, void> => {\n if (message.role !== \"system\") return Result.failVoid\n const text = messageText(message)\n return text.length === 0 ? Result.failVoid : Result.succeed(text)\n}\n\nconst allMessages = (history: ReadonlyArray<HistoryItem>): ReadonlyArray<Message> =>\n pipe(\n history,\n Arr.filterMap((item) => (item.type === \"message\" ? Result.succeed(item) : Result.failVoid)),\n )\n\n// ---------------------------------------------------------------------------\n// Function-call round-trip\n//\n// `ToolCall.arguments` and `ToolCallOutput.output` are JSON-encoded\n// strings; Gemini's `functionCall.args` and `functionResponse.response`\n// expect parsed JSON *objects*. Decode via Schema:\n// - object payload → use as-is\n// - scalar / array / malformed → wrap as `{ output: <raw string> }` so the\n// model still sees *some* response without crashing the request.\n// ---------------------------------------------------------------------------\n\nconst JsonObject = Schema.Record(Schema.String, Schema.Unknown)\nconst decodeJsonObject = Schema.decodeResult(Schema.fromJsonString(JsonObject))\n\nconst parsedJsonObject =\n (fallback: (raw: string) => Record<string, unknown>) =>\n (encoded: string): Record<string, unknown> =>\n pipe(\n decodeJsonObject(encoded),\n Result.match({\n onSuccess: (v) => v,\n onFailure: () => fallback(encoded),\n }),\n )\n\nconst parsedArgs = parsedJsonObject(() => ({}))\nconst parsedResponse = parsedJsonObject((raw) => ({ output: raw }))\n\n/**\n * `ToolCallOutput` only carries `call_id`; Gemini's `functionResponse`\n * requires the declared function `name`. Resolve the name by scanning prior\n * `function_call` items in the history for a matching `call_id`. If we\n * cannot resolve, fall back to `call_id` as the name - imperfect but\n * preserves stream shape so the model sees *some* response.\n */\nconst isFunctionCallItem = (item: HistoryItem): item is ToolCall => item.type === \"function_call\"\n\nconst nameForCallId = (\n history: ReadonlyArray<HistoryItem>,\n call_id: string,\n): Option.Option<string> =>\n pipe(\n history,\n Arr.findFirst((item): item is ToolCall => isFunctionCallItem(item) && item.call_id === call_id),\n Option.map((f) => f.name),\n )\n\n/**\n * Extract the Gemini-3 wire id and thought signature we stashed in\n * `providerData` on the way out. Schema-driven so the shape lives in one\n * place; failure or absence → `Option.none()`.\n */\nconst ProviderDataGemini = Schema.Struct({\n gemini: Schema.Struct({\n id: Schema.optional(Schema.String),\n thoughtSignature: Schema.optional(Schema.String),\n }),\n})\nconst decodeGemini = Schema.decodeUnknownResult(ProviderDataGemini)\n\nconst geminiField = (\n item: ToolCall,\n pick: (g: {\n readonly id?: string | undefined\n readonly thoughtSignature?: string | undefined\n }) => string | undefined,\n): Option.Option<string> =>\n pipe(\n decodeGemini(item.providerData),\n Result.match({\n onSuccess: (d) => Option.fromNullishOr(pick(d.gemini)),\n onFailure: () => Option.none<string>(),\n }),\n )\n\nconst providerIdFor = (item: ToolCall): Option.Option<string> => geminiField(item, (g) => g.id)\n\n// The originating call's Gemini id, looked up from history by our `call_id`.\n// Gemini 3 maps a `functionResponse` back to its call by this id, so parallel\n// calls to the same function are mis-paired if we drop it.\nconst providerIdForCallId = (\n history: ReadonlyArray<HistoryItem>,\n call_id: string,\n): Option.Option<string> =>\n pipe(\n history,\n Arr.findFirst((item): item is ToolCall => isFunctionCallItem(item) && item.call_id === call_id),\n Option.flatMap(providerIdFor),\n )\n\nconst signatureFor = (item: ToolCall): Option.Option<string> =>\n geminiField(item, (g) => g.thoughtSignature)\n\nconst itemToContent =\n (history: ReadonlyArray<HistoryItem>) =>\n (item: HistoryItem): Result.Result<RequestContent, void> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: messageToContent,\n function_call: (f) =>\n Result.succeed({\n role: \"model\" as const,\n parts: [\n {\n ...Option.match(signatureFor(f), {\n onSome: (thoughtSignature) => ({ thoughtSignature }),\n onNone: () => ({}),\n }),\n functionCall: {\n ...Option.match(providerIdFor(f), {\n onSome: (id) => ({ id }),\n onNone: () => ({}),\n }),\n name: f.name,\n args: parsedArgs(f.arguments),\n },\n },\n ],\n }),\n function_call_output: (o) =>\n Result.succeed({\n role: \"user\" as const,\n parts: [\n {\n functionResponse: {\n ...Option.match(providerIdForCallId(history, o.call_id), {\n onSome: (id) => ({ id }),\n onNone: () => ({}),\n }),\n name: Option.getOrElse(nameForCallId(history, o.call_id), () => o.call_id),\n response: parsedResponse(o.output),\n },\n },\n ],\n }),\n reasoning: () => Result.failVoid,\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Tool descriptors → Gemini `functionDeclarations`. Gemini accepts only a\n// strict OpenAPI 3.0 subset for `parameters`; strip JSON-Schema keys it\n// rejects (`$schema`, `$ref`, `additionalProperties`, `oneOf`,\n// `definitions`).\n// ---------------------------------------------------------------------------\n\nconst UNSUPPORTED_SCHEMA_KEYS: ReadonlySet<string> = new Set([\n \"$schema\",\n \"$ref\",\n \"$defs\",\n \"definitions\",\n \"additionalProperties\",\n \"oneOf\",\n])\n\nconst sanitizeSchema = (schema: unknown): unknown => {\n if (Array.isArray(schema)) return schema.map(sanitizeSchema)\n if (schema === null || typeof schema !== \"object\") return schema\n return pipe(\n Object.entries(schema as Record<string, unknown>),\n Arr.filterMap(([k, v]) =>\n UNSUPPORTED_SCHEMA_KEYS.has(k)\n ? Result.failVoid\n : Result.succeed([k, sanitizeSchema(v)] as const),\n ),\n Object.fromEntries,\n )\n}\n\nconst toolDescriptorsToTools = (\n tools: ReadonlyArray<ToolDescriptor>,\n): ReadonlyArray<RequestTool> =>\n tools.length === 0\n ? []\n : [\n {\n functionDeclarations: tools.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: sanitizeSchema(t.inputSchema) as Record<string, unknown>,\n })),\n },\n ]\n\n/**\n * Map our cross-provider `toolChoice` onto Gemini's `functionCallingConfig`.\n * Gemini honours the same three modes plus a function allow-list, so the\n * choice is mapped, not dropped. `undefined` keeps the API default (`AUTO`).\n */\nconst toolChoiceToFunctionCallingConfig = (\n toolChoice: CommonRequest[\"toolChoice\"],\n): RequestToolConfig[\"functionCallingConfig\"] =>\n Match.value(toolChoice ?? \"auto\").pipe(\n Match.when(\"auto\", () => ({ mode: \"AUTO\" as const })),\n Match.when(\"required\", () => ({ mode: \"ANY\" as const })),\n Match.when(\"none\", () => ({ mode: \"NONE\" as const })),\n Match.orElse((tc) => ({ mode: \"ANY\" as const, allowedFunctionNames: [tc.name] })),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP failure mapping, shared by every Google surface\n// ---------------------------------------------------------------------------\n\n/** `provider` names the surface that failed, so errors read `gemini` or `lyria`. */\nexport const transportFailure =\n (provider: string) =>\n (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider, raw: cause })\n\n/** 413 is the inline-payload cap, which is a context limit on every surface. */\nexport const httpStatusError =\n (provider: string) =>\n (status: number, body: string): AiError.AiError =>\n Match.value(status).pipe(\n Match.when(429, (): AiError.AiError => new AiError.RateLimited({ provider, raw: body })),\n Match.whenOr(408, 504, (): AiError.AiError => new AiError.Timeout({ provider, raw: body })),\n Match.when(\n 401,\n (): AiError.AiError => new AiError.AuthFailed({ provider, subtype: \"auth\", raw: body }),\n ),\n Match.when(\n 403,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider, subtype: \"permission\", raw: body }),\n ),\n Match.when(\n 402,\n (): AiError.AiError => new AiError.AuthFailed({ provider, subtype: \"billing\", raw: body }),\n ),\n Match.when(\n 413,\n (): AiError.AiError => new AiError.ContextLengthExceeded({ provider, raw: body }),\n ),\n Match.when(\n (n) => n >= 500,\n (n): AiError.AiError => new AiError.Unavailable({ provider, status: n, raw: body }),\n ),\n Match.orElse((): AiError.AiError => new AiError.InvalidRequest({ provider, raw: body })),\n )\n\n/**\n * URL-form images need Gemini's Files API (upload, then `fileData`); we\n * don't pre-upload, so such a part would otherwise vanish from the request.\n * The caller fails the request with `Unsupported` instead of silently\n * dropping the image.\n */\nexport const hasUrlImageSource = (history: ReadonlyArray<HistoryItem>): boolean =>\n history.some(\n (item) =>\n item.type === \"message\" &&\n item.content.some((b) => b.type === \"input_image\" && b.source._tag === \"url\"),\n )\n\nexport const buildRequestBody = (\n history: ReadonlyArray<HistoryItem>,\n generationConfig: Option.Option<GenerationConfig>,\n tools: ReadonlyArray<ToolDescriptor> = [],\n providerToolEntries: ReadonlyArray<RequestTool> = [],\n toolChoice: CommonRequest[\"toolChoice\"] = undefined,\n): RequestBody => {\n const systemTexts = pipe(allMessages(history), Arr.filterMap(systemMessageText))\n const contents = pipe(history, Arr.filterMap(itemToContent(history)))\n const functionTools = toolDescriptorsToTools(tools)\n const requestTools = [...functionTools, ...providerToolEntries]\n return {\n contents,\n ...(systemTexts.length > 0 && {\n systemInstruction: { parts: [{ text: systemTexts.join(\"\\n\") }] },\n }),\n ...Option.match(generationConfig, {\n onNone: () => ({}),\n onSome: (cfg) => ({ generationConfig: cfg }),\n }),\n ...(requestTools.length > 0 && { tools: requestTools }),\n // Only force a function-calling mode when there are function declarations;\n // a grounding-only request must keep the API default.\n ...(functionTools.length > 0 && {\n toolConfig: { functionCallingConfig: toolChoiceToFunctionCallingConfig(toolChoice) },\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stream-level state - accumulate chunk text + final usage/finish.\n//\n// `Accumulator` is immutable; `ingestChunk` returns a fresh one per chunk.\n// Drive it via `Stream.mapAccum` in the consumer.\n// ---------------------------------------------------------------------------\n\nconst finishReasonToStop = (reason: Option.Option<string>): Turn[\"stop_reason\"] =>\n Option.match(reason, {\n onNone: () => \"stop\" as const,\n onSome: (r) => (r === \"MAX_TOKENS\" ? (\"max_tokens\" as const) : (\"stop\" as const)),\n })\n\nexport type AccumulatedFunctionCall = {\n /** Synthesized id-or-name we surface as `call_id` on the canonical item. */\n readonly callId: string\n readonly name: string\n /** Wire id from Gemini 3, when present - echoed back on `functionResponse`. */\n readonly providerId: Option.Option<string>\n /** Thought signature from Gemini 3, replayed on the next request's part. */\n readonly signature: Option.Option<string>\n /** Args as JSON-encoded string, mirroring `Items.ToolCall.arguments`. */\n readonly arguments: string\n}\n\nexport type Accumulator = {\n readonly text: string\n readonly reasoning: string\n /** Images the model drew, in emission order. Image models only. */\n readonly images: ReadonlyArray<ImageSource>\n readonly functionCalls: ReadonlyArray<AccumulatedFunctionCall>\n /** Grounding citations, accumulated (de-duped by url) across chunks. */\n readonly annotations: ReadonlyArray<Annotation>\n readonly finishReason: Option.Option<string>\n readonly usage: {\n readonly input_tokens?: number\n readonly output_tokens?: number\n readonly total_tokens?: number\n readonly input_tokens_details?: { readonly cached_tokens?: number }\n readonly output_tokens_details?: { readonly reasoning_tokens?: number }\n }\n}\n\nexport const emptyAccumulator: Accumulator = {\n text: \"\",\n reasoning: \"\",\n images: [],\n functionCalls: [],\n annotations: [],\n finishReason: Option.none(),\n usage: {},\n}\n\n/**\n * One part's worth of streamable output. `text` and `reasoning` are\n * incremental string deltas; `function_call` arrives whole-in-one-chunk\n * (Gemini does not stream tool-call args).\n */\nexport type ChunkPart =\n | { readonly kind: \"text\"; readonly text: string }\n | { readonly kind: \"reasoning\"; readonly text: string }\n | { readonly kind: \"image\"; readonly source: ImageSource }\n | {\n readonly kind: \"function_call\"\n readonly id: Option.Option<string>\n readonly signature: Option.Option<string>\n readonly name: string\n readonly args: unknown\n }\n\nexport type ChunkResult = {\n readonly accumulator: Accumulator\n readonly parts: ReadonlyArray<ChunkPart>\n readonly finished: boolean\n}\n\nconst textToChunkParts = (p: typeof TextPart.Type): ReadonlyArray<ChunkPart> =>\n p.text.length === 0 ? [] : [{ kind: p.thought === true ? \"reasoning\" : \"text\", text: p.text }]\n\n/** Interim images drawn while planning are dropped, as thought text is. */\nconst inlineDataToChunkParts = (p: WireInlineDataPart): ReadonlyArray<ChunkPart> =>\n p.thought === true\n ? []\n : [\n {\n kind: \"image\",\n source: imageBase64(p.inlineData.data, p.inlineData.mimeType ?? \"image/png\"),\n },\n ]\n\nconst functionCallToChunkParts = (p: WireFunctionCallPart): ReadonlyArray<ChunkPart> => [\n {\n kind: \"function_call\",\n id: Option.fromNullishOr(p.functionCall.id),\n signature: Option.fromNullishOr(p.thoughtSignature),\n name: p.functionCall.name,\n args: p.functionCall.args ?? {},\n },\n]\n\n/** Parts are distinguished by which key they carry, not by a discriminator. */\nconst partToChunkParts: (p: typeof Part.Type) => ReadonlyArray<ChunkPart> = Match.type<\n typeof Part.Type\n>().pipe(\n Match.when({ text: Match.string }, textToChunkParts),\n Match.when({ inlineData: Match.defined }, inlineDataToChunkParts),\n Match.when({ functionCall: Match.defined }, functionCallToChunkParts),\n Match.exhaustive,\n)\n\nconst chunkParts = (chunk: WireChunk): ReadonlyArray<ChunkPart> =>\n pipe(chunk.candidates?.[0]?.content?.parts ?? [], Arr.flatMap(partToChunkParts))\n\nconst sumStrings = (parts: ReadonlyArray<ChunkPart>, kind: \"text\" | \"reasoning\"): string =>\n pipe(\n parts,\n Arr.filterMap((p) => (p.kind === kind ? Result.succeed(p.text) : Result.failVoid)),\n ).join(\"\")\n\nconst collectImages = (parts: ReadonlyArray<ChunkPart>): ReadonlyArray<ImageSource> =>\n pipe(\n parts,\n Arr.filterMap((p) => (p.kind === \"image\" ? Result.succeed(p.source) : Result.failVoid)),\n )\n\nconst collectFunctionCalls = (\n parts: ReadonlyArray<ChunkPart>,\n): ReadonlyArray<Extract<ChunkPart, { kind: \"function_call\" }>> =>\n pipe(\n parts,\n Arr.filterMap((p) => (p.kind === \"function_call\" ? Result.succeed(p) : Result.failVoid)),\n )\n\nconst mergeUsage = (\n prev: Accumulator[\"usage\"],\n next: WireChunk[\"usageMetadata\"],\n): Accumulator[\"usage\"] =>\n next === undefined\n ? prev\n : {\n ...prev,\n ...(next.promptTokenCount !== undefined && { input_tokens: next.promptTokenCount }),\n ...(next.candidatesTokenCount !== undefined && {\n output_tokens: next.candidatesTokenCount,\n }),\n ...(next.totalTokenCount !== undefined && { total_tokens: next.totalTokenCount }),\n ...(next.cachedContentTokenCount !== undefined && {\n input_tokens_details: { cached_tokens: next.cachedContentTokenCount },\n }),\n ...(next.thoughtsTokenCount !== undefined && {\n output_tokens_details: { reasoning_tokens: next.thoughtsTokenCount },\n }),\n }\n\n/**\n * Synthesize a stable `call_id` for a function call. Gemini 3 provides one\n * via `functionCall.id`; older models do not, so we fall back to\n * `<name>_<index>` based on prior calls' position in the accumulator.\n */\nconst synthesizeCallId = (\n call: Extract<ChunkPart, { kind: \"function_call\" }>,\n priorCalls: ReadonlyArray<AccumulatedFunctionCall>,\n): string =>\n Option.match(call.id, {\n onSome: (id) => id,\n onNone: () => `${call.name}_${priorCalls.length}`,\n })\n\nconst chunkCallToAccumulated = (\n prior: ReadonlyArray<AccumulatedFunctionCall>,\n call: Extract<ChunkPart, { kind: \"function_call\" }>,\n): AccumulatedFunctionCall => ({\n callId: synthesizeCallId(call, prior),\n name: call.name,\n providerId: call.id,\n signature: call.signature,\n // `functionCallToChunkParts` already replaced null/undefined with `{}`,\n // so `call.args` is always a JSON-encodable value here.\n arguments: JSON.stringify(call.args),\n})\n\nconst appendFunctionCalls = (\n prior: ReadonlyArray<AccumulatedFunctionCall>,\n fromChunk: ReadonlyArray<Extract<ChunkPart, { kind: \"function_call\" }>>,\n): ReadonlyArray<AccumulatedFunctionCall> =>\n fromChunk.reduce<ReadonlyArray<AccumulatedFunctionCall>>(\n (acc, call) => [...acc, chunkCallToAccumulated(acc, call)],\n prior,\n )\n\n// Grounding usually arrives on the final chunk, but merge across chunks and\n// de-dupe by url so a mid-stream metadata block is not lost or double-counted.\nconst mergeAnnotations = (\n prev: ReadonlyArray<Annotation>,\n next: ReadonlyArray<Annotation>,\n): ReadonlyArray<Annotation> =>\n next.length === 0 ? prev : Arr.dedupeWith(Arr.appendAll(prev, next), sameUrlCitation)\n\nexport const ingestChunk = (acc: Accumulator, chunk: WireChunk): ChunkResult => {\n const parts = chunkParts(chunk)\n const finishReason = Option.fromNullishOr(chunk.candidates?.[0]?.finishReason)\n return {\n parts,\n finished: Option.isSome(finishReason),\n accumulator: {\n text: acc.text + sumStrings(parts, \"text\"),\n reasoning: acc.reasoning + sumStrings(parts, \"reasoning\"),\n images: Arr.appendAll(acc.images, collectImages(parts)),\n functionCalls: appendFunctionCalls(acc.functionCalls, collectFunctionCalls(parts)),\n annotations: mergeAnnotations(\n acc.annotations,\n groundingToAnnotations(chunk.candidates?.[0]?.groundingMetadata),\n ),\n finishReason: Option.orElse(finishReason, () => acc.finishReason),\n usage: mergeUsage(acc.usage, chunk.usageMetadata),\n },\n }\n}\n\nconst reasoningItems = (acc: Accumulator): ReadonlyArray<HistoryItem> =>\n acc.reasoning.length > 0 ? [{ type: \"reasoning\", summary: acc.reasoning }] : []\n\n/**\n * Image models interleave text and image parts, and often answer with an\n * image and no prose at all, so the message exists when either is present.\n */\nconst assistantMessageItems = (acc: Accumulator): ReadonlyArray<HistoryItem> => {\n const content: ReadonlyArray<ContentBlock> = [\n ...(acc.text.length === 0\n ? []\n : [\n {\n type: \"output_text\" as const,\n text: acc.text,\n ...(acc.annotations.length > 0 && { annotations: acc.annotations }),\n },\n ]),\n ...acc.images.map((source) => ({ type: \"output_image\" as const, source })),\n ]\n return content.length === 0 ? [] : [{ type: \"message\", role: \"assistant\", content }]\n}\n\nconst functionCallItems = (acc: Accumulator): ReadonlyArray<HistoryItem> =>\n pipe(\n acc.functionCalls,\n Arr.map((c) => {\n const gemini = {\n ...(Option.isSome(c.providerId) && { id: c.providerId.value }),\n ...(Option.isSome(c.signature) && { thoughtSignature: c.signature.value }),\n }\n return {\n type: \"function_call\" as const,\n call_id: c.callId,\n name: c.name,\n arguments: c.arguments,\n ...(Object.keys(gemini).length > 0 && { providerData: { gemini } }),\n }\n }),\n )\n\nexport const accumulatorToTurn = (acc: Accumulator): Turn => ({\n stop_reason:\n acc.functionCalls.length > 0 ? (\"tool_calls\" as const) : finishReasonToStop(acc.finishReason),\n usage: { ...acc.usage },\n items: [...reasoningItems(acc), ...assistantMessageItems(acc), ...functionCallItems(acc)],\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Encoding, Match, Option, Result, Schema, pipe } from \"effect\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { ImageSource } from \"@effect-uai/core/Image\"\nimport { imageBase64 } from \"@effect-uai/core/Image\"\nimport type {\n ContentBlock,\n ToolCall,\n InputImage,\n HistoryItem,\n Message,\n Annotation,\n} from \"@effect-uai/core/Items\"\nimport type { ToolDescriptor } from \"@effect-uai/core/Tool\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\nimport type { CommonRequest } from \"@effect-uai/core/LanguageModel\"\n\n// ---------------------------------------------------------------------------\n// Wire schemas - the subset of Gemini's streamGenerateContent payload we\n// consume. Reference: https://ai.google.dev/api/generate-content\n// ---------------------------------------------------------------------------\n\nconst TextPart = Schema.Struct({\n text: Schema.String,\n /**\n * Gemini's flag for chain-of-thought parts. When `true`, the part is\n * reasoning trace, not the model's user-facing answer. Maps onto\n * `reasoning_delta { kind: \"trace\" }` in the canonical view.\n */\n thought: Schema.optional(Schema.Boolean),\n})\n\n/**\n * Gemini function-call part. Args are delivered as a whole JSON *object*\n * in a single chunk (Gemini does not stream tool-call args). On Gemini 3\n * the response also carries an `id` per call, which we echo back on the\n * corresponding `functionResponse`.\n */\nconst FunctionCallPart = Schema.Struct({\n functionCall: Schema.Struct({\n id: Schema.optional(Schema.String),\n name: Schema.String,\n args: Schema.optional(Schema.Unknown),\n }),\n // Gemini 3 thinking models attach an opaque signature to each function-call\n // part that must be replayed on the next request, or the call is rejected.\n thoughtSignature: Schema.optional(Schema.String),\n})\n\n/**\n * An image the model drew, as a part of its own turn. Only image models\n * emit these, and only when the request asked for the IMAGE modality.\n * `thought: true` marks an interim image drawn while planning, which is\n * not the answer and is dropped the same way thought text is.\n */\nconst InlineDataPart = Schema.Struct({\n inlineData: Schema.Struct({\n mimeType: Schema.optional(Schema.String),\n data: Schema.String,\n }),\n thought: Schema.optional(Schema.Boolean),\n})\n\nconst Part = Schema.Union([TextPart, FunctionCallPart, InlineDataPart])\ntype WireFunctionCallPart = typeof FunctionCallPart.Type\ntype WireInlineDataPart = typeof InlineDataPart.Type\n\nconst Content = Schema.Struct({\n role: Schema.optional(Schema.String),\n parts: Schema.optional(Schema.Array(Part)),\n})\n\n// Google grounding metadata. On a grounded response the candidate carries\n// `groundingChunks[].web.{uri,title}` - the structured form of the inline `[n]`\n// / trailing `**Sources:**` list the model renders into the answer text.\n// Decoded defensively (every field optional) as it is absent on ungrounded\n// turns. Shared with the Interactions deep-research surface via `codec.ts`.\nconst GroundingChunk = Schema.Struct({\n web: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n uri: Schema.optional(Schema.NullOr(Schema.String)),\n title: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n})\nexport const GroundingMetadata = Schema.Struct({\n groundingChunks: Schema.optional(Schema.NullOr(Schema.Array(GroundingChunk))),\n})\nexport type GroundingMetadata = typeof GroundingMetadata.Type\n\n// Two citations are the same source when they point at the same url.\nconst sameUrlCitation = (a: Annotation, b: Annotation): boolean =>\n a.type === \"url_citation\" && b.type === \"url_citation\" && a.url === b.url\n\n// Grounding chunks → `url_citation` annotations, de-duped by url across every\n// metadata block passed. `web.uri` is a `grounding-api-redirect` URL and\n// `web.title` the bare domain; both are kept as-is (the redirect resolves to\n// the real page).\nexport const groundingToAnnotations = (\n ...metas: ReadonlyArray<GroundingMetadata | null | undefined>\n): ReadonlyArray<Annotation> =>\n pipe(\n metas,\n Arr.flatMap((meta) => meta?.groundingChunks ?? []),\n Arr.filterMap((chunk) =>\n chunk.web?.uri == null\n ? Result.failVoid\n : Result.succeed<Annotation>({\n type: \"url_citation\",\n url: chunk.web.uri,\n title: chunk.web.title ?? chunk.web.uri,\n }),\n ),\n Arr.dedupeWith(sameUrlCitation),\n )\n\nconst Candidate = Schema.Struct({\n content: Schema.optional(Content),\n groundingMetadata: Schema.optional(GroundingMetadata),\n finishReason: Schema.optional(Schema.String),\n index: Schema.optional(Schema.Number),\n})\n\nconst UsageMetadata = Schema.Struct({\n promptTokenCount: Schema.optional(Schema.Number),\n candidatesTokenCount: Schema.optional(Schema.Number),\n totalTokenCount: Schema.optional(Schema.Number),\n cachedContentTokenCount: Schema.optional(Schema.Number),\n thoughtsTokenCount: Schema.optional(Schema.Number),\n})\n\nexport const WireChunk = Schema.Struct({\n candidates: Schema.optional(Schema.Array(Candidate)),\n usageMetadata: Schema.optional(UsageMetadata),\n})\nexport type WireChunk = typeof WireChunk.Type\n\n// ---------------------------------------------------------------------------\n// History → request body\n// ---------------------------------------------------------------------------\n\ntype RequestPart =\n | { readonly text: string }\n | { readonly inlineData: { readonly mimeType: string; readonly data: string } }\n | {\n readonly functionCall: {\n readonly id?: string\n readonly name: string\n readonly args: unknown\n }\n readonly thoughtSignature?: string\n }\n | {\n readonly functionResponse: {\n readonly id?: string\n readonly name: string\n readonly response: Record<string, unknown>\n }\n }\n\ntype RequestContent = {\n readonly role: \"user\" | \"model\"\n readonly parts: ReadonlyArray<RequestPart>\n}\n\ntype RequestSystemInstruction = {\n readonly parts: ReadonlyArray<{ readonly text: string }>\n}\n\n/**\n * Gemini's tool declaration. A `tools[]` entry is either a function-declaration\n * block (translated from `ToolDescriptor`s) or one of the native hosted-tool\n * entries. The native entries are empty objects keyed camelCase per the REST\n * v1beta `Tool` object, and sit as siblings of the declarations in the same\n * `tools` array.\n */\nexport type RequestTool =\n | {\n readonly functionDeclarations: ReadonlyArray<{\n readonly name: string\n readonly description: string\n readonly parameters: Record<string, unknown>\n }>\n }\n | { readonly googleSearch: {} }\n | { readonly urlContext: {} }\n | { readonly codeExecution: {} }\n\ntype RequestToolConfig = {\n readonly functionCallingConfig: {\n readonly mode: \"AUTO\" | \"ANY\" | \"NONE\"\n readonly allowedFunctionNames?: ReadonlyArray<string>\n }\n}\n\nexport type ThinkingConfig = {\n readonly thinkingBudget: number\n}\n\nexport type ImageConfig = {\n readonly aspectRatio?: string\n readonly imageSize?: string\n}\n\nexport type GenerationConfig = {\n readonly temperature?: number\n readonly maxOutputTokens?: number\n readonly topP?: number\n readonly thinkingConfig?: ThinkingConfig\n /** Image models need `[\"TEXT\", \"IMAGE\"]` or they answer in prose only. */\n readonly responseModalities?: ReadonlyArray<\"TEXT\" | \"IMAGE\">\n readonly imageConfig?: ImageConfig\n /** Set together with `responseJsonSchema` to constrain output to JSON. */\n readonly responseMimeType?: string\n /** JSON Schema constraint on the output. */\n readonly responseJsonSchema?: Record<string, unknown>\n}\n\nexport type RequestBody = {\n readonly contents: ReadonlyArray<RequestContent>\n readonly systemInstruction?: RequestSystemInstruction\n readonly generationConfig?: GenerationConfig\n readonly tools?: ReadonlyArray<RequestTool>\n readonly toolConfig?: RequestToolConfig\n}\n\nconst blockText = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => b.text,\n input_image: () => \"\",\n output_text: (b) => b.text,\n output_image: () => \"\",\n refusal: (b) => b.text,\n }),\n)\n\nconst messageText = (message: Message): string => message.content.map(blockText).join(\"\")\n\n/**\n * Gemini's `inlineData` form expects a base64 payload. URL-form images\n * would need to go through Gemini's Files API (upload then `fileData`\n * with the returned URI); pre-uploading isn't free, so we skip those for\n * now and document as a follow-up.\n */\nconst imageSourceToParts = Match.type<InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (): ReadonlyArray<RequestPart> => []),\n Match.tag(\"base64\", (s): ReadonlyArray<RequestPart> => [\n { inlineData: { mimeType: s.mimeType, data: s.base64 } },\n ]),\n Match.tag(\"bytes\", (s): ReadonlyArray<RequestPart> => [\n { inlineData: { mimeType: s.mimeType, data: Encoding.encodeBase64(s.bytes) } },\n ]),\n Match.exhaustive,\n)\n\nconst blockToParts = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b): ReadonlyArray<RequestPart> => (b.text.length === 0 ? [] : [{ text: b.text }]),\n input_image: (b): ReadonlyArray<RequestPart> => imageSourceToParts(b.source),\n output_text: (b): ReadonlyArray<RequestPart> => (b.text.length === 0 ? [] : [{ text: b.text }]),\n // An image the model drew goes back as a model-role `inlineData` part,\n // which is how a multi-turn edit keeps its subject.\n output_image: (b): ReadonlyArray<RequestPart> => imageSourceToParts(b.source),\n // Refusals are assistant-side content; they don't round-trip into Gemini's\n // request body as parts. Skip.\n refusal: (): ReadonlyArray<RequestPart> => [],\n }),\n)\n\nconst messageToContent = (message: Message): Result.Result<RequestContent, void> => {\n const parts = pipe(message.content, Arr.flatMap(blockToParts))\n if (parts.length === 0) return Result.failVoid\n return Match.value(message.role).pipe(\n Match.when(\"user\", () => Result.succeed({ role: \"user\" as const, parts })),\n Match.when(\"assistant\", () => Result.succeed({ role: \"model\" as const, parts })),\n Match.when(\"system\", () => Result.failVoid),\n Match.exhaustive,\n )\n}\n\nconst systemMessageText = (message: Message): Result.Result<string, void> => {\n if (message.role !== \"system\") return Result.failVoid\n const text = messageText(message)\n return text.length === 0 ? Result.failVoid : Result.succeed(text)\n}\n\nconst allMessages = (history: ReadonlyArray<HistoryItem>): ReadonlyArray<Message> =>\n pipe(\n history,\n Arr.filterMap((item) => (item.type === \"message\" ? Result.succeed(item) : Result.failVoid)),\n )\n\n// ---------------------------------------------------------------------------\n// Function-call round-trip\n//\n// `ToolCall.arguments` and `ToolCallOutput.output` are JSON-encoded\n// strings; Gemini's `functionCall.args` and `functionResponse.response`\n// expect parsed JSON *objects*. Decode via Schema:\n// - object payload → use as-is\n// - scalar / array / malformed → wrap as `{ output: <raw string> }` so the\n// model still sees *some* response without crashing the request.\n// ---------------------------------------------------------------------------\n\nconst JsonObject = Schema.Record(Schema.String, Schema.Unknown)\nconst decodeJsonObject = Schema.decodeResult(Schema.fromJsonString(JsonObject))\n\nconst parsedJsonObject =\n (fallback: (raw: string) => Record<string, unknown>) =>\n (encoded: string): Record<string, unknown> =>\n pipe(\n decodeJsonObject(encoded),\n Result.match({\n onSuccess: (v) => v,\n onFailure: () => fallback(encoded),\n }),\n )\n\nconst parsedArgs = parsedJsonObject(() => ({}))\n/** A `ToolCallOutput.output` string as the object `functionResponse` wants. */\nexport const parsedResponse = parsedJsonObject((raw) => ({ output: raw }))\n\n/**\n * `ToolCallOutput` only carries `call_id`; Gemini's `functionResponse`\n * requires the declared function `name`. Resolve the name by scanning prior\n * `function_call` items in the history for a matching `call_id`. If we\n * cannot resolve, fall back to `call_id` as the name - imperfect but\n * preserves stream shape so the model sees *some* response.\n */\nconst isFunctionCallItem = (item: HistoryItem): item is ToolCall => item.type === \"function_call\"\n\nconst nameForCallId = (\n history: ReadonlyArray<HistoryItem>,\n call_id: string,\n): Option.Option<string> =>\n pipe(\n history,\n Arr.findFirst((item): item is ToolCall => isFunctionCallItem(item) && item.call_id === call_id),\n Option.map((f) => f.name),\n )\n\n/**\n * Extract the Gemini-3 wire id and thought signature we stashed in\n * `providerData` on the way out. Schema-driven so the shape lives in one\n * place; failure or absence → `Option.none()`.\n */\nconst ProviderDataGemini = Schema.Struct({\n gemini: Schema.Struct({\n id: Schema.optional(Schema.String),\n thoughtSignature: Schema.optional(Schema.String),\n }),\n})\nconst decodeGemini = Schema.decodeUnknownResult(ProviderDataGemini)\n\nconst geminiField = (\n item: ToolCall,\n pick: (g: {\n readonly id?: string | undefined\n readonly thoughtSignature?: string | undefined\n }) => string | undefined,\n): Option.Option<string> =>\n pipe(\n decodeGemini(item.providerData),\n Result.match({\n onSuccess: (d) => Option.fromNullishOr(pick(d.gemini)),\n onFailure: () => Option.none<string>(),\n }),\n )\n\nconst providerIdFor = (item: ToolCall): Option.Option<string> => geminiField(item, (g) => g.id)\n\n// The originating call's Gemini id, looked up from history by our `call_id`.\n// Gemini 3 maps a `functionResponse` back to its call by this id, so parallel\n// calls to the same function are mis-paired if we drop it.\nconst providerIdForCallId = (\n history: ReadonlyArray<HistoryItem>,\n call_id: string,\n): Option.Option<string> =>\n pipe(\n history,\n Arr.findFirst((item): item is ToolCall => isFunctionCallItem(item) && item.call_id === call_id),\n Option.flatMap(providerIdFor),\n )\n\nconst signatureFor = (item: ToolCall): Option.Option<string> =>\n geminiField(item, (g) => g.thoughtSignature)\n\nconst itemToContent =\n (history: ReadonlyArray<HistoryItem>) =>\n (item: HistoryItem): Result.Result<RequestContent, void> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: messageToContent,\n function_call: (f) =>\n Result.succeed({\n role: \"model\" as const,\n parts: [\n {\n ...Option.match(signatureFor(f), {\n onSome: (thoughtSignature) => ({ thoughtSignature }),\n onNone: () => ({}),\n }),\n functionCall: {\n ...Option.match(providerIdFor(f), {\n onSome: (id) => ({ id }),\n onNone: () => ({}),\n }),\n name: f.name,\n args: parsedArgs(f.arguments),\n },\n },\n ],\n }),\n function_call_output: (o) =>\n Result.succeed({\n role: \"user\" as const,\n parts: [\n {\n functionResponse: {\n ...Option.match(providerIdForCallId(history, o.call_id), {\n onSome: (id) => ({ id }),\n onNone: () => ({}),\n }),\n name: Option.getOrElse(nameForCallId(history, o.call_id), () => o.call_id),\n response: parsedResponse(o.output),\n },\n },\n ],\n }),\n reasoning: () => Result.failVoid,\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Tool descriptors → Gemini `functionDeclarations`. Gemini accepts only a\n// strict OpenAPI 3.0 subset for `parameters`; strip JSON-Schema keys it\n// rejects (`$schema`, `$ref`, `additionalProperties`, `oneOf`,\n// `definitions`).\n// ---------------------------------------------------------------------------\n\nconst UNSUPPORTED_SCHEMA_KEYS: ReadonlySet<string> = new Set([\n \"$schema\",\n \"$ref\",\n \"$defs\",\n \"definitions\",\n \"additionalProperties\",\n \"oneOf\",\n])\n\nconst sanitizeSchema = (schema: unknown): unknown => {\n if (Array.isArray(schema)) return schema.map(sanitizeSchema)\n if (schema === null || typeof schema !== \"object\") return schema\n return pipe(\n Object.entries(schema as Record<string, unknown>),\n Arr.filterMap(([k, v]) =>\n UNSUPPORTED_SCHEMA_KEYS.has(k)\n ? Result.failVoid\n : Result.succeed([k, sanitizeSchema(v)] as const),\n ),\n Object.fromEntries,\n )\n}\n\nexport const toolDescriptorsToTools = (\n tools: ReadonlyArray<ToolDescriptor>,\n): ReadonlyArray<RequestTool> =>\n tools.length === 0\n ? []\n : [\n {\n functionDeclarations: tools.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: sanitizeSchema(t.inputSchema) as Record<string, unknown>,\n })),\n },\n ]\n\n/**\n * Map our cross-provider `toolChoice` onto Gemini's `functionCallingConfig`.\n * Gemini honours the same three modes plus a function allow-list, so the\n * choice is mapped, not dropped. `undefined` keeps the API default (`AUTO`).\n */\nconst toolChoiceToFunctionCallingConfig = (\n toolChoice: CommonRequest[\"toolChoice\"],\n): RequestToolConfig[\"functionCallingConfig\"] =>\n Match.value(toolChoice ?? \"auto\").pipe(\n Match.when(\"auto\", () => ({ mode: \"AUTO\" as const })),\n Match.when(\"required\", () => ({ mode: \"ANY\" as const })),\n Match.when(\"none\", () => ({ mode: \"NONE\" as const })),\n Match.orElse((tc) => ({ mode: \"ANY\" as const, allowedFunctionNames: [tc.name] })),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP failure mapping, shared by every Google surface\n// ---------------------------------------------------------------------------\n\n/** `provider` names the surface that failed, so errors read `gemini` or `lyria`. */\nexport const transportFailure =\n (provider: string) =>\n (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider, raw: cause })\n\n/** 413 is the inline-payload cap, which is a context limit on every surface. */\nexport const httpStatusError =\n (provider: string) =>\n (status: number, body: string): AiError.AiError =>\n Match.value(status).pipe(\n Match.when(429, (): AiError.AiError => new AiError.RateLimited({ provider, raw: body })),\n Match.whenOr(408, 504, (): AiError.AiError => new AiError.Timeout({ provider, raw: body })),\n Match.when(\n 401,\n (): AiError.AiError => new AiError.AuthFailed({ provider, subtype: \"auth\", raw: body }),\n ),\n Match.when(\n 403,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider, subtype: \"permission\", raw: body }),\n ),\n Match.when(\n 402,\n (): AiError.AiError => new AiError.AuthFailed({ provider, subtype: \"billing\", raw: body }),\n ),\n Match.when(\n 413,\n (): AiError.AiError => new AiError.ContextLengthExceeded({ provider, raw: body }),\n ),\n Match.when(\n (n) => n >= 500,\n (n): AiError.AiError => new AiError.Unavailable({ provider, status: n, raw: body }),\n ),\n Match.orElse((): AiError.AiError => new AiError.InvalidRequest({ provider, raw: body })),\n )\n\n/**\n * URL-form images need Gemini's Files API (upload, then `fileData`); we\n * don't pre-upload, so such a part would otherwise vanish from the request.\n * The caller fails the request with `Unsupported` instead of silently\n * dropping the image.\n */\nexport const hasUrlImageSource = (history: ReadonlyArray<HistoryItem>): boolean =>\n history.some(\n (item) =>\n item.type === \"message\" &&\n item.content.some((b) => b.type === \"input_image\" && b.source._tag === \"url\"),\n )\n\nexport const buildRequestBody = (\n history: ReadonlyArray<HistoryItem>,\n generationConfig: Option.Option<GenerationConfig>,\n tools: ReadonlyArray<ToolDescriptor> = [],\n providerToolEntries: ReadonlyArray<RequestTool> = [],\n toolChoice: CommonRequest[\"toolChoice\"] = undefined,\n): RequestBody => {\n const systemTexts = pipe(allMessages(history), Arr.filterMap(systemMessageText))\n const contents = pipe(history, Arr.filterMap(itemToContent(history)))\n const functionTools = toolDescriptorsToTools(tools)\n const requestTools = [...functionTools, ...providerToolEntries]\n return {\n contents,\n ...(systemTexts.length > 0 && {\n systemInstruction: { parts: [{ text: systemTexts.join(\"\\n\") }] },\n }),\n ...Option.match(generationConfig, {\n onNone: () => ({}),\n onSome: (cfg) => ({ generationConfig: cfg }),\n }),\n ...(requestTools.length > 0 && { tools: requestTools }),\n // Only force a function-calling mode when there are function declarations;\n // a grounding-only request must keep the API default.\n ...(functionTools.length > 0 && {\n toolConfig: { functionCallingConfig: toolChoiceToFunctionCallingConfig(toolChoice) },\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stream-level state - accumulate chunk text + final usage/finish.\n//\n// `Accumulator` is immutable; `ingestChunk` returns a fresh one per chunk.\n// Drive it via `Stream.mapAccum` in the consumer.\n// ---------------------------------------------------------------------------\n\nconst finishReasonToStop = (reason: Option.Option<string>): Turn[\"stop_reason\"] =>\n Option.match(reason, {\n onNone: () => \"stop\" as const,\n onSome: (r) => (r === \"MAX_TOKENS\" ? (\"max_tokens\" as const) : (\"stop\" as const)),\n })\n\nexport type AccumulatedFunctionCall = {\n /** Synthesized id-or-name we surface as `call_id` on the canonical item. */\n readonly callId: string\n readonly name: string\n /** Wire id from Gemini 3, when present - echoed back on `functionResponse`. */\n readonly providerId: Option.Option<string>\n /** Thought signature from Gemini 3, replayed on the next request's part. */\n readonly signature: Option.Option<string>\n /** Args as JSON-encoded string, mirroring `Items.ToolCall.arguments`. */\n readonly arguments: string\n}\n\nexport type Accumulator = {\n readonly text: string\n readonly reasoning: string\n /** Images the model drew, in emission order. Image models only. */\n readonly images: ReadonlyArray<ImageSource>\n readonly functionCalls: ReadonlyArray<AccumulatedFunctionCall>\n /** Grounding citations, accumulated (de-duped by url) across chunks. */\n readonly annotations: ReadonlyArray<Annotation>\n readonly finishReason: Option.Option<string>\n readonly usage: {\n readonly input_tokens?: number\n readonly output_tokens?: number\n readonly total_tokens?: number\n readonly input_tokens_details?: { readonly cached_tokens?: number }\n readonly output_tokens_details?: { readonly reasoning_tokens?: number }\n }\n}\n\nexport const emptyAccumulator: Accumulator = {\n text: \"\",\n reasoning: \"\",\n images: [],\n functionCalls: [],\n annotations: [],\n finishReason: Option.none(),\n usage: {},\n}\n\n/**\n * One part's worth of streamable output. `text` and `reasoning` are\n * incremental string deltas; `function_call` arrives whole-in-one-chunk\n * (Gemini does not stream tool-call args).\n */\nexport type ChunkPart =\n | { readonly kind: \"text\"; readonly text: string }\n | { readonly kind: \"reasoning\"; readonly text: string }\n | { readonly kind: \"image\"; readonly source: ImageSource }\n | {\n readonly kind: \"function_call\"\n readonly id: Option.Option<string>\n readonly signature: Option.Option<string>\n readonly name: string\n readonly args: unknown\n }\n\nexport type ChunkResult = {\n readonly accumulator: Accumulator\n readonly parts: ReadonlyArray<ChunkPart>\n readonly finished: boolean\n}\n\nconst textToChunkParts = (p: typeof TextPart.Type): ReadonlyArray<ChunkPart> =>\n p.text.length === 0 ? [] : [{ kind: p.thought === true ? \"reasoning\" : \"text\", text: p.text }]\n\n/** Interim images drawn while planning are dropped, as thought text is. */\nconst inlineDataToChunkParts = (p: WireInlineDataPart): ReadonlyArray<ChunkPart> =>\n p.thought === true\n ? []\n : [\n {\n kind: \"image\",\n source: imageBase64(p.inlineData.data, p.inlineData.mimeType ?? \"image/png\"),\n },\n ]\n\nconst functionCallToChunkParts = (p: WireFunctionCallPart): ReadonlyArray<ChunkPart> => [\n {\n kind: \"function_call\",\n id: Option.fromNullishOr(p.functionCall.id),\n signature: Option.fromNullishOr(p.thoughtSignature),\n name: p.functionCall.name,\n args: p.functionCall.args ?? {},\n },\n]\n\n/** Parts are distinguished by which key they carry, not by a discriminator. */\nconst partToChunkParts: (p: typeof Part.Type) => ReadonlyArray<ChunkPart> = Match.type<\n typeof Part.Type\n>().pipe(\n Match.when({ text: Match.string }, textToChunkParts),\n Match.when({ inlineData: Match.defined }, inlineDataToChunkParts),\n Match.when({ functionCall: Match.defined }, functionCallToChunkParts),\n Match.exhaustive,\n)\n\nconst chunkParts = (chunk: WireChunk): ReadonlyArray<ChunkPart> =>\n pipe(chunk.candidates?.[0]?.content?.parts ?? [], Arr.flatMap(partToChunkParts))\n\nconst sumStrings = (parts: ReadonlyArray<ChunkPart>, kind: \"text\" | \"reasoning\"): string =>\n pipe(\n parts,\n Arr.filterMap((p) => (p.kind === kind ? Result.succeed(p.text) : Result.failVoid)),\n ).join(\"\")\n\nconst collectImages = (parts: ReadonlyArray<ChunkPart>): ReadonlyArray<ImageSource> =>\n pipe(\n parts,\n Arr.filterMap((p) => (p.kind === \"image\" ? Result.succeed(p.source) : Result.failVoid)),\n )\n\nconst collectFunctionCalls = (\n parts: ReadonlyArray<ChunkPart>,\n): ReadonlyArray<Extract<ChunkPart, { kind: \"function_call\" }>> =>\n pipe(\n parts,\n Arr.filterMap((p) => (p.kind === \"function_call\" ? Result.succeed(p) : Result.failVoid)),\n )\n\nconst mergeUsage = (\n prev: Accumulator[\"usage\"],\n next: WireChunk[\"usageMetadata\"],\n): Accumulator[\"usage\"] =>\n next === undefined\n ? prev\n : {\n ...prev,\n ...(next.promptTokenCount !== undefined && { input_tokens: next.promptTokenCount }),\n ...(next.candidatesTokenCount !== undefined && {\n output_tokens: next.candidatesTokenCount,\n }),\n ...(next.totalTokenCount !== undefined && { total_tokens: next.totalTokenCount }),\n ...(next.cachedContentTokenCount !== undefined && {\n input_tokens_details: { cached_tokens: next.cachedContentTokenCount },\n }),\n ...(next.thoughtsTokenCount !== undefined && {\n output_tokens_details: { reasoning_tokens: next.thoughtsTokenCount },\n }),\n }\n\n/**\n * Synthesize a stable `call_id` for a function call. Gemini 3 provides one\n * via `functionCall.id`; older models do not, so we fall back to\n * `<name>_<index>` based on prior calls' position in the accumulator.\n */\nconst synthesizeCallId = (\n call: Extract<ChunkPart, { kind: \"function_call\" }>,\n priorCalls: ReadonlyArray<AccumulatedFunctionCall>,\n): string =>\n Option.match(call.id, {\n onSome: (id) => id,\n onNone: () => `${call.name}_${priorCalls.length}`,\n })\n\nconst chunkCallToAccumulated = (\n prior: ReadonlyArray<AccumulatedFunctionCall>,\n call: Extract<ChunkPart, { kind: \"function_call\" }>,\n): AccumulatedFunctionCall => ({\n callId: synthesizeCallId(call, prior),\n name: call.name,\n providerId: call.id,\n signature: call.signature,\n // `functionCallToChunkParts` already replaced null/undefined with `{}`,\n // so `call.args` is always a JSON-encodable value here.\n arguments: JSON.stringify(call.args),\n})\n\nconst appendFunctionCalls = (\n prior: ReadonlyArray<AccumulatedFunctionCall>,\n fromChunk: ReadonlyArray<Extract<ChunkPart, { kind: \"function_call\" }>>,\n): ReadonlyArray<AccumulatedFunctionCall> =>\n fromChunk.reduce<ReadonlyArray<AccumulatedFunctionCall>>(\n (acc, call) => [...acc, chunkCallToAccumulated(acc, call)],\n prior,\n )\n\n// Grounding usually arrives on the final chunk, but merge across chunks and\n// de-dupe by url so a mid-stream metadata block is not lost or double-counted.\nconst mergeAnnotations = (\n prev: ReadonlyArray<Annotation>,\n next: ReadonlyArray<Annotation>,\n): ReadonlyArray<Annotation> =>\n next.length === 0 ? prev : Arr.dedupeWith(Arr.appendAll(prev, next), sameUrlCitation)\n\nexport const ingestChunk = (acc: Accumulator, chunk: WireChunk): ChunkResult => {\n const parts = chunkParts(chunk)\n const finishReason = Option.fromNullishOr(chunk.candidates?.[0]?.finishReason)\n return {\n parts,\n finished: Option.isSome(finishReason),\n accumulator: {\n text: acc.text + sumStrings(parts, \"text\"),\n reasoning: acc.reasoning + sumStrings(parts, \"reasoning\"),\n images: Arr.appendAll(acc.images, collectImages(parts)),\n functionCalls: appendFunctionCalls(acc.functionCalls, collectFunctionCalls(parts)),\n annotations: mergeAnnotations(\n acc.annotations,\n groundingToAnnotations(chunk.candidates?.[0]?.groundingMetadata),\n ),\n finishReason: Option.orElse(finishReason, () => acc.finishReason),\n usage: mergeUsage(acc.usage, chunk.usageMetadata),\n },\n }\n}\n\nconst reasoningItems = (acc: Accumulator): ReadonlyArray<HistoryItem> =>\n acc.reasoning.length > 0 ? [{ type: \"reasoning\", summary: acc.reasoning }] : []\n\n/**\n * Image models interleave text and image parts, and often answer with an\n * image and no prose at all, so the message exists when either is present.\n */\nconst assistantMessageItems = (acc: Accumulator): ReadonlyArray<HistoryItem> => {\n const content: ReadonlyArray<ContentBlock> = [\n ...(acc.text.length === 0\n ? []\n : [\n {\n type: \"output_text\" as const,\n text: acc.text,\n ...(acc.annotations.length > 0 && { annotations: acc.annotations }),\n },\n ]),\n ...acc.images.map((source) => ({ type: \"output_image\" as const, source })),\n ]\n return content.length === 0 ? [] : [{ type: \"message\", role: \"assistant\", content }]\n}\n\nconst functionCallItems = (acc: Accumulator): ReadonlyArray<HistoryItem> =>\n pipe(\n acc.functionCalls,\n Arr.map((c) => {\n const gemini = {\n ...(Option.isSome(c.providerId) && { id: c.providerId.value }),\n ...(Option.isSome(c.signature) && { thoughtSignature: c.signature.value }),\n }\n return {\n type: \"function_call\" as const,\n call_id: c.callId,\n name: c.name,\n arguments: c.arguments,\n ...(Object.keys(gemini).length > 0 && { providerData: { gemini } }),\n }\n }),\n )\n\nexport const accumulatorToTurn = (acc: Accumulator): Turn => ({\n stop_reason:\n acc.functionCalls.length > 0 ? (\"tool_calls\" as const) : finishReasonToStop(acc.finishReason),\n usage: { ...acc.usage },\n items: [...reasoningItems(acc), ...assistantMessageItems(acc), ...functionCallItems(acc)],\n})\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
1
+ import { n as globalExpect, o as describe, s as it } from "./test.DNmyFkvJ-BPz3G1HS.mjs";
2
2
  import { audioSourceToInlineData, wrapPcmAsWav } from "./geminiSpeechCodec.mjs";
3
3
  import { Effect, Encoding } from "effect";
4
4
  //#region src/geminiSpeechCodec.test.ts
@@ -1,6 +1,6 @@
1
1
  import { codeExecutionTool, googleSearchTool, renderProviderTools } from "./GeminiTools.mjs";
2
2
  import { WireChunk, accumulatorToTurn, buildRequestBody, emptyAccumulator, ingestChunk } from "./codec.mjs";
3
- import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
3
+ import { n as globalExpect, o as describe, s as it } from "./test.DNmyFkvJ-BPz3G1HS.mjs";
4
4
  import { Effect, Option, Result, Schema } from "effect";
5
5
  import * as Tool from "@effect-uai/core/Tool";
6
6
  import { descriptorsOf, providerToolsOf } from "@effect-uai/core/Tool";
package/dist/index.d.mts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { t as codec_d_exports } from "./codec.mjs";
2
2
  import { t as GeminiTools_d_exports } from "./GeminiTools.mjs";
3
- import { GeminiImageModel, GeminiTtsModel, GeminiVoiceName, GoogleDeepResearchModel, GoogleEmbeddingModel, GoogleModel, LyriaModel } from "./models.mjs";
3
+ import { a as GoogleDeepResearchModel, c as LyriaModel, i as GeminiVoiceName, n as GeminiLiveModel, o as GoogleEmbeddingModel, r as GeminiTtsModel, s as GoogleModel, t as GeminiImageModel } from "./models-BVSKJwCx.mjs";
4
4
  import { t as Gemini_d_exports } from "./Gemini.mjs";
5
5
  import { t as GeminiEmbedding_d_exports } from "./GeminiEmbedding.mjs";
6
6
  import { t as GeminiImageGenerator_d_exports } from "./GeminiImageGenerator.mjs";
7
+ import { t as GeminiLiveSession_d_exports } from "./GeminiLiveSession.mjs";
7
8
  import { t as GeminiSynthesizer_d_exports } from "./GeminiSynthesizer.mjs";
8
9
  import { t as GoogleDeepResearch_d_exports } from "./GoogleDeepResearch.mjs";
9
10
  import { t as LyriaGenerator_d_exports } from "./LyriaGenerator.mjs";
10
- export { Gemini_d_exports as Gemini, GeminiEmbedding_d_exports as GeminiEmbedding, GeminiImageGenerator_d_exports as GeminiImageGenerator, GeminiImageModel, GeminiSynthesizer_d_exports as GeminiSynthesizer, GeminiTools_d_exports as GeminiTools, GeminiTtsModel, GeminiVoiceName, GoogleDeepResearch_d_exports as GoogleDeepResearch, GoogleDeepResearchModel, GoogleEmbeddingModel, GoogleModel, LyriaGenerator_d_exports as LyriaGenerator, LyriaModel, codec_d_exports as codec };
11
+ export { Gemini_d_exports as Gemini, GeminiEmbedding_d_exports as GeminiEmbedding, GeminiImageGenerator_d_exports as GeminiImageGenerator, GeminiImageModel, GeminiLiveModel, GeminiLiveSession_d_exports as GeminiLiveSession, GeminiSynthesizer_d_exports as GeminiSynthesizer, GeminiTools_d_exports as GeminiTools, GeminiTtsModel, GeminiVoiceName, GoogleDeepResearch_d_exports as GoogleDeepResearch, GoogleDeepResearchModel, GoogleEmbeddingModel, GoogleModel, LyriaGenerator_d_exports as LyriaGenerator, LyriaModel, codec_d_exports as codec };
package/dist/index.mjs CHANGED
@@ -3,8 +3,9 @@ import { t as codec_exports } from "./codec.mjs";
3
3
  import { t as Gemini_exports } from "./Gemini.mjs";
4
4
  import { t as GeminiEmbedding_exports } from "./GeminiEmbedding.mjs";
5
5
  import { t as GeminiImageGenerator_exports } from "./GeminiImageGenerator.mjs";
6
+ import { t as GeminiLiveSession_exports } from "./GeminiLiveSession.mjs";
6
7
  import { t as GeminiSynthesizer_exports } from "./GeminiSynthesizer.mjs";
7
8
  import { t as GoogleDeepResearch_exports } from "./GoogleDeepResearch.mjs";
8
9
  import { t as LyriaGenerator_exports } from "./LyriaGenerator.mjs";
9
10
  import "./models.mjs";
10
- export { Gemini_exports as Gemini, GeminiEmbedding_exports as GeminiEmbedding, GeminiImageGenerator_exports as GeminiImageGenerator, GeminiSynthesizer_exports as GeminiSynthesizer, GeminiTools_exports as GeminiTools, GoogleDeepResearch_exports as GoogleDeepResearch, LyriaGenerator_exports as LyriaGenerator, codec_exports as codec };
11
+ export { Gemini_exports as Gemini, GeminiEmbedding_exports as GeminiEmbedding, GeminiImageGenerator_exports as GeminiImageGenerator, GeminiLiveSession_exports as GeminiLiveSession, GeminiSynthesizer_exports as GeminiSynthesizer, GeminiTools_exports as GeminiTools, GoogleDeepResearch_exports as GoogleDeepResearch, LyriaGenerator_exports as LyriaGenerator, codec_exports as codec };
@@ -0,0 +1,87 @@
1
+ //#region src/models.d.ts
2
+ /**
3
+ * Known Gemini model identifiers (as of April 2026). The `(string & {})`
4
+ * tail keeps autocomplete on the literals while still accepting any string,
5
+ * so newly-released models work without an SDK update.
6
+ *
7
+ * Reference: https://ai.google.dev/gemini-api/docs/models
8
+ */
9
+ type GoogleModel = "gemini-3.1-pro-preview" | "gemini-3-flash-preview" | "gemini-3.1-flash-lite-preview" | "gemini-3.1-flash-live-preview" | "gemini-3.1-flash-tts-preview" | "gemini-2.5-pro" | "gemini-2.5-flash" | "gemini-2.5-flash-lite" | "gemini-3.1-flash-image" | "gemini-3.1-flash-lite-image" | "gemini-3-pro-image" | (string & {});
10
+ /**
11
+ * Known Gemini image-generation model identifiers. The `-preview` ids
12
+ * were shut down 2026-06-25 and are not listed; neither is the
13
+ * previous-generation `gemini-2.5-flash-image`, which the `(string & {})`
14
+ * tail still accepts.
15
+ *
16
+ * - `gemini-3.1-flash-image` — 512 / 1K / 2K / 4K, web and image search.
17
+ * - `gemini-3.1-flash-lite-image` — 1K only, cheapest, no search.
18
+ * - `gemini-3-pro-image` — 1K / 2K / 4K, the most reference images.
19
+ *
20
+ * Reference: https://ai.google.dev/gemini-api/docs/models
21
+ */
22
+ type GeminiImageModel = "gemini-3.1-flash-image" | "gemini-3.1-flash-lite-image" | "gemini-3-pro-image" | (string & {});
23
+ /**
24
+ * Deep-research agent identifiers for the Interactions API (`POST
25
+ * /v1beta/interactions`). `-preview` is the fast tier, `-max-` the
26
+ * comprehensive one. Preview surface: verify against live docs before relying on
27
+ * exact ids. The `(string & {})` tail accepts any string so a renamed agent
28
+ * works without an SDK update.
29
+ *
30
+ * Reference: https://ai.google.dev/gemini-api/docs/interactions/deep-research
31
+ */
32
+ type GoogleDeepResearchModel = "deep-research-preview-04-2026" | "deep-research-max-preview-04-2026" | (string & {});
33
+ /**
34
+ * Known Gemini embedding model identifiers.
35
+ *
36
+ * - `gemini-embedding-2` — natively multimodal (text, image, audio, video,
37
+ * PDF). Does *not* honour the `taskType` field; instead prepend a task
38
+ * instruction to the text yourself.
39
+ * - `gemini-embedding-001` — text-only, supports the full `taskType` enum
40
+ * (`RETRIEVAL_QUERY` / `RETRIEVAL_DOCUMENT` / classification / etc.).
41
+ *
42
+ * The `(string & {})` tail accepts any string so newly-released models
43
+ * work without an SDK update.
44
+ *
45
+ * Reference: https://ai.google.dev/gemini-api/docs/embeddings
46
+ */
47
+ /**
48
+ * Known Gemini TTS model identifiers. `(string & {})` tail keeps
49
+ * autocomplete on the literals while still accepting any string.
50
+ *
51
+ * Reference: https://ai.google.dev/gemini-api/docs/speech-generation
52
+ */
53
+ type GeminiTtsModel = "gemini-2.5-flash-preview-tts" | "gemini-2.5-pro-preview-tts" | "gemini-3.1-flash-tts-preview" | (string & {});
54
+ /**
55
+ * Live API models, for `BidiGenerateContent` speech-to-speech sessions. Every
56
+ * current one is native audio: output is audio only, and text comes from
57
+ * output transcription.
58
+ *
59
+ * `gemini-3.8-live` is the default for a voice agent. Its
60
+ * `-extended-thinking` sibling reasons in the background during a live
61
+ * exchange, which costs the latency a voice turn is most sensitive to.
62
+ *
63
+ * Reference: https://ai.google.dev/gemini-api/docs/live-api
64
+ */
65
+ type GeminiLiveModel = "gemini-3.8-live" | "gemini-3.8-live-extended-thinking" | "gemini-3.1-flash-live-preview" | "gemini-2.5-flash-native-audio-preview-12-2025" | (string & {});
66
+ /**
67
+ * 30 prebuilt voice names for Gemini TTS. No custom-voice / cloning path
68
+ * is exposed on this surface, so the tail is stock-only (no
69
+ * `(string & {})` escape).
70
+ *
71
+ * Reference: https://ai.google.dev/gemini-api/docs/speech-generation
72
+ */
73
+ type GeminiVoiceName = "Zephyr" | "Puck" | "Charon" | "Kore" | "Fenrir" | "Leda" | "Orus" | "Aoede" | "Callirrhoe" | "Autonoe" | "Enceladus" | "Iapetus" | "Umbriel" | "Algieba" | "Despina" | "Erinome" | "Algenib" | "Rasalgethi" | "Laomedeia" | "Achernar" | "Alnilam" | "Schedar" | "Gacrux" | "Pulcherrima" | "Achird" | "Zubenelgenubi" | "Vindemiatrix" | "Sadachbia" | "Sadaltager" | "Sulafat";
74
+ /**
75
+ * Known Lyria (music generation) model identifiers. `(string & {})` tail
76
+ * keeps autocomplete on the literals while still accepting any string.
77
+ *
78
+ * - `lyria-3-clip-preview` — fixed 30s output, MP3 only.
79
+ * - `lyria-3-pro-preview` — up to a couple of minutes, MP3 or WAV.
80
+ *
81
+ * Reference: https://ai.google.dev/gemini-api/docs/music-generation
82
+ */
83
+ type LyriaModel = "lyria-3-clip-preview" | "lyria-3-pro-preview" | (string & {});
84
+ type GoogleEmbeddingModel = "gemini-embedding-2" | "gemini-embedding-001" | (string & {});
85
+ //#endregion
86
+ export { GoogleDeepResearchModel as a, LyriaModel as c, GeminiVoiceName as i, GeminiLiveModel as n, GoogleEmbeddingModel as o, GeminiTtsModel as r, GoogleModel as s, GeminiImageModel as t };
87
+ //# sourceMappingURL=models-BVSKJwCx.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models-BVSKJwCx.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;;;;;;;KAOY;;;;;;;;;;;;;KA6BA;;;;;;;;;;KAgBA;;;;;;;;;;;;;;;;;;;;;KA0BA;;;;;;;;;;;;KAkBA;;;;;;;;KAeA;;;;;;;;;;KAyCA;KAMA"}