/** * Turn analyzer — one LLM call per turn that answers every routing-adjacent * question the harness needs to make BEFORE the main model runs. * * Why this exists: * Prior versions called separate classifiers for routing (what tier?) and * prefetch (is there a ticker?). Each additional harness decision tempted * us to add yet another classifier call (pushback? plan? needs-grounding?). * Each call adds ~500-800ms of serial latency; stack six of them and the * user waits multiple seconds before the main model even starts. * * This consolidates every LLM-decidable pre-turn question into a single * call with a structured JSON response. Net result: 1 classifier call per * turn (was 2), replacing multiple keyword rule engines (pushback regex, * shouldPlan keyword list, shouldCheckGrounding length gates). * * Principle: harness orchestrates, models decide. No keyword allowlists, * no length thresholds, no regex heuristics encoded in TypeScript. * * Budget discipline: * - Input capped at ~1500 chars across three anchors (current, prev reply, * session goal). Never the full history. * - Output capped at 128 tokens (compact single-line JSON). * - 2.5s hard timeout; on any failure, conservative default returned so * the main flow never blocks. * - 30s in-memory cache keyed on the three anchors so back-to-back near- * identical turns don't re-pay the latency. */ import type { ModelClient } from './llm.js'; import type { MarketCode } from '../trading/providers/standard-models.js'; import type { Tier } from '../router/index.js'; export interface TurnIntent { kind: 'ticker'; symbol: string; assetClass: 'stock' | 'crypto'; market?: MarketCode; wantNews: boolean; } export interface TurnAnalysis { tier: Tier; intent: TurnIntent | null; /** True for substantive multi-step engineering tasks worth a plan-then-execute split. */ needsPlanning: boolean; /** True when the user is correcting the previous assistant turn. */ isPushback: boolean; /** True when the user asks for current prices / today's state / recent news. */ asksForLiveData: boolean; } /** Test / reset helper. */ export declare function clearAnalyzerCache(): void; /** * Parse the analyzer's JSON output. Returns null on any structural issue; * caller falls back to conservative defaults. */ export declare function parseAnalysis(raw: string): TurnAnalysis | null; export interface AnalyzeOpts { lastAssistantText?: string; sessionGoal?: string; client: ModelClient; model?: string; signal?: AbortSignal; } /** * Analyze one turn. Always returns a TurnAnalysis — never throws. On any * failure path (timeout, parse error, empty response, gateway down) the * conservative default is returned so the main flow proceeds without the * harness's pre-decisions. The analyzer is a quality booster, not a * correctness requirement. */ export declare function analyzeTurn(userInput: string, opts: AnalyzeOpts): Promise;