@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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Logic App, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,277 @@
1
+ # Intent
2
+
3
+ `intent` is an LLM-based reranker library that offers ranking, filtering, and choice all with explicit, inspectable reasoning.
4
+
5
+ Unlike black-box models, `intent` generates an explanation alongside every score. This transparency allows for easier debugging and enables you to surface reasoning directly to users.
6
+
7
+ By being LLM powered, it also offers flexibility and dynamic tunability to
8
+ your ranking logic without needing to come up with custom embedding models or
9
+ retrain existing ones.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @with-logic/intent
15
+ ```
16
+
17
+ ## Quickstart
18
+
19
+ ```ts
20
+ import { Intent } from "@with-logic/intent";
21
+
22
+ const intent = new Intent({ relevancyThreshold: 1 });
23
+
24
+ const docs = [
25
+ "Many network requests can fail intermittently due to transient issues.",
26
+ "To reduce flaky tests, add exponential backoff with jitter to HTTP retries.",
27
+ "Citrus fruits like oranges and lemons are high in vitamin C.",
28
+ ];
29
+
30
+ const ranked = await intent.rank("exponential backoff retries", docs);
31
+ // => [doc1, doc2] (doc3 is filtered out via relevancy threshold)
32
+ ```
33
+
34
+ Intent will use a default Groq client when `GROQ_API_KEY` is set.
35
+
36
+ ## Core API
37
+
38
+ - `rank(query, candidates)` → rerank + threshold filter (score-based)
39
+ - `filter(query, candidates)` → keep only relevant items (boolean)
40
+ - `choice(query, candidates)` → choose exactly one best item
41
+
42
+ All three support `{ explain: true }` to return explanations.
43
+
44
+ ## Example Use Cases
45
+
46
+ ### 1) Ordering search results with `rank()`
47
+
48
+ Use `rank()` when you want ranked, ordered results.
49
+
50
+ ```ts
51
+ import { Intent } from "@with-logic/intent";
52
+
53
+ type Doc = {
54
+ id: string;
55
+ title: string;
56
+ body: string;
57
+ tags: string[];
58
+ };
59
+
60
+ const intent = new Intent<Doc>({
61
+ key: (d) => d.id,
62
+ relevancyThreshold: 5,
63
+ });
64
+
65
+ const docs: Doc[] = [
66
+ { id: "1", title: "Q2 expenses", body: "Travel, meals, ...", tags: ["finance"] },
67
+ { id: "2", title: "OKR planning", body: "Goals for ...", tags: ["strategy"] },
68
+ { id: "3", title: "Laptop purchases", body: "New laptops ...", tags: ["finance", "it"] },
69
+ ];
70
+
71
+ const results = await intent.rank("Find expense reports and anything about spend approvals", docs);
72
+ ```
73
+
74
+ ### Include explanations
75
+
76
+ ```ts
77
+ const results = await intent.rank("expense reports", docs, { explain: true });
78
+ // => [{ item: Doc, explanation: string }, ...]
79
+ ```
80
+
81
+ ## 2) Tool filtering with `filter()`
82
+
83
+ Use `filter()` when you want to keep the subset of items in a collection that
84
+ are relevant to a query.
85
+
86
+ ```ts
87
+ import { Intent } from "@with-logic/intent";
88
+
89
+ type Tool = {
90
+ name: string;
91
+ description: string;
92
+ };
93
+
94
+ const intent = new Intent<Tool>({
95
+ key: (t) => t.name,
96
+ summary: (t) => t.description,
97
+ });
98
+
99
+ const tools: Tool[] = [
100
+ { name: "search", description: "Search the web for up-to-date information" },
101
+ { name: "sendEmail", description: "Send an email to a recipient" },
102
+ { name: "runSQL", description: "Run a SQL query against the analytics DB" },
103
+ { name: "createInvoice", description: "Create an invoice for a customer" },
104
+ ];
105
+
106
+ const task = "Find the customer's last invoice total and email it to them.";
107
+
108
+ const relevantTools = await intent.filter(task, tools);
109
+ // [sendEmail, runSQL]
110
+ ```
111
+
112
+ ### Filter with explanations
113
+
114
+ ```ts
115
+ const relevantTools = await intent.filter(task, tools, { explain: true });
116
+ // => [{ item: Tool, explanation: string }, ...]
117
+ ```
118
+
119
+ ## 3) Model routing with `choice()`
120
+
121
+ Use `choice()` when you need exactly one selection from a set of items.
122
+
123
+ ```ts
124
+ import { Intent } from "@with-logic/intent";
125
+
126
+ type Model = {
127
+ id: string;
128
+ strengths: string;
129
+ };
130
+
131
+ const intent = new Intent<Model>({
132
+ key: (m) => m.id,
133
+ summary: (m) => m.strengths,
134
+ });
135
+
136
+ const models: Model[] = [
137
+ {
138
+ id: "gemini-3-pro",
139
+ strengths: "Hard reasoning, math, complex debugging. Slower but very strong.",
140
+ },
141
+ {
142
+ id: "gpt-5.2",
143
+ strengths: "Best for code generation, refactors, feature implementation.",
144
+ },
145
+ {
146
+ id: "haiku-4.5"
147
+ strengths: "Fast and cheap. Good for triage and simple edits.",
148
+ },
149
+ {
150
+ id: "nano-banana-pro",
151
+ strengths: "Image generation and visual content.",
152
+ },
153
+ ];
154
+
155
+ const task = "Implement a feature to add retries with exponential backoff and tests.";
156
+
157
+ const { item: selected, explanation } = await intent.choice(task, models, { explain: true });
158
+ // selected.id => gpt-5.2
159
+ ```
160
+
161
+ ## Configuration
162
+
163
+ Intent reads `.env` automatically when imported.
164
+
165
+ ```env
166
+ GROQ_API_KEY=your_groq_api_key_here
167
+
168
+ # Optional defaults
169
+ GROQ_DEFAULT_MODEL=openai/gpt-oss-20b
170
+ GROQ_DEFAULT_REASONING_EFFORT=medium
171
+ INTENT_TIMEOUT_MS=3000
172
+ INTENT_MIN_SCORE=0
173
+ INTENT_MAX_SCORE=10
174
+ INTENT_RELEVANCY_THRESHOLD=0
175
+ INTENT_BATCH_SIZE=20
176
+ INTENT_TINY_BATCH_FRACTION=0.2
177
+ ```
178
+
179
+ ### How configuration works
180
+
181
+ - You can configure Intent via **environment variables** (shown above) or via the `Intent` constructor.
182
+ - Constructor options override environment defaults for that instance.
183
+ - Most tuning is a trade-off between **quality**, **latency**, and **cost**.
184
+
185
+ ### Provider + model
186
+
187
+ #### `GROQ_API_KEY`
188
+
189
+ If you don't pass a custom `llm` client, Intent will create a default Groq client when this is set.
190
+
191
+ #### `GROQ_DEFAULT_MODEL`
192
+
193
+ Sets the model name used by the built-in Groq client.
194
+
195
+ - Choose a stronger model when you care about nuanced ranking or long candidate summaries.
196
+ - Choose a smaller model when you want lower latency/cost and your candidates are simple.
197
+
198
+ #### `GROQ_DEFAULT_REASONING_EFFORT`
199
+
200
+ Controls how much reasoning the model should do (`low | medium | high`).
201
+
202
+ - `low`: fastest; best for obvious matches.
203
+ - `medium`: good default.
204
+ - `high`: better for subtle intent, but typically slower/more expensive.
205
+
206
+ ### Ranking behavior
207
+
208
+ #### `INTENT_RELEVANCY_THRESHOLD`
209
+
210
+ Controls how selective the output is.
211
+
212
+ - Higher threshold → fewer results (higher precision)
213
+ - Lower threshold → more results (higher recall)
214
+
215
+ Important: threshold filtering is **strictly greater-than** (`score > threshold`).
216
+ So with the default score range `0..10`:
217
+
218
+ - `relevancyThreshold=0` keeps scores `1..10`
219
+ - `relevancyThreshold=5` keeps scores `6..10`
220
+
221
+ #### `INTENT_MIN_SCORE` / `INTENT_MAX_SCORE`
222
+
223
+ Controls the score range given to the LLM.
224
+
225
+ - Narrower ranges (e.g. `1..5`) can make scoring easier to calibrate.
226
+ - Wider ranges (e.g. `0..10`) give more resolution for ranking.
227
+
228
+ Note: Since the is an LLM's judgement, as opposed to an objective measurement,
229
+ scores may not use the full range perfectly and you may seem similar biases
230
+ that you'd see with human raters.
231
+
232
+ This also means that massive ranges (e.g. `0..1000`) may not yield more
233
+ precise results.
234
+
235
+ If you change the range, ensure your `relevancyThreshold` stays within it.
236
+
237
+ ### Performance knobs
238
+
239
+ #### `INTENT_TIMEOUT_MS`
240
+
241
+ Hard timeout per LLM call.
242
+
243
+ - Increase it when you have larger batches, longer summaries, or slower models.
244
+ - Decrease it when you prefer quick fallbacks over waiting.
245
+
246
+ If we timeout, we never throw an error; instead, we return the original
247
+ results.
248
+
249
+ #### `INTENT_BATCH_SIZE`
250
+
251
+ How many candidates are evaluated per LLM call.
252
+
253
+ - Larger batch size → fewer calls (often cheaper/faster), but higher token usage per call.
254
+ - Smaller batch size → more calls (often slower), but each call is smaller.
255
+
256
+ #### `INTENT_TINY_BATCH_FRACTION`
257
+
258
+ When the last batch is “too small”, Intent will merge it into the previous batch.
259
+
260
+ - Increase to avoid tiny extra calls (better latency/cost).
261
+ - Decrease if you frequently run near context limits.
262
+
263
+ ### Programmatic configuration (constructor)
264
+
265
+ Everything above can be set per instance:
266
+
267
+ ```ts
268
+ import { Intent } from "intent";
269
+
270
+ const intent = new Intent({
271
+ timeoutMs: 10_000,
272
+ batchSize: 25,
273
+ relevancyThreshold: 3,
274
+ minScore: 0,
275
+ maxScore: 10,
276
+ });
277
+ ```
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Utilities for creating and maintaining batches of items.
3
+ */
4
+ /**
5
+ * Slice a list into fixed-size batches.
6
+ *
7
+ * Divides the input array into chunks of the specified size. The final batch
8
+ * may be smaller than the requested size if items don't divide evenly.
9
+ *
10
+ * @param items - Array of items to batch
11
+ * @param size - Maximum number of items per batch
12
+ * @returns Array of batches, each containing up to `size` items
13
+ */
14
+ export declare function sliceIntoFixedBatches<T>(items: T[], size: number): T[][];
15
+ /**
16
+ * Merge the final batch into the previous when it's tiny (but non-empty).
17
+ *
18
+ * Small trailing batches are inefficient for LLM calls due to fixed overhead.
19
+ * This function merges the last batch into the second-to-last when the final
20
+ * batch is smaller than the threshold.
21
+ *
22
+ * @param batches - Array of batches to potentially merge
23
+ * @param tinyThreshold - Maximum size for a batch to be considered "tiny"
24
+ * @returns Modified array with tiny final batch merged, or original if no merge needed
25
+ */
26
+ export declare function mergeTinyFinalBatch<T>(batches: T[][], tinyThreshold: number): T[][];
27
+ /**
28
+ * Create batches then merge a tiny trailing batch.
29
+ *
30
+ * Combines sliceIntoFixedBatches and mergeTinyFinalBatch to efficiently
31
+ * partition items while avoiding wasteful small final batches.
32
+ *
33
+ * @param items - Array of items to batch
34
+ * @param size - Target batch size
35
+ * @param tinyFraction - Fraction of batch size (0-1) below which final batch is merged
36
+ * @returns Array of batches with no tiny trailing batch
37
+ */
38
+ export declare function createBatches<T>(items: T[], size: number, tinyFraction: number): T[][];
39
+ import type { LoggerLike } from "./types";
40
+ /**
41
+ * Split items into batches, process each batch with an async function, handle errors, and flatten results.
42
+ *
43
+ * Batches are processed in parallel using Promise.all. If a batch fails, the error
44
+ * handler (or fallback to original items) is used for that batch, while successful
45
+ * batches proceed normally.
46
+ *
47
+ * @param items - Array of items to process in batches
48
+ * @param size - Target batch size
49
+ * @param tinyFraction - Fraction of batch size below which final batch is merged
50
+ * @param fn - Async function to process each batch
51
+ * @param logger - Optional logger for warnings
52
+ * @param onError - Optional error handler returning fallback results for failed batch
53
+ * @returns Flattened array of all batch results
54
+ */
55
+ export declare function batchProcess<I, O = I>(items: I[], size: number, tinyFraction: number, fn: (batch: I[]) => Promise<O[]>, logger?: LoggerLike, onError?: (batch: I[], error: unknown) => O[]): Promise<O[]>;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Utilities for creating and maintaining batches of items.
3
+ */
4
+ /**
5
+ * Slice a list into fixed-size batches.
6
+ *
7
+ * Divides the input array into chunks of the specified size. The final batch
8
+ * may be smaller than the requested size if items don't divide evenly.
9
+ *
10
+ * @param items - Array of items to batch
11
+ * @param size - Maximum number of items per batch
12
+ * @returns Array of batches, each containing up to `size` items
13
+ */
14
+ export function sliceIntoFixedBatches(items, size) {
15
+ const out = [];
16
+ for (let i = 0; i < items.length; i += size)
17
+ out.push(items.slice(i, i + size));
18
+ return out;
19
+ }
20
+ /**
21
+ * Merge the final batch into the previous when it's tiny (but non-empty).
22
+ *
23
+ * Small trailing batches are inefficient for LLM calls due to fixed overhead.
24
+ * This function merges the last batch into the second-to-last when the final
25
+ * batch is smaller than the threshold.
26
+ *
27
+ * @param batches - Array of batches to potentially merge
28
+ * @param tinyThreshold - Maximum size for a batch to be considered "tiny"
29
+ * @returns Modified array with tiny final batch merged, or original if no merge needed
30
+ */
31
+ export function mergeTinyFinalBatch(batches, tinyThreshold) {
32
+ if (batches.length < 2)
33
+ return batches;
34
+ const last = batches[batches.length - 1];
35
+ if (last.length === 0 || last.length > tinyThreshold)
36
+ return batches;
37
+ const prev = batches[batches.length - 2];
38
+ batches[batches.length - 2] = prev.concat(last);
39
+ batches.pop();
40
+ return batches;
41
+ }
42
+ /**
43
+ * Create batches then merge a tiny trailing batch.
44
+ *
45
+ * Combines sliceIntoFixedBatches and mergeTinyFinalBatch to efficiently
46
+ * partition items while avoiding wasteful small final batches.
47
+ *
48
+ * @param items - Array of items to batch
49
+ * @param size - Target batch size
50
+ * @param tinyFraction - Fraction of batch size (0-1) below which final batch is merged
51
+ * @returns Array of batches with no tiny trailing batch
52
+ */
53
+ export function createBatches(items, size, tinyFraction) {
54
+ const batches = sliceIntoFixedBatches(items, size);
55
+ const tinyThreshold = Math.ceil(tinyFraction * size);
56
+ return mergeTinyFinalBatch(batches, tinyThreshold);
57
+ }
58
+ /**
59
+ * Split items into batches, process each batch with an async function, handle errors, and flatten results.
60
+ *
61
+ * Batches are processed in parallel using Promise.all. If a batch fails, the error
62
+ * handler (or fallback to original items) is used for that batch, while successful
63
+ * batches proceed normally.
64
+ *
65
+ * @param items - Array of items to process in batches
66
+ * @param size - Target batch size
67
+ * @param tinyFraction - Fraction of batch size below which final batch is merged
68
+ * @param fn - Async function to process each batch
69
+ * @param logger - Optional logger for warnings
70
+ * @param onError - Optional error handler returning fallback results for failed batch
71
+ * @returns Flattened array of all batch results
72
+ */
73
+ export async function batchProcess(items, size, tinyFraction, fn, logger, onError) {
74
+ const batches = createBatches(items, size, tinyFraction);
75
+ const results = await Promise.all(batches.map(async (b) => {
76
+ try {
77
+ return await fn(b);
78
+ }
79
+ catch (error) {
80
+ logger?.warn?.("intent reranker batch failed, preserving original order", {
81
+ error: error?.message,
82
+ });
83
+ if (onError) {
84
+ return onError(b, error);
85
+ }
86
+ return b;
87
+ }
88
+ }));
89
+ return results.flat();
90
+ }
91
+ //# sourceMappingURL=batches.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batches.js","sourceRoot":"","sources":["../src/batches.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAI,KAAU,EAAE,IAAY;IAC/D,MAAM,GAAG,GAAU,EAAE,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI;QAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAChF,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAI,OAAc,EAAE,aAAqB;IAC1E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,aAAa;QAAE,OAAO,OAAO,CAAC;IACrE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;IAC1C,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAI,KAAU,EAAE,IAAY,EAAE,YAAoB;IAC7E,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACrD,OAAO,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AACrD,CAAC;AAID;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAU,EACV,IAAY,EACZ,YAAoB,EACpB,EAAgC,EAChC,MAAmB,EACnB,OAA6C;IAE7C,MAAM,OAAO,GAAG,aAAa,CAAI,KAAK,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,EAAE,IAAI,EAAE,CAAC,yDAAyD,EAAE;gBACxE,KAAK,EAAG,KAAe,EAAE,OAAO;aACjC,CAAC,CAAC;YAEH,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YAC3B,CAAC;YAED,OAAO,CAAmB,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IACF,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import "dotenv/config";
2
+ /**
3
+ * Exported config object (no function call required) following API patterns.
4
+ */
5
+ export declare const CONFIG: {
6
+ readonly GROQ: {
7
+ readonly API_KEY: string;
8
+ readonly DEFAULT_MODEL: string;
9
+ readonly DEFAULT_REASONING_EFFORT: "low" | "medium" | "high";
10
+ readonly JSON_REPAIR_ATTEMPTS: number;
11
+ };
12
+ readonly INTENT: {
13
+ readonly PROVIDER: "GROQ";
14
+ readonly TIMEOUT_MS: number;
15
+ readonly MIN_SCORE: number;
16
+ readonly MAX_SCORE: number;
17
+ readonly RELEVANCY_THRESHOLD: number;
18
+ readonly BATCH_SIZE: number;
19
+ readonly TINY_BATCH_FRACTION: number;
20
+ };
21
+ readonly TEST: {
22
+ readonly SCOPE: string;
23
+ };
24
+ };
package/dist/config.js ADDED
@@ -0,0 +1,29 @@
1
+ import "dotenv/config";
2
+ import { enumeration, int, number, string } from "./lib/config";
3
+ /**
4
+ * Exported config object (no function call required) following API patterns.
5
+ */
6
+ export const CONFIG = {
7
+ GROQ: {
8
+ API_KEY: string("GROQ_API_KEY", { default: "" }),
9
+ DEFAULT_MODEL: string("GROQ_DEFAULT_MODEL", { default: "openai/gpt-oss-20b" }),
10
+ DEFAULT_REASONING_EFFORT: enumeration("GROQ_DEFAULT_REASONING_EFFORT", {
11
+ default: "medium",
12
+ values: ["low", "medium", "high"],
13
+ }),
14
+ JSON_REPAIR_ATTEMPTS: int("GROQ_JSON_REPAIR_ATTEMPTS", { default: 3, min: 0 }),
15
+ },
16
+ INTENT: {
17
+ PROVIDER: enumeration("INTENT_PROVIDER", { default: "GROQ", values: ["GROQ"] }),
18
+ TIMEOUT_MS: int("INTENT_TIMEOUT_MS", { default: 3000, min: 1 }),
19
+ MIN_SCORE: int("INTENT_MIN_SCORE", { default: 0 }),
20
+ MAX_SCORE: int("INTENT_MAX_SCORE", { default: 10, min: 1 }),
21
+ RELEVANCY_THRESHOLD: int("INTENT_RELEVANCY_THRESHOLD", { default: 0 }),
22
+ BATCH_SIZE: int("INTENT_BATCH_SIZE", { default: 20, min: 1 }),
23
+ TINY_BATCH_FRACTION: number("INTENT_TINY_BATCH_FRACTION", { default: 0.2, min: 0, max: 1 }),
24
+ },
25
+ TEST: {
26
+ SCOPE: string("TEST_SCOPE", { default: "all" }),
27
+ },
28
+ };
29
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEhE;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAChD,aAAa,EAAE,MAAM,CAAC,oBAAoB,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;QAC9E,wBAAwB,EAAE,WAAW,CAAC,+BAA+B,EAAE;YACrE,OAAO,EAAE,QAAQ;YACjB,MAAM,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAU;SAC3C,CAAC;QACF,oBAAoB,EAAE,GAAG,CAAC,2BAA2B,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KAC/E;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,WAAW,CAAC,iBAAiB,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,MAAM,CAAU,EAAE,CAAC;QACxF,UAAU,EAAE,GAAG,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QAC/D,SAAS,EAAE,GAAG,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAClD,SAAS,EAAE,GAAG,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QAC3D,mBAAmB,EAAE,GAAG,CAAC,4BAA4B,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACtE,UAAU,EAAE,GAAG,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QAC7D,mBAAmB,EAAE,MAAM,CAAC,4BAA4B,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KAC5F;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;KAChD;CACO,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Default extractor functions for Intent.
3
+ *
4
+ * These provide sensible defaults when users don't specify custom key/summary extractors.
5
+ */
6
+ /**
7
+ * Converts any value to a pretty-printed JSON string with error handling.
8
+ *
9
+ * This is the canonical helper for all JSON stringification in the codebase.
10
+ * Pretty-prints with 2-space indentation for better LLM readability.
11
+ * Falls back to String(value) if JSON.stringify fails (e.g., circular references).
12
+ * Handles undefined explicitly since JSON.stringify(undefined) returns undefined.
13
+ *
14
+ * @param value - The value to stringify
15
+ * @returns Pretty-printed JSON string, or String() fallback
16
+ * @throws Never throws - gracefully falls back to String() on any error
17
+ */
18
+ export declare function jsonStringify(value: unknown): string;
19
+ /**
20
+ * Converts any value to a hash-based string key.
21
+ *
22
+ * Serializes the value to JSON, computes a 32-bit hash, and returns it as a string.
23
+ * This provides unique-enough keys for items without requiring explicit key extractors.
24
+ *
25
+ * @param value - The value to convert to a key
26
+ * @returns A string representation of the hash (e.g., "2847561")
27
+ * @throws Never throws - uses jsonStringify which handles all errors internally
28
+ */
29
+ export declare function hashToString<T>(value: T): string;
30
+ /**
31
+ * Default key extractor: generates hash-based string keys from items.
32
+ *
33
+ * Generic function that works with any item type T. The generic parameter
34
+ * enables type-safe usage without type casts when assigned to typed extractors.
35
+ *
36
+ * @param item - The item to extract a key from
37
+ * @returns A hash-based string key
38
+ * @throws Never throws - uses hashToString which handles all errors internally
39
+ */
40
+ export declare function DEFAULT_KEY_EXTRACTOR<T>(item: T): string;
41
+ /**
42
+ * Default summary extractor: converts items to pretty-printed JSON strings.
43
+ *
44
+ * Uses 2-space indentation for better LLM readability.
45
+ * Generic function that works with any item type T. The generic parameter
46
+ * enables type-safe usage without type casts when assigned to typed extractors.
47
+ *
48
+ * @param item - The item to extract a summary from
49
+ * @returns A pretty-printed JSON string representation of the item
50
+ * @throws Never throws - uses jsonStringify which handles all errors internally
51
+ */
52
+ export declare function DEFAULT_SUMMARY_EXTRACTOR<T>(item: T): string;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Default extractor functions for Intent.
3
+ *
4
+ * These provide sensible defaults when users don't specify custom key/summary extractors.
5
+ */
6
+ /**
7
+ * Computes a simple 32-bit hash from a string using the djb2 algorithm.
8
+ *
9
+ * @param str - The string to hash
10
+ * @returns A 32-bit hash value
11
+ * @private
12
+ */
13
+ function hash32(str) {
14
+ let hash = 5381;
15
+ for (let i = 0; i < str.length; i++) {
16
+ const char = str.charCodeAt(i);
17
+ hash = ((hash << 5) + hash + char) | 0; // hash * 33 + char, keep in 32-bit range
18
+ }
19
+ return hash >>> 0; // Convert to unsigned 32-bit integer
20
+ }
21
+ /**
22
+ * Converts any value to a pretty-printed JSON string with error handling.
23
+ *
24
+ * This is the canonical helper for all JSON stringification in the codebase.
25
+ * Pretty-prints with 2-space indentation for better LLM readability.
26
+ * Falls back to String(value) if JSON.stringify fails (e.g., circular references).
27
+ * Handles undefined explicitly since JSON.stringify(undefined) returns undefined.
28
+ *
29
+ * @param value - The value to stringify
30
+ * @returns Pretty-printed JSON string, or String() fallback
31
+ * @throws Never throws - gracefully falls back to String() on any error
32
+ */
33
+ export function jsonStringify(value) {
34
+ try {
35
+ const result = JSON.stringify(value, null, 2);
36
+ // JSON.stringify returns undefined for undefined values
37
+ if (result === undefined) {
38
+ return String(value);
39
+ }
40
+ return result;
41
+ }
42
+ catch {
43
+ return String(value);
44
+ }
45
+ }
46
+ /**
47
+ * Converts any value to a hash-based string key.
48
+ *
49
+ * Serializes the value to JSON, computes a 32-bit hash, and returns it as a string.
50
+ * This provides unique-enough keys for items without requiring explicit key extractors.
51
+ *
52
+ * @param value - The value to convert to a key
53
+ * @returns A string representation of the hash (e.g., "2847561")
54
+ * @throws Never throws - uses jsonStringify which handles all errors internally
55
+ */
56
+ export function hashToString(value) {
57
+ const json = jsonStringify(value);
58
+ const hashValue = hash32(json);
59
+ return String(hashValue);
60
+ }
61
+ /**
62
+ * Default key extractor: generates hash-based string keys from items.
63
+ *
64
+ * Generic function that works with any item type T. The generic parameter
65
+ * enables type-safe usage without type casts when assigned to typed extractors.
66
+ *
67
+ * @param item - The item to extract a key from
68
+ * @returns A hash-based string key
69
+ * @throws Never throws - uses hashToString which handles all errors internally
70
+ */
71
+ export function DEFAULT_KEY_EXTRACTOR(item) {
72
+ return hashToString(item);
73
+ }
74
+ /**
75
+ * Default summary extractor: converts items to pretty-printed JSON strings.
76
+ *
77
+ * Uses 2-space indentation for better LLM readability.
78
+ * Generic function that works with any item type T. The generic parameter
79
+ * enables type-safe usage without type casts when assigned to typed extractors.
80
+ *
81
+ * @param item - The item to extract a summary from
82
+ * @returns A pretty-printed JSON string representation of the item
83
+ * @throws Never throws - uses jsonStringify which handles all errors internally
84
+ */
85
+ export function DEFAULT_SUMMARY_EXTRACTOR(item) {
86
+ return jsonStringify(item);
87
+ }
88
+ //# sourceMappingURL=extractors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractors.js","sourceRoot":"","sources":["../src/extractors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;GAMG;AACH,SAAS,MAAM,CAAC,GAAW;IACzB,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,yCAAyC;IACnF,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,qCAAqC;AAC1D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC9C,wDAAwD;QACxD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAI,KAAQ;IACtC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAI,IAAO;IAC9C,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CAAI,IAAO;IAClD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { Intent } from "./intent";
2
+ export type { ChatMessage, LlmClient, LlmCallConfig, LoggerLike, IntentCandidate, IntentExtractors, IntentOptions, IntentConfig, IntentContext, } from "./types";
3
+ export { CONFIG } from "./config";
4
+ export { createDefaultGroqClient } from "./providers/groq";
5
+ export { DEFAULT_KEY_EXTRACTOR, DEFAULT_SUMMARY_EXTRACTOR } from "./extractors";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { Intent } from "./intent";
2
+ export { CONFIG } from "./config";
3
+ export { createDefaultGroqClient } from "./providers/groq";
4
+ export { DEFAULT_KEY_EXTRACTOR, DEFAULT_SUMMARY_EXTRACTOR } from "./extractors";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAYlC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC"}