@effect-uai/google 0.2.0 → 0.4.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.
- package/dist/Gemini.d.mts +105 -0
- package/dist/Gemini.d.mts.map +1 -0
- package/dist/Gemini.mjs +170 -0
- package/dist/Gemini.mjs.map +1 -0
- package/dist/GeminiEmbedding.d.mts +64 -0
- package/dist/GeminiEmbedding.d.mts.map +1 -0
- package/dist/GeminiEmbedding.mjs +167 -0
- package/dist/GeminiEmbedding.mjs.map +1 -0
- package/dist/GeminiSynthesizer.d.mts +48 -0
- package/dist/GeminiSynthesizer.d.mts.map +1 -0
- package/dist/GeminiSynthesizer.mjs +95 -0
- package/dist/GeminiSynthesizer.mjs.map +1 -0
- package/dist/GeminiSynthesizer.test.d.mts +1 -0
- package/dist/GeminiSynthesizer.test.mjs +84 -0
- package/dist/GeminiSynthesizer.test.mjs.map +1 -0
- package/dist/GeminiTranscriber.d.mts +42 -0
- package/dist/GeminiTranscriber.d.mts.map +1 -0
- package/dist/GeminiTranscriber.mjs +80 -0
- package/dist/GeminiTranscriber.mjs.map +1 -0
- package/dist/GeminiTranscriber.test.d.mts +1 -0
- package/dist/GeminiTranscriber.test.mjs +125 -0
- package/dist/GeminiTranscriber.test.mjs.map +1 -0
- package/dist/LyriaGenerator.d.mts +57 -0
- package/dist/LyriaGenerator.d.mts.map +1 -0
- package/dist/LyriaGenerator.mjs +202 -0
- package/dist/LyriaGenerator.mjs.map +1 -0
- package/dist/LyriaGenerator.test.d.mts +1 -0
- package/dist/LyriaGenerator.test.mjs +138 -0
- package/dist/LyriaGenerator.test.mjs.map +1 -0
- package/dist/{chunk-CfYAbeIz.mjs → chunk-uyGKjUfl.mjs} +2 -1
- package/dist/codec.d.mts +107 -0
- package/dist/codec.d.mts.map +1 -0
- package/dist/codec.mjs +151 -0
- package/dist/codec.mjs.map +1 -0
- package/dist/dist-DMsg8YLb.mjs +13782 -0
- package/dist/dist-DMsg8YLb.mjs.map +1 -0
- package/dist/geminiSpeechCodec.d.mts +37 -0
- package/dist/geminiSpeechCodec.d.mts.map +1 -0
- package/dist/geminiSpeechCodec.mjs +110 -0
- package/dist/geminiSpeechCodec.mjs.map +1 -0
- package/dist/geminiSpeechCodec.test.d.mts +1 -0
- package/dist/geminiSpeechCodec.test.mjs +78 -0
- package/dist/geminiSpeechCodec.test.mjs.map +1 -0
- package/dist/index.d.mts +8 -214
- package/dist/index.mjs +8 -289
- package/dist/magic-string.es-CaWgF-3p.mjs +1013 -0
- package/dist/magic-string.es-CaWgF-3p.mjs.map +1 -0
- package/dist/models.d.mts +61 -0
- package/dist/models.d.mts.map +1 -0
- package/dist/models.mjs +1 -0
- package/package.json +32 -12
- package/src/Gemini.ts +3 -3
- package/src/GeminiEmbedding.ts +351 -0
- package/src/GeminiSynthesizer.test.ts +93 -0
- package/src/GeminiSynthesizer.ts +214 -0
- package/src/GeminiTranscriber.test.ts +136 -0
- package/src/GeminiTranscriber.ts +204 -0
- package/src/LyriaGenerator.test.ts +162 -0
- package/src/LyriaGenerator.ts +394 -0
- package/src/codec.ts +42 -35
- package/src/geminiSpeechCodec.test.ts +56 -0
- package/src/geminiSpeechCodec.ts +137 -0
- package/src/index.ts +6 -2
- package/src/models.ts +104 -0
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Effect, Encoding, Match, Result } from "effect"
|
|
2
|
+
import * as AiError from "@effect-uai/core/AiError"
|
|
3
|
+
import type { AudioSource } from "@effect-uai/core/Audio"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared codec helpers for Gemini speech endpoints. The TTS and STT
|
|
7
|
+
* variants both ride on `:generateContent`, so the audio-in / audio-out
|
|
8
|
+
* shapes are siblings.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const urlNotSupported: AiError.AiError = new AiError.InvalidRequest({
|
|
12
|
+
provider: "gemini",
|
|
13
|
+
param: "audio",
|
|
14
|
+
raw: 'Gemini inline audio does not accept URL sources. Either fetch the URL yourself and pass `{ _tag: "bytes", bytes, mimeType }`, or upload via the Files API and pass a `fileData` URI (not yet wired here).',
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
/** Wire shape for an inline-audio part on `generateContent`. */
|
|
18
|
+
export type InlineAudioData = {
|
|
19
|
+
readonly mimeType: string
|
|
20
|
+
readonly data: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Encode any `AudioSource` into the `inlineData` shape expected by
|
|
25
|
+
* Gemini's `generateContent`. URL variants are rejected with
|
|
26
|
+
* `InvalidRequest` — Files API upload is a separate flow.
|
|
27
|
+
*
|
|
28
|
+
* Caller is responsible for keeping the total request body under
|
|
29
|
+
* 20 MB. Beyond that, Gemini's Files API is the only path.
|
|
30
|
+
*/
|
|
31
|
+
export const audioSourceToInlineData: (
|
|
32
|
+
audio: AudioSource,
|
|
33
|
+
) => Effect.Effect<InlineAudioData, AiError.AiError> = Match.type<AudioSource>().pipe(
|
|
34
|
+
Match.tag("bytes", (a) =>
|
|
35
|
+
Effect.succeed<InlineAudioData>({
|
|
36
|
+
mimeType: a.mimeType,
|
|
37
|
+
data: Encoding.encodeBase64(a.bytes),
|
|
38
|
+
}),
|
|
39
|
+
),
|
|
40
|
+
Match.tag("base64", (a) =>
|
|
41
|
+
Effect.succeed<InlineAudioData>({ mimeType: a.mimeType, data: a.base64 }),
|
|
42
|
+
),
|
|
43
|
+
Match.tag("url", () => Effect.fail(urlNotSupported)),
|
|
44
|
+
Match.exhaustive,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
/** Decode a base64-encoded audio payload into bytes. */
|
|
48
|
+
export const decodeBase64Audio = (b64: string): Effect.Effect<Uint8Array, AiError.AiError> =>
|
|
49
|
+
Result.match(Encoding.decodeBase64(b64), {
|
|
50
|
+
onSuccess: Effect.succeed,
|
|
51
|
+
onFailure: (cause) =>
|
|
52
|
+
Effect.fail(new AiError.InvalidRequest({ provider: "gemini", param: "audio", raw: cause })),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Wrap a raw 16-bit signed little-endian PCM buffer in a standard 44-byte
|
|
57
|
+
* RIFF/WAVE header. Gemini TTS returns PCM-only — for playback or for
|
|
58
|
+
* round-tripping into Gemini transcription (which accepts wav, not raw
|
|
59
|
+
* PCM), the bytes need a container.
|
|
60
|
+
*/
|
|
61
|
+
export const wrapPcmAsWav = (
|
|
62
|
+
pcm: Uint8Array,
|
|
63
|
+
sampleRate: number,
|
|
64
|
+
channels: number,
|
|
65
|
+
bitsPerSample = 16,
|
|
66
|
+
): Uint8Array => {
|
|
67
|
+
const dataSize = pcm.length
|
|
68
|
+
const blockAlign = (channels * bitsPerSample) / 8
|
|
69
|
+
const byteRate = sampleRate * blockAlign
|
|
70
|
+
const totalSize = 44 + dataSize
|
|
71
|
+
const buffer = new ArrayBuffer(totalSize)
|
|
72
|
+
const view = new DataView(buffer)
|
|
73
|
+
const writeAscii = (offset: number, s: string): void => {
|
|
74
|
+
for (let i = 0; i < s.length; i++) view.setUint8(offset + i, s.charCodeAt(i))
|
|
75
|
+
}
|
|
76
|
+
writeAscii(0, "RIFF")
|
|
77
|
+
view.setUint32(4, totalSize - 8, true)
|
|
78
|
+
writeAscii(8, "WAVE")
|
|
79
|
+
writeAscii(12, "fmt ")
|
|
80
|
+
view.setUint32(16, 16, true)
|
|
81
|
+
view.setUint16(20, 1, true)
|
|
82
|
+
view.setUint16(22, channels, true)
|
|
83
|
+
view.setUint32(24, sampleRate, true)
|
|
84
|
+
view.setUint32(28, byteRate, true)
|
|
85
|
+
view.setUint16(32, blockAlign, true)
|
|
86
|
+
view.setUint16(34, bitsPerSample, true)
|
|
87
|
+
writeAscii(36, "data")
|
|
88
|
+
view.setUint32(40, dataSize, true)
|
|
89
|
+
new Uint8Array(buffer, 44, dataSize).set(pcm)
|
|
90
|
+
return new Uint8Array(buffer)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* HTTP status → typed `AiError`. Shared between synthesizer and
|
|
95
|
+
* transcriber.
|
|
96
|
+
*/
|
|
97
|
+
export const httpStatusError: (status: number, body: string) => AiError.AiError = (status, body) =>
|
|
98
|
+
Match.value(status).pipe(
|
|
99
|
+
Match.when(
|
|
100
|
+
429,
|
|
101
|
+
(): AiError.AiError => new AiError.RateLimited({ provider: "gemini", raw: body }),
|
|
102
|
+
),
|
|
103
|
+
Match.whenOr(
|
|
104
|
+
408,
|
|
105
|
+
504,
|
|
106
|
+
(): AiError.AiError => new AiError.Timeout({ provider: "gemini", raw: body }),
|
|
107
|
+
),
|
|
108
|
+
Match.when(
|
|
109
|
+
401,
|
|
110
|
+
(): AiError.AiError =>
|
|
111
|
+
new AiError.AuthFailed({ provider: "gemini", subtype: "auth", raw: body }),
|
|
112
|
+
),
|
|
113
|
+
Match.when(
|
|
114
|
+
403,
|
|
115
|
+
(): AiError.AiError =>
|
|
116
|
+
new AiError.AuthFailed({ provider: "gemini", subtype: "permission", raw: body }),
|
|
117
|
+
),
|
|
118
|
+
Match.when(
|
|
119
|
+
402,
|
|
120
|
+
(): AiError.AiError =>
|
|
121
|
+
new AiError.AuthFailed({ provider: "gemini", subtype: "billing", raw: body }),
|
|
122
|
+
),
|
|
123
|
+
Match.when(
|
|
124
|
+
413,
|
|
125
|
+
(): AiError.AiError => new AiError.ContextLengthExceeded({ provider: "gemini", raw: body }),
|
|
126
|
+
),
|
|
127
|
+
Match.when(
|
|
128
|
+
(n) => n >= 500,
|
|
129
|
+
(n): AiError.AiError => new AiError.Unavailable({ provider: "gemini", status: n, raw: body }),
|
|
130
|
+
),
|
|
131
|
+
Match.orElse(
|
|
132
|
+
(): AiError.AiError => new AiError.InvalidRequest({ provider: "gemini", raw: body }),
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
export const transportFailure = (cause: unknown): AiError.AiError =>
|
|
137
|
+
new AiError.Unavailable({ provider: "gemini", raw: cause })
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
export * from "./Gemini.js"
|
|
2
|
-
export * from "./
|
|
1
|
+
export * as Gemini from "./Gemini.js"
|
|
2
|
+
export * as GeminiEmbedding from "./GeminiEmbedding.js"
|
|
3
|
+
export * as GeminiSynthesizer from "./GeminiSynthesizer.js"
|
|
4
|
+
export * as GeminiTranscriber from "./GeminiTranscriber.js"
|
|
5
|
+
export * as LyriaGenerator from "./LyriaGenerator.js"
|
|
3
6
|
export * as codec from "./codec.js"
|
|
7
|
+
export * from "./models.js"
|
package/src/models.ts
CHANGED
|
@@ -16,3 +16,107 @@ export type GoogleModel =
|
|
|
16
16
|
| "gemini-2.5-flash-lite"
|
|
17
17
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
18
18
|
| (string & {})
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Known Gemini embedding model identifiers.
|
|
22
|
+
*
|
|
23
|
+
* - `gemini-embedding-2` — natively multimodal (text, image, audio, video,
|
|
24
|
+
* PDF). Does *not* honour the `taskType` field; instead prepend a task
|
|
25
|
+
* instruction to the text yourself.
|
|
26
|
+
* - `gemini-embedding-001` — text-only, supports the full `taskType` enum
|
|
27
|
+
* (`RETRIEVAL_QUERY` / `RETRIEVAL_DOCUMENT` / classification / etc.).
|
|
28
|
+
*
|
|
29
|
+
* The `(string & {})` tail accepts any string so newly-released models
|
|
30
|
+
* work without an SDK update.
|
|
31
|
+
*
|
|
32
|
+
* Reference: https://ai.google.dev/gemini-api/docs/embeddings
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Known Gemini TTS model identifiers. `(string & {})` tail keeps
|
|
36
|
+
* autocomplete on the literals while still accepting any string.
|
|
37
|
+
*
|
|
38
|
+
* Reference: https://ai.google.dev/gemini-api/docs/speech-generation
|
|
39
|
+
*/
|
|
40
|
+
export type GeminiTtsModel =
|
|
41
|
+
| "gemini-2.5-flash-preview-tts"
|
|
42
|
+
| "gemini-2.5-pro-preview-tts"
|
|
43
|
+
| "gemini-3.1-flash-tts-preview"
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
45
|
+
| (string & {})
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Known Gemini models that accept audio input via `inlineData` /
|
|
49
|
+
* `fileData` on `generateContent`. Used by `GeminiTranscriber` —
|
|
50
|
+
* transcription is prompt-driven on top of these models, not a
|
|
51
|
+
* dedicated endpoint.
|
|
52
|
+
*
|
|
53
|
+
* Reference: https://ai.google.dev/gemini-api/docs/audio
|
|
54
|
+
*/
|
|
55
|
+
export type GeminiSttModel =
|
|
56
|
+
| "gemini-3-flash-preview"
|
|
57
|
+
| "gemini-3.1-pro-preview"
|
|
58
|
+
| "gemini-3.1-flash-lite-preview"
|
|
59
|
+
| "gemini-2.5-flash"
|
|
60
|
+
| "gemini-2.5-pro"
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
62
|
+
| (string & {})
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 30 prebuilt voice names for Gemini TTS. No custom-voice / cloning path
|
|
66
|
+
* is exposed on this surface, so the tail is stock-only (no
|
|
67
|
+
* `(string & {})` escape).
|
|
68
|
+
*
|
|
69
|
+
* Reference: https://ai.google.dev/gemini-api/docs/speech-generation
|
|
70
|
+
*/
|
|
71
|
+
export type GeminiVoiceName =
|
|
72
|
+
| "Zephyr"
|
|
73
|
+
| "Puck"
|
|
74
|
+
| "Charon"
|
|
75
|
+
| "Kore"
|
|
76
|
+
| "Fenrir"
|
|
77
|
+
| "Leda"
|
|
78
|
+
| "Orus"
|
|
79
|
+
| "Aoede"
|
|
80
|
+
| "Callirrhoe"
|
|
81
|
+
| "Autonoe"
|
|
82
|
+
| "Enceladus"
|
|
83
|
+
| "Iapetus"
|
|
84
|
+
| "Umbriel"
|
|
85
|
+
| "Algieba"
|
|
86
|
+
| "Despina"
|
|
87
|
+
| "Erinome"
|
|
88
|
+
| "Algenib"
|
|
89
|
+
| "Rasalgethi"
|
|
90
|
+
| "Laomedeia"
|
|
91
|
+
| "Achernar"
|
|
92
|
+
| "Alnilam"
|
|
93
|
+
| "Schedar"
|
|
94
|
+
| "Gacrux"
|
|
95
|
+
| "Pulcherrima"
|
|
96
|
+
| "Achird"
|
|
97
|
+
| "Zubenelgenubi"
|
|
98
|
+
| "Vindemiatrix"
|
|
99
|
+
| "Sadachbia"
|
|
100
|
+
| "Sadaltager"
|
|
101
|
+
| "Sulafat"
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Known Lyria (music generation) model identifiers. `(string & {})` tail
|
|
105
|
+
* keeps autocomplete on the literals while still accepting any string.
|
|
106
|
+
*
|
|
107
|
+
* - `lyria-3-clip-preview` — fixed 30s output, MP3 only.
|
|
108
|
+
* - `lyria-3-pro-preview` — up to a couple of minutes, MP3 or WAV.
|
|
109
|
+
*
|
|
110
|
+
* Reference: https://ai.google.dev/gemini-api/docs/music-generation
|
|
111
|
+
*/
|
|
112
|
+
export type LyriaModel =
|
|
113
|
+
| "lyria-3-clip-preview"
|
|
114
|
+
| "lyria-3-pro-preview"
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
116
|
+
| (string & {})
|
|
117
|
+
|
|
118
|
+
export type GoogleEmbeddingModel =
|
|
119
|
+
| "gemini-embedding-2"
|
|
120
|
+
| "gemini-embedding-001"
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
122
|
+
| (string & {})
|
package/dist/index.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/codec.ts","../src/models.ts","../src/Gemini.ts"],"mappings":"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"}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["Arr"],"sources":["../src/codec.ts","../src/Gemini.ts"],"sourcesContent":["import { Array as Arr, Match, Option, Result, Schema, pipe } from \"effect\"\nimport type { ContentBlock, Item, Message } from \"@effect-uai/core/Items\"\nimport { matchType } from \"@effect-uai/core/Match\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\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\nconst Part = Schema.Union([TextPart])\n\nconst Content = Schema.Struct({\n role: Schema.optional(Schema.String),\n parts: Schema.optional(Schema.Array(Part)),\n})\n\nconst Candidate = Schema.Struct({\n content: Schema.optional(Content),\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\ninterface RequestContent {\n readonly role: \"user\" | \"model\"\n readonly parts: ReadonlyArray<RequestPart>\n}\n\ninterface RequestSystemInstruction {\n readonly parts: ReadonlyArray<{ readonly text: string }>\n}\n\nexport interface ThinkingConfig {\n readonly thinkingBudget: number\n}\n\nexport interface GenerationConfig {\n readonly temperature?: number\n readonly maxOutputTokens?: number\n readonly topP?: number\n readonly thinkingConfig?: ThinkingConfig\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 interface RequestBody {\n readonly contents: ReadonlyArray<RequestContent>\n readonly systemInstruction?: RequestSystemInstruction\n readonly generationConfig?: GenerationConfig\n}\n\nconst blockText = Match.type<ContentBlock>().pipe(\n matchType(\"input_text\", (b) => b.text),\n matchType(\"input_image\", () => \"\"),\n matchType(\"output_text\", (b) => b.text),\n matchType(\"refusal\", (b) => b.text),\n Match.exhaustive,\n)\n\nconst messageText = (message: Message): string => message.content.map(blockText).join(\"\")\n\nconst blockToParts = Match.type<ContentBlock>().pipe(\n matchType(\n \"input_text\",\n (b): ReadonlyArray<RequestPart> => (b.text.length === 0 ? [] : [{ text: b.text }]),\n ),\n matchType(\n \"input_image\",\n (b): ReadonlyArray<RequestPart> =>\n // Gemini's `fileData` form expects a pre-uploaded Files API URI, not an\n // arbitrary HTTPS URL - we'd need to fetch + re-upload to support that.\n // Skip URL-form images for now; document as a follow-up.\n b.source._tag === \"base64\"\n ? [{ inlineData: { mimeType: b.source.media_type, data: b.source.data } }]\n : [],\n ),\n matchType(\n \"output_text\",\n (b): ReadonlyArray<RequestPart> => (b.text.length === 0 ? [] : [{ text: b.text }]),\n ),\n // Refusals are assistant-side content; they don't round-trip into Gemini's\n // request body as parts. Skip.\n matchType(\"refusal\", (): ReadonlyArray<RequestPart> => []),\n Match.exhaustive,\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 messages = (history: ReadonlyArray<Item>): ReadonlyArray<Message> =>\n pipe(\n history,\n Arr.filterMap((item) => (item.type === \"message\" ? Result.succeed(item) : Result.failVoid)),\n )\n\nexport const buildRequestBody = (\n history: ReadonlyArray<Item>,\n generationConfig: Option.Option<GenerationConfig>,\n): RequestBody => {\n const msgs = messages(history)\n const systemTexts = pipe(msgs, Arr.filterMap(systemMessageText))\n const contents = pipe(msgs, Arr.filterMap(messageToContent))\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 }\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 interface Accumulator {\n readonly text: string\n readonly reasoning: string\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 finishReason: Option.none(),\n usage: {},\n}\n\n/**\n * One part's worth of streamable text: either the model's answer\n * (`kind: \"text\"`) or chain-of-thought (`kind: \"reasoning\"`). Emitted\n * in wire order so consumers can interleave faithfully.\n */\nexport type ChunkPart =\n | { readonly kind: \"text\"; readonly text: string }\n | { readonly kind: \"reasoning\"; readonly text: string }\n\nexport interface ChunkResult {\n readonly accumulator: Accumulator\n readonly parts: ReadonlyArray<ChunkPart>\n readonly finished: boolean\n}\n\nconst chunkParts = (chunk: WireChunk): ReadonlyArray<ChunkPart> =>\n pipe(\n chunk.candidates?.[0]?.content?.parts ?? [],\n Arr.filterMap((p) =>\n p.text.length === 0\n ? Result.failVoid\n : Result.succeed({\n kind: p.thought === true ? (\"reasoning\" as const) : (\"text\" as const),\n text: p.text,\n }),\n ),\n )\n\nconst sumByKind = (parts: ReadonlyArray<ChunkPart>, kind: ChunkPart[\"kind\"]): string =>\n pipe(\n parts,\n Arr.filterMap((p) => (p.kind === kind ? Result.succeed(p.text) : Result.failVoid)),\n ).join(\"\")\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\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 + sumByKind(parts, \"text\"),\n reasoning: acc.reasoning + sumByKind(parts, \"reasoning\"),\n finishReason: Option.orElse(finishReason, () => acc.finishReason),\n usage: mergeUsage(acc.usage, chunk.usageMetadata),\n },\n }\n}\n\nexport const accumulatorToTurn = (acc: Accumulator): Turn => ({\n stop_reason: finishReasonToStop(acc.finishReason),\n usage: { ...acc.usage },\n items: [\n ...(acc.reasoning.length > 0 ? [{ type: \"reasoning\" as const, summary: acc.reasoning }] : []),\n ...(acc.text.length === 0\n ? []\n : [\n {\n type: \"message\" as const,\n role: \"assistant\" as const,\n content: [{ type: \"output_text\" as const, text: acc.text }],\n },\n ]),\n ],\n})\n","import { Context, Effect, Layer, Option, Redacted, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n type CommonRequest,\n LanguageModel,\n type LanguageModelService,\n} from \"@effect-uai/core/LanguageModel\"\nimport { JsonParseError } from \"@effect-uai/core/JSONL\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport type { TurnEvent } from \"@effect-uai/core/Turn\"\nimport {\n type GenerationConfig,\n WireChunk,\n accumulatorToTurn,\n buildRequestBody,\n emptyAccumulator,\n ingestChunk,\n} from \"./codec.js\"\nimport type { GoogleModel } from \"./models.js\"\n\n/**\n * Gemini's native event vocabulary. Aliased from `WireChunk` so the public\n * surface matches `@effect-uai/responses` and `@effect-uai/anthropic`. Each\n * `ProviderEvent` is a full `GenerateContentResponse` chunk (not a per-field\n * delta) - that's how Gemini's SSE works.\n */\nexport const ProviderEvent = WireChunk\nexport type ProviderEvent = WireChunk\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface GeminiRequest extends Omit<CommonRequest, \"model\"> {\n /**\n * Narrows `CommonRequest.model` (`string`) to the typed `GoogleModel`\n * literal union for autocomplete.\n */\n readonly model: GoogleModel\n /**\n * Gemini 2.5 thinking budget. `0` disables thinking entirely (lowest\n * latency); higher values let the model think longer. Forwarded as\n * `generationConfig.thinkingConfig.thinkingBudget`.\n */\n readonly thinkingBudget?: number\n}\n\nexport interface GeminiService {\n /**\n * Stream the provider's native event vocabulary (post-SSE-decode). For\n * Gemini, each event is a full `GenerateContentResponse` chunk. Use this\n * when you need access to fields the canonical view doesn't surface\n * (`thoughtSignature`, granular `safetyRatings`, etc.). For\n * provider-portable code, use `streamTurn`.\n */\n readonly streamNative: (request: GeminiRequest) => Stream.Stream<ProviderEvent, AiError.AiError>\n /**\n * Stream canonical `TurnEvent`s. Implemented as\n * `streamNative |> toCanonical`.\n */\n readonly streamTurn: (request: GeminiRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Threads a fresh `Accumulator` so chunk-level text/usage merging happens\n * per stream.\n */\n readonly toCanonical: <E, R>(\n s: Stream.Stream<ProviderEvent, E, R>,\n ) => Stream.Stream<TurnEvent, E, R>\n}\n\n/**\n * Provider-typed service tag. Yield this when you want Gemini-specific\n * options (`thinkingBudget`); yield the generic `LanguageModel` tag for\n * provider-portable code. Both are registered by `layer`.\n */\nexport class Gemini extends Context.Service<Gemini, GeminiService>()(\n \"@betalyra/effect-uai/providers/google/Gemini\",\n) {}\n\nexport interface Config {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Request body\n// ---------------------------------------------------------------------------\n\nconst buildGenerationConfig = (request: GeminiRequest): Option.Option<GenerationConfig> => {\n const cfg: GenerationConfig = {\n ...(request.temperature !== undefined && { temperature: request.temperature }),\n ...(request.maxOutputTokens !== undefined && { maxOutputTokens: request.maxOutputTokens }),\n ...(request.topP !== undefined && { topP: request.topP }),\n ...(request.thinkingBudget !== undefined && {\n thinkingConfig: { thinkingBudget: request.thinkingBudget },\n }),\n ...(request.structured !== undefined && {\n responseMimeType: \"application/json\",\n responseJsonSchema: request.structured.schema[\"~standard\"].jsonSchema.input({\n target: \"draft-2020-12\",\n }),\n }),\n }\n return Object.keys(cfg).length === 0 ? Option.none() : Option.some(cfg)\n}\n\n// ---------------------------------------------------------------------------\n// SSE event → wire chunk\n// ---------------------------------------------------------------------------\n\nconst decodeChunk = Schema.decodeUnknownEffect(WireChunk)\n\n/**\n * Parse one SSE event's `data` payload into a `WireChunk`. Unlike the other\n * providers, Gemini's wire vocabulary is non-discriminated (a single\n * permissive struct, all fields optional) - there's no natural shape for\n * an `_unknown` variant, so JSON parse / schema decode failures flow\n * through as transport errors rather than being silently dropped.\n */\nconst sseEventToChunk = (ev: SSE.Event) =>\n Effect.try({\n try: () => JSON.parse(ev.data) as unknown,\n catch: (cause) => new JsonParseError({ line: ev.data, cause }),\n }).pipe(Effect.flatMap(decodeChunk))\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"gemini\"\n const raw = body\n if (status === 429) return new AiError.RateLimited({ provider, raw })\n if (status === 408 || status === 504) return new AiError.Timeout({ provider, raw })\n if (status === 401) return new AiError.AuthFailed({ provider, subtype: \"auth\", raw })\n if (status === 403) return new AiError.AuthFailed({ provider, subtype: \"permission\", raw })\n if (status === 402) return new AiError.AuthFailed({ provider, subtype: \"billing\", raw })\n if (status === 413) return new AiError.ContextLengthExceeded({ provider, raw })\n if (status >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\nconst buildNativeStream = (cfg: Config) => {\n const baseUrl = cfg.baseUrl ?? \"https://generativelanguage.googleapis.com/v1beta\"\n return (\n request: GeminiRequest,\n ): Stream.Stream<ProviderEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const url = `${baseUrl}/models/${request.model}:streamGenerateContent?alt=sse`\n const generationConfig = buildGenerationConfig(request)\n const body = buildRequestBody(request.history, generationConfig)\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"x-goog-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client\n .execute(httpRequest)\n .pipe(\n Effect.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"gemini\", raw: cause }),\n ),\n )\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n\n return response.stream.pipe(\n Stream.mapError(\n (cause): AiError.AiError => new AiError.Unavailable({ provider: \"gemini\", raw: cause }),\n ),\n SSE.fromBytes,\n Stream.mapEffect((ev) =>\n sseEventToChunk(ev).pipe(\n Effect.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"gemini\", raw: cause }),\n ),\n ),\n ),\n )\n }),\n )\n}\n\n/**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Threads a fresh `Accumulator` so chunk-level text/usage merging works\n * across the run.\n */\nexport const toCanonical = <E, R>(\n s: Stream.Stream<ProviderEvent, E, R>,\n): Stream.Stream<TurnEvent, E, R> =>\n s.pipe(\n Stream.mapAccum(\n () => emptyAccumulator,\n (acc, chunk) => {\n const result = ingestChunk(acc, chunk)\n const partDeltas = result.parts.map((p) =>\n p.kind === \"text\"\n ? { type: \"text_delta\" as const, text: p.text }\n : { type: \"reasoning_delta\" as const, text: p.text, kind: \"trace\" as const },\n )\n const deltas = result.finished\n ? [\n ...partDeltas,\n {\n type: \"turn_complete\" as const,\n turn: accumulatorToTurn(result.accumulator),\n },\n ]\n : partDeltas\n return [result.accumulator, deltas] as const\n },\n ),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `GeminiService` value. For Layer-based setup, prefer `layer`.\n */\nexport const make = (cfg: Config): Effect.Effect<GeminiService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient.asEffect(), (client) => {\n const streamNative: GeminiService[\"streamNative\"] = (request) =>\n buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))\n return {\n streamNative,\n streamTurn: (request) => toCanonical(streamNative(request)),\n toCanonical,\n }\n })\n\n/**\n * Layer that registers both the provider-specific `Gemini` tag and the\n * generic `LanguageModel` tag, sharing one underlying implementation.\n *\n * The generic tag accepts `CommonRequest`; the typed tag accepts the full\n * `GeminiRequest` surface.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<Gemini | LanguageModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(Gemini, make(cfg))\n const generic = Layer.effect(\n LanguageModel,\n Effect.map(\n make(cfg),\n (s): LanguageModelService => ({\n streamTurn: (request) => s.streamTurn(request as GeminiRequest),\n }),\n ),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|