@elizaos/plugin-groq 2.0.3-beta.5 → 2.0.3-beta.7
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/browser/index.browser.js +571 -0
- package/dist/browser/index.browser.js.map +11 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.node.cjs +600 -0
- package/dist/cjs/index.node.js.map +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/node/index.browser.d.ts +4 -0
- package/dist/node/index.browser.d.ts.map +1 -0
- package/dist/node/index.d.ts +17 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.node.d.ts +4 -0
- package/dist/node/index.node.d.ts.map +1 -0
- package/dist/node/index.node.js +571 -0
- package/dist/node/index.node.js.map +11 -0
- package/package.json +4 -4
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
// index.ts
|
|
2
|
+
import { createGroq } from "@ai-sdk/groq";
|
|
3
|
+
import {
|
|
4
|
+
buildCanonicalSystemPrompt,
|
|
5
|
+
EventType,
|
|
6
|
+
logger,
|
|
7
|
+
ModelType,
|
|
8
|
+
recordLlmCall,
|
|
9
|
+
renderChatMessagesForPrompt,
|
|
10
|
+
resolveEffectiveSystemPrompt
|
|
11
|
+
} from "@elizaos/core";
|
|
12
|
+
import {
|
|
13
|
+
APICallError,
|
|
14
|
+
generateText,
|
|
15
|
+
jsonSchema,
|
|
16
|
+
Output
|
|
17
|
+
} from "ai";
|
|
18
|
+
var _globalThis = globalThis;
|
|
19
|
+
_globalThis.AI_SDK_LOG_WARNINGS ??= false;
|
|
20
|
+
var DEFAULT_SMALL_MODEL = "openai/gpt-oss-120b";
|
|
21
|
+
var DEFAULT_LARGE_MODEL = "openai/gpt-oss-120b";
|
|
22
|
+
var DEFAULT_TTS_MODEL = "canopylabs/orpheus-v1-english";
|
|
23
|
+
var DEFAULT_TTS_VOICE = "troy";
|
|
24
|
+
var DEFAULT_TTS_RESPONSE_FORMAT = "wav";
|
|
25
|
+
var DEFAULT_TRANSCRIPTION_MODEL = "whisper-large-v3-turbo";
|
|
26
|
+
var DEFAULT_BASE_URL = "https://api.groq.com/openai/v1";
|
|
27
|
+
function resolveGroqSystemPrompt(runtime, params) {
|
|
28
|
+
return resolveEffectiveSystemPrompt({
|
|
29
|
+
params,
|
|
30
|
+
fallback: buildCanonicalSystemPrompt({ character: runtime.character })
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function resolveGroqPrompt(params, systemPrompt) {
|
|
34
|
+
return renderChatMessagesForPrompt(params.messages, {
|
|
35
|
+
omitDuplicateSystem: systemPrompt
|
|
36
|
+
}) ?? params.prompt ?? "";
|
|
37
|
+
}
|
|
38
|
+
function toFiniteNumber(value) {
|
|
39
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
return Math.max(0, Math.round(value));
|
|
43
|
+
}
|
|
44
|
+
function normalizeTokenUsage(usage) {
|
|
45
|
+
if (!usage || typeof usage !== "object") {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const record = usage;
|
|
49
|
+
const promptTokens = toFiniteNumber(record.inputTokens ?? record.promptTokens);
|
|
50
|
+
const completionTokens = toFiniteNumber(record.outputTokens ?? record.completionTokens);
|
|
51
|
+
const totalTokens = toFiniteNumber(record.totalTokens);
|
|
52
|
+
if (promptTokens === undefined && completionTokens === undefined && totalTokens === undefined) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const normalizedPromptTokens = promptTokens ?? (completionTokens === undefined && totalTokens !== undefined ? totalTokens : Math.max(0, (totalTokens ?? 0) - (completionTokens ?? 0)));
|
|
56
|
+
const normalizedCompletionTokens = completionTokens ?? Math.max(0, (totalTokens ?? normalizedPromptTokens) - normalizedPromptTokens);
|
|
57
|
+
return {
|
|
58
|
+
promptTokens: normalizedPromptTokens,
|
|
59
|
+
completionTokens: normalizedCompletionTokens,
|
|
60
|
+
totalTokens: totalTokens ?? normalizedPromptTokens + normalizedCompletionTokens
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function applyUsageToDetails(details, usage) {
|
|
64
|
+
const normalized = normalizeTokenUsage(usage);
|
|
65
|
+
if (!normalized) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
details.promptTokens = normalized.promptTokens;
|
|
69
|
+
details.completionTokens = normalized.completionTokens;
|
|
70
|
+
}
|
|
71
|
+
function estimateTokenCount(text) {
|
|
72
|
+
return text.length === 0 ? 0 : Math.ceil(text.length / 4);
|
|
73
|
+
}
|
|
74
|
+
function stringifyForUsage(value) {
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return JSON.stringify(value);
|
|
80
|
+
} catch {
|
|
81
|
+
return String(value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function estimateUsage(prompt, response) {
|
|
85
|
+
const promptTokens = estimateTokenCount(prompt);
|
|
86
|
+
const completionTokens = estimateTokenCount(stringifyForUsage(response));
|
|
87
|
+
return {
|
|
88
|
+
promptTokens,
|
|
89
|
+
completionTokens,
|
|
90
|
+
totalTokens: promptTokens + completionTokens,
|
|
91
|
+
estimated: true
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function boundedNumber(value, fallback, min, max) {
|
|
95
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
96
|
+
return fallback;
|
|
97
|
+
}
|
|
98
|
+
return Math.min(max, Math.max(min, value));
|
|
99
|
+
}
|
|
100
|
+
function positiveInteger(value, fallback) {
|
|
101
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
102
|
+
return fallback;
|
|
103
|
+
}
|
|
104
|
+
return Math.floor(value);
|
|
105
|
+
}
|
|
106
|
+
function stringArray(value) {
|
|
107
|
+
if (!Array.isArray(value)) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
return value.filter((item) => typeof item === "string");
|
|
111
|
+
}
|
|
112
|
+
function emitModelUsed(runtime, type, model, usage) {
|
|
113
|
+
runtime.emitEvent(EventType.MODEL_USED, {
|
|
114
|
+
runtime,
|
|
115
|
+
source: "groq",
|
|
116
|
+
provider: "groq",
|
|
117
|
+
type,
|
|
118
|
+
model,
|
|
119
|
+
modelName: model,
|
|
120
|
+
tokens: {
|
|
121
|
+
prompt: usage.promptTokens,
|
|
122
|
+
completion: usage.completionTokens,
|
|
123
|
+
total: usage.totalTokens,
|
|
124
|
+
...usage.estimated ? { estimated: true } : {}
|
|
125
|
+
},
|
|
126
|
+
...usage.estimated ? { usageEstimated: true } : {}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function isBrowser() {
|
|
130
|
+
return typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
131
|
+
}
|
|
132
|
+
function env(name) {
|
|
133
|
+
return _globalThis.process?.env?.[name] ?? null;
|
|
134
|
+
}
|
|
135
|
+
function nonEmptyString(value) {
|
|
136
|
+
if (typeof value !== "string") {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const trimmed = value.trim();
|
|
140
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
141
|
+
}
|
|
142
|
+
function getRuntimeBuffer() {
|
|
143
|
+
return _globalThis.Buffer ?? null;
|
|
144
|
+
}
|
|
145
|
+
function getBaseURL(runtime) {
|
|
146
|
+
const configured = nonEmptyString(runtime.getSetting("GROQ_BASE_URL"));
|
|
147
|
+
if (!configured) {
|
|
148
|
+
return DEFAULT_BASE_URL;
|
|
149
|
+
}
|
|
150
|
+
let parsed;
|
|
151
|
+
try {
|
|
152
|
+
parsed = new URL(configured);
|
|
153
|
+
} catch {
|
|
154
|
+
throw new Error("GROQ_BASE_URL must be a valid http(s) URL");
|
|
155
|
+
}
|
|
156
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
157
|
+
throw new Error("GROQ_BASE_URL must be a valid http(s) URL");
|
|
158
|
+
}
|
|
159
|
+
return configured.replace(/\/+$/, "");
|
|
160
|
+
}
|
|
161
|
+
function getSmallModel(runtime) {
|
|
162
|
+
const setting = runtime.getSetting("GROQ_SMALL_MODEL") || runtime.getSetting("SMALL_MODEL");
|
|
163
|
+
return typeof setting === "string" ? setting : DEFAULT_SMALL_MODEL;
|
|
164
|
+
}
|
|
165
|
+
function getNanoModel(runtime) {
|
|
166
|
+
const setting = runtime.getSetting("GROQ_NANO_MODEL") || runtime.getSetting("NANO_MODEL");
|
|
167
|
+
return typeof setting === "string" ? setting : getSmallModel(runtime);
|
|
168
|
+
}
|
|
169
|
+
function getMediumModel(runtime) {
|
|
170
|
+
const setting = runtime.getSetting("GROQ_MEDIUM_MODEL") || runtime.getSetting("MEDIUM_MODEL");
|
|
171
|
+
return typeof setting === "string" ? setting : getSmallModel(runtime);
|
|
172
|
+
}
|
|
173
|
+
function getLargeModel(runtime) {
|
|
174
|
+
const setting = runtime.getSetting("GROQ_LARGE_MODEL") || runtime.getSetting("LARGE_MODEL");
|
|
175
|
+
return typeof setting === "string" ? setting : DEFAULT_LARGE_MODEL;
|
|
176
|
+
}
|
|
177
|
+
function getMegaModel(runtime) {
|
|
178
|
+
const setting = runtime.getSetting("GROQ_MEGA_MODEL") || runtime.getSetting("MEGA_MODEL");
|
|
179
|
+
return typeof setting === "string" ? setting : getLargeModel(runtime);
|
|
180
|
+
}
|
|
181
|
+
function getResponseHandlerModel(runtime) {
|
|
182
|
+
const setting = runtime.getSetting("GROQ_RESPONSE_HANDLER_MODEL") || runtime.getSetting("GROQ_SHOULD_RESPOND_MODEL") || runtime.getSetting("RESPONSE_HANDLER_MODEL") || runtime.getSetting("SHOULD_RESPOND_MODEL");
|
|
183
|
+
return typeof setting === "string" ? setting : getNanoModel(runtime);
|
|
184
|
+
}
|
|
185
|
+
function getTranscriptionModel(runtime) {
|
|
186
|
+
const setting = runtime.getSetting("GROQ_TRANSCRIPTION_MODEL") || runtime.getSetting("TRANSCRIPTION_MODEL");
|
|
187
|
+
return typeof setting === "string" ? setting : DEFAULT_TRANSCRIPTION_MODEL;
|
|
188
|
+
}
|
|
189
|
+
function getActionPlannerModel(runtime) {
|
|
190
|
+
const setting = runtime.getSetting("GROQ_ACTION_PLANNER_MODEL") || runtime.getSetting("GROQ_PLANNER_MODEL") || runtime.getSetting("ACTION_PLANNER_MODEL") || runtime.getSetting("PLANNER_MODEL");
|
|
191
|
+
return typeof setting === "string" ? setting : getLargeModel(runtime);
|
|
192
|
+
}
|
|
193
|
+
function createGroqClient(runtime) {
|
|
194
|
+
const allowBrowserKey = !isBrowser() || String(runtime.getSetting("GROQ_ALLOW_BROWSER_API_KEY") ?? "").toLowerCase() === "true";
|
|
195
|
+
const apiKey = allowBrowserKey ? nonEmptyString(runtime.getSetting("GROQ_API_KEY")) : undefined;
|
|
196
|
+
return createGroq({
|
|
197
|
+
apiKey,
|
|
198
|
+
fetch: runtime.fetch ?? undefined,
|
|
199
|
+
baseURL: getBaseURL(runtime)
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function extractRetryDelay(message) {
|
|
203
|
+
const match = message.match(/try again in (\d+\.?\d*)s/i);
|
|
204
|
+
if (match?.[1]) {
|
|
205
|
+
return Math.ceil(Number.parseFloat(match[1]) * 1000) + 1000;
|
|
206
|
+
}
|
|
207
|
+
return 1e4;
|
|
208
|
+
}
|
|
209
|
+
function classifyRetryError(error) {
|
|
210
|
+
if (APICallError.isInstance(error)) {
|
|
211
|
+
if (error.statusCode === 429)
|
|
212
|
+
return "rate-limit";
|
|
213
|
+
if (typeof error.statusCode === "number" && error.statusCode >= 500 && error.statusCode < 600) {
|
|
214
|
+
return "transient";
|
|
215
|
+
}
|
|
216
|
+
if (error.isRetryable)
|
|
217
|
+
return "transient";
|
|
218
|
+
return "fatal";
|
|
219
|
+
}
|
|
220
|
+
if (!(error instanceof Error))
|
|
221
|
+
return "fatal";
|
|
222
|
+
const message = error.message.toLowerCase();
|
|
223
|
+
if (message.includes("rate limit") || message.includes("rate_limit") || message.includes("too many requests") || /try again in \d/i.test(error.message)) {
|
|
224
|
+
return "rate-limit";
|
|
225
|
+
}
|
|
226
|
+
if (message.includes("econnreset") || message.includes("etimedout") || message.includes("enotfound") || message.includes("econnrefused") || message.includes("socket hang up") || message.includes("network error") || message.includes("fetch failed")) {
|
|
227
|
+
return "transient";
|
|
228
|
+
}
|
|
229
|
+
return "fatal";
|
|
230
|
+
}
|
|
231
|
+
function buildGroqStructuredOutput(responseSchema) {
|
|
232
|
+
if (responseSchema && typeof responseSchema === "object" && "responseFormat" in responseSchema && "parseCompleteOutput" in responseSchema) {
|
|
233
|
+
return responseSchema;
|
|
234
|
+
}
|
|
235
|
+
const schemaOptions = responseSchema && typeof responseSchema === "object" && "schema" in responseSchema ? responseSchema : { schema: responseSchema };
|
|
236
|
+
return Output.object({
|
|
237
|
+
schema: jsonSchema(schemaOptions.schema),
|
|
238
|
+
...schemaOptions.name ? { name: schemaOptions.name } : {},
|
|
239
|
+
...schemaOptions.description ? { description: schemaOptions.description } : {}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function buildGroqNativeTextResult(result) {
|
|
243
|
+
const inputTokens = result.usage?.inputTokens ?? result.usage?.promptTokens ?? 0;
|
|
244
|
+
const outputTokens = result.usage?.outputTokens ?? result.usage?.completionTokens ?? 0;
|
|
245
|
+
const usage = result.usage ? {
|
|
246
|
+
promptTokens: inputTokens,
|
|
247
|
+
completionTokens: outputTokens,
|
|
248
|
+
totalTokens: result.usage.totalTokens ?? inputTokens + outputTokens
|
|
249
|
+
} : undefined;
|
|
250
|
+
return {
|
|
251
|
+
text: result.text,
|
|
252
|
+
toolCalls: result.toolCalls ?? [],
|
|
253
|
+
finishReason: result.finishReason,
|
|
254
|
+
...usage ? { usage } : {}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
async function generateWithRetry(runtime, groq, modelType, model, params) {
|
|
258
|
+
const generate = () => {
|
|
259
|
+
const details = {
|
|
260
|
+
model,
|
|
261
|
+
systemPrompt: params.system ?? "",
|
|
262
|
+
userPrompt: params.prompt,
|
|
263
|
+
temperature: params.temperature,
|
|
264
|
+
maxTokens: params.maxTokens ?? 0,
|
|
265
|
+
maxTokensOmitted: params.omitMaxTokens ? true : undefined,
|
|
266
|
+
purpose: "external_llm",
|
|
267
|
+
actionType: "ai.generateText"
|
|
268
|
+
};
|
|
269
|
+
return recordLlmCall(runtime, details, async () => {
|
|
270
|
+
const sharedSettings = {
|
|
271
|
+
model: groq.languageModel(model),
|
|
272
|
+
system: params.system,
|
|
273
|
+
temperature: params.temperature,
|
|
274
|
+
...params.omitMaxTokens ? {} : { maxOutputTokens: params.maxTokens },
|
|
275
|
+
maxRetries: 3,
|
|
276
|
+
frequencyPenalty: params.frequencyPenalty,
|
|
277
|
+
presencePenalty: params.presencePenalty,
|
|
278
|
+
stopSequences: params.stopSequences,
|
|
279
|
+
...params.tools ? { tools: params.tools } : {},
|
|
280
|
+
...params.toolChoice ? { toolChoice: params.toolChoice } : {},
|
|
281
|
+
...params.responseSchema ? { output: buildGroqStructuredOutput(params.responseSchema) } : {}
|
|
282
|
+
};
|
|
283
|
+
const result = params.messages && params.messages.length > 0 ? await generateText({ ...sharedSettings, messages: params.messages }) : await generateText({ ...sharedSettings, prompt: params.prompt });
|
|
284
|
+
details.response = result.text;
|
|
285
|
+
applyUsageToDetails(details, result.usage);
|
|
286
|
+
return result;
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
const MAX_RATE_LIMIT_RETRIES = 5;
|
|
290
|
+
const MAX_TRANSIENT_RETRIES = 2;
|
|
291
|
+
let rateLimitAttempts = 0;
|
|
292
|
+
let transientAttempts = 0;
|
|
293
|
+
while (true) {
|
|
294
|
+
try {
|
|
295
|
+
const result = await generate();
|
|
296
|
+
const usage = normalizeTokenUsage(result.usage) ?? estimateUsage(params.prompt, result.text);
|
|
297
|
+
emitModelUsed(runtime, modelType, model, usage);
|
|
298
|
+
if (params.returnNative) {
|
|
299
|
+
return buildGroqNativeTextResult(result);
|
|
300
|
+
}
|
|
301
|
+
const { text } = result;
|
|
302
|
+
return text;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
const kind = classifyRetryError(error);
|
|
305
|
+
if (kind === "rate-limit" && rateLimitAttempts < MAX_RATE_LIMIT_RETRIES) {
|
|
306
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
307
|
+
const hinted = extractRetryDelay(message);
|
|
308
|
+
const backoff = Math.min(30000, 500 * 2 ** rateLimitAttempts);
|
|
309
|
+
const delay = hinted + backoff;
|
|
310
|
+
rateLimitAttempts += 1;
|
|
311
|
+
logger.warn(`Groq rate limit hit (attempt ${rateLimitAttempts}/${MAX_RATE_LIMIT_RETRIES}), retrying in ${delay}ms`);
|
|
312
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (kind === "transient" && transientAttempts < MAX_TRANSIENT_RETRIES) {
|
|
316
|
+
const delay = 1000 + Math.floor(Math.random() * 1500);
|
|
317
|
+
transientAttempts += 1;
|
|
318
|
+
logger.warn(`Groq transient failure (attempt ${transientAttempts}/${MAX_TRANSIENT_RETRIES}), retrying in ${delay}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
319
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function buildGroqGenerateParams(params, systemPrompt, promptText) {
|
|
327
|
+
const paramsWithNative = params;
|
|
328
|
+
const returnNative = Boolean(paramsWithNative.messages || paramsWithNative.tools || paramsWithNative.toolChoice || paramsWithNative.responseSchema);
|
|
329
|
+
return {
|
|
330
|
+
prompt: promptText,
|
|
331
|
+
system: systemPrompt,
|
|
332
|
+
temperature: boundedNumber(params.temperature, 0.7, 0, 2),
|
|
333
|
+
maxTokens: params.omitMaxTokens ? undefined : positiveInteger(params.maxTokens, 8192),
|
|
334
|
+
omitMaxTokens: params.omitMaxTokens,
|
|
335
|
+
frequencyPenalty: boundedNumber(params.frequencyPenalty, 0.7, -2, 2),
|
|
336
|
+
presencePenalty: boundedNumber(params.presencePenalty, 0.7, -2, 2),
|
|
337
|
+
stopSequences: stringArray(params.stopSequences),
|
|
338
|
+
...paramsWithNative.messages ? { messages: paramsWithNative.messages } : {},
|
|
339
|
+
...paramsWithNative.tools ? { tools: paramsWithNative.tools } : {},
|
|
340
|
+
...paramsWithNative.toolChoice ? { toolChoice: paramsWithNative.toolChoice } : {},
|
|
341
|
+
...paramsWithNative.responseSchema ? { responseSchema: paramsWithNative.responseSchema } : {},
|
|
342
|
+
...returnNative ? { returnNative } : {}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
async function handleTextModel(runtime, params, modelType) {
|
|
346
|
+
const groq = createGroqClient(runtime);
|
|
347
|
+
const model = getTextModelForType(runtime, modelType);
|
|
348
|
+
const system = resolveGroqSystemPrompt(runtime, params);
|
|
349
|
+
const result = await generateWithRetry(runtime, groq, modelType, model, buildGroqGenerateParams(params, system, resolveGroqPrompt(params, system)));
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
352
|
+
function getTextModelForType(runtime, modelType) {
|
|
353
|
+
switch (modelType) {
|
|
354
|
+
case ModelType.TEXT_NANO:
|
|
355
|
+
return getNanoModel(runtime);
|
|
356
|
+
case ModelType.TEXT_MEDIUM:
|
|
357
|
+
return getMediumModel(runtime);
|
|
358
|
+
case ModelType.TEXT_SMALL:
|
|
359
|
+
return getSmallModel(runtime);
|
|
360
|
+
case ModelType.TEXT_LARGE:
|
|
361
|
+
return getLargeModel(runtime);
|
|
362
|
+
case ModelType.TEXT_MEGA:
|
|
363
|
+
return getMegaModel(runtime);
|
|
364
|
+
case ModelType.RESPONSE_HANDLER:
|
|
365
|
+
return getResponseHandlerModel(runtime);
|
|
366
|
+
case ModelType.ACTION_PLANNER:
|
|
367
|
+
return getActionPlannerModel(runtime);
|
|
368
|
+
default:
|
|
369
|
+
return getLargeModel(runtime);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
var groqPlugin = {
|
|
373
|
+
name: "groq",
|
|
374
|
+
description: "Groq LLM provider - fast inference with GPT-OSS models",
|
|
375
|
+
autoEnable: {
|
|
376
|
+
envKeys: ["GROQ_API_KEY"]
|
|
377
|
+
},
|
|
378
|
+
config: {
|
|
379
|
+
GROQ_API_KEY: env("GROQ_API_KEY"),
|
|
380
|
+
GROQ_BASE_URL: env("GROQ_BASE_URL"),
|
|
381
|
+
GROQ_NANO_MODEL: env("GROQ_NANO_MODEL"),
|
|
382
|
+
GROQ_MEDIUM_MODEL: env("GROQ_MEDIUM_MODEL"),
|
|
383
|
+
GROQ_SMALL_MODEL: env("GROQ_SMALL_MODEL"),
|
|
384
|
+
GROQ_LARGE_MODEL: env("GROQ_LARGE_MODEL"),
|
|
385
|
+
GROQ_MEGA_MODEL: env("GROQ_MEGA_MODEL"),
|
|
386
|
+
GROQ_RESPONSE_HANDLER_MODEL: env("GROQ_RESPONSE_HANDLER_MODEL"),
|
|
387
|
+
GROQ_SHOULD_RESPOND_MODEL: env("GROQ_SHOULD_RESPOND_MODEL"),
|
|
388
|
+
GROQ_ACTION_PLANNER_MODEL: env("GROQ_ACTION_PLANNER_MODEL"),
|
|
389
|
+
GROQ_PLANNER_MODEL: env("GROQ_PLANNER_MODEL"),
|
|
390
|
+
GROQ_TRANSCRIPTION_MODEL: env("GROQ_TRANSCRIPTION_MODEL"),
|
|
391
|
+
TRANSCRIPTION_MODEL: env("TRANSCRIPTION_MODEL"),
|
|
392
|
+
NANO_MODEL: env("NANO_MODEL"),
|
|
393
|
+
MEDIUM_MODEL: env("MEDIUM_MODEL"),
|
|
394
|
+
SMALL_MODEL: env("SMALL_MODEL"),
|
|
395
|
+
LARGE_MODEL: env("LARGE_MODEL"),
|
|
396
|
+
MEGA_MODEL: env("MEGA_MODEL"),
|
|
397
|
+
RESPONSE_HANDLER_MODEL: env("RESPONSE_HANDLER_MODEL"),
|
|
398
|
+
SHOULD_RESPOND_MODEL: env("SHOULD_RESPOND_MODEL"),
|
|
399
|
+
ACTION_PLANNER_MODEL: env("ACTION_PLANNER_MODEL"),
|
|
400
|
+
PLANNER_MODEL: env("PLANNER_MODEL")
|
|
401
|
+
},
|
|
402
|
+
async init(_config, runtime) {
|
|
403
|
+
const apiKey = nonEmptyString(runtime.getSetting("GROQ_API_KEY"));
|
|
404
|
+
if (!apiKey && !isBrowser()) {
|
|
405
|
+
throw new Error("GROQ_API_KEY is required");
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
models: {
|
|
409
|
+
[ModelType.TEXT_NANO]: (runtime, params) => handleTextModel(runtime, params, ModelType.TEXT_NANO),
|
|
410
|
+
[ModelType.TEXT_SMALL]: (runtime, params) => handleTextModel(runtime, params, ModelType.TEXT_SMALL),
|
|
411
|
+
[ModelType.TEXT_MEDIUM]: (runtime, params) => handleTextModel(runtime, params, ModelType.TEXT_MEDIUM),
|
|
412
|
+
[ModelType.TEXT_LARGE]: (runtime, params) => handleTextModel(runtime, params, ModelType.TEXT_LARGE),
|
|
413
|
+
[ModelType.TEXT_MEGA]: (runtime, params) => handleTextModel(runtime, params, ModelType.TEXT_MEGA),
|
|
414
|
+
[ModelType.RESPONSE_HANDLER]: (runtime, params) => handleTextModel(runtime, params, ModelType.RESPONSE_HANDLER),
|
|
415
|
+
[ModelType.ACTION_PLANNER]: (runtime, params) => handleTextModel(runtime, params, ModelType.ACTION_PLANNER),
|
|
416
|
+
[ModelType.TRANSCRIPTION]: async (runtime, params) => {
|
|
417
|
+
function hasAudioData(obj) {
|
|
418
|
+
return "audioData" in obj && obj.audioData instanceof Uint8Array;
|
|
419
|
+
}
|
|
420
|
+
if (isBrowser()) {
|
|
421
|
+
throw new Error("Groq TRANSCRIPTION is not supported directly in browsers. Use a server proxy or submit a Blob/ArrayBuffer to a server.");
|
|
422
|
+
}
|
|
423
|
+
const buffer = getRuntimeBuffer();
|
|
424
|
+
if (!buffer) {
|
|
425
|
+
throw new Error("Groq TRANSCRIPTION requires Buffer support outside browsers.");
|
|
426
|
+
}
|
|
427
|
+
const audioBuffer = typeof params === "string" ? buffer.from(params, "base64") : buffer.isBuffer(params) ? params : typeof params === "object" && params !== null && hasAudioData(params) ? buffer.from(params.audioData) : buffer.alloc(0);
|
|
428
|
+
if (audioBuffer.byteLength === 0) {
|
|
429
|
+
throw new Error("Groq TRANSCRIPTION requires non-empty audio data.");
|
|
430
|
+
}
|
|
431
|
+
const baseURL = getBaseURL(runtime);
|
|
432
|
+
const transcriptionModel = getTranscriptionModel(runtime);
|
|
433
|
+
const formData = new FormData;
|
|
434
|
+
formData.append("file", new File([audioBuffer], "audio.mp3", { type: "audio/mp3" }));
|
|
435
|
+
formData.append("model", transcriptionModel);
|
|
436
|
+
const apiKey = nonEmptyString(runtime.getSetting("GROQ_API_KEY"));
|
|
437
|
+
const details = {
|
|
438
|
+
model: transcriptionModel,
|
|
439
|
+
systemPrompt: "",
|
|
440
|
+
userPrompt: `audio transcription request: ${audioBuffer.byteLength} bytes`,
|
|
441
|
+
temperature: 0,
|
|
442
|
+
maxTokens: 0,
|
|
443
|
+
purpose: "external_llm",
|
|
444
|
+
actionType: "groq.audio.transcriptions.create"
|
|
445
|
+
};
|
|
446
|
+
const data = await recordLlmCall(runtime, details, async () => {
|
|
447
|
+
const response = await fetch(`${baseURL}/audio/transcriptions`, {
|
|
448
|
+
method: "POST",
|
|
449
|
+
headers: {
|
|
450
|
+
Authorization: `Bearer ${apiKey ?? ""}`
|
|
451
|
+
},
|
|
452
|
+
body: formData
|
|
453
|
+
});
|
|
454
|
+
if (!response.ok) {
|
|
455
|
+
throw new Error(`Transcription failed: ${response.status} ${await response.text()}`);
|
|
456
|
+
}
|
|
457
|
+
const result = await response.json();
|
|
458
|
+
details.response = result.text;
|
|
459
|
+
return result;
|
|
460
|
+
});
|
|
461
|
+
return data.text;
|
|
462
|
+
},
|
|
463
|
+
[ModelType.TEXT_TO_SPEECH]: async (runtime, params) => {
|
|
464
|
+
if (isBrowser()) {
|
|
465
|
+
throw new Error("Groq TEXT_TO_SPEECH is not supported directly in browsers. Use a server proxy.");
|
|
466
|
+
}
|
|
467
|
+
const payload = typeof params === "string" ? { text: params } : params && typeof params === "object" ? params : {};
|
|
468
|
+
const text = nonEmptyString(payload.text);
|
|
469
|
+
if (!text) {
|
|
470
|
+
throw new Error("Groq TEXT_TO_SPEECH requires non-empty text.");
|
|
471
|
+
}
|
|
472
|
+
const baseURL = getBaseURL(runtime);
|
|
473
|
+
const modelSetting = runtime.getSetting("GROQ_TTS_MODEL");
|
|
474
|
+
const voiceSetting = runtime.getSetting("GROQ_TTS_VOICE");
|
|
475
|
+
const responseFormatSetting = runtime.getSetting("GROQ_TTS_RESPONSE_FORMAT");
|
|
476
|
+
const model = typeof payload.model === "string" && payload.model ? payload.model : typeof modelSetting === "string" ? modelSetting : DEFAULT_TTS_MODEL;
|
|
477
|
+
const voice = typeof payload.voice === "string" && payload.voice ? payload.voice : typeof voiceSetting === "string" ? voiceSetting : DEFAULT_TTS_VOICE;
|
|
478
|
+
const responseFormat = typeof payload.responseFormat === "string" && payload.responseFormat ? payload.responseFormat : typeof payload.response_format === "string" && payload.response_format ? payload.response_format : typeof responseFormatSetting === "string" ? responseFormatSetting : DEFAULT_TTS_RESPONSE_FORMAT;
|
|
479
|
+
const apiKey = nonEmptyString(runtime.getSetting("GROQ_API_KEY"));
|
|
480
|
+
const details = {
|
|
481
|
+
model,
|
|
482
|
+
systemPrompt: "",
|
|
483
|
+
userPrompt: text,
|
|
484
|
+
temperature: 0,
|
|
485
|
+
maxTokens: 0,
|
|
486
|
+
purpose: "external_llm",
|
|
487
|
+
actionType: "groq.audio.speech.create"
|
|
488
|
+
};
|
|
489
|
+
const arrayBuffer = await recordLlmCall(runtime, details, async () => {
|
|
490
|
+
const response = await fetch(`${baseURL}/audio/speech`, {
|
|
491
|
+
method: "POST",
|
|
492
|
+
headers: {
|
|
493
|
+
Authorization: `Bearer ${apiKey ?? ""}`,
|
|
494
|
+
"Content-Type": "application/json"
|
|
495
|
+
},
|
|
496
|
+
body: JSON.stringify({
|
|
497
|
+
model,
|
|
498
|
+
voice,
|
|
499
|
+
input: text,
|
|
500
|
+
response_format: responseFormat
|
|
501
|
+
})
|
|
502
|
+
});
|
|
503
|
+
if (!response.ok) {
|
|
504
|
+
throw new Error(`TTS failed: ${response.status} ${await response.text()}`);
|
|
505
|
+
}
|
|
506
|
+
const result = await response.arrayBuffer();
|
|
507
|
+
details.response = `[audio bytes=${result.byteLength} format=${responseFormat}]`;
|
|
508
|
+
return result;
|
|
509
|
+
});
|
|
510
|
+
return new Uint8Array(arrayBuffer);
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
tests: [
|
|
514
|
+
{
|
|
515
|
+
name: "groq_plugin_tests",
|
|
516
|
+
tests: [
|
|
517
|
+
{
|
|
518
|
+
name: "validate_api_key",
|
|
519
|
+
fn: async (runtime) => {
|
|
520
|
+
const baseURL = getBaseURL(runtime);
|
|
521
|
+
const response = await fetch(`${baseURL}/models`, {
|
|
522
|
+
headers: {
|
|
523
|
+
Authorization: `Bearer ${runtime.getSetting("GROQ_API_KEY")}`
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
if (!response.ok) {
|
|
527
|
+
throw new Error(`API key validation failed: ${response.statusText}`);
|
|
528
|
+
}
|
|
529
|
+
const data = await response.json();
|
|
530
|
+
logger.info(`Groq API validated, ${data.data.length} models available`);
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "text_small",
|
|
535
|
+
fn: async (runtime) => {
|
|
536
|
+
const text = await runtime.useModel(ModelType.TEXT_SMALL, {
|
|
537
|
+
prompt: "Say hello in exactly 3 words."
|
|
538
|
+
});
|
|
539
|
+
if (!text || text.length === 0) {
|
|
540
|
+
throw new Error("Empty response from TEXT_SMALL");
|
|
541
|
+
}
|
|
542
|
+
logger.info("TEXT_SMALL:", text);
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
name: "text_large",
|
|
547
|
+
fn: async (runtime) => {
|
|
548
|
+
const text = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
549
|
+
prompt: "What is 2+2? Answer with just the number."
|
|
550
|
+
});
|
|
551
|
+
if (!text || text.length === 0) {
|
|
552
|
+
throw new Error("Empty response from TEXT_LARGE");
|
|
553
|
+
}
|
|
554
|
+
logger.info("TEXT_LARGE:", text);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
]
|
|
558
|
+
}
|
|
559
|
+
]
|
|
560
|
+
};
|
|
561
|
+
var plugin_groq_default = groqPlugin;
|
|
562
|
+
|
|
563
|
+
// index.node.ts
|
|
564
|
+
var index_node_default = plugin_groq_default;
|
|
565
|
+
export {
|
|
566
|
+
groqPlugin,
|
|
567
|
+
index_node_default as default,
|
|
568
|
+
classifyRetryError
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
//# debugId=BC4554A41753610164756E2164756E21
|