@ai-sdk/groq 4.0.0-beta.3 → 4.0.0-beta.31

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.
@@ -1,25 +1,29 @@
1
1
  import {
2
- InvalidResponseDataError,
3
- LanguageModelV3,
4
- LanguageModelV3CallOptions,
5
- LanguageModelV3Content,
6
- LanguageModelV3FinishReason,
7
- LanguageModelV3GenerateResult,
8
- LanguageModelV3StreamPart,
9
- LanguageModelV3StreamResult,
10
- SharedV3ProviderMetadata,
11
- SharedV3Warning,
2
+ LanguageModelV4,
3
+ LanguageModelV4CallOptions,
4
+ LanguageModelV4Content,
5
+ LanguageModelV4FinishReason,
6
+ LanguageModelV4GenerateResult,
7
+ LanguageModelV4StreamPart,
8
+ LanguageModelV4StreamResult,
9
+ SharedV4ProviderMetadata,
10
+ SharedV4Warning,
12
11
  } from '@ai-sdk/provider';
13
12
  import {
14
13
  FetchFunction,
15
14
  ParseResult,
15
+ StreamingToolCallTracker,
16
16
  combineHeaders,
17
17
  createEventSourceResponseHandler,
18
18
  createJsonResponseHandler,
19
19
  generateId,
20
- isParsableJson,
20
+ isCustomReasoning,
21
+ mapReasoningToProviderEffort,
21
22
  parseProviderOptions,
22
23
  postJsonToApi,
24
+ serializeModelOptions,
25
+ WORKFLOW_SERIALIZE,
26
+ WORKFLOW_DESERIALIZE,
23
27
  } from '@ai-sdk/provider-utils';
24
28
  import { z } from 'zod/v4';
25
29
  import { convertGroqUsage } from './convert-groq-usage';
@@ -32,13 +36,13 @@ import { mapGroqFinishReason } from './map-groq-finish-reason';
32
36
 
33
37
  type GroqChatConfig = {
34
38
  provider: string;
35
- headers: () => Record<string, string | undefined>;
39
+ headers?: () => Record<string, string | undefined>;
36
40
  url: (options: { modelId: string; path: string }) => string;
37
41
  fetch?: FetchFunction;
38
42
  };
39
43
 
