@umituz/web-ai-groq-provider 1.0.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.
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Text Generation Service
3
+ * Handles basic text generation using Groq API
4
+ */
5
+
6
+ import type {
7
+ GroqChatRequest,
8
+ GroqMessage,
9
+ GroqGenerationConfig,
10
+ } from "./types";
11
+ import { groqHttpClient } from "./client";
12
+ import { DEFAULT_MODELS } from "./types";
13
+ import { GroqError, GroqErrorType } from "./types";
14
+
15
+ const isDevelopment = process.env.NODE_ENV === "development";
16
+
17
+ export interface TextGenerationOptions {
18
+ model?: string;
19
+ generationConfig?: GroqGenerationConfig;
20
+ }
21
+
22
+ /**
23
+ * Generate text from a simple prompt
24
+ */
25
+ export async function textGeneration(
26
+ prompt: string,
27
+ options: TextGenerationOptions = {}
28
+ ): Promise<string> {
29
+ const startTime = Date.now();
30
+ const model = options.model || DEFAULT_MODELS.TEXT;
31
+
32
+ if (isDevelopment) {
33
+ console.log("[Groq] textGeneration called:", {
34
+ model,
35
+ promptLength: prompt.length,
36
+ promptPreview: prompt.substring(0, 100) + "...",
37
+ options: options.generationConfig,
38
+ });
39
+ }
40
+
41
+ const messages: GroqMessage[] = [
42
+ {
43
+ role: "user",
44
+ content: prompt,
45
+ },
46
+ ];
47
+
48
+ const request: GroqChatRequest = {
49
+ model,
50
+ messages,
51
+ temperature: options.generationConfig?.temperature || 0.7,
52
+ max_tokens: options.generationConfig?.maxTokens || 1024,
53
+ top_p: options.generationConfig?.topP,
54
+ n: options.generationConfig?.n,
55
+ stop: options.generationConfig?.stop,
56
+ frequency_penalty: options.generationConfig?.frequencyPenalty,
57
+ presence_penalty: options.generationConfig?.presencePenalty,
58
+ };
59
+
60
+ if (isDevelopment) {
61
+ console.log("[Groq] Sending request to API:", {
62
+ endpoint: "/v1/chat/completions",
63
+ requestBody: {
64
+ model: request.model,
65
+ messageCount: request.messages.length,
66
+ temperature: request.temperature,
67
+ max_tokens: request.max_tokens,
68
+ },
69
+ });
70
+ }
71
+
72
+ const apiStartTime = Date.now();
73
+ const response = await groqHttpClient.chatCompletion(request);
74
+ const apiDuration = Date.now() - apiStartTime;
75
+
76
+ if (isDevelopment) {
77
+ console.log("[Groq] API response received:", {
78
+ apiDuration: `${apiDuration}ms`,
79
+ hasChoices: !!response.choices?.length,
80
+ choiceCount: response.choices?.length || 0,
81
+ usage: response.usage,
82
+ finishReason: response.choices?.[0]?.finish_reason,
83
+ });
84
+ }
85
+
86
+ const content = response.choices[0]?.message?.content;
87
+ if (!content) {
88
+ throw new GroqError(
89
+ GroqErrorType.UNKNOWN_ERROR,
90
+ "No content generated from Groq API"
91
+ );
92
+ }
93
+
94
+ const totalDuration = Date.now() - startTime;
95
+ if (isDevelopment) {
96
+ console.log("[Groq] textGeneration complete:", {
97
+ totalDuration: `${totalDuration}ms`,
98
+ responseLength: content.length,
99
+ responsePreview: content.substring(0, 200) + "...",
100
+ });
101
+ }
102
+
103
+ return content;
104
+ }
105
+
106
+ /**
107
+ * Generate text from an array of messages
108
+ */
109
+ export async function chatGeneration(
110
+ messages: GroqMessage[],
111
+ options: TextGenerationOptions = {}
112
+ ): Promise<string> {
113
+ const startTime = Date.now();
114
+ const model = options.model || DEFAULT_MODELS.TEXT;
115
+
116
+ if (isDevelopment) {
117
+ console.log("[Groq] chatGeneration called:", {
118
+ model,
119
+ messageCount: messages.length,
120
+ options: options.generationConfig,
121
+ });
122
+ }
123
+
124
+ const request: GroqChatRequest = {
125
+ model,
126
+ messages,
127
+ temperature: options.generationConfig?.temperature || 0.7,
128
+ max_tokens: options.generationConfig?.maxTokens || 1024,
129
+ top_p: options.generationConfig?.topP,
130
+ n: options.generationConfig?.n,
131
+ stop: options.generationConfig?.stop,
132
+ frequency_penalty: options.generationConfig?.frequencyPenalty,
133
+ presence_penalty: options.generationConfig?.presencePenalty,
134
+ };
135
+
136
+ const apiStartTime = Date.now();
137
+ const response = await groqHttpClient.chatCompletion(request);
138
+ const apiDuration = Date.now() - apiStartTime;
139
+
140
+ if (isDevelopment) {
141
+ console.log("[Groq] chatGeneration API response:", {
142
+ apiDuration: `${apiDuration}ms`,
143
+ usage: response.usage,
144
+ finishReason: response.choices?.[0]?.finish_reason,
145
+ });
146
+ }
147
+
148
+ const content = response.choices[0]?.message?.content;
149
+ if (!content) {
150
+ throw new GroqError(
151
+ GroqErrorType.UNKNOWN_ERROR,
152
+ "No content generated from Groq API"
153
+ );
154
+ }
155
+
156
+ const totalDuration = Date.now() - startTime;
157
+ if (isDevelopment) {
158
+ console.log("[Groq] chatGeneration complete:", {
159
+ totalDuration: `${totalDuration}ms`,
160
+ responseLength: content.length,
161
+ });
162
+ }
163
+
164
+ return content;
165
+ }
package/src/types.ts ADDED
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Groq API Types
3
+ */
4
+
5
+ /**
6
+ * Configuration for Groq client initialization
7
+ */
8
+ export interface GroqConfig {
9
+ /** API key for authentication */
10
+ apiKey: string;
11
+ /** Base URL for API requests (default: https://api.groq.com/openai/v1) */
12
+ baseUrl?: string;
13
+ /** Default timeout in milliseconds */
14
+ timeoutMs?: number;
15
+ /** Default model to use for text generation */
16
+ textModel?: string;
17
+ }
18
+
19
+ /**
20
+ * Generation configuration for AI requests
21
+ */
22
+ export interface GroqGenerationConfig {
23
+ /** Controls randomness (0.0 - 2.0, default: 0.7) */
24
+ temperature?: number;
25
+ /** Maximum number of tokens to generate */
26
+ maxTokens?: number;
27
+ /** Nucleus sampling threshold (0.0 - 1.0) */
28
+ topP?: number;
29
+ /** Number of completions to generate */
30
+ n?: number;
31
+ /** Stop sequences */
32
+ stop?: string[];
33
+ /** Frequency penalty (-2.0 to 2.0) */
34
+ frequencyPenalty?: number;
35
+ /** Presence penalty (-2.0 to 2.0) */
36
+ presencePenalty?: number;
37
+ }
38
+
39
+ /**
40
+ * Message role in chat conversation
41
+ */
42
+ export type GroqMessageRole = "system" | "user" | "assistant";
43
+
44
+ /**
45
+ * Chat message structure
46
+ */
47
+ export interface GroqMessage {
48
+ /** Role of the message sender */
49
+ role: GroqMessageRole;
50
+ /** Content of the message */
51
+ content: string;
52
+ }
53
+
54
+ /**
55
+ * Chat completion request
56
+ */
57
+ export interface GroqChatRequest {
58
+ /** Model to use for generation */
59
+ model: string;
60
+ /** Array of messages in the conversation */
61
+ messages: GroqMessage[];
62
+ /** Generation configuration */
63
+ temperature?: number;
64
+ max_tokens?: number;
65
+ top_p?: number;
66
+ n?: number;
67
+ stop?: string[];
68
+ frequency_penalty?: number;
69
+ presence_penalty?: number;
70
+ /** Enable streaming response */
71
+ stream?: boolean;
72
+ }
73
+
74
+ /**
75
+ * Chat completion response
76
+ */
77
+ export interface GroqChatResponse {
78
+ /** Unique identifier for the response */
79
+ id: string;
80
+ /** Object type (chat.completion) */
81
+ object: string;
82
+ /** Timestamp of creation */
83
+ created: number;
84
+ /** Model used for generation */
85
+ model: string;
86
+ /** Array of completion choices */
87
+ choices: GroqChoice[];
88
+ /** Token usage information */
89
+ usage: GroqUsage;
90
+ /** System fingerprint (Groq specific) */
91
+ system_fingerprint?: string;
92
+ /** X Groq (Groq specific) */
93
+ x_groq?: {
94
+ id?: string;
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Individual completion choice
100
+ */
101
+ export interface GroqChoice {
102
+ /** Index of the choice */
103
+ index: number;
104
+ /** Generated message */
105
+ message: {
106
+ role: "assistant";
107
+ content: string;
108
+ };
109
+ /** Reason for finish (stop, length, etc.) */
110
+ finish_reason: GroqFinishReason;
111
+ /** Logprobs (optional) */
112
+ logprobs: null | object;
113
+ }
114
+
115
+ /**
116
+ * Finish reason types
117
+ */
118
+ export type GroqFinishReason = "stop" | "length" | "content_filter";
119
+
120
+ /**
121
+ * Token usage information
122
+ */
123
+ export interface GroqUsage {
124
+ /** Number of tokens in the prompt */
125
+ prompt_tokens: number;
126
+ /** Number of tokens in the completion */
127
+ completion_tokens: number;
128
+ /** Total number of tokens used */
129
+ total_tokens: number;
130
+ /** Prompt time (Groq specific) */
131
+ prompt_time?: number;
132
+ /** Completion time (Groq specific) */
133
+ completion_time?: number;
134
+ /** Total time (Groq specific) */
135
+ total_time?: number;
136
+ }
137
+
138
+ /**
139
+ * Streaming chunk response
140
+ */
141
+ export interface GroqChatChunk {
142
+ /** Unique identifier for the response */
143
+ id: string;
144
+ /** Object type (chat.completion.chunk) */
145
+ object: string;
146
+ /** Timestamp of creation */
147
+ created: number;
148
+ /** Model used for generation */
149
+ model: string;
150
+ /** Array of completion choices */
151
+ choices: GroqChunkChoice[];
152
+ /** System fingerprint (Groq specific) */
153
+ system_fingerprint?: string;
154
+ /** X Groq (Groq specific) */
155
+ x_groq?: {
156
+ id?: string;
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Individual chunk choice for streaming
162
+ */
163
+ export interface GroqChunkChoice {
164
+ /** Index of the choice */
165
+ index: number;
166
+ /** Delta message (partial content) */
167
+ delta: {
168
+ role?: "assistant";
169
+ content?: string;
170
+ };
171
+ /** Reason for finish (null if not finished) */
172
+ finish_reason: GroqFinishReason | null;
173
+ /** Logprobs (optional) */
174
+ logprobs: null | object;
175
+ }
176
+
177
+ /**
178
+ * API error response
179
+ */
180
+ export interface GroqErrorResponse {
181
+ /** Error type */
182
+ error: {
183
+ /** Error message */
184
+ message: string;
185
+ /** Error type */
186
+ type: string;
187
+ /** Error code */
188
+ code?: string;
189
+ };
190
+ }
191
+
192
+ /**
193
+ * Available Groq models
194
+ */
195
+ export const GROQ_MODELS = {
196
+ /** Llama 3.1 8B - Fast and efficient (560 T/s) */
197
+ LLAMA_3_1_8B_INSTANT: "llama-3.1-8b-instant",
198
+ /** Llama 3.3 70B - Versatile and powerful (280 T/s) */
199
+ LLAMA_3_3_70B_VERSATILE: "llama-3.3-70b-versatile",
200
+ /** Llama 3.1 70B - Versatile (280 T/s) */
201
+ LLAMA_3_1_70B_VERSATILE: "llama-3.1-70b-versatile",
202
+ /** GPT-OSS 20B - Experimental (1000 T/s) */
203
+ GPT_OSS_20B: "openai/gpt-oss-20b",
204
+ /** GPT-OSS 120B - Large experimental model */
205
+ GPT_OSS_120B: "openai/gpt-oss-120b",
206
+ /** Mixtral 8x7b - MoE model */
207
+ MIXTRAL_8X7B: "mixtral-8x7b-32768",
208
+ /** Gemma 2 9B - Google's model */
209
+ GEMMA_2_9B: "gemma2-9b-it",
210
+ /** Llama 4 Scout 17B - New model (30K T/s) */
211
+ LLAMA_4_SCOUT_17B: "meta-llama/llama-4-scout-17b-16e-instruct",
212
+ /** Kimi K2 - Moonshot AI model */
213
+ KIMI_K2_INSTRUCT: "moonshotai/kimi-k2-instruct",
214
+ /** Qwen 3 32B - Alibaba's model */
215
+ QWEN3_32B: "qwen/qwen3-32b",
216
+ } as const;
217
+
218
+ /**
219
+ * Default models for different use cases
220
+ */
221
+ export const DEFAULT_MODELS = {
222
+ TEXT: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
223
+ FAST: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
224
+ EXPERIMENTAL: GROQ_MODELS.GPT_OSS_20B,
225
+ } as const;
226
+
227
+ /**
228
+ * Error types
229
+ */
230
+ export enum GroqErrorType {
231
+ INVALID_API_KEY = "INVALID_API_KEY",
232
+ MISSING_CONFIG = "MISSING_CONFIG",
233
+ NETWORK_ERROR = "NETWORK_ERROR",
234
+ ABORT_ERROR = "ABORT_ERROR",
235
+ RATE_LIMIT_ERROR = "RATE_LIMIT_ERROR",
236
+ SERVER_ERROR = "SERVER_ERROR",
237
+ UNKNOWN_ERROR = "UNKNOWN_ERROR",
238
+ }
239
+
240
+ /**
241
+ * Custom error class for Groq API errors
242
+ */
243
+ export class GroqError extends Error {
244
+ constructor(
245
+ public type: GroqErrorType,
246
+ message: string,
247
+ public cause?: Error,
248
+ public metadata?: { status?: number; url?: string }
249
+ ) {
250
+ super(message);
251
+ this.name = "GroqError";
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Map HTTP status to error type
257
+ */
258
+ export function mapHttpStatusToErrorType(status: number): GroqErrorType {
259
+ if (status === 401 || status === 403) return GroqErrorType.INVALID_API_KEY;
260
+ if (status === 429) return GroqErrorType.RATE_LIMIT_ERROR;
261
+ if (status >= 500) return GroqErrorType.SERVER_ERROR;
262
+ if (status >= 400) return GroqErrorType.INVALID_API_KEY;
263
+ return GroqErrorType.UNKNOWN_ERROR;
264
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Utility functions for error handling and content mapping
3
+ */
4
+
5
+ import { GroqError, GroqErrorType } from "./types";
6
+ import type { GroqMessage } from "./types";
7
+
8
+ /**
9
+ * Get user-friendly error message
10
+ */
11
+ export function getUserFriendlyError(error: unknown): string {
12
+ if (error instanceof GroqError) {
13
+ switch (error.type) {
14
+ case GroqErrorType.INVALID_API_KEY:
15
+ return "Invalid API key. Please check your Groq API key.";
16
+ case GroqErrorType.MISSING_CONFIG:
17
+ return "Groq provider not initialized. Please call configureProvider() first.";
18
+ case GroqErrorType.NETWORK_ERROR:
19
+ return "Network error. Please check your internet connection.";
20
+ case GroqErrorType.ABORT_ERROR:
21
+ return "Request was cancelled.";
22
+ case GroqErrorType.RATE_LIMIT_ERROR:
23
+ return "Rate limit exceeded. Please try again later.";
24
+ case GroqErrorType.SERVER_ERROR:
25
+ return "Groq API server error. Please try again later.";
26
+ default:
27
+ return error.message || "An unknown error occurred.";
28
+ }
29
+ }
30
+
31
+ if (error instanceof Error) {
32
+ return error.message;
33
+ }
34
+
35
+ return "An unknown error occurred.";
36
+ }
37
+
38
+ /**
39
+ * Check if error is retryable
40
+ */
41
+ export function isRetryableError(error: unknown): boolean {
42
+ if (error instanceof GroqError) {
43
+ return (
44
+ error.type === GroqErrorType.NETWORK_ERROR ||
45
+ error.type === GroqErrorType.SERVER_ERROR ||
46
+ error.type === GroqErrorType.RATE_LIMIT_ERROR
47
+ );
48
+ }
49
+ return false;
50
+ }
51
+
52
+ /**
53
+ * Check if error is auth-related
54
+ */
55
+ export function isAuthError(error: unknown): boolean {
56
+ if (error instanceof GroqError) {
57
+ return error.type === GroqErrorType.INVALID_API_KEY;
58
+ }
59
+ return false;
60
+ }
61
+
62
+ /**
63
+ * Format error for logging
64
+ */
65
+ export function formatErrorForLogging(error: unknown): string {
66
+ if (error instanceof GroqError) {
67
+ return JSON.stringify({
68
+ type: error.type,
69
+ message: error.message,
70
+ metadata: error.metadata,
71
+ });
72
+ }
73
+
74
+ if (error instanceof Error) {
75
+ return JSON.stringify({
76
+ name: error.name,
77
+ message: error.message,
78
+ stack: error.stack,
79
+ });
80
+ }
81
+
82
+ return String(error);
83
+ }
84
+
85
+ /**
86
+ * Create a user message
87
+ */
88
+ export function createUserMessage(content: string): GroqMessage {
89
+ return { role: "user", content };
90
+ }
91
+
92
+ /**
93
+ * Create an assistant message
94
+ */
95
+ export function createAssistantMessage(content: string): GroqMessage {
96
+ return { role: "assistant", content };
97
+ }
98
+
99
+ /**
100
+ * Create a system message
101
+ */
102
+ export function createSystemMessage(content: string): GroqMessage {
103
+ return { role: "system", content };
104
+ }
105
+
106
+ /**
107
+ * Create a text message (defaults to user role)
108
+ */
109
+ export function createTextMessage(
110
+ content: string,
111
+ role: GroqMessage["role"] = "user"
112
+ ): GroqMessage {
113
+ return { role, content };
114
+ }
115
+
116
+ /**
117
+ * Convert a prompt to messages array
118
+ */
119
+ export function promptToMessages(prompt: string): GroqMessage[] {
120
+ return [createUserMessage(prompt)];
121
+ }
122
+
123
+ /**
124
+ * Extract text content from messages
125
+ */
126
+ export function extractTextFromMessages(messages: GroqMessage[]): string {
127
+ return messages.map((m) => `[${m.role}]: ${m.content}`).join("\n");
128
+ }
129
+
130
+ /**
131
+ * Format messages for display
132
+ */
133
+ export function formatMessagesForDisplay(messages: GroqMessage[]): string {
134
+ return messages
135
+ .map(
136
+ (m) =>
137
+ `${m.role.charAt(0).toUpperCase() + m.role.slice(1)}: ${m.content}`
138
+ )
139
+ .join("\n\n");
140
+ }