@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.
@@ -0,0 +1,335 @@
1
+ import Groq from "groq-sdk";
2
+ import { CONFIG } from "../config";
3
+ /**
4
+ * Return a best-effort nested error record from groq-sdk.
5
+ *
6
+ * @param err - Any thrown value
7
+ * @returns Nested `error` object when present
8
+ * @private
9
+ */
10
+ function getNestedErrorObject(err) {
11
+ if (err == null || typeof err !== "object") {
12
+ return undefined;
13
+ }
14
+ const record = err;
15
+ const error = record.error;
16
+ if (error == null || typeof error !== "object") {
17
+ return undefined;
18
+ }
19
+ return error;
20
+ }
21
+ /**
22
+ * Map internal ChatMessage to groq-sdk ChatCompletionMessageParam.
23
+ *
24
+ * Converts our generic message format to Groq's expected format.
25
+ * Only supports system, user, and assistant roles. Throws on unsupported
26
+ * roles (like "tool") to ensure predictable provider behavior.
27
+ *
28
+ * @param messages - Array of generic chat messages
29
+ * @returns Array of Groq-formatted messages
30
+ * @throws {Error} If message contains unsupported role
31
+ * @private
32
+ */
33
+ function mapToGroqMessages(messages) {
34
+ return messages.map((m) => {
35
+ if (m.role === "system" || m.role === "user" || m.role === "assistant") {
36
+ return { role: m.role, content: m.content };
37
+ }
38
+ throw new Error(`intent: '${m.role}' role messages are not supported in provider calls`);
39
+ });
40
+ }
41
+ /**
42
+ * Build the request payload expected by groq-sdk with strict JSON schema.
43
+ *
44
+ * Constructs the complete request object including model, reasoning effort, messages,
45
+ * optional user ID, and the response_format configuration that enforces strict
46
+ * JSON schema validation on the model's output.
47
+ *
48
+ * @param outputSchema - JSON schema defining expected response structure
49
+ * @param groqMessages - Formatted chat messages
50
+ * @param config - Optional config overriding model and reasoning effort
51
+ * @param userId - Optional user identifier for Groq's abuse monitoring
52
+ * @returns Request payload ready for groq-sdk
53
+ * @private
54
+ */
55
+ function buildGroqRequest(outputSchema, groqMessages, config, userId, defaults) {
56
+ return {
57
+ model: config?.model ?? defaults.model,
58
+ reasoning_effort: config?.reasoningEffort ?? defaults.reasoningEffort,
59
+ messages: groqMessages,
60
+ ...(userId ? { user: userId } : {}),
61
+ response_format: {
62
+ type: "json_schema",
63
+ json_schema: {
64
+ name: "intent_relevancy",
65
+ schema: outputSchema,
66
+ strict: true,
67
+ },
68
+ },
69
+ };
70
+ }
71
+ /**
72
+ * Execute the chat completion with an optional timeout.
73
+ *
74
+ * Wraps the Groq SDK's chat.completions.create call with optional timeout support.
75
+ *
76
+ * @param client - Initialized Groq SDK client
77
+ * @param request - Complete request payload
78
+ * @param timeoutMs - Optional timeout in milliseconds
79
+ * @returns Raw completion response from Groq
80
+ * @private
81
+ */
82
+ async function executeCompletion(client, request, timeoutMs) {
83
+ const timeout = typeof timeoutMs === "number" ? { timeout: timeoutMs } : undefined;
84
+ return client.chat.completions.create(request, timeout);
85
+ }
86
+ /**
87
+ * Extract and validate the content string from a Groq response.
88
+ *
89
+ * Safely navigates the response structure to find the message content.
90
+ * Throws if content is missing or not a string.
91
+ *
92
+ * @param response - Raw completion response from Groq SDK
93
+ * @returns The message content as a string
94
+ * @throws {Error} If content is missing or invalid
95
+ * @private
96
+ */
97
+ function getResponseContent(response) {
98
+ const content = response?.choices?.[0]?.message?.content;
99
+ if (typeof content !== "string") {
100
+ throw new Error("Groq did not return content");
101
+ }
102
+ return content;
103
+ }
104
+ /**
105
+ * Parse the model's JSON content, throwing a clear error on failure.
106
+ *
107
+ * Attempts to parse the string content as JSON and wraps it in a { data } object
108
+ * to match the expected LlmClient return type.
109
+ *
110
+ * @param content - JSON string from model response
111
+ * @returns Wrapped parsed data
112
+ * @throws {Error} If content is not valid JSON
113
+ * @private
114
+ */
115
+ function parseJson(content) {
116
+ try {
117
+ const data = JSON.parse(content);
118
+ return { data };
119
+ }
120
+ catch (error) {
121
+ const detail = error instanceof Error ? error.message : String(error);
122
+ const err = new Error(`Groq returned invalid JSON: ${detail}`);
123
+ err.rawOutput = content;
124
+ throw err;
125
+ }
126
+ }
127
+ /**
128
+ * Build a repair conversation turn to help the model correct invalid JSON.
129
+ *
130
+ * We include the raw output and the error message, then remind the model to
131
+ * return only valid JSON that matches the already-provided schema.
132
+ *
133
+ * @param baseMessages - Original Groq-formatted messages
134
+ * @param rawOutput - Raw model output that failed parsing
135
+ * @param errorMessage - Parse/validation error message
136
+ * @returns New messages array including a repair request
137
+ * @private
138
+ */
139
+ function buildJsonRepairMessages(baseMessages, rawOutput, errorMessage) {
140
+ return [
141
+ ...baseMessages,
142
+ { role: "assistant", content: rawOutput },
143
+ {
144
+ role: "user",
145
+ content: "Your previous response was invalid JSON or did not match the required JSON schema. " +
146
+ "Please correct it and return ONLY valid JSON that matches the schema.\n\n" +
147
+ `Error: ${errorMessage}`,
148
+ },
149
+ ];
150
+ }
151
+ /**
152
+ * Build a repair request object if we still have attempts remaining.
153
+ *
154
+ * @param remaining - Attempts remaining for the overall call
155
+ * @param request - The current Groq request
156
+ * @param repair - Repair context
157
+ * @returns The repaired request and decremented remaining attempts, or undefined
158
+ * @private
159
+ */
160
+ function buildRepairRetry(state, repair) {
161
+ if (state.remaining <= 1) {
162
+ return undefined;
163
+ }
164
+ const repairedRequest = {
165
+ ...state.request,
166
+ messages: buildJsonRepairMessages(state.request.messages, repair.rawOutput, repair.errorMessage),
167
+ };
168
+ return { remaining: state.remaining - 1, request: repairedRequest };
169
+ }
170
+ /**
171
+ * Convert a JSON parse error into a repair input.
172
+ *
173
+ * @param rawOutput - Raw model output
174
+ * @param parseError - JSON.parse error
175
+ * @returns Repair input
176
+ * @private
177
+ */
178
+ function parseErrorToRepairInput(rawOutput, parseError) {
179
+ return { rawOutput, errorMessage: String(parseError) };
180
+ }
181
+ /**
182
+ * Extract raw output from a JSON parse error thrown by parseJson.
183
+ *
184
+ * @param error - Error thrown by parseJson
185
+ * @returns Raw output when present
186
+ * @private
187
+ */
188
+ function getRawOutputFromParseJsonError(error) {
189
+ if (!(error instanceof Error)) {
190
+ return undefined;
191
+ }
192
+ const record = error;
193
+ return typeof record.rawOutput === "string" ? record.rawOutput : undefined;
194
+ }
195
+ /**
196
+ * Extract repair inputs from a Groq schema-validation failure.
197
+ *
198
+ * Groq can return a rich error payload for `json_validate_failed`, sometimes
199
+ * including the model's `generated_response`. We use this to ask the model to
200
+ * correct its output without re-sending the schema.
201
+ *
202
+ * @param err - Unknown thrown error from groq-sdk
203
+ * @returns Repair inputs when present; otherwise undefined
204
+ * @private
205
+ */
206
+ function extractJsonValidateFailedRepairInput(err) {
207
+ if (err instanceof Error) {
208
+ const match = err.message.match(/"failed_generation":"(\{.*?\})"/);
209
+ const failedGeneration = match?.[1] ? match[1].replace(/\\"/g, '"') : undefined;
210
+ if (err.message.includes('"code":"json_validate_failed"')) {
211
+ return {
212
+ rawOutput: failedGeneration ?? "(Groq did not include the rejected generation in the error payload)",
213
+ errorMessage: err.message,
214
+ };
215
+ }
216
+ }
217
+ const errorObj = getNestedErrorObject(err);
218
+ const nested = errorObj && typeof errorObj.error === "object"
219
+ ? errorObj.error
220
+ : undefined;
221
+ const serverError = nested ?? errorObj;
222
+ if (!serverError) {
223
+ return undefined;
224
+ }
225
+ const code = typeof serverError.code === "string" ? serverError.code : undefined;
226
+ if (code !== "json_validate_failed") {
227
+ return undefined;
228
+ }
229
+ const message = typeof serverError.message === "string" ? serverError.message : undefined;
230
+ const generatedResponse = typeof serverError.generated_response === "string" ? serverError.generated_response : undefined;
231
+ const failedGeneration = typeof serverError.failed_generation === "string" ? serverError.failed_generation : undefined;
232
+ const rawOutput = generatedResponse ?? failedGeneration;
233
+ return {
234
+ rawOutput: rawOutput ?? "(Groq did not include the rejected generation in the error payload)",
235
+ errorMessage: message ?? String(err),
236
+ };
237
+ }
238
+ /**
239
+ * Create a GroqSdkLike wrapper around groq-sdk.
240
+ *
241
+ * This keeps our provider surface strongly typed while isolating groq-sdk's
242
+ * broader request/response types to a single boundary.
243
+ *
244
+ * @param apiKey - Groq API key
245
+ * @returns GroqSdkLike wrapper
246
+ * @private
247
+ */
248
+ export function createGroqSdkLike(apiKey) {
249
+ const sdk = new Groq({ apiKey });
250
+ return {
251
+ chat: {
252
+ completions: {
253
+ create: async (req, opts) => {
254
+ // groq-sdk's types lag behind json_schema support; keep the boundary localized.
255
+ return (await sdk.chat.completions.create(req, opts));
256
+ },
257
+ },
258
+ },
259
+ };
260
+ }
261
+ /**
262
+ * Create the underlying Groq SDK client.
263
+ *
264
+ * This wrapper exists to make the default SDK construction path unit-testable.
265
+ *
266
+ * @param options - Groq SDK constructor options
267
+ * @returns Groq SDK client
268
+ * @private
269
+ */
270
+ export function createGroqSdk(options) {
271
+ return new Groq(options);
272
+ }
273
+ export function createDefaultGroqClient(apiKey, options) {
274
+ const defaults = {
275
+ model: options?.defaults?.model ?? CONFIG.GROQ.DEFAULT_MODEL,
276
+ reasoningEffort: options?.defaults?.reasoningEffort ?? CONFIG.GROQ.DEFAULT_REASONING_EFFORT,
277
+ };
278
+ const makeSdk = options?.makeSdk ?? createGroqSdkLike;
279
+ const jsonRepairAttempts = options?.jsonRepairAttempts ?? CONFIG.GROQ.JSON_REPAIR_ATTEMPTS;
280
+ return {
281
+ /**
282
+ * Call Groq with JSON schema enforced response and return parsed data.
283
+ *
284
+ * Implements the LlmClient interface with Groq-specific features:
285
+ * - Creates a new SDK client per call with the provided API key
286
+ * - Maps generic messages to Groq format
287
+ * - Builds request with strict JSON schema response format
288
+ * - Retries up to 3 times on json_validate_failed errors
289
+ * - Parses and returns the structured response
290
+ *
291
+ * @param messages - Chat messages to send to the model
292
+ * @param outputSchema - JSON schema defining expected response structure
293
+ * @param config - Optional model, reasoning effort, and timeout overrides
294
+ * @param userId - Optional user ID for Groq's abuse monitoring
295
+ * @returns Parsed response data wrapped in { data } object
296
+ * @throws {Error} If all retry attempts fail or response is invalid
297
+ */
298
+ async call(messages, outputSchema, config, userId) {
299
+ const client = makeSdk(apiKey);
300
+ const groqMessages = mapToGroqMessages(messages);
301
+ const baseRequest = buildGroqRequest(outputSchema, groqMessages, config, userId, defaults);
302
+ const createWithRetry = async (state) => {
303
+ try {
304
+ const response = await executeCompletion(client, state.request, config?.timeoutMs);
305
+ const content = getResponseContent(response);
306
+ const parsed = parseJson(content);
307
+ return parsed;
308
+ }
309
+ catch (err) {
310
+ // If the model returned bad JSON, retry by appending a repair turn.
311
+ const parseRawOutput = getRawOutputFromParseJsonError(err);
312
+ if (parseRawOutput !== undefined) {
313
+ const retry = buildRepairRetry(state, parseErrorToRepairInput(parseRawOutput, err));
314
+ if (retry) {
315
+ return createWithRetry(retry);
316
+ }
317
+ throw err;
318
+ }
319
+ const validationRepairInput = extractJsonValidateFailedRepairInput(err);
320
+ if (validationRepairInput) {
321
+ const retry = buildRepairRetry(state, validationRepairInput);
322
+ if (retry) {
323
+ return createWithRetry(retry);
324
+ }
325
+ }
326
+ // Non-JSON errors are terminal (quota/outage/etc.).
327
+ throw err;
328
+ }
329
+ };
330
+ const attempts = Math.max(1, jsonRepairAttempts);
331
+ return createWithRetry({ remaining: attempts, request: baseRequest });
332
+ },
333
+ };
334
+ }
335
+ //# sourceMappingURL=groq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groq.js","sourceRoot":"","sources":["../../src/providers/groq.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,UAAU,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAgCnC;;;;;;GAMG;AACH,SAAS,oBAAoB,CAAC,GAAY;IACxC,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,MAAM,GAAG,GAA8B,CAAC;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,KAAgC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,iBAAiB,CAAC,QAAuB;IAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACvE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAgC,CAAC;QAC5E,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,qDAAqD,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,gBAAgB,CACvB,YAAwB,EACxB,YAA0C,EAC1C,MAAiC,EACjC,MAA0B,EAC1B,QAAuE;IAEvE,OAAO;QACL,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,QAAQ,CAAC,KAAK;QACtC,gBAAgB,EAAE,MAAM,EAAE,eAAe,IAAI,QAAQ,CAAC,eAAe;QACrE,QAAQ,EAAE,YAAY;QACtB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,eAAe,EAAE;YACf,IAAI,EAAE,aAAa;YACnB,WAAW,EAAE;gBACX,IAAI,EAAE,kBAAkB;gBACxB,MAAM,EAAE,YAAY;gBACpB,MAAM,EAAE,IAAI;aACb;SACF;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,iBAAiB,CAC9B,MAAmB,EACnB,OAAkC,EAClC,SAAkB;IAElB,MAAM,OAAO,GACX,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,OAAO,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,kBAAkB,CAAC,QAAoC;IAC9D,MAAM,OAAO,GAA8B,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;IACpF,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,SAAS,CAAI,OAAe;IACnC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,EAAE,IAAI,EAAiB,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;QAC9D,GAAsC,CAAC,SAAS,GAAG,OAAO,CAAC;QAC5D,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAYD;;;;;;;;;;;GAWG;AACH,SAAS,uBAAuB,CAC9B,YAA0C,EAC1C,SAAiB,EACjB,YAAoB;IAEpB,OAAO;QACL,GAAG,YAAY;QACf,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAgC;QACvE;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EACL,qFAAqF;gBACrF,2EAA2E;gBAC3E,UAAU,YAAY,EAAE;SACG;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,gBAAgB,CAAC,KAAiB,EAAE,MAAuB;IAClE,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,eAAe,GAA8B;QACjD,GAAG,KAAK,CAAC,OAAO;QAChB,QAAQ,EAAE,uBAAuB,CAC/B,KAAK,CAAC,OAAO,CAAC,QAAQ,EACtB,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,YAAY,CACpB;KACF,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC;AACtE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,uBAAuB,CAAC,SAAiB,EAAE,UAAmB;IACrE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;AACzD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,8BAA8B,CAAC,KAAc;IACpD,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,MAAM,GAAG,KAAwC,CAAC;IACxD,OAAO,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,oCAAoC,CAAC,GAAY;IACxD,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACnE,MAAM,gBAAgB,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAChF,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,+BAA+B,CAAC,EAAE,CAAC;YAC1D,OAAO;gBACL,SAAS,EACP,gBAAgB,IAAI,qEAAqE;gBAC3F,YAAY,EAAE,GAAG,CAAC,OAAO;aAC1B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,MAAM,GACV,QAAQ,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ;QAC5C,CAAC,CAAE,QAAQ,CAAC,KAAiC;QAC7C,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,WAAW,GAAG,MAAM,IAAI,QAAQ,CAAC;IACvC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,WAAW,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,IAAI,IAAI,KAAK,sBAAsB,EAAE,CAAC;QACpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1F,MAAM,iBAAiB,GACrB,OAAO,WAAW,CAAC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC;IAClG,MAAM,gBAAgB,GACpB,OAAO,WAAW,CAAC,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;IAChG,MAAM,SAAS,GAAG,iBAAiB,IAAI,gBAAgB,CAAC;IAExD,OAAO;QACL,SAAS,EAAE,SAAS,IAAI,qEAAqE;QAC7F,YAAY,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC;KACrC,CAAC;AACJ,CAAC;AAiCD;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,CAAQ,CAAC;IACxC,OAAO;QACL,IAAI,EAAE;YACJ,WAAW,EAAE;gBACX,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;oBAC1B,gFAAgF;oBAChF,OAAO,CAAC,MAAO,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,MAAc,CAChD,GAAG,EACH,IAAI,CACL,CAA+B,CAAC;gBACnC,CAAC;aACF;SACF;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,OAA2B;IACvD,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,MAAc,EACd,OAIC;IAED,MAAM,QAAQ,GAAG;QACf,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa;QAC5D,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,IAAI,MAAM,CAAC,IAAI,CAAC,wBAAwB;KACnF,CAAC;IACX,MAAM,OAAO,GAAsB,OAAO,EAAE,OAAO,IAAI,iBAAiB,CAAC;IACzE,MAAM,kBAAkB,GAAG,OAAO,EAAE,kBAAkB,IAAI,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC;IAC3F,OAAO;QACL;;;;;;;;;;;;;;;;WAgBG;QACH,KAAK,CAAC,IAAI,CACR,QAAuB,EACvB,YAAwB,EACxB,MAAsB,EACtB,MAAe;YAEf,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAC/B,MAAM,YAAY,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YACjD,MAAM,WAAW,GAAG,gBAAgB,CAAC,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;YAE3F,MAAM,eAAe,GAAG,KAAK,EAAE,KAAiB,EAAwB,EAAE;gBACxE,IAAI,CAAC;oBACH,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;oBACnF,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;oBAE7C,MAAM,MAAM,GAAG,SAAS,CAAI,OAAO,CAAC,CAAC;oBACrC,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,oEAAoE;oBACpE,MAAM,cAAc,GAAG,8BAA8B,CAAC,GAAG,CAAC,CAAC;oBAC3D,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;wBACjC,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,uBAAuB,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC;wBACpF,IAAI,KAAK,EAAE,CAAC;4BACV,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;wBAChC,CAAC;wBACD,MAAM,GAAG,CAAC;oBACZ,CAAC;oBAED,MAAM,qBAAqB,GAAG,oCAAoC,CAAC,GAAG,CAAC,CAAC;oBACxE,IAAI,qBAAqB,EAAE,CAAC;wBAC1B,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;wBAC7D,IAAI,KAAK,EAAE,CAAC;4BACV,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;wBAChC,CAAC;oBACH,CAAC;oBAED,oDAAoD;oBACpD,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;YACjD,OAAO,eAAe,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QACxE,CAAC;KACkB,CAAC;AACxB,CAAC"}
@@ -0,0 +1,82 @@
1
+ import type { JSONObject } from "./types";
2
+ type IntegerSchema = {
3
+ type: "integer";
4
+ };
5
+ type StringSchema = {
6
+ type: "string";
7
+ };
8
+ type BooleanSchema = {
9
+ type: "boolean";
10
+ };
11
+ type CandidateEvaluationSchema = {
12
+ type: "object";
13
+ properties: {
14
+ explanation: StringSchema;
15
+ score: IntegerSchema;
16
+ };
17
+ required: ["explanation", "score"];
18
+ additionalProperties: false;
19
+ };
20
+ type CandidateFilterSchema = {
21
+ type: "object";
22
+ properties: {
23
+ explanation: StringSchema;
24
+ isRelevant: BooleanSchema;
25
+ };
26
+ required: ["explanation", "isRelevant"];
27
+ additionalProperties: false;
28
+ };
29
+ /**
30
+ * Build the schema used for a single candidate's evaluation.
31
+ *
32
+ * Property order is intentional: `explanation` is defined before `score` to
33
+ * encourage structured-output models to generate explanations first.
34
+ *
35
+ * @returns JSON schema for a single candidate evaluation
36
+ */
37
+ export declare function buildCandidateEvaluationSchema(): CandidateEvaluationSchema;
38
+ /**
39
+ * Build the schema used for a single candidate's filter decision.
40
+ *
41
+ * Property order is intentional: `explanation` is defined before `isRelevant`.
42
+ *
43
+ * @returns JSON schema for a single candidate filter decision
44
+ */
45
+ export declare function buildCandidateFilterSchema(): CandidateFilterSchema;
46
+ /**
47
+ * Build a strict JSON schema mapping candidate keys to evaluation objects.
48
+ *
49
+ * Each candidate key maps to an object containing:
50
+ * - explanation: a short justification of the score
51
+ * - score: an integer within the configured range
52
+ *
53
+ * Property order is intentional (`explanation` then `score`) to encourage the
54
+ * model to generate explanations before scores in structured-output modes.
55
+ *
56
+ * @param keys - Array of unique candidate keys
57
+ * @returns JSON schema object enforcing exact structure of response
58
+ */
59
+ export declare function buildRelevancySchema(keys: string[], minScore: number, maxScore: number): JSONObject;
60
+ /**
61
+ * Build a strict JSON schema mapping candidate keys to boolean relevancy decisions.
62
+ *
63
+ * Each candidate key maps to an object containing:
64
+ * - explanation: a short justification
65
+ * - isRelevant: boolean
66
+ *
67
+ * @param keys - Array of unique candidate keys
68
+ * @returns JSON schema object enforcing exact structure of response
69
+ */
70
+ export declare function buildFilterSchema(keys: string[]): JSONObject;
71
+ /**
72
+ * Build a strict JSON schema for choosing a single candidate key.
73
+ *
74
+ * The model must return:
75
+ * - explanation: string
76
+ * - selectedKey: one of the provided candidate keys (enum)
77
+ *
78
+ * @param keys - Array of unique candidate keys
79
+ * @returns JSON schema enforcing a single selected key
80
+ */
81
+ export declare function buildChoiceSchema(keys: string[]): JSONObject;
82
+ export {};
package/dist/schema.js ADDED
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Build the schema used for a single candidate's evaluation.
3
+ *
4
+ * Property order is intentional: `explanation` is defined before `score` to
5
+ * encourage structured-output models to generate explanations first.
6
+ *
7
+ * @returns JSON schema for a single candidate evaluation
8
+ */
9
+ export function buildCandidateEvaluationSchema() {
10
+ return {
11
+ type: "object",
12
+ properties: {
13
+ explanation: { type: "string" },
14
+ score: { type: "integer" },
15
+ },
16
+ required: ["explanation", "score"],
17
+ additionalProperties: false,
18
+ };
19
+ }
20
+ /**
21
+ * Build the schema used for a single candidate's filter decision.
22
+ *
23
+ * Property order is intentional: `explanation` is defined before `isRelevant`.
24
+ *
25
+ * @returns JSON schema for a single candidate filter decision
26
+ */
27
+ export function buildCandidateFilterSchema() {
28
+ return {
29
+ type: "object",
30
+ properties: {
31
+ explanation: { type: "string" },
32
+ isRelevant: { type: "boolean" },
33
+ },
34
+ required: ["explanation", "isRelevant"],
35
+ additionalProperties: false,
36
+ };
37
+ }
38
+ /**
39
+ * Build a strict JSON schema mapping candidate keys to evaluation objects.
40
+ *
41
+ * Each candidate key maps to an object containing:
42
+ * - explanation: a short justification of the score
43
+ * - score: an integer within the configured range
44
+ *
45
+ * Property order is intentional (`explanation` then `score`) to encourage the
46
+ * model to generate explanations before scores in structured-output modes.
47
+ *
48
+ * @param keys - Array of unique candidate keys
49
+ * @returns JSON schema object enforcing exact structure of response
50
+ */
51
+ export function buildRelevancySchema(keys, minScore, maxScore) {
52
+ const evaluationSchema = buildCandidateEvaluationSchema();
53
+ const properties = {};
54
+ for (const k of keys) {
55
+ properties[k] = evaluationSchema;
56
+ }
57
+ return {
58
+ title: "Query / Candidate Relevancy Assessment",
59
+ description: `Map candidate results for a search query to relevancy scores (${minScore}-${maxScore}) with explanations.`,
60
+ type: "object",
61
+ properties,
62
+ required: keys,
63
+ additionalProperties: false,
64
+ };
65
+ }
66
+ /**
67
+ * Build a strict JSON schema mapping candidate keys to boolean relevancy decisions.
68
+ *
69
+ * Each candidate key maps to an object containing:
70
+ * - explanation: a short justification
71
+ * - isRelevant: boolean
72
+ *
73
+ * @param keys - Array of unique candidate keys
74
+ * @returns JSON schema object enforcing exact structure of response
75
+ */
76
+ export function buildFilterSchema(keys) {
77
+ const decisionSchema = buildCandidateFilterSchema();
78
+ const properties = {};
79
+ for (const k of keys) {
80
+ properties[k] = decisionSchema;
81
+ }
82
+ return {
83
+ title: "Query / Candidate Relevancy Filter",
84
+ description: "Map candidate results for a search query to boolean relevancy decisions with explanations.",
85
+ type: "object",
86
+ properties,
87
+ required: keys,
88
+ additionalProperties: false,
89
+ };
90
+ }
91
+ /**
92
+ * Build a strict JSON schema for choosing a single candidate key.
93
+ *
94
+ * The model must return:
95
+ * - explanation: string
96
+ * - selectedKey: one of the provided candidate keys (enum)
97
+ *
98
+ * @param keys - Array of unique candidate keys
99
+ * @returns JSON schema enforcing a single selected key
100
+ */
101
+ export function buildChoiceSchema(keys) {
102
+ return {
103
+ title: "Query / Candidate Single Choice",
104
+ description: "Choose exactly one candidate key for the query and explain why.",
105
+ type: "object",
106
+ properties: {
107
+ explanation: { type: "string" },
108
+ selectedKey: { type: "string", enum: keys },
109
+ },
110
+ required: ["explanation", "selectedKey"],
111
+ additionalProperties: false,
112
+ };
113
+ }
114
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAoCA;;;;;;;GAOG;AACH,MAAM,UAAU,8BAA8B;IAC5C,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC/B,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;SAC3B;QACD,QAAQ,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;QAClC,oBAAoB,EAAE,KAAK;KAC5B,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B;IACxC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC/B,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;SAChC;QACD,QAAQ,EAAE,CAAC,aAAa,EAAE,YAAY,CAAC;QACvC,oBAAoB,EAAE,KAAK;KAC5B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAc,EACd,QAAgB,EAChB,QAAgB;IAEhB,MAAM,gBAAgB,GAAG,8BAA8B,EAAE,CAAC;IAE1D,MAAM,UAAU,GAA8C,EAAE,CAAC;IACjE,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,UAAU,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC;IACnC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,wCAAwC;QAC/C,WAAW,EAAE,iEAAiE,QAAQ,IAAI,QAAQ,sBAAsB;QACxH,IAAI,EAAE,QAAQ;QACd,UAAU;QACV,QAAQ,EAAE,IAAI;QACd,oBAAoB,EAAE,KAAK;KACd,CAAC;AAClB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAc;IAC9C,MAAM,cAAc,GAAG,0BAA0B,EAAE,CAAC;IAEpD,MAAM,UAAU,GAA0C,EAAE,CAAC;IAC7D,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,UAAU,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC;IACjC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,oCAAoC;QAC3C,WAAW,EACT,4FAA4F;QAC9F,IAAI,EAAE,QAAQ;QACd,UAAU;QACV,QAAQ,EAAE,IAAI;QACd,oBAAoB,EAAE,KAAK;KACd,CAAC;AAClB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAc;IAC9C,OAAO;QACL,KAAK,EAAE,iCAAiC;QACxC,WAAW,EAAE,iEAAiE;QAC9E,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC/B,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE;SAC5C;QACD,QAAQ,EAAE,CAAC,aAAa,EAAE,aAAa,CAAC;QACxC,oBAAoB,EAAE,KAAK;KACE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,74 @@
1
+ export type JSONPrimitive = boolean | number | string | null;
2
+ export type JSONValue = JSONPrimitive | JSONObject | JSONArray;
3
+ export type JSONObject = {
4
+ [key: string]: JSONValue;
5
+ };
6
+ export type JSONArray = JSONValue[];
7
+ export type ChatRole = "system" | "user" | "assistant" | "tool";
8
+ export type ChatMessage = {
9
+ role: ChatRole;
10
+ content: string;
11
+ };
12
+ export type LlmCallConfig = {
13
+ model?: string;
14
+ reasoningEffort?: "low" | "medium" | "high";
15
+ timeoutMs?: number;
16
+ };
17
+ export interface LlmClient {
18
+ call<T>(messages: ChatMessage[], outputSchema: JSONObject, config?: LlmCallConfig, userId?: string): Promise<{
19
+ data: T;
20
+ }>;
21
+ }
22
+ export interface LoggerLike {
23
+ info?(msg: string, meta?: unknown): void;
24
+ warn?(msg: string, meta?: unknown): void;
25
+ error?(msg: string, meta?: unknown): void;
26
+ }
27
+ export type IntentCandidate = {
28
+ key: string;
29
+ summary: string;
30
+ };
31
+ export type IntentExtractors<T> = {
32
+ key?: (item: T) => string;
33
+ summary?: (item: T) => string;
34
+ };
35
+ export type IntentContext = {
36
+ llm?: LlmClient;
37
+ logger?: LoggerLike;
38
+ userId?: string;
39
+ };
40
+ export type CamelCase<S extends string> = S extends `${infer H}_${infer T}` ? `${Lowercase<H>}${Capitalize<CamelCase<T>>}` : S extends `${infer H}-${infer T}` ? `${Lowercase<H>}${Capitalize<CamelCase<T>>}` : Lowercase<S>;
41
+ export type CamelCasedProps<T> = {
42
+ [K in keyof T as K extends string ? CamelCase<K> : K]: T[K];
43
+ };
44
+ /**
45
+ * Configuration options for Intent.
46
+ *
47
+ * This is a camelCase version of the INTENT config object from config.ts.
48
+ */
49
+ export type IntentConfig = {
50
+ provider?: "GROQ";
51
+ timeoutMs?: number;
52
+ relevancyThreshold?: number;
53
+ batchSize?: number;
54
+ tinyBatchFraction?: number;
55
+ minScore?: number;
56
+ maxScore?: number;
57
+ };
58
+ /**
59
+ * Complete options object for Intent constructor.
60
+ *
61
+ * Merges all configuration into a single, fully optional object:
62
+ * - LLM client and runtime context (llm, logger, userId)
63
+ * - Item extractors (key, summary)
64
+ * - Intent configuration (model, timeoutMs, relevancyThreshold, batchSize, tinyBatchFraction)
65
+ *
66
+ * All fields are optional with sensible defaults:
67
+ * - llm: Auto-detected from GROQ_API_KEY if available
68
+ * - key: Hash-based string from JSON representation
69
+ * - summary: Pretty-printed JSON of the item (2-space indentation for LLM readability)
70
+ * - Config values: From environment variables or built-in defaults
71
+ *
72
+ * @template T - The type of items to rerank
73
+ */
74
+ export type IntentOptions<T> = IntentContext & IntentExtractors<T> & IntentConfig;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}