40
- export class GroqChatLanguageModel implements LanguageModelV3 {
41
- readonly specificationVersion = 'v3';
44
+ export class GroqChatLanguageModel implements LanguageModelV4 {
45
+ readonly specificationVersion = 'v4';
42
46
 
43
47
  readonly modelId: GroqChatModelId;
44
48
 
@@ -48,6 +52,20 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
48
52
 
49
53
  private readonly config: GroqChatConfig;
50
54
 
55
+ static [WORKFLOW_SERIALIZE](model: GroqChatLanguageModel) {
56
+ return serializeModelOptions({
57
+ modelId: model.modelId,
58
+ config: model.config,
59
+ });
60
+ }
61
+
62
+ static [WORKFLOW_DESERIALIZE](options: {
63
+ modelId: GroqChatModelId;
64
+ config: GroqChatConfig;
65
+ }) {
66
+ return new GroqChatLanguageModel(options.modelId, options.config);
67
+ }
68
+
51
69
  constructor(modelId: GroqChatModelId, config: GroqChatConfig) {
52
70
  this.modelId = modelId;
53
71
  this.config = config;
@@ -68,14 +86,12 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
68
86
  stopSequences,
69
87
  responseFormat,
70
88
  seed,
71
- stream,
89
+ reasoning,
72
90
  tools,
73
91
  toolChoice,
74
92
  providerOptions,
75
- }: LanguageModelV3CallOptions & {
76
- stream: boolean;
77
- }) {
78
- const warnings: SharedV3Warning[] = [];
93
+ }: LanguageModelV4CallOptions) {
94
+ const warnings: SharedV4Warning[] = [];
79
95
 
80
96
  const groqOptions = await parseProviderOptions({
81
97
  provider: 'groq',
@@ -145,7 +161,21 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
145
161
 
146
162
  // provider options:
147
163
  reasoning_format: groqOptions?.reasoningFormat,
148
- reasoning_effort: groqOptions?.reasoningEffort,
164
+ reasoning_effort:
165
+ groqOptions?.reasoningEffort ??
166
+ (isCustomReasoning(reasoning) && reasoning !== 'none'
167
+ ? mapReasoningToProviderEffort({
168
+ reasoning,
169
+ effortMap: {
170
+ minimal: 'low',
171
+ low: 'low',
172
+ medium: 'medium',
173
+ high: 'high',
174
+ xhigh: 'high',
175
+ },
176
+ warnings,
177
+ })
178
+ : undefined),
149
179
  service_tier: groqOptions?.serviceTier,
150
180
 
151
181
  // messages:
@@ -160,12 +190,9 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
160
190
  }
161
191
 
162
192
  async doGenerate(
163
- options: LanguageModelV3CallOptions,
164
- ): Promise<LanguageModelV3GenerateResult> {
165
- const { args, warnings } = await this.getArgs({
166
- ...options,
167
- stream: false,
168
- });
193
+ options: LanguageModelV4CallOptions,
194
+ ): Promise<LanguageModelV4GenerateResult> {
195
+ const { args, warnings } = await this.getArgs(options);
169
196
 
170
197
  const body = JSON.stringify(args);
171
198
 
@@ -178,7 +205,7 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
178
205
  path: '/chat/completions',
179
206
  modelId: this.modelId,
180
207
  }),
181
- headers: combineHeaders(this.config.headers(), options.headers),
208
+ headers: combineHeaders(this.config.headers?.(), options.headers),
182
209
  body: args,
183
210
  failedResponseHandler: groqFailedResponseHandler,
184
211
  successfulResponseHandler: createJsonResponseHandler(
@@ -189,7 +216,7 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
189
216
  });
190
217
 
191
218
  const choice = response.choices[0];
192
- const content: Array<LanguageModelV3Content> = [];
219
+ const content: Array<LanguageModelV4Content> = [];
193
220
 
194
221
  // text content:
195
222
  const text = choice.message.content;
@@ -236,22 +263,19 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
236
263
  }
237
264
 
