@ai-sdk/google 4.0.0-canary.58 → 4.0.0-canary.60

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ai-sdk/google
2
2
 
3
+ ## 4.0.0-canary.60
4
+
5
+ ### Patch Changes
6
+
7
+ - 6a26901: feat(google): add `fileData` support to embedding model
8
+
9
+ ## 4.0.0-canary.59
10
+
11
+ ### Patch Changes
12
+
13
+ - db394ab: feat(provider/google): support cancelling long-running Interactions API agents via AbortSignal, and process their intermittent stream
14
+ - Updated dependencies [ca446f8]
15
+ - @ai-sdk/provider-utils@5.0.0-canary.38
16
+
3
17
  ## 4.0.0-canary.58
4
18
 
5
19
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -228,6 +228,11 @@ declare const googleEmbeddingModelOptions: _ai_sdk_provider_utils.LazySchema<{
228
228
  mimeType: string;
229
229
  data: string;
230
230
  };
231
+ } | {
232
+ fileData: {
233
+ fileUri: string;
234
+ mimeType: string;
235
+ };
231
236
  })[] | null)[] | undefined;
232
237
  }>;
233
238
  type GoogleEmbeddingModelOptions = InferSchema<typeof googleEmbeddingModelOptions>;
package/dist/index.js CHANGED
@@ -3,11 +3,11 @@ import {
3
3
  generateId as generateId2,
4
4
  loadApiKey,
5
5
  withoutTrailingSlash,
6
- withUserAgentSuffix
6
+ withUserAgentSuffix as withUserAgentSuffix2
7
7
  } from "@ai-sdk/provider-utils";
8
8
 
9
9
  // src/version.ts
10
- var VERSION = true ? "4.0.0-canary.58" : "0.0.0-test";
10
+ var VERSION = true ? "4.0.0-canary.60" : "0.0.0-test";
11
11
 
12
12
  // src/google-embedding-model.ts
13
13
  import {
@@ -63,6 +63,12 @@ var googleEmbeddingContentPartSchema = z2.union([
63
63
  mimeType: z2.string(),
64
64
  data: z2.string()
65
65
  })
66
+ }),
67
+ z2.object({
68
+ fileData: z2.object({
69
+ fileUri: z2.string(),
70
+ mimeType: z2.string()
71
+ })
66
72
  })
67
73
  ]);
