@umituz/react-native-ai-groq-provider 1.0.0

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,79 @@
1
+ /**
2
+ * Telemetry Hooks
3
+ * Simple telemetry tracking for Groq operations
4
+ */
5
+
6
+ type TelemetryEvent = {
7
+ name: string;
8
+ timestamp: number;
9
+ data?: Record<string, unknown>;
10
+ };
11
+
12
+ class Telemetry {
13
+ private events: TelemetryEvent[] = [];
14
+ private enabled = __DEV__;
15
+
16
+ /**
17
+ * Log a telemetry event
18
+ */
19
+ log(name: string, data?: Record<string, unknown>): void {
20
+ if (!this.enabled) return;
21
+
22
+ const event: TelemetryEvent = {
23
+ name,
24
+ timestamp: Date.now(),
25
+ data,
26
+ };
27
+
28
+ this.events.push(event);
29
+
30
+ if (__DEV__) {
31
+ console.log(`[Groq Telemetry] ${name}`, data);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Get all events
37
+ */
38
+ getEvents(): TelemetryEvent[] {
39
+ return [...this.events];
40
+ }
41
+
42
+ /**
43
+ * Clear all events
44
+ */
45
+ clear(): void {
46
+ this.events = [];
47
+ }
48
+
49
+ /**
50
+ * Enable/disable telemetry
51
+ */
52
+ setEnabled(enabled: boolean): void {
53
+ this.enabled = enabled;
54
+ }
55
+
56
+ /**
57
+ * Check if telemetry is enabled
58
+ */
59
+ isEnabled(): boolean {
60
+ return this.enabled;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Singleton instance
66
+ */
67
+ export const telemetry = new Telemetry();
68
+
69
+ /**
70
+ * Hook to use telemetry in components
71
+ */
72
+ export function useTelemetry() {
73
+ return {
74
+ log: telemetry.log.bind(telemetry),
75
+ getEvents: telemetry.getEvents.bind(telemetry),
76
+ clear: telemetry.clear.bind(telemetry),
77
+ isEnabled: telemetry.isEnabled.bind(telemetry),
78
+ };
79
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Telemetry
3
+ */
4
+
5
+ export { telemetry, useTelemetry } from "./TelemetryHooks";
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Async State Utilities
3
+ */
4
+
5
+ /**
6
+ * State setters for async operations
7
+ */
8
+ export interface AsyncStateSetters<T> {
9
+ setLoading: (loading: boolean) => void;
10
+ setError: (error: string | null) => void;
11
+ setData?: (data: T | null) => void;
12
+ }
13
+
14
+ /**
15
+ * Callbacks for async operations
16
+ */
17
+ export interface AsyncCallbacks<T> {
18
+ onSuccess?: (data: T) => void;
19
+ onError?: (error: Error) => void;
20
+ }
21
+
22
+ /**
23
+ * Execute async function with state management
24
+ */
25
+ export async function executeWithState<T>(
26
+ setters: AsyncStateSetters<T>,
27
+ asyncFn: () => Promise<T>,
28
+ callbacks?: AsyncCallbacks<T>
29
+ ): Promise<T> {
30
+ const { setLoading, setError, setData } = setters;
31
+
32
+ try {
33
+ setLoading(true);
34
+ setError(null);
35
+
36
+ const result = await asyncFn();
37
+
38
+ setData?.(result);
39
+ callbacks?.onSuccess?.(result);
40
+
41
+ return result;
42
+ } catch (error) {
43
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
44
+ setError(errorMessage);
45
+ callbacks?.onError?.(error as Error);
46
+ throw error;
47
+ } finally {
48
+ setLoading(false);
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Execute async function with retry logic
54
+ */
55
+ export async function executeWithRetry<T>(
56
+ asyncFn: () => Promise<T>,
57
+ maxRetries: number = 3,
58
+ delayMs: number = 1000
59
+ ): Promise<T> {
60
+ let lastError: Error | null = null;
61
+
62
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
63
+ try {
64
+ return await asyncFn();
65
+ } catch (error) {
66
+ lastError = error as Error;
67
+
68
+ if (attempt < maxRetries - 1) {
69
+ // Wait before retrying with exponential backoff
70
+ await new Promise((resolve) => setTimeout(resolve, delayMs * Math.pow(2, attempt)));
71
+ }
72
+ }
73
+ }
74
+
75
+ throw lastError;
76
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Async utilities
3
+ */
4
+
5
+ export { executeWithState, executeWithRetry } from "./execute-state.util";
6
+ export type { AsyncStateSetters, AsyncCallbacks } from "./execute-state.util";
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Content Mapper Utility
3
+ * Utilities for working with message content
4
+ */
5
+
6
+ import type { GroqMessage } from "../../domain/entities";
7
+
8
+ /**
9
+ * Create a user message
10
+ */
11
+ export function createUserMessage(content: string): GroqMessage {
12
+ return {
13
+ role: "user",
14
+ content,
15
+ };
16
+ }
17
+
18
+ /**
19
+ * Create an assistant message
20
+ */
21
+ export function createAssistantMessage(content: string): GroqMessage {
22
+ return {
23
+ role: "assistant",
24
+ content,
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Create a system message
30
+ */
31
+ export function createSystemMessage(content: string): GroqMessage {
32
+ return {
33
+ role: "system",
34
+ content,
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Create a text message (defaults to user role)
40
+ */
41
+ export function createTextMessage(content: string, role: "user" | "assistant" | "system" = "user"): GroqMessage {
42
+ return {
43
+ role,
44
+ content,
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Convert a simple prompt string to an array of messages
50
+ */
51
+ export function promptToMessages(prompt: string, systemPrompt?: string): GroqMessage[] {
52
+ const messages: GroqMessage[] = [];
53
+
54
+ if (systemPrompt) {
55
+ messages.push({
56
+ role: "system",
57
+ content: systemPrompt,
58
+ });
59
+ }
60
+
61
+ messages.push({
62
+ role: "user",
63
+ content: prompt,
64
+ });
65
+
66
+ return messages;
67
+ }
68
+
69
+ /**
70
+ * Extract text content from an array of messages
71
+ */
72
+ export function extractTextFromMessages(messages: GroqMessage[]): string {
73
+ return messages.map((m) => `[${m.role}]: ${m.content}`).join("\n\n");
74
+ }
75
+
76
+ /**
77
+ * Format messages for display in UI
78
+ */
79
+ export function formatMessagesForDisplay(messages: GroqMessage[]): string {
80
+ return messages
81
+ .map((m) => {
82
+ const role = m.role === "system" ? "System" : m.role === "user" ? "You" : "Assistant";
83
+ return `${role}:\n${m.content}`;
84
+ })
85
+ .join("\n\n---\n\n");
86
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Error Mapper Utility
3
+ * Utilities for handling and mapping errors
4
+ */
5
+
6
+ import { GroqError, GroqErrorType } from "../../domain/entities/error.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 credentials.";
16
+ case GroqErrorType.MISSING_CONFIG:
17
+ return "Configuration missing. Please initialize the Groq provider.";
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 wait a moment and try again.";
24
+ case GroqErrorType.QUOTA_EXCEEDED:
25
+ return "API quota exceeded. Please check your Groq account.";
26
+ case GroqErrorType.SERVER_ERROR:
27
+ return "Groq server error. Please try again later.";
28
+ default:
29
+ return error.message || "An unknown error occurred.";
30
+ }
31
+ }
32
+
33
+ if (error instanceof Error) {
34
+ return error.message;
35
+ }
36
+
37
+ return "An unknown error occurred.";
38
+ }
39
+
40
+ /**
41
+ * Check if error is retryable
42
+ */
43
+ export function isRetryableError(error: unknown): boolean {
44
+ if (error instanceof GroqError) {
45
+ return (
46
+ error.type === GroqErrorType.RATE_LIMIT_ERROR ||
47
+ error.type === GroqErrorType.SERVER_ERROR ||
48
+ error.type === GroqErrorType.NETWORK_ERROR
49
+ );
50
+ }
51
+ return false;
52
+ }
53
+
54
+ /**
55
+ * Check if error is authentication related
56
+ */
57
+ export function isAuthError(error: unknown): boolean {
58
+ if (error instanceof GroqError) {
59
+ return error.type === GroqErrorType.INVALID_API_KEY;
60
+ }
61
+ return false;
62
+ }
63
+
64
+ /**
65
+ * Format error for logging
66
+ */
67
+ export function formatErrorForLogging(error: unknown): string {
68
+ if (error instanceof GroqError) {
69
+ return `[GroqError:${error.type}] ${error.message}`;
70
+ }
71
+
72
+ if (error instanceof Error) {
73
+ return `[${error.name}] ${error.message}`;
74
+ }
75
+
76
+ return String(error);
77
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Infrastructure Utilities
3
+ */
4
+
5
+ export * from "./content-mapper.util";
6
+ export * from "./error-mapper.util";
7
+ export * from "./async";
@@ -0,0 +1,224 @@
1
+ /**
2
+ * useGroq Hook
3
+ * Main React hook for Groq text generation
4
+ */
5
+
6
+ import { useState, useCallback, useRef } from "react";
7
+ import type { GroqGenerationConfig, GroqChatConfig } from "../../domain/entities";
8
+ import { groqHttpClient } from "../../infrastructure/services/GroqClient";
9
+ import { chatSessionService } from "../../infrastructure/services/ChatSession";
10
+ import { textGeneration, chatGeneration } from "../../infrastructure/services/TextGeneration";
11
+ import { structuredText } from "../../infrastructure/services/StructuredText";
12
+ import { streaming } from "../../infrastructure/services/Streaming";
13
+ import { getUserFriendlyError } from "../../infrastructure/utils/error-mapper.util";
14
+ import { telemetry } from "../../infrastructure/telemetry";
15
+
16
+ export interface UseGroqOptions {
17
+ /** Initial model to use */
18
+ model?: string;
19
+ /** Default generation config */
20
+ generationConfig?: GroqGenerationConfig;
21
+ /** Callback when generation starts */
22
+ onStart?: () => void;
23
+ /** Callback when generation completes */
24
+ onSuccess?: (result: string) => void;
25
+ /** Callback when generation fails */
26
+ onError?: (error: string) => void;
27
+ }
28
+
29
+ export interface UseGroqReturn {
30
+ /** Loading state */
31
+ isLoading: boolean;
32
+ /** Error message */
33
+ error: string | null;
34
+ /** Generated result */
35
+ result: string | null;
36
+ /** Generate text from a prompt */
37
+ generate: (prompt: string, options?: GroqGenerationConfig) => Promise<string>;
38
+ /** Generate structured JSON output */
39
+ generateJSON: <T = Record<string, unknown>>(
40
+ prompt: string,
41
+ options?: GroqGenerationConfig & { schema?: Record<string, unknown> }
42
+ ) => Promise<T>;
43
+ /** Stream text generation */
44
+ stream: (
45
+ prompt: string,
46
+ onChunk: (chunk: string) => void,
47
+ options?: GroqGenerationConfig
48
+ ) => Promise<void>;
49
+ /** Reset state */
50
+ reset: () => void;
51
+ /** Clear error */
52
+ clearError: () => void;
53
+ }
54
+
55
+ /**
56
+ * Hook for Groq text generation
57
+ */
58
+ export function useGroq(options: UseGroqOptions = {}): UseGroqReturn {
59
+ const [isLoading, setIsLoading] = useState(false);
60
+ const [error, setError] = useState<string | null>(null);
61
+ const [result, setResult] = useState<string | null>(null);
62
+ const abortControllerRef = useRef<AbortController | null>(null);
63
+
64
+ const generate = useCallback(
65
+ async (prompt: string, config?: GroqGenerationConfig): Promise<string> => {
66
+ // Cancel any ongoing request
67
+ if (abortControllerRef.current) {
68
+ abortControllerRef.current.abort();
69
+ }
70
+
71
+ abortControllerRef.current = new AbortController();
72
+ setIsLoading(true);
73
+ setError(null);
74
+ setResult(null);
75
+
76
+ telemetry.log("groq_generate_start", { prompt: prompt.substring(0, 100) });
77
+ options.onStart?.();
78
+
79
+ try {
80
+ const response = await textGeneration(prompt, {
81
+ model: options.model,
82
+ generationConfig: { ...options.generationConfig, ...config },
83
+ });
84
+
85
+ setResult(response);
86
+ options.onSuccess?.(response);
87
+ telemetry.log("groq_generate_success", { responseLength: response.length });
88
+
89
+ return response;
90
+ } catch (err) {
91
+ const errorMessage = getUserFriendlyError(err);
92
+ setError(errorMessage);
93
+ options.onError?.(errorMessage);
94
+ telemetry.log("groq_generate_error", { error: errorMessage });
95
+ throw err;
96
+ } finally {
97
+ setIsLoading(false);
98
+ abortControllerRef.current = null;
99
+ }
100
+ },
101
+ [options]
102
+ );
103
+
104
+ const generateJSON = useCallback(
105
+ async <T = Record<string, unknown>,>(
106
+ prompt: string,
107
+ config?: GroqGenerationConfig & { schema?: Record<string, unknown> }
108
+ ): Promise<T> => {
109
+ // Cancel any ongoing request
110
+ if (abortControllerRef.current) {
111
+ abortControllerRef.current.abort();
112
+ }
113
+
114
+ abortControllerRef.current = new AbortController();
115
+ setIsLoading(true);
116
+ setError(null);
117
+ setResult(null);
118
+
119
+ telemetry.log("groq_generate_json_start", { prompt: prompt.substring(0, 100) });
120
+ options.onStart?.();
121
+
122
+ try {
123
+ const response = await structuredText<T>(prompt, {
124
+ model: options.model,
125
+ generationConfig: { ...options.generationConfig, ...config },
126
+ schema: config?.schema,
127
+ });
128
+
129
+ setResult(JSON.stringify(response, null, 2));
130
+ options.onSuccess?.(JSON.stringify(response, null, 2));
131
+ telemetry.log("groq_generate_json_success");
132
+
133
+ return response;
134
+ } catch (err) {
135
+ const errorMessage = getUserFriendlyError(err);
136
+ setError(errorMessage);
137
+ options.onError?.(errorMessage);
138
+ telemetry.log("groq_generate_json_error", { error: errorMessage });
139
+ throw err;
140
+ } finally {
141
+ setIsLoading(false);
142
+ abortControllerRef.current = null;
143
+ }
144
+ },
145
+ [options]
146
+ );
147
+
148
+ const stream = useCallback(
149
+ async (
150
+ prompt: string,
151
+ onChunk: (chunk: string) => void,
152
+ config?: GroqGenerationConfig
153
+ ): Promise<void> => {
154
+ // Cancel any ongoing request
155
+ if (abortControllerRef.current) {
156
+ abortControllerRef.current.abort();
157
+ }
158
+
159
+ abortControllerRef.current = new AbortController();
160
+ setIsLoading(true);
161
+ setError(null);
162
+ setResult(null);
163
+
164
+ let fullContent = "";
165
+
166
+ telemetry.log("groq_stream_start", { prompt: prompt.substring(0, 100) });
167
+ options.onStart?.();
168
+
169
+ try {
170
+ for await (const chunk of streaming(prompt, {
171
+ model: options.model,
172
+ generationConfig: { ...options.generationConfig, ...config },
173
+ callbacks: {
174
+ onChunk: (c) => {
175
+ fullContent += c;
176
+ onChunk(c);
177
+ },
178
+ },
179
+ })) {
180
+ // Streamed via callback
181
+ }
182
+
183
+ setResult(fullContent);
184
+ options.onSuccess?.(fullContent);
185
+ telemetry.log("groq_stream_success", { contentLength: fullContent.length });
186
+ } catch (err) {
187
+ const errorMessage = getUserFriendlyError(err);
188
+ setError(errorMessage);
189
+ options.onError?.(errorMessage);
190
+ telemetry.log("groq_stream_error", { error: errorMessage });
191
+ throw err;
192
+ } finally {
193
+ setIsLoading(false);
194
+ abortControllerRef.current = null;
195
+ }
196
+ },
197
+ [options]
198
+ );
199
+
200
+ const reset = useCallback(() => {
201
+ if (abortControllerRef.current) {
202
+ abortControllerRef.current.abort();
203
+ abortControllerRef.current = null;
204
+ }
205
+ setIsLoading(false);
206
+ setError(null);
207
+ setResult(null);
208
+ }, []);
209
+
210
+ const clearError = useCallback(() => {
211
+ setError(null);
212
+ }, []);
213
+
214
+ return {
215
+ isLoading,
216
+ error,
217
+ result,
218
+ generate,
219
+ generateJSON,
220
+ stream,
221
+ reset,
222
+ clearError,
223
+ };
224
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * useOperationManager Hook
3
+ * Manages async operations with loading, error, and success states
4
+ */
5
+
6
+ import { useState, useCallback, useRef } from "react";
7
+ import { getUserFriendlyError } from "../../infrastructure/utils/error-mapper.util";
8
+ import { telemetry } from "../../infrastructure/telemetry";
9
+
10
+ export interface OperationState<T> {
11
+ isLoading: boolean;
12
+ error: string | null;
13
+ data: T | null;
14
+ }
15
+
16
+ export interface UseOperationManagerOptions<T> {
17
+ /** Initial data */
18
+ initialData?: T | null;
19
+ /** Callback when operation starts */
20
+ onStart?: () => void;
21
+ /** Callback when operation succeeds */
22
+ onSuccess?: (data: T) => void;
23
+ /** Callback when operation fails */
24
+ onError?: (error: string) => void;
25
+ }
26
+
27
+ /**
28
+ * Hook for managing async operations
29
+ */
30
+ export function useOperationManager<T = unknown>(
31
+ options: UseOperationManagerOptions<T> = {}
32
+ ) {
33
+ const [state, setState] = useState<OperationState<T>>({
34
+ isLoading: false,
35
+ error: null,
36
+ data: options.initialData ?? null,
37
+ });
38
+
39
+ const abortControllerRef = useRef<AbortController | null>(null);
40
+ const operationNameRef = useRef<string>("");
41
+
42
+ const execute = useCallback(
43
+ async <R = T,>(
44
+ operationName: string,
45
+ asyncFn: (signal?: AbortSignal) => Promise<R>,
46
+ config?: { abortPrevious?: boolean }
47
+ ): Promise<R> => {
48
+ // Cancel previous operation if configured
49
+ if (config?.abortPrevious && abortControllerRef.current) {
50
+ abortControllerRef.current.abort();
51
+ }
52
+
53
+ abortControllerRef.current = new AbortController();
54
+ operationNameRef.current = operationName;
55
+
56
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
57
+
58
+ telemetry.log(`${operationName}_start`);
59
+ options.onStart?.();
60
+
61
+ try {
62
+ const result = await asyncFn(abortControllerRef.current.signal);
63
+
64
+ setState((prev) => ({ ...prev, isLoading: false, data: result as unknown as T }));
65
+ options.onSuccess?.(result as unknown as T);
66
+ telemetry.log(`${operationName}_success`);
67
+
68
+ return result;
69
+ } catch (error) {
70
+ const errorMessage = getUserFriendlyError(error);
71
+ setState((prev) => ({ ...prev, isLoading: false, error: errorMessage }));
72
+ options.onError?.(errorMessage);
73
+ telemetry.log(`${operationName}_error`, { error: errorMessage });
74
+ throw error;
75
+ } finally {
76
+ abortControllerRef.current = null;
77
+ }
78
+ },
79
+ [options]
80
+ );
81
+
82
+ const reset = useCallback(() => {
83
+ if (abortControllerRef.current) {
84
+ abortControllerRef.current.abort();
85
+ abortControllerRef.current = null;
86
+ }
87
+ setState((prev) => ({
88
+ ...prev,
89
+ isLoading: false,
90
+ error: null,
91
+ data: options.initialData ?? null,
92
+ }));
93
+ }, [options.initialData]);
94
+
95
+ const clearError = useCallback(() => {
96
+ setState((prev) => ({ ...prev, error: null }));
97
+ }, []);
98
+
99
+ const setData = useCallback((data: T | null) => {
100
+ setState((prev) => ({ ...prev, data }));
101
+ }, []);
102
+
103
+ return {
104
+ ...state,
105
+ execute,
106
+ reset,
107
+ clearError,
108
+ setData,
109
+ };
110
+ }