llm-fns 1.0.5 → 1.0.7

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,29 @@
1
+ import OpenAI from 'openai';
2
+ import { PromptFunction, LlmPromptOptions, IsPromptCachedFunction } from "./createLlmClient.js";
3
+ export type JsonSchemaLlmClientOptions = Omit<LlmPromptOptions, 'messages' | 'response_format'> & {
4
+ maxRetries?: number;
5
+ /**
6
+ * If true, passes `response_format: { type: 'json_object' }` to the model.
7
+ * If false, only includes the schema in the system prompt.
8
+ * Defaults to true.
9
+ */
10
+ useResponseFormat?: boolean;
11
+ /**
12
+ * A hook to process the parsed JSON data before it is validated.
13
+ * This can be used to merge partial results or perform other transformations.
14
+ * @param data The parsed JSON data from the LLM response.
15
+ * @returns The processed data to be validated.
16
+ */
17
+ beforeValidation?: (data: any) => any;
18
+ };
19
+ export interface CreateJsonSchemaLlmClientParams {
20
+ prompt: PromptFunction;
21
+ isPromptCached: IsPromptCachedFunction;
22
+ fallbackPrompt?: PromptFunction;
23
+ disableJsonFixer?: boolean;
24
+ }
25
+ export declare function createJsonSchemaLlmClient(params: CreateJsonSchemaLlmClientParams): {
26
+ promptJson: <T>(messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[], schema: Record<string, any>, validator: (data: any) => T, options?: JsonSchemaLlmClientOptions) => Promise<T>;
27
+ isPromptJsonCached: (messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[], schema: Record<string, any>, options?: JsonSchemaLlmClientOptions) => Promise<boolean>;
28
+ };
29
+ export type JsonSchemaClient = ReturnType<typeof createJsonSchemaLlmClient>;
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createJsonSchemaLlmClient = createJsonSchemaLlmClient;
4
+ const createLlmRetryClient_js_1 = require("./createLlmRetryClient.js");
5
+ function createJsonSchemaLlmClient(params) {
6
+ const { prompt, isPromptCached, fallbackPrompt, disableJsonFixer = false } = params;
7
+ const llmRetryClient = (0, createLlmRetryClient_js_1.createLlmRetryClient)({ prompt, fallbackPrompt });
8
+ async function _tryToFixJson(brokenResponse, schemaJsonString, errorDetails, options) {
9
+ const fixupPrompt = `
10
+ An attempt to generate a JSON object resulted in the following output, which is either not valid JSON or does not conform to the required schema.
11
+
12
+ Your task is to act as a JSON fixer. Analyze the provided "BROKEN RESPONSE" and correct it to match the "REQUIRED JSON SCHEMA".
13
+
14
+ - If the broken response contains all the necessary information to create a valid JSON object according to the schema, please provide the corrected, valid JSON object.
15
+ - If the broken response is missing essential information, or is too garbled to be fixed, please respond with the exact string: "CANNOT_FIX".
16
+ - Your response must be ONLY the corrected JSON object or the string "CANNOT_FIX". Do not include any other text, explanations, or markdown formatting.
17
+
18
+ REQUIRED JSON SCHEMA:
19
+ ${schemaJsonString}
20
+
21
+ ERROR DETAILS:
22
+ ${errorDetails}
23
+
24
+ BROKEN RESPONSE:
25
+ ${brokenResponse}
26
+ `;
27
+ const messages = [
28
+ { role: 'system', content: 'You are an expert at fixing malformed JSON data to match a specific schema.' },
29
+ { role: 'user', content: fixupPrompt }
30
+ ];
31
+ const useResponseFormat = options?.useResponseFormat ?? true;
32
+ const response_format = useResponseFormat
33
+ ? { type: 'json_object' }
34
+ : undefined;
35
+ const { maxRetries, useResponseFormat: _useResponseFormat, ...restOptions } = options || {};
36
+ const completion = await prompt({
37
+ messages,
38
+ response_format,
39
+ ...restOptions
40
+ });
41
+ const fixedResponse = completion.choices[0]?.message?.content;
42
+ if (fixedResponse && fixedResponse.trim() === 'CANNOT_FIX') {
43
+ return null;
44
+ }
45
+ return fixedResponse || null;
46
+ }
47
+ async function _parseOrFixJson(llmResponseString, schemaJsonString, options) {
48
+ let jsonDataToParse = llmResponseString.trim();
49
+ // Robust handling for responses wrapped in markdown code blocks
50
+ const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)\s*```/;
51
+ const match = codeBlockRegex.exec(jsonDataToParse);
52
+ if (match && match[1]) {
53
+ jsonDataToParse = match[1].trim();
54
+ }
55
+ if (jsonDataToParse === "") {
56
+ throw new Error("LLM returned an empty string.");
57
+ }
58
+ try {
59
+ return JSON.parse(jsonDataToParse);
60
+ }
61
+ catch (parseError) {
62
+ if (disableJsonFixer) {
63
+ throw parseError; // re-throw original error
64
+ }
65
+ // Attempt a one-time fix before failing.
66
+ const errorDetails = `JSON Parse Error: ${parseError.message}`;
67
+ const fixedResponse = await _tryToFixJson(jsonDataToParse, schemaJsonString, errorDetails, options);
68
+ if (fixedResponse) {
69
+ try {
70
+ return JSON.parse(fixedResponse);
71
+ }
72
+ catch (e) {
73
+ // Fix-up failed, throw original error.
74
+ throw parseError;
75
+ }
76
+ }
77
+ throw parseError; // if no fixed response
78
+ }
79
+ }
80
+ async function _validateOrFix(jsonData, validator, schemaJsonString, options) {
81
+ try {
82
+ if (options?.beforeValidation) {
83
+ jsonData = options.beforeValidation(jsonData);
84
+ }
85
+ return validator(jsonData);
86
+ }
87
+ catch (validationError) {
88
+ if (disableJsonFixer) {
89
+ throw validationError;
90
+ }
91
+ // Attempt a one-time fix for schema validation errors.
92
+ const errorDetails = `Schema Validation Error: ${validationError.message}`;
93
+ const fixedResponse = await _tryToFixJson(JSON.stringify(jsonData, null, 2), schemaJsonString, errorDetails, options);
94
+ if (fixedResponse) {
95
+ try {
96
+ let fixedJsonData = JSON.parse(fixedResponse);
97
+ if (options?.beforeValidation) {
98
+ fixedJsonData = options.beforeValidation(fixedJsonData);
99
+ }
100
+ return validator(fixedJsonData);
101
+ }
102
+ catch (e) {
103
+ // Fix-up failed, throw original validation error
104
+ throw validationError;
105
+ }
106
+ }
107
+ throw validationError; // if no fixed response
108
+ }
109
+ }
110
+ function _getJsonPromptConfig(messages, schema, options) {
111
+ const schemaJsonString = JSON.stringify(schema);
112
+ const commonPromptFooter = `
113
+ Your response MUST be a single JSON entity (object or array) that strictly adheres to the following JSON schema.
114
+ Do NOT include any other text, explanations, or markdown formatting (like \`\`\`json) before or after the JSON entity.
115
+
116
+ JSON schema:
117
+ ${schemaJsonString}`;
118
+ // Clone messages to avoid mutating the input
119
+ const finalMessages = [...messages];
120
+ // Find the first system message to append instructions to
121
+ const systemMessageIndex = finalMessages.findIndex(m => m.role === 'system');
122
+ if (systemMessageIndex !== -1) {
123
+ // Append to existing system message
124
+ const existingContent = finalMessages[systemMessageIndex].content;
125
+ finalMessages[systemMessageIndex] = {
126
+ ...finalMessages[systemMessageIndex],
127
+ content: `${existingContent}\n${commonPromptFooter}`
128
+ };
129
+ }
130
+ else {
131
+ // Prepend new system message
132
+ finalMessages.unshift({
133
+ role: 'system',
134
+ content: commonPromptFooter
135
+ });
136
+ }
137
+ const useResponseFormat = options?.useResponseFormat ?? true;
138
+ const response_format = useResponseFormat
139
+ ? { type: 'json_object' }
140
+ : undefined;
141
+ return { finalMessages, schemaJsonString, response_format };
142
+ }
143
+ async function promptJson(messages, schema, validator, options) {
144
+ const { finalMessages, schemaJsonString, response_format } = _getJsonPromptConfig(messages, schema, options);
145
+ const processResponse = async (llmResponseString) => {
146
+ let jsonData;
147
+ try {
148
+ jsonData = await _parseOrFixJson(llmResponseString, schemaJsonString, options);
149
+ }
150
+ catch (parseError) {
151
+ const errorMessage = `Your previous response resulted in an error.
152
+ Error Type: JSON_PARSE_ERROR
153
+ Error Details: ${parseError.message}
154
+ The response provided was not valid JSON. Please correct it.`;
155
+ throw new createLlmRetryClient_js_1.LlmRetryError(errorMessage, 'JSON_PARSE_ERROR', undefined, llmResponseString);
156
+ }
157
+ try {
158
+ const validatedData = await _validateOrFix(jsonData, validator, schemaJsonString, options);
159
+ return validatedData;
160
+ }
161
+ catch (validationError) {
162
+ // We assume the validator throws an error with a meaningful message
163
+ const rawResponseForError = JSON.stringify(jsonData, null, 2);
164
+ const errorDetails = validationError.message;
165
+ const errorMessage = `Your previous response resulted in an error.
166
+ Error Type: SCHEMA_VALIDATION_ERROR
167
+ Error Details: ${errorDetails}
168
+ The response was valid JSON but did not conform to the required schema. Please review the errors and the schema to provide a corrected response.`;
169
+ throw new createLlmRetryClient_js_1.LlmRetryError(errorMessage, 'CUSTOM_ERROR', validationError, rawResponseForError);
170
+ }
171
+ };
172
+ const retryOptions = {
173
+ ...options,
174
+ messages: finalMessages,
175
+ response_format,
176
+ validate: processResponse
177
+ };
178
+ return llmRetryClient.promptTextRetry(retryOptions);
179
+ }
180
+ async function isPromptJsonCached(messages, schema, options) {
181
+ const { finalMessages, response_format } = _getJsonPromptConfig(messages, schema, options);
182
+ const { maxRetries, useResponseFormat: _u, beforeValidation, ...restOptions } = options || {};
183
+ return isPromptCached({
184
+ messages: finalMessages,
185
+ response_format,
186
+ ...restOptions
187
+ });
188
+ }
189
+ return { promptJson, isPromptJsonCached };
190
+ }
@@ -1,44 +1,27 @@
1
1
  import OpenAI from 'openai';