68
74
  var googleEmbeddingModelOptions = lazySchema2(
@@ -3462,8 +3468,8 @@ var googleOperationSchema = z17.object({
3462
3468
 
3463
3469
  // src/interactions/google-interactions-language-model.ts
3464
3470
  import {
3465
- combineHeaders as combineHeaders6,
3466
- createEventSourceResponseHandler as createEventSourceResponseHandler2,
3471
+ combineHeaders as combineHeaders7,
3472
+ createEventSourceResponseHandler as createEventSourceResponseHandler3,
3467
3473
  createJsonResponseHandler as createJsonResponseHandler7,
3468
3474
  generateId as defaultGenerateId2,
3469
3475
  parseProviderOptions as parseProviderOptions6,
@@ -5074,8 +5080,45 @@ function parseGoogleInteractionsOutputs({
5074
5080
  import {
5075
5081
  createJsonResponseHandler as createJsonResponseHandler6,
5076
5082
  delay as delay3,
5077
- getFromApi as getFromApi3
5083
+ getFromApi as getFromApi3,
5084
+ isAbortError
5085
+ } from "@ai-sdk/provider-utils";
5086
+
5087
+ // src/interactions/cancel-google-interaction.ts
5088
+ import {
5089
+ combineHeaders as combineHeaders6,
5090
+ getRuntimeEnvironmentUserAgent,
5091
+ withUserAgentSuffix
5078
5092
  } from "@ai-sdk/provider-utils";
5093
+ var getOriginalFetch = () => globalThis.fetch;
5094
+ async function cancelGoogleInteraction({
5095
+ baseURL,
5096
+ interactionId,
5097
+ headers,
5098
+ fetch = getOriginalFetch()
5099
+ }) {
5100
+ if (interactionId == null || interactionId.length === 0) {
5101
+ return;
5102
+ }
5103
+ const url = `${baseURL}/interactions/${encodeURIComponent(interactionId)}/cancel`;
5104
+ try {
5105
+ const response = await fetch(url, {
5106
+ method: "POST",
5107
+ headers: withUserAgentSuffix(
5108
+ combineHeaders6({ "Content-Type": "application/json" }, headers),
5109
+ getRuntimeEnvironmentUserAgent()
5110
+ ),
5111
+ body: "{}"
5112
+ });
5113
+ try {
5114
+ await response.text();
5115
+ } catch (e) {
5116
+ }
5117
+ } catch (e) {
5118
+ }
5119
+ }
5120
+
5121
+ // src/interactions/poll-google-interactions.ts
5079
5122
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "incomplete"]);
5080
5123
  function isTerminalStatus(status) {
5081
5124
  return status != null && TERMINAL_STATUSES.has(status);
@@ -5101,34 +5144,43 @@ async function pollGoogleInteractionUntilTerminal({
5101
5144
  const startedAt = Date.now();
5102
5145
  let nextDelayMs = initialDelayMs;
5103
5146
  const url = `${baseURL}/interactions/${encodeURIComponent(interactionId)}`;
5104
- while (true) {
5105
- if (abortSignal == null ? void 0 : abortSignal.aborted) {
5106
- throw new DOMException("Polling was aborted", "AbortError");
5107
- }
5108
- if (Date.now() - startedAt > timeoutMs) {
5109
- throw new Error(
5110
- `google.interactions: timed out polling interaction ${interactionId} after ${timeoutMs}ms.`
5111
- );
5147
+ const cancelOnServer = () => cancelGoogleInteraction({ baseURL, interactionId, headers, fetch });
5148
+ try {
5149
+ while (true) {
5150
+ if (abortSignal == null ? void 0 : abortSignal.aborted) {
5151
+ await cancelOnServer();
5152
+ throw new DOMException("Polling was aborted", "AbortError");
5153
+ }
5154
+ if (Date.now() - startedAt > timeoutMs) {
5155
+ throw new Error(
5156
+ `google.interactions: timed out polling interaction ${interactionId} after ${timeoutMs}ms.`
5157
+ );
5158
+ }
5159
+ await delay3(nextDelayMs, { abortSignal });
5160
+ const {
5161
+ value: response,
5162
+ rawValue: rawResponse,
5163
+ responseHeaders
5164
+ } = await getFromApi3({
5165
+ url,
5166
+ headers,
5167
+ failedResponseHandler: googleFailedResponseHandler,
5168
+ successfulResponseHandler: createJsonResponseHandler6(
5169
+ googleInteractionsResponseSchema
5170
+ ),
5171
+ abortSignal,
5172
+ fetch
5173
+ });
5174
+ if (isTerminalStatus(response.status)) {
5175
+ return { response, rawResponse, responseHeaders };
5176
+ }
5177
+ nextDelayMs = Math.min(nextDelayMs * 2, maxDelayMs);
5112
5178
  }
5113
- await delay3(nextDelayMs, { abortSignal });
5114
- const {
5115
- value: response,
5116
- rawValue: rawResponse,
5117
- responseHeaders
5118
- } = await getFromApi3({
5119
- url,
5120
- headers,
5121
- failedResponseHandler: googleFailedResponseHandler,
5122
- successfulResponseHandler: createJsonResponseHandler6(
5123
- googleInteractionsResponseSchema
5124
- ),
5125
- abortSignal,
5126
- fetch
5127
- });
5128
- if (isTerminalStatus(response.status)) {
5129
- return { response, rawResponse, responseHeaders };
5179
+ } catch (error) {
5180
+ if (isAbortError(error)) {
5181
+ await cancelOnServer();
5130
5182
  }
5131
- nextDelayMs = Math.min(nextDelayMs * 2, maxDelayMs);
5183
+ throw error;
5132
5184
  }
5133
5185
  }
5134
5186
 
@@ -5280,6 +5332,174 @@ function prepareGoogleInteractionsTools({
5280
5332
  };
5281
5333
  }
5282
5334
 
5335
+ // src/interactions/stream-google-interactions.ts
5336
+ import {
5337
+ createEventSourceResponseHandler as createEventSourceResponseHandler2,
5338
+ delay as delay4,
5339
+ getFromApi as getFromApi4,
5340
+ isAbortError as isAbortError2
5341
+ } from "@ai-sdk/provider-utils";
5342
+ var DEFAULT_MAX_RETRIES = 3;
5343
+ var DEFAULT_RETRY_DELAY_MS = 500;
5344
+ function streamGoogleInteractionEvents({
5345
+ baseURL,
5346
+ interactionId,
5347
+ headers,
5348
+ fetch,
5349
+ abortSignal,
5350
+ maxRetries = DEFAULT_MAX_RETRIES,
5351
+ retryDelayMs = DEFAULT_RETRY_DELAY_MS
5352
+ }) {
5353
+ if (interactionId.length === 0) {
5354
+ throw new Error(
5355
+ "google.interactions: cannot stream a background interaction without an id."
5356
+ );
5357
+ }
5358
+ const eventSourceHeaders = {
5359
+ ...headers,
5360
+ accept: "text/event-stream"
5361
+ };
5362
+ let lastEventId;
5363
+ let complete = false;
5364
+ let attempt = 0;
5365
+ let receivedAnyEventThisAttempt = false;
5366
+ let currentReader;
5367
+ const internalAbort = new AbortController();
5368
+ const upstreamAbortHandler = () => internalAbort.abort();
5369
+ if (abortSignal != null) {
5370
+ if (abortSignal.aborted) {
5371
+ internalAbort.abort();
5372
+ } else {
5373
+ abortSignal.addEventListener("abort", upstreamAbortHandler, {
5374
+ once: true
5375
+ });
5376
+ }
5377
+ }
5378
+ const effectiveSignal = internalAbort.signal;
5379
+ function buildUrl() {
5380
+ const base = `${baseURL}/interactions/${encodeURIComponent(interactionId)}`;
5381
+ const params = new URLSearchParams({ stream: "true" });
5382
+ if (lastEventId != null) {
5383
+ params.set("last_event_id", lastEventId);
5384
+ }
5385
+ return `${base}?${params.toString()}`;
5386
+ }
5387
+ async function openReader() {
5388
+ const { value: stream } = await getFromApi4({
5389
+ url: buildUrl(),
5390
+ headers: eventSourceHeaders,
5391
+ failedResponseHandler: googleFailedResponseHandler,
5392
+ successfulResponseHandler: createEventSourceResponseHandler2(
5393
+ googleInteractionsEventSchema
5394
+ ),
5395
+ abortSignal: effectiveSignal,
5396
+ fetch
5397
+ });
5398
+ return stream.getReader();
5399
+ }
5400
+ return new ReadableStream({
5401
+ async start(controller) {
5402
+ try {
5403
+ while (!complete && !effectiveSignal.aborted) {
5404
+ if (currentReader == null) {
5405
+ try {
5406
+ currentReader = await openReader();
5407
+ receivedAnyEventThisAttempt = false;
5408
+ } catch (error) {
5409
+ if (isAbortError2(error) || effectiveSignal.aborted) {
5410
+ controller.error(error);
5411
+ return;
5412
+ }
5413
+ attempt++;
5414
+ if (attempt >= maxRetries) {
5415
+ controller.error(error);
5416
+ return;
5417
+ }
5418
+ await delay4(retryDelayMs * attempt, {
5419
+ abortSignal: effectiveSignal
5420
+ });
5421
+ continue;
5422
+ }
5423
+ }
5424
+ try {
5425
+ const { done, value } = await currentReader.read();
5426
+ if (done) {
5427
+ currentReader = void 0;
5428
+ if (complete) break;
5429
+ if (!receivedAnyEventThisAttempt) {
5430
+ attempt++;
5431
+ if (attempt >= maxRetries) {
5432
+ controller.error(
5433
+ new Error(
5434
+ "google.interactions: SSE stream closed without producing any events."
5435
+ )
5436
+ );
5437
+ return;
5438
+ }
5439
+ await delay4(retryDelayMs * attempt, {
5440
+ abortSignal: effectiveSignal
5441
+ });
5442
+ } else {
5443
+ attempt = 0;
5444
+ }
5445
+ continue;
5446
+ }
5447
+ receivedAnyEventThisAttempt = true;
5448
+ if (value.success) {
5449
+ const ev = value.value;
5450
+ if (typeof ev.event_id === "string" && ev.event_id.length > 0) {
5451
+ lastEventId = ev.event_id;
5452
+ }
5453
+ if (ev.event_type === "interaction.complete" || ev.event_type === "error") {
5454
+ complete = true;
5455
+ }
5456
+ }
5457
+ controller.enqueue(value);
5458
+ } catch (error) {
5459
+ if (isAbortError2(error) || effectiveSignal.aborted) {
5460
+ controller.error(error);
5461
+ return;
5462
+ }
5463
+ currentReader = void 0;
5464
+ attempt++;
5465
+ if (attempt >= maxRetries) {
5466
+ controller.error(error);
5467
+ return;
5468
+ }
5469
+ await delay4(retryDelayMs * attempt, {
5470
+ abortSignal: effectiveSignal
5471
+ });
5472
+ }
5473
+ }
5474
+ controller.close();
5475
+ } catch (error) {
5476
+ controller.error(error);
5477
+ } finally {
5478
+ if (abortSignal != null) {
5479
+ abortSignal.removeEventListener("abort", upstreamAbortHandler);
5480
+ }
5481
+ currentReader == null ? void 0 : currentReader.cancel().catch(() => {
5482
+ });
5483
+ currentReader = void 0;
5484
+ if (effectiveSignal.aborted && !complete) {
5485
+ await cancelGoogleInteraction({
5486
+ baseURL,
5487
+ interactionId,
5488
+ headers,
5489
+ fetch
5490
+ });
5491
+ }
5492
+ }
5493
+ },
5494
+ cancel() {
5495
+ internalAbort.abort();
5496
+ currentReader == null ? void 0 : currentReader.cancel().catch(() => {
5497
+ });
5498
+ currentReader = void 0;
5499
+ }
5500
+ });
5501
+ }
5502
+
5283
5503
  // src/interactions/synthesize-google-interactions-agent-stream.ts
5284
5504
  function synthesizeGoogleInteractionsAgentStream({
5285
5505
  response,
@@ -5608,7 +5828,7 @@ var GoogleInteractionsLanguageModel = class _GoogleInteractionsLanguageModel {
5608
5828
  var _a, _b, _c, _d, _e, _f;
5609
5829
  const { args, warnings, isAgent, pollingTimeoutMs } = await this.getArgs(options);
5610
5830
  const url = `${this.config.baseURL}/interactions`;
5611
- const mergedHeaders = combineHeaders6(
5831
+ const mergedHeaders = combineHeaders7(
5612
5832
  this.config.headers ? await resolve5(this.config.headers) : void 0,
5613
5833
  options.headers
5614
5834
  );
@@ -5688,12 +5908,12 @@ var GoogleInteractionsLanguageModel = class _GoogleInteractionsLanguageModel {
5688
5908
  var _a;
5689
5909
  const { args, warnings, isAgent, pollingTimeoutMs } = await this.getArgs(options);
5690
5910
  const url = `${this.config.baseURL}/interactions`;
5691
- const mergedHeaders = combineHeaders6(
5911
+ const mergedHeaders = combineHeaders7(
5692
5912
  this.config.headers ? await resolve5(this.config.headers) : void 0,
5693
5913
  options.headers
5694
5914
  );
5695
5915
  if (isAgent) {
5696
- return this.doStreamAgent({
5916
+ return this.doStreamBackground({
5697
5917
  args,
5698
5918
  warnings,
5699
5919
  url,
@@ -5708,7 +5928,7 @@ var GoogleInteractionsLanguageModel = class _GoogleInteractionsLanguageModel {
5708
5928
  headers: mergedHeaders,
5709
5929
  body,
5710
5930
  failedResponseHandler: googleFailedResponseHandler,
5711
- successfulResponseHandler: createEventSourceResponseHandler2(
5931
+ successfulResponseHandler: createEventSourceResponseHandler3(
5712
5932
  googleInteractionsEventSchema
5713
5933
  ),
5714
5934
  abortSignal: options.abortSignal,
@@ -5728,26 +5948,24 @@ var GoogleInteractionsLanguageModel = class _GoogleInteractionsLanguageModel {
5728
5948
  };
5729
5949
  }
5730
5950
  /*
5731
- * Drive the streaming surface for agent calls. Agent calls require
5951
+ * Drive the streaming surface for agent calls. Agents require
5732
5952
  * `background: true`, which is incompatible with `stream: true` on POST.
5733
5953
  *
5734
- * In principle the API also exposes `GET /interactions/{id}?stream=true`
5735
- * to replay events as the agent runs. In practice the connection is
5736
- * idle for long stretches while the agent thinks (deep-research can run
5737
- * for a minute or more between SSE events), and undici's default body
5738
- * timeout terminates the request mid-flight with `UND_ERR_BODY_TIMEOUT`.
5739
- * Tuning the timeout per-call would require the caller to thread an
5740
- * `undici.Agent` through `fetch`, which contradicts the AI SDK's
5741
- * pluggable-fetch contract.
5954
+ * Approach:
5955
+ * 1. POST `/interactions` with `background: true`. The response includes
5956
+ * the interaction id and an initial (usually non-terminal) status.
5957
+ * 2. If the POST status is already terminal (rare), synthesize a stream
5958
+ * from the polled outputs and we're done.
5959
+ * 3. Otherwise open `GET /interactions/{id}?stream=true` and pipe the
5960
+ * SSE events through `buildGoogleInteractionsStreamTransform` so the
5961
+ * consumer receives text deltas / thinking summaries / tool events as
5962
+ * they happen instead of all at once at the end.
5742
5963
  *
5743
- * We therefore drive `doStream` exactly like `doGenerate` for agents:
5744
- * POST with `background: true`, poll `GET /interactions/{id}` until
5745
- * terminal, then synthesize the stream from the final outputs. The
5746
- * user-facing surface stays identical -- text-start / text-delta /
5747
- * text-end / finish parts arrive in the same order as a true SSE
5748
- * response, just buffered until the agent completes.
5964
+ * The SSE connection can drop while the agent idles between events
5965
+ * (`UND_ERR_BODY_TIMEOUT`); `streamGoogleInteractionEvents` handles the
5966
+ * reconnect-with-`last_event_id` loop transparently.
5749
5967
  */
5750
- async doStreamAgent({
5968
+ async doStreamBackground({
5751
5969
  args,
5752
5970
  warnings,
5753
5971
  url,
@@ -5767,34 +5985,44 @@ var GoogleInteractionsLanguageModel = class _GoogleInteractionsLanguageModel {
5767
5985
  abortSignal: options.abortSignal,
5768
5986
  fetch: this.config.fetch
5769
5987
  });
5770
- let { responseHeaders: postHeaders, value: postResponse } = postResult;
5988
+ const { responseHeaders: postHeaders, value: postResponse } = postResult;
5771
5989
  const interactionId = postResponse.id;
5772
5990
  if (interactionId == null || interactionId.length === 0) {
5773
5991
  throw new Error(
5774
- "google.interactions: agent POST response did not include an interaction id; cannot poll for the agent result."
5992
+ "google.interactions: background POST response did not include an interaction id; cannot stream the result."
5775
5993
  );
5776
5994
  }
5777
- if (!isTerminalStatus(postResponse.status)) {
5778
- const polled = await pollGoogleInteractionUntilTerminal({
5779
- baseURL: this.config.baseURL,
5780
- interactionId,
5781
- headers: mergedHeaders,
5782
- fetch: this.config.fetch,
5783
- abortSignal: options.abortSignal,
5784
- timeoutMs: pollingTimeoutMs
5995
+ const headerServiceTier = postHeaders == null ? void 0 : postHeaders["x-gemini-service-tier"];
5996
+ if (isTerminalStatus(postResponse.status)) {
5997
+ const synthesized = synthesizeGoogleInteractionsAgentStream({
5998
+ response: postResponse,
5999
+ warnings,
6000
+ generateId: (_a = this.config.generateId) != null ? _a : defaultGenerateId2,
6001
+ includeRawChunks: options.includeRawChunks,
6002
+ headerServiceTier
5785
6003
  });
5786
- postResponse = polled.response;
5787
- postHeaders = (_a = polled.responseHeaders) != null ? _a : postHeaders;
6004
+ return {
6005
+ stream: synthesized,
6006
+ request: { body: args },
6007
+ response: { headers: postHeaders }
6008
+ };
5788
6009
  }
5789
- const stream = synthesizeGoogleInteractionsAgentStream({
5790
- response: postResponse,
6010
+ void pollingTimeoutMs;
6011
+ const events = streamGoogleInteractionEvents({
6012
+ baseURL: this.config.baseURL,
6013
+ interactionId,
6014
+ headers: mergedHeaders,
6015
+ fetch: this.config.fetch,
6016
+ abortSignal: options.abortSignal
6017
+ });
6018
+ const transform = buildGoogleInteractionsStreamTransform({
5791
6019
  warnings,
5792
6020
  generateId: (_b = this.config.generateId) != null ? _b : defaultGenerateId2,
5793
6021
  includeRawChunks: options.includeRawChunks,
5794
- headerServiceTier: postHeaders == null ? void 0 : postHeaders["x-gemini-service-tier"]
6022
+ serviceTier: headerServiceTier
5795
6023
  });
5796
6024
  return {
5797
- stream,
6025
+ stream: events.pipeThrough(transform),
5798
6026
  request: { body: args },
5799
6027
  response: { headers: postHeaders }
5800
6028
  };
@@ -5814,7 +6042,7 @@ function createGoogle(options = {}) {
5814
6042
  var _a, _b;
5815
6043
  const baseURL = (_a = withoutTrailingSlash(options.baseURL)) != null ? _a : "https://generativelanguage.googleapis.com/v1beta";
5816
6044
  const providerName = (_b = options.name) != null ? _b : "google.generative-ai";
5817
- const getHeaders = () => withUserAgentSuffix(
6045
+ const getHeaders = () => withUserAgentSuffix2(
5818
6046
  {
5819
6047
  "x-goog-api-key": loadApiKey({
5820
6048
  apiKey: options.apiKey,