238
265
  async doStream(
239
- options: LanguageModelV3CallOptions,
240
- ): Promise<LanguageModelV3StreamResult> {
241
- const { args, warnings } = await this.getArgs({ ...options, stream: true });
266
+ options: LanguageModelV4CallOptions,
267
+ ): Promise<LanguageModelV4StreamResult> {
268
+ const { args, warnings } = await this.getArgs(options);
242
269
 
243
- const body = JSON.stringify({ ...args, stream: true });
270
+ const body = { ...args, stream: true };
244
271
 
245
272
  const { responseHeaders, value: response } = await postJsonToApi({
246
273
  url: this.config.url({
247
274
  path: '/chat/completions',
248
275
  modelId: this.modelId,
249
276
  }),
250
- headers: combineHeaders(this.config.headers(), options.headers),
251
- body: {
252
- ...args,
253
- stream: true,
254
- },
277
+ headers: combineHeaders(this.config.headers?.(), options.headers),
278
+ body,
255
279
  failedResponseHandler: groqFailedResponseHandler,
256
280
  successfulResponseHandler:
257
281
  createEventSourceResponseHandler(groqChatChunkSchema),
@@ -259,17 +283,9 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
259
283
  fetch: this.config.fetch,
260
284
  });
261
285
 
262
- const toolCalls: Array<{
263
- id: string;
264
- type: 'function';
265
- function: {
266
- name: string;
267
- arguments: string;
268
- };
269
- hasFinished: boolean;
270
- }> = [];
271
-
272
- let finishReason: LanguageModelV3FinishReason = {
286
+ let toolCallTracker: StreamingToolCallTracker;
287
+
288
+ let finishReason: LanguageModelV4FinishReason = {
273
289
  unified: 'other',
274
290
  raw: undefined,
275
291
  };
@@ -295,14 +311,18 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
295
311
  let isActiveText = false;
296
312
  let isActiveReasoning = false;
297
313
 
298
- let providerMetadata: SharedV3ProviderMetadata | undefined;
314
+ let providerMetadata: SharedV4ProviderMetadata | undefined;
299
315
  return {
300
316
  stream: response.pipeThrough(
301
317
  new TransformStream<
302
318
  ParseResult<z.infer<typeof groqChatChunkSchema>>,
303
- LanguageModelV3StreamPart
319
+ LanguageModelV4StreamPart
304
320
  >({
305
321
  start(controller) {
322
+ toolCallTracker = new StreamingToolCallTracker(controller, {
323
+ generateId,
324
+ typeValidation: 'required',
325
+ });
306
326
  controller.enqueue({ type: 'stream-start', warnings });
307
327
  },
308
328
 
@@ -411,120 +431,7 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
411
431
  }
412
432
 
413
433
  for (const toolCallDelta of delta.tool_calls) {
414
- const index = toolCallDelta.index;
415
-
416
- if (toolCalls[index] == null) {
417
- if (toolCallDelta.type !== 'function') {
418
- throw new InvalidResponseDataError({
419
- data: toolCallDelta,
420
- message: `Expected 'function' type.`,
421
- });
422
- }
423
-
424
- if (toolCallDelta.id == null) {
425
- throw new InvalidResponseDataError({
426
- data: toolCallDelta,
427
- message: `Expected 'id' to be a string.`,
428
- });
429
- }
430
-
431
- if (toolCallDelta.function?.name == null) {
432
- throw new InvalidResponseDataError({
433
- data: toolCallDelta,
434
- message: `Expected 'function.name' to be a string.`,
435
- });
436
- }
437
-
438
- controller.enqueue({
439
- type: 'tool-input-start',
440
- id: toolCallDelta.id,
441
- toolName: toolCallDelta.function.name,
442
- });
443
-
444
- toolCalls[index] = {
445
- id: toolCallDelta.id,
446
- type: 'function',
447
- function: {
448
- name: toolCallDelta.function.name,
449
- arguments: toolCallDelta.function.arguments ?? '',
450
- },
451
- hasFinished: false,
452
- };
453
-
454
- const toolCall = toolCalls[index];
455
-
456
- if (
457
- toolCall.function?.name != null &&
458
- toolCall.function?.arguments != null
459
- ) {
460
- // send delta if the argument text has already started:
461
- if (toolCall.function.arguments.length > 0) {
462
- controller.enqueue({
463
- type: 'tool-input-delta',
464
- id: toolCall.id,
465
- delta: toolCall.function.arguments,
466
- });
467
- }
468
-
469
- // check if tool call is complete
470
- // (some providers send the full tool call in one chunk):
471
- if (isParsableJson(toolCall.function.arguments)) {
472
- controller.enqueue({
473
- type: 'tool-input-end',
474
- id: toolCall.id,
475
- });
476
-
477
- controller.enqueue({
478
- type: 'tool-call',
479
- toolCallId: toolCall.id ?? generateId(),
480
- toolName: toolCall.function.name,
481
- input: toolCall.function.arguments,
482
- });
483
- toolCall.hasFinished = true;
484
- }
485
- }
486
-
487
- continue;
488
- }
489
-
490
- // existing tool call, merge if not finished
491
- const toolCall = toolCalls[index];
492
-
493
- if (toolCall.hasFinished) {
494
- continue;
495
- }
496
-
497
- if (toolCallDelta.function?.arguments != null) {
498
- toolCall.function!.arguments +=
499
- toolCallDelta.function?.arguments ?? '';
500
- }
501
-
502
- // send delta
503
- controller.enqueue({
504
- type: 'tool-input-delta',
505
- id: toolCall.id,
506
- delta: toolCallDelta.function.arguments ?? '',
507
- });
508
-
509
- // check if tool call is complete
510
- if (
511
- toolCall.function?.name != null &&
512
- toolCall.function?.arguments != null &&
513
- isParsableJson(toolCall.function.arguments)
514
- ) {
515
- controller.enqueue({
516
- type: 'tool-input-end',
517
- id: toolCall.id,
518
- });
519
-
520
- controller.enqueue({
521
- type: 'tool-call',
522
- toolCallId: toolCall.id ?? generateId(),
523
- toolName: toolCall.function.name,
524
- input: toolCall.function.arguments,
525
- });
526
- toolCall.hasFinished = true;
527
- }
434
+ toolCallTracker.processDelta(toolCallDelta);
528
435
  }
529
436
  }
530
437
  },
@@ -538,6 +445,8 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
538
445
  controller.enqueue({ type: 'text-end', id: 'txt-0' });
539
446
  }
540
447
 
448
+ toolCallTracker.flush();
449
+
541
450
  controller.enqueue({
542
451
  type: 'finish',
543
452
  finishReason,
@@ -547,7 +456,7 @@ export class GroqChatLanguageModel implements LanguageModelV3 {
547
456
  },
548
457
  }),
549
458
  ),
550
- request: { body },
459
+ request: { body: JSON.stringify(body) },
551
460
  response: { headers: responseHeaders },
552
461
  };
