@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.
package/src/hook.ts ADDED
@@ -0,0 +1,295 @@
1
+ /**
2
+ * useGroq Hook
3
+ * Main React hook for Groq text generation
4
+ */
5
+
6
+ import { useState, useCallback, useRef, useMemo } from "react";
7
+ import type { GroqGenerationConfig } from "./types";
8
+ import { textGeneration } from "./text-generation";
9
+ import { structuredText } from "./structured-text";
10
+ import { streaming } from "./streaming";
11
+
12
+ const isDevelopment = process.env.NODE_ENV === "development";
13
+
14
+ export interface UseGroqOptions {
15
+ /** Initial model to use */
16
+ model?: string;
17
+ /** Default generation config */
18
+ generationConfig?: GroqGenerationConfig;
19
+ /** Callback when generation starts */
20
+ onStart?: () => void;
21
+ /** Callback when generation completes */
22
+ onSuccess?: (result: string) => void;
23
+ /** Callback when generation fails */
24
+ onError?: (error: string) => void;
25
+ }
26
+
27
+ export interface UseGroqReturn {
28
+ /** Loading state */
29
+ isLoading: boolean;
30
+ /** Error message */
31
+ error: string | null;
32
+ /** Generated result */
33
+ result: string | null;
34
+ /** Generate text from a prompt */
35
+ generate: (
36
+ prompt: string,
37
+ options?: GroqGenerationConfig
38
+ ) => Promise<string>;
39
+ /** Generate structured JSON output */
40
+ generateJSON: <T = Record<string, unknown>>(
41
+ prompt: string,
42
+ options?: GroqGenerationConfig & { schema?: Record<string, unknown> }
43
+ ) => Promise<T>;
44
+ /** Stream text generation */
45
+ stream: (
46
+ prompt: string,
47
+ onChunk: (chunk: string) => void,
48
+ options?: GroqGenerationConfig
49
+ ) => Promise<void>;
50
+ /** Reset state */
51
+ reset: () => void;
52
+ /** Clear error */
53
+ clearError: () => void;
54
+ }
55
+
56
+ /**
57
+ * Hook for Groq text generation
58
+ * Optimized to prevent unnecessary re-renders and memory leaks
59
+ */
60
+ export function useGroq(options: UseGroqOptions = {}): UseGroqReturn {
61
+ const [isLoading, setIsLoading] = useState(false);
62
+ const [error, setError] = useState<string | null>(null);
63
+ const [result, setResult] = useState<string | null>(null);
64
+ const abortControllerRef = useRef<AbortController | null>(null);
65
+
66
+ // Memoize options to prevent unnecessary callback recreations
67
+ const stableOptions = useMemo(
68
+ () => options,
69
+ [
70
+ options.model,
71
+ options.generationConfig?.temperature,
72
+ options.generationConfig?.maxTokens,
73
+ options.generationConfig?.topP,
74
+ options.onStart,
75
+ options.onSuccess,
76
+ options.onError,
77
+ ]
78
+ );
79
+
80
+ const generate = useCallback(
81
+ async (prompt: string, config?: GroqGenerationConfig): Promise<string> => {
82
+ // Cancel any ongoing request
83
+ if (abortControllerRef.current) {
84
+ abortControllerRef.current.abort();
85
+ }
86
+
87
+ abortControllerRef.current = new AbortController();
88
+ setIsLoading(true);
89
+ setError(null);
90
+ setResult(null);
91
+
92
+ if (isDevelopment) {
93
+ console.log("[useGroq] generate called:", {
94
+ promptLength: prompt.length,
95
+ });
96
+ }
97
+
98
+ stableOptions.onStart?.();
99
+
100
+ try {
101
+ const response = await textGeneration(prompt, {
102
+ model: stableOptions.model,
103
+ generationConfig: {
104
+ ...stableOptions.generationConfig,
105
+ ...config,
106
+ },
107
+ });
108
+
109
+ setResult(response);
110
+ stableOptions.onSuccess?.(response);
111
+
112
+ if (isDevelopment) {
113
+ console.log("[useGroq] generate success:", {
114
+ responseLength: response.length,
115
+ });
116
+ }
117
+
118
+ return response;
119
+ } catch (err) {
120
+ const errorMessage =
121
+ err instanceof Error ? err.message : "Unknown error occurred";
122
+ setError(errorMessage);
123
+ stableOptions.onError?.(errorMessage);
124
+
125
+ if (isDevelopment) {
126
+ console.error("[useGroq] generate error:", { error: err });
127
+ }
128
+
129
+ throw err;
130
+ } finally {
131
+ setIsLoading(false);
132
+ abortControllerRef.current = null;
133
+ }
134
+ },
135
+ [stableOptions]
136
+ );
137
+
138
+ const generateJSON = useCallback(
139
+ async <T = Record<string, unknown>,>(
140
+ prompt: string,
141
+ config?: GroqGenerationConfig & { schema?: Record<string, unknown> }
142
+ ): Promise<T> => {
143
+ // Cancel any ongoing request
144
+ if (abortControllerRef.current) {
145
+ abortControllerRef.current.abort();
146
+ }
147
+
148
+ abortControllerRef.current = new AbortController();
149
+ setIsLoading(true);
150
+ setError(null);
151
+ setResult(null);
152
+
153
+ if (isDevelopment) {
154
+ console.log("[useGroq] generateJSON called:", {
155
+ promptLength: prompt.length,
156
+ });
157
+ }
158
+
159
+ stableOptions.onStart?.();
160
+
161
+ try {
162
+ const response = await structuredText<T>(prompt, {
163
+ model: stableOptions.model,
164
+ generationConfig: {
165
+ ...stableOptions.generationConfig,
166
+ ...config,
167
+ },
168
+ schema: config?.schema,
169
+ });
170
+
171
+ setResult(JSON.stringify(response, null, 2));
172
+ stableOptions.onSuccess?.(JSON.stringify(response, null, 2));
173
+
174
+ if (isDevelopment) {
175
+ console.log("[useGroq] generateJSON success:", {
176
+ response: !!response,
177
+ });
178
+ }
179
+
180
+ return response;
181
+ } catch (err) {
182
+ const errorMessage =
183
+ err instanceof Error ? err.message : "Unknown error occurred";
184
+ setError(errorMessage);
185
+ stableOptions.onError?.(errorMessage);
186
+
187
+ if (isDevelopment) {
188
+ console.error("[useGroq] generateJSON error:", { error: err });
189
+ }
190
+
191
+ throw err;
192
+ } finally {
193
+ setIsLoading(false);
194
+ abortControllerRef.current = null;
195
+ }
196
+ },
197
+ [stableOptions]
198
+ );
199
+
200
+ const stream = useCallback(
201
+ async (
202
+ prompt: string,
203
+ onChunk: (chunk: string) => void,
204
+ config?: GroqGenerationConfig
205
+ ): Promise<void> => {
206
+ // Cancel any ongoing request
207
+ if (abortControllerRef.current) {
208
+ abortControllerRef.current.abort();
209
+ }
210
+
211
+ abortControllerRef.current = new AbortController();
212
+ setIsLoading(true);
213
+ setError(null);
214
+ setResult(null);
215
+
216
+ let fullContent = "";
217
+
218
+ if (isDevelopment) {
219
+ console.log("[useGroq] stream called:", {
220
+ promptLength: prompt.length,
221
+ });
222
+ }
223
+
224
+ stableOptions.onStart?.();
225
+
226
+ try {
227
+ for await (const streamingResult of streaming(prompt, {
228
+ model: stableOptions.model,
229
+ generationConfig: {
230
+ ...stableOptions.generationConfig,
231
+ ...config,
232
+ },
233
+ callbacks: {
234
+ onChunk: (c) => {
235
+ fullContent += c;
236
+ onChunk(c);
237
+ },
238
+ },
239
+ })) {
240
+ // Consume the async iterator (streaming is handled via callbacks)
241
+ void streamingResult;
242
+ }
243
+
244
+ setResult(fullContent);
245
+ stableOptions.onSuccess?.(fullContent);
246
+
247
+ if (isDevelopment) {
248
+ console.log("[useGroq] stream success:", {
249
+ contentLength: fullContent.length,
250
+ });
251
+ }
252
+ } catch (err) {
253
+ const errorMessage =
254
+ err instanceof Error ? err.message : "Unknown error occurred";
255
+ setError(errorMessage);
256
+ stableOptions.onError?.(errorMessage);
257
+
258
+ if (isDevelopment) {
259
+ console.error("[useGroq] stream error:", { error: err });
260
+ }
261
+
262
+ throw err;
263
+ } finally {
264
+ setIsLoading(false);
265
+ abortControllerRef.current = null;
266
+ }
267
+ },
268
+ [stableOptions]
269
+ );
270
+
271
+ const reset = useCallback(() => {
272
+ if (abortControllerRef.current) {
273
+ abortControllerRef.current.abort();
274
+ abortControllerRef.current = null;
275
+ }
276
+ setIsLoading(false);
277
+ setError(null);
278
+ setResult(null);
279
+ }, []);
280
+
281
+ const clearError = useCallback(() => {
282
+ setError(null);
283
+ }, []);
284
+
285
+ return {
286
+ isLoading,
287
+ error,
288
+ result,
289
+ generate,
290
+ generateJSON,
291
+ stream,
292
+ reset,
293
+ clearError,
294
+ };
295
+ }
package/src/index.ts ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @umituz/web-ai-groq-provider
3
+ * Groq AI text generation provider for React web applications
4
+ *
5
+ * @author umituz
6
+ * @license MIT
7
+ *
8
+ * IMPORTANT: Apps should NOT use this root barrel import.
9
+ * Use subpath imports instead:
10
+ * - @umituz/web-ai-groq-provider/domain
11
+ * - @umituz/web-ai-groq-provider/services
12
+ * - @umituz/web-ai-groq-provider/hooks
13
+ */
14
+
15
+ // Domain layer
16
+ export type {
17
+ GroqMessageRole,
18
+ GroqMessage,
19
+ GroqChatRequest,
20
+ GroqChoice,
21
+ GroqFinishReason,
22
+ GroqUsage,
23
+ GroqChatResponse,
24
+ GroqChunkChoice,
25
+ GroqChatChunk,
26
+ GroqErrorResponse,
27
+ } from "./domain/entities";
28
+
29
+ export type {
30
+ GroqConfig,
31
+ GroqGenerationConfig,
32
+ TextGenerationOptions,
33
+ StreamingCallbacks,
34
+ StructuredGenerationOptions,
35
+ } from "./domain/interfaces";
36
+
37
+ export type { IGroqChatService, IGroqHttpClient } from "./domain/interfaces";
38
+
39
+ // Infrastructure layer
40
+ export {
41
+ GROQ_MODELS,
42
+ DEFAULT_MODELS,
43
+ DEFAULT_GENERATION_CONFIG,
44
+ API_ENDPOINTS,
45
+ DEFAULT_BASE_URL,
46
+ TIMEOUTS,
47
+ } from "./infrastructure/constants";
48
+
49
+ export {
50
+ GroqErrorType,
51
+ mapHttpStatusToErrorType,
52
+ isRetryableError,
53
+ isAuthError,
54
+ } from "./infrastructure/constants";
55
+
56
+ export { groqHttpClient } from "./infrastructure/services";
57
+ export { textGenerationService } from "./infrastructure/services";
58
+
59
+ export {
60
+ createUserMessage,
61
+ createAssistantMessage,
62
+ createSystemMessage,
63
+ createTextMessage,
64
+ promptToMessages,
65
+ extractTextFromMessages,
66
+ formatMessagesForDisplay,
67
+ } from "./infrastructure/utils";
68
+
69
+ export {
70
+ getUserFriendlyError,
71
+ formatErrorForLogging,
72
+ } from "./infrastructure/utils";
73
+
74
+ export { GroqError } from "./infrastructure/utils";
75
+
76
+ // Presentation layer
77
+ export { useGroq } from "./presentation/hooks";
78
+ export type { UseGroqOptions, UseGroqReturn } from "./presentation/hooks";
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Error Type Constants
3
+ * @description Error types and mappings for Groq API
4
+ */
5
+
6
+ /**
7
+ * Error types enum
8
+ */
9
+ export enum GroqErrorType {
10
+ INVALID_API_KEY = "INVALID_API_KEY",
11
+ MISSING_CONFIG = "MISSING_CONFIG",
12
+ NETWORK_ERROR = "NETWORK_ERROR",
13
+ ABORT_ERROR = "ABORT_ERROR",
14
+ RATE_LIMIT_ERROR = "RATE_LIMIT_ERROR",
15
+ SERVER_ERROR = "SERVER_ERROR",
16
+ UNKNOWN_ERROR = "UNKNOWN_ERROR",
17
+ }
18
+
19
+ /**
20
+ * Map HTTP status to error type
21
+ */
22
+ export function mapHttpStatusToErrorType(status: number): GroqErrorType {
23
+ if (status === 401 || status === 403) return GroqErrorType.INVALID_API_KEY;
24
+ if (status === 429) return GroqErrorType.RATE_LIMIT_ERROR;
25
+ if (status >= 500) return GroqErrorType.SERVER_ERROR;
26
+ if (status >= 400) return GroqErrorType.INVALID_API_KEY;
27
+ return GroqErrorType.UNKNOWN_ERROR;
28
+ }
29
+
30
+ /**
31
+ * Check if error is retryable
32
+ */
33
+ export function isRetryableError(errorType: GroqErrorType): boolean {
34
+ return [
35
+ GroqErrorType.NETWORK_ERROR,
36
+ GroqErrorType.RATE_LIMIT_ERROR,
37
+ GroqErrorType.SERVER_ERROR,
38
+ ].includes(errorType);
39
+ }
40
+
41
+ /**
42
+ * Check if error is auth-related
43
+ */
44
+ export function isAuthError(errorType: GroqErrorType): boolean {
45
+ return errorType === GroqErrorType.INVALID_API_KEY;
46
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Groq Constants
3
+ * @description Available models and default configurations
4
+ */
5
+
6
+ /**
7
+ * Available Groq models
8
+ */
9
+ export const GROQ_MODELS = {
10
+ /** Llama 3.1 8B - Fast and efficient (560 T/s) */
11
+ LLAMA_3_1_8B_INSTANT: "llama-3.1-8b-instant",
12
+ /** Llama 3.3 70B - Versatile and powerful (280 T/s) */
13
+ LLAMA_3_3_70B_VERSATILE: "llama-3.3-70b-versatile",
14
+ /** Llama 3.1 70B - Versatile (280 T/s) */
15
+ LLAMA_3_1_70B_VERSATILE: "llama-3.1-70b-versatile",
16
+ /** GPT-OSS 20B - Experimental (1000 T/s) */
17
+ GPT_OSS_20B: "openai/gpt-oss-20b",
18
+ /** GPT-OSS 120B - Large experimental model */
19
+ GPT_OSS_120B: "openai/gpt-oss-120b",
20
+ /** Mixtral 8x7b - MoE model */
21
+ MIXTRAL_8X7B: "mixtral-8x7b-32768",
22
+ /** Gemma 2 9B - Google's model */
23
+ GEMMA_2_9B: "gemma2-9b-it",
24
+ /** Llama 4 Scout 17B - New model (30K T/s) */
25
+ LLAMA_4_SCOUT_17B: "meta-llama/llama-4-scout-17b-16e-instruct",
26
+ /** Kimi K2 - Moonshot AI model */
27
+ KIMI_K2_INSTRUCT: "moonshotai/kimi-k2-instruct",
28
+ /** Qwen 3 32B - Alibaba's model */
29
+ QWEN3_32B: "qwen/qwen3-32b",
30
+ } as const;
31
+
32
+ /**
33
+ * Default models for different use cases
34
+ */
35
+ export const DEFAULT_MODELS = {
36
+ TEXT: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
37
+ FAST: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
38
+ EXPERIMENTAL: GROQ_MODELS.GPT_OSS_20B,
39
+ } as const;
40
+
41
+ /**
42
+ * Default generation configuration
43
+ */
44
+ export const DEFAULT_GENERATION_CONFIG = {
45
+ temperature: 0.7,
46
+ maxTokens: 1024,
47
+ topP: 1.0,
48
+ } as const;
49
+
50
+ /**
51
+ * API endpoints
52
+ */
53
+ export const API_ENDPOINTS = {
54
+ CHAT: "/chat/completions",
55
+ } as const;
56
+
57
+ /**
58
+ * Default base URL
59
+ */
60
+ export const DEFAULT_BASE_URL = "https://api.groq.com/openai/v1" as const;
61
+
62
+ /**
63
+ * Default timeouts
64
+ */
65
+ export const TIMEOUTS = {
66
+ DEFAULT: 30000, // 30 seconds
67
+ STREAMING: 60000, // 60 seconds
68
+ } as const;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Infrastructure Constants Index
3
+ * Subpath: @umituz/web-ai-groq-provider/services
4
+ */
5
+
6
+ export {
7
+ GROQ_MODELS,
8
+ DEFAULT_MODELS,
9
+ DEFAULT_GENERATION_CONFIG,
10
+ API_ENDPOINTS,
11
+ DEFAULT_BASE_URL,
12
+ TIMEOUTS,
13
+ } from "./groq.constants";
14
+
15
+ export {
16
+ GroqErrorType,
17
+ mapHttpStatusToErrorType,
18
+ isRetryableError,
19
+ isAuthError,
20
+ } from "./error.constants";