/** * Schematic generator for behavioral guideline batches. * * Port of boocontext-audit/src/generation.ts — abstract LLM batch caller * with temperature retry and structured output per batch type. */ import { type GenerationInfo } from './matching.js'; // ─── Output types per batch ─── export interface ObservationalOutput { checks: { guideline_id: string; condition: string; rationale: string; applies: boolean; }[]; } export interface ActionableOutput { checks: { guideline_id: string; condition: string; action: string; rationale: string; applies: boolean; }[]; } export interface PreviouslyAppliedOutput { checks: { guideline_id: string; condition: string; action_segment: string; rationale: string; is_still_applicable: boolean; }[]; } export interface DisambiguationOutput { source_guideline_id: string; rationale: string; enriched_action: string; targets: string[]; } export interface ResponseAnalysisOutput { guideline_id: string; condition: string; was_followed: boolean; rationale: string; } // ─── Batch output map ─── export interface BatchOutputMap { observational: ObservationalOutput; actionable: ActionableOutput; previously_applied: PreviouslyAppliedOutput; disambiguation: DisambiguationOutput; response_analysis: ResponseAnalysisOutput; } export type BatchTypeKey = keyof BatchOutputMap; export type OutputForBatch = BatchOutputMap[T]; // ─── SchematicGenerator ─── export abstract class SchematicGenerator { constructor(public modelName: string) {} abstract generate( prompt: string, hints?: Record, ): Promise<{ content: TSchema; info: GenerationInfo; }>; } /** * Default stub implementation that returns empty results. * Replace with a real LLM caller in production. */ export class DefaultSchematicGenerator implements SchematicGenerator { constructor( public modelName: string, public defaultTemperature = 0.7, ) {} async generate( _prompt: string, hints?: Record, ): Promise<{ content: unknown; info: GenerationInfo }> { const temperature = (hints?.temperature as number) ?? this.defaultTemperature; return { content: {}, info: { model: this.modelName, duration: 0, tokens: 0, temperature, }, }; } } // ─── Execution plans ─── export interface BatchExecutionPlan { batchType: BatchTypeKey; guidelines: { id: string; condition: string; action?: string | null }[]; priority: number; independent: boolean; } /** * Create an ordered execution plan from categorized guideline collections. * Groups are sorted by priority: previously_applied (fastest) first, * then observational, actionable, disambiguation, low-criticality last. */ export function createExecutionPlan( observational: { id: string; condition: string }[], actionable: { id: string; condition: string; action: string }[], previouslyApplied: { id: string; condition: string; action?: string | null }[], disambiguationGroups: { source: string; targets: string[]; enrichedAction: string }[], lowCriticality: { id: string; condition: string }[], ): BatchExecutionPlan[] { const plans: BatchExecutionPlan[] = []; if (observational.length > 0) { plans.push({ batchType: 'observational', guidelines: observational.map((g) => ({ id: g.id, condition: g.condition })), priority: 1, independent: true, }); } if (actionable.length > 0) { plans.push({ batchType: 'actionable', guidelines: actionable.map((g) => ({ id: g.id, condition: g.condition, action: g.action, })), priority: 2, independent: true, }); } if (previouslyApplied.length > 0) { plans.push({ batchType: 'previously_applied', guidelines: previouslyApplied.map((g) => ({ id: g.id, condition: g.condition, action: g.action, })), priority: 0, independent: true, }); } if (disambiguationGroups.length > 0) { plans.push({ batchType: 'disambiguation', guidelines: disambiguationGroups.map((g) => ({ id: g.source, condition: g.enrichedAction, })), priority: 3, independent: true, }); } if (lowCriticality.length > 0) { plans.push({ batchType: 'observational', guidelines: lowCriticality.map((g) => ({ id: g.id, condition: g.condition })), priority: 10, independent: true, }); } return plans.sort((a, b) => a.priority - b.priority); } /** * Compute retry temperatures: base + 0.2 * attempt. * Provides progressive temperature increases for failed calls. */ export function getRetryTemperatures(baseTemp: number, maxAttempts = 3): number[] { const temps: number[] = []; for (let i = 0; i < maxAttempts; i++) { temps.push(baseTemp + i * 0.2); } return temps; }