Files
waggle-os/packages/agent/src/model-class-router.ts
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

58 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Deterministic capability-aware model selection — the portable subset of
* OpenHuman's model routing (docs/analysis/openhuman-adoption-2026-06-28.md §3.B).
*
* Scope is deliberately NARROW (§3.2): a fixed allowlist of KNOWN-lightweight
* internal calls (compaction summary, classification, short extraction) is
* routed to the cheapest ready model — Haiku on the built-in Anthropic proxy by
* default. There is NO task-complexity classifier (that would need its own
* cost-bearing model and risks mis-routing real reasoning); call sites declare
* `class: 'lightweight'` a priori, so routing stays deterministic.
*
* The universal win is Haiku-on-proxy: ~1012× cheaper than Sonnet and it
* materializes for a vanilla FREE user with no local model, protecting the
* Waggle-funded built-in proxy. A `privacyRequired` call is NEVER downgraded to
* a cloud budget model — it keeps the caller's model (or a provided on-device
* model), so "sensitive work stays on-device" can never silently leak to cloud.
*/
export type ModelClass = 'lightweight' | 'reasoning' | 'general';
export interface ModelClassOpts {
/** Declared workload class for this call (named `class` to match LlmCallInput). */
class?: ModelClass;
/** When true, never route to a cloud budget model; keep on-device. */
privacyRequired?: boolean;
/** Cheap model for lightweight internal calls (default applied by the caller). */
lightweightModel?: string;
/** A ready on-device model id (e.g. 'ollama/llama3.1'), when one is configured. */
localModel?: string;
}
/** The default cheap model for lightweight internal calls on the built-in proxy. */
export const LIGHTWEIGHT_MODEL = 'claude-haiku-4-5';
/**
* Resolve the effective model for a known internal call. Pure + synchronous.
*
* - privacyRequired → the on-device model; FAILS CLOSED (throws) when none is
* configured, rather than silently falling back to cloud.
* - class lightweight → the cheap model, when one is supplied.
* - otherwise → unchanged.
*
* Callers that may pass `privacyRequired` must handle the throw (e.g. return an
* error result without making a network call).
*/
export function resolveModelForClass(requestedModel: string, opts: ModelClassOpts = {}): string {
if (opts.privacyRequired) {
if (!opts.localModel) {
throw new Error('privacyRequired: no on-device model is configured — refusing cloud fallback');
}
return opts.localModel;
}
if (opts.class === 'lightweight' && opts.lightweightModel) return opts.lightweightModel;
// 'reasoning' and 'general' are passthrough today; 'reasoning' is reserved for a
// future frontier/extended-thinking upsell once telemetry justifies the cost.
return requestedModel;
}