@umituz/web-ai-groq-provider 1.1.13 → 2.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.
Files changed (55) hide show
  1. package/README.md +154 -72
  2. package/dist/domains/chat/index.cjs +539 -0
  3. package/dist/domains/chat/index.cjs.map +1 -0
  4. package/dist/domains/chat/index.d.cts +322 -0
  5. package/dist/domains/chat/index.d.ts +322 -0
  6. package/dist/domains/chat/index.js +531 -0
  7. package/dist/domains/chat/index.js.map +1 -0
  8. package/dist/domains/groq/index.cjs +1499 -0
  9. package/dist/domains/groq/index.cjs.map +1 -0
  10. package/dist/domains/groq/index.d.cts +561 -0
  11. package/dist/domains/groq/index.d.ts +561 -0
  12. package/dist/domains/groq/index.js +1453 -0
  13. package/dist/domains/groq/index.js.map +1 -0
  14. package/dist/index.cjs +1993 -0
  15. package/dist/index.cjs.map +1 -0
  16. package/dist/index.d.cts +53 -0
  17. package/dist/index.d.ts +53 -0
  18. package/dist/index.js +1937 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/text-generation.service-BP4Q_SH0.d.cts +252 -0
  21. package/dist/text-generation.service-BP4Q_SH0.d.ts +252 -0
  22. package/package.json +25 -11
  23. package/src/domains/chat/entities/chat.entities.ts +0 -113
  24. package/src/domains/chat/entities/index.ts +0 -14
  25. package/src/domains/chat/hooks/index.ts +0 -5
  26. package/src/domains/chat/hooks/use-chat.hook.ts +0 -259
  27. package/src/domains/chat/index.ts +0 -32
  28. package/src/domains/chat/interfaces/chat.interface.ts +0 -80
  29. package/src/domains/chat/interfaces/index.ts +0 -10
  30. package/src/domains/chat/services/chat.service.ts +0 -195
  31. package/src/domains/chat/services/index.ts +0 -5
  32. package/src/domains/chat/utils/message-formatter.ts +0 -49
  33. package/src/domains/groq/constants/error.constants.ts +0 -46
  34. package/src/domains/groq/constants/groq.constants.ts +0 -68
  35. package/src/domains/groq/constants/index.ts +0 -20
  36. package/src/domains/groq/entities/groq.entities.ts +0 -196
  37. package/src/domains/groq/entities/index.ts +0 -20
  38. package/src/domains/groq/hooks/index.ts +0 -7
  39. package/src/domains/groq/hooks/use-groq.hook.ts +0 -232
  40. package/src/domains/groq/index.ts +0 -72
  41. package/src/domains/groq/interfaces/groq.interface.ts +0 -77
  42. package/src/domains/groq/interfaces/index.ts +0 -22
  43. package/src/domains/groq/services/http-client.service.ts +0 -265
  44. package/src/domains/groq/services/index.ts +0 -9
  45. package/src/domains/groq/services/text-generation.service.ts +0 -238
  46. package/src/domains/groq/utils/cache-manager.util.ts +0 -215
  47. package/src/domains/groq/utils/debounce.util.ts +0 -268
  48. package/src/domains/groq/utils/error.util.ts +0 -57
  49. package/src/domains/groq/utils/groq-error.util.ts +0 -18
  50. package/src/domains/groq/utils/index.ts +0 -37
  51. package/src/domains/groq/utils/message.util.ts +0 -57
  52. package/src/domains/groq/utils/request-deduplicator.util.ts +0 -123
  53. package/src/domains/groq/utils/request-queue.util.ts +0 -178
  54. package/src/domains/groq/utils/retry.util.ts +0 -207
  55. package/src/index.ts +0 -15
