@with-logic/intent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/dist/batches.d.ts +55 -0
- package/dist/batches.js +91 -0
- package/dist/batches.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +29 -0
- package/dist/config.js.map +1 -0
- package/dist/extractors.d.ts +52 -0
- package/dist/extractors.js +88 -0
- package/dist/extractors.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/intent.d.ts +402 -0
- package/dist/intent.js +540 -0
- package/dist/intent.js.map +1 -0
- package/dist/lib/config.d.ts +30 -0
- package/dist/lib/config.js +81 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/number.d.ts +5 -0
- package/dist/lib/number.js +15 -0
- package/dist/lib/number.js.map +1 -0
- package/dist/llm_client.d.ts +14 -0
- package/dist/llm_client.js +29 -0
- package/dist/llm_client.js.map +1 -0
- package/dist/messages.d.ts +41 -0
- package/dist/messages.js +136 -0
- package/dist/messages.js.map +1 -0
- package/dist/providers/groq.d.ts +84 -0
- package/dist/providers/groq.js +335 -0
- package/dist/providers/groq.js.map +1 -0
- package/dist/schema.d.ts +82 -0
- package/dist/schema.js +114 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +78 -0
package/dist/intent.d.ts
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { CONFIG } from "./config";
|
|
2
|
+
import type { IntentOptions } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* LLM-based reranker for arbitrary items.
|
|
5
|
+
*
|
|
6
|
+
* Uses a listwise LLM approach to score candidates within a configurable range based on relevance to a query,
|
|
7
|
+
* then filters by threshold and returns results sorted by score with stable ordering.
|
|
8
|
+
*
|
|
9
|
+
* @template T - The type of items to rerank (defaults to any)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // Simplest: uses defaults and GROQ_API_KEY from environment
|
|
14
|
+
* const intent = new Intent();
|
|
15
|
+
* const ranked = await intent.rank("find expense reports", items);
|
|
16
|
+
*
|
|
17
|
+
* // With custom extractors
|
|
18
|
+
* type Document = { id: string; title: string; content: string };
|
|
19
|
+
* const intent = new Intent<Document>({
|
|
20
|
+
* key: doc => doc.title,
|
|
21
|
+
* summary: doc => doc.content.slice(0, 200),
|
|
22
|
+
* relevancyThreshold: 5,
|
|
23
|
+
* batchSize: 20
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // With custom LLM client
|
|
27
|
+
* const intent = new Intent<Document>({
|
|
28
|
+
* llm: myClient,
|
|
29
|
+
* userId: "user-123",
|
|
30
|
+
* key: doc => doc.title,
|
|
31
|
+
* summary: doc => doc.content.slice(0, 200)
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare class Intent<T = any> {
|
|
36
|
+
private readonly cfg;
|
|
37
|
+
private readonly llm;
|
|
38
|
+
private readonly ctx;
|
|
39
|
+
private readonly extractors;
|
|
40
|
+
private readonly env;
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the model name to use for this Intent instance.
|
|
43
|
+
*
|
|
44
|
+
* Intent is provider-driven. Today only GROQ is supported; when using GROQ
|
|
45
|
+
* we always take the model from GROQ's config defaults.
|
|
46
|
+
*
|
|
47
|
+
* @returns Provider-specific model name
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
private resolveModel;
|
|
51
|
+
/**
|
|
52
|
+
* Builds the context object from options.
|
|
53
|
+
*
|
|
54
|
+
* Constructs an IntentContext with only defined properties to satisfy
|
|
55
|
+
* TypeScript's exactOptionalPropertyTypes requirement.
|
|
56
|
+
*
|
|
57
|
+
* @param options - The options object containing llm, logger, and userId
|
|
58
|
+
* @returns IntentContext with only defined properties
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
private buildContext;
|
|
62
|
+
/**
|
|
63
|
+
* Builds the extractors object from options.
|
|
64
|
+
*
|
|
65
|
+
* Uses provided extractors or falls back to generic defaults that work
|
|
66
|
+
* for any type T via JSON stringification and hashing.
|
|
67
|
+
*
|
|
68
|
+
* @param options - The options object containing key and summary extractors
|
|
69
|
+
* @returns Required extractors with defaults applied
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
private buildExtractors;
|
|
73
|
+
/**
|
|
74
|
+
* Builds the configuration object from options.
|
|
75
|
+
*
|
|
76
|
+
* Merges user-provided options with environment-based CONFIG defaults.
|
|
77
|
+
*
|
|
78
|
+
* @param options - The options object containing config overrides
|
|
79
|
+
* @returns Required config with all values populated
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
private buildConfig;
|
|
83
|
+
/**
|
|
84
|
+
* Validates the configuration values.
|
|
85
|
+
*
|
|
86
|
+
* Ensures the configured score range is valid and the relevancyThreshold is in range.
|
|
87
|
+
*
|
|
88
|
+
* @throws {Error} If maxScore is below minScore
|
|
89
|
+
* @throws {Error} If relevancyThreshold is not within [minScore, maxScore]
|
|
90
|
+
* @private
|
|
91
|
+
*/
|
|
92
|
+
private validateConfig;
|
|
93
|
+
/**
|
|
94
|
+
* Selects and validates the LLM client.
|
|
95
|
+
*
|
|
96
|
+
* Uses the provided client from context or attempts to create a default
|
|
97
|
+
* Groq client if GROQ_API_KEY is available.
|
|
98
|
+
*
|
|
99
|
+
* @returns The selected LLM client
|
|
100
|
+
* @throws {Error} If no LLM client is provided and GROQ_API_KEY is not set
|
|
101
|
+
* @private
|
|
102
|
+
*/
|
|
103
|
+
private selectAndValidateLlmClient;
|
|
104
|
+
/**
|
|
105
|
+
* Creates a new Intent instance.
|
|
106
|
+
*
|
|
107
|
+
* All options are optional with sensible defaults:
|
|
108
|
+
* - llm: Auto-detected from GROQ_API_KEY environment variable if available
|
|
109
|
+
* - key: Hash-based string from JSON representation of items
|
|
110
|
+
* - summary: Pretty-printed JSON of items (2-space indentation for LLM readability)
|
|
111
|
+
* - Config values: From INTENT_* environment variables or built-in defaults
|
|
112
|
+
*
|
|
113
|
+
* @param options - Optional configuration object
|
|
114
|
+
* @param options.llm - Optional LLM client. If omitted, uses Groq client when GROQ_API_KEY is set
|
|
115
|
+
* @param options.logger - Optional logger for warnings and errors
|
|
116
|
+
* @param options.userId - Optional user identifier for LLM provider abuse monitoring
|
|
117
|
+
* @param options.key - Optional function extracting a short human-readable key from items
|
|
118
|
+
* @param options.summary - Optional function extracting a short description for LLM reasoning
|
|
119
|
+
* @param options.provider - Optional provider override (default: INTENT_PROVIDER or "GROQ")
|
|
120
|
+
* @param options.timeoutMs - Optional timeout in milliseconds (default: INTENT_TIMEOUT_MS or 3000)
|
|
121
|
+
* @param options.relevancyThreshold - Optional minimum score to include results (default: INTENT_RELEVANCY_THRESHOLD)
|
|
122
|
+
* @param options.minScore - Optional minimum score value (default: INTENT_MIN_SCORE or 0)
|
|
123
|
+
* @param options.maxScore - Optional maximum score value (default: INTENT_MAX_SCORE or 10)
|
|
124
|
+
* @param options.batchSize - Optional number of candidates per LLM call (default: INTENT_BATCH_SIZE or 20)
|
|
125
|
+
* @param options.tinyBatchFraction - Optional threshold for merging small batches (default: INTENT_TINY_BATCH_FRACTION or 0.2)
|
|
126
|
+
* @throws {Error} If no LLM client is provided and GROQ_API_KEY is not set
|
|
127
|
+
* @throws {Error} If maxScore is below minScore
|
|
128
|
+
* @throws {Error} If relevancyThreshold is not within [minScore, maxScore]
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* // Minimal - uses all defaults
|
|
133
|
+
* const intent = new Intent();
|
|
134
|
+
*
|
|
135
|
+
* // With extractors
|
|
136
|
+
* const intent = new Intent<Doc>({
|
|
137
|
+
* key: doc => doc.title,
|
|
138
|
+
* summary: doc => doc.content
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* // Full configuration
|
|
142
|
+
* const intent = new Intent<Doc>({
|
|
143
|
+
* llm: myClient,
|
|
144
|
+
* userId: "org-123",
|
|
145
|
+
* key: doc => doc.title,
|
|
146
|
+
* summary: doc => doc.content,
|
|
147
|
+
* relevancyThreshold: 5,
|
|
148
|
+
* batchSize: 20
|
|
149
|
+
* });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
constructor(options?: IntentOptions<T> & {
|
|
153
|
+
config?: typeof CONFIG;
|
|
154
|
+
});
|
|
155
|
+
/**
|
|
156
|
+
* Rerank candidates based on relevance to a query.
|
|
157
|
+
*
|
|
158
|
+
* Calls the LLM to evaluate each candidate and returns only those above the
|
|
159
|
+
* configured threshold, sorted by score (desc) with stable ordering on ties.
|
|
160
|
+
*
|
|
161
|
+
* The LLM is instructed to always generate an explanation before the score.
|
|
162
|
+
* Explanations are only returned when `options.explain` is true.
|
|
163
|
+
*
|
|
164
|
+
* Fast-path optimizations:
|
|
165
|
+
* - Returns empty array for 0 candidates without LLM call
|
|
166
|
+
* - Returns single candidate unchanged without LLM call
|
|
167
|
+
*
|
|
168
|
+
* Error handling:
|
|
169
|
+
* - On any batch error, returns that batch's items in original order
|
|
170
|
+
* - On top-level error, returns all items in original order
|
|
171
|
+
* - All errors are logged via the configured logger
|
|
172
|
+
*
|
|
173
|
+
* @param query - The search query or user intent to rank against
|
|
174
|
+
* @param candidates - Array of items to rerank
|
|
175
|
+
* @param options - Optional per-call configuration
|
|
176
|
+
* @param options.explain - When true, return `{ item, explanation }[]` instead of `T[]`
|
|
177
|
+
* @param options.userId - Optional user ID for this specific call, overrides ctx.userId
|
|
178
|
+
* @returns Filtered and sorted array of items, or original order on any error
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* const itemsOnly = await intent.rank("find expense reports", docs);
|
|
183
|
+
*
|
|
184
|
+
* const withExplanations = await intent.rank("find expense reports", docs, { explain: true });
|
|
185
|
+
* // => [{ item: Doc, explanation: string }, ...]
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
rank(query: string, candidates: T[], options?: {
|
|
189
|
+
explain?: false;
|
|
190
|
+
userId?: string;
|
|
191
|
+
}): Promise<T[]>;
|
|
192
|
+
rank(query: string, candidates: T[], options: {
|
|
193
|
+
explain: true;
|
|
194
|
+
userId?: string;
|
|
195
|
+
}): Promise<Array<{
|
|
196
|
+
item: T;
|
|
197
|
+
explanation: string;
|
|
198
|
+
}>>;
|
|
199
|
+
/**
|
|
200
|
+
* Filter candidates based on relevance to a query.
|
|
201
|
+
*
|
|
202
|
+
* Calls the LLM to decide whether each candidate is relevant, then returns
|
|
203
|
+
* only the relevant items in the same order they were provided.
|
|
204
|
+
*
|
|
205
|
+
* Explanations are only returned when `options.explain` is true.
|
|
206
|
+
*
|
|
207
|
+
* Fast paths:
|
|
208
|
+
* - Returns [] for 0 candidates without LLM call
|
|
209
|
+
* - Returns the single candidate unchanged without LLM call
|
|
210
|
+
*
|
|
211
|
+
* Error handling:
|
|
212
|
+
* - On any batch error, preserves that batch's original order
|
|
213
|
+
* - On top-level error, preserves original order
|
|
214
|
+
*
|
|
215
|
+
* @param query - The search query or user intent to filter against
|
|
216
|
+
* @param candidates - Array of items to filter
|
|
217
|
+
* @param options - Optional per-call configuration
|
|
218
|
+
* @param options.explain - When true, return `{ item, explanation }[]` instead of `T[]`
|
|
219
|
+
* @param options.userId - Optional user ID for this specific call, overrides ctx.userId
|
|
220
|
+
* @returns Filtered array of items, preserving input order
|
|
221
|
+
*/
|
|
222
|
+
filter(query: string, candidates: T[], options?: {
|
|
223
|
+
explain?: false;
|
|
224
|
+
userId?: string;
|
|
225
|
+
}): Promise<T[]>;
|
|
226
|
+
filter(query: string, candidates: T[], options: {
|
|
227
|
+
explain: true;
|
|
228
|
+
userId?: string;
|
|
229
|
+
}): Promise<Array<{
|
|
230
|
+
item: T;
|
|
231
|
+
explanation: string;
|
|
232
|
+
}>>;
|
|
233
|
+
/**
|
|
234
|
+
* Choose exactly one candidate as the best match for a query.
|
|
235
|
+
*
|
|
236
|
+
* Uses a tournament strategy when inputs exceed the batch size:
|
|
237
|
+
* - Choose one from each batch
|
|
238
|
+
* - Then choose one from the batch winners
|
|
239
|
+
*
|
|
240
|
+
* This method always returns a single item.
|
|
241
|
+
*
|
|
242
|
+
* @param query - The search query or user intent
|
|
243
|
+
* @param candidates - Array of items to choose from
|
|
244
|
+
* @param options - Optional per-call configuration
|
|
245
|
+
* @param options.explain - When true, return `{ item, explanation }` instead of `T`
|
|
246
|
+
* @param options.userId - Optional user ID for this specific call, overrides ctx.userId
|
|
247
|
+
* @returns The single chosen item (or item + explanation)
|
|
248
|
+
*/
|
|
249
|
+
choice(query: string, candidates: T[], options?: {
|
|
250
|
+
explain?: false;
|
|
251
|
+
userId?: string;
|
|
252
|
+
}): Promise<T>;
|
|
253
|
+
choice(query: string, candidates: T[], options: {
|
|
254
|
+
explain: true;
|
|
255
|
+
userId?: string;
|
|
256
|
+
}): Promise<{
|
|
257
|
+
item: T;
|
|
258
|
+
explanation: string;
|
|
259
|
+
}>;
|
|
260
|
+
/**
|
|
261
|
+
* Normalize incoming items into a consistent shape for downstream processing.
|
|
262
|
+
*
|
|
263
|
+
* Extracts the key and summary from each item using the configured extractors,
|
|
264
|
+
* and attaches the original input index for stable sorting later.
|
|
265
|
+
*
|
|
266
|
+
* @param candidates - Raw items to prepare
|
|
267
|
+
* @returns Array of prepared candidates with extracted metadata and original index
|
|
268
|
+
* @private
|
|
269
|
+
*/
|
|
270
|
+
private prepareCandidates;
|
|
271
|
+
/**
|
|
272
|
+
* Ensure keys are unique by suffixing duplicates with their input index.
|
|
273
|
+
*
|
|
274
|
+
* When multiple items share the same key, subsequent occurrences are renamed
|
|
275
|
+
* to "Key (idx)" where idx is the original input index. This prevents JSON
|
|
276
|
+
* schema validation errors and ensures the LLM can score each item independently.
|
|
277
|
+
*
|
|
278
|
+
* @param itemsBase - Prepared candidates with potentially duplicate keys
|
|
279
|
+
* @returns Candidates with guaranteed unique keys
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
private ensureUniqueKeys;
|
|
283
|
+
/**
|
|
284
|
+
* Build the JSON schema and chat messages payload for the LLM.
|
|
285
|
+
*
|
|
286
|
+
* Creates a strict JSON schema requiring one integer property (minScore-maxScore) per candidate key,
|
|
287
|
+
* and constructs system + user messages instructing the LLM to score relevance.
|
|
288
|
+
*
|
|
289
|
+
* @param query - The search query to evaluate candidates against
|
|
290
|
+
* @param items - Candidates with unique keys and summaries
|
|
291
|
+
* @returns Object containing JSON schema and chat messages array
|
|
292
|
+
* @private
|
|
293
|
+
*/
|
|
294
|
+
private buildRequest;
|
|
295
|
+
/**
|
|
296
|
+
* Build the JSON schema and chat messages payload for the LLM filter call.
|
|
297
|
+
*
|
|
298
|
+
* @param query - The search query to evaluate candidates against
|
|
299
|
+
* @param items - Candidates with unique keys and summaries
|
|
300
|
+
* @returns Object containing JSON schema and chat messages array
|
|
301
|
+
* @private
|
|
302
|
+
*/
|
|
303
|
+
private buildFilterRequest;
|
|
304
|
+
/**
|
|
305
|
+
* Build the JSON schema and chat messages payload for the LLM choice call.
|
|
306
|
+
*
|
|
307
|
+
* @param query - The search query to choose against
|
|
308
|
+
* @param items - Candidates with unique keys and summaries
|
|
309
|
+
* @returns Object containing JSON schema and chat messages array
|
|
310
|
+
* @private
|
|
311
|
+
*/
|
|
312
|
+
private buildChoiceRequest;
|
|
313
|
+
/**
|
|
314
|
+
* Invoke the LLM and return the parsed map of candidate scores.
|
|
315
|
+
*
|
|
316
|
+
* Calls the configured LLM client with the messages, JSON schema, model config,
|
|
317
|
+
* and user ID. Returns null if the response is invalid or missing.
|
|
318
|
+
*
|
|
319
|
+
* @param messages - Chat messages (system + user) to send to LLM
|
|
320
|
+
* @param schema - Strict JSON schema defining expected response structure
|
|
321
|
+
* @param userId - Optional user identifier for provider abuse monitoring
|
|
322
|
+
* @returns Map of candidate keys to numeric scores, or null if response invalid
|
|
323
|
+
* @private
|
|
324
|
+
*/
|
|
325
|
+
private fetchEvaluations;
|
|
326
|
+
/**
|
|
327
|
+
* Invoke the LLM and return boolean relevancy decisions.
|
|
328
|
+
*
|
|
329
|
+
* @param messages - Chat messages to send
|
|
330
|
+
* @param schema - Strict JSON schema defining expected response structure
|
|
331
|
+
* @param userId - Optional user id
|
|
332
|
+
* @returns Map of candidate keys to filter decisions, or null if invalid
|
|
333
|
+
* @private
|
|
334
|
+
*/
|
|
335
|
+
private fetchFilterDecisions;
|
|
336
|
+
/**
|
|
337
|
+
* Invoke the LLM and return a single selected key.
|
|
338
|
+
*
|
|
339
|
+
* @param messages - Chat messages to send
|
|
340
|
+
* @param schema - Strict JSON schema defining expected response structure
|
|
341
|
+
* @param userId - Optional user id
|
|
342
|
+
* @returns Choice result, or null if invalid
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
345
|
+
private fetchChoice;
|
|
346
|
+
/**
|
|
347
|
+
* Apply boolean filter decisions while preserving input order.
|
|
348
|
+
*
|
|
349
|
+
* @param items - Candidates with unique keys
|
|
350
|
+
* @param decisions - Map of candidate keys to decisions
|
|
351
|
+
* @returns Filtered items with explanations
|
|
352
|
+
* @private
|
|
353
|
+
*/
|
|
354
|
+
private applyFilter;
|
|
355
|
+
/**
|
|
356
|
+
* Process a single batch of candidates through the LLM filter.
|
|
357
|
+
*
|
|
358
|
+
* @param query - Query to filter against
|
|
359
|
+
* @param batch - Prepared candidates
|
|
360
|
+
* @param options - Optional userId override
|
|
361
|
+
* @returns Filtered items (stable order), with explanations
|
|
362
|
+
* @private
|
|
363
|
+
*/
|
|
364
|
+
private processFilterBatch;
|
|
365
|
+
/**
|
|
366
|
+
* Process a batch of candidates and choose a single winner.
|
|
367
|
+
*
|
|
368
|
+
* @param query - Query to choose against
|
|
369
|
+
* @param batch - Candidates with unique keys
|
|
370
|
+
* @param userId - Optional userId override
|
|
371
|
+
* @returns Winner item with explanation
|
|
372
|
+
* @private
|
|
373
|
+
*/
|
|
374
|
+
private processChoiceBatch;
|
|
375
|
+
/**
|
|
376
|
+
* Apply relevancy threshold filtering and stable sorting.
|
|
377
|
+
*
|
|
378
|
+
* Scores are clamped to the configured score range, then filtered to keep only items with
|
|
379
|
+
* score > threshold. Results are sorted by score descending, with ties
|
|
380
|
+
* preserving original input order for deterministic results.
|
|
381
|
+
*
|
|
382
|
+
* @param items - Candidates with unique keys
|
|
383
|
+
* @param scores - Map of candidate keys to LLM-assigned scores
|
|
384
|
+
* @returns Filtered and sorted array of original items
|
|
385
|
+
* @private
|
|
386
|
+
*/
|
|
387
|
+
private rankAndFilter;
|
|
388
|
+
/**
|
|
389
|
+
* Process a single batch of candidates through the LLM.
|
|
390
|
+
*
|
|
391
|
+
* Ensures unique keys, builds the request payload, fetches scores from the LLM,
|
|
392
|
+
* and returns filtered and sorted results. On any error or null response from
|
|
393
|
+
* the LLM, returns items in their original order as a fallback.
|
|
394
|
+
*
|
|
395
|
+
* @param query - The search query to evaluate candidates against
|
|
396
|
+
* @param batch - Batch of prepared candidates to process
|
|
397
|
+
* @param userId - Optional user identifier for provider abuse monitoring
|
|
398
|
+
* @returns Ranked and filtered items, or original order on error
|
|
399
|
+
* @private
|
|
400
|
+
*/
|
|
401
|
+
private processBatch;
|
|
402
|
+
}
|