553
462
  }
@@ -67,12 +67,13 @@ export const groqLanguageModelOptions = z.object({
67
67
  /**
68
68
  * Service tier for the request.
69
69
  * - 'on_demand': Default tier with consistent performance and fairness
70
+ * - 'performance': Prioritized tier for latency-sensitive workloads
70
71
  * - 'flex': Higher throughput tier optimized for workloads that can handle occasional request failures
71
72
  * - 'auto': Uses on_demand rate limits, then falls back to flex tier if exceeded
72
73
  *
73
74
  * @default 'on_demand'
74
75
  */
75
- serviceTier: z.enum(['on_demand', 'flex', 'auto']).optional(),
76
+ serviceTier: z.enum(['on_demand', 'performance', 'flex', 'auto']).optional(),
76
77
  });
77
78
 
78
79
  export type GroqLanguageModelOptions = z.infer<typeof groqLanguageModelOptions>;
@@ -3,7 +3,7 @@ import { FetchFunction } from '@ai-sdk/provider-utils';
3
3
  export type GroqConfig = {
4
4
  provider: string;
5
5
  url: (options: { modelId: string; path: string }) => string;
6
- headers: () => Record<string, string | undefined>;
6
+ headers?: () => Record<string, string | undefined>;
7
7
  fetch?: FetchFunction;
8
8
  generateId?: () => string;
9
9
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
- LanguageModelV3CallOptions,
3
- SharedV3Warning,
2
+ LanguageModelV4CallOptions,
3
+ SharedV4Warning,
4
4
  UnsupportedFunctionalityError,
5
5
  } from '@ai-sdk/provider';
