@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/README.md +200 -0
- package/package.json +58 -0
- package/src/client.ts +357 -0
- package/src/config.ts +149 -0
- package/src/domain/entities/groq.entities.ts +196 -0
- package/src/domain/entities/index.ts +20 -0
- package/src/domain/index.ts +32 -0
- package/src/domain/interfaces/groq.interface.ts +79 -0
- package/src/domain/interfaces/index.ts +22 -0
- package/src/factory.ts +92 -0
- package/src/hook.ts +295 -0
- package/src/index.ts +78 -0
- package/src/infrastructure/constants/error.constants.ts +46 -0
- package/src/infrastructure/constants/groq.constants.ts +68 -0
- package/src/infrastructure/constants/index.ts +20 -0
- package/src/infrastructure/services/http-client.service.ts +246 -0
- package/src/infrastructure/services/index.ts +9 -0
- package/src/infrastructure/services/text-generation.service.ts +221 -0
- package/src/infrastructure/utils/error.util.ts +72 -0
- package/src/infrastructure/utils/groq-error.util.ts +18 -0
- package/src/infrastructure/utils/index.ts +23 -0
- package/src/infrastructure/utils/message.util.ts +57 -0
- package/src/presentation/hooks/index.ts +7 -0
- package/src/presentation/hooks/use-groq.hook.ts +278 -0
- package/src/streaming.ts +194 -0
- package/src/structured-text.ts +211 -0
- package/src/text-generation.ts +165 -0
- package/src/types.ts +264 -0
- package/src/utils.ts +140 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useGroq Hook
|
|
3
|
+
* @description Main React hook for Groq text generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback, useMemo } from "react";
|
|
7
|
+
import type { GroqGenerationConfig } from "../../domain/interfaces";
|
|
8
|
+
import type { TextGenerationOptions, StreamingCallbacks } from "../../domain/interfaces";
|
|
9
|
+
import { textGenerationService } from "../../infrastructure/services/text-generation.service";
|
|
10
|
+
import { groqHttpClient } from "../../infrastructure/services/http-client.service";
|
|
11
|
+
import { GroqError } from "../../infrastructure/utils/groq-error.util";
|
|
12
|
+
import { getUserFriendlyError } from "../../infrastructure/utils/error.util";
|
|
13
|
+
import { DEFAULT_MODELS } from "../../infrastructure/constants/groq.constants";
|
|
14
|
+
|
|
15
|
+
const isDevelopment = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
|
|
16
|
+
|
|
17
|
+
export interface UseGroqOptions {
|
|
18
|
+
/** Initial model to use */
|
|
19
|
+
readonly model?: string;
|
|
20
|
+
/** Default generation config */
|
|
21
|
+
readonly generationConfig?: GroqGenerationConfig;
|
|
22
|
+
/** Callback when generation starts */
|
|
23
|
+
readonly onStart?: () => void;
|
|
24
|
+
/** Callback when generation completes */
|
|
25
|
+
readonly onSuccess?: (result: string) => void;
|
|
26
|
+
/** Callback when generation fails */
|
|
27
|
+
readonly onError?: (error: string) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface UseGroqReturn {
|
|
31
|
+
/** Loading state */
|
|
32
|
+
readonly isLoading: boolean;
|
|
33
|
+
/** Error message */
|
|
34
|
+
readonly error: string | null;
|
|
35
|
+
/** Generated result */
|
|
36
|
+
readonly result: string | null;
|
|
37
|
+
/** Generate text from a prompt */
|
|
38
|
+
generate: (prompt: string, options?: GroqGenerationConfig) => 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
|
+
*/
|
|
59
|
+
export function useGroq(options: UseGroqOptions = {}): UseGroqReturn {
|
|
60
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
61
|
+
const [error, setError] = useState<string | null>(null);
|
|
62
|
+
const [result, setResult] = useState<string | null>(null);
|
|
63
|
+
|
|
64
|
+
// Memoize options to prevent unnecessary callback recreations
|
|
65
|
+
const stableOptions = useMemo(
|
|
66
|
+
() => options,
|
|
67
|
+
[
|
|
68
|
+
options.model,
|
|
69
|
+
options.generationConfig?.temperature,
|
|
70
|
+
options.generationConfig?.maxTokens,
|
|
71
|
+
options.generationConfig?.topP,
|
|
72
|
+
options.onStart,
|
|
73
|
+
options.onSuccess,
|
|
74
|
+
options.onError,
|
|
75
|
+
]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const generate = useCallback(
|
|
79
|
+
async (prompt: string, config?: GroqGenerationConfig): Promise<string> => {
|
|
80
|
+
setIsLoading(true);
|
|
81
|
+
setError(null);
|
|
82
|
+
setResult(null);
|
|
83
|
+
|
|
84
|
+
if (isDevelopment) {
|
|
85
|
+
console.log("[useGroq] generate called:", {
|
|
86
|
+
promptLength: prompt.length,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
stableOptions.onStart?.();
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const response = await textGenerationService.generateCompletion(prompt, {
|
|
94
|
+
model: stableOptions.model,
|
|
95
|
+
generationConfig: {
|
|
96
|
+
...stableOptions.generationConfig,
|
|
97
|
+
...config,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
setResult(response);
|
|
102
|
+
stableOptions.onSuccess?.(response);
|
|
103
|
+
|
|
104
|
+
if (isDevelopment) {
|
|
105
|
+
console.log("[useGroq] generate success:", {
|
|
106
|
+
responseLength: response.length,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return response;
|
|
111
|
+
} catch (err) {
|
|
112
|
+
const errorMessage = getUserFriendlyError(
|
|
113
|
+
err instanceof Error ? err : new Error("Unknown error")
|
|
114
|
+
);
|
|
115
|
+
setError(errorMessage);
|
|
116
|
+
stableOptions.onError?.(errorMessage);
|
|
117
|
+
|
|
118
|
+
if (isDevelopment) {
|
|
119
|
+
console.error("[useGroq] generate error:", { error: err });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
throw err;
|
|
123
|
+
} finally {
|
|
124
|
+
setIsLoading(false);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
[stableOptions]
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const generateJSON = useCallback(
|
|
131
|
+
async <T = Record<string, unknown>,>(
|
|
132
|
+
prompt: string,
|
|
133
|
+
config?: GroqGenerationConfig & { schema?: Record<string, unknown> }
|
|
134
|
+
): Promise<T> => {
|
|
135
|
+
setIsLoading(true);
|
|
136
|
+
setError(null);
|
|
137
|
+
setResult(null);
|
|
138
|
+
|
|
139
|
+
if (isDevelopment) {
|
|
140
|
+
console.log("[useGroq] generateJSON called:", {
|
|
141
|
+
promptLength: prompt.length,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
stableOptions.onStart?.();
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const response = await textGenerationService.generateStructured<T>(prompt, {
|
|
149
|
+
model: stableOptions.model,
|
|
150
|
+
generationConfig: {
|
|
151
|
+
...stableOptions.generationConfig,
|
|
152
|
+
...config,
|
|
153
|
+
},
|
|
154
|
+
schema: config?.schema,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const jsonString = JSON.stringify(response, null, 2);
|
|
158
|
+
setResult(jsonString);
|
|
159
|
+
stableOptions.onSuccess?.(jsonString);
|
|
160
|
+
|
|
161
|
+
if (isDevelopment) {
|
|
162
|
+
console.log("[useGroq] generateJSON success:", {
|
|
163
|
+
hasResponse: !!response,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return response;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
const errorMessage = getUserFriendlyError(
|
|
170
|
+
err instanceof Error ? err : new Error("Unknown error")
|
|
171
|
+
);
|
|
172
|
+
setError(errorMessage);
|
|
173
|
+
stableOptions.onError?.(errorMessage);
|
|
174
|
+
|
|
175
|
+
if (isDevelopment) {
|
|
176
|
+
console.error("[useGroq] generateJSON error:", { error: err });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
throw err;
|
|
180
|
+
} finally {
|
|
181
|
+
setIsLoading(false);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
[stableOptions]
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const stream = useCallback(
|
|
188
|
+
async (
|
|
189
|
+
prompt: string,
|
|
190
|
+
onChunk: (chunk: string) => void,
|
|
191
|
+
config?: GroqGenerationConfig
|
|
192
|
+
): Promise<void> => {
|
|
193
|
+
setIsLoading(true);
|
|
194
|
+
setError(null);
|
|
195
|
+
setResult(null);
|
|
196
|
+
|
|
197
|
+
let fullContent = "";
|
|
198
|
+
|
|
199
|
+
if (isDevelopment) {
|
|
200
|
+
console.log("[useGroq] stream called:", {
|
|
201
|
+
promptLength: prompt.length,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
stableOptions.onStart?.();
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const callbacks: StreamingCallbacks = {
|
|
209
|
+
onChunk: (c) => {
|
|
210
|
+
fullContent += c;
|
|
211
|
+
onChunk(c);
|
|
212
|
+
},
|
|
213
|
+
onComplete: (text) => {
|
|
214
|
+
setResult(text);
|
|
215
|
+
stableOptions.onSuccess?.(text);
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
for await (const _ of textGenerationService.streamCompletion(
|
|
220
|
+
prompt,
|
|
221
|
+
callbacks,
|
|
222
|
+
{
|
|
223
|
+
model: stableOptions.model,
|
|
224
|
+
generationConfig: {
|
|
225
|
+
...stableOptions.generationConfig,
|
|
226
|
+
...config,
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
)) {
|
|
230
|
+
// Consume the async generator
|
|
231
|
+
void _;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (isDevelopment) {
|
|
235
|
+
console.log("[useGroq] stream success:", {
|
|
236
|
+
contentLength: fullContent.length,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
} catch (err) {
|
|
240
|
+
const errorMessage = getUserFriendlyError(
|
|
241
|
+
err instanceof Error ? err : new Error("Unknown error")
|
|
242
|
+
);
|
|
243
|
+
setError(errorMessage);
|
|
244
|
+
stableOptions.onError?.(errorMessage);
|
|
245
|
+
|
|
246
|
+
if (isDevelopment) {
|
|
247
|
+
console.error("[useGroq] stream error:", { error: err });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
throw err;
|
|
251
|
+
} finally {
|
|
252
|
+
setIsLoading(false);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
[stableOptions]
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const reset = useCallback(() => {
|
|
259
|
+
setIsLoading(false);
|
|
260
|
+
setError(null);
|
|
261
|
+
setResult(null);
|
|
262
|
+
}, []);
|
|
263
|
+
|
|
264
|
+
const clearError = useCallback(() => {
|
|
265
|
+
setError(null);
|
|
266
|
+
}, []);
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
isLoading,
|
|
270
|
+
error,
|
|
271
|
+
result,
|
|
272
|
+
generate,
|
|
273
|
+
generateJSON,
|
|
274
|
+
stream,
|
|
275
|
+
reset,
|
|
276
|
+
clearError,
|
|
277
|
+
};
|
|
278
|
+
}
|
package/src/streaming.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming Text Generation Service
|
|
3
|
+
* Handles streaming text generation using Groq API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
GroqChatRequest,
|
|
8
|
+
GroqMessage,
|
|
9
|
+
GroqGenerationConfig,
|
|
10
|
+
GroqChatChunk,
|
|
11
|
+
} from "./types";
|
|
12
|
+
import { groqHttpClient } from "./client";
|
|
13
|
+
import { DEFAULT_MODELS } from "./types";
|
|
14
|
+
import { GroqError, GroqErrorType } from "./types";
|
|
15
|
+
|
|
16
|
+
const isDevelopment = process.env.NODE_ENV === "development";
|
|
17
|
+
|
|
18
|
+
export interface StreamingOptions {
|
|
19
|
+
model?: string;
|
|
20
|
+
generationConfig?: GroqGenerationConfig;
|
|
21
|
+
callbacks?: StreamingCallbacks;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface StreamingCallbacks {
|
|
25
|
+
onChunk?: (chunk: string) => void;
|
|
26
|
+
onComplete?: (fullText: string) => void;
|
|
27
|
+
onError?: (error: Error) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Stream text generation from a prompt
|
|
32
|
+
* Returns an async generator of text chunks
|
|
33
|
+
*/
|
|
34
|
+
export async function* streaming(
|
|
35
|
+
prompt: string,
|
|
36
|
+
options: StreamingOptions = {}
|
|
37
|
+
): AsyncGenerator<string> {
|
|
38
|
+
const startTime = Date.now();
|
|
39
|
+
const model = options.model || DEFAULT_MODELS.TEXT;
|
|
40
|
+
|
|
41
|
+
if (isDevelopment) {
|
|
42
|
+
console.log("[Groq] streaming called:", {
|
|
43
|
+
model,
|
|
44
|
+
promptLength: prompt.length,
|
|
45
|
+
promptPreview: prompt.substring(0, 100) + "...",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const messages: GroqMessage[] = [
|
|
50
|
+
{
|
|
51
|
+
role: "user",
|
|
52
|
+
content: prompt,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const request: GroqChatRequest = {
|
|
57
|
+
model,
|
|
58
|
+
messages,
|
|
59
|
+
temperature: options.generationConfig?.temperature || 0.7,
|
|
60
|
+
max_tokens: options.generationConfig?.maxTokens || 1024,
|
|
61
|
+
top_p: options.generationConfig?.topP,
|
|
62
|
+
n: options.generationConfig?.n,
|
|
63
|
+
stop: options.generationConfig?.stop,
|
|
64
|
+
frequency_penalty: options.generationConfig?.frequencyPenalty,
|
|
65
|
+
presence_penalty: options.generationConfig?.presencePenalty,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (isDevelopment) {
|
|
69
|
+
console.log("[Groq] Starting streaming request");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
let fullText = "";
|
|
74
|
+
|
|
75
|
+
for await (const chunk of groqHttpClient.chatCompletionStream(request)) {
|
|
76
|
+
const content = chunk.choices[0]?.delta?.content;
|
|
77
|
+
|
|
78
|
+
if (content) {
|
|
79
|
+
fullText += content;
|
|
80
|
+
options.callbacks?.onChunk?.(content);
|
|
81
|
+
yield content;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check if generation is complete
|
|
85
|
+
if (chunk.choices[0]?.finish_reason) {
|
|
86
|
+
if (isDevelopment) {
|
|
87
|
+
console.log("[Groq] Streaming complete:", {
|
|
88
|
+
finishReason: chunk.choices[0].finish_reason,
|
|
89
|
+
totalLength: fullText.length,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
options.callbacks?.onComplete?.(fullText);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const totalDuration = Date.now() - startTime;
|
|
98
|
+
if (isDevelopment) {
|
|
99
|
+
console.log("[Groq] streaming complete:", {
|
|
100
|
+
totalDuration: `${totalDuration}ms`,
|
|
101
|
+
totalLength: fullText.length,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (isDevelopment) {
|
|
106
|
+
console.error("[Groq] streaming error:", {
|
|
107
|
+
error,
|
|
108
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
options.callbacks?.onError?.(error as Error);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Stream chat generation from messages
|
|
119
|
+
* Returns an async generator of text chunks
|
|
120
|
+
*/
|
|
121
|
+
export async function* streamingChat(
|
|
122
|
+
messages: GroqMessage[],
|
|
123
|
+
options: StreamingOptions = {}
|
|
124
|
+
): AsyncGenerator<string> {
|
|
125
|
+
const startTime = Date.now();
|
|
126
|
+
const model = options.model || DEFAULT_MODELS.TEXT;
|
|
127
|
+
|
|
128
|
+
if (isDevelopment) {
|
|
129
|
+
console.log("[Groq] streamingChat called:", {
|
|
130
|
+
model,
|
|
131
|
+
messageCount: messages.length,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const request: GroqChatRequest = {
|
|
136
|
+
model,
|
|
137
|
+
messages,
|
|
138
|
+
temperature: options.generationConfig?.temperature || 0.7,
|
|
139
|
+
max_tokens: options.generationConfig?.maxTokens || 1024,
|
|
140
|
+
top_p: options.generationConfig?.topP,
|
|
141
|
+
n: options.generationConfig?.n,
|
|
142
|
+
stop: options.generationConfig?.stop,
|
|
143
|
+
frequency_penalty: options.generationConfig?.frequencyPenalty,
|
|
144
|
+
presence_penalty: options.generationConfig?.presencePenalty,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
if (isDevelopment) {
|
|
148
|
+
console.log("[Groq] Starting streaming chat request");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
let fullText = "";
|
|
153
|
+
|
|
154
|
+
for await (const chunk of groqHttpClient.chatCompletionStream(request)) {
|
|
155
|
+
const content = chunk.choices[0]?.delta?.content;
|
|
156
|
+
|
|
157
|
+
if (content) {
|
|
158
|
+
fullText += content;
|
|
159
|
+
options.callbacks?.onChunk?.(content);
|
|
160
|
+
yield content;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check if generation is complete
|
|
164
|
+
if (chunk.choices[0]?.finish_reason) {
|
|
165
|
+
if (isDevelopment) {
|
|
166
|
+
console.log("[Groq] Streaming chat complete:", {
|
|
167
|
+
finishReason: chunk.choices[0].finish_reason,
|
|
168
|
+
totalLength: fullText.length,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
options.callbacks?.onComplete?.(fullText);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const totalDuration = Date.now() - startTime;
|
|
177
|
+
if (isDevelopment) {
|
|
178
|
+
console.log("[Groq] streamingChat complete:", {
|
|
179
|
+
totalDuration: `${totalDuration}ms`,
|
|
180
|
+
totalLength: fullText.length,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (isDevelopment) {
|
|
185
|
+
console.error("[Groq] streamingChat error:", {
|
|
186
|
+
error,
|
|
187
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
options.callbacks?.onError?.(error as Error);
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Text Generation Service
|
|
3
|
+
* Handles JSON/structured output generation using Groq API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
GroqMessage,
|
|
8
|
+
GroqGenerationConfig,
|
|
9
|
+
} from "./types";
|
|
10
|
+
import { textGeneration } from "./text-generation";
|
|
11
|
+
import { GroqError, GroqErrorType } from "./types";
|
|
12
|
+
|
|
13
|
+
const isDevelopment = process.env.NODE_ENV === "development";
|
|
14
|
+
|
|
15
|
+
export interface StructuredTextOptions<T = unknown> {
|
|
16
|
+
model?: string;
|
|
17
|
+
generationConfig?: GroqGenerationConfig;
|
|
18
|
+
schema?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate structured JSON output from a prompt
|
|
23
|
+
*/
|
|
24
|
+
export async function structuredText<T = Record<string, unknown>>(
|
|
25
|
+
prompt: string,
|
|
26
|
+
options: StructuredTextOptions<T> = {}
|
|
27
|
+
): Promise<T> {
|
|
28
|
+
const startTime = Date.now();
|
|
29
|
+
|
|
30
|
+
if (isDevelopment) {
|
|
31
|
+
console.log("[Groq] structuredText called:", {
|
|
32
|
+
model: options.model,
|
|
33
|
+
promptLength: prompt.length,
|
|
34
|
+
hasSchema: !!options.schema,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Build a prompt that encourages JSON output
|
|
39
|
+
let enhancedPrompt = prompt;
|
|
40
|
+
|
|
41
|
+
if (options.schema) {
|
|
42
|
+
enhancedPrompt = `
|
|
43
|
+
${prompt}
|
|
44
|
+
|
|
45
|
+
Please respond with a JSON object that follows this schema:
|
|
46
|
+
${JSON.stringify(options.schema, null, 2)}
|
|
47
|
+
|
|
48
|
+
Your response must be valid JSON only, with no additional text or formatting.
|
|
49
|
+
`;
|
|
50
|
+
} else {
|
|
51
|
+
enhancedPrompt = `
|
|
52
|
+
${prompt}
|
|
53
|
+
|
|
54
|
+
Please respond with a valid JSON object. Your response must be valid JSON only, with no additional text or formatting.
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// Generate text with lower temperature for more consistent structured output
|
|
60
|
+
const response = await textGeneration(enhancedPrompt, {
|
|
61
|
+
model: options.model,
|
|
62
|
+
generationConfig: {
|
|
63
|
+
temperature: 0.3, // Lower temperature for more deterministic output
|
|
64
|
+
maxTokens: 2048,
|
|
65
|
+
...options.generationConfig,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (isDevelopment) {
|
|
70
|
+
console.log("[Groq] structuredText response received:", {
|
|
71
|
+
responseLength: response.length,
|
|
72
|
+
responsePreview: response.substring(0, 200) + "...",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Extract JSON from response (handle markdown code blocks)
|
|
77
|
+
let jsonStr = response.trim();
|
|
78
|
+
|
|
79
|
+
// Remove markdown code blocks if present
|
|
80
|
+
if (jsonStr.startsWith("```json")) {
|
|
81
|
+
jsonStr = jsonStr.slice(7);
|
|
82
|
+
} else if (jsonStr.startsWith("```")) {
|
|
83
|
+
jsonStr = jsonStr.slice(3);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (jsonStr.endsWith("```")) {
|
|
87
|
+
jsonStr = jsonStr.slice(0, -3);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
jsonStr = jsonStr.trim();
|
|
91
|
+
|
|
92
|
+
// Parse JSON
|
|
93
|
+
const parsed = JSON.parse(jsonStr) as T;
|
|
94
|
+
|
|
95
|
+
const totalDuration = Date.now() - startTime;
|
|
96
|
+
if (isDevelopment) {
|
|
97
|
+
console.log("[Groq] structuredText complete:", {
|
|
98
|
+
totalDuration: `${totalDuration}ms`,
|
|
99
|
+
parsed: !!parsed,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return parsed;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (isDevelopment) {
|
|
106
|
+
console.error("[Groq] structuredText error:", {
|
|
107
|
+
error,
|
|
108
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
throw new GroqError(
|
|
113
|
+
GroqErrorType.UNKNOWN_ERROR,
|
|
114
|
+
`Failed to generate structured output: ${
|
|
115
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
116
|
+
}`,
|
|
117
|
+
error as Error
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Generate structured JSON output from chat messages
|
|
124
|
+
*/
|
|
125
|
+
export async function structuredChat<T = Record<string, unknown>>(
|
|
126
|
+
messages: GroqMessage[],
|
|
127
|
+
options: StructuredTextOptions<T> = {}
|
|
128
|
+
): Promise<T> {
|
|
129
|
+
const startTime = Date.now();
|
|
130
|
+
|
|
131
|
+
if (isDevelopment) {
|
|
132
|
+
console.log("[Groq] structuredChat called:", {
|
|
133
|
+
model: options.model,
|
|
134
|
+
messageCount: messages.length,
|
|
135
|
+
hasSchema: !!options.schema,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Add a system message requesting JSON output
|
|
140
|
+
const systemMessage: GroqMessage = {
|
|
141
|
+
role: "system",
|
|
142
|
+
content: options.schema
|
|
143
|
+
? `You must respond with valid JSON that follows this schema:\n${JSON.stringify(options.schema, null, 2)}\n\nYour response must be valid JSON only, with no additional text or formatting.`
|
|
144
|
+
: "You must respond with valid JSON only. Your response must be valid JSON only, with no additional text or formatting.",
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const enhancedMessages = [systemMessage, ...messages];
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const { chatGeneration } = await import("./text-generation");
|
|
151
|
+
|
|
152
|
+
// Generate with lower temperature for more deterministic output
|
|
153
|
+
const response = await chatGeneration(enhancedMessages, {
|
|
154
|
+
model: options.model,
|
|
155
|
+
generationConfig: {
|
|
156
|
+
temperature: 0.3,
|
|
157
|
+
maxTokens: 2048,
|
|
158
|
+
...options.generationConfig,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (isDevelopment) {
|
|
163
|
+
console.log("[Groq] structuredChat response received:", {
|
|
164
|
+
responseLength: response.length,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Extract JSON from response
|
|
169
|
+
let jsonStr = response.trim();
|
|
170
|
+
|
|
171
|
+
// Remove markdown code blocks if present
|
|
172
|
+
if (jsonStr.startsWith("```json")) {
|
|
173
|
+
jsonStr = jsonStr.slice(7);
|
|
174
|
+
} else if (jsonStr.startsWith("```")) {
|
|
175
|
+
jsonStr = jsonStr.slice(3);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (jsonStr.endsWith("```")) {
|
|
179
|
+
jsonStr = jsonStr.slice(0, -3);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
jsonStr = jsonStr.trim();
|
|
183
|
+
|
|
184
|
+
// Parse JSON
|
|
185
|
+
const parsed = JSON.parse(jsonStr) as T;
|
|
186
|
+
|
|
187
|
+
const totalDuration = Date.now() - startTime;
|
|
188
|
+
if (isDevelopment) {
|
|
189
|
+
console.log("[Groq] structuredChat complete:", {
|
|
190
|
+
totalDuration: `${totalDuration}ms`,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return parsed;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (isDevelopment) {
|
|
197
|
+
console.error("[Groq] structuredChat error:", {
|
|
198
|
+
error,
|
|
199
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
throw new GroqError(
|
|
204
|
+
GroqErrorType.UNKNOWN_ERROR,
|
|
205
|
+
`Failed to generate structured output: ${
|
|
206
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
207
|
+
}`,
|
|
208
|
+
error as Error
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|