@with-logic/intent 0.1.0 → 0.1.2
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/README.md +78 -11
- package/dist/index.cjs +1143 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.mjs +1102 -0
- package/dist/index.mjs.map +7 -0
- package/package.json +16 -6
- package/dist/batches.js +0 -91
- package/dist/batches.js.map +0 -1
- package/dist/config.js +0 -29
- package/dist/config.js.map +0 -1
- package/dist/extractors.js +0 -88
- package/dist/extractors.js.map +0 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +0 -1
- package/dist/intent.js +0 -540
- package/dist/intent.js.map +0 -1
- package/dist/lib/config.js +0 -81
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/number.js +0 -15
- package/dist/lib/number.js.map +0 -1
- package/dist/llm_client.js +0 -29
- package/dist/llm_client.js.map +0 -1
- package/dist/messages.js +0 -136
- package/dist/messages.js.map +0 -1
- package/dist/providers/groq.js +0 -335
- package/dist/providers/groq.js.map +0 -1
- package/dist/schema.js +0 -114
- package/dist/schema.js.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/batches.ts", "../src/config.ts", "../src/lib/number.ts", "../src/lib/config.ts", "../src/extractors.ts", "../src/providers/groq.ts", "../src/llm_client.ts", "../src/messages.ts", "../src/schema.ts", "../src/intent.ts"],
|
|
4
|
+
"sourcesContent": ["export { Intent } from \"./intent\";\nexport type {\n ChatMessage,\n LlmClient,\n LlmCallConfig,\n LoggerLike,\n IntentCandidate,\n IntentExtractors,\n IntentOptions,\n IntentConfig,\n IntentContext,\n} from \"./types\";\nexport { CONFIG } from \"./config\";\nexport { createDefaultGroqClient } from \"./providers/groq\";\nexport { DEFAULT_KEY_EXTRACTOR, DEFAULT_SUMMARY_EXTRACTOR } from \"./extractors\";\n", "/**\n * Utilities for creating and maintaining batches of items.\n */\n\n/**\n * Slice a list into fixed-size batches.\n *\n * Divides the input array into chunks of the specified size. The final batch\n * may be smaller than the requested size if items don't divide evenly.\n *\n * @param items - Array of items to batch\n * @param size - Maximum number of items per batch\n * @returns Array of batches, each containing up to `size` items\n */\nexport function sliceIntoFixedBatches<T>(items: T[], size: number): T[][] {\n const out: T[][] = [];\n for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));\n return out;\n}\n\n/**\n * Merge the final batch into the previous when it's tiny (but non-empty).\n *\n * Small trailing batches are inefficient for LLM calls due to fixed overhead.\n * This function merges the last batch into the second-to-last when the final\n * batch is smaller than the threshold.\n *\n * @param batches - Array of batches to potentially merge\n * @param tinyThreshold - Maximum size for a batch to be considered \"tiny\"\n * @returns Modified array with tiny final batch merged, or original if no merge needed\n */\nexport function mergeTinyFinalBatch<T>(batches: T[][], tinyThreshold: number): T[][] {\n if (batches.length < 2) return batches;\n const last = batches[batches.length - 1]!;\n if (last.length === 0 || last.length > tinyThreshold) return batches;\n const prev = batches[batches.length - 2]!;\n batches[batches.length - 2] = prev.concat(last);\n batches.pop();\n return batches;\n}\n\n/**\n * Create batches then merge a tiny trailing batch.\n *\n * Combines sliceIntoFixedBatches and mergeTinyFinalBatch to efficiently\n * partition items while avoiding wasteful small final batches.\n *\n * @param items - Array of items to batch\n * @param size - Target batch size\n * @param tinyFraction - Fraction of batch size (0-1) below which final batch is merged\n * @returns Array of batches with no tiny trailing batch\n */\nexport function createBatches<T>(items: T[], size: number, tinyFraction: number): T[][] {\n const batches = sliceIntoFixedBatches(items, size);\n const tinyThreshold = Math.ceil(tinyFraction * size);\n return mergeTinyFinalBatch(batches, tinyThreshold);\n}\n\nimport type { LoggerLike } from \"./types\";\n\n/**\n * Split items into batches, process each batch with an async function, handle errors, and flatten results.\n *\n * Batches are processed in parallel using Promise.all. If a batch fails, the error\n * handler (or fallback to original items) is used for that batch, while successful\n * batches proceed normally.\n *\n * @param items - Array of items to process in batches\n * @param size - Target batch size\n * @param tinyFraction - Fraction of batch size below which final batch is merged\n * @param fn - Async function to process each batch\n * @param logger - Optional logger for warnings\n * @param onError - Optional error handler returning fallback results for failed batch\n * @returns Flattened array of all batch results\n */\nexport async function batchProcess<I, O = I>(\n items: I[],\n size: number,\n tinyFraction: number,\n fn: (batch: I[]) => Promise<O[]>,\n logger?: LoggerLike,\n onError?: (batch: I[], error: unknown) => O[],\n): Promise<O[]> {\n const batches = createBatches<I>(items, size, tinyFraction);\n const results = await Promise.all(\n batches.map(async (b) => {\n try {\n return await fn(b);\n } catch (error) {\n logger?.warn?.(\"intent reranker batch failed, preserving original order\", {\n error: (error as Error)?.message,\n });\n\n if (onError) {\n return onError(b, error);\n }\n\n return b as unknown as O[];\n }\n }),\n );\n return results.flat();\n}\n", "import \"dotenv/config\";\nimport { enumeration, int, number, string } from \"./lib/config\";\n\n/**\n * Exported config object (no function call required) following API patterns.\n */\nexport const CONFIG = {\n GROQ: {\n API_KEY: string(\"GROQ_API_KEY\", { default: \"\" }),\n DEFAULT_MODEL: string(\"GROQ_DEFAULT_MODEL\", { default: \"openai/gpt-oss-20b\" }),\n DEFAULT_REASONING_EFFORT: enumeration(\"GROQ_DEFAULT_REASONING_EFFORT\", {\n default: \"medium\",\n values: [\"low\", \"medium\", \"high\"] as const,\n }),\n JSON_REPAIR_ATTEMPTS: int(\"GROQ_JSON_REPAIR_ATTEMPTS\", { default: 3, min: 0 }),\n },\n INTENT: {\n PROVIDER: enumeration(\"INTENT_PROVIDER\", { default: \"GROQ\", values: [\"GROQ\"] as const }),\n TIMEOUT_MS: int(\"INTENT_TIMEOUT_MS\", { default: 3000, min: 1 }),\n MIN_SCORE: int(\"INTENT_MIN_SCORE\", { default: 0 }),\n MAX_SCORE: int(\"INTENT_MAX_SCORE\", { default: 10, min: 1 }),\n RELEVANCY_THRESHOLD: int(\"INTENT_RELEVANCY_THRESHOLD\", { default: 0 }),\n BATCH_SIZE: int(\"INTENT_BATCH_SIZE\", { default: 20, min: 1 }),\n TINY_BATCH_FRACTION: number(\"INTENT_TINY_BATCH_FRACTION\", { default: 0.2, min: 0, max: 1 }),\n },\n TEST: {\n SCOPE: string(\"TEST_SCOPE\", { default: \"all\" }),\n },\n} as const;\n", "/**\n * Clamp a number to the provided [min, max] range. If min or max are\n * undefined, only the defined bound(s) are applied.\n */\nexport function clamp(n: number, min?: number, max?: number): number {\n let out = n;\n if (typeof min === \"number\" && out < min) {\n out = min;\n }\n if (typeof max === \"number\" && out > max) {\n out = max;\n }\n return out;\n}\n", "/**\n * Lightweight env var readers matching the API service pattern.\n * - If the env var is set (non-empty), parse and return it.\n * - Otherwise, if a default is provided, return the default.\n * - Otherwise, throw a helpful error.\n *\n * This version exposes four self-contained readers with an options object\n * supporting { default, min, max } where min/max apply to int/number only.\n */\n\nexport type BaseOptions<T> = { default?: T };\nexport type RangeOptions = BaseOptions<number> & { min?: number; max?: number };\nexport type EnumOptions<T extends readonly string[]> = BaseOptions<T[number]> & { values: T };\n\nfunction throwRequiredEnvVar(name: string): never {\n throw new Error(`${name} is required.`);\n}\n\nimport { clamp } from \"./number\";\n\nexport function string(name: string, opts?: BaseOptions<string>): string {\n const value = process.env[name];\n const exists = value != null && value.length > 0;\n if (exists) {\n return value as string;\n }\n if (opts?.default !== undefined) {\n return opts.default;\n }\n return throwRequiredEnvVar(name);\n}\n\n/**\n * Read an env var as a constrained enum string.\n *\n * When set, validates the value is included in opts.values.\n * When unset, returns opts.default if provided, otherwise throws.\n */\nexport function enumeration<const T extends readonly string[]>(\n name: string,\n opts: EnumOptions<T>,\n): T[number] {\n const value = opts.default !== undefined ? string(name, { default: opts.default }) : string(name);\n if (opts.values.includes(value)) {\n return value;\n }\n throw new Error(`${name} must be one of: ${opts.values.join(\", \")}`);\n}\n\nexport function boolean(name: string, opts?: BaseOptions<boolean>): boolean {\n const value = process.env[name];\n const exists = value != null && value.length > 0;\n if (exists) {\n const v = value as string;\n return [\"0\", \"false\"].includes(v) === false;\n }\n if (opts?.default !== undefined) {\n return opts.default;\n }\n return throwRequiredEnvVar(name);\n}\n\nexport function int(name: string, opts?: RangeOptions): number {\n const value = process.env[name];\n const exists = value != null && value.length > 0;\n if (exists) {\n const parsed = Number.parseInt(value as string, 10);\n if (Number.isNaN(parsed)) {\n throw new Error(`${name} must be a valid integer`);\n }\n return clamp(parsed, opts?.min, opts?.max);\n }\n if (opts?.default !== undefined) {\n const def = Math.trunc(opts.default);\n return clamp(def, opts?.min, opts?.max);\n }\n return throwRequiredEnvVar(name);\n}\n\nexport function number(name: string, opts?: RangeOptions): number {\n const value = process.env[name];\n const exists = value != null && value.length > 0;\n if (exists) {\n const parsed = Number.parseFloat(value as string);\n if (Number.isNaN(parsed)) {\n throw new Error(`${name} must be a valid number`);\n }\n return clamp(parsed, opts?.min, opts?.max);\n }\n if (opts?.default !== undefined) {\n return clamp(opts.default, opts?.min, opts?.max);\n }\n return throwRequiredEnvVar(name);\n}\n", "/**\n * Default extractor functions for Intent.\n *\n * These provide sensible defaults when users don't specify custom key/summary extractors.\n */\n\n/**\n * Computes a simple 32-bit hash from a string using the djb2 algorithm.\n *\n * @param str - The string to hash\n * @returns A 32-bit hash value\n * @private\n */\nfunction hash32(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) + hash + char) | 0; // hash * 33 + char, keep in 32-bit range\n }\n return hash >>> 0; // Convert to unsigned 32-bit integer\n}\n\n/**\n * Converts any value to a pretty-printed JSON string with error handling.\n *\n * This is the canonical helper for all JSON stringification in the codebase.\n * Pretty-prints with 2-space indentation for better LLM readability.\n * Falls back to String(value) if JSON.stringify fails (e.g., circular references).\n * Handles undefined explicitly since JSON.stringify(undefined) returns undefined.\n *\n * @param value - The value to stringify\n * @returns Pretty-printed JSON string, or String() fallback\n * @throws Never throws - gracefully falls back to String() on any error\n */\nexport function jsonStringify(value: unknown): string {\n try {\n const result = JSON.stringify(value, null, 2);\n // JSON.stringify returns undefined for undefined values\n if (result === undefined) {\n return String(value);\n }\n return result;\n } catch {\n return String(value);\n }\n}\n\n/**\n * Converts any value to a hash-based string key.\n *\n * Serializes the value to JSON, computes a 32-bit hash, and returns it as a string.\n * This provides unique-enough keys for items without requiring explicit key extractors.\n *\n * @param value - The value to convert to a key\n * @returns A string representation of the hash (e.g., \"2847561\")\n * @throws Never throws - uses jsonStringify which handles all errors internally\n */\nexport function hashToString<T>(value: T): string {\n const json = jsonStringify(value);\n const hashValue = hash32(json);\n return String(hashValue);\n}\n\n/**\n * Default key extractor: generates hash-based string keys from items.\n *\n * Generic function that works with any item type T. The generic parameter\n * enables type-safe usage without type casts when assigned to typed extractors.\n *\n * @param item - The item to extract a key from\n * @returns A hash-based string key\n * @throws Never throws - uses hashToString which handles all errors internally\n */\nexport function DEFAULT_KEY_EXTRACTOR<T>(item: T): string {\n return hashToString(item);\n}\n\n/**\n * Default summary extractor: converts items to pretty-printed JSON strings.\n *\n * Uses 2-space indentation for better LLM readability.\n * Generic function that works with any item type T. The generic parameter\n * enables type-safe usage without type casts when assigned to typed extractors.\n *\n * @param item - The item to extract a summary from\n * @returns A pretty-printed JSON string representation of the item\n * @throws Never throws - uses jsonStringify which handles all errors internally\n */\nexport function DEFAULT_SUMMARY_EXTRACTOR<T>(item: T): string {\n return jsonStringify(item);\n}\n", "import Groq from \"groq-sdk\";\n\nimport { CONFIG } from \"../config\";\n\nimport type { ChatMessage, JSONObject, LlmCallConfig, LlmClient } from \"../types\";\nimport type { ChatCompletionMessageParam } from \"groq-sdk/resources/chat/completions\";\n\ntype GroqTimeoutOptions = { timeout?: number };\n\ntype GroqJsonSchemaResponseFormat = {\n type: \"json_schema\";\n json_schema: {\n name: string;\n schema: JSONObject;\n strict: true;\n };\n};\n\ntype GroqChatCompletionRequest = {\n model: string;\n reasoning_effort: \"low\" | \"medium\" | \"high\";\n messages: ChatCompletionMessageParam[];\n user?: string;\n response_format: GroqJsonSchemaResponseFormat;\n};\n\ntype GroqChatCompletionResponse = {\n choices: Array<{\n message?: {\n content?: string | null;\n };\n }>;\n};\n\n/**\n * Return a best-effort nested error record from groq-sdk.\n *\n * @param err - Any thrown value\n * @returns Nested `error` object when present\n * @private\n */\nfunction getNestedErrorObject(err: unknown): Record<string, unknown> | undefined {\n if (err == null || typeof err !== \"object\") {\n return undefined;\n }\n\n const record = err as Record<string, unknown>;\n const error = record.error;\n if (error == null || typeof error !== \"object\") {\n return undefined;\n }\n\n return error as Record<string, unknown>;\n}\n\n/**\n * Map internal ChatMessage to groq-sdk ChatCompletionMessageParam.\n *\n * Converts our generic message format to Groq's expected format.\n * Only supports system, user, and assistant roles. Throws on unsupported\n * roles (like \"tool\") to ensure predictable provider behavior.\n *\n * @param messages - Array of generic chat messages\n * @returns Array of Groq-formatted messages\n * @throws {Error} If message contains unsupported role\n * @private\n */\nfunction mapToGroqMessages(messages: ChatMessage[]): ChatCompletionMessageParam[] {\n return messages.map((m) => {\n if (m.role === \"system\" || m.role === \"user\" || m.role === \"assistant\") {\n return { role: m.role, content: m.content } as ChatCompletionMessageParam;\n }\n throw new Error(`intent: '${m.role}' role messages are not supported in provider calls`);\n });\n}\n\n/**\n * Build the request payload expected by groq-sdk with strict JSON schema.\n *\n * Constructs the complete request object including model, reasoning effort, messages,\n * optional user ID, and the response_format configuration that enforces strict\n * JSON schema validation on the model's output.\n *\n * @param outputSchema - JSON schema defining expected response structure\n * @param groqMessages - Formatted chat messages\n * @param config - Optional config overriding model and reasoning effort\n * @param userId - Optional user identifier for Groq's abuse monitoring\n * @returns Request payload ready for groq-sdk\n * @private\n */\nfunction buildGroqRequest(\n outputSchema: JSONObject,\n groqMessages: ChatCompletionMessageParam[],\n config: LlmCallConfig | undefined,\n userId: string | undefined,\n defaults: { model: string; reasoningEffort: \"low\" | \"medium\" | \"high\" },\n): GroqChatCompletionRequest {\n return {\n model: config?.model ?? defaults.model,\n reasoning_effort: config?.reasoningEffort ?? defaults.reasoningEffort,\n messages: groqMessages,\n ...(userId ? { user: userId } : {}),\n response_format: {\n type: \"json_schema\",\n json_schema: {\n name: \"intent_relevancy\",\n schema: outputSchema,\n strict: true,\n },\n },\n };\n}\n\n/**\n * Execute the chat completion with an optional timeout.\n *\n * Wraps the Groq SDK's chat.completions.create call with optional timeout support.\n *\n * @param client - Initialized Groq SDK client\n * @param request - Complete request payload\n * @param timeoutMs - Optional timeout in milliseconds\n * @returns Raw completion response from Groq\n * @private\n */\nasync function executeCompletion(\n client: GroqSdkLike,\n request: GroqChatCompletionRequest,\n timeoutMs?: number,\n): Promise<GroqChatCompletionResponse> {\n const timeout: GroqTimeoutOptions | undefined =\n typeof timeoutMs === \"number\" ? { timeout: timeoutMs } : undefined;\n return client.chat.completions.create(request, timeout);\n}\n\n/**\n * Extract and validate the content string from a Groq response.\n *\n * Safely navigates the response structure to find the message content.\n * Throws if content is missing or not a string.\n *\n * @param response - Raw completion response from Groq SDK\n * @returns The message content as a string\n * @throws {Error} If content is missing or invalid\n * @private\n */\nfunction getResponseContent(response: GroqChatCompletionResponse): string {\n const content: string | null | undefined = response?.choices?.[0]?.message?.content;\n if (typeof content !== \"string\") {\n throw new Error(\"Groq did not return content\");\n }\n return content;\n}\n\n/**\n * Parse the model's JSON content, throwing a clear error on failure.\n *\n * Attempts to parse the string content as JSON and wraps it in a { data } object\n * to match the expected LlmClient return type.\n *\n * @param content - JSON string from model response\n * @returns Wrapped parsed data\n * @throws {Error} If content is not valid JSON\n * @private\n */\nfunction parseJson<T>(content: string): { data: T } {\n try {\n const data = JSON.parse(content);\n return { data } as { data: T };\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n const err = new Error(`Groq returned invalid JSON: ${detail}`);\n (err as Error & { rawOutput?: string }).rawOutput = content;\n throw err;\n }\n}\n\ntype JsonRepairInput = {\n rawOutput: string;\n errorMessage: string;\n};\n\ntype RetryState = {\n remaining: number;\n request: GroqChatCompletionRequest;\n};\n\n/**\n * Build a repair conversation turn to help the model correct invalid JSON.\n *\n * We include the raw output and the error message, then remind the model to\n * return only valid JSON that matches the already-provided schema.\n *\n * @param baseMessages - Original Groq-formatted messages\n * @param rawOutput - Raw model output that failed parsing\n * @param errorMessage - Parse/validation error message\n * @returns New messages array including a repair request\n * @private\n */\nfunction buildJsonRepairMessages(\n baseMessages: ChatCompletionMessageParam[],\n rawOutput: string,\n errorMessage: string,\n): ChatCompletionMessageParam[] {\n return [\n ...baseMessages,\n { role: \"assistant\", content: rawOutput } as ChatCompletionMessageParam,\n {\n role: \"user\",\n content:\n \"Your previous response was invalid JSON or did not match the required JSON schema. \" +\n \"Please correct it and return ONLY valid JSON that matches the schema.\\n\\n\" +\n `Error: ${errorMessage}`,\n } as ChatCompletionMessageParam,\n ];\n}\n\n/**\n * Build a repair request object if we still have attempts remaining.\n *\n * @param remaining - Attempts remaining for the overall call\n * @param request - The current Groq request\n * @param repair - Repair context\n * @returns The repaired request and decremented remaining attempts, or undefined\n * @private\n */\nfunction buildRepairRetry(state: RetryState, repair: JsonRepairInput): RetryState | undefined {\n if (state.remaining <= 1) {\n return undefined;\n }\n\n const repairedRequest: GroqChatCompletionRequest = {\n ...state.request,\n messages: buildJsonRepairMessages(\n state.request.messages,\n repair.rawOutput,\n repair.errorMessage,\n ),\n };\n\n return { remaining: state.remaining - 1, request: repairedRequest };\n}\n\n/**\n * Convert a JSON parse error into a repair input.\n *\n * @param rawOutput - Raw model output\n * @param parseError - JSON.parse error\n * @returns Repair input\n * @private\n */\nfunction parseErrorToRepairInput(rawOutput: string, parseError: unknown): JsonRepairInput {\n return { rawOutput, errorMessage: String(parseError) };\n}\n\n/**\n * Extract raw output from a JSON parse error thrown by parseJson.\n *\n * @param error - Error thrown by parseJson\n * @returns Raw output when present\n * @private\n */\nfunction getRawOutputFromParseJsonError(error: unknown): string | undefined {\n if (!(error instanceof Error)) {\n return undefined;\n }\n\n const record = error as Error & { rawOutput?: unknown };\n return typeof record.rawOutput === \"string\" ? record.rawOutput : undefined;\n}\n\n/**\n * Extract repair inputs from a Groq schema-validation failure.\n *\n * Groq can return a rich error payload for `json_validate_failed`, sometimes\n * including the model's `generated_response`. We use this to ask the model to\n * correct its output without re-sending the schema.\n *\n * @param err - Unknown thrown error from groq-sdk\n * @returns Repair inputs when present; otherwise undefined\n * @private\n */\nfunction extractJsonValidateFailedRepairInput(err: unknown): JsonRepairInput | undefined {\n if (err instanceof Error) {\n const match = err.message.match(/\"failed_generation\":\"(\\{.*?\\})\"/);\n const failedGeneration = match?.[1] ? match[1].replace(/\\\\\"/g, '\"') : undefined;\n if (err.message.includes('\"code\":\"json_validate_failed\"')) {\n return {\n rawOutput:\n failedGeneration ?? \"(Groq did not include the rejected generation in the error payload)\",\n errorMessage: err.message,\n };\n }\n }\n\n const errorObj = getNestedErrorObject(err);\n const nested =\n errorObj && typeof errorObj.error === \"object\"\n ? (errorObj.error as Record<string, unknown>)\n : undefined;\n const serverError = nested ?? errorObj;\n if (!serverError) {\n return undefined;\n }\n\n const code = typeof serverError.code === \"string\" ? serverError.code : undefined;\n if (code !== \"json_validate_failed\") {\n return undefined;\n }\n\n const message = typeof serverError.message === \"string\" ? serverError.message : undefined;\n const generatedResponse =\n typeof serverError.generated_response === \"string\" ? serverError.generated_response : undefined;\n const failedGeneration =\n typeof serverError.failed_generation === \"string\" ? serverError.failed_generation : undefined;\n const rawOutput = generatedResponse ?? failedGeneration;\n\n return {\n rawOutput: rawOutput ?? \"(Groq did not include the rejected generation in the error payload)\",\n errorMessage: message ?? String(err),\n };\n}\n\n/**\n * Create a default Groq LLM client with retry logic.\n *\n * Returns an LlmClient implementation that uses the Groq SDK with:\n * - Strict JSON schema enforcement via response_format\n * - Automatic retry on schema validation failures (up to 3 attempts)\n * - Support for custom model, reasoning effort, timeout, and user ID\n *\n * @param apiKey - Groq API key\n * @returns LlmClient implementation for Groq\n *\n * @example\n * ```typescript\n * import { CONFIG } from \"../config\";\n * const client = createDefaultGroqClient(CONFIG.GROQ.API_KEY);\n * const result = await client.call(messages, schema, { model: \"llama-3.3-70b\" });\n * ```\n */\nexport type GroqSdkLike = {\n chat: {\n completions: {\n create: (\n req: GroqChatCompletionRequest,\n opts?: GroqTimeoutOptions,\n ) => Promise<GroqChatCompletionResponse>;\n };\n };\n};\n\ntype GroqClientFactory = (apiKey: string) => GroqSdkLike;\n\n/**\n * Create a GroqSdkLike wrapper around groq-sdk.\n *\n * This keeps our provider surface strongly typed while isolating groq-sdk's\n * broader request/response types to a single boundary.\n *\n * @param apiKey - Groq API key\n * @returns GroqSdkLike wrapper\n * @private\n */\nexport function createGroqSdkLike(apiKey: string): GroqSdkLike {\n const sdk = new Groq({ apiKey }) as any;\n return {\n chat: {\n completions: {\n create: async (req, opts) => {\n // groq-sdk's types lag behind json_schema support; keep the boundary localized.\n return (await (sdk.chat.completions.create as any)(\n req,\n opts,\n )) as GroqChatCompletionResponse;\n },\n },\n },\n };\n}\n\n/**\n * Create the underlying Groq SDK client.\n *\n * This wrapper exists to make the default SDK construction path unit-testable.\n *\n * @param options - Groq SDK constructor options\n * @returns Groq SDK client\n * @private\n */\nexport function createGroqSdk(options: { apiKey: string }): unknown {\n return new Groq(options);\n}\n\nexport function createDefaultGroqClient(\n apiKey: string,\n options?: {\n defaults?: { model?: string; reasoningEffort?: \"low\" | \"medium\" | \"high\" };\n makeSdk?: (apiKey: string) => GroqSdkLike;\n jsonRepairAttempts?: number;\n },\n): LlmClient {\n const defaults = {\n model: options?.defaults?.model ?? CONFIG.GROQ.DEFAULT_MODEL,\n reasoningEffort: options?.defaults?.reasoningEffort ?? CONFIG.GROQ.DEFAULT_REASONING_EFFORT,\n } as const;\n const makeSdk: GroqClientFactory = options?.makeSdk ?? createGroqSdkLike;\n const jsonRepairAttempts = options?.jsonRepairAttempts ?? CONFIG.GROQ.JSON_REPAIR_ATTEMPTS;\n return {\n /**\n * Call Groq with JSON schema enforced response and return parsed data.\n *\n * Implements the LlmClient interface with Groq-specific features:\n * - Creates a new SDK client per call with the provided API key\n * - Maps generic messages to Groq format\n * - Builds request with strict JSON schema response format\n * - Retries up to 3 times on json_validate_failed errors\n * - Parses and returns the structured response\n *\n * @param messages - Chat messages to send to the model\n * @param outputSchema - JSON schema defining expected response structure\n * @param config - Optional model, reasoning effort, and timeout overrides\n * @param userId - Optional user ID for Groq's abuse monitoring\n * @returns Parsed response data wrapped in { data } object\n * @throws {Error} If all retry attempts fail or response is invalid\n */\n async call<T>(\n messages: ChatMessage[],\n outputSchema: JSONObject,\n config?: LlmCallConfig,\n userId?: string,\n ): Promise<{ data: T }> {\n const client = makeSdk(apiKey);\n const groqMessages = mapToGroqMessages(messages);\n const baseRequest = buildGroqRequest(outputSchema, groqMessages, config, userId, defaults);\n\n const createWithRetry = async (state: RetryState): Promise<{ data: T }> => {\n try {\n const response = await executeCompletion(client, state.request, config?.timeoutMs);\n const content = getResponseContent(response);\n\n const parsed = parseJson<T>(content);\n return parsed;\n } catch (err) {\n // If the model returned bad JSON, retry by appending a repair turn.\n const parseRawOutput = getRawOutputFromParseJsonError(err);\n if (parseRawOutput !== undefined) {\n const retry = buildRepairRetry(state, parseErrorToRepairInput(parseRawOutput, err));\n if (retry) {\n return createWithRetry(retry);\n }\n throw err;\n }\n\n const validationRepairInput = extractJsonValidateFailedRepairInput(err);\n if (validationRepairInput) {\n const retry = buildRepairRetry(state, validationRepairInput);\n if (retry) {\n return createWithRetry(retry);\n }\n }\n\n // Non-JSON errors are terminal (quota/outage/etc.).\n throw err;\n }\n };\n\n const attempts = Math.max(1, jsonRepairAttempts);\n return createWithRetry({ remaining: attempts, request: baseRequest });\n },\n } satisfies LlmClient;\n}\n", "import { CONFIG } from \"./config\";\nimport { createDefaultGroqClient } from \"./providers/groq\";\n\nimport type { LlmClient, IntentContext } from \"./types\";\n\n/**\n * Select an LLM client to use for reranking.\n *\n * Implements the client selection logic:\n * 1. If ctx.llm is provided, use it directly\n * 2. Else if GROQ_API_KEY environment variable is set, create default Groq client\n * 3. Else return undefined (caller must handle error)\n *\n * @param ctx - Context object potentially containing an LLM client\n * @returns Selected LLM client, or undefined if none available\n */\nexport function selectLlmClient(\n ctx: IntentContext,\n config: typeof CONFIG = CONFIG,\n): LlmClient | undefined {\n if (ctx.llm) {\n return ctx.llm;\n }\n const groqKey = config.GROQ.API_KEY;\n if (groqKey && groqKey !== \"\") {\n return createDefaultGroqClient(groqKey, {\n defaults: {\n model: config.GROQ.DEFAULT_MODEL,\n reasoningEffort: config.GROQ.DEFAULT_REASONING_EFFORT,\n },\n });\n }\n return undefined;\n}\n", "import { jsonStringify } from \"./extractors\";\n\nimport type { ChatMessage, IntentCandidate } from \"./types\";\n\n/**\n * Build system + user messages instructing the model to score candidates.\n *\n * Constructs a two-message conversation:\n * 1. System message: Defines the scoring task, output format, and constraints\n * 2. User message: Contains the query and candidate items as JSON\n *\n * The system prompt emphasizes using the full configured score range and being decisive\n * about relevance, with strict instructions to return only the score mapping.\n *\n * @param query - The search query or user intent\n * @param candidates - Array of candidates with keys and summaries\n * @returns Array of chat messages ready for LLM consumption\n */\nexport function buildMessages(\n query: string,\n candidates: IntentCandidate[],\n scoreRange: { minScore: number; maxScore: number },\n): ChatMessage[] {\n const system = `You will receive a JSON blob containing candidate_search_results (each candidate has a key and a short summary) plus a short user request.\n\nYour task is to assess each candidate and return a JSON object that maps candidate keys to objects of the form {\"explanation\": string, \"score\": integer} avoiding ambiguity.\n\nThe score must be an integer from ${scoreRange.minScore} to ${scoreRange.maxScore}:\n- ${scoreRange.minScore} means not relevant at all\n- ${scoreRange.maxScore} means highly relevant\n\nSometimes none are relevant, sometimes all are relevant. Be decisive.\n\nIt is okay to return ${scoreRange.minScore} if the candidate is not relevant to the query. It is okay to return ${scoreRange.maxScore} if the candidate is highly relevant to the query. Use the full range of scores.\n\nEvery candidate MUST include an explanation. Write the explanation first, then the score. The explanation should be concise (1-3 sentences), concrete, and reference the query intent and the candidate summary.\n\nWrite explanations as end-user-facing justifications:\n- Do NOT say \"the query\" or talk about prompt mechanics.\n- Write in a direct, item-first voice (e.g., \"gpt-5.2 is best here because it specializes in feature implementation and testing.\").\n- Avoid \"I\"/\"we\".\n\nEvery key in candidate_search_results must be present in your output mapping. Do not add any keys that are not present in candidate_search_results.\nEvery key in candidate_search_results must map to an object with:\n- explanation: string\n- score: integer from ${scoreRange.minScore} to ${scoreRange.maxScore}\nDo not, in your generated JSON, include anything other than the \\`\"{key}\": {\"explanation\": \"...\", \"score\": 7}\\` mappings. Do not include any other text outside the JSON.\n\nReturn a JSON object that matches the enforced JSON schema for response formatting. Use the candidate.key as the property name in the output mapping.\n\nThe JSON you return should be of the form: {\n \"Key for document 1\": { \"explanation\": \"...\", \"score\": ${scoreRange.minScore} },\n \"Key for document 2\": { \"explanation\": \"...\", \"score\": ${scoreRange.maxScore} },\n ...\n}\n\nPretty-print the JSON for readability.`;\n\n const payload = {\n query,\n candidate_search_results: candidates.map((c) => ({ key: c.key, summary: c.summary })),\n } as const;\n\n return [\n { role: \"system\", content: system },\n { role: \"user\", content: jsonStringify(payload) },\n ];\n}\n\n/**\n * Build system + user messages instructing the model to filter candidates.\n *\n * The model must output a JSON object mapping each candidate key to:\n * Example: {\"Some key\": {\"explanation\": \"...\", \"isRelevant\": true}}.\n *\n * @param query - The search query or user intent\n * @param candidates - Array of candidates with keys and summaries\n * @returns Array of chat messages ready for LLM consumption\n */\nexport function buildFilterMessages(query: string, candidates: IntentCandidate[]): ChatMessage[] {\n const system = `You will receive a JSON blob containing candidate_search_results (each candidate has a key and a short summary) plus a short user request.\n\nYour task is to assess each candidate and return a JSON object that maps candidate keys to objects of the form {\"explanation\": string, \"isRelevant\": boolean}.\n\nReturn isRelevant=true only when the candidate clearly helps satisfy the query intent. Otherwise return isRelevant=false.\n\nEvery candidate MUST include an explanation. Write the explanation first, then the boolean. The explanation should be concise (1-3 sentences), concrete, and reference the query intent and the candidate summary.\n\nWrite explanations as end-user-facing justifications:\n- Do NOT say \"the query\" or talk about prompt mechanics.\n- Write in a direct, item-first voice.\n- Avoid \"I\"/\"we\".\n\nEvery key in candidate_search_results must be present in your output mapping. Do not add any keys that are not present in candidate_search_results.\nEvery key in candidate_search_results must map to an object with:\n- explanation: string\n- isRelevant: boolean\nDo not include anything other than the mapping JSON object. Return only JSON matching the enforced schema.\n\nPretty-print the JSON for readability.`;\n\n const payload = {\n query,\n candidate_search_results: candidates.map((c) => ({ key: c.key, summary: c.summary })),\n } as const;\n\n return [\n { role: \"system\", content: system },\n { role: \"user\", content: jsonStringify(payload) },\n ];\n}\n\n/**\n * Build system + user messages instructing the model to choose exactly one candidate.\n *\n * The model must output a JSON object of the form:\n * Example: {\"explanation\": \"...\", \"selectedKey\": \"Some key\"}.\n *\n * @param query - The search query or user intent\n * @param candidates - Array of candidates with keys and summaries\n * @returns Array of chat messages ready for LLM consumption\n */\nexport function buildChoiceMessages(query: string, candidates: IntentCandidate[]): ChatMessage[] {\n const system = `You will receive a JSON blob containing candidate_search_results (each candidate has a key and a short summary) plus a short user request.\n\nYour task is to choose exactly one candidate as the best match for what the user wants.\n\nYou MUST choose one candidate key from the provided list. Do not choose multiple.\n\nReturn ONLY JSON of the form: {\"explanation\": string, \"selectedKey\": string} where selectedKey is exactly one of the candidate keys. The explanation should be concise (1-3 sentences), concrete, and reference the query intent and the candidate summary.\n\nWrite the explanation as an end-user-facing justification:\n- Do NOT say \"the query\" or talk about prompt mechanics.\n- Write in a direct, item-first voice.\n- Avoid \"I\"/\"we\".\n\nDo not include any other text outside the JSON. Return only JSON matching the enforced schema.\n\nPretty-print the JSON for readability.`;\n\n const payload = {\n query,\n candidate_search_results: candidates.map((c) => ({ key: c.key, summary: c.summary })),\n } as const;\n\n return [\n { role: \"system\", content: system },\n { role: \"user\", content: jsonStringify(payload) },\n ];\n}\n", "import type { JSONObject } from \"./types\";\n\ntype IntegerSchema = { type: \"integer\" };\ntype StringSchema = { type: \"string\" };\ntype BooleanSchema = { type: \"boolean\" };\n\ntype CandidateEvaluationSchema = {\n type: \"object\";\n properties: {\n explanation: StringSchema;\n score: IntegerSchema;\n };\n required: [\"explanation\", \"score\"];\n additionalProperties: false;\n};\n\ntype CandidateFilterSchema = {\n type: \"object\";\n properties: {\n explanation: StringSchema;\n isRelevant: BooleanSchema;\n };\n required: [\"explanation\", \"isRelevant\"];\n additionalProperties: false;\n};\n\ntype ChoiceSchema = {\n type: \"object\";\n properties: {\n explanation: StringSchema;\n selectedKey: { type: \"string\"; enum: string[] };\n };\n required: [\"explanation\", \"selectedKey\"];\n additionalProperties: false;\n};\n\n/**\n * Build the schema used for a single candidate's evaluation.\n *\n * Property order is intentional: `explanation` is defined before `score` to\n * encourage structured-output models to generate explanations first.\n *\n * @returns JSON schema for a single candidate evaluation\n */\nexport function buildCandidateEvaluationSchema(): CandidateEvaluationSchema {\n return {\n type: \"object\",\n properties: {\n explanation: { type: \"string\" },\n score: { type: \"integer\" },\n },\n required: [\"explanation\", \"score\"],\n additionalProperties: false,\n };\n}\n\n/**\n * Build the schema used for a single candidate's filter decision.\n *\n * Property order is intentional: `explanation` is defined before `isRelevant`.\n *\n * @returns JSON schema for a single candidate filter decision\n */\nexport function buildCandidateFilterSchema(): CandidateFilterSchema {\n return {\n type: \"object\",\n properties: {\n explanation: { type: \"string\" },\n isRelevant: { type: \"boolean\" },\n },\n required: [\"explanation\", \"isRelevant\"],\n additionalProperties: false,\n };\n}\n\n/**\n * Build a strict JSON schema mapping candidate keys to evaluation objects.\n *\n * Each candidate key maps to an object containing:\n * - explanation: a short justification of the score\n * - score: an integer within the configured range\n *\n * Property order is intentional (`explanation` then `score`) to encourage the\n * model to generate explanations before scores in structured-output modes.\n *\n * @param keys - Array of unique candidate keys\n * @returns JSON schema object enforcing exact structure of response\n */\nexport function buildRelevancySchema(\n keys: string[],\n minScore: number,\n maxScore: number,\n): JSONObject {\n const evaluationSchema = buildCandidateEvaluationSchema();\n\n const properties: Record<string, CandidateEvaluationSchema> = {};\n for (const k of keys) {\n properties[k] = evaluationSchema;\n }\n\n return {\n title: \"Query / Candidate Relevancy Assessment\",\n description: `Map candidate results for a search query to relevancy scores (${minScore}-${maxScore}) with explanations.`,\n type: \"object\",\n properties,\n required: keys,\n additionalProperties: false,\n } as JSONObject;\n}\n\n/**\n * Build a strict JSON schema mapping candidate keys to boolean relevancy decisions.\n *\n * Each candidate key maps to an object containing:\n * - explanation: a short justification\n * - isRelevant: boolean\n *\n * @param keys - Array of unique candidate keys\n * @returns JSON schema object enforcing exact structure of response\n */\nexport function buildFilterSchema(keys: string[]): JSONObject {\n const decisionSchema = buildCandidateFilterSchema();\n\n const properties: Record<string, CandidateFilterSchema> = {};\n for (const k of keys) {\n properties[k] = decisionSchema;\n }\n\n return {\n title: \"Query / Candidate Relevancy Filter\",\n description:\n \"Map candidate results for a search query to boolean relevancy decisions with explanations.\",\n type: \"object\",\n properties,\n required: keys,\n additionalProperties: false,\n } as JSONObject;\n}\n\n/**\n * Build a strict JSON schema for choosing a single candidate key.\n *\n * The model must return:\n * - explanation: string\n * - selectedKey: one of the provided candidate keys (enum)\n *\n * @param keys - Array of unique candidate keys\n * @returns JSON schema enforcing a single selected key\n */\nexport function buildChoiceSchema(keys: string[]): JSONObject {\n return {\n title: \"Query / Candidate Single Choice\",\n description: \"Choose exactly one candidate key for the query and explain why.\",\n type: \"object\",\n properties: {\n explanation: { type: \"string\" },\n selectedKey: { type: \"string\", enum: keys },\n },\n required: [\"explanation\", \"selectedKey\"],\n additionalProperties: false,\n } as ChoiceSchema as JSONObject;\n}\n", "import { batchProcess } from \"./batches\";\nimport { CONFIG } from \"./config\";\nimport { DEFAULT_KEY_EXTRACTOR, DEFAULT_SUMMARY_EXTRACTOR } from \"./extractors\";\nimport { clamp } from \"./lib/number\";\nimport { selectLlmClient } from \"./llm_client\";\nimport { buildChoiceMessages, buildFilterMessages, buildMessages } from \"./messages\";\nimport { buildChoiceSchema, buildFilterSchema, buildRelevancySchema } from \"./schema\";\n\nimport type {\n ChatMessage,\n JSONObject,\n IntentOptions,\n IntentConfig,\n LlmClient,\n LlmCallConfig,\n IntentContext,\n IntentExtractors,\n} from \"./types\";\n\n/**\n * LLM-based reranker for arbitrary items.\n *\n * Uses a listwise LLM approach to score candidates within a configurable range based on relevance to a query,\n * then filters by threshold and returns results sorted by score with stable ordering.\n *\n * @template T - The type of items to rerank (defaults to any)\n *\n * @example\n * ```typescript\n * // Simplest: uses defaults and GROQ_API_KEY from environment\n * const intent = new Intent();\n * const ranked = await intent.rank(\"find expense reports\", items);\n *\n * // With custom extractors\n * type Document = { id: string; title: string; content: string };\n * const intent = new Intent<Document>({\n * key: doc => doc.title,\n * summary: doc => doc.content.slice(0, 200),\n * relevancyThreshold: 5,\n * batchSize: 20\n * });\n *\n * // With custom LLM client\n * const intent = new Intent<Document>({\n * llm: myClient,\n * userId: \"user-123\",\n * key: doc => doc.title,\n * summary: doc => doc.content.slice(0, 200)\n * });\n * ```\n */\nexport class Intent<T = any> {\n private readonly cfg: Required<IntentConfig>;\n private readonly llm: LlmClient;\n private readonly ctx: IntentContext;\n private readonly extractors: Required<IntentExtractors<T>>;\n private readonly env: typeof CONFIG;\n\n /**\n * Resolve the model name to use for this Intent instance.\n *\n * Intent is provider-driven. Today only GROQ is supported; when using GROQ\n * we always take the model from GROQ's config defaults.\n *\n * @returns Provider-specific model name\n * @private\n */\n private resolveModel(): string {\n return this.env.GROQ.DEFAULT_MODEL;\n }\n\n /**\n * Builds the context object from options.\n *\n * Constructs an IntentContext with only defined properties to satisfy\n * TypeScript's exactOptionalPropertyTypes requirement.\n *\n * @param options - The options object containing llm, logger, and userId\n * @returns IntentContext with only defined properties\n * @private\n */\n private buildContext(options: IntentOptions<T>): IntentContext {\n return {\n ...(options.llm !== undefined && { llm: options.llm }),\n ...(options.logger !== undefined && { logger: options.logger }),\n ...(options.userId !== undefined && { userId: options.userId }),\n };\n }\n\n /**\n * Builds the extractors object from options.\n *\n * Uses provided extractors or falls back to generic defaults that work\n * for any type T via JSON stringification and hashing.\n *\n * @param options - The options object containing key and summary extractors\n * @returns Required extractors with defaults applied\n * @private\n */\n private buildExtractors(options: IntentOptions<T>): Required<IntentExtractors<T>> {\n return {\n key: options.key ?? DEFAULT_KEY_EXTRACTOR<T>,\n summary: options.summary ?? DEFAULT_SUMMARY_EXTRACTOR<T>,\n };\n }\n\n /**\n * Builds the configuration object from options.\n *\n * Merges user-provided options with environment-based CONFIG defaults.\n *\n * @param options - The options object containing config overrides\n * @returns Required config with all values populated\n * @private\n */\n private buildConfig(options: IntentOptions<T>): Required<IntentConfig> {\n return {\n provider: options.provider ?? this.env.INTENT.PROVIDER,\n timeoutMs: options.timeoutMs ?? this.env.INTENT.TIMEOUT_MS,\n relevancyThreshold: options.relevancyThreshold ?? this.env.INTENT.RELEVANCY_THRESHOLD,\n batchSize: options.batchSize ?? this.env.INTENT.BATCH_SIZE,\n tinyBatchFraction: options.tinyBatchFraction ?? this.env.INTENT.TINY_BATCH_FRACTION,\n minScore: options.minScore ?? this.env.INTENT.MIN_SCORE,\n maxScore: options.maxScore ?? this.env.INTENT.MAX_SCORE,\n };\n }\n\n /**\n * Validates the configuration values.\n *\n * Ensures the configured score range is valid and the relevancyThreshold is in range.\n *\n * @throws {Error} If maxScore is below minScore\n * @throws {Error} If relevancyThreshold is not within [minScore, maxScore]\n * @private\n */\n private validateConfig(): void {\n if (this.cfg.maxScore < this.cfg.minScore) {\n throw new Error(\n `intent: maxScore must be >= minScore, got minScore=${this.cfg.minScore} maxScore=${this.cfg.maxScore}`,\n );\n }\n\n if (\n this.cfg.relevancyThreshold < this.cfg.minScore ||\n this.cfg.relevancyThreshold > this.cfg.maxScore\n ) {\n throw new Error(\n `intent: relevancyThreshold must be between ${this.cfg.minScore} and ${this.cfg.maxScore}, got ${this.cfg.relevancyThreshold}`,\n );\n }\n }\n\n /**\n * Selects and validates the LLM client.\n *\n * Uses the provided client from context or attempts to create a default\n * Groq client if GROQ_API_KEY is available.\n *\n * @returns The selected LLM client\n * @throws {Error} If no LLM client is provided and GROQ_API_KEY is not set\n * @private\n */\n private selectAndValidateLlmClient(): LlmClient {\n const selectedClient = selectLlmClient(this.ctx, this.env);\n if (!selectedClient) {\n throw new Error(\n \"intent: No LLM client provided and GROQ_API_KEY not set. Provide options.llm or set GROQ_API_KEY.\",\n );\n }\n return selectedClient;\n }\n\n /**\n * Creates a new Intent instance.\n *\n * All options are optional with sensible defaults:\n * - llm: Auto-detected from GROQ_API_KEY environment variable if available\n * - key: Hash-based string from JSON representation of items\n * - summary: Pretty-printed JSON of items (2-space indentation for LLM readability)\n * - Config values: From INTENT_* environment variables or built-in defaults\n *\n * @param options - Optional configuration object\n * @param options.llm - Optional LLM client. If omitted, uses Groq client when GROQ_API_KEY is set\n * @param options.logger - Optional logger for warnings and errors\n * @param options.userId - Optional user identifier for LLM provider abuse monitoring\n * @param options.key - Optional function extracting a short human-readable key from items\n * @param options.summary - Optional function extracting a short description for LLM reasoning\n * @param options.provider - Optional provider override (default: INTENT_PROVIDER or \"GROQ\")\n * @param options.timeoutMs - Optional timeout in milliseconds (default: INTENT_TIMEOUT_MS or 3000)\n * @param options.relevancyThreshold - Optional minimum score to include results (default: INTENT_RELEVANCY_THRESHOLD)\n * @param options.minScore - Optional minimum score value (default: INTENT_MIN_SCORE or 0)\n * @param options.maxScore - Optional maximum score value (default: INTENT_MAX_SCORE or 10)\n * @param options.batchSize - Optional number of candidates per LLM call (default: INTENT_BATCH_SIZE or 20)\n * @param options.tinyBatchFraction - Optional threshold for merging small batches (default: INTENT_TINY_BATCH_FRACTION or 0.2)\n * @throws {Error} If no LLM client is provided and GROQ_API_KEY is not set\n * @throws {Error} If maxScore is below minScore\n * @throws {Error} If relevancyThreshold is not within [minScore, maxScore]\n *\n * @example\n * ```typescript\n * // Minimal - uses all defaults\n * const intent = new Intent();\n *\n * // With extractors\n * const intent = new Intent<Doc>({\n * key: doc => doc.title,\n * summary: doc => doc.content\n * });\n *\n * // Full configuration\n * const intent = new Intent<Doc>({\n * llm: myClient,\n * userId: \"org-123\",\n * key: doc => doc.title,\n * summary: doc => doc.content,\n * relevancyThreshold: 5,\n * batchSize: 20\n * });\n * ```\n */\n constructor(options: IntentOptions<T> & { config?: typeof CONFIG } = {}) {\n this.env = options.config ?? CONFIG;\n this.ctx = this.buildContext(options);\n this.extractors = this.buildExtractors(options);\n this.cfg = this.buildConfig(options);\n\n this.validateConfig();\n this.llm = this.selectAndValidateLlmClient();\n }\n\n /**\n * Rerank candidates based on relevance to a query.\n *\n * Calls the LLM to evaluate each candidate and returns only those above the\n * configured threshold, sorted by score (desc) with stable ordering on ties.\n *\n * The LLM is instructed to always generate an explanation before the score.\n * Explanations are only returned when `options.explain` is true.\n *\n * Fast-path optimizations:\n * - Returns empty array for 0 candidates without LLM call\n * - Returns single candidate unchanged without LLM call\n *\n * Error handling:\n * - On any batch error, returns that batch's items in original order\n * - On top-level error, returns all items in original order\n * - All errors are logged via the configured logger\n *\n * @param query - The search query or user intent to rank against\n * @param candidates - Array of items to rerank\n * @param options - Optional per-call configuration\n * @param options.explain - When true, return `{ item, explanation }[]` instead of `T[]`\n * @param options.userId - Optional user ID for this specific call, overrides ctx.userId\n * @returns Filtered and sorted array of items, or original order on any error\n *\n * @example\n * ```typescript\n * const itemsOnly = await intent.rank(\"find expense reports\", docs);\n *\n * const withExplanations = await intent.rank(\"find expense reports\", docs, { explain: true });\n * // => [{ item: Doc, explanation: string }, ...]\n * ```\n */\n public async rank(\n query: string,\n candidates: T[],\n options?: { explain?: false; userId?: string },\n ): Promise<T[]>;\n\n public async rank(\n query: string,\n candidates: T[],\n options: { explain: true; userId?: string },\n ): Promise<Array<{ item: T; explanation: string }>>;\n\n public async rank(\n query: string,\n candidates: T[],\n options?: { explain?: boolean; userId?: string },\n ): Promise<T[] | Array<{ item: T; explanation: string }>> {\n try {\n if (candidates.length === 0) {\n return [];\n }\n\n if (candidates.length === 1) {\n if (options?.explain) {\n const [firstCandidate] = candidates;\n return [{ item: firstCandidate!, explanation: \"\" }];\n }\n return candidates;\n }\n\n const prepared = this.prepareCandidates(candidates);\n\n const rankedWithExplanations = await batchProcess(\n prepared,\n this.cfg.batchSize,\n this.cfg.tinyBatchFraction,\n async (batch) =>\n (await this.processBatch(\n query,\n batch,\n options?.userId !== undefined ? { userId: options.userId } : undefined,\n )) as Array<{ item: T; explanation: string }>,\n this.ctx.logger,\n (batch) => batch.map(({ item }) => ({ item, explanation: \"\" })),\n );\n\n if (options?.explain) {\n return rankedWithExplanations;\n }\n\n return rankedWithExplanations.map(({ item }) => item);\n } catch (error) {\n this.ctx.logger?.warn?.(\"intent reranker failed, using fallback\", {\n error: (error as Error)?.message,\n });\n\n if (options?.explain) {\n return candidates.map((item) => ({ item, explanation: \"\" }));\n }\n return candidates;\n }\n }\n\n /**\n * Filter candidates based on relevance to a query.\n *\n * Calls the LLM to decide whether each candidate is relevant, then returns\n * only the relevant items in the same order they were provided.\n *\n * Explanations are only returned when `options.explain` is true.\n *\n * Fast paths:\n * - Returns [] for 0 candidates without LLM call\n * - Returns the single candidate unchanged without LLM call\n *\n * Error handling:\n * - On any batch error, preserves that batch's original order\n * - On top-level error, preserves original order\n *\n * @param query - The search query or user intent to filter against\n * @param candidates - Array of items to filter\n * @param options - Optional per-call configuration\n * @param options.explain - When true, return `{ item, explanation }[]` instead of `T[]`\n * @param options.userId - Optional user ID for this specific call, overrides ctx.userId\n * @returns Filtered array of items, preserving input order\n */\n public async filter(\n query: string,\n candidates: T[],\n options?: { explain?: false; userId?: string },\n ): Promise<T[]>;\n\n public async filter(\n query: string,\n candidates: T[],\n options: { explain: true; userId?: string },\n ): Promise<Array<{ item: T; explanation: string }>>;\n\n public async filter(\n query: string,\n candidates: T[],\n options?: { explain?: boolean; userId?: string },\n ): Promise<T[] | Array<{ item: T; explanation: string }>> {\n if (candidates.length === 0) {\n return [];\n }\n\n if (candidates.length === 1) {\n if (options?.explain) {\n const [firstCandidate] = candidates;\n return [{ item: firstCandidate!, explanation: \"\" }];\n }\n return candidates;\n }\n\n const prepared = this.prepareCandidates(candidates);\n\n const filteredWithExplanations = await batchProcess(\n prepared,\n this.cfg.batchSize,\n this.cfg.tinyBatchFraction,\n async (batch) =>\n (await this.processFilterBatch(\n query,\n batch,\n options?.userId !== undefined ? { userId: options.userId } : undefined,\n )) as Array<{ item: T; explanation: string }>,\n this.ctx.logger,\n (batch) => batch.map(({ item }) => ({ item, explanation: \"\" })),\n );\n\n if (options?.explain) {\n return filteredWithExplanations;\n }\n\n return filteredWithExplanations.map(({ item }) => item);\n }\n\n /**\n * Choose exactly one candidate as the best match for a query.\n *\n * Uses a tournament strategy when inputs exceed the batch size:\n * - Choose one from each batch\n * - Then choose one from the batch winners\n *\n * This method always returns a single item.\n *\n * @param query - The search query or user intent\n * @param candidates - Array of items to choose from\n * @param options - Optional per-call configuration\n * @param options.explain - When true, return `{ item, explanation }` instead of `T`\n * @param options.userId - Optional user ID for this specific call, overrides ctx.userId\n * @returns The single chosen item (or item + explanation)\n */\n public async choice(\n query: string,\n candidates: T[],\n options?: { explain?: false; userId?: string },\n ): Promise<T>;\n\n public async choice(\n query: string,\n candidates: T[],\n options: { explain: true; userId?: string },\n ): Promise<{ item: T; explanation: string }>;\n\n public async choice(\n query: string,\n candidates: T[],\n options?: { explain?: boolean; userId?: string },\n ): Promise<T | { item: T; explanation: string }> {\n if (candidates.length === 0) {\n throw new Error(\"intent: choice requires at least one candidate\");\n }\n\n if (candidates.length === 1) {\n const [firstCandidate] = candidates;\n if (options?.explain) {\n return { item: firstCandidate!, explanation: \"\" };\n }\n return firstCandidate!;\n }\n\n const prepared = this.prepareCandidates(candidates);\n const keyed = this.ensureUniqueKeys(prepared);\n\n const winners = await batchProcess(\n keyed,\n this.cfg.batchSize,\n this.cfg.tinyBatchFraction,\n async (batch) => [await this.processChoiceBatch(query, batch, options?.userId)],\n this.ctx.logger,\n (batch) => [{ item: batch[0]!.item, explanation: \"\" }],\n );\n\n // batchProcess guarantees at least one winner via its per-batch fallback.\n\n if (winners.length === 1) {\n const [onlyWinner] = winners;\n if (options?.explain) {\n return onlyWinner!;\n }\n return onlyWinner!.item;\n }\n\n const preparedFinalists = this.prepareCandidates(winners.map((w) => w.item));\n const keyedFinalists = this.ensureUniqueKeys(preparedFinalists);\n\n const final = await this.processChoiceBatch(query, keyedFinalists, options?.userId);\n if (options?.explain) {\n return final;\n }\n return final.item;\n }\n\n /**\n * Normalize incoming items into a consistent shape for downstream processing.\n *\n * Extracts the key and summary from each item using the configured extractors,\n * and attaches the original input index for stable sorting later.\n *\n * @param candidates - Raw items to prepare\n * @returns Array of prepared candidates with extracted metadata and original index\n * @private\n */\n private prepareCandidates(candidates: T[]): Array<{\n item: T;\n idx: number;\n baseKey: string;\n summary: string;\n }> {\n return candidates.map((item, idx) => ({\n item,\n idx,\n baseKey: this.extractors.key(item),\n summary: this.extractors.summary(item),\n }));\n }\n\n /**\n * Ensure keys are unique by suffixing duplicates with their input index.\n *\n * When multiple items share the same key, subsequent occurrences are renamed\n * to \"Key (idx)\" where idx is the original input index. This prevents JSON\n * schema validation errors and ensures the LLM can score each item independently.\n *\n * @param itemsBase - Prepared candidates with potentially duplicate keys\n * @returns Candidates with guaranteed unique keys\n * @private\n */\n private ensureUniqueKeys(\n itemsBase: Array<{ item: T; idx: number; baseKey: string; summary: string }>,\n ): Array<{ item: T; idx: number; key: string; summary: string }> {\n const counts = new Map<string, number>();\n return itemsBase.map(({ item, baseKey, summary, idx }) => {\n const n = (counts.get(baseKey) ?? 0) + 1;\n counts.set(baseKey, n);\n const key = n === 1 ? baseKey : `${baseKey} (${idx})`;\n return { item, idx, key, summary };\n });\n }\n\n /**\n * Build the JSON schema and chat messages payload for the LLM.\n *\n * Creates a strict JSON schema requiring one integer property (minScore-maxScore) per candidate key,\n * and constructs system + user messages instructing the LLM to score relevance.\n *\n * @param query - The search query to evaluate candidates against\n * @param items - Candidates with unique keys and summaries\n * @returns Object containing JSON schema and chat messages array\n * @private\n */\n private buildRequest(\n query: string,\n items: Array<{ key: string; summary: string }>,\n ): { schema: JSONObject; messages: ChatMessage[] } {\n const keys = items.map((x) => x.key);\n const schema: JSONObject = buildRelevancySchema(keys, this.cfg.minScore, this.cfg.maxScore);\n const messages = buildMessages(query, items, {\n minScore: this.cfg.minScore,\n maxScore: this.cfg.maxScore,\n });\n return { schema, messages };\n }\n\n /**\n * Build the JSON schema and chat messages payload for the LLM filter call.\n *\n * @param query - The search query to evaluate candidates against\n * @param items - Candidates with unique keys and summaries\n * @returns Object containing JSON schema and chat messages array\n * @private\n */\n private buildFilterRequest(\n query: string,\n items: Array<{ key: string; summary: string }>,\n ): { schema: JSONObject; messages: ChatMessage[] } {\n const keys = items.map((x) => x.key);\n const schema: JSONObject = buildFilterSchema(keys);\n const messages = buildFilterMessages(query, items);\n return { schema, messages };\n }\n\n /**\n * Build the JSON schema and chat messages payload for the LLM choice call.\n *\n * @param query - The search query to choose against\n * @param items - Candidates with unique keys and summaries\n * @returns Object containing JSON schema and chat messages array\n * @private\n */\n private buildChoiceRequest(\n query: string,\n items: Array<{ key: string; summary: string }>,\n ): { schema: JSONObject; messages: ChatMessage[] } {\n const keys = items.map((x) => x.key);\n const schema: JSONObject = buildChoiceSchema(keys);\n const messages = buildChoiceMessages(query, items);\n return { schema, messages };\n }\n\n /**\n * Invoke the LLM and return the parsed map of candidate scores.\n *\n * Calls the configured LLM client with the messages, JSON schema, model config,\n * and user ID. Returns null if the response is invalid or missing.\n *\n * @param messages - Chat messages (system + user) to send to LLM\n * @param schema - Strict JSON schema defining expected response structure\n * @param userId - Optional user identifier for provider abuse monitoring\n * @returns Map of candidate keys to numeric scores, or null if response invalid\n * @private\n */\n private async fetchEvaluations(\n messages: ChatMessage[],\n schema: JSONObject,\n userId?: string,\n ): Promise<Record<string, { explanation: string; score: number }> | null> {\n const config: LlmCallConfig = {\n model: this.resolveModel(),\n reasoningEffort: \"medium\",\n timeoutMs: this.cfg.timeoutMs,\n };\n const { data } = await this.llm.call<Record<string, { explanation: string; score: number }>>(\n messages,\n schema,\n config,\n userId ?? this.ctx.userId,\n );\n\n if (data == null || typeof data !== \"object\") return null;\n return data as Record<string, { explanation: string; score: number }>;\n }\n\n /**\n * Invoke the LLM and return boolean relevancy decisions.\n *\n * @param messages - Chat messages to send\n * @param schema - Strict JSON schema defining expected response structure\n * @param userId - Optional user id\n * @returns Map of candidate keys to filter decisions, or null if invalid\n * @private\n */\n private async fetchFilterDecisions(\n messages: ChatMessage[],\n schema: JSONObject,\n userId?: string,\n ): Promise<Record<string, { explanation: string; isRelevant: boolean }> | null> {\n const config: LlmCallConfig = {\n model: this.resolveModel(),\n reasoningEffort: \"medium\",\n timeoutMs: this.cfg.timeoutMs,\n };\n const { data } = await this.llm.call<\n Record<string, { explanation: string; isRelevant: boolean }>\n >(messages, schema, config, userId ?? this.ctx.userId);\n\n if (data == null || typeof data !== \"object\") return null;\n return data as Record<string, { explanation: string; isRelevant: boolean }>;\n }\n\n /**\n * Invoke the LLM and return a single selected key.\n *\n * @param messages - Chat messages to send\n * @param schema - Strict JSON schema defining expected response structure\n * @param userId - Optional user id\n * @returns Choice result, or null if invalid\n * @private\n */\n private async fetchChoice(\n messages: ChatMessage[],\n schema: JSONObject,\n userId?: string,\n ): Promise<{ explanation: string; selectedKey: string } | null> {\n const config: LlmCallConfig = {\n model: this.resolveModel(),\n reasoningEffort: \"medium\",\n timeoutMs: this.cfg.timeoutMs,\n };\n const { data } = await this.llm.call<{ explanation: string; selectedKey: string }>(\n messages,\n schema,\n config,\n userId ?? this.ctx.userId,\n );\n\n if (data == null || typeof data !== \"object\") return null;\n const record = data as Record<string, unknown>;\n const explanation = typeof record.explanation === \"string\" ? record.explanation : \"\";\n const selectedKey = typeof record.selectedKey === \"string\" ? record.selectedKey : \"\";\n if (selectedKey === \"\") return null;\n return { explanation, selectedKey };\n }\n\n /**\n * Apply boolean filter decisions while preserving input order.\n *\n * @param items - Candidates with unique keys\n * @param decisions - Map of candidate keys to decisions\n * @returns Filtered items with explanations\n * @private\n */\n private applyFilter(\n items: Array<{ item: T; idx: number; key: string; summary: string }>,\n decisions: Record<string, { explanation: string; isRelevant: boolean }>,\n ): Array<{ item: T; explanation: string }> {\n const kept: Array<{ item: T; explanation: string }> = [];\n for (const it of items) {\n const decision = decisions[it.key];\n if (decision?.isRelevant === true) {\n kept.push({\n item: it.item,\n explanation: typeof decision.explanation === \"string\" ? decision.explanation : \"\",\n });\n }\n }\n return kept;\n }\n\n /**\n * Process a single batch of candidates through the LLM filter.\n *\n * @param query - Query to filter against\n * @param batch - Prepared candidates\n * @param options - Optional userId override\n * @returns Filtered items (stable order), with explanations\n * @private\n */\n private async processFilterBatch(\n query: string,\n batch: Array<{ item: T; idx: number; baseKey: string; summary: string }>,\n options?: { userId?: string },\n ): Promise<Array<{ item: T; explanation: string }>> {\n const keyed = this.ensureUniqueKeys(batch);\n const { schema, messages } = this.buildFilterRequest(query, keyed);\n const decisions = await this.fetchFilterDecisions(messages, schema, options?.userId);\n if (decisions == null) {\n return keyed.map(({ item }) => ({ item, explanation: \"\" }));\n }\n\n return this.applyFilter(keyed, decisions);\n }\n\n /**\n * Process a batch of candidates and choose a single winner.\n *\n * @param query - Query to choose against\n * @param batch - Candidates with unique keys\n * @param userId - Optional userId override\n * @returns Winner item with explanation\n * @private\n */\n private async processChoiceBatch(\n query: string,\n batch: Array<{ item: T; idx: number; key: string; summary: string }>,\n userId?: string,\n ): Promise<{ item: T; explanation: string }> {\n const { schema, messages } = this.buildChoiceRequest(query, batch);\n const choice = await this.fetchChoice(messages, schema, userId);\n if (choice == null) {\n return { item: batch[0]!.item, explanation: \"\" };\n }\n\n const winner = batch.find((x) => x.key === choice.selectedKey);\n if (!winner) {\n return { item: batch[0]!.item, explanation: choice.explanation };\n }\n\n return { item: winner.item, explanation: choice.explanation };\n }\n\n /**\n * Apply relevancy threshold filtering and stable sorting.\n *\n * Scores are clamped to the configured score range, then filtered to keep only items with\n * score > threshold. Results are sorted by score descending, with ties\n * preserving original input order for deterministic results.\n *\n * @param items - Candidates with unique keys\n * @param scores - Map of candidate keys to LLM-assigned scores\n * @returns Filtered and sorted array of original items\n * @private\n */\n private rankAndFilter(\n items: Array<{ item: T; idx: number; key: string; summary: string }>,\n evaluations: Record<string, { explanation: string; score: number }>,\n ): Array<{ item: T; explanation: string; score: number }> {\n const threshold = this.cfg.relevancyThreshold;\n const scored = items.map(({ item, idx, key }) => ({\n item,\n idx,\n explanation:\n typeof evaluations[key]?.explanation === \"string\" ? evaluations[key].explanation : \"\",\n score: clamp(\n evaluations[key]?.score ?? this.cfg.minScore,\n this.cfg.minScore,\n this.cfg.maxScore,\n ),\n }));\n\n const filtered = scored.filter(({ score }) => score > threshold);\n const sorted = filtered.sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score;\n return a.idx - b.idx;\n });\n return sorted;\n }\n\n /**\n * Process a single batch of candidates through the LLM.\n *\n * Ensures unique keys, builds the request payload, fetches scores from the LLM,\n * and returns filtered and sorted results. On any error or null response from\n * the LLM, returns items in their original order as a fallback.\n *\n * @param query - The search query to evaluate candidates against\n * @param batch - Batch of prepared candidates to process\n * @param userId - Optional user identifier for provider abuse monitoring\n * @returns Ranked and filtered items, or original order on error\n * @private\n */\n private async processBatch(\n query: string,\n batch: Array<{ item: T; idx: number; baseKey: string; summary: string }>,\n options?: { userId?: string },\n ): Promise<Array<{ item: T; explanation: string }>> {\n const keyed = this.ensureUniqueKeys(batch);\n const { schema, messages } = this.buildRequest(query, keyed);\n const evaluations = await this.fetchEvaluations(messages, schema, options?.userId);\n if (evaluations == null) {\n return keyed.map(({ item }) => ({ item, explanation: \"\" }));\n }\n\n const ranked = this.rankAndFilter(keyed, evaluations);\n return ranked.map(({ item, explanation }) => ({ item, explanation }));\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["failedGeneration", "Groq"]
|
|
7
|
+
}
|