2
2
  import * as z from "zod";
3
- import { PromptFunction, LlmPromptOptions, IsPromptCachedFunction } from "./createLlmClient.js";
4
3
  import { ZodTypeAny } from "zod";
5
- export type ZodLlmClientOptions = Omit<LlmPromptOptions, 'messages' | 'response_format'> & {
6
- maxRetries?: number;
7
- /**
8
- * If true, passes `response_format: { type: 'json_object' }` to the model.
9
- * If false, only includes the schema in the system prompt.
10
- * Defaults to true.
11
- */
12
- useResponseFormat?: boolean;
13
- /**
14
- * A hook to process the parsed JSON data before it is validated against the Zod schema.
15
- * This can be used to merge partial results or perform other transformations.
16
- * @param data The parsed JSON data from the LLM response.
17
- * @returns The processed data to be validated.
18
- */
19
- beforeValidation?: (data: any) => any;
20
- };
4
+ import { JsonSchemaClient, JsonSchemaLlmClientOptions } from "./createJsonSchemaLlmClient.js";
5
+ export type ZodLlmClientOptions = JsonSchemaLlmClientOptions;
21
6
  export interface CreateZodLlmClientParams {
22
- prompt: PromptFunction;
23
- isPromptCached: IsPromptCachedFunction;
24
- fallbackPrompt?: PromptFunction;
25
- disableJsonFixer?: boolean;
7
+ jsonSchemaClient: JsonSchemaClient;
26
8
  }
