@tanstack/ai-groq 0.1.11 → 0.2.1
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/text.d.ts +30 -68
- package/dist/esm/adapters/text.js +31 -383
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/message-types.d.ts +9 -96
- package/dist/esm/text/text-provider-options.d.ts +1 -26
- package/dist/esm/utils/client.d.ts +6 -8
- package/dist/esm/utils/client.js +15 -11
- package/dist/esm/utils/client.js.map +1 -1
- package/dist/esm/utils/index.d.ts +2 -1
- package/dist/esm/utils/schema-converter.js.map +1 -1
- package/package.json +4 -4
- package/src/adapters/text.ts +63 -552
- package/src/message-types.ts +9 -129
- package/src/text/text-provider-options.ts +0 -36
- package/src/utils/client.ts +18 -15
- package/src/utils/index.ts +2 -2
- package/src/utils/schema-converter.ts +2 -2
- package/dist/esm/text/text-provider-options.js +0 -6
- package/dist/esm/text/text-provider-options.js.map +0 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { default as OpenAI } from 'openai';
|
|
2
|
+
import { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base';
|
|
3
|
+
import { Modality, TextOptions } from '@tanstack/ai';
|
|
2
4
|
import { GROQ_CHAT_MODELS, GroqChatModelToolCapabilitiesByName, ResolveInputModalities, ResolveProviderOptions } from '../model-meta.js';
|
|
3
|
-
import { Modality, StreamChunk, TextOptions } from '@tanstack/ai';
|
|
4
|
-
import { ExternalTextProviderOptions } from '../text/text-provider-options.js';
|
|
5
5
|
import { GroqMessageMetadataByModality } from '../message-types.js';
|
|
6
6
|
import { GroqClientConfig } from '../utils.js';
|
|
7
|
-
type GroqTextProviderOptions = ExternalTextProviderOptions;
|
|
8
7
|
type ResolveToolCapabilities<TModel extends string> = TModel extends keyof GroqChatModelToolCapabilitiesByName ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]> : readonly [];
|
|
9
8
|
/**
|
|
10
9
|
* Configuration for Groq text adapter
|
|
@@ -12,96 +11,59 @@ type ResolveToolCapabilities<TModel extends string> = TModel extends keyof GroqC
|
|
|
12
11
|
export interface GroqTextConfig extends GroqClientConfig {
|
|
13
12
|
}
|
|
14
13
|
/**
|
|
15
|
-
*
|
|
14
|
+
* Re-export of the public provider options type
|
|
16
15
|
*/
|
|
17
16
|
export type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options.js';
|
|
18
17
|
/**
|
|
19
18
|
* Groq Text (Chat) Adapter
|
|
20
19
|
*
|
|
21
|
-
* Tree-shakeable adapter for Groq chat/text completion
|
|
22
|
-
*
|
|
20
|
+
* Tree-shakeable adapter for Groq chat/text completion. Groq exposes an
|
|
21
|
+
* OpenAI-compatible Chat Completions endpoint at `/openai/v1`, so we drive
|
|
22
|
+
* it with the OpenAI SDK via a `baseURL` override (the same pattern as
|
|
23
|
+
* `ai-grok`).
|
|
24
|
+
*
|
|
25
|
+
* Quirk: when usage is present on a stream, Groq historically delivered it
|
|
26
|
+
* under `chunk.x_groq.usage` rather than `chunk.usage`. The override below
|
|
27
|
+
* promotes it to the standard location so the base's RUN_FINISHED usage
|
|
28
|
+
* accounting works unchanged.
|
|
23
29
|
*/
|
|
24
|
-
export declare class GroqTextAdapter<TModel extends (typeof GROQ_CHAT_MODELS)[number], TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>, TInputModalities extends ReadonlyArray<Modality> = ResolveInputModalities<TModel>, TToolCapabilities extends ReadonlyArray<string> = ResolveToolCapabilities<TModel>> extends
|
|
30
|
+
export declare class GroqTextAdapter<TModel extends (typeof GROQ_CHAT_MODELS)[number], TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>, TInputModalities extends ReadonlyArray<Modality> = ResolveInputModalities<TModel>, TToolCapabilities extends ReadonlyArray<string> = ResolveToolCapabilities<TModel>> extends OpenAIBaseChatCompletionsTextAdapter<TModel, TProviderOptions, TInputModalities, GroqMessageMetadataByModality, TToolCapabilities> {
|
|
25
31
|
readonly kind: "text";
|
|
26
32
|
readonly name: "groq";
|
|
27
|
-
private client;
|
|
28
33
|
constructor(config: GroqTextConfig, model: TModel);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* - Optional fields should have null added to their type union
|
|
37
|
-
* - additionalProperties must be false for all objects
|
|
38
|
-
*
|
|
39
|
-
* The outputSchema is already JSON Schema (converted in the ai layer).
|
|
40
|
-
* We apply Groq-specific transformations for structured output compatibility.
|
|
41
|
-
*/
|
|
42
|
-
structuredOutput(options: StructuredOutputOptions<GroqTextProviderOptions>): Promise<StructuredOutputResult<unknown>>;
|
|
43
|
-
/**
|
|
44
|
-
* Processes streaming chunks from the Groq API and yields AG-UI stream events.
|
|
45
|
-
* Handles text content deltas, tool call assembly, and lifecycle events.
|
|
46
|
-
*/
|
|
47
|
-
private processGroqStreamChunks;
|
|
48
|
-
/**
|
|
49
|
-
* Maps common TextOptions to Groq-specific Chat Completions request parameters.
|
|
50
|
-
*/
|
|
51
|
-
private mapTextOptionsToGroq;
|
|
34
|
+
protected makeStructuredOutputCompatible(schema: Record<string, any>, originalRequired?: Array<string>): Record<string, any>;
|
|
35
|
+
protected processStreamChunks(stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>, options: TextOptions, aguiState: {
|
|
36
|
+
runId: string;
|
|
37
|
+
threadId: string;
|
|
38
|
+
messageId: string;
|
|
39
|
+
hasEmittedRunStarted: boolean;
|
|
40
|
+
}): AsyncGenerator<import('@tanstack/ai').AGUIEvent, void, any>;
|
|
52
41
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
42
|
+
* Surfaces Groq's reasoning deltas during streaming structured output.
|
|
43
|
+
* Groq emits `delta.reasoning` (or legacy `delta.reasoning_content`) on
|
|
44
|
+
* reasoning models when the caller sets `reasoning_format: 'parsed'` in
|
|
45
|
+
* modelOptions. The base's chatStream and structuredOutputStream both
|
|
46
|
+
* route reasoning through this hook.
|
|
55
47
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
* Handles backward compatibility with string content.
|
|
60
|
-
*/
|
|
61
|
-
private normalizeContent;
|
|
62
|
-
/**
|
|
63
|
-
* Extracts text content from a content value that may be string, null, or ContentPart array.
|
|
64
|
-
*/
|
|
65
|
-
private extractTextContent;
|
|
48
|
+
protected extractReasoning(chunk: OpenAI.Chat.Completions.ChatCompletionChunk): {
|
|
49
|
+
text: string;
|
|
50
|
+
} | undefined;
|
|
66
51
|
}
|
|
67
52
|
/**
|
|
68
53
|
* Creates a Groq text adapter with explicit API key.
|
|
69
|
-
* Type resolution happens here at the call site.
|
|
70
|
-
*
|
|
71
|
-
* @param model - The model name (e.g., 'llama-3.3-70b-versatile', 'openai/gpt-oss-120b')
|
|
72
|
-
* @param apiKey - Your Groq API key
|
|
73
|
-
* @param config - Optional additional configuration
|
|
74
|
-
* @returns Configured Groq text adapter instance with resolved types
|
|
75
54
|
*
|
|
76
55
|
* @example
|
|
77
56
|
* ```typescript
|
|
78
57
|
* const adapter = createGroqText('llama-3.3-70b-versatile', "gsk_...");
|
|
79
|
-
* // adapter has type-safe providerOptions for llama-3.3-70b-versatile
|
|
80
58
|
* ```
|
|
81
59
|
*/
|
|
82
60
|
export declare function createGroqText<TModel extends (typeof GROQ_CHAT_MODELS)[number]>(model: TModel, apiKey: string, config?: Omit<GroqTextConfig, 'apiKey'>): GroqTextAdapter<TModel>;
|
|
83
61
|
/**
|
|
84
|
-
* Creates a Groq text adapter with
|
|
85
|
-
* Type resolution happens here at the call site.
|
|
86
|
-
*
|
|
87
|
-
* Looks for `GROQ_API_KEY` in:
|
|
88
|
-
* - `process.env` (Node.js)
|
|
89
|
-
* - `window.env` (Browser with injected env)
|
|
90
|
-
*
|
|
91
|
-
* @param model - The model name (e.g., 'llama-3.3-70b-versatile', 'openai/gpt-oss-120b')
|
|
92
|
-
* @param config - Optional configuration (excluding apiKey which is auto-detected)
|
|
93
|
-
* @returns Configured Groq text adapter instance with resolved types
|
|
94
|
-
* @throws Error if GROQ_API_KEY is not found in environment
|
|
62
|
+
* Creates a Groq text adapter with API key from `GROQ_API_KEY`.
|
|
95
63
|
*
|
|
96
64
|
* @example
|
|
97
65
|
* ```typescript
|
|
98
|
-
* // Automatically uses GROQ_API_KEY from environment
|
|
99
66
|
* const adapter = groqText('llama-3.3-70b-versatile');
|
|
100
|
-
*
|
|
101
|
-
* const stream = chat({
|
|
102
|
-
* adapter,
|
|
103
|
-
* messages: [{ role: "user", content: "Hello!" }]
|
|
104
|
-
* });
|
|
105
67
|
* ```
|
|
106
68
|
*/
|
|
107
69
|
export declare function groqText<TModel extends (typeof GROQ_CHAT_MODELS)[number]>(model: TModel, config?: Omit<GroqTextConfig, 'apiKey'>): GroqTextAdapter<TModel>;
|
|
@@ -1,399 +1,47 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
import { OpenAIBaseChatCompletionsTextAdapter } from "@tanstack/openai-base";
|
|
3
|
+
import { withGroqDefaults, getGroqApiKeyFromEnv } from "../utils/client.js";
|
|
3
4
|
import { makeGroqStructuredOutputCompatible } from "../utils/schema-converter.js";
|
|
4
|
-
|
|
5
|
-
import { createGroqClient, generateId, getGroqApiKeyFromEnv } from "../utils/client.js";
|
|
6
|
-
import { transformNullsToUndefined } from "@tanstack/ai-utils";
|
|
7
|
-
const asChunk = (chunk) => chunk;
|
|
8
|
-
class GroqTextAdapter extends BaseTextAdapter {
|
|
5
|
+
class GroqTextAdapter extends OpenAIBaseChatCompletionsTextAdapter {
|
|
9
6
|
constructor(config, model) {
|
|
10
|
-
super(
|
|
7
|
+
super(model, "groq", new OpenAI(withGroqDefaults(config)));
|
|
11
8
|
this.kind = "text";
|
|
12
9
|
this.name = "groq";
|
|
13
|
-
this.client = createGroqClient(config);
|
|
14
10
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const timestamp = Date.now();
|
|
18
|
-
const { logger } = options;
|
|
19
|
-
const aguiState = {
|
|
20
|
-
runId: options.runId ?? generateId(this.name),
|
|
21
|
-
threadId: options.threadId ?? generateId(this.name),
|
|
22
|
-
messageId: generateId(this.name),
|
|
23
|
-
timestamp,
|
|
24
|
-
hasEmittedRunStarted: false
|
|
25
|
-
};
|
|
26
|
-
try {
|
|
27
|
-
logger.request(
|
|
28
|
-
`activity=chat provider=groq model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,
|
|
29
|
-
{ provider: "groq", model: this.model }
|
|
30
|
-
);
|
|
31
|
-
const stream = await this.client.chat.completions.create({
|
|
32
|
-
...requestParams,
|
|
33
|
-
stream: true
|
|
34
|
-
});
|
|
35
|
-
yield* this.processGroqStreamChunks(stream, options, aguiState, logger);
|
|
36
|
-
} catch (error) {
|
|
37
|
-
const err = error;
|
|
38
|
-
if (!aguiState.hasEmittedRunStarted) {
|
|
39
|
-
aguiState.hasEmittedRunStarted = true;
|
|
40
|
-
yield asChunk({
|
|
41
|
-
type: "RUN_STARTED",
|
|
42
|
-
runId: aguiState.runId,
|
|
43
|
-
threadId: aguiState.threadId,
|
|
44
|
-
model: options.model,
|
|
45
|
-
timestamp
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
yield asChunk({
|
|
49
|
-
type: "RUN_ERROR",
|
|
50
|
-
runId: aguiState.runId,
|
|
51
|
-
model: options.model,
|
|
52
|
-
timestamp,
|
|
53
|
-
message: err.message || "Unknown error",
|
|
54
|
-
code: err.code,
|
|
55
|
-
error: {
|
|
56
|
-
message: err.message || "Unknown error",
|
|
57
|
-
code: err.code
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
logger.errors("groq.chatStream fatal", {
|
|
61
|
-
error,
|
|
62
|
-
source: "groq.chatStream"
|
|
63
|
-
});
|
|
64
|
-
}
|
|
11
|
+
makeStructuredOutputCompatible(schema, originalRequired) {
|
|
12
|
+
return makeGroqStructuredOutputCompatible(schema, originalRequired);
|
|
65
13
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
* - All properties must be in the `required` array
|
|
72
|
-
* - Optional fields should have null added to their type union
|
|
73
|
-
* - additionalProperties must be false for all objects
|
|
74
|
-
*
|
|
75
|
-
* The outputSchema is already JSON Schema (converted in the ai layer).
|
|
76
|
-
* We apply Groq-specific transformations for structured output compatibility.
|
|
77
|
-
*/
|
|
78
|
-
async structuredOutput(options) {
|
|
79
|
-
const { chatOptions, outputSchema } = options;
|
|
80
|
-
const requestParams = this.mapTextOptionsToGroq(chatOptions);
|
|
81
|
-
const { logger } = chatOptions;
|
|
82
|
-
const jsonSchema = makeGroqStructuredOutputCompatible(
|
|
83
|
-
outputSchema,
|
|
84
|
-
outputSchema.required || []
|
|
14
|
+
async *processStreamChunks(stream, options, aguiState) {
|
|
15
|
+
yield* super.processStreamChunks(
|
|
16
|
+
promoteGroqUsage(stream),
|
|
17
|
+
options,
|
|
18
|
+
aguiState
|
|
85
19
|
);
|
|
86
|
-
try {
|
|
87
|
-
logger.request(
|
|
88
|
-
`activity=chat provider=groq model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,
|
|
89
|
-
{ provider: "groq", model: this.model }
|
|
90
|
-
);
|
|
91
|
-
const response = await this.client.chat.completions.create({
|
|
92
|
-
...requestParams,
|
|
93
|
-
stream: false,
|
|
94
|
-
response_format: {
|
|
95
|
-
type: "json_schema",
|
|
96
|
-
json_schema: {
|
|
97
|
-
name: "structured_output",
|
|
98
|
-
schema: jsonSchema,
|
|
99
|
-
strict: true
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
const rawText = response.choices[0]?.message.content || "";
|
|
104
|
-
let parsed;
|
|
105
|
-
try {
|
|
106
|
-
parsed = JSON.parse(rawText);
|
|
107
|
-
} catch {
|
|
108
|
-
throw new Error(
|
|
109
|
-
`Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? "..." : ""}`
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
const transformed = transformNullsToUndefined(parsed);
|
|
113
|
-
return {
|
|
114
|
-
data: transformed,
|
|
115
|
-
rawText
|
|
116
|
-
};
|
|
117
|
-
} catch (error) {
|
|
118
|
-
logger.errors("groq.structuredOutput fatal", {
|
|
119
|
-
error,
|
|
120
|
-
source: "groq.structuredOutput"
|
|
121
|
-
});
|
|
122
|
-
throw error;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Processes streaming chunks from the Groq API and yields AG-UI stream events.
|
|
127
|
-
* Handles text content deltas, tool call assembly, and lifecycle events.
|
|
128
|
-
*/
|
|
129
|
-
async *processGroqStreamChunks(stream, options, aguiState, logger) {
|
|
130
|
-
let accumulatedContent = "";
|
|
131
|
-
const timestamp = aguiState.timestamp;
|
|
132
|
-
let hasEmittedTextMessageStart = false;
|
|
133
|
-
const toolCallsInProgress = /* @__PURE__ */ new Map();
|
|
134
|
-
try {
|
|
135
|
-
for await (const chunk of stream) {
|
|
136
|
-
logger.provider(`provider=groq`, { chunk });
|
|
137
|
-
const choice = chunk.choices[0];
|
|
138
|
-
if (!choice) continue;
|
|
139
|
-
if (!aguiState.hasEmittedRunStarted) {
|
|
140
|
-
aguiState.hasEmittedRunStarted = true;
|
|
141
|
-
yield asChunk({
|
|
142
|
-
type: "RUN_STARTED",
|
|
143
|
-
runId: aguiState.runId,
|
|
144
|
-
threadId: aguiState.threadId,
|
|
145
|
-
model: chunk.model || options.model,
|
|
146
|
-
timestamp
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
const delta = choice.delta;
|
|
150
|
-
const deltaContent = delta.content;
|
|
151
|
-
const deltaToolCalls = delta.tool_calls;
|
|
152
|
-
if (deltaContent) {
|
|
153
|
-
if (!hasEmittedTextMessageStart) {
|
|
154
|
-
hasEmittedTextMessageStart = true;
|
|
155
|
-
yield asChunk({
|
|
156
|
-
type: "TEXT_MESSAGE_START",
|
|
157
|
-
messageId: aguiState.messageId,
|
|
158
|
-
model: chunk.model || options.model,
|
|
159
|
-
timestamp,
|
|
160
|
-
role: "assistant"
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
accumulatedContent += deltaContent;
|
|
164
|
-
yield asChunk({
|
|
165
|
-
type: "TEXT_MESSAGE_CONTENT",
|
|
166
|
-
messageId: aguiState.messageId,
|
|
167
|
-
model: chunk.model || options.model,
|
|
168
|
-
timestamp,
|
|
169
|
-
delta: deltaContent,
|
|
170
|
-
content: accumulatedContent
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
if (deltaToolCalls) {
|
|
174
|
-
for (const toolCallDelta of deltaToolCalls) {
|
|
175
|
-
const index = toolCallDelta.index;
|
|
176
|
-
if (!toolCallsInProgress.has(index)) {
|
|
177
|
-
toolCallsInProgress.set(index, {
|
|
178
|
-
id: toolCallDelta.id || "",
|
|
179
|
-
name: toolCallDelta.function?.name || "",
|
|
180
|
-
arguments: "",
|
|
181
|
-
started: false
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
const toolCall = toolCallsInProgress.get(index);
|
|
185
|
-
if (toolCallDelta.id) {
|
|
186
|
-
toolCall.id = toolCallDelta.id;
|
|
187
|
-
}
|
|
188
|
-
if (toolCallDelta.function?.name) {
|
|
189
|
-
toolCall.name = toolCallDelta.function.name;
|
|
190
|
-
}
|
|
191
|
-
if (toolCallDelta.function?.arguments) {
|
|
192
|
-
toolCall.arguments += toolCallDelta.function.arguments;
|
|
193
|
-
}
|
|
194
|
-
if (toolCall.id && toolCall.name && !toolCall.started) {
|
|
195
|
-
toolCall.started = true;
|
|
196
|
-
yield asChunk({
|
|
197
|
-
type: "TOOL_CALL_START",
|
|
198
|
-
toolCallId: toolCall.id,
|
|
199
|
-
toolCallName: toolCall.name,
|
|
200
|
-
toolName: toolCall.name,
|
|
201
|
-
model: chunk.model || options.model,
|
|
202
|
-
timestamp,
|
|
203
|
-
index
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
if (toolCallDelta.function?.arguments && toolCall.started) {
|
|
207
|
-
yield asChunk({
|
|
208
|
-
type: "TOOL_CALL_ARGS",
|
|
209
|
-
toolCallId: toolCall.id,
|
|
210
|
-
model: chunk.model || options.model,
|
|
211
|
-
timestamp,
|
|
212
|
-
delta: toolCallDelta.function.arguments
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (choice.finish_reason) {
|
|
218
|
-
if (choice.finish_reason === "tool_calls" || toolCallsInProgress.size > 0) {
|
|
219
|
-
for (const [, toolCall] of toolCallsInProgress) {
|
|
220
|
-
if (!toolCall.started || !toolCall.id || !toolCall.name) {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
let parsedInput = {};
|
|
224
|
-
try {
|
|
225
|
-
parsedInput = toolCall.arguments ? JSON.parse(toolCall.arguments) : {};
|
|
226
|
-
} catch {
|
|
227
|
-
parsedInput = {};
|
|
228
|
-
}
|
|
229
|
-
yield asChunk({
|
|
230
|
-
type: "TOOL_CALL_END",
|
|
231
|
-
toolCallId: toolCall.id,
|
|
232
|
-
toolCallName: toolCall.name,
|
|
233
|
-
toolName: toolCall.name,
|
|
234
|
-
model: chunk.model || options.model,
|
|
235
|
-
timestamp,
|
|
236
|
-
input: parsedInput
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
const computedFinishReason = choice.finish_reason === "tool_calls" || toolCallsInProgress.size > 0 ? "tool_calls" : choice.finish_reason === "length" ? "length" : "stop";
|
|
241
|
-
if (hasEmittedTextMessageStart) {
|
|
242
|
-
yield asChunk({
|
|
243
|
-
type: "TEXT_MESSAGE_END",
|
|
244
|
-
messageId: aguiState.messageId,
|
|
245
|
-
model: chunk.model || options.model,
|
|
246
|
-
timestamp
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
const groqUsage = chunk.x_groq?.usage;
|
|
250
|
-
yield asChunk({
|
|
251
|
-
type: "RUN_FINISHED",
|
|
252
|
-
runId: aguiState.runId,
|
|
253
|
-
threadId: aguiState.threadId,
|
|
254
|
-
model: chunk.model || options.model,
|
|
255
|
-
timestamp,
|
|
256
|
-
usage: groqUsage ? {
|
|
257
|
-
promptTokens: groqUsage.prompt_tokens || 0,
|
|
258
|
-
completionTokens: groqUsage.completion_tokens || 0,
|
|
259
|
-
totalTokens: groqUsage.total_tokens || 0
|
|
260
|
-
} : void 0,
|
|
261
|
-
finishReason: computedFinishReason
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
} catch (error) {
|
|
266
|
-
const err = error;
|
|
267
|
-
logger.errors("groq stream ended with error", {
|
|
268
|
-
error,
|
|
269
|
-
source: "groq.processGroqStreamChunks"
|
|
270
|
-
});
|
|
271
|
-
yield asChunk({
|
|
272
|
-
type: "RUN_ERROR",
|
|
273
|
-
runId: aguiState.runId,
|
|
274
|
-
model: options.model,
|
|
275
|
-
timestamp,
|
|
276
|
-
message: err.message || "Unknown error occurred",
|
|
277
|
-
code: err.code,
|
|
278
|
-
error: {
|
|
279
|
-
message: err.message || "Unknown error occurred",
|
|
280
|
-
code: err.code
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Maps common TextOptions to Groq-specific Chat Completions request parameters.
|
|
287
|
-
*/
|
|
288
|
-
mapTextOptionsToGroq(options) {
|
|
289
|
-
const modelOptions = options.modelOptions;
|
|
290
|
-
if (modelOptions) {
|
|
291
|
-
validateTextProviderOptions({
|
|
292
|
-
...modelOptions,
|
|
293
|
-
model: options.model
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
const tools = options.tools ? convertToolsToProviderFormat(options.tools) : void 0;
|
|
297
|
-
const messages = [];
|
|
298
|
-
if (options.systemPrompts && options.systemPrompts.length > 0) {
|
|
299
|
-
messages.push({
|
|
300
|
-
role: "system",
|
|
301
|
-
content: options.systemPrompts.join("\n")
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
for (const message of options.messages) {
|
|
305
|
-
messages.push(this.convertMessageToGroq(message));
|
|
306
|
-
}
|
|
307
|
-
return {
|
|
308
|
-
model: options.model,
|
|
309
|
-
messages,
|
|
310
|
-
temperature: options.temperature,
|
|
311
|
-
max_tokens: options.maxTokens,
|
|
312
|
-
top_p: options.topP,
|
|
313
|
-
tools,
|
|
314
|
-
stream: true
|
|
315
|
-
};
|
|
316
20
|
}
|
|
317
21
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
22
|
+
* Surfaces Groq's reasoning deltas during streaming structured output.
|
|
23
|
+
* Groq emits `delta.reasoning` (or legacy `delta.reasoning_content`) on
|
|
24
|
+
* reasoning models when the caller sets `reasoning_format: 'parsed'` in
|
|
25
|
+
* modelOptions. The base's chatStream and structuredOutputStream both
|
|
26
|
+
* route reasoning through this hook.
|
|
320
27
|
*/
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
content: typeof message.content === "string" ? message.content : JSON.stringify(message.content)
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
if (message.role === "assistant") {
|
|
330
|
-
const toolCalls = message.toolCalls?.map((tc) => ({
|
|
331
|
-
id: tc.id,
|
|
332
|
-
type: "function",
|
|
333
|
-
function: {
|
|
334
|
-
name: tc.function.name,
|
|
335
|
-
arguments: typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
336
|
-
}
|
|
337
|
-
}));
|
|
338
|
-
return {
|
|
339
|
-
role: "assistant",
|
|
340
|
-
content: this.extractTextContent(message.content),
|
|
341
|
-
...toolCalls && toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
const contentParts = this.normalizeContent(message.content);
|
|
345
|
-
if (contentParts.length === 1 && contentParts[0]?.type === "text") {
|
|
346
|
-
return {
|
|
347
|
-
role: "user",
|
|
348
|
-
content: contentParts[0].content
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
const parts = [];
|
|
352
|
-
for (const part of contentParts) {
|
|
353
|
-
if (part.type === "text") {
|
|
354
|
-
parts.push({ type: "text", text: part.content });
|
|
355
|
-
} else if (part.type === "image") {
|
|
356
|
-
const imageMetadata = part.metadata;
|
|
357
|
-
const imageValue = part.source.value;
|
|
358
|
-
const imageUrl = part.source.type === "data" && !imageValue.startsWith("data:") ? `data:${part.source.mimeType};base64,${imageValue}` : imageValue;
|
|
359
|
-
parts.push({
|
|
360
|
-
type: "image_url",
|
|
361
|
-
image_url: {
|
|
362
|
-
url: imageUrl,
|
|
363
|
-
detail: imageMetadata?.detail || "auto"
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
}
|
|
28
|
+
extractReasoning(chunk) {
|
|
29
|
+
const delta = chunk.choices[0]?.delta;
|
|
30
|
+
const raw = delta?.reasoning ?? delta?.reasoning_content;
|
|
31
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
32
|
+
return { text: raw };
|
|
367
33
|
}
|
|
368
|
-
return
|
|
369
|
-
role: "user",
|
|
370
|
-
content: parts.length > 0 ? parts : ""
|
|
371
|
-
};
|
|
34
|
+
return void 0;
|
|
372
35
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (typeof content === "string") {
|
|
382
|
-
return [{ type: "text", content }];
|
|
383
|
-
}
|
|
384
|
-
return content;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Extracts text content from a content value that may be string, null, or ContentPart array.
|
|
388
|
-
*/
|
|
389
|
-
extractTextContent(content) {
|
|
390
|
-
if (content === null) {
|
|
391
|
-
return "";
|
|
392
|
-
}
|
|
393
|
-
if (typeof content === "string") {
|
|
394
|
-
return content;
|
|
36
|
+
}
|
|
37
|
+
async function* promoteGroqUsage(stream) {
|
|
38
|
+
for await (const chunk of stream) {
|
|
39
|
+
const groqChunk = chunk;
|
|
40
|
+
if (!chunk.usage && groqChunk.x_groq?.usage) {
|
|
41
|
+
yield { ...chunk, usage: groqChunk.x_groq.usage };
|
|
42
|
+
} else {
|
|
43
|
+
yield chunk;
|
|
395
44
|
}
|
|
396
|
-
return content.filter((p) => p.type === "text").map((p) => p.content).join("");
|
|
397
45
|
}
|
|
398
46
|
}
|
|
399
47
|
function createGroqText(model, apiKey, config) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { validateTextProviderOptions } from '../text/text-provider-options'\nimport { convertToolsToProviderFormat } from '../tools'\nimport {\n createGroqClient,\n generateId,\n getGroqApiKeyFromEnv,\n makeGroqStructuredOutputCompatible,\n transformNullsToUndefined,\n} from '../utils'\nimport type {\n GROQ_CHAT_MODELS,\n GroqChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type GROQ_SDK from 'groq-sdk'\nimport type { ChatCompletionCreateParamsStreaming } from 'groq-sdk/resources/chat/completions'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type {\n ExternalTextProviderOptions,\n InternalTextProviderOptions,\n} from '../text/text-provider-options'\nimport type {\n ChatCompletionContentPart,\n ChatCompletionMessageParam,\n GroqImageMetadata,\n GroqMessageMetadataByModality,\n} from '../message-types'\nimport type { GroqClientConfig } from '../utils'\n\ntype GroqTextProviderOptions = ExternalTextProviderOptions\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GroqChatModelToolCapabilitiesByName\n ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/** Cast an event object to StreamChunk. Adapters construct events with string\n * literal types which are structurally compatible with the EventType enum. */\nconst asChunk = (chunk: Record<string, unknown>) =>\n chunk as unknown as StreamChunk\n\n/**\n * Configuration for Groq text adapter\n */\nexport interface GroqTextConfig extends GroqClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Groq Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Groq chat/text completion functionality.\n * Uses the Groq SDK which provides an OpenAI-compatible Chat Completions API.\n */\nexport class GroqTextAdapter<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GroqMessageMetadataByModality,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name = 'groq' as const\n\n private client: GROQ_SDK\n\n constructor(config: GroqTextConfig, model: TModel) {\n super({}, model)\n this.client = createGroqClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GroqTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const requestParams = this.mapTextOptionsToGroq(options)\n const timestamp = Date.now()\n const { logger } = options\n\n const aguiState = {\n runId: options.runId ?? generateId(this.name),\n threadId: options.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n try {\n logger.request(\n `activity=chat provider=groq model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: 'groq', model: this.model },\n )\n const stream = await this.client.chat.completions.create({\n ...requestParams,\n stream: true,\n })\n\n yield* this.processGroqStreamChunks(stream, options, aguiState, logger)\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield asChunk({\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: options.model,\n timestamp,\n })\n }\n\n yield asChunk({\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n message: err.message || 'Unknown error',\n code: err.code,\n error: {\n message: err.message || 'Unknown error',\n code: err.code,\n },\n })\n\n logger.errors('groq.chatStream fatal', {\n error,\n source: 'groq.chatStream',\n })\n }\n }\n\n /**\n * Generate structured output using Groq's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * Groq has strict requirements for structured output:\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply Groq-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<GroqTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapTextOptionsToGroq(chatOptions)\n const { logger } = chatOptions\n\n const jsonSchema = makeGroqStructuredOutputCompatible(\n outputSchema,\n outputSchema.required || [],\n )\n\n try {\n logger.request(\n `activity=chat provider=groq model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n { provider: 'groq', model: this.model },\n )\n const response = await this.client.chat.completions.create({\n ...requestParams,\n stream: false,\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n })\n\n const rawText = response.choices[0]?.message.content || ''\n\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n const transformed = transformNullsToUndefined(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n logger.errors('groq.structuredOutput fatal', {\n error,\n source: 'groq.structuredOutput',\n })\n throw error\n }\n }\n\n /**\n * Processes streaming chunks from the Groq API and yields AG-UI stream events.\n * Handles text content deltas, tool call assembly, and lifecycle events.\n */\n private async *processGroqStreamChunks(\n stream: AsyncIterable<GROQ_SDK.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n timestamp: number\n hasEmittedRunStarted: boolean\n },\n logger: InternalLogger,\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n const timestamp = aguiState.timestamp\n let hasEmittedTextMessageStart = false\n\n const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean\n }\n >()\n\n try {\n for await (const chunk of stream) {\n logger.provider(`provider=groq`, { chunk })\n const choice = chunk.choices[0]\n\n if (!choice) continue\n\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield asChunk({\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp,\n })\n }\n\n const delta = choice.delta\n const deltaContent = delta.content\n const deltaToolCalls = delta.tool_calls\n\n if (deltaContent) {\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield asChunk({\n type: 'TEXT_MESSAGE_START',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n role: 'assistant',\n })\n }\n\n accumulatedContent += deltaContent\n\n yield asChunk({\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n delta: deltaContent,\n content: accumulatedContent,\n })\n }\n\n if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\n if (!toolCallsInProgress.has(index)) {\n toolCallsInProgress.set(index, {\n id: toolCallDelta.id || '',\n name: toolCallDelta.function?.name || '',\n arguments: '',\n started: false,\n })\n }\n\n const toolCall = toolCallsInProgress.get(index)!\n\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments\n }\n\n if (toolCall.id && toolCall.name && !toolCall.started) {\n toolCall.started = true\n yield asChunk({\n type: 'TOOL_CALL_START',\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n index,\n })\n }\n\n if (toolCallDelta.function?.arguments && toolCall.started) {\n yield asChunk({\n type: 'TOOL_CALL_ARGS',\n toolCallId: toolCall.id,\n model: chunk.model || options.model,\n timestamp,\n delta: toolCallDelta.function.arguments,\n })\n }\n }\n }\n\n if (choice.finish_reason) {\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n if (!toolCall.started || !toolCall.id || !toolCall.name) {\n continue\n }\n\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.arguments\n ? JSON.parse(toolCall.arguments)\n : {}\n } catch {\n parsedInput = {}\n }\n\n yield asChunk({\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n input: parsedInput,\n })\n }\n }\n\n const computedFinishReason =\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ? 'tool_calls'\n : choice.finish_reason === 'length'\n ? 'length'\n : 'stop'\n\n if (hasEmittedTextMessageStart) {\n yield asChunk({\n type: 'TEXT_MESSAGE_END',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n })\n }\n\n const groqUsage = chunk.x_groq?.usage\n\n yield asChunk({\n type: 'RUN_FINISHED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp,\n usage: groqUsage\n ? {\n promptTokens: groqUsage.prompt_tokens || 0,\n completionTokens: groqUsage.completion_tokens || 0,\n totalTokens: groqUsage.total_tokens || 0,\n }\n : undefined,\n finishReason: computedFinishReason,\n })\n }\n }\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n logger.errors('groq stream ended with error', {\n error,\n source: 'groq.processGroqStreamChunks',\n })\n\n yield asChunk({\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n message: err.message || 'Unknown error occurred',\n code: err.code,\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code,\n },\n })\n }\n }\n\n /**\n * Maps common TextOptions to Groq-specific Chat Completions request parameters.\n */\n private mapTextOptionsToGroq(\n options: TextOptions,\n ): ChatCompletionCreateParamsStreaming {\n const modelOptions = options.modelOptions as\n | Omit<\n InternalTextProviderOptions,\n 'max_tokens' | 'tools' | 'temperature' | 'input' | 'top_p'\n >\n | undefined\n\n if (modelOptions) {\n validateTextProviderOptions({\n ...modelOptions,\n model: options.model,\n })\n }\n\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n const messages: Array<ChatCompletionMessageParam> = []\n\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n for (const message of options.messages) {\n messages.push(this.convertMessageToGroq(message))\n }\n\n return {\n model: options.model,\n messages,\n temperature: options.temperature,\n max_tokens: options.maxTokens,\n top_p: options.topP,\n tools,\n stream: true,\n }\n }\n\n /**\n * Converts a TanStack AI ModelMessage to a Groq ChatCompletionMessageParam.\n * Handles tool, assistant, and user messages including multimodal content.\n */\n private convertMessageToGroq(\n message: ModelMessage,\n ): ChatCompletionMessageParam {\n if (message.role === 'tool') {\n return {\n role: 'tool',\n tool_call_id: message.toolCallId || '',\n content:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n }\n }\n\n if (message.role === 'assistant') {\n const toolCalls = message.toolCalls?.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === 'string'\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }))\n\n return {\n role: 'assistant',\n content: this.extractTextContent(message.content),\n ...(toolCalls && toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),\n }\n }\n\n const contentParts = this.normalizeContent(message.content)\n\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n return {\n role: 'user',\n content: contentParts[0].content,\n }\n }\n\n const parts: Array<ChatCompletionContentPart> = []\n for (const part of contentParts) {\n if (part.type === 'text') {\n parts.push({ type: 'text', text: part.content })\n } else if (part.type === 'image') {\n const imageMetadata = part.metadata as GroqImageMetadata | undefined\n const imageValue = part.source.value\n const imageUrl =\n part.source.type === 'data' && !imageValue.startsWith('data:')\n ? `data:${part.source.mimeType};base64,${imageValue}`\n : imageValue\n parts.push({\n type: 'image_url',\n image_url: {\n url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n },\n })\n }\n }\n\n return {\n role: 'user',\n content: parts.length > 0 ? parts : '',\n }\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n private normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n private extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n\n/**\n * Creates a Groq text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'llama-3.3-70b-versatile', 'openai/gpt-oss-120b')\n * @param apiKey - Your Groq API key\n * @param config - Optional additional configuration\n * @returns Configured Groq text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGroqText('llama-3.3-70b-versatile', \"gsk_...\");\n * // adapter has type-safe providerOptions for llama-3.3-70b-versatile\n * ```\n */\nexport function createGroqText<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n return new GroqTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq text adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `GROQ_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'llama-3.3-70b-versatile', 'openai/gpt-oss-120b')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Groq text adapter instance with resolved types\n * @throws Error if GROQ_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses GROQ_API_KEY from environment\n * const adapter = groqText('llama-3.3-70b-versatile');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function groqText<TModel extends (typeof GROQ_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqText(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'\nimport { makeGroqStructuredOutputCompatible } from '../utils/schema-converter'\nimport type { Modality, TextOptions } from '@tanstack/ai'\nimport type {\n GROQ_CHAT_MODELS,\n GroqChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { GroqMessageMetadataByModality } from '../message-types'\nimport type { GroqClientConfig } from '../utils'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GroqChatModelToolCapabilitiesByName\n ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Configuration for Groq text adapter\n */\nexport interface GroqTextConfig extends GroqClientConfig {}\n\n/**\n * Re-export of the public provider options type\n */\nexport type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Groq Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Groq chat/text completion. Groq exposes an\n * OpenAI-compatible Chat Completions endpoint at `/openai/v1`, so we drive\n * it with the OpenAI SDK via a `baseURL` override (the same pattern as\n * `ai-grok`).\n *\n * Quirk: when usage is present on a stream, Groq historically delivered it\n * under `chunk.x_groq.usage` rather than `chunk.usage`. The override below\n * promotes it to the standard location so the base's RUN_FINISHED usage\n * accounting works unchanged.\n */\nexport class GroqTextAdapter<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseChatCompletionsTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GroqMessageMetadataByModality,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name = 'groq' as const\n\n constructor(config: GroqTextConfig, model: TModel) {\n super(model, 'groq', new OpenAI(withGroqDefaults(config)))\n }\n\n protected override makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ): Record<string, any> {\n return makeGroqStructuredOutputCompatible(schema, originalRequired)\n }\n\n protected override async *processStreamChunks(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ) {\n yield* super.processStreamChunks(\n promoteGroqUsage(stream),\n options,\n aguiState,\n )\n }\n\n /**\n * Surfaces Groq's reasoning deltas during streaming structured output.\n * Groq emits `delta.reasoning` (or legacy `delta.reasoning_content`) on\n * reasoning models when the caller sets `reasoning_format: 'parsed'` in\n * modelOptions. The base's chatStream and structuredOutputStream both\n * route reasoning through this hook.\n */\n protected override extractReasoning(\n chunk: OpenAI.Chat.Completions.ChatCompletionChunk,\n ): { text: string } | undefined {\n const delta = chunk.choices[0]?.delta as\n | { reasoning?: unknown; reasoning_content?: unknown }\n | undefined\n const raw = delta?.reasoning ?? delta?.reasoning_content\n if (typeof raw === 'string' && raw.length > 0) {\n return { text: raw }\n }\n return undefined\n }\n}\n\n/**\n * Promotes Groq's non-standard `x_groq.usage` to the standard `chunk.usage`\n * slot the base reads. Pass-through for chunks that already carry usage at\n * the documented location.\n */\nasync function* promoteGroqUsage(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n): AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk> {\n for await (const chunk of stream) {\n const groqChunk = chunk as typeof chunk & {\n x_groq?: { usage?: OpenAI.Chat.Completions.ChatCompletionChunk['usage'] }\n }\n if (!chunk.usage && groqChunk.x_groq?.usage) {\n yield { ...chunk, usage: groqChunk.x_groq.usage }\n } else {\n yield chunk\n }\n }\n}\n\n/**\n * Creates a Groq text adapter with explicit API key.\n *\n * @example\n * ```typescript\n * const adapter = createGroqText('llama-3.3-70b-versatile', \"gsk_...\");\n * ```\n */\nexport function createGroqText<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n return new GroqTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq text adapter with API key from `GROQ_API_KEY`.\n *\n * @example\n * ```typescript\n * const adapter = groqText('llama-3.3-70b-versatile');\n * ```\n */\nexport function groqText<TModel extends (typeof GROQ_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqText(model, apiKey, config)\n}\n"],"names":[],"mappings":";;;;AA0CO,MAAM,wBAOH,qCAMR;AAAA,EAIA,YAAY,QAAwB,OAAe;AACjD,UAAM,OAAO,QAAQ,IAAI,OAAO,iBAAiB,MAAM,CAAC,CAAC;AAJ3D,SAAS,OAAO;AAChB,SAAS,OAAO;AAAA,EAIhB;AAAA,EAEmB,+BACjB,QACA,kBACqB;AACrB,WAAO,mCAAmC,QAAQ,gBAAgB;AAAA,EACpE;AAAA,EAEA,OAA0B,oBACxB,QACA,SACA,WAMA;AACA,WAAO,MAAM;AAAA,MACX,iBAAiB,MAAM;AAAA,MACvB;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASmB,iBACjB,OAC8B;AAC9B,UAAM,QAAQ,MAAM,QAAQ,CAAC,GAAG;AAGhC,UAAM,MAAM,OAAO,aAAa,OAAO;AACvC,QAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG;AAC7C,aAAO,EAAE,MAAM,IAAA;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AACF;AAOA,gBAAgB,iBACd,QAC4D;AAC5D,mBAAiB,SAAS,QAAQ;AAChC,UAAM,YAAY;AAGlB,QAAI,CAAC,MAAM,SAAS,UAAU,QAAQ,OAAO;AAC3C,YAAM,EAAE,GAAG,OAAO,OAAO,UAAU,OAAO,MAAA;AAAA,IAC5C,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAUO,SAAS,eAGd,OACA,QACA,QACyB;AACzB,SAAO,IAAI,gBAAgB,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AACzD;AAUO,SAAS,SACd,OACA,QACyB;AACzB,QAAM,SAAS,qBAAA;AACf,SAAO,eAAe,OAAO,QAAQ,MAAM;AAC7C;"}
|