@tanstack/ai-gemini 0.20.1 → 0.22.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/README.md +15 -1
- package/dist/esm/adapters/audio.js +113 -61
- package/dist/esm/adapters/audio.js.map +1 -1
- package/dist/esm/adapters/embedding.d.ts +77 -0
- package/dist/esm/adapters/embedding.js +119 -0
- package/dist/esm/adapters/embedding.js.map +1 -0
- package/dist/esm/adapters/image.js +224 -225
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/summarize.js +33 -12
- package/dist/esm/adapters/summarize.js.map +1 -1
- package/dist/esm/adapters/text.js +574 -650
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/adapters/tts.js +197 -178
- package/dist/esm/adapters/tts.js.map +1 -1
- package/dist/esm/adapters/video.js +456 -409
- package/dist/esm/adapters/video.js.map +1 -1
- package/dist/esm/embedding/embedding-provider-options.d.ts +20 -0
- package/dist/esm/experimental/index.js +1 -6
- package/dist/esm/experimental/text-interactions/adapter.js +877 -1014
- package/dist/esm/experimental/text-interactions/adapter.js.map +1 -1
- package/dist/esm/image/image-provider-options.js +74 -54
- package/dist/esm/image/image-provider-options.js.map +1 -1
- package/dist/esm/index.d.ts +4 -1
- package/dist/esm/index.js +5 -36
- package/dist/esm/model-meta.d.ts +72 -5
- package/dist/esm/model-meta.js +737 -129
- package/dist/esm/model-meta.js.map +1 -1
- package/dist/esm/realtime/adapter.js +242 -228
- package/dist/esm/realtime/adapter.js.map +1 -1
- package/dist/esm/realtime/client.js +314 -381
- package/dist/esm/realtime/client.js.map +1 -1
- package/dist/esm/realtime/index.js +3 -0
- package/dist/esm/realtime/token.js +50 -36
- package/dist/esm/realtime/token.js.map +1 -1
- package/dist/esm/realtime/utils.js +202 -245
- package/dist/esm/realtime/utils.js.map +1 -1
- package/dist/esm/tools/code-execution-tool.js +11 -13
- package/dist/esm/tools/code-execution-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.js +18 -28
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.js +11 -14
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-declaration-tool.js +11 -25
- package/dist/esm/tools/function-declaration-tool.js.map +1 -1
- package/dist/esm/tools/google-maps-tool.js +11 -14
- package/dist/esm/tools/google-maps-tool.js.map +1 -1
- package/dist/esm/tools/google-search-retriveal-tool.js +11 -14
- package/dist/esm/tools/google-search-retriveal-tool.js.map +1 -1
- package/dist/esm/tools/google-search-tool.js +11 -14
- package/dist/esm/tools/google-search-tool.js.map +1 -1
- package/dist/esm/tools/index.js +2 -13
- package/dist/esm/tools/tool-converter.js +64 -57
- package/dist/esm/tools/tool-converter.js.map +1 -1
- package/dist/esm/tools/url-context-tool.js +11 -13
- package/dist/esm/tools/url-context-tool.js.map +1 -1
- package/dist/esm/usage.js +81 -87
- package/dist/esm/usage.js.map +1 -1
- package/dist/esm/utils/client.js +31 -24
- package/dist/esm/utils/client.js.map +1 -1
- package/dist/esm/utils/index.js +2 -0
- package/dist/esm/video/video-provider-options.js +73 -20
- package/dist/esm/video/video-provider-options.js.map +1 -1
- package/package.json +7 -7
- package/src/adapters/embedding.ts +173 -0
- package/src/embedding/embedding-provider-options.ts +29 -0
- package/src/index.ts +13 -0
- package/src/model-meta.ts +143 -10
- package/dist/esm/experimental/index.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/tools/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<
|
|
2
|
+
<picture>
|
|
3
|
+
<source
|
|
4
|
+
media="(prefers-color-scheme: dark)"
|
|
5
|
+
srcset="https://tanstack.com/api/readme/ai.png?theme=dark"
|
|
6
|
+
/>
|
|
7
|
+
<source
|
|
8
|
+
media="(prefers-color-scheme: light)"
|
|
9
|
+
srcset="https://tanstack.com/api/readme/ai.png"
|
|
10
|
+
/>
|
|
11
|
+
<img
|
|
12
|
+
src="https://tanstack.com/api/readme/ai.png"
|
|
13
|
+
alt="TanStack AI"
|
|
14
|
+
width="900"
|
|
15
|
+
/>
|
|
16
|
+
</picture>
|
|
3
17
|
</div>
|
|
4
18
|
|
|
5
19
|
<br />
|
|
@@ -1,66 +1,118 @@
|
|
|
1
|
-
import { BaseAudioAdapter } from "@tanstack/ai/adapters";
|
|
2
|
-
import { createGeminiClient, generateId, getGeminiApiKeyFromEnv } from "../utils/client.js";
|
|
3
1
|
import { buildGeminiUsage } from "../usage.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
2
|
+
import { createGeminiClient, generateId, getGeminiApiKeyFromEnv } from "../utils/client.js";
|
|
3
|
+
import "../utils/index.js";
|
|
4
|
+
import { BaseAudioAdapter } from "@tanstack/ai/adapters";
|
|
5
|
+
//#region src/adapters/audio.ts
|
|
6
|
+
/**
|
|
7
|
+
* Gemini Lyria Music Generation Adapter.
|
|
8
|
+
*
|
|
9
|
+
* Tree-shakeable adapter for Google Lyria music generation via the Gemini API.
|
|
10
|
+
*
|
|
11
|
+
* Models:
|
|
12
|
+
* - `lyria-3-pro-preview` — flagship model, full-length songs with verses,
|
|
13
|
+
* choruses, and bridges. Outputs MP3 or WAV at 48 kHz stereo.
|
|
14
|
+
* - `lyria-3-clip-preview` — 30-second clips in MP3.
|
|
15
|
+
*
|
|
16
|
+
* @see https://ai.google.dev/gemini-api/docs/music-generation
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const adapter = geminiAudio('lyria-3-pro-preview')
|
|
21
|
+
* const result = await generateAudio({
|
|
22
|
+
* adapter,
|
|
23
|
+
* prompt: 'An upbeat jazz track with saxophone and drums',
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
var GeminiAudioAdapter = class extends BaseAudioAdapter {
|
|
28
|
+
name = "gemini";
|
|
29
|
+
client;
|
|
30
|
+
constructor(config, model) {
|
|
31
|
+
super(model, config);
|
|
32
|
+
this.client = createGeminiClient(config);
|
|
33
|
+
}
|
|
34
|
+
async generateAudio(options) {
|
|
35
|
+
const { model, prompt, modelOptions, logger } = options;
|
|
36
|
+
logger.request(`activity=generateAudio provider=gemini model=${model}`, {
|
|
37
|
+
provider: "gemini",
|
|
38
|
+
model
|
|
39
|
+
});
|
|
40
|
+
try {
|
|
41
|
+
const response = await this.client.models.generateContent({
|
|
42
|
+
model,
|
|
43
|
+
contents: [{
|
|
44
|
+
role: "user",
|
|
45
|
+
parts: [{ text: prompt }]
|
|
46
|
+
}],
|
|
47
|
+
config: {
|
|
48
|
+
responseModalities: ["AUDIO", "TEXT"],
|
|
49
|
+
...modelOptions?.seed != null ? { seed: modelOptions.seed } : {}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const audioPart = (response.candidates?.[0]?.content?.parts ?? []).find((part) => part.inlineData?.mimeType?.startsWith("audio/"));
|
|
53
|
+
if (!audioPart?.inlineData?.data) throw new Error("No audio data in Gemini Lyria response");
|
|
54
|
+
const contentType = audioPart.inlineData.mimeType;
|
|
55
|
+
return {
|
|
56
|
+
id: generateId(this.name),
|
|
57
|
+
model,
|
|
58
|
+
audio: {
|
|
59
|
+
b64Json: audioPart.inlineData.data,
|
|
60
|
+
...contentType !== void 0 && { contentType }
|
|
61
|
+
},
|
|
62
|
+
...response.usageMetadata ? { usage: buildGeminiUsage(response.usageMetadata) } : {}
|
|
63
|
+
};
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.errors("gemini.generateAudio fatal", {
|
|
66
|
+
error,
|
|
67
|
+
source: "gemini.generateAudio"
|
|
68
|
+
});
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Creates a Gemini Lyria audio adapter with an explicit API key.
|
|
75
|
+
*
|
|
76
|
+
* @param model - The Lyria model name (e.g., 'lyria-3-pro-preview')
|
|
77
|
+
* @param apiKey - Your Google API key
|
|
78
|
+
* @param config - Optional additional configuration
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const adapter = createGeminiAudio('lyria-3-pro-preview', 'your-api-key')
|
|
83
|
+
* const result = await generateAudio({
|
|
84
|
+
* adapter,
|
|
85
|
+
* prompt: 'Ambient electronic music with soft pads',
|
|
86
|
+
* })
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
54
89
|
function createGeminiAudio(model, apiKey, config) {
|
|
55
|
-
|
|
90
|
+
return new GeminiAudioAdapter({
|
|
91
|
+
...config,
|
|
92
|
+
apiKey
|
|
93
|
+
}, model);
|
|
56
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Creates a Gemini Lyria audio adapter with automatic API key detection.
|
|
97
|
+
*
|
|
98
|
+
* Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in the environment.
|
|
99
|
+
*
|
|
100
|
+
* @param model - The Lyria model name (e.g., 'lyria-3-pro-preview')
|
|
101
|
+
* @param config - Optional configuration (excluding apiKey)
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const adapter = geminiAudio('lyria-3-pro-preview')
|
|
106
|
+
* const result = await generateAudio({
|
|
107
|
+
* adapter,
|
|
108
|
+
* prompt: 'An orchestral piece with strings and brass',
|
|
109
|
+
* })
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
57
112
|
function geminiAudio(model, config) {
|
|
58
|
-
|
|
59
|
-
return createGeminiAudio(model, apiKey, config);
|
|
113
|
+
return createGeminiAudio(model, getGeminiApiKeyFromEnv(), config);
|
|
60
114
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
};
|
|
66
|
-
//# sourceMappingURL=audio.js.map
|
|
115
|
+
//#endregion
|
|
116
|
+
export { GeminiAudioAdapter, createGeminiAudio, geminiAudio };
|
|
117
|
+
|
|
118
|
+
//# sourceMappingURL=audio.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio.js","sources":["../../../src/adapters/audio.ts"],"sourcesContent":["import { BaseAudioAdapter } from '@tanstack/ai/adapters'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport { buildGeminiUsage } from '../usage'\nimport type { GEMINI_AUDIO_MODELS } from '../model-meta'\nimport type {\n AudioGenerationOptions,\n AudioGenerationResult,\n} from '@tanstack/ai'\nimport type { GoogleGenAI } from '@google/genai'\nimport type { GeminiClientConfig } from '../utils/client'\n\n/**\n * Provider options for Gemini Lyria music generation.\n *\n * Notes on the Lyria 3 surface area:\n * - `lyria-3-clip-preview` always returns MP3 (30-second clips). It does\n * not accept `responseMimeType`, and duration is fixed at 30 seconds —\n * the generic `duration` option on `AudioActivityOptions` is ignored.\n * - `lyria-3-pro-preview` returns MP3 by default. Duration is controlled\n * via the natural-language prompt, not a separate SDK field, so the\n * generic `duration` option is similarly ignored.\n * - `negativePrompt` is NOT accepted by `GenerateContentConfig` and has\n * therefore been removed from this surface to avoid giving callers a\n * silently-dropped knob.\n *\n * @see https://ai.google.dev/gemini-api/docs/music-generation\n */\nexport interface GeminiAudioProviderOptions {\n /**\n * Seed for deterministic generation.\n */\n seed?: number\n}\n\nexport interface GeminiAudioConfig extends GeminiClientConfig {}\n\n/** Model type for Gemini Lyria audio generation */\nexport type GeminiAudioModel = (typeof GEMINI_AUDIO_MODELS)[number]\n\n/**\n * Gemini Lyria Music Generation Adapter.\n *\n * Tree-shakeable adapter for Google Lyria music generation via the Gemini API.\n *\n * Models:\n * - `lyria-3-pro-preview` — flagship model, full-length songs with verses,\n * choruses, and bridges. Outputs MP3 or WAV at 48 kHz stereo.\n * - `lyria-3-clip-preview` — 30-second clips in MP3.\n *\n * @see https://ai.google.dev/gemini-api/docs/music-generation\n *\n * @example\n * ```typescript\n * const adapter = geminiAudio('lyria-3-pro-preview')\n * const result = await generateAudio({\n * adapter,\n * prompt: 'An upbeat jazz track with saxophone and drums',\n * })\n * ```\n */\nexport class GeminiAudioAdapter<\n TModel extends GeminiAudioModel,\n> extends BaseAudioAdapter<TModel, GeminiAudioProviderOptions> {\n readonly name = 'gemini' as const\n\n private readonly client: GoogleGenAI\n\n constructor(config: GeminiAudioConfig, model: TModel) {\n super(model, config)\n this.client = createGeminiClient(config)\n }\n\n async generateAudio(\n options: AudioGenerationOptions<GeminiAudioProviderOptions>,\n ): Promise<AudioGenerationResult> {\n const { model, prompt, modelOptions, logger } = options\n\n logger.request(`activity=generateAudio provider=gemini model=${model}`, {\n provider: 'gemini',\n model,\n })\n\n try {\n // FIXME (SDK audit): Lyria 3 music generation may not belong on\n // generateContent at all — @google/genai exposes a `LiveMusicSession`\n // (`ai.live.music.connect`) with a `musicGenerationConfig` object.\n // `seed` is valid on GenerateContentConfig, and Lyria always returns\n // MP3 today, so we don't forward `responseMimeType` either.\n // The runtime test `emits only GenerateContentConfig-valid fields`\n // asserts the config shape so a later SDK audit can catch regressions.\n const response = await this.client.models.generateContent({\n model,\n contents: [{ role: 'user', parts: [{ text: prompt }] }],\n config: {\n responseModalities: ['AUDIO', 'TEXT'],\n ...(modelOptions?.seed != null ? { seed: modelOptions.seed } : {}),\n },\n })\n\n const parts = response.candidates?.[0]?.content?.parts ?? []\n const audioPart = parts.find((part: any) =>\n part.inlineData?.mimeType?.startsWith('audio/'),\n )\n\n if (!audioPart?.inlineData?.data) {\n throw new Error('No audio data in Gemini Lyria response')\n }\n\n // audioPart was selected because mimeType.startsWith('audio/') was\n // truthy, so the mime type is guaranteed to be a string here. Trust the\n // value Gemini returned rather than inventing a non-standard\n // `audio/mp3` fallback (IANA is `audio/mpeg`).\n const contentType = audioPart.inlineData.mimeType\n\n return {\n id: generateId(this.name),\n model,\n audio: {\n b64Json: audioPart.inlineData.data,\n ...(contentType !== undefined && { contentType }),\n },\n // Surface token usage (with per-modality breakdown) when Gemini reports\n // it. Spread conditionally for exactOptionalPropertyTypes.\n ...(response.usageMetadata\n ? { usage: buildGeminiUsage(response.usageMetadata) }\n : {}),\n }\n } catch (error) {\n logger.errors('gemini.generateAudio fatal', {\n error,\n source: 'gemini.generateAudio',\n })\n throw error\n }\n }\n}\n\n/**\n * Creates a Gemini Lyria audio adapter with an explicit API key.\n *\n * @param model - The Lyria model name (e.g., 'lyria-3-pro-preview')\n * @param apiKey - Your Google API key\n * @param config - Optional additional configuration\n *\n * @example\n * ```typescript\n * const adapter = createGeminiAudio('lyria-3-pro-preview', 'your-api-key')\n * const result = await generateAudio({\n * adapter,\n * prompt: 'Ambient electronic music with soft pads',\n * })\n * ```\n */\nexport function createGeminiAudio<TModel extends GeminiAudioModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiAudioConfig, 'apiKey'>,\n): GeminiAudioAdapter<TModel> {\n // Put apiKey LAST so caller-supplied config can't silently override the\n // explicit argument.\n return new GeminiAudioAdapter({ ...config, apiKey }, model)\n}\n\n/**\n * Creates a Gemini Lyria audio adapter with automatic API key detection.\n *\n * Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in the environment.\n *\n * @param model - The Lyria model name (e.g., 'lyria-3-pro-preview')\n * @param config - Optional configuration (excluding apiKey)\n *\n * @example\n * ```typescript\n * const adapter = geminiAudio('lyria-3-pro-preview')\n * const result = await generateAudio({\n * adapter,\n * prompt: 'An orchestral piece with strings and brass',\n * })\n * ```\n */\nexport function geminiAudio<TModel extends GeminiAudioModel>(\n model: TModel,\n config?: Omit<GeminiAudioConfig, 'apiKey'>,\n): GeminiAudioAdapter<TModel> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiAudio(model, apiKey, config)\n}\n"],"
|
|
1
|
+
{"version":3,"file":"audio.js","names":[],"sources":["../../../src/adapters/audio.ts"],"sourcesContent":["import { BaseAudioAdapter } from '@tanstack/ai/adapters'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport { buildGeminiUsage } from '../usage'\nimport type { GEMINI_AUDIO_MODELS } from '../model-meta'\nimport type {\n AudioGenerationOptions,\n AudioGenerationResult,\n} from '@tanstack/ai'\nimport type { GoogleGenAI } from '@google/genai'\nimport type { GeminiClientConfig } from '../utils/client'\n\n/**\n * Provider options for Gemini Lyria music generation.\n *\n * Notes on the Lyria 3 surface area:\n * - `lyria-3-clip-preview` always returns MP3 (30-second clips). It does\n * not accept `responseMimeType`, and duration is fixed at 30 seconds —\n * the generic `duration` option on `AudioActivityOptions` is ignored.\n * - `lyria-3-pro-preview` returns MP3 by default. Duration is controlled\n * via the natural-language prompt, not a separate SDK field, so the\n * generic `duration` option is similarly ignored.\n * - `negativePrompt` is NOT accepted by `GenerateContentConfig` and has\n * therefore been removed from this surface to avoid giving callers a\n * silently-dropped knob.\n *\n * @see https://ai.google.dev/gemini-api/docs/music-generation\n */\nexport interface GeminiAudioProviderOptions {\n /**\n * Seed for deterministic generation.\n */\n seed?: number\n}\n\nexport interface GeminiAudioConfig extends GeminiClientConfig {}\n\n/** Model type for Gemini Lyria audio generation */\nexport type GeminiAudioModel = (typeof GEMINI_AUDIO_MODELS)[number]\n\n/**\n * Gemini Lyria Music Generation Adapter.\n *\n * Tree-shakeable adapter for Google Lyria music generation via the Gemini API.\n *\n * Models:\n * - `lyria-3-pro-preview` — flagship model, full-length songs with verses,\n * choruses, and bridges. Outputs MP3 or WAV at 48 kHz stereo.\n * - `lyria-3-clip-preview` — 30-second clips in MP3.\n *\n * @see https://ai.google.dev/gemini-api/docs/music-generation\n *\n * @example\n * ```typescript\n * const adapter = geminiAudio('lyria-3-pro-preview')\n * const result = await generateAudio({\n * adapter,\n * prompt: 'An upbeat jazz track with saxophone and drums',\n * })\n * ```\n */\nexport class GeminiAudioAdapter<\n TModel extends GeminiAudioModel,\n> extends BaseAudioAdapter<TModel, GeminiAudioProviderOptions> {\n readonly name = 'gemini' as const\n\n private readonly client: GoogleGenAI\n\n constructor(config: GeminiAudioConfig, model: TModel) {\n super(model, config)\n this.client = createGeminiClient(config)\n }\n\n async generateAudio(\n options: AudioGenerationOptions<GeminiAudioProviderOptions>,\n ): Promise<AudioGenerationResult> {\n const { model, prompt, modelOptions, logger } = options\n\n logger.request(`activity=generateAudio provider=gemini model=${model}`, {\n provider: 'gemini',\n model,\n })\n\n try {\n // FIXME (SDK audit): Lyria 3 music generation may not belong on\n // generateContent at all — @google/genai exposes a `LiveMusicSession`\n // (`ai.live.music.connect`) with a `musicGenerationConfig` object.\n // `seed` is valid on GenerateContentConfig, and Lyria always returns\n // MP3 today, so we don't forward `responseMimeType` either.\n // The runtime test `emits only GenerateContentConfig-valid fields`\n // asserts the config shape so a later SDK audit can catch regressions.\n const response = await this.client.models.generateContent({\n model,\n contents: [{ role: 'user', parts: [{ text: prompt }] }],\n config: {\n responseModalities: ['AUDIO', 'TEXT'],\n ...(modelOptions?.seed != null ? { seed: modelOptions.seed } : {}),\n },\n })\n\n const parts = response.candidates?.[0]?.content?.parts ?? []\n const audioPart = parts.find((part: any) =>\n part.inlineData?.mimeType?.startsWith('audio/'),\n )\n\n if (!audioPart?.inlineData?.data) {\n throw new Error('No audio data in Gemini Lyria response')\n }\n\n // audioPart was selected because mimeType.startsWith('audio/') was\n // truthy, so the mime type is guaranteed to be a string here. Trust the\n // value Gemini returned rather than inventing a non-standard\n // `audio/mp3` fallback (IANA is `audio/mpeg`).\n const contentType = audioPart.inlineData.mimeType\n\n return {\n id: generateId(this.name),\n model,\n audio: {\n b64Json: audioPart.inlineData.data,\n ...(contentType !== undefined && { contentType }),\n },\n // Surface token usage (with per-modality breakdown) when Gemini reports\n // it. Spread conditionally for exactOptionalPropertyTypes.\n ...(response.usageMetadata\n ? { usage: buildGeminiUsage(response.usageMetadata) }\n : {}),\n }\n } catch (error) {\n logger.errors('gemini.generateAudio fatal', {\n error,\n source: 'gemini.generateAudio',\n })\n throw error\n }\n }\n}\n\n/**\n * Creates a Gemini Lyria audio adapter with an explicit API key.\n *\n * @param model - The Lyria model name (e.g., 'lyria-3-pro-preview')\n * @param apiKey - Your Google API key\n * @param config - Optional additional configuration\n *\n * @example\n * ```typescript\n * const adapter = createGeminiAudio('lyria-3-pro-preview', 'your-api-key')\n * const result = await generateAudio({\n * adapter,\n * prompt: 'Ambient electronic music with soft pads',\n * })\n * ```\n */\nexport function createGeminiAudio<TModel extends GeminiAudioModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiAudioConfig, 'apiKey'>,\n): GeminiAudioAdapter<TModel> {\n // Put apiKey LAST so caller-supplied config can't silently override the\n // explicit argument.\n return new GeminiAudioAdapter({ ...config, apiKey }, model)\n}\n\n/**\n * Creates a Gemini Lyria audio adapter with automatic API key detection.\n *\n * Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in the environment.\n *\n * @param model - The Lyria model name (e.g., 'lyria-3-pro-preview')\n * @param config - Optional configuration (excluding apiKey)\n *\n * @example\n * ```typescript\n * const adapter = geminiAudio('lyria-3-pro-preview')\n * const result = await generateAudio({\n * adapter,\n * prompt: 'An orchestral piece with strings and brass',\n * })\n * ```\n */\nexport function geminiAudio<TModel extends GeminiAudioModel>(\n model: TModel,\n config?: Omit<GeminiAudioConfig, 'apiKey'>,\n): GeminiAudioAdapter<TModel> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiAudio(model, apiKey, config)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,IAAa,qBAAb,cAEU,iBAAqD;CAC7D,OAAgB;CAEhB;CAEA,YAAY,QAA2B,OAAe;EACpD,MAAM,OAAO,MAAM;EACnB,KAAK,SAAS,mBAAmB,MAAM;CACzC;CAEA,MAAM,cACJ,SACgC;EAChC,MAAM,EAAE,OAAO,QAAQ,cAAc,WAAW;EAEhD,OAAO,QAAQ,gDAAgD,SAAS;GACtE,UAAU;GACV;EACF,CAAC;EAED,IAAI;GAQF,MAAM,WAAW,MAAM,KAAK,OAAO,OAAO,gBAAgB;IACxD;IACA,UAAU,CAAC;KAAE,MAAM;KAAQ,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;IAAE,CAAC;IACtD,QAAQ;KACN,oBAAoB,CAAC,SAAS,MAAM;KACpC,GAAI,cAAc,QAAQ,OAAO,EAAE,MAAM,aAAa,KAAK,IAAI,CAAC;IAClE;GACF,CAAC;GAGD,MAAM,aADQ,SAAS,aAAa,EAAE,EAAE,SAAS,SAAS,CAAC,EAAA,CACnC,MAAM,SAC5B,KAAK,YAAY,UAAU,WAAW,QAAQ,CAChD;GAEA,IAAI,CAAC,WAAW,YAAY,MAC1B,MAAM,IAAI,MAAM,wCAAwC;GAO1D,MAAM,cAAc,UAAU,WAAW;GAEzC,OAAO;IACL,IAAI,WAAW,KAAK,IAAI;IACxB;IACA,OAAO;KACL,SAAS,UAAU,WAAW;KAC9B,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;IACjD;IAGA,GAAI,SAAS,gBACT,EAAE,OAAO,iBAAiB,SAAS,aAAa,EAAE,IAClD,CAAC;GACP;EACF,SAAS,OAAO;GACd,OAAO,OAAO,8BAA8B;IAC1C;IACA,QAAQ;GACV,CAAC;GACD,MAAM;EACR;CACF;AACF;;;;;;;;;;;;;;;;;AAkBA,SAAgB,kBACd,OACA,QACA,QAC4B;CAG5B,OAAO,IAAI,mBAAmB;EAAE,GAAG;EAAQ;CAAO,GAAG,KAAK;AAC5D;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,YACd,OACA,QAC4B;CAE5B,OAAO,kBAAkB,OADV,uBACiB,GAAQ,MAAM;AAChD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters';
|
|
2
|
+
import { EmbeddingOptions, EmbeddingResult } from '@tanstack/ai';
|
|
3
|
+
import { GoogleGenAI } from '@google/genai';
|
|
4
|
+
import { GeminiEmbeddingModel, GeminiEmbeddingModelInputModalitiesByName, GeminiEmbeddingModelProviderOptionsByName } from '../model-meta.js';
|
|
5
|
+
import { GeminiEmbeddingProviderOptions } from '../embedding/embedding-provider-options.js';
|
|
6
|
+
import { GeminiClientConfig } from '../utils/client.js';
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for Gemini Embedding adapter
|
|
9
|
+
*/
|
|
10
|
+
export interface GeminiEmbeddingConfig extends GeminiClientConfig {
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Gemini Embedding Adapter
|
|
14
|
+
*
|
|
15
|
+
* Tree-shakeable adapter for Gemini text embeddings.
|
|
16
|
+
* Supports gemini-embedding-001.
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Batch embedding (one request for the whole input array)
|
|
20
|
+
* - Matryoshka dimension reduction via the top-level `dimensions` option
|
|
21
|
+
* (mapped to the SDK's `outputDimensionality`)
|
|
22
|
+
* - Task-type hints (`taskType`, `title`) via provider options
|
|
23
|
+
*/
|
|
24
|
+
export declare class GeminiEmbeddingAdapter<TModel extends GeminiEmbeddingModel> extends BaseEmbeddingAdapter<TModel, GeminiEmbeddingProviderOptions, GeminiEmbeddingModelProviderOptionsByName, GeminiEmbeddingModelInputModalitiesByName> {
|
|
25
|
+
readonly name: "gemini";
|
|
26
|
+
protected client: GoogleGenAI;
|
|
27
|
+
constructor(config: GeminiEmbeddingConfig, model: TModel);
|
|
28
|
+
createEmbeddings(options: EmbeddingOptions<GeminiEmbeddingProviderOptions>): Promise<EmbeddingResult>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates a Gemini embedding adapter with explicit API key.
|
|
32
|
+
* Type resolution happens here at the call site.
|
|
33
|
+
*
|
|
34
|
+
* @param model - The model name (e.g., 'gemini-embedding-001')
|
|
35
|
+
* @param apiKey - Your Google API key
|
|
36
|
+
* @param config - Optional additional configuration
|
|
37
|
+
* @returns Configured Gemini embedding adapter instance with resolved types
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const adapter = createGeminiEmbedding('gemini-embedding-001', "your-api-key");
|
|
42
|
+
*
|
|
43
|
+
* const result = await embed({
|
|
44
|
+
* adapter,
|
|
45
|
+
* input: 'a red guitar'
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function createGeminiEmbedding<TModel extends GeminiEmbeddingModel>(model: TModel, apiKey: string, config?: Omit<GeminiEmbeddingConfig, 'apiKey'>): GeminiEmbeddingAdapter<TModel>;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a Gemini embedding adapter with automatic API key detection from environment variables.
|
|
52
|
+
* Type resolution happens here at the call site.
|
|
53
|
+
*
|
|
54
|
+
* Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in:
|
|
55
|
+
* - `process.env` (Node.js)
|
|
56
|
+
* - `window.env` (Browser with injected env)
|
|
57
|
+
*
|
|
58
|
+
* @param model - The model name (e.g., 'gemini-embedding-001')
|
|
59
|
+
* @param config - Optional configuration (excluding apiKey which is auto-detected)
|
|
60
|
+
* @returns Configured Gemini embedding adapter instance with resolved types
|
|
61
|
+
* @throws Error if GOOGLE_API_KEY or GEMINI_API_KEY is not found in environment
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Automatically uses GOOGLE_API_KEY from environment
|
|
66
|
+
* const adapter = geminiEmbedding('gemini-embedding-001');
|
|
67
|
+
*
|
|
68
|
+
* const result = await embed({
|
|
69
|
+
* adapter,
|
|
70
|
+
* input: ['a red guitar', 'a blue drum kit'],
|
|
71
|
+
* dimensions: 1536
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* console.log(result.embeddings[0].vector)
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function geminiEmbedding<TModel extends GeminiEmbeddingModel>(model: TModel, config?: Omit<GeminiEmbeddingConfig, 'apiKey'>): GeminiEmbeddingAdapter<TModel>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createGeminiClient, generateId, getGeminiApiKeyFromEnv } from "../utils/client.js";
|
|
2
|
+
import "../utils/index.js";
|
|
3
|
+
import { requireTextOnlyEmbeddingInput } from "@tanstack/ai";
|
|
4
|
+
import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
|
|
5
|
+
import { BaseEmbeddingAdapter } from "@tanstack/ai/adapters";
|
|
6
|
+
//#region src/adapters/embedding.ts
|
|
7
|
+
/**
|
|
8
|
+
* Gemini Embedding Adapter
|
|
9
|
+
*
|
|
10
|
+
* Tree-shakeable adapter for Gemini text embeddings.
|
|
11
|
+
* Supports gemini-embedding-001.
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Batch embedding (one request for the whole input array)
|
|
15
|
+
* - Matryoshka dimension reduction via the top-level `dimensions` option
|
|
16
|
+
* (mapped to the SDK's `outputDimensionality`)
|
|
17
|
+
* - Task-type hints (`taskType`, `title`) via provider options
|
|
18
|
+
*/
|
|
19
|
+
var GeminiEmbeddingAdapter = class extends BaseEmbeddingAdapter {
|
|
20
|
+
name = "gemini";
|
|
21
|
+
client;
|
|
22
|
+
constructor(config, model) {
|
|
23
|
+
super(model, {});
|
|
24
|
+
this.client = createGeminiClient(config);
|
|
25
|
+
}
|
|
26
|
+
async createEmbeddings(options) {
|
|
27
|
+
const { model, logger, modelOptions } = options;
|
|
28
|
+
const texts = requireTextOnlyEmbeddingInput(options.input, this.name, model);
|
|
29
|
+
try {
|
|
30
|
+
const config = {};
|
|
31
|
+
if (options.dimensions !== void 0) config.outputDimensionality = options.dimensions;
|
|
32
|
+
if (modelOptions?.taskType !== void 0) config.taskType = modelOptions.taskType;
|
|
33
|
+
if (modelOptions?.title !== void 0) config.title = modelOptions.title;
|
|
34
|
+
logger.request(`activity=embed provider=${this.name} model=${model} inputs=${texts.length}`, {
|
|
35
|
+
provider: this.name,
|
|
36
|
+
model
|
|
37
|
+
});
|
|
38
|
+
const embeddings = (await this.client.models.embedContent({
|
|
39
|
+
model,
|
|
40
|
+
contents: texts,
|
|
41
|
+
config
|
|
42
|
+
})).embeddings;
|
|
43
|
+
if (!embeddings || embeddings.length !== texts.length) throw new Error(`Gemini embedContent returned ${embeddings ? embeddings.length : "no"} embeddings for ${texts.length} inputs (model: ${model}).`);
|
|
44
|
+
return {
|
|
45
|
+
id: generateId(this.name),
|
|
46
|
+
model,
|
|
47
|
+
embeddings: embeddings.map((embedding, index) => ({
|
|
48
|
+
vector: embedding.values ?? [],
|
|
49
|
+
index
|
|
50
|
+
}))
|
|
51
|
+
};
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.errors(`${this.name}.createEmbeddings fatal`, {
|
|
54
|
+
error: toRunErrorPayload(error, `${this.name}.createEmbeddings failed`),
|
|
55
|
+
source: `${this.name}.createEmbeddings`
|
|
56
|
+
});
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Creates a Gemini embedding adapter with explicit API key.
|
|
63
|
+
* Type resolution happens here at the call site.
|
|
64
|
+
*
|
|
65
|
+
* @param model - The model name (e.g., 'gemini-embedding-001')
|
|
66
|
+
* @param apiKey - Your Google API key
|
|
67
|
+
* @param config - Optional additional configuration
|
|
68
|
+
* @returns Configured Gemini embedding adapter instance with resolved types
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const adapter = createGeminiEmbedding('gemini-embedding-001', "your-api-key");
|
|
73
|
+
*
|
|
74
|
+
* const result = await embed({
|
|
75
|
+
* adapter,
|
|
76
|
+
* input: 'a red guitar'
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
function createGeminiEmbedding(model, apiKey, config) {
|
|
81
|
+
return new GeminiEmbeddingAdapter({
|
|
82
|
+
...config,
|
|
83
|
+
apiKey
|
|
84
|
+
}, model);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Creates a Gemini embedding adapter with automatic API key detection from environment variables.
|
|
88
|
+
* Type resolution happens here at the call site.
|
|
89
|
+
*
|
|
90
|
+
* Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in:
|
|
91
|
+
* - `process.env` (Node.js)
|
|
92
|
+
* - `window.env` (Browser with injected env)
|
|
93
|
+
*
|
|
94
|
+
* @param model - The model name (e.g., 'gemini-embedding-001')
|
|
95
|
+
* @param config - Optional configuration (excluding apiKey which is auto-detected)
|
|
96
|
+
* @returns Configured Gemini embedding adapter instance with resolved types
|
|
97
|
+
* @throws Error if GOOGLE_API_KEY or GEMINI_API_KEY is not found in environment
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* // Automatically uses GOOGLE_API_KEY from environment
|
|
102
|
+
* const adapter = geminiEmbedding('gemini-embedding-001');
|
|
103
|
+
*
|
|
104
|
+
* const result = await embed({
|
|
105
|
+
* adapter,
|
|
106
|
+
* input: ['a red guitar', 'a blue drum kit'],
|
|
107
|
+
* dimensions: 1536
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* console.log(result.embeddings[0].vector)
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
function geminiEmbedding(model, config) {
|
|
114
|
+
return createGeminiEmbedding(model, getGeminiApiKeyFromEnv(), config);
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { GeminiEmbeddingAdapter, createGeminiEmbedding, geminiEmbedding };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=embedding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.js","names":[],"sources":["../../../src/adapters/embedding.ts"],"sourcesContent":["import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { requireTextOnlyEmbeddingInput } from '@tanstack/ai'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport type { EmbeddingOptions, EmbeddingResult } from '@tanstack/ai'\nimport type { EmbedContentConfig, GoogleGenAI } from '@google/genai'\nimport type {\n GeminiEmbeddingModel,\n GeminiEmbeddingModelInputModalitiesByName,\n GeminiEmbeddingModelProviderOptionsByName,\n} from '../model-meta'\nimport type { GeminiEmbeddingProviderOptions } from '../embedding/embedding-provider-options'\nimport type { GeminiClientConfig } from '../utils/client'\n\n/**\n * Configuration for Gemini Embedding adapter\n */\nexport interface GeminiEmbeddingConfig extends GeminiClientConfig {}\n\n/**\n * Gemini Embedding Adapter\n *\n * Tree-shakeable adapter for Gemini text embeddings.\n * Supports gemini-embedding-001.\n *\n * Features:\n * - Batch embedding (one request for the whole input array)\n * - Matryoshka dimension reduction via the top-level `dimensions` option\n * (mapped to the SDK's `outputDimensionality`)\n * - Task-type hints (`taskType`, `title`) via provider options\n */\nexport class GeminiEmbeddingAdapter<\n TModel extends GeminiEmbeddingModel,\n> extends BaseEmbeddingAdapter<\n TModel,\n GeminiEmbeddingProviderOptions,\n GeminiEmbeddingModelProviderOptionsByName,\n GeminiEmbeddingModelInputModalitiesByName\n> {\n readonly name = 'gemini' as const\n\n protected client: GoogleGenAI\n\n constructor(config: GeminiEmbeddingConfig, model: TModel) {\n super(model, {})\n this.client = createGeminiClient(config)\n }\n\n async createEmbeddings(\n options: EmbeddingOptions<GeminiEmbeddingProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, logger, modelOptions } = options\n const texts = requireTextOnlyEmbeddingInput(options.input, this.name, model)\n\n try {\n // Build the config incrementally so possibly-undefined values are never\n // assigned to optional fields (exactOptionalPropertyTypes).\n const config: EmbedContentConfig = {}\n if (options.dimensions !== undefined) {\n config.outputDimensionality = options.dimensions\n }\n if (modelOptions?.taskType !== undefined) {\n config.taskType = modelOptions.taskType\n }\n if (modelOptions?.title !== undefined) {\n config.title = modelOptions.title\n }\n\n logger.request(\n `activity=embed provider=${this.name} model=${model} inputs=${texts.length}`,\n { provider: this.name, model },\n )\n\n const response = await this.client.models.embedContent({\n model,\n contents: texts,\n config,\n })\n\n const embeddings = response.embeddings\n if (!embeddings || embeddings.length !== texts.length) {\n throw new Error(\n `Gemini embedContent returned ${embeddings ? embeddings.length : 'no'} embeddings for ${texts.length} inputs (model: ${model}).`,\n )\n }\n\n // The Gemini embedding API does not report token usage, so `usage` is\n // omitted from the result.\n return {\n id: generateId(this.name),\n model,\n embeddings: embeddings.map((embedding, index) => ({\n vector: embedding.values ?? [],\n index,\n })),\n }\n } catch (error: unknown) {\n logger.errors(`${this.name}.createEmbeddings fatal`, {\n error: toRunErrorPayload(error, `${this.name}.createEmbeddings failed`),\n source: `${this.name}.createEmbeddings`,\n })\n throw error\n }\n }\n}\n\n/**\n * Creates a Gemini embedding adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'gemini-embedding-001')\n * @param apiKey - Your Google API key\n * @param config - Optional additional configuration\n * @returns Configured Gemini embedding adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGeminiEmbedding('gemini-embedding-001', \"your-api-key\");\n *\n * const result = await embed({\n * adapter,\n * input: 'a red guitar'\n * });\n * ```\n */\nexport function createGeminiEmbedding<TModel extends GeminiEmbeddingModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiEmbeddingConfig, 'apiKey'>,\n): GeminiEmbeddingAdapter<TModel> {\n // Put apiKey LAST so caller-supplied config can't silently override the\n // explicit argument.\n return new GeminiEmbeddingAdapter({ ...config, apiKey }, model)\n}\n\n/**\n * Creates a Gemini embedding adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'gemini-embedding-001')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Gemini embedding adapter instance with resolved types\n * @throws Error if GOOGLE_API_KEY or GEMINI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses GOOGLE_API_KEY from environment\n * const adapter = geminiEmbedding('gemini-embedding-001');\n *\n * const result = await embed({\n * adapter,\n * input: ['a red guitar', 'a blue drum kit'],\n * dimensions: 1536\n * });\n *\n * console.log(result.embeddings[0].vector)\n * ```\n */\nexport function geminiEmbedding<TModel extends GeminiEmbeddingModel>(\n model: TModel,\n config?: Omit<GeminiEmbeddingConfig, 'apiKey'>,\n): GeminiEmbeddingAdapter<TModel> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiEmbedding(model, apiKey, config)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmCA,IAAa,yBAAb,cAEU,qBAKR;CACA,OAAgB;CAEhB;CAEA,YAAY,QAA+B,OAAe;EACxD,MAAM,OAAO,CAAC,CAAC;EACf,KAAK,SAAS,mBAAmB,MAAM;CACzC;CAEA,MAAM,iBACJ,SAC0B;EAC1B,MAAM,EAAE,OAAO,QAAQ,iBAAiB;EACxC,MAAM,QAAQ,8BAA8B,QAAQ,OAAO,KAAK,MAAM,KAAK;EAE3E,IAAI;GAGF,MAAM,SAA6B,CAAC;GACpC,IAAI,QAAQ,eAAe,KAAA,GACzB,OAAO,uBAAuB,QAAQ;GAExC,IAAI,cAAc,aAAa,KAAA,GAC7B,OAAO,WAAW,aAAa;GAEjC,IAAI,cAAc,UAAU,KAAA,GAC1B,OAAO,QAAQ,aAAa;GAG9B,OAAO,QACL,2BAA2B,KAAK,KAAK,SAAS,MAAM,UAAU,MAAM,UACpE;IAAE,UAAU,KAAK;IAAM;GAAM,CAC/B;GAQA,MAAM,cAAa,MANI,KAAK,OAAO,OAAO,aAAa;IACrD;IACA,UAAU;IACV;GACF,CAAC,EAAA,CAE2B;GAC5B,IAAI,CAAC,cAAc,WAAW,WAAW,MAAM,QAC7C,MAAM,IAAI,MACR,gCAAgC,aAAa,WAAW,SAAS,KAAK,kBAAkB,MAAM,OAAO,kBAAkB,MAAM,GAC/H;GAKF,OAAO;IACL,IAAI,WAAW,KAAK,IAAI;IACxB;IACA,YAAY,WAAW,KAAK,WAAW,WAAW;KAChD,QAAQ,UAAU,UAAU,CAAC;KAC7B;IACF,EAAE;GACJ;EACF,SAAS,OAAgB;GACvB,OAAO,OAAO,GAAG,KAAK,KAAK,0BAA0B;IACnD,OAAO,kBAAkB,OAAO,GAAG,KAAK,KAAK,yBAAyB;IACtE,QAAQ,GAAG,KAAK,KAAK;GACvB,CAAC;GACD,MAAM;EACR;CACF;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,sBACd,OACA,QACA,QACgC;CAGhC,OAAO,IAAI,uBAAuB;EAAE,GAAG;EAAQ;CAAO,GAAG,KAAK;AAChE;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,gBACd,OACA,QACgC;CAEhC,OAAO,sBAAsB,OADd,uBACqB,GAAQ,MAAM;AACpD"}
|