@@ -1,22 +0,0 @@
1
- /**
2
- * Domain Interfaces Index
3
- * Subpath: @umituz/web-ai-groq-provider/domain
4
- */
5
-
6
- export type {
7
- TextGenerationOptions,
8
- StreamingCallbacks,
9
- StructuredGenerationOptions,
10
- } from "./groq.interface";
11
-
12
- export type { IGroqChatService, IGroqHttpClient } from "./groq.interface";
13
-
14
- // Re-export from entities for convenience
15
- export type {
16
- GroqConfig,
17
- GroqGenerationConfig,
18
- GroqMessage,
19
- GroqChatRequest,
20
- GroqChatResponse,
21
- GroqChatChunk,
22
- } from "../entities";
@@ -1,265 +0,0 @@
1
- /**
2
- * Groq HTTP Client Service
3
- * @description Handles all HTTP communication with Groq API with performance optimizations
4
- */
5
-
6
- import type { IGroqHttpClient } from "../interfaces";
7
- import type { GroqConfig, GroqChatRequest, GroqChatResponse } from "../entities";
8
- import { GroqError } from "../utils/groq-error.util";
9
- import { GroqErrorType, mapHttpStatusToErrorType } from "../constants/error.constants";
10
- import { DEFAULT_BASE_URL, TIMEOUTS } from "../constants/groq.constants";
11
- import { requestDeduplicator } from "../utils/request-deduplicator.util";
12
- import { requestQueue } from "../utils/request-queue.util";
13
- import { retryManager } from "../utils/retry.util";
14
-
15
- class GroqHttpClientService implements IGroqHttpClient {
16
- private config: GroqConfig | null = null;
17
- private initialized = false;
18
- private pendingAbortControllers = new Set<AbortController>();
19
-
20
- initialize(config: GroqConfig): void {
21
- const apiKey = config.apiKey?.trim();
22
-
23
- if (!apiKey || apiKey.length < 10) {
24
- throw new GroqError(
25
- GroqErrorType.INVALID_API_KEY,
26
- "API key is required and must be at least 10 characters"
27
- );
28
- }
29
-
30
- this.config = {
31
- apiKey,
32
- baseUrl: config.baseUrl || DEFAULT_BASE_URL,
33
- timeoutMs: config.timeoutMs || TIMEOUTS.DEFAULT,
34
- textModel: config.textModel,
35
- };
36
- this.initialized = true;
37
- }
38
-
39
- /**
40
- * Cancel all pending requests
41
- */
42
- cancelPendingRequests(): void {
43
- for (const controller of this.pendingAbortControllers) {
44
- controller.abort();
45
- }
46
- this.pendingAbortControllers.clear();
47
- requestQueue.cancelAll();
48
- requestDeduplicator.cancelAll();
49
- }
50
-
51
- isInitialized(): boolean {
52
- return this.initialized && this.config !== null;
53
- }
54
-
55
- getApiKey(): string {
56
- if (!this.config) {
57
- throw new GroqError(
58
- GroqErrorType.MISSING_CONFIG,
59
- "Client not initialized"
60
- );
61
- }
62
- return this.config.apiKey;
63
- }
64
-
65
- setApiKey(apiKey: string): void {
66
- if (!this.config) {
67
- throw new GroqError(
68
- GroqErrorType.MISSING_CONFIG,
69
- "Client not initialized. Call initialize() first."
70
- );
71
- }
72
- this.config = { ...this.config, apiKey: apiKey.trim() };
73
- }
74
-
75
- async postChatCompletion(request: GroqChatRequest): Promise<GroqChatResponse> {
76
- // Generate deduplication key
77
- const dedupeKey = requestDeduplicator.generateKey({
78
- endpoint: "/chat/completions",
79
- ...request,
80
- });
81
-
82
- // Execute with deduplication, retry, and queue management
83
- return requestDeduplicator.execute(dedupeKey, () =>
84
- retryManager.execute(() =>
85
- requestQueue.add(
86
- () => this.request<GroqChatResponse>("/chat/completions", { ...request, stream: false }),
87
- 10 // High priority for user-initiated requests
88
- )
89
- )
90
- );
91
- }
92
-
93
- async streamChatCompletion(request: GroqChatRequest): Promise<ReadableStream> {
94
- if (!this.config || !this.initialized) {
95
- throw new GroqError(
96
- GroqErrorType.MISSING_CONFIG,
97
- "Client not initialized. Call initialize() first."
98
- );
99
- }
100
-
101
- const url = `${this.config.baseUrl}/chat/completions`;
102
- const timeout = this.config.timeoutMs || TIMEOUTS.STREAMING;
103
-
104
- let controller: AbortController | null = null;
105
- let timeoutId: NodeJS.Timeout | null = null;
106
-
107
- try {
108
- controller = new AbortController();
109
- timeoutId = setTimeout(() => controller?.abort(), timeout);
110
-
111
- this.pendingAbortControllers.add(controller);
112
-
113
- const response = await fetch(url, {
114
- method: "POST",
115
- headers: {
116
- "Content-Type": "application/json",
117
- Authorization: `Bearer ${this.config.apiKey}`,
118
- },
119
- body: JSON.stringify({ ...request, stream: true }),
120
- signal: controller.signal,
121
- });
122
-
123
- if (!response.ok) {
124
- throw new GroqError(
125
- mapHttpStatusToErrorType(response.status),
126
- `HTTP ${response.status}: ${response.statusText}`,
127
- undefined,
128
- { status: response.status, url: response.url }
129
- );
130
- }
131
-
132
- if (!response.body) {
133
- throw new GroqError(
134
- GroqErrorType.NETWORK_ERROR,
135
- "Response body is null"
136
- );
137
- }
138
-
139
- return response.body;
140
- } catch (error) {
141
- throw this.handleRequestError(error);
142
- } finally {
143
- if (timeoutId) {
144
- clearTimeout(timeoutId);
145
- }
146
- if (controller) {
147
- this.pendingAbortControllers.delete(controller);
148
- }
149
- }
150
- }
151
-
152
- private async request<T>(
153
- endpoint: string,
154
- body: unknown,
155
- signal?: AbortSignal
156
- ): Promise<T> {
157
- if (!this.config || !this.initialized) {
158
- throw new GroqError(
159
- GroqErrorType.MISSING_CONFIG,
160
- "Client not initialized. Call initialize() first."
161
- );
162
- }
163
-
164
- const url = `${this.config.baseUrl}${endpoint}`;
165
- const timeout = this.config.timeoutMs || TIMEOUTS.DEFAULT;
166
-
167
- let controller: AbortController | null = null;
168
- let timeoutId: NodeJS.Timeout | null = null;
169
-
170
- try {
171
- controller = new AbortController();
172
- timeoutId = setTimeout(() => controller?.abort(), timeout);
173
-
174
- if (signal) {
175
- signal.addEventListener("abort", () => controller?.abort());
176
- }
177
-
178
- this.pendingAbortControllers.add(controller);
179
-
180
- const response = await fetch(url, {
181
- method: "POST",
182
- headers: {
183
- "Content-Type": "application/json",
184
- Authorization: `Bearer ${this.config.apiKey}`,
185
- },
186
- body: JSON.stringify(body),
187
- signal: controller.signal,
188
- });
189
-
190
- if (!response.ok) {
191
- await this.handleErrorResponse(response);
192
- }
193
-
194
- return (await response.json()) as T;
195
- } catch (error) {
196
- throw this.handleRequestError(error);
197
- } finally {
198
- if (timeoutId) {
199
- clearTimeout(timeoutId);
200
- }
201
- if (controller) {
202
- this.pendingAbortControllers.delete(controller);
203
- }
204
- }
205
- }
206
-
207
- private async handleErrorResponse(response: Response): Promise<never> {
208
- let errorMessage = `HTTP ${response.status}`;
209
- const errorType = mapHttpStatusToErrorType(response.status);
210
-
211
- try {
212
- const errorData = (await response.json()) as {
213
- error?: { message?: string };
214
- };
215
- if (errorData.error?.message) {
216
- errorMessage = errorData.error.message;
217
- }
218
- } catch {
219
- // If parsing fails, use default message
220
- }
221
-
222
- throw new GroqError(errorType, errorMessage, undefined, {
223
- status: response.status,
224
- url: response.url,
225
- });
226
- }
227
-
228
- private handleRequestError(error: unknown): GroqError {
229
- if (error instanceof GroqError) {
230
- return error;
231
- }
232
-
233
- if (error instanceof Error) {
234
- if (error.name === "AbortError") {
235
- return new GroqError(
236
- GroqErrorType.ABORT_ERROR,
237
- "Request was aborted by the client",
238
- error
239
- );
240
- }
241
-
242
- if (error.name === "TypeError" && error.message.includes("network")) {
243
- return new GroqError(
244
- GroqErrorType.NETWORK_ERROR,
245
- "Network error: Unable to connect to Groq API",
246
- error
247
- );
248
- }
249
- }
250
-
251
- return new GroqError(
252
- GroqErrorType.UNKNOWN_ERROR,
253
- error instanceof Error ? error.message : "Unknown error occurred",
254
- error as Error
255
- );
256
- }
257
-
258
- reset(): void {
259
- this.cancelPendingRequests();
260
- this.config = null;
261
- this.initialized = false;
262
- }
263
- }
264
-
265
- export const groqHttpClient = new GroqHttpClientService();
@@ -1,9 +0,0 @@
1
- /**
2
- * Infrastructure Services Index
3
- * Subpath: @umituz/web-ai-groq-provider/services
4
- */
5
-
6
- export { groqHttpClient } from "./http-client.service";
7
- export { textGenerationService } from "./text-generation.service";
8
- export type { IGroqHttpClient } from "../interfaces";
9
- export type { IGroqChatService } from "../interfaces";
@@ -1,238 +0,0 @@
1
- /**
2
- * Text Generation Service
3
- * @description Handles text generation using Groq API with caching and performance optimizations
4
- */
5
-
6
- import type { IGroqChatService } from "../interfaces";
7
- import type {
8
- GroqMessage,
9
- GroqChatRequest,
10
- TextGenerationOptions,
11
- StreamingCallbacks,
12
- StructuredGenerationOptions,
13
- } from "../interfaces";
14
- import { groqHttpClient } from "./http-client.service";
15
- import { GroqError } from "../utils/groq-error.util";
16
- import { GroqErrorType } from "../constants/error.constants";
17
- import { DEFAULT_MODELS, DEFAULT_GENERATION_CONFIG } from "../constants/groq.constants";
18
- import { cacheManager } from "../utils/cache-manager.util";
19
-
20
- class TextGenerationService implements IGroqChatService {
21
- private readonly CACHE_TTL = 300000; // 5 minutes
22
- async generateCompletion(
23
- prompt: string,
24
- options: TextGenerationOptions = {}
25
- ): Promise<string> {
26
- const model = options.model || DEFAULT_MODELS.TEXT;
27
-
28
- // Check cache first
29
- const cacheKey = cacheManager.generateKey({
30
- type: "completion",
31
- model,
32
- prompt,
33
- config: options.generationConfig,
34
- });
35
-
36
- const cached = cacheManager.get<string>(cacheKey);
37
- if (cached !== null) {
38
- return cached;
39
- }
40
-
41
- const messages: GroqMessage[] = [{ role: "user", content: prompt }];
42
- const request = this.buildRequest(model, messages, options.generationConfig);
43
-
44
- const response = await groqHttpClient.postChatCompletion(request);
45
- const content = response.choices[0]?.message?.content;
46
-
47
- if (!content) {
48
- throw new GroqError(
49
- GroqErrorType.UNKNOWN_ERROR,
50
- "No content generated from Groq API"
51
- );
52
- }
53
-
54
- // Cache the result
55
- cacheManager.set(cacheKey, content);
56
-
57
- return content;
58
- }
59
-
60
- async generateChatCompletion(
61
- messages: GroqMessage[],
62
- options: TextGenerationOptions = {}
63
- ): Promise<string> {
64
- const model = options.model || DEFAULT_MODELS.TEXT;
65
-
66
- // Check cache first
67
- const cacheKey = cacheManager.generateKey({
68
- type: "chat",
69
- model,
70
- messages,
71
- config: options.generationConfig,
72
- });
73
-
74
- const cached = cacheManager.get<string>(cacheKey);
75
- if (cached !== null) {
76
- return cached;
77
- }
78
-
79
- const request = this.buildRequest(model, messages, options.generationConfig);
80
- const response = await groqHttpClient.postChatCompletion(request);
81
- const content = response.choices[0]?.message?.content;
82
-
83
- if (!content) {
84
- throw new GroqError(
85
- GroqErrorType.UNKNOWN_ERROR,
86
- "No content generated from Groq API"
87
- );
88
- }
89
-
90
- // Cache the result
91
- cacheManager.set(cacheKey, content);
92
-
93
- return content;
94
- }
95
-
96
- async generateStructured<T>(
97
- prompt: string,
98
- options: StructuredGenerationOptions = {}
99
- ): Promise<T> {
100
- const model = options.model || DEFAULT_MODELS.TEXT;
101
-
102
- // Build JSON prompt
103
- const jsonPrompt = this.buildStructuredPrompt(prompt, options.schema);
104
- const messages: GroqMessage[] = [{ role: "user", content: jsonPrompt }];
105
- const request = this.buildRequest(model, messages, {
106
- ...options.generationConfig,
107
- temperature: 0.1, // Lower temperature for structured output
108
- });
109
-
110
- const response = await groqHttpClient.postChatCompletion(request);
111
- const content = response.choices[0]?.message?.content;
112
-
113
- if (!content) {
114
- throw new GroqError(
115
- GroqErrorType.UNKNOWN_ERROR,
116
- "No content generated from Groq API"
117
- );
118
- }
119
-
120
- // Parse JSON response
121
- try {
122
- const jsonMatch = content.match(/```json\n([\s\S]*?)\n```/) ||
123
- content.match(/\{[\s\S]*\}/);
124
- const jsonStr = jsonMatch ? jsonMatch[1] || jsonMatch[0] : content;
125
- const parsed = JSON.parse(jsonStr) as T;
126
-
127
- return parsed;
128
- } catch (error) {
129
- throw new GroqError(
130
- GroqErrorType.UNKNOWN_ERROR,
131
- "Failed to parse structured response as JSON",
132
- error as Error
133
- );
134
- }
135
- }
136
-
137
- async streamCompletion(
138
- prompt: string,
139
- callbacks: StreamingCallbacks,
140
- options: TextGenerationOptions = {}
141
- ): Promise<void> {
142
- const model = options.model || DEFAULT_MODELS.TEXT;
143
- const messages: GroqMessage[] = [{ role: "user", content: prompt }];
144
- const request = this.buildRequest(model, messages, options.generationConfig);
145
-
146
- try {
147
- const stream = await groqHttpClient.streamChatCompletion(request);
148
- const reader = stream.getReader();
149
- const decoder = new TextDecoder();
150
- const contentChunks: string[] = []; // Use array instead of string concatenation
151
-
152
- while (true) {
153
- const { done, value } = await reader.read();
154
- if (done) break;
155
-
156
- const chunk = decoder.decode(value, { stream: true }); // Optimize with stream option
157
- const lines = chunk.split("\n").filter(line => line.trim() !== "");
158
-
159
- for (const line of lines) {
160
- if (line.startsWith("data: ")) {
161
- const data = line.slice(6);
162
- if (data === "[DONE]") continue;
163
-
164
- try {
165
- const parsed = JSON.parse(data) as {
166
- choices?: Array<{ delta?: { content?: string } }>;
167
- };
168
- const content = parsed.choices?.[0]?.delta?.content;
169
- if (content) {
170
- contentChunks.push(content); // Push to array instead of concatenation
171
- callbacks.onChunk?.(content);
172
- }
173
- } catch {
174
- // Skip invalid JSON chunks
175
- }
176
- }
177
- }
178
- }
179
-
180
- // Join chunks at the end - more efficient than repeated concatenation
181
- const fullContent = contentChunks.join('');
182
- callbacks.onComplete?.(fullContent);
183
- } catch (error) {
184
- callbacks.onError?.(error as Error);
185
- throw error;
186
- }
187
- }
188
-
189
- private buildRequest(
190
- model: string,
191
- messages: GroqMessage[],
192
- config?: TextGenerationOptions["generationConfig"]
193
- ): GroqChatRequest {
194
- return {
195
- model,
196
- messages,
197
- temperature: config?.temperature ?? DEFAULT_GENERATION_CONFIG.temperature,
198
- max_tokens: config?.maxTokens ?? DEFAULT_GENERATION_CONFIG.maxTokens,
199
- top_p: config?.topP ?? DEFAULT_GENERATION_CONFIG.topP,
200
- n: config?.n,
201
- stop: config?.stop,
202
- frequency_penalty: config?.frequencyPenalty,
203
- presence_penalty: config?.presencePenalty,
204
- };
205
- }
206
-
207
- private buildStructuredPrompt(
208
- prompt: string,
209
- schema?: Record<string, unknown>
210
- ): string {
211
- let structuredPrompt = `Please generate a JSON response for the following request.\n\n`;
212
-
213
- if (schema) {
214
- structuredPrompt += `Expected JSON schema:\n${JSON.stringify(schema, null, 2)}\n\n`;
215
- }
216
-
217
- structuredPrompt += `Request:\n${prompt}\n\n`;
218
- structuredPrompt += `Respond only with valid JSON, formatted as:\n\`\`\`json\n{...}\n\`\`\``;
219
-
220
- return structuredPrompt;
221
- }
222
- }
223
-
224
- export const textGenerationService = new TextGenerationService();
225
-
226
- /**
227
- * Clear all cached responses
228
- */
229
- export function clearGenerationCache(): void {
230
- cacheManager.clear();
231
- }
232
-
233
- /**
234
- * Get cache statistics
235
- */
236
- export function getCacheStats() {
237
- return cacheManager.getStats();
238
- }