/** * weight-eft.ts — Optional-dependency service wrapper around * `@metaharness/weight-eft` (agenticow / ADR-150 weight-eft slice). * * WHAT THIS SHIPS (and what it deliberately does NOT) * --------------------------------------------------- * This service turns ruflo's captured run transcripts into AUDITED TRAINING * DATA + a COST-PARETO measurement + a GPU TRAINING PLAN. It does NOT train a * model and it does NOT "reduce escalation": * - `runExport` → SFT (OpenAI chat) + DPO (TRL preference) JSONL + a guard * report (contamination / reward-hack / long-context). $0. * - `runPlan` → the GPU training plan + the exact `ruvllm microlora` * command a GPU host would run. $0 dry-run — never spawns. * - `runEval` → the cost-Pareto delta folded from two CascadeOutcome[]. $0. * - `runRemoteTrain` → an SSH-based remote-GPU invocation. DRY-RUN by default * (prints commands + read-only preflight); real compute only * behind explicit `execute && yes`. This is the ONLY path * that can spend, and it spends on the USER's host, not $0. * * HARD HONESTY RULE (do not overclaim): weight-eft's own `train` never spawns * (its README §Status), and no GPU tune has run here. `resolved` in the * captured archive is a PROXY (ruflo has no SWE-bench gold oracle) — the SFT * data-quality caveat stands. Nothing here claims ruflo "trains a model" as a * $0/local capability. * * ADR-150 GRACEFUL DEGRADATION * ---------------------------- * `@metaharness/weight-eft` is an OPTIONAL dependency. Every entry point loads * it via a dynamic import through `loadWeightEft()` and returns a structured * `{ degraded: true }` result when it is absent — never a throw. There is NO * static `import … from '@metaharness/weight-eft'` anywhere in this file; the * types below are LOCAL mirrors so tsc stays green with the package removed. * * @module services/weight-eft */ import type { RunTranscriptRecord } from '../ruvector/run-transcript-recorder.js'; export interface WeftToolCall { id: string; type: 'function'; function: { name: string; arguments: string; }; } export interface WeftChatMessage { role: 'system' | 'user' | 'assistant' | 'tool'; content: string | null; tool_calls?: WeftToolCall[]; tool_call_id?: string; name?: string; } export type WeftPolicyTier = 'cheap' | 'frontier'; /** Input contract for the exporter. Mirrors weight-eft's DarwinTrajectory. */ export interface DarwinTrajectory { instance_id: string; model: string; tier: WeftPolicyTier; resolved: boolean; messages: WeftChatMessage[]; model_patch: string; sample?: number; source?: string; } export interface WeftExportReport { totalTrajectories: number; excludedByHoldout: number; droppedOverLength: number; truncatedOverLength: number; droppedRewardHacked: number; sftRows: number; dpoRows: number; sftInstanceIds: string[]; dpoInstanceIds: string[]; notes: string[]; } export interface WeftCascadeOutcome { instance_id: string; cheapResolved: boolean; escalated: boolean; resolved: boolean; costUsd: number; } export interface WeftCostParetoDelta { base: unknown; adapter: unknown; cheapResolveLift: number; escalationRateReduction: number; costPerResolvedReduction: number; resolveRateDelta: number; verdict: string; } export interface WeftBaseModelSpec { id: string; paramsB: number; } export interface WeftTrainingPlan { config: unknown; command: string; summary: string; } /** The subset of the weight-eft module surface this service calls. */ interface WeightEftApi { exportTrainingData: (t: DarwinTrajectory[], o: { evalHoldout: string[]; maxTokens?: number; truncateOverLength?: boolean; dropRewardHacked?: boolean; }) => { sft: { messages: WeftChatMessage[]; }[]; dpo: unknown[]; report: WeftExportReport; }; sftToJsonl: (rows: { messages: WeftChatMessage[]; }[]) => string; dpoToJsonl: (rows: unknown[]) => string; costParetoDelta: (base: WeftCascadeOutcome[], adapter: WeftCascadeOutcome[]) => WeftCostParetoDelta; twoStagePlan: (base: WeftBaseModelSpec, sftPath: string, dpoPath: string, adapterPrefix: string) => { sft: WeftTrainingPlan; dpo: WeftTrainingPlan; }; } /** Injectable importer so tests can force the degraded path deterministically. */ export type WeightEftImporter = () => Promise; /** * Load `@metaharness/weight-eft` (optional dep). Returns the module or `null` * when it is absent/broken. Never throws. The specifier is held in a variable * so tsc does not statically resolve the package (keeps the build green when * the optional dep is removed — ADR-150). */ export declare function loadWeightEft(importer?: WeightEftImporter): Promise; export interface ArchiveBuildStats { total: number; resolved: number; byTier: Record; /** Honest breakdown: how many `resolved` booleans came from which proxy. */ byResolvedSource: Record; skipped: number; } export interface ArchiveBuildResult { trajectories: DarwinTrajectory[]; stats: ArchiveBuildStats; /** Load-bearing honesty banner surfaced to CLI/report output. */ proxyNote: string; } /** * Map captured ruflo run transcripts to the DarwinTrajectory[] contract the * weight-eft exporter codes against. PURE + synchronous — this is the seam the * archive-builder unit test exercises. * * A record is skipped (not thrown) when it lacks the minimum an exporter needs: * an instance_id and a non-empty messages array. `resolved` is copied verbatim * and its proxy provenance is tallied so the caller can print an honest note. */ export declare function buildArchiveFromRecords(records: RunTranscriptRecord[]): ArchiveBuildResult; export type ExportOutcome = { degraded: false; report: WeftExportReport; sftJsonl: string; dpoJsonl: string; sftRows: number; dpoRows: number; } | { degraded: true; reason: string; }; /** Run the weight-eft exporter over a DarwinTrajectory[] archive. $0. */ export declare function runExport(opts: { archive: DarwinTrajectory[]; evalHoldout?: string[]; maxTokens?: number; truncateOverLength?: boolean; dropRewardHacked?: boolean; importer?: WeightEftImporter; }): Promise; export type PlanOutcome = { degraded: false; base: WeftBaseModelSpec; sft: WeftTrainingPlan; dpo: WeftTrainingPlan; } | { degraded: true; reason: string; }; /** Emit the two-stage (SFT → on-policy DPO) GPU training plan. $0 dry-run. */ export declare function runPlan(opts: { base?: WeftBaseModelSpec; sftPath: string; dpoPath: string; adapterPrefix?: string; importer?: WeightEftImporter; }): Promise; export type EvalOutcome = { degraded: false; delta: WeftCostParetoDelta; } | { degraded: true; reason: string; }; /** Fold base + adapter CascadeOutcome[] into the cost-Pareto delta. $0. */ export declare function runEval(opts: { baseOutcomes: WeftCascadeOutcome[]; adapterOutcomes: WeftCascadeOutcome[]; importer?: WeightEftImporter; }): Promise; /** Default cheap-tier tune target — a 7B coder in the tunable [1,14]B band. */ export declare const DEFAULT_BASE_MODEL: WeftBaseModelSpec; export interface RemoteTrainArgs { /** SSH host or tailscale name (parameterized; never hard-coded). */ host: string; /** Base model id to tune. Default DEFAULT_BASE_MODEL.id. */ base?: string; /** Local path to the exported SFT jsonl. */ sftPath: string; /** Local path to the exported DPO jsonl. */ dpoPath: string; /** Local dir to fetch the trained LoRA adapter back into. Default .claude-flow/neural. */ adapterDir?: string; /** Remote working dir. Default ~/.ruflo-weft/. */ remoteWorkdir?: string; /** SSH user (default: current, i.e. no user@ prefix). */ sshUser?: string; /** SSH port. Default 22. */ sshPort?: number; /** Stable run id used in remote workdir + adapter names. Default derived. */ runId?: string; /** Adapter name prefix. Default 'ruflo-weft'. */ adapterPrefix?: string; } export interface RemoteStep { label: string; argv: string[]; } export interface RemoteTrainPlan { host: string; base: string; runId: string; remoteWorkdir: string; adapterDir: string; sftAdapter: string; dpoAdapter: string; /** Read-only reachability/capability probes (safe to run in dry-run). */ preflight: RemoteStep[]; /** The mutating steps: rsync data up, train, fetch adapter back. */ steps: RemoteStep[]; /** Rendered one-line commands for human display. */ humanCommands: string[]; } /** * Construct the exact ssh/rsync/ruvllm invocations a remote GPU tune would run. * PURE — no spawning, no filesystem, deterministic. This is the seam the * command-construction unit test exercises. The ruvllm commands mirror the * canonical `ruvllm microlora sft … && ruvllm microlora dpo --init-from …` * plan weight-eft emits, wrapped for SSH execution on the remote host. */ export declare function buildRemoteTrainInvocation(args: RemoteTrainArgs): RemoteTrainPlan; /** Minimal spawn result shape (subset of child_process.SpawnSyncReturns). */ export interface SpawnLike { status: number | null; stdout?: string; stderr?: string; error?: Error; } export type SpawnFn = (cmd: string, argv: string[]) => SpawnLike; export type RemoteTrainOutcome = { degraded: true; reason: string; } | { degraded: false; mode: 'dry-run' | 'refused' | 'preflight-failed' | 'executed'; plan: RemoteTrainPlan; preflight?: { label: string; ok: boolean; detail: string; }[]; steps?: { label: string; ok: boolean; detail: string; }[]; reason?: string; }; /** * Run (or, by default, DRY-RUN) a remote-GPU tune. SAFETY MODEL: * - DEFAULT (no execute): builds the plan, runs read-only preflight probes, * and returns the commands that WOULD run. NO rsync of data, NO training. * - execute && !yes → 'refused' (real GPU spend needs an explicit second gate). * - execute && yes → runs preflight; if unreachable → 'preflight-failed' * WITHOUT training; else runs rsync + ssh ruvllm sft/dpo + fetch-back. * - No `ssh` binary / host unreachable / any spawn error → structured * degraded/preflight-failed. NEVER throws. * * `spawn` is injected for testing so CI never touches a live host. */ export declare function runRemoteTrain(args: RemoteTrainArgs & { execute?: boolean; yes?: boolean; preflight?: boolean; spawn?: SpawnFn; }): Promise; export {}; //# sourceMappingURL=weight-eft.d.ts.map