27
9
  export interface NormalizedZodArgs<T extends ZodTypeAny> {
28
- mainInstruction: string;
29
- userMessagePayload: string | OpenAI.Chat.Completions.ChatCompletionContentPart[];
10
+ messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[];
30
11
  dataExtractionSchema: T;
31
12
  options?: ZodLlmClientOptions;
32
13
  }
33
- export declare function normalizeZodArgs<T extends ZodTypeAny>(arg1: string | T, arg2?: string | OpenAI.Chat.Completions.ChatCompletionContentPart[] | T | ZodLlmClientOptions, arg3?: T | ZodLlmClientOptions, arg4?: ZodLlmClientOptions): NormalizedZodArgs<T>;
14
+ export declare function normalizeZodArgs<T extends ZodTypeAny>(arg1: string | OpenAI.Chat.Completions.ChatCompletionMessageParam[] | T, arg2?: string | OpenAI.Chat.Completions.ChatCompletionContentPart[] | T | ZodLlmClientOptions, arg3?: T | ZodLlmClientOptions, arg4?: ZodLlmClientOptions): NormalizedZodArgs<T>;
34
15
  export declare function createZodLlmClient(params: CreateZodLlmClientParams): {
35
16
  promptZod: {
36
17
  <T extends ZodTypeAny>(schema: T, options?: ZodLlmClientOptions): Promise<z.infer<T>>;
18
+ <T extends ZodTypeAny>(messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[], schema: T, options?: ZodLlmClientOptions): Promise<z.infer<T>>;
37
19
  <T extends ZodTypeAny>(prompt: string, schema: T, options?: ZodLlmClientOptions): Promise<z.infer<T>>;
38
20
  <T extends ZodTypeAny>(mainInstruction: string, userMessagePayload: string | OpenAI.Chat.Completions.ChatCompletionContentPart[], dataExtractionSchema: T, options?: ZodLlmClientOptions): Promise<z.infer<T>>;
39
21
  };
40
22
  isPromptZodCached: {
41
23
  <T extends ZodTypeAny>(schema: T, options?: ZodLlmClientOptions): Promise<boolean>;
24
+ <T extends ZodTypeAny>(messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[], schema: T, options?: ZodLlmClientOptions): Promise<boolean>;
42
25
  <T extends ZodTypeAny>(prompt: string, schema: T, options?: ZodLlmClientOptions): Promise<boolean>;
43
26
  <T extends ZodTypeAny>(mainInstruction: string, userMessagePayload: string | OpenAI.Chat.Completions.ChatCompletionContentPart[], dataExtractionSchema: T, options?: ZodLlmClientOptions): Promise<boolean>;
44
27
  };
@@ -36,7 +36,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.normalizeZodArgs = normalizeZodArgs;
37
37
  exports.createZodLlmClient = createZodLlmClient;
38
38
  const z = __importStar(require("zod"));
39
- const createLlmRetryClient_js_1 = require("./createLlmRetryClient.js");
40
39
  const zod_1 = require("zod");
41
40
  function isZodSchema(obj) {
42
41
  return (typeof obj === 'object' &&
@@ -45,11 +44,21 @@ function isZodSchema(obj) {
45
44
  '_def' in obj);
46
45
  }
47
46
  function normalizeZodArgs(arg1, arg2, arg3, arg4) {
47
+ // Case 0: promptZod(messages[], schema, options?)
48
+ if (Array.isArray(arg1)) {
49
+ return {
50
+ messages: arg1,
51
+ dataExtractionSchema: arg2,
52
+ options: arg3
53
+ };
54
+ }
48
55
  if (isZodSchema(arg1)) {
49
56
  // Case 1: promptZod(schema, options?)
50
57
  return {
51
- mainInstruction: "Generate a valid JSON object based on the schema.",
52
- userMessagePayload: "Generate the data.",
58
+ messages: [
59
+ { role: 'system', content: "Generate a valid JSON object based on the schema." },
60
+ { role: 'user', content: "Generate the data." }
61
+ ],
53
62
  dataExtractionSchema: arg1,
54
63
  options: arg2
55
64
  };
@@ -58,16 +67,20 @@ function normalizeZodArgs(arg1, arg2, arg3, arg4) {
58
67
  if (isZodSchema(arg2)) {
59
68
  // Case 2: promptZod(prompt, schema, options?)
60
69
  return {
61
- mainInstruction: "You are a helpful assistant that outputs JSON matching the provided schema.",
62
- userMessagePayload: arg1,
70
+ messages: [
71
+ { role: 'system', content: "You are a helpful assistant that outputs JSON matching the provided schema." },
72
+ { role: 'user', content: arg1 }
73
+ ],
63
74
  dataExtractionSchema: arg2,
64
75
  options: arg3
65
76
  };
66
77
  }
67
78
  // Case 3: promptZod(system, user, schema, options?)
68
79
  return {
69
- mainInstruction: arg1,
70
- userMessagePayload: arg2,
80
+ messages: [
81
+ { role: 'system', content: arg1 },
82
+ { role: 'user', content: arg2 }
83
+ ],
71
84
  dataExtractionSchema: arg3,
72
85
  options: arg4
73
86
  };
@@ -75,187 +88,32 @@ function normalizeZodArgs(arg1, arg2, arg3, arg4) {
75
88
  throw new Error("Invalid arguments passed to promptZod");
76
89
  }
77
90
  function createZodLlmClient(params) {
78
- const { prompt, isPromptCached, fallbackPrompt, disableJsonFixer = false } = params;
79
- const llmRetryClient = (0, createLlmRetryClient_js_1.createLlmRetryClient)({ prompt, fallbackPrompt });
80
- async function _tryToFixJson(brokenResponse, schemaJsonString, errorDetails, options) {
81
- const fixupPrompt = `
82
- An attempt to generate a JSON object resulted in the following output, which is either not valid JSON or does not conform to the required schema.
83
-
84
- Your task is to act as a JSON fixer. Analyze the provided "BROKEN RESPONSE" and correct it to match the "REQUIRED JSON SCHEMA".
85
-
86
- - If the broken response contains all the necessary information to create a valid JSON object according to the schema, please provide the corrected, valid JSON object.
87
- - If the broken response is missing essential information, or is too garbled to be fixed, please respond with the exact string: "CANNOT_FIX".
88
- - Your response must be ONLY the corrected JSON object or the string "CANNOT_FIX". Do not include any other text, explanations, or markdown formatting.
89
-
90
- REQUIRED JSON SCHEMA:
91
- ${schemaJsonString}
92
-
93
- ERROR DETAILS:
94
- ${errorDetails}
95
-
96
- BROKEN RESPONSE:
97
- ${brokenResponse}
98
- `;
99
- const messages = [
100
- { role: 'system', content: 'You are an expert at fixing malformed JSON data to match a specific schema.' },
101
- { role: 'user', content: fixupPrompt }
102
- ];
103
- const useResponseFormat = options?.useResponseFormat ?? true;
104
- const response_format = useResponseFormat
105
- ? { type: 'json_object' }
106
- : undefined;
107
- const { maxRetries, useResponseFormat: _useResponseFormat, ...restOptions } = options || {};
108
- const completion = await prompt({
109
- messages,
110
- response_format,
111
- ...restOptions
112
- });
113
- const fixedResponse = completion.choices[0]?.message?.content;
114
- if (fixedResponse && fixedResponse.trim() === 'CANNOT_FIX') {
115
- return null;
116
- }
117
- return fixedResponse || null;
118
- }
119
- async function _parseOrFixJson(llmResponseString, schemaJsonString, options) {
120
- let jsonDataToParse = llmResponseString.trim();
121
- // Robust handling for responses wrapped in markdown code blocks
122
- const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)\s*```/;
123
- const match = codeBlockRegex.exec(jsonDataToParse);
124
- if (match && match[1]) {
125
- jsonDataToParse = match[1].trim();
126
- }
127
- if (jsonDataToParse === "") {
128
- throw new Error("LLM returned an empty string.");
129
- }
130
- try {
131
- return JSON.parse(jsonDataToParse);
132
- }
133
- catch (parseError) {
134
- if (disableJsonFixer) {
135
- throw parseError; // re-throw original error
136
- }
137
- // Attempt a one-time fix before failing.
138
- const errorDetails = `JSON Parse Error: ${parseError.message}`;
139
- const fixedResponse = await _tryToFixJson(jsonDataToParse, schemaJsonString, errorDetails, options);
140
- if (fixedResponse) {
141
- try {
142
- return JSON.parse(fixedResponse);
143
- }
144
- catch (e) {
145
- // Fix-up failed, throw original error.
146
- throw parseError;
147
- }
148
- }
149
- throw parseError; // if no fixed response
150
- }
151
- }
152
- async function _validateOrFixSchema(jsonData, dataExtractionSchema, schemaJsonString, options) {
153
- try {
154
- if (options?.beforeValidation) {
155
- jsonData = options.beforeValidation(jsonData);
156
- }
157
- return dataExtractionSchema.parse(jsonData);
158
- }
159
- catch (validationError) {
160
- if (!(validationError instanceof zod_1.ZodError) || disableJsonFixer) {
161
- throw validationError;
162
- }
163
- // Attempt a one-time fix for schema validation errors.
164
- const errorDetails = `Schema Validation Error: ${JSON.stringify(validationError.format(), null, 2)}`;
165
- const fixedResponse = await _tryToFixJson(JSON.stringify(jsonData, null, 2), schemaJsonString, errorDetails, options);
166
- if (fixedResponse) {
167
- try {
168
- let fixedJsonData = JSON.parse(fixedResponse);
169
- if (options?.beforeValidation) {
170
- fixedJsonData = options.beforeValidation(fixedJsonData);
171
- }
172
- return dataExtractionSchema.parse(fixedJsonData);
173
- }
174
- catch (e) {
175
- // Fix-up failed, throw original validation error
176
- throw validationError;
177
- }
178
- }
179
- throw validationError; // if no fixed response
180
- }
181
- }
182
- function _getZodPromptConfig(mainInstruction, dataExtractionSchema, options) {
91
+ const { jsonSchemaClient } = params;
92
+ async function promptZod(arg1, arg2, arg3, arg4) {
93
+ const { messages, dataExtractionSchema, options } = normalizeZodArgs(arg1, arg2, arg3, arg4);
183
94
  const schema = z.toJSONSchema(dataExtractionSchema, {
184
95
  unrepresentable: 'any'
185
96
  });
186
- const schemaJsonString = JSON.stringify(schema);
187
- const commonPromptFooter = `
188
- Your response MUST be a single JSON entity (object or array) that strictly adheres to the following JSON schema.
189
- Do NOT include any other text, explanations, or markdown formatting (like \`\`\`json) before or after the JSON entity.
190
-
191
- JSON schema:
192
- ${schemaJsonString}`;
193
- const finalMainInstruction = `${mainInstruction}\n${commonPromptFooter}`;
194
- const useResponseFormat = options?.useResponseFormat ?? true;
195
- const response_format = useResponseFormat
196
- ? { type: 'json_object' }
197
- : undefined;
198
- return { finalMainInstruction, schemaJsonString, response_format };
199
- }
200
- async function promptZod(arg1, arg2, arg3, arg4) {
201
- const { mainInstruction, userMessagePayload, dataExtractionSchema, options } = normalizeZodArgs(arg1, arg2, arg3, arg4);
202
- const { finalMainInstruction, schemaJsonString, response_format } = _getZodPromptConfig(mainInstruction, dataExtractionSchema, options);
203
- const processResponse = async (llmResponseString) => {
204
- let jsonData;
205
- try {
206
- jsonData = await _parseOrFixJson(llmResponseString, schemaJsonString, options);
207
- }
208
- catch (parseError) {
209
- const errorMessage = `Your previous response resulted in an error.
210
- Error Type: JSON_PARSE_ERROR
211
- Error Details: ${parseError.message}
212
- The response provided was not valid JSON. Please correct it.`;
213
- throw new createLlmRetryClient_js_1.LlmRetryError(errorMessage, 'JSON_PARSE_ERROR', undefined, llmResponseString);
214
- }
97
+ const validator = (data) => {
215
98
  try {
216
- const validatedData = await _validateOrFixSchema(jsonData, dataExtractionSchema, schemaJsonString, options);
217
- return validatedData;
99
+ return dataExtractionSchema.parse(data);
218
100
  }
219
- catch (validationError) {
220
- if (validationError instanceof zod_1.ZodError) {
221
- const rawResponseForError = JSON.stringify(jsonData, null, 2);
222
- const errorDetails = JSON.stringify(validationError.format(), null, 2);
223
- const errorMessage = `Your previous response resulted in an error.
224
- Error Type: SCHEMA_VALIDATION_ERROR
225
- Error Details: ${errorDetails}
226
- The response was valid JSON but did not conform to the required schema. Please review the errors and the schema to provide a corrected response.`;
227
- throw new createLlmRetryClient_js_1.LlmRetryError(errorMessage, 'CUSTOM_ERROR', validationError.format(), rawResponseForError);
101
+ catch (error) {
102
+ if (error instanceof zod_1.ZodError) {
103
+ // Format the error nicely for the LLM
104
+ throw new Error(JSON.stringify(error.format(), null, 2));
228
105
  }
229
- // For other errors, rethrow and let LlmRetryClient handle as critical.
230
- throw validationError;
106
+ throw error;
231
107
  }
232
108
  };
233
- const messages = [
234
- { role: "system", content: finalMainInstruction },
235
- { role: "user", content: userMessagePayload }
236
- ];
237
- const retryOptions = {
238
- ...options,
239
- messages,
240
- response_format,
241
- validate: processResponse
242
- };
243
- // Use promptTextRetry because we expect a string response to parse as JSON
244
- return llmRetryClient.promptTextRetry(retryOptions);
109
+ return jsonSchemaClient.promptJson(messages, schema, validator, options);
245
110
  }
246
111
  async function isPromptZodCached(arg1, arg2, arg3, arg4) {
247
- const { mainInstruction, userMessagePayload, dataExtractionSchema, options } = normalizeZodArgs(arg1, arg2, arg3, arg4);
248
- const { finalMainInstruction, response_format } = _getZodPromptConfig(mainInstruction, dataExtractionSchema, options);
249
- const messages = [
250
- { role: "system", content: finalMainInstruction },
251
- { role: "user", content: userMessagePayload }
252
- ];
253
- const { maxRetries, useResponseFormat: _u, beforeValidation, ...restOptions } = options || {};
254
- return isPromptCached({
255
- messages,
256
- response_format,
257
- ...restOptions
112
+ const { messages, dataExtractionSchema, options } = normalizeZodArgs(arg1, arg2, arg3, arg4);
113
+ const schema = z.toJSONSchema(dataExtractionSchema, {
114
+ unrepresentable: 'any'
258
115
  });
116
+ return jsonSchemaClient.isPromptJsonCached(messages, schema, options);
259
117
  }
260
118
  return { promptZod, isPromptZodCached };
261
119
  }
@@ -9,24 +9,24 @@ const createZodLlmClient_js_1 = require("./createZodLlmClient.js");
9
9
  });
10
10
  (0, vitest_1.it)('should normalize Schema Only (Case 1)', () => {
11
11
  const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(TestSchema);
12
- (0, vitest_1.expect)(result.mainInstruction).toContain('Generate a valid JSON');
13
- (0, vitest_1.expect)(result.userMessagePayload).toBe('Generate the data.');
12
+ (0, vitest_1.expect)(result.messages[0].content).toContain('Generate a valid JSON');
13
+ (0, vitest_1.expect)(result.messages[1].content).toBe('Generate the data.');
14
14
  (0, vitest_1.expect)(result.dataExtractionSchema).toBe(TestSchema);
15
15
  (0, vitest_1.expect)(result.options).toBeUndefined();
16
16
  });
17
17
  (0, vitest_1.it)('should normalize Schema Only with Options (Case 1)', () => {
18
18
  const options = { temperature: 0.5 };
19
19
  const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(TestSchema, options);
20
- (0, vitest_1.expect)(result.mainInstruction).toContain('Generate a valid JSON');
21
- (0, vitest_1.expect)(result.userMessagePayload).toBe('Generate the data.');
20
+ (0, vitest_1.expect)(result.messages[0].content).toContain('Generate a valid JSON');
21
+ (0, vitest_1.expect)(result.messages[1].content).toBe('Generate the data.');
22
22
  (0, vitest_1.expect)(result.dataExtractionSchema).toBe(TestSchema);
23
23
  (0, vitest_1.expect)(result.options).toBe(options);
24
24
  });
25
25
  (0, vitest_1.it)('should normalize Prompt + Schema (Case 2)', () => {
26
26
  const prompt = "Extract data";
27
27
  const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(prompt, TestSchema);
28
- (0, vitest_1.expect)(result.mainInstruction).toContain('You are a helpful assistant');
29
- (0, vitest_1.expect)(result.userMessagePayload).toBe(prompt);
28
+ (0, vitest_1.expect)(result.messages[0].content).toContain('You are a helpful assistant');
29
+ (0, vitest_1.expect)(result.messages[1].content).toBe(prompt);
30
30
  (0, vitest_1.expect)(result.dataExtractionSchema).toBe(TestSchema);
31
31
  (0, vitest_1.expect)(result.options).toBeUndefined();
32
32
  });
@@ -34,8 +34,8 @@ const createZodLlmClient_js_1 = require("./createZodLlmClient.js");
34
34
  const prompt = "Extract data";
35
35
  const options = { temperature: 0.5 };
36
36
  const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(prompt, TestSchema, options);
37
- (0, vitest_1.expect)(result.mainInstruction).toContain('You are a helpful assistant');
38
- (0, vitest_1.expect)(result.userMessagePayload).toBe(prompt);
37
+ (0, vitest_1.expect)(result.messages[0].content).toContain('You are a helpful assistant');
38
+ (0, vitest_1.expect)(result.messages[1].content).toBe(prompt);
39
39
  (0, vitest_1.expect)(result.dataExtractionSchema).toBe(TestSchema);
40
40
  (0, vitest_1.expect)(result.options).toBe(options);
41
41
  });
@@ -43,8 +43,8 @@ const createZodLlmClient_js_1 = require("./createZodLlmClient.js");
43
43
  const system = "System prompt";
44
44
  const user = "User prompt";
45
45
  const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(system, user, TestSchema);
46
- (0, vitest_1.expect)(result.mainInstruction).toBe(system);
47
- (0, vitest_1.expect)(result.userMessagePayload).toBe(user);
46
+ (0, vitest_1.expect)(result.messages[0].content).toBe(system);
47
+ (0, vitest_1.expect)(result.messages[1].content).toBe(user);
48
48
  (0, vitest_1.expect)(result.dataExtractionSchema).toBe(TestSchema);
49
49
  (0, vitest_1.expect)(result.options).toBeUndefined();
50
50
  });
@@ -53,11 +53,45 @@ const createZodLlmClient_js_1 = require("./createZodLlmClient.js");
53
53
  const user = "User prompt";
54
54
  const options = { temperature: 0.5 };
55
55
  const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(system, user, TestSchema, options);
56
- (0, vitest_1.expect)(result.mainInstruction).toBe(system);
57
- (0, vitest_1.expect)(result.userMessagePayload).toBe(user);
56
+ (0, vitest_1.expect)(result.messages[0].content).toBe(system);
57
+ (0, vitest_1.expect)(result.messages[1].content).toBe(user);
58
58
  (0, vitest_1.expect)(result.dataExtractionSchema).toBe(TestSchema);
59
59
  (0, vitest_1.expect)(result.options).toBe(options);
60
60
  });
61
+ (0, vitest_1.it)('should normalize Messages Array + Schema (Case 0)', () => {
62
+ const messages = [
63
+ { role: 'system', content: 'Sys' },
64
+ { role: 'user', content: 'Usr' }
65
+ ];
66
+ const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(messages, TestSchema);
67
+ (0, vitest_1.expect)(result.messages).toBe(messages);
68
+ (0, vitest_1.expect)(result.dataExtractionSchema).toBe(TestSchema);
69
+ (0, vitest_1.expect)(result.options).toBeUndefined();
70
+ });
71
+ (0, vitest_1.it)('should normalize Messages Array + Schema with Options (Case 0)', () => {
72
+ const messages = [
73
+ { role: 'system', content: 'Sys' },
74
+ { role: 'user', content: 'Usr' }
75
+ ];
76
+ const options = { temperature: 0.5 };
77
+ const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(messages, TestSchema, options);
78
+ (0, vitest_1.expect)(result.messages).toBe(messages);
79
+ (0, vitest_1.expect)(result.dataExtractionSchema).toBe(TestSchema);
80
+ (0, vitest_1.expect)(result.options).toBe(options);
81
+ });
82
+ (0, vitest_1.it)('should normalize Messages Array with few-shot examples + Schema (Case 0)', () => {
83
+ const messages = [
84
+ { role: 'system', content: 'Extract sentiment.' },
85
+ { role: 'user', content: 'I love this!' },
86
+ { role: 'assistant', content: JSON.stringify({ sentiment: 'positive' }) },
87
+ { role: 'user', content: 'I hate this.' }
88
+ ];
89
+ const Schema = zod_1.z.object({ sentiment: zod_1.z.enum(['positive', 'negative']) });
90
+ const result = (0, createZodLlmClient_js_1.normalizeZodArgs)(messages, Schema);
91
+ (0, vitest_1.expect)(result.messages).toBe(messages);
92
+ (0, vitest_1.expect)(result.dataExtractionSchema).toBe(Schema);
93
+ (0, vitest_1.expect)(result.options).toBeUndefined();
94
+ });
61
95
  (0, vitest_1.it)('should throw error for invalid arguments', () => {
62
96
  (0, vitest_1.expect)(() => (0, createZodLlmClient_js_1.normalizeZodArgs)({})).toThrow('Invalid arguments');
63
97
  });
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './createLlmClient.js';
2
2
  export * from './createLlmRetryClient.js';
3
3
  export * from './createZodLlmClient.js';
4
+ export * from './createJsonSchemaLlmClient.js';
4
5
  export * from './llmFactory.js';
5
6
  export * from './retryUtils.js';
package/dist/index.js CHANGED
@@ -17,5 +17,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./createLlmClient.js"), exports);
18
18
  __exportStar(require("./createLlmRetryClient.js"), exports);
19
19
  __exportStar(require("./createZodLlmClient.js"), exports);
20
+ __exportStar(require("./createJsonSchemaLlmClient.js"), exports);
20
21
  __exportStar(require("./llmFactory.js"), exports);
21
22
  __exportStar(require("./retryUtils.js"), exports);
@@ -4,14 +4,18 @@ export interface CreateLlmFactoryParams extends CreateLlmClientParams {
4
4
  export declare function createLlm(params: CreateLlmFactoryParams): {
5
5
  promptZod: {
6
6
  <T extends import("zod").ZodType>(schema: T, options?: import("./createZodLlmClient.js").ZodLlmClientOptions): Promise<import("zod").infer<T>>;
7
+ <T extends import("zod").ZodType>(messages: import("openai/resources/index.js").ChatCompletionMessageParam[], schema: T, options?: import("./createZodLlmClient.js").ZodLlmClientOptions): Promise<import("zod").infer<T>>;
7
8
  <T extends import("zod").ZodType>(prompt: string, schema: T, options?: import("./createZodLlmClient.js").ZodLlmClientOptions): Promise<import("zod").infer<T>>;
8
9
  <T extends import("zod").ZodType>(mainInstruction: string, userMessagePayload: string | import("openai/resources/index.js").ChatCompletionContentPart[], dataExtractionSchema: T, options?: import("./createZodLlmClient.js").ZodLlmClientOptions): Promise<import("zod").infer<T>>;
9
10
  };
10
11
  isPromptZodCached: {
11
12
  <T extends import("zod").ZodType>(schema: T, options?: import("./createZodLlmClient.js").ZodLlmClientOptions): Promise<boolean>;
13
+ <T extends import("zod").ZodType>(messages: import("openai/resources/index.js").ChatCompletionMessageParam[], schema: T, options?: import("./createZodLlmClient.js").ZodLlmClientOptions): Promise<boolean>;
12
14
  <T extends import("zod").ZodType>(prompt: string, schema: T, options?: import("./createZodLlmClient.js").ZodLlmClientOptions): Promise<boolean>;
13
15
  <T extends import("zod").ZodType>(mainInstruction: string, userMessagePayload: string | import("openai/resources/index.js").ChatCompletionContentPart[], dataExtractionSchema: T, options?: import("./createZodLlmClient.js").ZodLlmClientOptions): Promise<boolean>;
14
16
  };
17
+ promptJson: <T>(messages: import("openai/resources/index.js").ChatCompletionMessageParam[], schema: Record<string, any>, validator: (data: any) => T, options?: import("./createJsonSchemaLlmClient.js").JsonSchemaLlmClientOptions) => Promise<T>;
18
+ isPromptJsonCached: (messages: import("openai/resources/index.js").ChatCompletionMessageParam[], schema: Record<string, any>, options?: import("./createJsonSchemaLlmClient.js").JsonSchemaLlmClientOptions) => Promise<boolean>;
15
19
  promptRetry: {
16
20
  <T = import("openai/resources/index.js").ChatCompletion>(content: string, options?: Omit<import("./createLlmRetryClient.js").LlmRetryOptions<T>, "messages">): Promise<T>;
17
21
  <T = import("openai/resources/index.js").ChatCompletion>(options: import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
@@ -4,18 +4,23 @@ exports.createLlm = createLlm;
4
4
  const createLlmClient_js_1 = require("./createLlmClient.js");
5
5
  const createLlmRetryClient_js_1 = require("./createLlmRetryClient.js");
6
6
  const createZodLlmClient_js_1 = require("./createZodLlmClient.js");
7
+ const createJsonSchemaLlmClient_js_1 = require("./createJsonSchemaLlmClient.js");
7
8
  function createLlm(params) {
8
9
  const baseClient = (0, createLlmClient_js_1.createLlmClient)(params);
9
10
  const retryClient = (0, createLlmRetryClient_js_1.createLlmRetryClient)({
10
11
  prompt: baseClient.prompt
11
12
  });
12
- const zodClient = (0, createZodLlmClient_js_1.createZodLlmClient)({
13
+ const jsonSchemaClient = (0, createJsonSchemaLlmClient_js_1.createJsonSchemaLlmClient)({
13
14
  prompt: baseClient.prompt,
14
15
  isPromptCached: baseClient.isPromptCached
15
16
  });
17
+ const zodClient = (0, createZodLlmClient_js_1.createZodLlmClient)({
18
+ jsonSchemaClient
19
+ });
16
20
  return {
17
21
  ...baseClient,
18
22
  ...retryClient,
23
+ ...jsonSchemaClient,
19
24
  ...zodClient
20
25
  };
21
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-fns",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",