llm-fns 1.0.18 → 1.0.19
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/dist/createCachedFetcher.d.ts +1 -6
- package/dist/createCachedFetcher.js +34 -16
- package/dist/createJsonSchemaLlmClient.d.ts +7 -3
- package/dist/createJsonSchemaLlmClient.js +8 -17
- package/dist/createLlmClient.d.ts +51 -9
- package/dist/createLlmClient.js +40 -23
- package/dist/createLlmClient.spec.js +72 -0
- package/dist/createLlmRetryClient.d.ts +13 -9
- package/dist/createLlmRetryClient.js +16 -23
- package/dist/llmFactory.d.ts +9 -9
- package/package.json +1 -1
- package/readme.md +8 -2
|
@@ -14,12 +14,7 @@ export interface CreateFetcherDependencies {
|
|
|
14
14
|
prefix?: string;
|
|
15
15
|
/** Time-to-live for cache entries, in milliseconds. */
|
|
16
16
|
ttl?: number;
|
|
17
|
-
/** Request timeout in milliseconds. If not provided, no timeout is applied
|
|
18
|
-
|
|
19
|
-
I'm now generating the corrected version of `src/createCachedFetcher.ts`. The primary fix is removing the extraneous text from the `set` method signature within the `CacheLike` interface. I've ensured the syntax is correct, and I'm confident the test run should now pass. After this is output, I plan to assess its integration within the wider project.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*/
|
|
17
|
+
/** Request timeout in milliseconds. If not provided, no timeout is applied. */
|
|
23
18
|
timeout?: number;
|
|
24
19
|
/** User-Agent string for requests. */
|
|
25
20
|
userAgent?: string;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// src/createCachedFetcher.ts
|
|
3
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
4
|
};
|
|
@@ -7,20 +6,43 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
6
|
exports.CachedResponse = void 0;
|
|
8
7
|
exports.createCachedFetcher = createCachedFetcher;
|
|
9
8
|
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
-
// A custom Response class to correctly handle the `.url` property on cache HITs.
|
|
11
|
-
// This is an implementation detail and doesn't need to be exported.
|
|
12
9
|
class CachedResponse extends Response {
|
|
13
10
|
#finalUrl;
|
|
14
11
|
constructor(body, init, finalUrl) {
|
|
15
12
|
super(body, init);
|
|
16
13
|
this.#finalUrl = finalUrl;
|
|
17
14
|
}
|
|
18
|
-
// Override the read-only `url` property
|
|
19
15
|
get url() {
|
|
20
16
|
return this.#finalUrl;
|
|
21
17
|
}
|
|
22
18
|
}
|
|
23
19
|
exports.CachedResponse = CachedResponse;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a deterministic hash of headers for cache key generation.
|
|
22
|
+
* Headers are sorted alphabetically to ensure consistency.
|
|
23
|
+
*/
|
|
24
|
+
function hashHeaders(headers) {
|
|
25
|
+
if (!headers)
|
|
26
|
+
return '';
|
|
27
|
+
let headerEntries;
|
|
28
|
+
if (headers instanceof Headers) {
|
|
29
|
+
headerEntries = Array.from(headers.entries());
|
|
30
|
+
}
|
|
31
|
+
else if (Array.isArray(headers)) {
|
|
32
|
+
headerEntries = headers;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
headerEntries = Object.entries(headers);
|
|
36
|
+
}
|
|
37
|
+
if (headerEntries.length === 0)
|
|
38
|
+
return '';
|
|
39
|
+
// Sort alphabetically by key for deterministic ordering
|
|
40
|
+
headerEntries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
41
|
+
const headerString = headerEntries
|
|
42
|
+
.map(([key, value]) => `${key}:${value}`)
|
|
43
|
+
.join('|');
|
|
44
|
+
return crypto_1.default.createHash('md5').update(headerString).digest('hex');
|
|
45
|
+
}
|
|
24
46
|
/**
|
|
25
47
|
* Factory function that creates a `fetch` replacement with a caching layer.
|
|
26
48
|
* @param deps - Dependencies including the cache instance, prefix, TTL, and timeout.
|
|
@@ -30,8 +52,6 @@ function createCachedFetcher(deps) {
|
|
|
30
52
|
const { cache, prefix = 'http-cache', ttl, timeout, userAgent, fetch: customFetch, shouldCache } = deps;
|
|
31
53
|
const fetchImpl = customFetch ?? fetch;
|
|
32
54
|
const fetchWithTimeout = async (url, options) => {
|
|
33
|
-
// Correctly merge headers using Headers API to handle various input formats (plain object, Headers instance, array)
|
|
34
|
-
// and avoid issues with spreading Headers objects which can lead to lost headers or Symbol errors.
|
|
35
55
|
const headers = new Headers(options?.headers);
|
|
36
56
|
if (userAgent) {
|
|
37
57
|
headers.set('User-Agent', userAgent);
|
|
@@ -70,10 +90,7 @@ function createCachedFetcher(deps) {
|
|
|
70
90
|
clearTimeout(timeoutId);
|
|
71
91
|
}
|
|
72
92
|
};
|
|
73
|
-
// This is the actual fetcher implementation, returned by the factory.
|
|
74
|
-
// It "closes over" the dependencies provided to the factory.
|
|
75
93
|
return async (url, options) => {
|
|
76
|
-
// Determine the request method. Default to GET for fetch.
|
|
77
94
|
let method = 'GET';
|
|
78
95
|
if (options?.method) {
|
|
79
96
|
method = options.method;
|
|
@@ -87,7 +104,7 @@ function createCachedFetcher(deps) {
|
|
|
87
104
|
return fetchWithTimeout(url, options);
|
|
88
105
|
}
|
|
89
106
|
let cacheKey = `${prefix}:${urlString}`;
|
|
90
|
-
//
|
|
107
|
+
// Hash body for POST requests
|
|
91
108
|
if (method.toUpperCase() === 'POST' && options?.body) {
|
|
92
109
|
let bodyStr = '';
|
|
93
110
|
if (typeof options.body === 'string') {
|
|
@@ -97,7 +114,6 @@ function createCachedFetcher(deps) {
|
|
|
97
114
|
bodyStr = options.body.toString();
|
|
98
115
|
}
|
|
99
116
|
else {
|
|
100
|
-
// Fallback for other types, though mostly we expect string/JSON here
|
|
101
117
|
try {
|
|
102
118
|
bodyStr = JSON.stringify(options.body);
|
|
103
119
|
}
|
|
@@ -105,13 +121,17 @@ function createCachedFetcher(deps) {
|
|
|
105
121
|
bodyStr = 'unserializable';
|
|
106
122
|
}
|
|
107
123
|
}
|
|
108
|
-
const
|
|
109
|
-
cacheKey +=
|
|
124
|
+
const bodyHash = crypto_1.default.createHash('md5').update(bodyStr).digest('hex');
|
|
125
|
+
cacheKey += `:body:${bodyHash}`;
|
|
126
|
+
}
|
|
127
|
+
// Hash all request headers into cache key
|
|
128
|
+
const headersHash = hashHeaders(options?.headers);
|
|
129
|
+
if (headersHash) {
|
|
130
|
+
cacheKey += `:headers:${headersHash}`;
|
|
110
131
|
}
|
|
111
132
|
// 1. Check the cache
|
|
112
133
|
const cachedItem = await cache.get(cacheKey);
|
|
113
134
|
if (cachedItem) {
|
|
114
|
-
// Decode the base64 body back into a Buffer.
|
|
115
135
|
const body = Buffer.from(cachedItem.bodyBase64, 'base64');
|
|
116
136
|
return new CachedResponse(body, {
|
|
117
137
|
status: cachedItem.status,
|
|
@@ -135,7 +155,6 @@ function createCachedFetcher(deps) {
|
|
|
135
155
|
}
|
|
136
156
|
}
|
|
137
157
|
else {
|
|
138
|
-
// Default behavior: check for .error in JSON responses
|
|
139
158
|
const contentType = response.headers.get('content-type');
|
|
140
159
|
if (contentType && contentType.includes('application/json')) {
|
|
141
160
|
const checkClone = response.clone();
|
|
@@ -154,7 +173,6 @@ function createCachedFetcher(deps) {
|
|
|
154
173
|
if (isCacheable) {
|
|
155
174
|
const responseClone = response.clone();
|
|
156
175
|
const bodyBuffer = await responseClone.arrayBuffer();
|
|
157
|
-
// Convert ArrayBuffer to a base64 string for safe JSON serialization.
|
|
158
176
|
const bodyBase64 = Buffer.from(bodyBuffer).toString('base64');
|
|
159
177
|
const headers = Object.fromEntries(response.headers.entries());
|
|
160
178
|
const itemToCache = {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { PromptFunction,
|
|
3
|
-
|
|
2
|
+
import { PromptFunction, LlmCommonOptions } from "./createLlmClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* Options for JSON schema prompt functions.
|
|
5
|
+
* Extends common options with JSON-specific settings.
|
|
6
|
+
*/
|
|
7
|
+
export interface JsonSchemaLlmClientOptions extends LlmCommonOptions {
|
|
4
8
|
maxRetries?: number;
|
|
5
9
|
/**
|
|
6
10
|
* If true, passes `response_format: { type: 'json_object' }` to the model.
|
|
@@ -22,7 +26,7 @@ export type JsonSchemaLlmClientOptions = Omit<LlmPromptOptions, 'messages' | 're
|
|
|
22
26
|
* If not provided, an AJV-based validator will be used.
|
|
23
27
|
*/
|
|
24
28
|
validator?: (data: any) => any;
|
|
25
|
-
}
|
|
29
|
+
}
|
|
26
30
|
export interface CreateJsonSchemaLlmClientParams {
|
|
27
31
|
prompt: PromptFunction;
|
|
28
32
|
fallbackPrompt?: PromptFunction;
|
|
@@ -9,7 +9,7 @@ const createLlmRetryClient_js_1 = require("./createLlmRetryClient.js");
|
|
|
9
9
|
function createJsonSchemaLlmClient(params) {
|
|
10
10
|
const { prompt, fallbackPrompt, disableJsonFixer = false } = params;
|
|
11
11
|
const llmRetryClient = (0, createLlmRetryClient_js_1.createLlmRetryClient)({ prompt, fallbackPrompt });
|
|
12
|
-
const ajv = new ajv_1.default({ strict: false });
|
|
12
|
+
const ajv = new ajv_1.default({ strict: false });
|
|
13
13
|
async function _tryToFixJson(brokenResponse, schemaJsonString, errorDetails, options) {
|
|
14
14
|
const fixupPrompt = `
|
|
15
15
|
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.
|
|
@@ -37,7 +37,7 @@ ${brokenResponse}
|
|
|
37
37
|
const response_format = useResponseFormat
|
|
38
38
|
? { type: 'json_object' }
|
|
39
39
|
: undefined;
|
|
40
|
-
const { maxRetries, useResponseFormat: _useResponseFormat, ...restOptions } = options || {};
|
|
40
|
+
const { maxRetries, useResponseFormat: _useResponseFormat, beforeValidation, validator, ...restOptions } = options || {};
|
|
41
41
|
const completion = await prompt({
|
|
42
42
|
messages,
|
|
43
43
|
response_format,
|
|
@@ -51,7 +51,6 @@ ${brokenResponse}
|
|
|
51
51
|
}
|
|
52
52
|
async function _parseOrFixJson(llmResponseString, schemaJsonString, options) {
|
|
53
53
|
let jsonDataToParse = llmResponseString.trim();
|
|
54
|
-
// Robust handling for responses wrapped in markdown code blocks
|
|
55
54
|
const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)\s*```/;
|
|
56
55
|
const match = codeBlockRegex.exec(jsonDataToParse);
|
|
57
56
|
if (match && match[1]) {
|
|
@@ -65,9 +64,8 @@ ${brokenResponse}
|
|
|
65
64
|
}
|
|
66
65
|
catch (parseError) {
|
|
67
66
|
if (disableJsonFixer) {
|
|
68
|
-
throw parseError;
|
|
67
|
+
throw parseError;
|
|
69
68
|
}
|
|
70
|
-
// Attempt a one-time fix before failing.
|
|
71
69
|
const errorDetails = `JSON Parse Error: ${parseError.message}`;
|
|
72
70
|
const fixedResponse = await _tryToFixJson(jsonDataToParse, schemaJsonString, errorDetails, options);
|
|
73
71
|
if (fixedResponse) {
|
|
@@ -75,11 +73,10 @@ ${brokenResponse}
|
|
|
75
73
|
return JSON.parse(fixedResponse);
|
|
76
74
|
}
|
|
77
75
|
catch (e) {
|
|
78
|
-
// Fix-up failed, throw original error.
|
|
79
76
|
throw parseError;
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
|
-
throw parseError;
|
|
79
|
+
throw parseError;
|
|
83
80
|
}
|
|
84
81
|
}
|
|
85
82
|
async function _validateOrFix(jsonData, validator, schemaJsonString, options) {
|
|
@@ -93,7 +90,6 @@ ${brokenResponse}
|
|
|
93
90
|
if (disableJsonFixer) {
|
|
94
91
|
throw validationError;
|
|
95
92
|
}
|
|
96
|
-
// Attempt a one-time fix for schema validation errors.
|
|
97
93
|
const errorDetails = `Schema Validation Error: ${validationError.message}`;
|
|
98
94
|
const fixedResponse = await _tryToFixJson(JSON.stringify(jsonData, null, 2), schemaJsonString, errorDetails, options);
|
|
99
95
|
if (fixedResponse) {
|
|
@@ -105,11 +101,10 @@ ${brokenResponse}
|
|
|
105
101
|
return validator(fixedJsonData);
|
|
106
102
|
}
|
|
107
103
|
catch (e) {
|
|
108
|
-
// Fix-up failed, throw original validation error
|
|
109
104
|
throw validationError;
|
|
110
105
|
}
|
|
111
106
|
}
|
|
112
|
-
throw validationError;
|
|
107
|
+
throw validationError;
|
|
113
108
|
}
|
|
114
109
|
}
|
|
115
110
|
function _getJsonPromptConfig(messages, schema, options) {
|
|
@@ -120,12 +115,9 @@ Do NOT include any other text, explanations, or markdown formatting (like \`\`\`
|
|
|
120
115
|
|
|
121
116
|
JSON schema:
|
|
122
117
|
${schemaJsonString}`;
|
|
123
|
-
// Clone messages to avoid mutating the input
|
|
124
118
|
const finalMessages = [...messages];
|
|
125
|
-
// Find the first system message to append instructions to
|
|
126
119
|
const systemMessageIndex = finalMessages.findIndex(m => m.role === 'system');
|
|
127
120
|
if (systemMessageIndex !== -1) {
|
|
128
|
-
// Append to existing system message
|
|
129
121
|
const existingContent = finalMessages[systemMessageIndex].content;
|
|
130
122
|
finalMessages[systemMessageIndex] = {
|
|
131
123
|
...finalMessages[systemMessageIndex],
|
|
@@ -133,7 +125,6 @@ ${schemaJsonString}`;
|
|
|
133
125
|
};
|
|
134
126
|
}
|
|
135
127
|
else {
|
|
136
|
-
// Prepend new system message
|
|
137
128
|
finalMessages.unshift({
|
|
138
129
|
role: 'system',
|
|
139
130
|
content: commonPromptFooter
|
|
@@ -146,7 +137,6 @@ ${schemaJsonString}`;
|
|
|
146
137
|
return { finalMessages, schemaJsonString, response_format };
|
|
147
138
|
}
|
|
148
139
|
async function promptJson(messages, schema, options) {
|
|
149
|
-
// Default validator using AJV
|
|
150
140
|
const defaultValidator = (data) => {
|
|
151
141
|
try {
|
|
152
142
|
const validate = ajv.compile(schema);
|
|
@@ -180,7 +170,6 @@ The response provided was not valid JSON. Please correct it.`;
|
|
|
180
170
|
return validatedData;
|
|
181
171
|
}
|
|
182
172
|
catch (validationError) {
|
|
183
|
-
// We assume the validator throws an error with a meaningful message
|
|
184
173
|
const rawResponseForError = JSON.stringify(jsonData, null, 2);
|
|
185
174
|
const errorDetails = validationError.message;
|
|
186
175
|
const errorMessage = `Your previous response resulted in an error.
|
|
@@ -190,8 +179,10 @@ The response was valid JSON but did not conform to the required schema. Please r
|
|
|
190
179
|
throw new createLlmRetryClient_js_1.LlmRetryError(errorMessage, 'CUSTOM_ERROR', validationError, rawResponseForError);
|
|
191
180
|
}
|
|
192
181
|
};
|
|
182
|
+
const { maxRetries, useResponseFormat: _useResponseFormat, beforeValidation, validator: _validator, ...restOptions } = options || {};
|
|
193
183
|
const retryOptions = {
|
|
194
|
-
...
|
|
184
|
+
...restOptions,
|
|
185
|
+
maxRetries,
|
|
195
186
|
messages: finalMessages,
|
|
196
187
|
response_format,
|
|
197
188
|
validate: processResponse
|
|
@@ -21,12 +21,24 @@ export type OpenRouterResponseFormat = {
|
|
|
21
21
|
};
|
|
22
22
|
};
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
* These
|
|
26
|
-
* 'messages' is a required property, inherited from OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming.
|
|
24
|
+
* Request-level options passed to the OpenAI SDK.
|
|
25
|
+
* These are separate from the body parameters.
|
|
27
26
|
*/
|
|
28
|
-
export interface
|
|
29
|
-
|
|
27
|
+
export interface LlmRequestOptions {
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
timeout?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Merges two LlmRequestOptions objects.
|
|
34
|
+
* Headers are merged (override wins on conflict), other properties are replaced.
|
|
35
|
+
*/
|
|
36
|
+
export declare function mergeRequestOptions(base?: LlmRequestOptions, override?: LlmRequestOptions): LlmRequestOptions | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Common options shared by all prompt functions.
|
|
39
|
+
* Does NOT include messages - those are handled separately.
|
|
40
|
+
*/
|
|
41
|
+
export interface LlmCommonOptions {
|
|
30
42
|
model?: ModelConfig;
|
|
31
43
|
retries?: number;
|
|
32
44
|
/** @deprecated Use `reasoning` object instead. */
|
|
@@ -35,6 +47,31 @@ export interface LlmPromptOptions extends Omit<OpenAI.Chat.Completions.ChatCompl
|
|
|
35
47
|
image_config?: {
|
|
36
48
|
aspect_ratio?: string;
|
|
37
49
|
};
|
|
50
|
+
requestOptions?: LlmRequestOptions;
|
|
51
|
+
temperature?: number;
|
|
52
|
+
max_tokens?: number;
|
|
53
|
+
top_p?: number;
|
|
54
|
+
frequency_penalty?: number;
|
|
55
|
+
presence_penalty?: number;
|
|
56
|
+
stop?: string | string[];
|
|
57
|
+
reasoning_effort?: 'low' | 'medium' | 'high';
|
|
58
|
+
seed?: number;
|
|
59
|
+
user?: string;
|
|
60
|
+
tools?: OpenAI.Chat.Completions.ChatCompletionTool[];
|
|
61
|
+
tool_choice?: OpenAI.Chat.Completions.ChatCompletionToolChoiceOption;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Options for the individual "prompt" function calls.
|
|
65
|
+
* Allows messages as string or array for convenience.
|
|
66
|
+
*/
|
|
67
|
+
export interface LlmPromptOptions extends LlmCommonOptions {
|
|
68
|
+
messages: string | OpenAI.Chat.Completions.ChatCompletionMessageParam[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Internal normalized params - messages is always an array.
|
|
72
|
+
*/
|
|
73
|
+
export interface LlmPromptParams extends LlmCommonOptions {
|
|
74
|
+
messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[];
|
|
38
75
|
}
|
|
39
76
|
/**
|
|
40
77
|
* Options required to create an instance of the LlmClient.
|
|
@@ -45,8 +82,13 @@ export interface CreateLlmClientParams {
|
|
|
45
82
|
defaultModel: ModelConfig;
|
|
46
83
|
maxConversationChars?: number;
|
|
47
84
|
queue?: PQueue;
|
|
85
|
+
defaultRequestOptions?: LlmRequestOptions;
|
|
48
86
|
}
|
|
49
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Normalizes input arguments to LlmPromptParams.
|
|
89
|
+
* Handles string shorthand and messages-as-string.
|
|
90
|
+
*/
|
|
91
|
+
export declare function normalizeOptions(arg1: string | LlmPromptOptions, arg2?: LlmCommonOptions): LlmPromptParams;
|
|
50
92
|
/**
|
|
51
93
|
* Factory function that creates a GPT "prompt" function.
|
|
52
94
|
* @param params - The core dependencies (API key, base URL, default model).
|
|
@@ -54,15 +96,15 @@ export declare function normalizeOptions(arg1: string | LlmPromptOptions, arg2?:
|
|
|
54
96
|
*/
|
|
55
97
|
export declare function createLlmClient(params: CreateLlmClientParams): {
|
|
56
98
|
prompt: {
|
|
57
|
-
(content: string, options?:
|
|
99
|
+
(content: string, options?: LlmCommonOptions): Promise<OpenAI.Chat.Completions.ChatCompletion>;
|
|
58
100
|
(options: LlmPromptOptions): Promise<OpenAI.Chat.Completions.ChatCompletion>;
|
|
59
101
|
};
|
|
60
102
|
promptText: {
|
|
61
|
-
(content: string, options?:
|
|
103
|
+
(content: string, options?: LlmCommonOptions): Promise<string>;
|
|
62
104
|
(options: LlmPromptOptions): Promise<string>;
|
|
63
105
|
};
|
|
64
106
|
promptImage: {
|
|
65
|
-
(content: string, options?:
|
|
107
|
+
(content: string, options?: LlmCommonOptions): Promise<Buffer>;
|
|
66
108
|
(options: LlmPromptOptions): Promise<Buffer>;
|
|
67
109
|
};
|
|
68
110
|
};
|
package/dist/createLlmClient.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.countChars = countChars;
|
|
4
4
|
exports.truncateSingleMessage = truncateSingleMessage;
|
|
5
5
|
exports.truncateMessages = truncateMessages;
|
|
6
|
+
exports.mergeRequestOptions = mergeRequestOptions;
|
|
6
7
|
exports.normalizeOptions = normalizeOptions;
|
|
7
8
|
exports.createLlmClient = createLlmClient;
|
|
8
9
|
const retryUtils_js_1 = require("./retryUtils.js");
|
|
@@ -49,14 +50,12 @@ function truncateSingleMessage(message, charLimit) {
|
|
|
49
50
|
return messageCopy;
|
|
50
51
|
}
|
|
51
52
|
if (Array.isArray(messageCopy.content)) {
|
|
52
|
-
// Complex case: multipart message.
|
|
53
|
-
// Strategy: consolidate text, remove images if needed, then truncate text.
|
|
54
53
|
const textParts = messageCopy.content.filter((p) => p.type === 'text');
|
|
55
54
|
const imageParts = messageCopy.content.filter((p) => p.type === 'image_url');
|
|
56
55
|
let combinedText = textParts.map((p) => p.text).join('\n');
|
|
57
56
|
let keptImages = [...imageParts];
|
|
58
57
|
while (combinedText.length + (keptImages.length * 2500) > charLimit && keptImages.length > 0) {
|
|
59
|
-
keptImages.pop();
|
|
58
|
+
keptImages.pop();
|
|
60
59
|
}
|
|
61
60
|
const imageChars = keptImages.length * 2500;
|
|
62
61
|
const textCharLimit = charLimit - imageChars;
|
|
@@ -89,7 +88,6 @@ function truncateMessages(messages, limit) {
|
|
|
89
88
|
}
|
|
90
89
|
const mutableOtherMessages = JSON.parse(JSON.stringify(otherMessages));
|
|
91
90
|
let excessChars = totalChars - limit;
|
|
92
|
-
// Truncate messages starting from the second one.
|
|
93
91
|
for (let i = 1; i < mutableOtherMessages.length; i++) {
|
|
94
92
|
if (excessChars <= 0)
|
|
95
93
|
break;
|
|
@@ -100,7 +98,6 @@ function truncateMessages(messages, limit) {
|
|
|
100
98
|
mutableOtherMessages[i] = truncateSingleMessage(message, newCharCount);
|
|
101
99
|
excessChars -= charsToCut;
|
|
102
100
|
}
|
|
103
|
-
// If still over limit, truncate the first message.
|
|
104
101
|
if (excessChars > 0) {
|
|
105
102
|
const firstMessage = mutableOtherMessages[0];
|
|
106
103
|
const firstMessageChars = countChars(firstMessage);
|
|
@@ -108,7 +105,6 @@ function truncateMessages(messages, limit) {
|
|
|
108
105
|
const newCharCount = firstMessageChars - charsToCut;
|
|
109
106
|
mutableOtherMessages[0] = truncateSingleMessage(firstMessage, newCharCount);
|
|
110
107
|
}
|
|
111
|
-
// Filter out empty messages (char count is 0)
|
|
112
108
|
const finalMessages = mutableOtherMessages.filter(msg => countChars(msg) > 0);
|
|
113
109
|
return systemMessage ? [systemMessage, ...finalMessages] : finalMessages;
|
|
114
110
|
}
|
|
@@ -135,7 +131,6 @@ function concatMessageText(messages) {
|
|
|
135
131
|
}
|
|
136
132
|
function getPromptSummary(messages) {
|
|
137
133
|
const fullText = concatMessageText(messages);
|
|
138
|
-
// Replace multiple whitespace chars with a single space and trim.
|
|
139
134
|
const cleanedText = fullText.replace(/\s+/g, ' ').trim();
|
|
140
135
|
if (cleanedText.length <= 50) {
|
|
141
136
|
return cleanedText;
|
|
@@ -149,6 +144,30 @@ function getPromptSummary(messages) {
|
|
|
149
144
|
const middle = cleanedText.substring(midStart, midEnd);
|
|
150
145
|
return `${start}...${middle}...${end}`;
|
|
151
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Merges two LlmRequestOptions objects.
|
|
149
|
+
* Headers are merged (override wins on conflict), other properties are replaced.
|
|
150
|
+
*/
|
|
151
|
+
function mergeRequestOptions(base, override) {
|
|
152
|
+
if (!base && !override)
|
|
153
|
+
return undefined;
|
|
154
|
+
if (!base)
|
|
155
|
+
return override;
|
|
156
|
+
if (!override)
|
|
157
|
+
return base;
|
|
158
|
+
return {
|
|
159
|
+
...base,
|
|
160
|
+
...override,
|
|
161
|
+
headers: {
|
|
162
|
+
...base.headers,
|
|
163
|
+
...override.headers
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Normalizes input arguments to LlmPromptParams.
|
|
169
|
+
* Handles string shorthand and messages-as-string.
|
|
170
|
+
*/
|
|
152
171
|
function normalizeOptions(arg1, arg2) {
|
|
153
172
|
if (typeof arg1 === 'string') {
|
|
154
173
|
return {
|
|
@@ -171,14 +190,12 @@ function normalizeOptions(arg1, arg2) {
|
|
|
171
190
|
* @returns An async function `prompt` ready to make OpenAI calls.
|
|
172
191
|
*/
|
|
173
192
|
function createLlmClient(params) {
|
|
174
|
-
const { openai, defaultModel: factoryDefaultModel, maxConversationChars, queue } = params;
|
|
175
|
-
const getCompletionParams = (
|
|
176
|
-
const { model: callSpecificModel, messages,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
? [{ role: 'user', content: messages }]
|
|
193
|
+
const { openai, defaultModel: factoryDefaultModel, maxConversationChars, queue, defaultRequestOptions } = params;
|
|
194
|
+
const getCompletionParams = (promptParams) => {
|
|
195
|
+
const { model: callSpecificModel, messages, retries, requestOptions, ...restApiOptions } = promptParams;
|
|
196
|
+
const finalMessages = maxConversationChars
|
|
197
|
+
? truncateMessages(messages, maxConversationChars)
|
|
180
198
|
: messages;
|
|
181
|
-
const finalMessages = maxConversationChars ? truncateMessages(messagesArray, maxConversationChars) : messagesArray;
|
|
182
199
|
const baseConfig = typeof factoryDefaultModel === 'object' && factoryDefaultModel !== null
|
|
183
200
|
? factoryDefaultModel
|
|
184
201
|
: (typeof factoryDefaultModel === 'string' ? { model: factoryDefaultModel } : {});
|
|
@@ -196,15 +213,16 @@ function createLlmClient(params) {
|
|
|
196
213
|
messages: finalMessages,
|
|
197
214
|
...restApiOptions,
|
|
198
215
|
};
|
|
199
|
-
|
|
216
|
+
const mergedRequestOptions = mergeRequestOptions(defaultRequestOptions, requestOptions);
|
|
217
|
+
return { completionParams, modelToUse, finalMessages, retries, requestOptions: mergedRequestOptions };
|
|
200
218
|
};
|
|
201
219
|
async function prompt(arg1, arg2) {
|
|
202
|
-
const
|
|
203
|
-
const { completionParams, finalMessages, retries } = getCompletionParams(
|
|
220
|
+
const promptParams = normalizeOptions(arg1, arg2);
|
|
221
|
+
const { completionParams, finalMessages, retries, requestOptions } = getCompletionParams(promptParams);
|
|
204
222
|
const promptSummary = getPromptSummary(finalMessages);
|
|
205
223
|
const apiCall = async () => {
|
|
206
224
|
const task = () => (0, retryUtils_js_1.executeWithRetry)(async () => {
|
|
207
|
-
return openai.chat.completions.create(completionParams);
|
|
225
|
+
return openai.chat.completions.create(completionParams, requestOptions);
|
|
208
226
|
}, async (completion) => {
|
|
209
227
|
if (completion.error) {
|
|
210
228
|
return {
|
|
@@ -213,7 +231,6 @@ function createLlmClient(params) {
|
|
|
213
231
|
}
|
|
214
232
|
return { isValid: true, data: completion };
|
|
215
233
|
}, retries ?? 3, undefined, (error) => {
|
|
216
|
-
// Do not retry if the API key is invalid (401) or if the error code explicitly states it.
|
|
217
234
|
if (error?.status === 401 || error?.code === 'invalid_api_key') {
|
|
218
235
|
return false;
|
|
219
236
|
}
|
|
@@ -225,8 +242,8 @@ function createLlmClient(params) {
|
|
|
225
242
|
return apiCall();
|
|
226
243
|
}
|
|
227
244
|
async function promptText(arg1, arg2) {
|
|
228
|
-
const
|
|
229
|
-
const response = await prompt(
|
|
245
|
+
const promptParams = normalizeOptions(arg1, arg2);
|
|
246
|
+
const response = await prompt(promptParams);
|
|
230
247
|
const content = response.choices[0]?.message?.content;
|
|
231
248
|
if (content === null || content === undefined) {
|
|
232
249
|
throw new Error("LLM returned no text content.");
|
|
@@ -234,8 +251,8 @@ function createLlmClient(params) {
|
|
|
234
251
|
return content;
|
|
235
252
|
}
|
|
236
253
|
async function promptImage(arg1, arg2) {
|
|
237
|
-
const
|
|
238
|
-
const response = await prompt(
|
|
254
|
+
const promptParams = normalizeOptions(arg1, arg2);
|
|
255
|
+
const response = await prompt(promptParams);
|
|
239
256
|
const message = response.choices[0]?.message;
|
|
240
257
|
if (message.images && Array.isArray(message.images) && message.images.length > 0) {
|
|
241
258
|
const imageUrl = message.images[0].image_url.url;
|
|
@@ -37,4 +37,76 @@ const createLlmClient_js_1 = require("./createLlmClient.js");
|
|
|
37
37
|
temperature: 0.7
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
|
+
(0, vitest_1.it)('should include requestOptions when provided', () => {
|
|
41
|
+
const result = (0, createLlmClient_js_1.normalizeOptions)('Hello world', {
|
|
42
|
+
temperature: 0.5,
|
|
43
|
+
requestOptions: {
|
|
44
|
+
headers: { 'X-Custom': 'value' },
|
|
45
|
+
timeout: 5000
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
(0, vitest_1.expect)(result).toEqual({
|
|
49
|
+
messages: [{ role: 'user', content: 'Hello world' }],
|
|
50
|
+
temperature: 0.5,
|
|
51
|
+
requestOptions: {
|
|
52
|
+
headers: { 'X-Custom': 'value' },
|
|
53
|
+
timeout: 5000
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
(0, vitest_1.describe)('mergeRequestOptions', () => {
|
|
59
|
+
(0, vitest_1.it)('should return undefined when both are undefined', () => {
|
|
60
|
+
const result = (0, createLlmClient_js_1.mergeRequestOptions)(undefined, undefined);
|
|
61
|
+
(0, vitest_1.expect)(result).toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.it)('should return override when base is undefined', () => {
|
|
64
|
+
const override = { timeout: 5000 };
|
|
65
|
+
const result = (0, createLlmClient_js_1.mergeRequestOptions)(undefined, override);
|
|
66
|
+
(0, vitest_1.expect)(result).toBe(override);
|
|
67
|
+
});
|
|
68
|
+
(0, vitest_1.it)('should return base when override is undefined', () => {
|
|
69
|
+
const base = { timeout: 5000 };
|
|
70
|
+
const result = (0, createLlmClient_js_1.mergeRequestOptions)(base, undefined);
|
|
71
|
+
(0, vitest_1.expect)(result).toBe(base);
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.it)('should merge headers from both', () => {
|
|
74
|
+
const base = {
|
|
75
|
+
headers: { 'X-Base': 'base-value' },
|
|
76
|
+
timeout: 5000
|
|
77
|
+
};
|
|
78
|
+
const override = {
|
|
79
|
+
headers: { 'X-Override': 'override-value' }
|
|
80
|
+
};
|
|
81
|
+
const result = (0, createLlmClient_js_1.mergeRequestOptions)(base, override);
|
|
82
|
+
(0, vitest_1.expect)(result).toEqual({
|
|
83
|
+
headers: {
|
|
84
|
+
'X-Base': 'base-value',
|
|
85
|
+
'X-Override': 'override-value'
|
|
86
|
+
},
|
|
87
|
+
timeout: 5000
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
(0, vitest_1.it)('should override scalar properties', () => {
|
|
91
|
+
const base = { timeout: 5000 };
|
|
92
|
+
const override = { timeout: 10000 };
|
|
93
|
+
const result = (0, createLlmClient_js_1.mergeRequestOptions)(base, override);
|
|
94
|
+
(0, vitest_1.expect)(result).toEqual({ timeout: 10000, headers: {} });
|
|
95
|
+
});
|
|
96
|
+
(0, vitest_1.it)('should override conflicting headers', () => {
|
|
97
|
+
const base = {
|
|
98
|
+
headers: { 'X-Shared': 'base-value', 'X-Base': 'base' }
|
|
99
|
+
};
|
|
100
|
+
const override = {
|
|
101
|
+
headers: { 'X-Shared': 'override-value', 'X-Override': 'override' }
|
|
102
|
+
};
|
|
103
|
+
const result = (0, createLlmClient_js_1.mergeRequestOptions)(base, override);
|
|
104
|
+
(0, vitest_1.expect)(result).toEqual({
|
|
105
|
+
headers: {
|
|
106
|
+
'X-Shared': 'override-value',
|
|
107
|
+
'X-Base': 'base',
|
|
108
|
+
'X-Override': 'override'
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
40
112
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { PromptFunction, LlmPromptOptions } from "./createLlmClient.js";
|
|
2
|
+
import { PromptFunction, LlmCommonOptions, LlmPromptOptions } from "./createLlmClient.js";
|
|
3
3
|
export declare class LlmRetryError extends Error {
|
|
4
4
|
readonly message: string;
|
|
5
5
|
readonly type: 'JSON_PARSE_ERROR' | 'CUSTOM_ERROR';
|
|
@@ -23,26 +23,30 @@ export interface LlmRetryResponseInfo {
|
|
|
23
23
|
conversation: OpenAI.Chat.Completions.ChatCompletionMessageParam[];
|
|
24
24
|
attemptNumber: number;
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Options for retry prompt functions.
|
|
28
|
+
* Extends common options with retry-specific settings.
|
|
29
|
+
*/
|
|
30
|
+
export interface LlmRetryOptions<T = any> extends LlmCommonOptions {
|
|
27
31
|
maxRetries?: number;
|
|
28
32
|
validate?: (response: any, info: LlmRetryResponseInfo) => Promise<T>;
|
|
29
|
-
}
|
|
33
|
+
}
|
|
30
34
|
export interface CreateLlmRetryClientParams {
|
|
31
35
|
prompt: PromptFunction;
|
|
32
36
|
fallbackPrompt?: PromptFunction;
|
|
33
37
|
}
|
|
34
38
|
export declare function createLlmRetryClient(params: CreateLlmRetryClientParams): {
|
|
35
39
|
promptRetry: {
|
|
36
|
-
<T = OpenAI.Chat.Completions.ChatCompletion>(content: string, options?:
|
|
37
|
-
<T = OpenAI.Chat.Completions.ChatCompletion>(options: LlmRetryOptions<T>): Promise<T>;
|
|
40
|
+
<T = OpenAI.Chat.Completions.ChatCompletion>(content: string, options?: LlmRetryOptions<T>): Promise<T>;
|
|
41
|
+
<T = OpenAI.Chat.Completions.ChatCompletion>(options: LlmPromptOptions & LlmRetryOptions<T>): Promise<T>;
|
|
38
42
|
};
|
|
39
43
|
promptTextRetry: {
|
|
40
|
-
<T = string>(content: string, options?:
|
|
41
|
-
<T = string>(options: LlmRetryOptions<T>): Promise<T>;
|
|
44
|
+
<T = string>(content: string, options?: LlmRetryOptions<T>): Promise<T>;
|
|
45
|
+
<T = string>(options: LlmPromptOptions & LlmRetryOptions<T>): Promise<T>;
|
|
42
46
|
};
|
|
43
47
|
promptImageRetry: {
|
|
44
|
-
<T = Buffer<ArrayBufferLike>>(content: string, options?:
|
|
45
|
-
<T = Buffer<ArrayBufferLike>>(options: LlmRetryOptions<T>): Promise<T>;
|
|
48
|
+
<T = Buffer<ArrayBufferLike>>(content: string, options?: LlmRetryOptions<T>): Promise<T>;
|
|
49
|
+
<T = Buffer<ArrayBufferLike>>(options: LlmPromptOptions & LlmRetryOptions<T>): Promise<T>;
|
|
46
50
|
};
|
|
47
51
|
};
|
|
48
52
|
export type LlmRetryClient = ReturnType<typeof createLlmRetryClient>;
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.LlmRetryAttemptError = exports.LlmRetryExhaustedError = exports.LlmRetryError = void 0;
|
|
4
4
|
exports.createLlmRetryClient = createLlmRetryClient;
|
|
5
5
|
const createLlmClient_js_1 = require("./createLlmClient.js");
|
|
6
|
-
// Custom error for the querier to handle, allowing retries with structured feedback.
|
|
7
6
|
class LlmRetryError extends Error {
|
|
8
7
|
message;
|
|
9
8
|
type;
|
|
@@ -28,8 +27,6 @@ class LlmRetryExhaustedError extends Error {
|
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
exports.LlmRetryExhaustedError = LlmRetryExhaustedError;
|
|
31
|
-
// This error is thrown by LlmRetryClient for each failed attempt.
|
|
32
|
-
// It wraps the underlying error (from API call or validation) and adds context.
|
|
33
30
|
class LlmRetryAttemptError extends Error {
|
|
34
31
|
message;
|
|
35
32
|
mode;
|
|
@@ -45,13 +42,19 @@ class LlmRetryAttemptError extends Error {
|
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
exports.LlmRetryAttemptError = LlmRetryAttemptError;
|
|
45
|
+
function normalizeRetryOptions(arg1, arg2) {
|
|
46
|
+
const baseParams = (0, createLlmClient_js_1.normalizeOptions)(arg1, arg2);
|
|
47
|
+
return {
|
|
48
|
+
...baseParams,
|
|
49
|
+
...arg2,
|
|
50
|
+
messages: baseParams.messages
|
|
51
|
+
};
|
|
52
|
+
}
|
|
48
53
|
function constructLlmMessages(initialMessages, attemptNumber, previousError) {
|
|
49
54
|
if (attemptNumber === 0) {
|
|
50
|
-
// First attempt
|
|
51
55
|
return initialMessages;
|
|
52
56
|
}
|
|
53
57
|
if (!previousError) {
|
|
54
|
-
// Should not happen for attempt > 0, but as a safeguard...
|
|
55
58
|
throw new Error("Invariant violation: previousError is missing for a retry attempt.");
|
|
56
59
|
}
|
|
57
60
|
const cause = previousError.cause;
|
|
@@ -64,10 +67,8 @@ function constructLlmMessages(initialMessages, attemptNumber, previousError) {
|
|
|
64
67
|
}
|
|
65
68
|
function createLlmRetryClient(params) {
|
|
66
69
|
const { prompt, fallbackPrompt } = params;
|
|
67
|
-
async function runPromptLoop(
|
|
68
|
-
const { maxRetries = 3, validate, messages, ...restOptions } =
|
|
69
|
-
// Ensure messages is an array (normalizeOptions ensures this but types might be loose)
|
|
70
|
-
const initialMessages = messages;
|
|
70
|
+
async function runPromptLoop(retryParams, responseType) {
|
|
71
|
+
const { maxRetries = 3, validate, messages: initialMessages, ...restOptions } = retryParams;
|
|
71
72
|
let lastError;
|
|
72
73
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
73
74
|
const useFallback = !!fallbackPrompt && attempt > 0;
|
|
@@ -111,7 +112,6 @@ function createLlmRetryClient(params) {
|
|
|
111
112
|
throw new LlmRetryError("LLM returned no image.", 'CUSTOM_ERROR', undefined, JSON.stringify(completion));
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
|
-
// Construct conversation history for success or potential error reporting
|
|
115
115
|
const finalConversation = [...currentMessages];
|
|
116
116
|
if (assistantMessage) {
|
|
117
117
|
finalConversation.push(assistantMessage);
|
|
@@ -129,20 +129,13 @@ function createLlmRetryClient(params) {
|
|
|
129
129
|
}
|
|
130
130
|
catch (error) {
|
|
131
131
|
if (error instanceof LlmRetryError) {
|
|
132
|
-
// This is a recoverable error, so we'll create a detailed attempt error and continue the loop.
|
|
133
132
|
const conversationForError = [...currentMessages];
|
|
134
|
-
// If the error contains the raw response (e.g. the invalid text), add it to history
|
|
135
|
-
// so the LLM knows what it generated previously.
|
|
136
133
|
if (error.rawResponse) {
|
|
137
134
|
conversationForError.push({ role: 'assistant', content: error.rawResponse });
|
|
138
135
|
}
|
|
139
|
-
else if (responseType === 'raw' && error.details) {
|
|
140
|
-
// For raw mode, if we have details, maybe we can infer something, but usually rawResponse is key.
|
|
141
|
-
}
|
|
142
136
|
lastError = new LlmRetryAttemptError(`Attempt ${attempt + 1} failed.`, mode, conversationForError, attempt, { cause: error });
|
|
143
137
|
}
|
|
144
138
|
else {
|
|
145
|
-
// This is a non-recoverable error (e.g., network, API key), so we re-throw it immediately.
|
|
146
139
|
throw error;
|
|
147
140
|
}
|
|
148
141
|
}
|
|
@@ -150,16 +143,16 @@ function createLlmRetryClient(params) {
|
|
|
150
143
|
throw new LlmRetryExhaustedError(`Operation failed after ${maxRetries + 1} attempts.`, { cause: lastError });
|
|
151
144
|
}
|
|
152
145
|
async function promptRetry(arg1, arg2) {
|
|
153
|
-
const
|
|
154
|
-
return runPromptLoop(
|
|
146
|
+
const retryParams = normalizeRetryOptions(arg1, arg2);
|
|
147
|
+
return runPromptLoop(retryParams, 'raw');
|
|
155
148
|
}
|
|
156
149
|
async function promptTextRetry(arg1, arg2) {
|
|
157
|
-
const
|
|
158
|
-
return runPromptLoop(
|
|
150
|
+
const retryParams = normalizeRetryOptions(arg1, arg2);
|
|
151
|
+
return runPromptLoop(retryParams, 'text');
|
|
159
152
|
}
|
|
160
153
|
async function promptImageRetry(arg1, arg2) {
|
|
161
|
-
const
|
|
162
|
-
return runPromptLoop(
|
|
154
|
+
const retryParams = normalizeRetryOptions(arg1, arg2);
|
|
155
|
+
return runPromptLoop(retryParams, 'image');
|
|
163
156
|
}
|
|
164
157
|
return { promptRetry, promptTextRetry, promptImageRetry };
|
|
165
158
|
}
|
package/dist/llmFactory.d.ts
CHANGED
|
@@ -10,27 +10,27 @@ export declare function createLlm(params: CreateLlmFactoryParams): {
|
|
|
10
10
|
};
|
|
11
11
|
promptJson: <T>(messages: import("openai/resources/index.js").ChatCompletionMessageParam[], schema: Record<string, any>, options?: import("./createJsonSchemaLlmClient.js").JsonSchemaLlmClientOptions) => Promise<T>;
|
|
12
12
|
promptRetry: {
|
|
13
|
-
<T = import("openai/resources/index.js").ChatCompletion>(content: string, options?:
|
|
14
|
-
<T = import("openai/resources/index.js").ChatCompletion>(options: import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
13
|
+
<T = import("openai/resources/index.js").ChatCompletion>(content: string, options?: import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
14
|
+
<T = import("openai/resources/index.js").ChatCompletion>(options: import("./createLlmClient.js").LlmPromptOptions & import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
15
15
|
};
|
|
16
16
|
promptTextRetry: {
|
|
17
|
-
<T = string>(content: string, options?:
|
|
18
|
-
<T = string>(options: import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
17
|
+
<T = string>(content: string, options?: import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
18
|
+
<T = string>(options: import("./createLlmClient.js").LlmPromptOptions & import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
19
19
|
};
|
|
20
20
|
promptImageRetry: {
|
|
21
|
-
<T = Buffer<ArrayBufferLike>>(content: string, options?:
|
|
22
|
-
<T = Buffer<ArrayBufferLike>>(options: import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
21
|
+
<T = Buffer<ArrayBufferLike>>(content: string, options?: import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
22
|
+
<T = Buffer<ArrayBufferLike>>(options: import("./createLlmClient.js").LlmPromptOptions & import("./createLlmRetryClient.js").LlmRetryOptions<T>): Promise<T>;
|
|
23
23
|
};
|
|
24
24
|
prompt: {
|
|
25
|
-
(content: string, options?:
|
|
25
|
+
(content: string, options?: import("./createLlmClient.js").LlmCommonOptions): Promise<import("openai/resources/index.js").ChatCompletion>;
|
|
26
26
|
(options: import("./createLlmClient.js").LlmPromptOptions): Promise<import("openai/resources/index.js").ChatCompletion>;
|
|
27
27
|
};
|
|
28
28
|
promptText: {
|
|
29
|
-
(content: string, options?:
|
|
29
|
+
(content: string, options?: import("./createLlmClient.js").LlmCommonOptions): Promise<string>;
|
|
30
30
|
(options: import("./createLlmClient.js").LlmPromptOptions): Promise<string>;
|
|
31
31
|
};
|
|
32
32
|
promptImage: {
|
|
33
|
-
(content: string, options?:
|
|
33
|
+
(content: string, options?: import("./createLlmClient.js").LlmCommonOptions): Promise<Buffer>;
|
|
34
34
|
(options: import("./createLlmClient.js").LlmPromptOptions): Promise<Buffer>;
|
|
35
35
|
};
|
|
36
36
|
};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -27,6 +27,7 @@ const llm = createLlm({
|
|
|
27
27
|
// cache: Cache instance (cache-manager)
|
|
28
28
|
// queue: PQueue instance for concurrency control
|
|
29
29
|
// maxConversationChars: number (auto-truncation)
|
|
30
|
+
// defaultRequestOptions: { headers, timeout, signal }
|
|
30
31
|
});
|
|
31
32
|
```
|
|
32
33
|
|
|
@@ -214,8 +215,14 @@ const res = await llm.prompt({
|
|
|
214
215
|
|
|
215
216
|
// Library Extensions
|
|
216
217
|
model: "gpt-4o", // Override default model for this call
|
|
217
|
-
ttl: 5000, // Cache this specific call for 5s (in ms)
|
|
218
218
|
retries: 5, // Retry network errors 5 times
|
|
219
|
+
|
|
220
|
+
// Request-level options (headers, timeout, abort signal)
|
|
221
|
+
requestOptions: {
|
|
222
|
+
headers: { 'X-Cache-Salt': 'v2' }, // Affects cache key
|
|
223
|
+
timeout: 60000,
|
|
224
|
+
signal: abortController.signal
|
|
225
|
+
}
|
|
219
226
|
});
|
|
220
227
|
```
|
|
221
228
|
|
|
@@ -299,7 +306,6 @@ const gameState = await llm.promptZod(
|
|
|
299
306
|
model: "google/gemini-flash-1.5",
|
|
300
307
|
disableJsonFixer: true, // Turn off the automatic JSON repair agent
|
|
301
308
|
maxRetries: 0, // Fail immediately on error
|
|
302
|
-
ttl: 60000 // Cache result
|
|
303
309
|
}
|
|
304
310
|
);
|
|
305
311
|
```
|