@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.
Files changed (70) hide show
  1. package/README.md +15 -1
  2. package/dist/esm/adapters/audio.js +113 -61
  3. package/dist/esm/adapters/audio.js.map +1 -1
  4. package/dist/esm/adapters/embedding.d.ts +77 -0
  5. package/dist/esm/adapters/embedding.js +119 -0
  6. package/dist/esm/adapters/embedding.js.map +1 -0
  7. package/dist/esm/adapters/image.js +224 -225
  8. package/dist/esm/adapters/image.js.map +1 -1
  9. package/dist/esm/adapters/summarize.js +33 -12
  10. package/dist/esm/adapters/summarize.js.map +1 -1
  11. package/dist/esm/adapters/text.js +574 -650
  12. package/dist/esm/adapters/text.js.map +1 -1
  13. package/dist/esm/adapters/tts.js +197 -178
  14. package/dist/esm/adapters/tts.js.map +1 -1
  15. package/dist/esm/adapters/video.js +456 -409
  16. package/dist/esm/adapters/video.js.map +1 -1
  17. package/dist/esm/embedding/embedding-provider-options.d.ts +20 -0
  18. package/dist/esm/experimental/index.js +1 -6
  19. package/dist/esm/experimental/text-interactions/adapter.js +877 -1014
  20. package/dist/esm/experimental/text-interactions/adapter.js.map +1 -1
  21. package/dist/esm/image/image-provider-options.js +74 -54
  22. package/dist/esm/image/image-provider-options.js.map +1 -1
  23. package/dist/esm/index.d.ts +4 -1
  24. package/dist/esm/index.js +5 -36
  25. package/dist/esm/model-meta.d.ts +72 -5
  26. package/dist/esm/model-meta.js +737 -129
  27. package/dist/esm/model-meta.js.map +1 -1
  28. package/dist/esm/realtime/adapter.js +242 -228
  29. package/dist/esm/realtime/adapter.js.map +1 -1
  30. package/dist/esm/realtime/client.js +314 -381
  31. package/dist/esm/realtime/client.js.map +1 -1
  32. package/dist/esm/realtime/index.js +3 -0
  33. package/dist/esm/realtime/token.js +50 -36
  34. package/dist/esm/realtime/token.js.map +1 -1
  35. package/dist/esm/realtime/utils.js +202 -245
  36. package/dist/esm/realtime/utils.js.map +1 -1
  37. package/dist/esm/tools/code-execution-tool.js +11 -13
  38. package/dist/esm/tools/code-execution-tool.js.map +1 -1
  39. package/dist/esm/tools/computer-use-tool.js +18 -28
  40. package/dist/esm/tools/computer-use-tool.js.map +1 -1
  41. package/dist/esm/tools/file-search-tool.js +11 -14
  42. package/dist/esm/tools/file-search-tool.js.map +1 -1
  43. package/dist/esm/tools/function-declaration-tool.js +11 -25
  44. package/dist/esm/tools/function-declaration-tool.js.map +1 -1
  45. package/dist/esm/tools/google-maps-tool.js +11 -14
  46. package/dist/esm/tools/google-maps-tool.js.map +1 -1
  47. package/dist/esm/tools/google-search-retriveal-tool.js +11 -14
  48. package/dist/esm/tools/google-search-retriveal-tool.js.map +1 -1
  49. package/dist/esm/tools/google-search-tool.js +11 -14
  50. package/dist/esm/tools/google-search-tool.js.map +1 -1
  51. package/dist/esm/tools/index.js +2 -13
  52. package/dist/esm/tools/tool-converter.js +64 -57
  53. package/dist/esm/tools/tool-converter.js.map +1 -1
  54. package/dist/esm/tools/url-context-tool.js +11 -13
  55. package/dist/esm/tools/url-context-tool.js.map +1 -1
  56. package/dist/esm/usage.js +81 -87
  57. package/dist/esm/usage.js.map +1 -1
  58. package/dist/esm/utils/client.js +31 -24
  59. package/dist/esm/utils/client.js.map +1 -1
  60. package/dist/esm/utils/index.js +2 -0
  61. package/dist/esm/video/video-provider-options.js +73 -20
  62. package/dist/esm/video/video-provider-options.js.map +1 -1
  63. package/package.json +7 -7
  64. package/src/adapters/embedding.ts +173 -0
  65. package/src/embedding/embedding-provider-options.ts +29 -0
  66. package/src/index.ts +13 -0
  67. package/src/model-meta.ts +143 -10
  68. package/dist/esm/experimental/index.js.map +0 -1
  69. package/dist/esm/index.js.map +0 -1
  70. package/dist/esm/tools/index.js.map +0 -1
package/README.md CHANGED
@@ -1,5 +1,19 @@
1
1
  <div align="center">
