@umituz/react-native-ai-groq-provider 1.0.24 → 1.0.26

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.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/src/application/use-cases/chat-session.usecase.ts +62 -14
  3. package/src/application/use-cases/streaming.usecase.ts +13 -7
  4. package/src/application/use-cases/structured-generation.usecase.ts +27 -10
  5. package/src/application/use-cases/text-generation.usecase.ts +4 -3
  6. package/src/domain/entities/error.types.ts +17 -2
  7. package/src/index.ts +24 -66
  8. package/src/infrastructure/http/groq-http-client.ts +68 -12
  9. package/src/infrastructure/http/streaming-client.ts +139 -87
  10. package/src/infrastructure/telemetry/TelemetryHooks.ts +39 -19
  11. package/src/infrastructure/utils/calculation.util.ts +59 -63
  12. package/src/infrastructure/utils/content-mapper.util.ts +1 -1
  13. package/src/presentation/hooks/use-groq.hook.ts +58 -41
  14. package/src/providers/ConfigBuilder.ts +2 -73
  15. package/src/providers/ProviderFactory.ts +7 -62
  16. package/src/shared/request-builder.ts +29 -10
  17. package/src/shared/response-handler.ts +93 -0
  18. package/src/types/react-native-global.d.ts +12 -0
  19. package/src/application/use-cases/index.ts +0 -19
  20. package/src/domain/entities/index.ts +0 -7
  21. package/src/infrastructure/http/index.ts +0 -7
  22. package/src/infrastructure/telemetry/index.ts +0 -5
  23. package/src/infrastructure/utils/async/index.ts +0 -6
  24. package/src/infrastructure/utils/index.ts +0 -8
  25. package/src/presentation/hooks/index.ts +0 -7
  26. package/src/presentation/hooks/useGroq.ts +0 -235
  27. package/src/presentation/hooks/useOperationManager.ts +0 -119
  28. package/src/presentation/index.ts +0 -6
  29. package/src/providers/index.ts +0 -16
  30. package/src/shared/index.ts +0 -16
@@ -3,10 +3,12 @@
3
3
  * Main React hook for Groq text generation
4
4
  */
5
5
 
6
- import { useState, useCallback, useMemo } from "react";
7
- import type { GroqGenerationConfig } from "../../domain/entities";
8
- import { generateText, generateStructured, streamText } from "../../application/use-cases";
9
- import { getUserFriendlyError } from "../../utils/error-mapper.util";
6
+ import { useState, useCallback, useRef } from "react";
7
+ import type { GroqGenerationConfig } from "../../domain/entities/groq.types";
8
+ import { generateText } from "../../application/use-cases/text-generation.usecase";
9
+ import { generateStructured } from "../../application/use-cases/structured-generation.usecase";
10
+ import { streamText } from "../../application/use-cases/streaming.usecase";
11
+ import { getUserFriendlyError } from "../../infrastructure/utils/error-mapper.util";
10
12
 
