@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/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[]>;
|
package/dist/batches.js
ADDED
|
@@ -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"}
|
package/dist/config.d.ts
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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"}
|