2
- <img src="https://raw.githubusercontent.com/TanStack/ai/main/media/header_ai.png" alt="TanStack AI" />
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
- class GeminiAudioAdapter extends BaseAudioAdapter {
5
- name = "gemini";
6
- client;
7
- constructor(config, model) {
8
- super(model, config);
9
- this.client = createGeminiClient(config);
10
- }
11
- async generateAudio(options) {
12
- const { model, prompt, modelOptions, logger } = options;
13
- logger.request(`activity=generateAudio provider=gemini model=${model}`, {
14
- provider: "gemini",
15
- model
16
- });
17
- try {
18
- const response = await this.client.models.generateContent({
19
- model,
20
- contents: [{ role: "user", parts: [{ text: prompt }] }],
21
- config: {
22
- responseModalities: ["AUDIO", "TEXT"],
23
- ...modelOptions?.seed != null ? { seed: modelOptions.seed } : {}
24
- }
25
- });
26
- const parts = response.candidates?.[0]?.content?.parts ?? [];
27
- const audioPart = parts.find(
28
- (part) => part.inlineData?.mimeType?.startsWith("audio/")
29
- );
30
- if (!audioPart?.inlineData?.data) {
31
- throw new Error("No audio data in Gemini Lyria response");
32
- }
33
- const contentType = audioPart.inlineData.mimeType;
34
- return {
35
- id: generateId(this.name),
36
- model,
37
- audio: {
38
- b64Json: audioPart.inlineData.data,
39
- ...contentType !== void 0 && { contentType }
40
- },
41
- // Surface token usage (with per-modality breakdown) when Gemini reports
42
- // it. Spread conditionally for exactOptionalPropertyTypes.
43
- ...response.usageMetadata ? { usage: buildGeminiUsage(response.usageMetadata) } : {}
44
- };
45
- } catch (error) {
46
- logger.errors("gemini.generateAudio fatal", {
47
- error,
48
- source: "gemini.generateAudio"
49
- });
50
- throw error;
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
- return new GeminiAudioAdapter({ ...config, apiKey }, model);
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
- const apiKey = getGeminiApiKeyFromEnv();
59
- return createGeminiAudio(model, apiKey, config);
113
+ return createGeminiAudio(model, getGeminiApiKeyFromEnv(), config);
60
114
  }
61
- export {
62
- GeminiAudioAdapter,
63
- createGeminiAudio,
64
- geminiAudio
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"],"names":[],"mappings":";;;AAgEO,MAAM,2BAEH,iBAAqD;AAAA,EACpD,OAAO;AAAA,EAEC;AAAA,EAEjB,YAAY,QAA2B,OAAe;AACpD,UAAM,OAAO,MAAM;AACnB,SAAK,SAAS,mBAAmB,MAAM;AAAA,EACzC;AAAA,EAEA,MAAM,cACJ,SACgC;AAChC,UAAM,EAAE,OAAO,QAAQ,cAAc,WAAW;AAEhD,WAAO,QAAQ,gDAAgD,KAAK,IAAI;AAAA,MACtE,UAAU;AAAA,MACV;AAAA,IAAA,CACD;AAED,QAAI;AAQF,YAAM,WAAW,MAAM,KAAK,OAAO,OAAO,gBAAgB;AAAA,QACxD;AAAA,QACA,UAAU,CAAC,EAAE,MAAM,QAAQ,OAAO,CAAC,EAAE,MAAM,OAAA,CAAQ,GAAG;AAAA,QACtD,QAAQ;AAAA,UACN,oBAAoB,CAAC,SAAS,MAAM;AAAA,UACpC,GAAI,cAAc,QAAQ,OAAO,EAAE,MAAM,aAAa,SAAS,CAAA;AAAA,QAAC;AAAA,MAClE,CACD;AAED,YAAM,QAAQ,SAAS,aAAa,CAAC,GAAG,SAAS,SAAS,CAAA;AAC1D,YAAM,YAAY,MAAM;AAAA,QAAK,CAAC,SAC5B,KAAK,YAAY,UAAU,WAAW,QAAQ;AAAA,MAAA;AAGhD,UAAI,CAAC,WAAW,YAAY,MAAM;AAChC,cAAM,IAAI,MAAM,wCAAwC;AAAA,MAC1D;AAMA,YAAM,cAAc,UAAU,WAAW;AAEzC,aAAO;AAAA,QACL,IAAI,WAAW,KAAK,IAAI;AAAA,QACxB;AAAA,QACA,OAAO;AAAA,UACL,SAAS,UAAU,WAAW;AAAA,UAC9B,GAAI,gBAAgB,UAAa,EAAE,YAAA;AAAA,QAAY;AAAA;AAAA;AAAA,QAIjD,GAAI,SAAS,gBACT,EAAE,OAAO,iBAAiB,SAAS,aAAa,MAChD,CAAA;AAAA,MAAC;AAAA,IAET,SAAS,OAAO;AACd,aAAO,OAAO,8BAA8B;AAAA,QAC1C;AAAA,QACA,QAAQ;AAAA,MAAA,CACT;AACD,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAkBO,SAAS,kBACd,OACA,QACA,QAC4B;AAG5B,SAAO,IAAI,mBAAmB,EAAE,GAAG,QAAQ,OAAA,GAAU,KAAK;AAC5D;AAmBO,SAAS,YACd,OACA,QAC4B;AAC5B,QAAM,SAAS,uBAAA;AACf,SAAO,kBAAkB,OAAO,QAAQ,MAAM;AAChD;"}
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"}