/** * Query generation (M5): profile -> buyer prompts, via ONE guarded judge call. * * Pure helpers (intent selection, parsing, competitor exclusion, pack assembly) * are separated from the single I/O function `generateQueries`, so the whole * module unit-tests against a mocked judge with no network. */ import { CostGuard } from '../costs.js'; import { type BrandProfile, type JudgeClient, type QueryIntent, type QueryPack } from '../types.js'; /** The full intent set, in stable order; `local` is gated on geo. */ export declare const QUERY_INTENTS: QueryIntent[]; /** * Which axis a pack is written on (M5a). A `maker` is measured on what buyers * should buy; a `retailer` on WHERE they should buy it, because product * questions are answered with manufacturers and never name shops. */ export type QueryAxis = 'maker' | 'retailer'; /** * The axis for a profile. Anything not explicitly a retailer - `maker`, * `service`, an unrecognized value, or an older profile with no field at all - * takes the maker path, which is the behavior that shipped before M5a. A new * business type therefore degrades to the old default instead of silently * rewriting someone's prompt pack. */ export declare function axisFor(profile: BrandProfile): QueryAxis; /** Default number of prompts in a generated pack. */ export declare const DEFAULT_QUERY_COUNT = 20; /** * Relative weight of each intent when allocating a capped pack, per axis. * * For a maker, `best-of` is the most rewrite-stable prompt form across engines * (Profound fanout study), so it earns the largest share. For a retailer, * `where-to-buy` leads harder still, because it is the only intent whose * answers name shops at all. A zero weight belongs to an intent the axis never * activates, so it can never be picked. */ export declare const INTENT_WEIGHTS: Record>; /** * Which intents apply to this profile. `local` needs a geo; the two * axis-specific intents are gated the same way - a retailer asks `where-to-buy` * and never `best-of`, whose answers name manufacturers rather than shops. */ export declare function activeIntents(profile: BrandProfile): QueryIntent[]; /** * Split `target` slots across `intents` in proportion to {@link INTENT_WEIGHTS}, * so `best-of` gets the biggest share. Largest-remainder rounding keeps the sum * exactly `target`; ties break by the intents' given order (best-of first). */ export declare function intentQuotas(target: number, intents: QueryIntent[], axis?: QueryAxis): Record; /** * Remove prompts that name any competitor. Boundary-aware and Unicode-safe * (case/accent-folded), so "Quest" does not knock out "question" but "C++", * ".NET", and non-Latin names are still caught (shared matcher). */ export declare function excludeCompetitors(prompts: string[], competitors: string[]): string[]; /** * Parse judge output into prompts keyed by intent. Expects a JSON object * `{ intent: string[] }` (optionally fenced); unknown intents are ignored and * every requested intent is present (empty when the model omitted it). */ export declare function parseIntentQueries(text: string, intents: QueryIntent[]): Record; export interface BuildPackInput { domain: string; byIntent: Record; intents: QueryIntent[]; competitors: string[]; target: number; /** Which axis to weight by (M5a); defaults to the maker axis. */ axis?: QueryAxis; generatedAt: string; } /** * Assemble a capped, competitor-free {@link QueryPack} from parsed prompts. * * Prompts are interleaved by {@link INTENT_WEIGHTS} using smooth weighted * round-robin, so best-of is over-represented but SPREAD through the pack rather * than clustered. This matters because a cost-capped or otherwise truncated run * asks prompts in pack order and stops partway: an interleaved pack keeps that * partial run balanced across intents (a front-loaded best-of block would skew * the headline score toward one intent). Each prompt gets a stable `q` id. */ export declare function buildQueryPack(input: BuildPackInput): QueryPack; export interface GenerateDeps { judge: JudgeClient; guard: CostGuard; /** Projected setup cost for authorization; defaults to a per-model estimate. */ projectedCostUsd?: number; } export interface GenerateOptions { /** Target pack size (default {@link DEFAULT_QUERY_COUNT}). */ count?: number; generatedAt: string; } export interface GenerateResult { pack: QueryPack; /** Set when generation was skipped or failed (cap hit or judge error). */ skipped?: string; } /** Generate a buyer-query pack for a profile via one guarded judge call. */ export declare function generateQueries(profile: BrandProfile, deps: GenerateDeps, opts: GenerateOptions): Promise;