Files
wisp/src/lib/generation/index.ts
T
NilsBriggen 7611ada001
CI / test (push) Successful in 20s
CI / build-apk (push) Has been skipped
CI / deploy-web (push) Successful in 33s
feat(phase5): optional generative RAG — ask your lectures, with citations
Strictly opt-in, gated, with the deterministic features as the always-present floor:
- GenerationEngine: WebLLM (Qwen2.5-1.5B, WebGPU, CDN-loaded) + BYO-key cloud
  (OpenAI-compatible); native stub. Pure grounding prompt builder (4 tests).
- rag.askLectures: retrieve Phase-1 hits -> grounded prompt -> answer with
  citations; refuses when nothing relevant; falls back to search-only when no
  engine is available. Never sends raw audio/transcripts — only question + snippets.
- aiStore (BYO key persisted in localStorage on web), Ask screen (answer +
  tappable citation chips that jump to the audio + honest "verify" disclaimer),
  Settings AI section (engine status + bring-your-own-key form).

279 tests green, 0 tsc errors, web export builds. ROADMAP phases 0-5 complete.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 15:56:57 +02:00

52 lines
1.8 KiB
TypeScript

// Public entry point for the optional generative ("ask your lectures") layer.
//
// `./engineImpl` is resolved by Metro to engineImpl.web.ts or
// engineImpl.native.ts by platform extension; the base engineImpl.ts re-export
// (web) is what TypeScript resolves for typechecking. Consumers call
// getGenerationEngine() and stay platform-agnostic.
//
// IMPORTANT: relative imports only inside src/lib (vitest has no '@/*' alias).
import type { CloudConfig, GenerationEngine } from './engine';
import { createCloudEngine } from './cloud';
import { webllm } from './engineImpl';
/**
* An engine that does nothing useful — represents "no generation backend here".
* isAvailable() is always false; generate()/loadModel() throw. Callers should
* check isAvailable() and fall back to the search-only path (no fake answers).
*/
export const noneEngine: GenerationEngine = {
kind: 'none',
label: 'No model',
async isAvailable(): Promise<boolean> {
return false;
},
async loadModel(): Promise<void> {
throw new Error('No generation engine is available.');
},
isLoaded(): boolean {
return false;
},
async generate(): Promise<string> {
throw new Error('No generation engine is available.');
},
};
/**
* Pick the generation engine.
*
* - If a cloud config WITH an apiKey is given, use the BYO-key cloud engine.
* - Otherwise return the platform on-device (webllm) engine. On a device without
* WebGPU (web) or on native, that engine's isAvailable() resolves false, which
* is how the "none" state is represented in practice — callers MUST check
* isAvailable() before loading/generating, and fall back to search-only.
*/
export function getGenerationEngine(cloud?: CloudConfig): GenerationEngine {
if (cloud?.apiKey) return createCloudEngine(cloud);
return webllm;
}
export { createCloudEngine, webllm };
export * from './engine';