11
13
  export interface UseGroqOptions {
12
14
  model?: string;
@@ -39,24 +41,30 @@ export function useGroq(options: UseGroqOptions = {}): UseGroqReturn {
39
41
  const [error, setError] = useState<string | null>(null);
40
42
  const [result, setResult] = useState<string | null>(null);
41
43
 
42
- const stableOptions = useMemo(
43
- () => ({
44
- model: options.model,
45
- generationConfig: options.generationConfig,
46
- onStart: options.onStart,
47
- onSuccess: options.onSuccess,
48
- onError: options.onError,
49
- }),
50
- [
51
- options.model,
52
- options.generationConfig?.temperature,
53
- options.generationConfig?.maxTokens,
54
- options.generationConfig?.topP,
55
- options.onStart,
56
- options.onSuccess,
57
- options.onError,
58
- ]
59
- );
44
+ // Use refs to avoid unnecessary re-creates and JSON.stringify
45
+ const optionsRef = useRef(options);
46
+ const callbacksRef = useRef({
47
+ onStart: options.onStart,
48
+ onSuccess: options.onSuccess,
49
+ onError: options.onError,
50
+ });
51
+
52
+ // Update refs when options change
53
+ if (options.model !== optionsRef.current.model) {
54
+ optionsRef.current.model = options.model;
55
+ }
56
+ if (options.generationConfig !== optionsRef.current.generationConfig) {
57
+ optionsRef.current.generationConfig = options.generationConfig;
58
+ }
59
+ if (options.onStart !== callbacksRef.current.onStart) {
60
+ callbacksRef.current.onStart = options.onStart;
61
+ }
62
+ if (options.onSuccess !== callbacksRef.current.onSuccess) {
63
+ callbacksRef.current.onSuccess = options.onSuccess;
64
+ }
65
+ if (options.onError !== callbacksRef.current.onError) {
66
+ callbacksRef.current.onError = options.onError;
67
+ }
60
68
 
61
69
  const generate = useCallback(
62
70
  async (prompt: string, config?: GroqGenerationConfig): Promise<string> => {
@@ -64,27 +72,30 @@ export function useGroq(options: UseGroqOptions = {}): UseGroqReturn {
64
72
  setError(null);
65
73
  setResult(null);
66
74
 
67
- stableOptions.onStart?.();
75
+ callbacksRef.current.onStart?.();
68
76
 
69
77
  try {
70
78
  const response = await generateText(prompt, {
71
- model: stableOptions.model,
72
- generationConfig: { ...stableOptions.generationConfig, ...config },
79
+ model: optionsRef.current.model,
80
+ generationConfig: {
81
+ ...optionsRef.current.generationConfig,
82
+ ...config,
83
+ },
73
84
  });
74
85
 
75
86
  setResult(response);
76
- stableOptions.onSuccess?.(response);
87
+ callbacksRef.current.onSuccess?.(response);
77
88
  return response;
78
89
  } catch (err) {
79
90
  const errorMessage = getUserFriendlyError(err);
80
91
  setError(errorMessage);
81
- stableOptions.onError?.(errorMessage);
92
+ callbacksRef.current.onError?.(errorMessage);
82
93
  throw err;
83
94
  } finally {
84
95
  setIsLoading(false);
85
96
  }
86
97
  },
87
- [stableOptions]
98
+ [] // No deps - uses refs
88
99
  );
89
100
 
90
101
  const generateJSON = useCallback(
@@ -96,29 +107,32 @@ export function useGroq(options: UseGroqOptions = {}): UseGroqReturn {
96
107
  setError(null);
97
108
  setResult(null);
98
109
 
99
- stableOptions.onStart?.();
110
+ callbacksRef.current.onStart?.();
100
111
 
101
112
  try {
102
113
  const response = await generateStructured<T>(prompt, {
103
- model: stableOptions.model,
104
- generationConfig: { ...stableOptions.generationConfig, ...config },
114
+ model: optionsRef.current.model,
115
+ generationConfig: {
116
+ ...optionsRef.current.generationConfig,
117
+ ...config,
118
+ },
105
119
  schema: config?.schema,
106
120
  });
107
121
 
108
122
  const jsonStr = JSON.stringify(response, null, 2);
109
123
  setResult(jsonStr);
110
- stableOptions.onSuccess?.(jsonStr);
124
+ callbacksRef.current.onSuccess?.(jsonStr);
111
125
  return response;
112
126
  } catch (err) {
113
127
  const errorMessage = getUserFriendlyError(err);
114
128
  setError(errorMessage);
115
- stableOptions.onError?.(errorMessage);
129
+ callbacksRef.current.onError?.(errorMessage);
116
130
  throw err;
117
131
  } finally {
118
132
  setIsLoading(false);
119
133
  }
120
134
  },
121
- [stableOptions]
135
+ [] // No deps - uses refs
122
136
  );
123
137
 
124
138
  const stream = useCallback(
@@ -133,32 +147,35 @@ export function useGroq(options: UseGroqOptions = {}): UseGroqReturn {
133
147
 
134
148
  let fullContent = "";
135
149
 
136
- stableOptions.onStart?.();
150
+ callbacksRef.current.onStart?.();
137
151
 
138
152
  try {
139
153
  for await (const chunk of streamText(prompt, {
140
- model: stableOptions.model,
141
- generationConfig: { ...stableOptions.generationConfig, ...config },
154
+ model: optionsRef.current.model,
155
+ generationConfig: {
156
+ ...optionsRef.current.generationConfig,
157
+ ...config,
158
+ },
142
159
  callbacks: { onChunk: (c) => {
143
160
  fullContent += c;
144
161
  onChunk(c);
145
162
  }},
146
163
  })) {
147
- // Consume iterator
164
+ fullContent += chunk;
148
165
  }
149
166
 
150
167
  setResult(fullContent);
151
- stableOptions.onSuccess?.(fullContent);
168
+ callbacksRef.current.onSuccess?.(fullContent);
152
169
  } catch (err) {
153
170
  const errorMessage = getUserFriendlyError(err);
154
171
  setError(errorMessage);
155
- stableOptions.onError?.(errorMessage);
172
+ callbacksRef.current.onError?.(errorMessage);
156
173
  throw err;
157
174
  } finally {
158
175
  setIsLoading(false);
159
176
  }
160
177
  },
161
- [stableOptions]
178
+ [] // No deps - uses refs
162
179
  );
163
180
 
164
181
  const reset = useCallback(() => {
@@ -3,8 +3,8 @@
3
3
  * Builder pattern for Groq configuration
4
4
  */
5
5
 
6
- import type { GroqConfig, GroqGenerationConfig } from "../domain/entities";
7
- import { DEFAULT_MODELS } from "../domain/entities";
6
+ import type { GroqConfig, GroqGenerationConfig } from "../domain/entities/groq.types";
7
+ import { DEFAULT_MODELS } from "../domain/entities/groq.types";
8
8
 
9
9
  /**
10
10
  * Builder for Groq configuration
@@ -12,41 +12,26 @@ import { DEFAULT_MODELS } from "../domain/entities";
12
12
  export class ConfigBuilder {
13
13
  private config: Partial<GroqConfig> = {};
14
14
 
15
- /**
16
- * Set API key
17
- */
18
15
  withApiKey(apiKey: string): ConfigBuilder {
19
16
  this.config.apiKey = apiKey;
20
17
  return this;
21
18
  }
22
19
 
23
- /**
24
- * Set base URL
25
- */
26
20
  withBaseUrl(baseUrl: string): ConfigBuilder {
27
21
  this.config.baseUrl = baseUrl;
28
22
  return this;
29
23
  }
30
24
 
31
- /**
32
- * Set timeout
33
- */
34
25
  withTimeout(timeoutMs: number): ConfigBuilder {
35
26
  this.config.timeoutMs = timeoutMs;
36
27
  return this;
37
28
  }
38
29
 
39
- /**
40
- * Set default text model
41
- */
42
30
  withTextModel(model: string): ConfigBuilder {
43
31
  this.config.textModel = model;
44
32
  return this;
45
33
  }
46
34
 
47
- /**
48
- * Build configuration
49
- */
50
35
  build(): GroqConfig {
51
36
  if (!this.config.apiKey) {
52
37
  throw new Error("API key is required. Use withApiKey() to set it.");
@@ -59,13 +44,6 @@ export class ConfigBuilder {
59
44
  textModel: this.config.textModel || DEFAULT_MODELS.TEXT,
60
45
  };
61
46
  }
62
-
63
- /**
64
- * Create a new builder instance
65
- */
66
- static create(): ConfigBuilder {
67
- return new ConfigBuilder();
68
- }
69
47
  }
70
48
 
71
49
  /**
@@ -74,86 +52,37 @@ export class ConfigBuilder {
74
52
  export class GenerationConfigBuilder {
75
53
  private config: GroqGenerationConfig = {};
76
54
 
77
- /**
78
- * Set temperature
79
- */
80
55
  withTemperature(temperature: number): GenerationConfigBuilder {
81
56
  this.config.temperature = temperature;
82
57
  return this;
83
58
  }
84
59
 
85
- /**
86
- * Set max tokens
87
- */
88
60
  withMaxTokens(maxTokens: number): GenerationConfigBuilder {
89
61
  this.config.maxTokens = maxTokens;
90
62
  return this;
91
63
  }
92
64
 
93
- /**
94
- * Set top P
95
- */
96
65
  withTopP(topP: number): GenerationConfigBuilder {
97
66
  this.config.topP = topP;
98
67
  return this;
99
68
  }
100
69
 
101
- /**
102
- * Set frequency penalty
103
- */
104
70
  withFrequencyPenalty(penalty: number): GenerationConfigBuilder {
105
71
  this.config.frequencyPenalty = penalty;
106
72
  return this;
107
73
  }
108
74
 
109
- /**
110
- * Set presence penalty
111
- */
112
75
  withPresencePenalty(penalty: number): GenerationConfigBuilder {
113
76
  this.config.presencePenalty = penalty;
114
77
  return this;
115
78
  }
116
79
 
117
- /**
118
- * Set stop sequences
119
- */
120
80
  withStop(stop: string[]): GenerationConfigBuilder {
121
81
  this.config.stop = stop;
122
82
  return this;
123
83
  }
124
84
 
125
- /**
126
- * Build configuration
127
- */
128
85
  build(): GroqGenerationConfig {
129
86
  return { ...this.config };
130
87
  }
131
-
132
- /**
133
- * Create a new builder instance
134
- */
135
- static create(): GenerationConfigBuilder {
136
- return new GenerationConfigBuilder();
137
- }
138
-
139
- /**
140
- * Create a balanced configuration
141
- */
142
- static balanced(): GenerationConfigBuilder {
143
- return new GenerationConfigBuilder().withTemperature(0.7);
144
- }
145
-
146
- /**
147
- * Create a creative configuration
148
- */
149
- static creative(): GenerationConfigBuilder {
150
- return new GenerationConfigBuilder().withTemperature(1.0);
151
- }
152
-
153
- /**
154
- * Create a precise configuration
155
- */
156
- static precise(): GenerationConfigBuilder {
157
- return new GenerationConfigBuilder().withTemperature(0.3);
158
- }
159
88
  }
@@ -3,8 +3,7 @@
3
3
  * Factory for creating configured Groq provider instances
4
4
  */
5
5
 
6
- import { groqHttpClient } from "../infrastructure/services/GroqClient";
7
- import { ConfigBuilder, GenerationConfigBuilder } from "./ConfigBuilder";
6
+ import { groqHttpClient } from "../infrastructure/http/groq-http-client";
8
7
 
9
8
  /**
10
9
  * Provider configuration options
@@ -16,14 +15,6 @@ export interface ProviderConfig {
16
15
  defaultModel?: string;
17
16
  }
18
17
 
19
- /**
20
- * Provider factory options
21
- */
22
- export interface ProviderFactoryOptions {
23
- enableTelemetry?: boolean;
24
- onError?: (error: Error) => void;
25
- }
26
-
27
18
  /**
28
19
  * Initialize Groq provider with configuration
29
20
  */
@@ -37,61 +28,15 @@ export function initializeProvider(config: ProviderConfig): void {
37
28
  }
38
29
 
39
30
  /**
40
- * Provider factory - creates configured provider instances
31
+ * Reset provider configuration
41
32
  */
42
- export const providerFactory = {
43
- /**
44
- * Create a new provider instance
45
- */
46
- create(config: ProviderConfig): void {
47
- initializeProvider(config);
48
- },
49
-
50
- /**
51
- * Create provider from environment variables
52
- */
53
- fromEnv(): void {
54
- const apiKey = process.env.GROQ_API_KEY;
55
-
56
- if (!apiKey) {
57
- throw new Error("GROQ_API_KEY environment variable is not set");
58
- }
59
-
60
- initializeProvider({
61
- apiKey,
62
- baseUrl: process.env.GROQ_BASE_URL,
63
- timeoutMs: process.env.GROQ_TIMEOUT_MS ? parseInt(process.env.GROQ_TIMEOUT_MS) : undefined,
64
- });
65
- },
66
-
67
- /**
68
- * Reset provider (clear configuration)
69
- */
70
- reset(): void {
71
- groqHttpClient.reset();
72
- },
73
-
74
- /**
75
- * Check if provider is initialized
76
- */
77
- isInitialized(): boolean {
78
- return groqHttpClient.isInitialized();
79
- },
80
- };
81
-
82
- /**
83
- * Convenience function to initialize provider
84
- */
85
- export function configureProvider(config: ProviderConfig): void {
86
- providerFactory.create(config);
33
+ export function resetProvider(): void {
34
+ groqHttpClient.reset();
87
35
  }
88
36
 
89
37
  /**
90
- * Convenience function to reset provider
38
+ * Check if provider is initialized
91
39
  */
92
- export function resetProvider(): void {
93
- providerFactory.reset();
40
+ export function isProviderInitialized(): boolean {
41
+ return groqHttpClient.isInitialized();
94
42
  }
95
-
96
- // Re-export builders
97
- export { ConfigBuilder, GenerationConfigBuilder };
@@ -7,8 +7,8 @@ import type {
7
7
  GroqChatRequest,
8
8
  GroqGenerationConfig,
9
9
  GroqMessage,
10
- } from "../domain/entities";
11
- import { DEFAULT_MODELS } from "../domain/entities";
10
+ } from "../domain/entities/groq.types";
11
+ import { DEFAULT_MODELS } from "../domain/entities/groq.types";
12
12
 
13
13
  export interface RequestBuilderOptions {
14
14
  model?: string;
@@ -29,17 +29,36 @@ export class RequestBuilder {
29
29
  defaultMaxTokens = 1024,
30
30
  } = options;
31
31
 
32
- return {
32
+ const request: GroqChatRequest = {
33
33
  model,
34
34
  messages,
35
- temperature: generationConfig.temperature ?? defaultTemperature,
36
- max_tokens: generationConfig.maxTokens ?? defaultMaxTokens,
37
- top_p: generationConfig.topP,
38
- n: generationConfig.n,
39
- stop: generationConfig.stop,
40
- frequency_penalty: generationConfig.frequencyPenalty,
41
- presence_penalty: generationConfig.presencePenalty,
35
+ temperature: generationConfig.temperature !== undefined
36
+ ? generationConfig.temperature
37
+ : defaultTemperature,
38
+ max_tokens: generationConfig.maxTokens !== undefined
39
+ ? generationConfig.maxTokens
40
+ : defaultMaxTokens,
42
41
  };
42
+
43
+ // Only include defined optional properties
44
+ // Map camelCase to snake_case for API
45
+ if (generationConfig.topP !== undefined) {
46
+ request.top_p = generationConfig.topP;
47
+ }
48
+ if (generationConfig.n !== undefined) {
49
+ request.n = generationConfig.n;
50
+ }
51
+ if (generationConfig.stop !== undefined) {
52
+ request.stop = generationConfig.stop;
53
+ }
54
+ if (generationConfig.frequencyPenalty !== undefined) {
55
+ request.frequency_penalty = generationConfig.frequencyPenalty;
56
+ }
57
+ if (generationConfig.presencePenalty !== undefined) {
58
+ request.presence_penalty = generationConfig.presencePenalty;
59
+ }
60
+
61
+ return request;
43
62
  }
44
63
 
45
64
  static buildPromptRequest(
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Response Handler Utility
3
+ * Handles API response parsing and extraction
4
+ */
5
+
6
+ import type { GroqChatResponse, GroqUsage, GroqFinishReason } from "../domain/entities/groq.types";
7
+
8
+ interface Logger {
9
+ debug: (tag: string, message: string, context?: Record<string, unknown>) => void;
10
+ isEnabled?: () => boolean;
11
+ }
12
+
13
+ export interface ResponseHandlerResult {
14
+ content: string;
15
+ usage: {
16
+ promptTokens: number;
17
+ completionTokens: number;
18
+ totalTokens: number;
19
+ };
20
+ finishReason: GroqFinishReason;
21
+ }
22
+
23
+ export class ResponseHandler {
24
+ /**
25
+ * Extract content from chat completion response
26
+ */
27
+ static extractContent(response: GroqChatResponse): string {
28
+ const choices = response.choices;
29
+ if (!choices || choices.length === 0) {
30
+ return "";
31
+ }
32
+ return choices[0].message?.content || "";
33
+ }
34
+
35
+ /**
36
+ * Handle complete response and extract all relevant data
37
+ */
38
+ static handleResponse(response: GroqChatResponse): ResponseHandlerResult {
39
+ const choices = response.choices;
40
+
41
+ if (!choices || choices.length === 0) {
42
+ return {
43
+ content: "",
44
+ usage: this.extractUsage(response.usage),
45
+ finishReason: "stop",
46
+ };
47
+ }
48
+
49
+ const choice = choices[0];
50
+ return {
51
+ content: choice.message?.content || "",
52
+ usage: this.extractUsage(response.usage),
53
+ finishReason: choice.finish_reason || "stop",
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Extract usage information
59
+ */
60
+ private static extractUsage(usage: GroqUsage): {
61
+ promptTokens: number;
62
+ completionTokens: number;
63
+ totalTokens: number;
64
+ } {
65
+ return {
66
+ promptTokens: usage.prompt_tokens || 0,
67
+ completionTokens: usage.completion_tokens || 0,
68
+ totalTokens: usage.total_tokens || 0,
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Log response details (only if logger is enabled)
74
+ */
75
+ static logResponse(logger: Logger, response: GroqChatResponse, apiMs: number): void {
76
+ // Early return if logging is disabled
77
+ if (logger.isEnabled && !logger.isEnabled()) {
78
+ return;
79
+ }
80
+
81
+ const choices = response.choices;
82
+ const firstChoice = choices?.[0];
83
+
84
+ logger.debug("ResponseHandler", "API response received", {
85
+ model: response.model,
86
+ promptTokens: response.usage.prompt_tokens,
87
+ completionTokens: response.usage.completion_tokens,
88
+ totalTokens: response.usage.total_tokens,
89
+ finishReason: firstChoice?.finish_reason,
90
+ apiDuration: `${apiMs}ms`,
91
+ });
92
+ }
93
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * React Native Global Declarations
3
+ * Type definitions for React Native globals
4
+ */
5
+
6
+ declare const __DEV__: boolean;
7
+
8
+ declare global {
9
+ const __DEV__: boolean;
10
+ }
11
+
12
+ export {};
@@ -1,19 +0,0 @@
1
- /**
2
- * Application Layer - Use Cases
3
- * Business logic orchestrators
4
- */
5
-
6
- export { generateText } from "./text-generation.usecase";
7
- export type { TextGenerationOptions } from "./text-generation.usecase";
8
-
9
- export { generateStructured } from "./structured-generation.usecase";
10
- export type { StructuredGenerationOptions } from "./structured-generation.usecase";
11
-
12
- export { streamText } from "./streaming.usecase";
13
- export type { StreamingCallbacks, StreamingOptions } from "./streaming.usecase";
14
-
15
- export {
16
- chatSessionManager,
17
- type ChatSession,
18
- type ChatSendResult,
19
- } from "./chat-session.usecase";
@@ -1,7 +0,0 @@
1
- /**
2
- * Domain Entities
3
- */
4
-
5
- export * from "./groq.types";
6
- export * from "./models";
7
- export * from "./error.types";
@@ -1,7 +0,0 @@
1
- /**
2
- * Infrastructure HTTP Layer
3
- * HTTP communication with Groq API
4
- */
5
-
6
- export { groqHttpClient } from "./groq-http-client";
7
- export { streamChatCompletion } from "./streaming-client";
@@ -1,5 +0,0 @@
1
- /**
2
- * Telemetry
3
- */
4
-
5
- export { telemetry, useTelemetry } from "./TelemetryHooks";
@@ -1,6 +0,0 @@
1
- /**
2
- * Async utilities
3
- */
4
-
5
- export { executeWithState, executeWithRetry } from "./execute-state.util";
6
- export type { AsyncStateSetters, AsyncCallbacks } from "./execute-state.util";
@@ -1,8 +0,0 @@
1
- /**
2
- * Infrastructure Utilities
3
- */
4
-
5
- export * from "./content-mapper.util";
6
- export * from "./error-mapper.util";
7
- export * from "./calculation.util";
8
- export * from "./async";
@@ -1,7 +0,0 @@
1
- /**
2
- * Presentation Layer - Hooks
3
- * React hooks for UI integration
4
- */
5
-
6
- export { useGroq } from "./use-groq.hook";
7
- export type { UseGroqOptions, UseGroqReturn } from "./use-groq.hook";