@tanstack/ai-gemini 0.10.7 → 0.10.8
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/esm/adapters/audio.d.ts +1 -1
- package/dist/esm/adapters/audio.js +3 -2
- package/dist/esm/adapters/audio.js.map +1 -1
- package/dist/esm/adapters/image.d.ts +1 -1
- package/dist/esm/adapters/image.js +13 -8
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/text.d.ts +1 -1
- package/dist/esm/adapters/text.js +45 -25
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/adapters/tts.d.ts +1 -1
- package/dist/esm/adapters/tts.js +6 -5
- package/dist/esm/adapters/tts.js.map +1 -1
- package/dist/esm/image/image-provider-options.js +3 -2
- package/dist/esm/image/image-provider-options.js.map +1 -1
- package/dist/esm/tools/code-execution-tool.js +3 -2
- package/dist/esm/tools/code-execution-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.d.ts +2 -2
- package/dist/esm/tools/computer-use-tool.js +15 -6
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.js +3 -2
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-declaration-tool.js +1 -2
- package/dist/esm/tools/function-declaration-tool.js.map +1 -1
- package/dist/esm/tools/google-maps-tool.js +3 -2
- package/dist/esm/tools/google-maps-tool.js.map +1 -1
- package/dist/esm/tools/google-search-retriveal-tool.js +3 -2
- package/dist/esm/tools/google-search-retriveal-tool.js.map +1 -1
- package/dist/esm/tools/google-search-tool.js +3 -2
- package/dist/esm/tools/google-search-tool.js.map +1 -1
- package/dist/esm/tools/url-context-tool.js +3 -2
- package/dist/esm/tools/url-context-tool.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/audio.ts +2 -2
- package/src/adapters/image.ts +13 -6
- package/src/adapters/text.ts +69 -36
- package/src/adapters/tts.ts +7 -6
- package/src/image/image-provider-options.ts +3 -2
- package/src/tools/code-execution-tool.ts +3 -3
- package/src/tools/computer-use-tool.ts +18 -7
- package/src/tools/file-search-tool.ts +3 -3
- package/src/tools/function-declaration-tool.ts +1 -2
- package/src/tools/google-maps-tool.ts +3 -3
- package/src/tools/google-search-retriveal-tool.ts +3 -3
- package/src/tools/google-search-tool.ts +3 -3
- package/src/tools/url-context-tool.ts +3 -3
package/src/adapters/text.ts
CHANGED
|
@@ -106,10 +106,10 @@ export class GeminiTextAdapter<
|
|
|
106
106
|
TToolCapabilities,
|
|
107
107
|
GeminiToolCallMetadata
|
|
108
108
|
> {
|
|
109
|
-
readonly kind = 'text' as const
|
|
109
|
+
override readonly kind = 'text' as const
|
|
110
110
|
readonly name = 'gemini' as const
|
|
111
111
|
|
|
112
|
-
private client: GoogleGenAI
|
|
112
|
+
private readonly client: GoogleGenAI
|
|
113
113
|
|
|
114
114
|
constructor(config: GeminiTextConfig, model: TModel) {
|
|
115
115
|
super({}, model)
|
|
@@ -315,10 +315,12 @@ export class GeminiTextAdapter<
|
|
|
315
315
|
|
|
316
316
|
accumulatedThinking += part.text
|
|
317
317
|
|
|
318
|
-
// Spec REASONING content event
|
|
318
|
+
// Spec REASONING content event — reasoningMessageId is set in the
|
|
319
|
+
// hasEmittedStepStarted block above (entered on the same `part.thought` path)
|
|
320
|
+
if (!reasoningMessageId) continue
|
|
319
321
|
yield {
|
|
320
322
|
type: EventType.REASONING_MESSAGE_CONTENT,
|
|
321
|
-
messageId: reasoningMessageId
|
|
323
|
+
messageId: reasoningMessageId,
|
|
322
324
|
delta: part.text,
|
|
323
325
|
model,
|
|
324
326
|
timestamp: Date.now(),
|
|
@@ -399,7 +401,11 @@ export class GeminiTextAdapter<
|
|
|
399
401
|
: JSON.stringify(functionArgs),
|
|
400
402
|
index: nextToolIndex++,
|
|
401
403
|
started: false,
|
|
402
|
-
thoughtSignature
|
|
404
|
+
// Only set thoughtSignature when present — under EOPT, the
|
|
405
|
+
// optional field cannot accept an explicit `undefined`.
|
|
406
|
+
...(partThoughtSignature !== undefined && {
|
|
407
|
+
thoughtSignature: partThoughtSignature,
|
|
408
|
+
}),
|
|
403
409
|
}
|
|
404
410
|
toolCallMap.set(toolCallId, toolCallData)
|
|
405
411
|
} else {
|
|
@@ -617,13 +623,16 @@ export class GeminiTextAdapter<
|
|
|
617
623
|
model,
|
|
618
624
|
timestamp: Date.now(),
|
|
619
625
|
finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',
|
|
620
|
-
usage
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
626
|
+
// RunFinishedEvent.usage is `usage?: {...}` (no `| undefined`) under
|
|
627
|
+
// exactOptionalPropertyTypes; only include it when usageMetadata is
|
|
628
|
+
// present rather than assigning an explicit `undefined`.
|
|
629
|
+
...(chunk.usageMetadata && {
|
|
630
|
+
usage: {
|
|
631
|
+
promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,
|
|
632
|
+
completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
|
|
633
|
+
totalTokens: chunk.usageMetadata.totalTokenCount ?? 0,
|
|
634
|
+
},
|
|
635
|
+
}),
|
|
627
636
|
}
|
|
628
637
|
}
|
|
629
638
|
}
|
|
@@ -707,10 +716,7 @@ export class GeminiTextAdapter<
|
|
|
707
716
|
>)
|
|
708
717
|
: {}
|
|
709
718
|
} catch {
|
|
710
|
-
parsedArgs =
|
|
711
|
-
string,
|
|
712
|
-
unknown
|
|
713
|
-
>
|
|
719
|
+
parsedArgs = {}
|
|
714
720
|
}
|
|
715
721
|
|
|
716
722
|
const thoughtSignature = (
|
|
@@ -819,31 +825,58 @@ export class GeminiTextAdapter<
|
|
|
819
825
|
private mapCommonOptionsToGemini(
|
|
820
826
|
options: TextOptions<GeminiTextProviderOptions>,
|
|
821
827
|
) {
|
|
822
|
-
|
|
823
|
-
|
|
828
|
+
// Separate `thinkingConfig` from the other model options so the loose
|
|
829
|
+
// local `thinkingLevel?: keyof typeof ThinkingLevel` type doesn't leak
|
|
830
|
+
// into the SDK config object via the `...modelOpts` spread — we re-add a
|
|
831
|
+
// properly-typed `ThinkingConfig` below.
|
|
832
|
+
const { thinkingConfig, ...modelOpts } =
|
|
833
|
+
options.modelOptions ?? ({} as GeminiTextProviderOptions)
|
|
834
|
+
// Build the thinkingConfig payload only when the caller actually supplied
|
|
835
|
+
// one. Our local `thinkingLevel` is typed as `keyof typeof ThinkingLevel`
|
|
836
|
+
// (string union) so users can pass plain strings; the SDK target is the
|
|
837
|
+
// `ThinkingLevel` enum, and every field is `field?: T` under EOPT — so we
|
|
838
|
+
// re-emit fields via conditional spreads.
|
|
839
|
+
const mappedThinkingConfig = thinkingConfig
|
|
840
|
+
? {
|
|
841
|
+
...(thinkingConfig.includeThoughts !== undefined && {
|
|
842
|
+
includeThoughts: thinkingConfig.includeThoughts,
|
|
843
|
+
}),
|
|
844
|
+
...(thinkingConfig.thinkingBudget !== undefined && {
|
|
845
|
+
thinkingBudget: thinkingConfig.thinkingBudget,
|
|
846
|
+
}),
|
|
847
|
+
...(thinkingConfig.thinkingLevel
|
|
848
|
+
? {
|
|
849
|
+
thinkingLevel: thinkingConfig.thinkingLevel as ThinkingLevel,
|
|
850
|
+
}
|
|
851
|
+
: {}),
|
|
852
|
+
}
|
|
853
|
+
: undefined
|
|
854
|
+
|
|
855
|
+
const normalizedPrompts = normalizeSystemPrompts(options.systemPrompts)
|
|
856
|
+
const systemInstruction =
|
|
857
|
+
normalizedPrompts.length > 0
|
|
858
|
+
? normalizedPrompts.map((p) => p.content).join('\n')
|
|
859
|
+
: undefined
|
|
860
|
+
|
|
861
|
+
// Vendor `GenerateContentConfig` fields are `field?: T` (no `| undefined`)
|
|
862
|
+
// under EOPT, so spread each common option only when present rather than
|
|
863
|
+
// emitting `field: undefined`s into the wire payload.
|
|
824
864
|
const requestOptions: GenerateContentParameters = {
|
|
825
865
|
model: options.model,
|
|
826
866
|
contents: this.formatMessages(options.messages),
|
|
827
867
|
config: {
|
|
828
868
|
...modelOpts,
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
: undefined,
|
|
841
|
-
systemInstruction: (() => {
|
|
842
|
-
const prompts = normalizeSystemPrompts(options.systemPrompts)
|
|
843
|
-
return prompts.length > 0
|
|
844
|
-
? prompts.map((p) => p.content).join('\n')
|
|
845
|
-
: undefined
|
|
846
|
-
})(),
|
|
869
|
+
...(options.temperature !== undefined && {
|
|
870
|
+
temperature: options.temperature,
|
|
871
|
+
}),
|
|
872
|
+
...(options.topP !== undefined && { topP: options.topP }),
|
|
873
|
+
...(options.maxTokens !== undefined && {
|
|
874
|
+
maxOutputTokens: options.maxTokens,
|
|
875
|
+
}),
|
|
876
|
+
...(mappedThinkingConfig !== undefined && {
|
|
877
|
+
thinkingConfig: mappedThinkingConfig,
|
|
878
|
+
}),
|
|
879
|
+
...(systemInstruction !== undefined && { systemInstruction }),
|
|
847
880
|
tools: convertToolsToProviderFormat(options.tools),
|
|
848
881
|
},
|
|
849
882
|
}
|
package/src/adapters/tts.ts
CHANGED
|
@@ -121,7 +121,7 @@ export class GeminiTTSAdapter<
|
|
|
121
121
|
> extends BaseTTSAdapter<TModel, GeminiTTSProviderOptions> {
|
|
122
122
|
readonly name = 'gemini' as const
|
|
123
123
|
|
|
124
|
-
private client: GoogleGenAI
|
|
124
|
+
private readonly client: GoogleGenAI
|
|
125
125
|
|
|
126
126
|
constructor(config: GeminiTTSConfig, model: TModel) {
|
|
127
127
|
super(model, config)
|
|
@@ -251,8 +251,9 @@ export class GeminiTTSAdapter<
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
// Strip any mime parameters (e.g. `audio/ogg;codec=opus`) before pulling
|
|
254
|
-
// the subtype out as the file format.
|
|
255
|
-
|
|
254
|
+
// the subtype out as the file format. `String.split` always returns at
|
|
255
|
+
// least one element, so [0] is defined.
|
|
256
|
+
const format = (mimeType.split(';')[0] ?? '').split('/')[1] || 'wav'
|
|
256
257
|
|
|
257
258
|
return {
|
|
258
259
|
id: generateId(this.name),
|
|
@@ -275,7 +276,7 @@ function parsePcmMimeType(
|
|
|
275
276
|
mimeType: string,
|
|
276
277
|
): { sampleRate: number; channels: number; bitsPerSample: number } | undefined {
|
|
277
278
|
const normalized = mimeType.toLowerCase()
|
|
278
|
-
const subtype = normalized.split(';')[0]
|
|
279
|
+
const subtype = (normalized.split(';')[0] ?? '').split('/')[1] ?? ''
|
|
279
280
|
// Exclude containerized wav (e.g. `audio/wav;codec=pcm`) — those already
|
|
280
281
|
// carry a RIFF header and must not be re-wrapped.
|
|
281
282
|
if (subtype.includes('wav')) return undefined
|
|
@@ -351,8 +352,8 @@ function wrapPcmBase64AsWav(
|
|
|
351
352
|
}
|
|
352
353
|
let binary = ''
|
|
353
354
|
const bytes = new Uint8Array(buffer)
|
|
354
|
-
for (
|
|
355
|
-
binary += String.fromCharCode(
|
|
355
|
+
for (const byte of bytes) {
|
|
356
|
+
binary += String.fromCharCode(byte)
|
|
356
357
|
}
|
|
357
358
|
return btoa(binary)
|
|
358
359
|
}
|
|
@@ -304,6 +304,7 @@ export function parseNativeImageSize(
|
|
|
304
304
|
size: string,
|
|
305
305
|
): { aspectRatio: string; resolution: string } | undefined {
|
|
306
306
|
const match = size.match(/^(\d+:\d+)_(.+)$/)
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
const [, aspectRatio, resolution] = match ?? []
|
|
308
|
+
if (aspectRatio === undefined || resolution === undefined) return undefined
|
|
309
|
+
return { aspectRatio, resolution }
|
|
309
310
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from '@tanstack/ai'
|
|
1
2
|
import type { ProviderTool, Tool } from '@tanstack/ai'
|
|
2
3
|
|
|
3
4
|
export interface CodeExecutionToolConfig {}
|
|
@@ -14,10 +15,9 @@ export function convertCodeExecutionToolToAdapterFormat(_tool: Tool) {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export function codeExecutionTool(): GeminiCodeExecutionTool {
|
|
17
|
-
|
|
18
|
-
return {
|
|
18
|
+
return brandProviderTool<GeminiCodeExecutionTool>({
|
|
19
19
|
name: 'code_execution',
|
|
20
20
|
description: '',
|
|
21
21
|
metadata: {},
|
|
22
|
-
}
|
|
22
|
+
})
|
|
23
23
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from '@tanstack/ai'
|
|
1
2
|
import type { ComputerUse } from '@google/genai'
|
|
2
3
|
import type { ProviderTool, Tool } from '@tanstack/ai'
|
|
3
4
|
|
|
@@ -10,10 +11,17 @@ export type GeminiComputerUseTool = ProviderTool<'gemini', 'computer_use'>
|
|
|
10
11
|
|
|
11
12
|
export function convertComputerUseToolToAdapterFormat(tool: Tool) {
|
|
12
13
|
const metadata = tool.metadata as ComputerUseToolConfig
|
|
14
|
+
// Vendor `ComputerUse` fields are `field?: T` (no `| undefined`) under EOPT,
|
|
15
|
+
// so spread each field conditionally rather than emitting explicit
|
|
16
|
+
// `undefined`s on the wire payload.
|
|
13
17
|
return {
|
|
14
18
|
computerUse: {
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
...(metadata.environment !== undefined && {
|
|
20
|
+
environment: metadata.environment,
|
|
21
|
+
}),
|
|
22
|
+
...(metadata.excludedPredefinedFunctions !== undefined && {
|
|
23
|
+
excludedPredefinedFunctions: metadata.excludedPredefinedFunctions,
|
|
24
|
+
}),
|
|
17
25
|
},
|
|
18
26
|
}
|
|
19
27
|
}
|
|
@@ -21,13 +29,16 @@ export function convertComputerUseToolToAdapterFormat(tool: Tool) {
|
|
|
21
29
|
export function computerUseTool(
|
|
22
30
|
config: ComputerUseToolConfig,
|
|
23
31
|
): GeminiComputerUseTool {
|
|
24
|
-
|
|
25
|
-
return {
|
|
32
|
+
return brandProviderTool<GeminiComputerUseTool>({
|
|
26
33
|
name: 'computer_use',
|
|
27
34
|
description: '',
|
|
28
35
|
metadata: {
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
...(config.environment !== undefined && {
|
|
37
|
+
environment: config.environment,
|
|
38
|
+
}),
|
|
39
|
+
...(config.excludedPredefinedFunctions !== undefined && {
|
|
40
|
+
excludedPredefinedFunctions: config.excludedPredefinedFunctions,
|
|
41
|
+
}),
|
|
31
42
|
},
|
|
32
|
-
}
|
|
43
|
+
})
|
|
33
44
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from '@tanstack/ai'
|
|
1
2
|
import type { FileSearch } from '@google/genai'
|
|
2
3
|
import type { ProviderTool, Tool } from '@tanstack/ai'
|
|
3
4
|
|
|
@@ -18,10 +19,9 @@ export function convertFileSearchToolToAdapterFormat(tool: Tool) {
|
|
|
18
19
|
export function fileSearchTool(
|
|
19
20
|
config: FileSearchToolConfig,
|
|
20
21
|
): GeminiFileSearchTool {
|
|
21
|
-
|
|
22
|
-
return {
|
|
22
|
+
return brandProviderTool<GeminiFileSearchTool>({
|
|
23
23
|
name: 'file_search',
|
|
24
24
|
description: '',
|
|
25
25
|
metadata: config,
|
|
26
|
-
}
|
|
26
|
+
})
|
|
27
27
|
}
|
|
@@ -4,8 +4,7 @@ export type FunctionDeclarationTool = FunctionDeclaration
|
|
|
4
4
|
|
|
5
5
|
const validateFunctionDeclarationTool = (tool: FunctionDeclarationTool) => {
|
|
6
6
|
const nameRegex = /^[a-zA-Z0-9_:.-]{1,64}$/
|
|
7
|
-
|
|
8
|
-
if (!valid) {
|
|
7
|
+
if (tool.name === undefined || !nameRegex.test(tool.name)) {
|
|
9
8
|
throw new Error(
|
|
10
9
|
`Invalid function name: ${tool.name}. Must be 1-64 characters long and contain only a-z, A-Z, 0-9, underscores, colons, dots, and dashes.`,
|
|
11
10
|
)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from '@tanstack/ai'
|
|
1
2
|
import type { GoogleMaps } from '@google/genai'
|
|
2
3
|
import type { ProviderTool, Tool } from '@tanstack/ai'
|
|
3
4
|
|
|
@@ -18,10 +19,9 @@ export function convertGoogleMapsToolToAdapterFormat(tool: Tool) {
|
|
|
18
19
|
export function googleMapsTool(
|
|
19
20
|
config?: GoogleMapsToolConfig,
|
|
20
21
|
): GeminiGoogleMapsTool {
|
|
21
|
-
|
|
22
|
-
return {
|
|
22
|
+
return brandProviderTool<GeminiGoogleMapsTool>({
|
|
23
23
|
name: 'google_maps',
|
|
24
24
|
description: '',
|
|
25
25
|
metadata: config,
|
|
26
|
-
}
|
|
26
|
+
})
|
|
27
27
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from '@tanstack/ai'
|
|
1
2
|
import type { GoogleSearchRetrieval } from '@google/genai'
|
|
2
3
|
import type { ProviderTool, Tool } from '@tanstack/ai'
|
|
3
4
|
|
|
@@ -21,10 +22,9 @@ export function convertGoogleSearchRetrievalToolToAdapterFormat(tool: Tool) {
|
|
|
21
22
|
export function googleSearchRetrievalTool(
|
|
22
23
|
config?: GoogleSearchRetrievalToolConfig,
|
|
23
24
|
): GeminiGoogleSearchRetrievalTool {
|
|
24
|
-
|
|
25
|
-
return {
|
|
25
|
+
return brandProviderTool<GeminiGoogleSearchRetrievalTool>({
|
|
26
26
|
name: 'google_search_retrieval',
|
|
27
27
|
description: '',
|
|
28
28
|
metadata: config,
|
|
29
|
-
}
|
|
29
|
+
})
|
|
30
30
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from '@tanstack/ai'
|
|
1
2
|
import type { GoogleSearch } from '@google/genai'
|
|
2
3
|
import type { ProviderTool, Tool } from '@tanstack/ai'
|
|
3
4
|
|
|
@@ -18,10 +19,9 @@ export function convertGoogleSearchToolToAdapterFormat(tool: Tool) {
|
|
|
18
19
|
export function googleSearchTool(
|
|
19
20
|
config?: GoogleSearchToolConfig,
|
|
20
21
|
): GeminiGoogleSearchTool {
|
|
21
|
-
|
|
22
|
-
return {
|
|
22
|
+
return brandProviderTool<GeminiGoogleSearchTool>({
|
|
23
23
|
name: 'google_search',
|
|
24
24
|
description: '',
|
|
25
25
|
metadata: config,
|
|
26
|
-
}
|
|
26
|
+
})
|
|
27
27
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from '@tanstack/ai'
|
|
1
2
|
import type { ProviderTool, Tool } from '@tanstack/ai'
|
|
2
3
|
|
|
3
4
|
export interface UrlContextToolConfig {}
|
|
@@ -14,10 +15,9 @@ export function convertUrlContextToolToAdapterFormat(_tool: Tool) {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export function urlContextTool(): GeminiUrlContextTool {
|
|
17
|
-
|
|
18
|
-
return {
|
|
18
|
+
return brandProviderTool<GeminiUrlContextTool>({
|
|
19
19
|
name: 'url_context',
|
|
20
20
|
description: '',
|
|
21
21
|
metadata: {},
|
|
22
|
-
}
|
|
22
|
+
})
|
|
23
23
|
}
|