llm-fns 1.0.3 → 1.0.5

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.
@@ -1,129 +1,95 @@
1
- import OpenAI from 'openai';
2
- import { PromptFunction, LlmPromptOptions, normalizeOptions } from "./createLlmClient.js";
3
-
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LlmRetryAttemptError = exports.LlmRetryExhaustedError = exports.LlmRetryError = void 0;
4
+ exports.createLlmRetryClient = createLlmRetryClient;
5
+ const createLlmClient_js_1 = require("./createLlmClient.js");
4
6
  // Custom error for the querier to handle, allowing retries with structured feedback.
5
- export class LlmRetryError extends Error {
6
- constructor(
7
- public readonly message: string,
8
- public readonly type: 'JSON_PARSE_ERROR' | 'CUSTOM_ERROR',
9
- public readonly details?: any,
10
- public readonly rawResponse?: string | null,
11
- ) {
7
+ class LlmRetryError extends Error {
8
+ message;
9
+ type;
10
+ details;
11
+ rawResponse;
12
+ constructor(message, type, details, rawResponse) {
12
13
  super(message);
14
+ this.message = message;
15
+ this.type = type;
16
+ this.details = details;
17
+ this.rawResponse = rawResponse;
13
18
  this.name = 'LlmRetryError';
14
19
  }
15
20
  }
16
-
17
- export class LlmRetryExhaustedError extends Error {
18
- constructor(
19
- public readonly message: string,
20
- options?: ErrorOptions
21
- ) {
21
+ exports.LlmRetryError = LlmRetryError;
22
+ class LlmRetryExhaustedError extends Error {
23
+ message;
24
+ constructor(message, options) {
22
25
  super(message, options);
26
+ this.message = message;
23
27
  this.name = 'LlmRetryExhaustedError';
24
28
  }
25
29
  }
26
-
30
+ exports.LlmRetryExhaustedError = LlmRetryExhaustedError;
27
31
  // This error is thrown by LlmRetryClient for each failed attempt.
28
32
  // It wraps the underlying error (from API call or validation) and adds context.
29
- export class LlmRetryAttemptError extends Error {
30
- constructor(
31
- public readonly message: string,
32
- public readonly mode: 'main' | 'fallback',
33
- public readonly conversation: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
34
- public readonly attemptNumber: number,
35
- options?: ErrorOptions
36
- ) {
33
+ class LlmRetryAttemptError extends Error {
34
+ message;
35
+ mode;
36
+ conversation;
37
+ attemptNumber;
38
+ constructor(message, mode, conversation, attemptNumber, options) {
37
39
  super(message, options);
40
+ this.message = message;
41
+ this.mode = mode;
42
+ this.conversation = conversation;
43
+ this.attemptNumber = attemptNumber;
38
44
  this.name = 'LlmRetryAttemptError';
39
45
  }
40
46
  }
41
-
42
- export interface LlmRetryResponseInfo {
43
- mode: 'main' | 'fallback';
44
- conversation: OpenAI.Chat.Completions.ChatCompletionMessageParam[];
45
- attemptNumber: number;
46
- }
47
-
48
- export type LlmRetryOptions<T = any> = LlmPromptOptions & {
49
- maxRetries?: number;
50
- validate?: (response: any, info: LlmRetryResponseInfo) => Promise<T>;
51
- };
52
-
53
- export interface CreateLlmRetryClientParams {
54
- prompt: PromptFunction;
55
- fallbackPrompt?: PromptFunction;
56
- }
57
-
58
- function constructLlmMessages(
59
- initialMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
60
- attemptNumber: number,
61
- previousError?: LlmRetryAttemptError
62
- ): OpenAI.Chat.Completions.ChatCompletionMessageParam[] {
47
+ exports.LlmRetryAttemptError = LlmRetryAttemptError;
48
+ function constructLlmMessages(initialMessages, attemptNumber, previousError) {
63
49
  if (attemptNumber === 0) {
64
50
  // First attempt
65
51
  return initialMessages;
66
52
  }
67
-
68
53
  if (!previousError) {
69
54
  // Should not happen for attempt > 0, but as a safeguard...
70
55
  throw new Error("Invariant violation: previousError is missing for a retry attempt.");
71
56
  }
72
57
  const cause = previousError.cause;
73
-
74
58
  if (!(cause instanceof LlmRetryError)) {
75
- throw Error('cause must be an instanceof LlmRetryError')
59
+ throw Error('cause must be an instanceof LlmRetryError');
76
60
  }
77
-
78
- const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [...previousError.conversation];
79
-
61
+ const messages = [...previousError.conversation];
80
62
  messages.push({ role: "user", content: cause.message });
81
-
82
63
  return messages;
83
64
  }
84
-
85
- export function createLlmRetryClient(params: CreateLlmRetryClientParams) {
65
+ function createLlmRetryClient(params) {
86
66
  const { prompt, fallbackPrompt } = params;
87
-
88
- async function runPromptLoop<T>(
89
- options: LlmRetryOptions<T>,
90
- responseType: 'raw' | 'text' | 'image'
91
- ): Promise<T> {
67
+ async function runPromptLoop(options, responseType) {
92
68
  const { maxRetries = 3, validate, messages, ...restOptions } = options;
93
-
94
69
  // Ensure messages is an array (normalizeOptions ensures this but types might be loose)
95
- const initialMessages = messages as OpenAI.Chat.Completions.ChatCompletionMessageParam[];
96
-
97
- let lastError: LlmRetryAttemptError | undefined;
98
-
70
+ const initialMessages = messages;
71
+ let lastError;
99
72
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
100
73
  const useFallback = !!fallbackPrompt && attempt > 0;
101
- const currentPrompt = useFallback ? fallbackPrompt! : prompt;
74
+ const currentPrompt = useFallback ? fallbackPrompt : prompt;
102
75
  const mode = useFallback ? 'fallback' : 'main';
103
-
104
- const currentMessages = constructLlmMessages(
105
- initialMessages,
106
- attempt,
107
- lastError
108
- );
109
-
76
+ const currentMessages = constructLlmMessages(initialMessages, attempt, lastError);
110
77
  try {
111
78
  const completion = await currentPrompt({
112
79
  messages: currentMessages,
113
80
  ...restOptions,
114
81
  });
115
-
116
82
  const assistantMessage = completion.choices[0]?.message;
117
- let dataToProcess: any = completion;
118
-
83
+ let dataToProcess = completion;
119
84
  if (responseType === 'text') {
120
85
  const content = assistantMessage?.content;
121
86
  if (content === null || content === undefined) {
122
87
  throw new LlmRetryError("LLM returned no text content.", 'CUSTOM_ERROR', undefined, JSON.stringify(completion));
123
88
  }
124
89
  dataToProcess = content;
125
- } else if (responseType === 'image') {
126
- const messageAny = assistantMessage as any;
90
+ }
91
+ else if (responseType === 'image') {
92
+ const messageAny = assistantMessage;
127
93
  if (messageAny.images && Array.isArray(messageAny.images) && messageAny.images.length > 0) {
128
94
  const imageUrl = messageAny.images[0].image_url.url;
129
95
  if (typeof imageUrl === 'string') {
@@ -131,114 +97,69 @@ export function createLlmRetryClient(params: CreateLlmRetryClientParams) {
131
97
  const imgRes = await fetch(imageUrl);
132
98
  const arrayBuffer = await imgRes.arrayBuffer();
133
99
  dataToProcess = Buffer.from(arrayBuffer);
134
- } else {
100
+ }
101
+ else {
135
102
  const base64Data = imageUrl.replace(/^data:image\/\w+;base64,/, "");
136
103
  dataToProcess = Buffer.from(base64Data, 'base64');
137
104
  }
138
- } else {
105
+ }
106
+ else {
139
107
  throw new LlmRetryError("LLM returned invalid image URL.", 'CUSTOM_ERROR', undefined, JSON.stringify(completion));
140
108
  }
141
- } else {
109
+ }
110
+ else {
142
111
  throw new LlmRetryError("LLM returned no image.", 'CUSTOM_ERROR', undefined, JSON.stringify(completion));
143
112
  }
144
113
  }
145
-
146
114
  // Construct conversation history for success or potential error reporting
147
115
  const finalConversation = [...currentMessages];
148
116
  if (assistantMessage) {
149
117
  finalConversation.push(assistantMessage);
150
118
  }
151
-
152
- const info: LlmRetryResponseInfo = {
119
+ const info = {
153
120
  mode,
154
121
  conversation: finalConversation,
155
122
  attemptNumber: attempt,
156
123
  };
157
-
158
124
  if (validate) {
159
125
  const result = await validate(dataToProcess, info);
160
126
  return result;
161
127
  }
162
-
163
- return dataToProcess as T;
164
-
165
- } catch (error: any) {
128
+ return dataToProcess;
129
+ }
130
+ catch (error) {
166
131
  if (error instanceof LlmRetryError) {
167
132
  // This is a recoverable error, so we'll create a detailed attempt error and continue the loop.
168
133
  const conversationForError = [...currentMessages];
169
-
170
134
  // If the error contains the raw response (e.g. the invalid text), add it to history
171
135
  // so the LLM knows what it generated previously.
172
136
  if (error.rawResponse) {
173
137
  conversationForError.push({ role: 'assistant', content: error.rawResponse });
174
- } else if (responseType === 'raw' && error.details) {
138
+ }
139
+ else if (responseType === 'raw' && error.details) {
175
140
  // For raw mode, if we have details, maybe we can infer something, but usually rawResponse is key.
176
141
  }
177
-
178
- lastError = new LlmRetryAttemptError(
179
- `Attempt ${attempt + 1} failed.`,
180
- mode,
181
- conversationForError,
182
- attempt,
183
- { cause: error }
184
- );
185
- } else {
142
+ lastError = new LlmRetryAttemptError(`Attempt ${attempt + 1} failed.`, mode, conversationForError, attempt, { cause: error });
143
+ }
144
+ else {
186
145
  // This is a non-recoverable error (e.g., network, API key), so we re-throw it immediately.
187
146
  throw error;
188
147
  }
189
148
  }
190
149
  }
191
-
192
- throw new LlmRetryExhaustedError(
193
- `Operation failed after ${maxRetries + 1} attempts.`,
194
- { cause: lastError }
195
- );
150
+ throw new LlmRetryExhaustedError(`Operation failed after ${maxRetries + 1} attempts.`, { cause: lastError });
196
151
  }
197
-
198
- async function promptRetry<T = OpenAI.Chat.Completions.ChatCompletion>(
199
- content: string,
200
- options?: Omit<LlmRetryOptions<T>, 'messages'>
201
- ): Promise<T>;
202
- async function promptRetry<T = OpenAI.Chat.Completions.ChatCompletion>(
203
- options: LlmRetryOptions<T>
204
- ): Promise<T>;
205
- async function promptRetry<T = OpenAI.Chat.Completions.ChatCompletion>(
206
- arg1: string | LlmRetryOptions<T>,
207
- arg2?: Omit<LlmRetryOptions<T>, 'messages'>
208
- ): Promise<T> {
209
- const options = normalizeOptions(arg1, arg2) as LlmRetryOptions<T>;
152
+ async function promptRetry(arg1, arg2) {
153
+ const options = (0, createLlmClient_js_1.normalizeOptions)(arg1, arg2);
210
154
  return runPromptLoop(options, 'raw');
211
155
  }
212
-
213
- async function promptTextRetry<T = string>(
214
- content: string,
215
- options?: Omit<LlmRetryOptions<T>, 'messages'>
216
- ): Promise<T>;
217
- async function promptTextRetry<T = string>(
218
- options: LlmRetryOptions<T>
219
- ): Promise<T>;
220
- async function promptTextRetry<T = string>(
221
- arg1: string | LlmRetryOptions<T>,
222
- arg2?: Omit<LlmRetryOptions<T>, 'messages'>
223
- ): Promise<T> {
224
- const options = normalizeOptions(arg1, arg2) as LlmRetryOptions<T>;
156
+ async function promptTextRetry(arg1, arg2) {
157
+ const options = (0, createLlmClient_js_1.normalizeOptions)(arg1, arg2);
225
158
  return runPromptLoop(options, 'text');
226
159
  }
227
-
228
- async function promptImageRetry<T = Buffer>(
229
- content: string,
230
- options?: Omit<LlmRetryOptions<T>, 'messages'>
231
- ): Promise<T>;
232
- async function promptImageRetry<T = Buffer>(
233
- options: LlmRetryOptions<T>
234
- ): Promise<T>;
235
- async function promptImageRetry<T = Buffer>(
236
- arg1: string | LlmRetryOptions<T>,
237
- arg2?: Omit<LlmRetryOptions<T>, 'messages'>
238
- ): Promise<T> {
239
- const options = normalizeOptions(arg1, arg2) as LlmRetryOptions<T>;
160
+ async function promptImageRetry(arg1, arg2) {
161
+ const options = (0, createLlmClient_js_1.normalizeOptions)(arg1, arg2);
240
162
  return runPromptLoop(options, 'image');
241
163
  }
242
-
243
164
  return { promptRetry, promptTextRetry, promptImageRetry };
244
165
  }
@@ -0,0 +1,45 @@
1
+ import OpenAI from 'openai';
2
+ import * as z from "zod";
3
+ import { PromptFunction, LlmPromptOptions, IsPromptCachedFunction } from "./createLlmClient.js";
4
+ 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
+ };
21
+ export interface CreateZodLlmClientParams {
22
+ prompt: PromptFunction;
23
+ isPromptCached: IsPromptCachedFunction;
24
+ fallbackPrompt?: PromptFunction;
25
+ disableJsonFixer?: boolean;
26
+ }
27
+ export interface NormalizedZodArgs<T extends ZodTypeAny> {
28
+ mainInstruction: string;
29
+ userMessagePayload: string | OpenAI.Chat.Completions.ChatCompletionContentPart[];
30
+ dataExtractionSchema: T;
31
+ options?: ZodLlmClientOptions;
32
+ }
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>;
34
+ export declare function createZodLlmClient(params: CreateZodLlmClientParams): {
35
+ promptZod: {
36
+ <T extends ZodTypeAny>(schema: T, options?: ZodLlmClientOptions): Promise<z.infer<T>>;
37
+ <T extends ZodTypeAny>(prompt: string, schema: T, options?: ZodLlmClientOptions): Promise<z.infer<T>>;
38
+ <T extends ZodTypeAny>(mainInstruction: string, userMessagePayload: string | OpenAI.Chat.Completions.ChatCompletionContentPart[], dataExtractionSchema: T, options?: ZodLlmClientOptions): Promise<z.infer<T>>;
39
+ };
40
+ isPromptZodCached: {
41
+ <T extends ZodTypeAny>(schema: T, options?: ZodLlmClientOptions): Promise<boolean>;
42
+ <T extends ZodTypeAny>(prompt: string, schema: T, options?: ZodLlmClientOptions): Promise<boolean>;
43
+ <T extends ZodTypeAny>(mainInstruction: string, userMessagePayload: string | OpenAI.Chat.Completions.ChatCompletionContentPart[], dataExtractionSchema: T, options?: ZodLlmClientOptions): Promise<boolean>;
44
+ };
45
+ };