@ai-sdk/google 4.0.111 → 4.0.112

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.
@@ -3,11 +3,15 @@ import type {
3
3
  Experimental_RealtimeModelV4ClientEvent as RealtimeModelV4ClientEvent,
4
4
  Experimental_RealtimeModelV4ClientSecretOptions as RealtimeModelV4ClientSecretOptions,
5
5
  Experimental_RealtimeModelV4ClientSecretResult as RealtimeModelV4ClientSecretResult,
6
+ Experimental_RealtimeModelV4ServerConnection as RealtimeModelV4ServerConnection,
6
7
  Experimental_RealtimeModelV4ServerEvent as RealtimeModelV4ServerEvent,
7
8
  Experimental_RealtimeModelV4SessionConfig as RealtimeModelV4SessionConfig,
9
+ Experimental_RealtimeModelV4SessionIntent as RealtimeModelV4SessionIntent,
8
10
  } from '@ai-sdk/provider';
9
11
  import {
10
12
  resolve,
13
+ resolveWith,
14
+ removeUndefinedEntries,
11
15
  type Resolvable,
12
16
  type FetchFunction,
13
17
  } from '@ai-sdk/provider-utils';
@@ -23,6 +27,13 @@ import {
23
27
  const realtimeWebSocketPath =
24
28
  'google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained';
25
29
 
30
+ // Server-side connections use the unconstrained Bidi endpoint: a trusted server
31
+ // holds the API key and sends the session `setup` over the socket, rather than
32
+ // baking it into an ephemeral token at mint time (which the constrained
33
+ // endpoint requires).
34
+ const realtimeServerWebSocketPath =
35
+ 'google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent';
36
+
26
37
  function getAuthTokensURL(baseURL: string): string {
27
38
  const url = getRealtimeBaseURL(baseURL);
28
39
  url.pathname = `${url.pathname.replace(/\/$/, '')}/v1alpha/auth_tokens`;
@@ -33,6 +44,13 @@ function getWebSocketURL(baseURL: string): string {
33
44
  return getRealtimeWebSocketURL(baseURL, realtimeWebSocketPath).toString();
34
45
  }
35
46
 
47
+ function getServerWebSocketURL(baseURL: string): string {
48
+ const url = getRealtimeBaseURL(baseURL);
49
+ url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
50
+ url.pathname = `${url.pathname.replace(/\/$/, '')}/ws/${realtimeServerWebSocketPath}`;
51
+ return url.toString();
52
+ }
53
+
36
54
  export type GoogleRealtimeModelConfig = {
37
55
  provider: string;
38
56
  baseURL: string;
@@ -42,6 +60,11 @@ export type GoogleRealtimeModelConfig = {
42
60
 
43
61
  export class GoogleRealtimeModel implements RealtimeModelV4 {
44
62
  readonly specificationVersion = 'v4' as const;
63
+ readonly capabilities = {
64
+ conversation: 'turn-based',
65
+ transports: ['websocket'],
66
+ connections: ['client-secret-websocket', 'server-websocket'],
67
+ } as const;
45
68
  readonly provider: string;
46
69
  readonly modelId: string;
47
70
 
@@ -132,21 +155,56 @@ export class GoogleRealtimeModel implements RealtimeModelV4 {
132
155
  };
133
156
  }
134
157
 
158
+ getServerWebSocketConfig(options?: {
159
+ intent?: RealtimeModelV4SessionIntent;
160
+ }):
161
+ | RealtimeModelV4ServerConnection
162
+ | PromiseLike<RealtimeModelV4ServerConnection> {
163
+ // Gemini Live exposes only the conversational endpoint; fail fast instead
164
+ // of silently downgrading unsupported intents.
165
+ this.assertConversationIntent(options);
166
+ return resolveWith(this.config.headers, headers => ({
167
+ url: getServerWebSocketURL(this.config.baseURL),
168
+ headers: removeUndefinedEntries(headers),
169
+ }));
170
+ }
171
+
172
+ private assertConversationIntent(options?: {
173
+ intent?: RealtimeModelV4SessionIntent;
174
+ }): void {
175
+ if (options?.intent != null && options.intent !== 'conversation') {
176
+ throw new Error(
177
+ `Google realtime does not support the '${options.intent}' session intent.`,
178
+ );
179
+ }
180
+ }
181
+
135
182
  parseServerEvent(
136
183
  raw: unknown,
137
184
  ): RealtimeModelV4ServerEvent | RealtimeModelV4ServerEvent[] {
138
185
  return this.mapper.parseServerEvent(raw);
139
186
  }
140
187
 
188
+ createServerEventParser(): ReturnType<
189
+ NonNullable<RealtimeModelV4['createServerEventParser']>
190
+ > {
191
+ const mapper = new GoogleRealtimeEventMapper();
192
+ return raw => mapper.parseServerEvent(raw);
193
+ }
194
+
141
195
  serializeClientEvent(
142
196
  event: RealtimeModelV4ClientEvent,
197
+ options?: { intent?: RealtimeModelV4SessionIntent },
143
198
  ): ReturnType<RealtimeModelV4['serializeClientEvent']> {
199
+ this.assertConversationIntent(options);
144
200
  return this.mapper.serializeClientEvent(event, this.modelId);
145
201
  }
146
202
 
147
203
  buildSessionConfig(
148
204
  config: RealtimeModelV4SessionConfig,
205
+ options?: { intent?: RealtimeModelV4SessionIntent },
149
206
  ): Record<string, unknown> {
207
+ this.assertConversationIntent(options);
150
208
  return buildGoogleSessionConfig(config, this.modelId);
151
209
  }
152
210
  }