6
6
  import {
@@ -14,8 +14,8 @@ export function prepareTools({
14
14
  toolChoice,
15
15
  modelId,
16
16
  }: {
17
- tools: LanguageModelV3CallOptions['tools'];
18
- toolChoice?: LanguageModelV3CallOptions['toolChoice'];
17
+ tools: LanguageModelV4CallOptions['tools'];
18
+ toolChoice?: LanguageModelV4CallOptions['toolChoice'];
19
19
  modelId: GroqChatModelId;
20
20
  }): {
21
21
  tools:
@@ -40,12 +40,12 @@ export function prepareTools({
40
40
  | 'none'
41
41
  | 'required'
42
42
  | undefined;
43
- toolWarnings: SharedV3Warning[];
43
+ toolWarnings: SharedV4Warning[];
44
44
  } {
45
45
  // when the tools array is empty, change it to undefined to prevent errors:
46
46
  tools = tools?.length ? tools : undefined;
47
47
 
48
- const toolWarnings: SharedV3Warning[] = [];
48
+ const toolWarnings: SharedV4Warning[] = [];
49
49
 
50
50
  if (tools == null) {
51
51
  return { tools: undefined, toolChoice: undefined, toolWarnings };
@@ -1,8 +1,8 @@
1
1
  import {
2
- LanguageModelV3,
2
+ LanguageModelV4,
3
3
  NoSuchModelError,
4
- ProviderV3,
5
- TranscriptionModelV3,
4
+ ProviderV4,
5
+ TranscriptionModelV4,
6
6
  } from '@ai-sdk/provider';
7
7
  import {
8
8
  FetchFunction,
@@ -17,21 +17,21 @@ import { GroqTranscriptionModel } from './groq-transcription-model';
17
17
 
18
18
  import { groqTools } from './groq-tools';
19
19
  import { VERSION } from './version';
20
- export interface GroqProvider extends ProviderV3 {
20
+ export interface GroqProvider extends ProviderV4 {
21
21
  /**
22
22
  * Creates a model for text generation.
23
23
  */
24
- (modelId: GroqChatModelId): LanguageModelV3;
24
+ (modelId: GroqChatModelId): LanguageModelV4;
25
25
 
26
26
  /**
27
27
  * Creates an Groq chat model for text generation.
28
28
  */
29
- languageModel(modelId: GroqChatModelId): LanguageModelV3;
29
+ languageModel(modelId: GroqChatModelId): LanguageModelV4;
30
30
 
31
31
  /**
32
32
  * Creates a model for transcription.
33
33
  */
34
- transcription(modelId: GroqTranscriptionModelId): TranscriptionModelV3;
34
+ transcription(modelId: GroqTranscriptionModelId): TranscriptionModelV4;
35
35
 
36
36
  /**
37
37
  * Tools provided by Groq.
@@ -118,7 +118,7 @@ export function createGroq(options: GroqProviderSettings = {}): GroqProvider {
118
118
  return createLanguageModel(modelId);
119
119
  };
120
120
 
121
- provider.specificationVersion = 'v3' as const;
121
+ provider.specificationVersion = 'v4' as const;
122
122
  provider.languageModel = createLanguageModel;
123
123
  provider.chat = createChatModel;
124
124
 
@@ -1,4 +1,4 @@
1
- import { TranscriptionModelV3, SharedV3Warning } from '@ai-sdk/provider';
1
+ import { TranscriptionModelV4, SharedV4Warning } from '@ai-sdk/provider';
2
2
  import {
3
3
  combineHeaders,
4
4
  convertBase64ToUint8Array,
@@ -6,6 +6,9 @@ import {
6
6
  mediaTypeToExtension,
7
7
  parseProviderOptions,
8
8
  postFormDataToApi,
9
+ serializeModelOptions,
10
+ WORKFLOW_SERIALIZE,
11
+ WORKFLOW_DESERIALIZE,
9
12
  } from '@ai-sdk/provider-utils';
10
13
  import { z } from 'zod/v4';
11
14
  import { GroqConfig } from './groq-config';
@@ -22,13 +25,27 @@ interface GroqTranscriptionModelConfig extends GroqConfig {
22
25
  };
23
26
  }
24
27
 
25
- export class GroqTranscriptionModel implements TranscriptionModelV3 {
26
- readonly specificationVersion = 'v3';
28
+ export class GroqTranscriptionModel implements TranscriptionModelV4 {
29
+ readonly specificationVersion = 'v4';
27
30
 
28
31
  get provider(): string {
29
32
  return this.config.provider;
30
33
  }
31
34
 
35
+ static [WORKFLOW_SERIALIZE](model: GroqTranscriptionModel) {
36
+ return serializeModelOptions({
37
+ modelId: model.modelId,
38
+ config: model.config,
39
+ });
40
+ }
41
+
42
+ static [WORKFLOW_DESERIALIZE](options: {
43
+ modelId: GroqTranscriptionModelId;
44
+ config: GroqTranscriptionModelConfig;
45
+ }) {
46
+ return new GroqTranscriptionModel(options.modelId, options.config);
47
+ }
48
+
32
49
  constructor(
33
50
  readonly modelId: GroqTranscriptionModelId,
34
51
  private readonly config: GroqTranscriptionModelConfig,
@@ -38,8 +55,8 @@ export class GroqTranscriptionModel implements TranscriptionModelV3 {
38
55
  audio,
39
56
  mediaType,
40
57
  providerOptions,
41
- }: Parameters<TranscriptionModelV3['doGenerate']>[0]) {
42
- const warnings: SharedV3Warning[] = [];
58
+ }: Parameters<TranscriptionModelV4['doGenerate']>[0]) {
59
+ const warnings: SharedV4Warning[] = [];
43
60
 
44
61
  // Parse provider options
45
62
  const groqOptions = await parseProviderOptions({
@@ -101,8 +118,8 @@ export class GroqTranscriptionModel implements TranscriptionModelV3 {
101
118
  }
102
119
 
103
120
  async doGenerate(
104
- options: Parameters<TranscriptionModelV3['doGenerate']>[0],
105
- ): Promise<Awaited<ReturnType<TranscriptionModelV3['doGenerate']>>> {
121
+ options: Parameters<TranscriptionModelV4['doGenerate']>[0],
122
+ ): Promise<Awaited<ReturnType<TranscriptionModelV4['doGenerate']>>> {
106
123
  const currentDate = this.config._internal?.currentDate?.() ?? new Date();
107
124
  const { formData, warnings } = await this.getArgs(options);
108
125
 
@@ -115,7 +132,7 @@ export class GroqTranscriptionModel implements TranscriptionModelV3 {
115
132
  path: '/audio/transcriptions',
116
133
  modelId: this.modelId,
117
134
  }),
118
- headers: combineHeaders(this.config.headers(), options.headers),
135
+ headers: combineHeaders(this.config.headers?.(), options.headers),
119
136
  formData,
120
137
  failedResponseHandler: groqFailedResponseHandler,
121
138
  successfulResponseHandler: createJsonResponseHandler(
@@ -1,8 +1,8 @@
1
- import { LanguageModelV3FinishReason } from '@ai-sdk/provider';
1
+ import { LanguageModelV4FinishReason } from '@ai-sdk/provider';
2
2
 
3
3
  export function mapGroqFinishReason(
4
4
  finishReason: string | null | undefined,
5
- ): LanguageModelV3FinishReason['unified'] {
5
+ ): LanguageModelV4FinishReason['unified'] {
6
6
  switch (finishReason) {
7
7
  case 'stop':
8
8
  return 'stop';
@@ -1,4 +1,8 @@
1
- import { createProviderToolFactory } from '@ai-sdk/provider-utils';
1
+ import {
2
+ createProviderExecutedToolFactory,
3
+ lazySchema,
4
+ zodSchema,
5
+ } from '@ai-sdk/provider-utils';
2
6
  import { z } from 'zod/v4';
3
7
 
4
8
  /**
@@ -13,16 +17,20 @@ import { z } from 'zod/v4';
13
17
  *
14
18
  * @see https://console.groq.com/docs/browser-search
15
19
  */
16
- export const browserSearch = createProviderToolFactory<
20
+ export const browserSearch = createProviderExecutedToolFactory<
17
21
  {
18
22
  // Browser search doesn't take input parameters - it's controlled by the prompt
19
23
  // The tool is activated automatically when included in the tools array
20
24
  },
25
+ {
26
+ // Browser search doesn't have any output parameters
27
+ },
21
28
  {
22
29
  // No configuration options needed - the tool works automatically
23
30
  // when included in the tools array for supported models
24
31
  }
25
32
  >({
26
33
  id: 'groq.browser_search',
27
- inputSchema: z.object({}),
34
+ inputSchema: lazySchema(() => zodSchema(z.object({}))),
35
+ outputSchema: lazySchema(() => zodSchema(z.object({}))),
28
36
  });