/** * MockProvider — deterministic LLMProvider for tests + examples. * * Pattern: Adapter (GoF, Design Patterns ch. 4). * Role: Ports-and-Adapters outer ring (Cockburn, 2005) — implements the * LLMProvider port without calling out to a real LLM service. * Emits: N/A (adapters don't emit; recorders observe them). * * Two modes, same provider: * • DEFAULT (no latency options) — instant `complete()`, no streaming. * Use in unit tests; behavior unchanged from earlier revisions. * • REALISTIC (`thinkingMs` set) — randomised "thinking" latency before * the response, plus a `stream()` implementation that emits the * content word-by-word with `chunkDelayMs` between chunks. Lets * consumers SEE the run unfold (pauses, streaming tokens, tool * dispatch) instead of having every step finish in microseconds. * * Realistic mode is the right default for the playground / Lens demos: * a real OpenAI / Anthropic call takes 3–8 s, so the UX you build for * that timing is what matters. Use `MockProvider.realistic()` for the * common 3–8 s thinking + 30–80 ms per word streaming preset. */ import type { LLMChunk, LLMProvider, LLMRequest, LLMResponse } from '../types.js'; /** Either a fixed value (in ms) or a random `[min, max]` range (inclusive). */ export type LatencyMs = number | readonly [number, number]; /** * One scripted reply consumed in order from `MockProviderOptions.replies`. * String → plain text content; Partial → can include * `toolCalls`, `usage`, `stopReason` for tool-using ReAct loops. */ export type MockReply = string | Partial; export interface MockProviderOptions { readonly name?: string; /** Fixed response content. Overrides `respond` when set. */ readonly reply?: string; /** * Scripted replies for multi-turn / tool-using agents. Each entry * is consumed in order — iteration 1 reads `replies[0]`, iteration * 2 reads `replies[1]`, and so on. Use Partial to * inject `toolCalls`: * * ```ts * mock({ * replies: [ * { toolCalls: [{ id: '1', name: 'lookup', args: { id: 42 } }] }, * { content: 'Found it: refunds take 3 business days.' }, * ], * }); * ``` * * **Exhaustion semantics:** if the agent calls the LLM more times * than there are replies, `complete()` / `stream()` throw a clear * error. This makes mock-script bugs loud, not silent. Tune the * agent's `maxIterations` to bound the call count. * * Takes precedence over `reply` and `respond` when set. */ readonly replies?: readonly MockReply[]; /** * Build the response from the request. Returns either a plain * string (renders as content with no tool calls) or a partial * `LLMResponse` so consumers can simulate tool calls + multi-turn * loops without needing a separate `scripted()` helper. * * Default: echoes the last user message. */ readonly respond?: (req: LLMRequest) => string | Partial; /** * Simulated wall-clock delay per request (ms). * Pass a single number for a fixed delay or a `[min, max]` tuple for * a uniformly random delay (e.g. `[3000, 8000]` for "real LLM" * thinking time). Default 0 (instant). * * Aliased via `delayMs` for backward compatibility. */ readonly thinkingMs?: LatencyMs; /** Alias for `thinkingMs`. Kept for back-compat with prior revisions. */ readonly delayMs?: LatencyMs; /** * For `stream()`: delay between successive chunks (ms). Pass a * single number for a fixed delay or a `[min, max]` tuple for a * uniformly random delay per chunk (e.g. `[30, 80]` for typing-like * cadence). Default 30ms. * * Has no effect on `complete()`. */ readonly chunkDelayMs?: LatencyMs; /** Fixed stop reason to return. Default 'stop'. */ readonly stopReason?: string; /** Override usage counts returned. Default: chars/4 heuristic. */ readonly usage?: Readonly<{ input?: number; output?: number; cacheRead?: number; cacheWrite?: number; }>; } export declare class MockProvider implements LLMProvider { readonly name: string; private readonly reply?; private readonly replies?; private repliesCursor; private readonly respond; private readonly thinkingMs; private readonly chunkDelayMs; private readonly stopReason; private readonly usageOverride; constructor(options?: MockProviderOptions); /** * Reset the scripted-replies cursor. Useful when reusing one * `MockProvider` instance across multiple test scenarios — each * scenario can `provider.resetReplies()` to start from `replies[0]` * again. No-op when `replies` was not supplied. */ resetReplies(): void; /** * Convenience factory for the playground / Lens demo defaults: a * real-feel mock with 3–8 s of "thinking" before the response and * 30–80 ms per streamed word. Lets users observe pause/resume, * streaming, and tool dispatch happening live without hitting a * paid API. */ static realistic(options?: MockProviderOptions): MockProvider; complete(req: LLMRequest): Promise; /** * Streaming mode — emits the response content word-by-word so * consumers (Lens commentary, chat UIs) can render tokens as they * arrive. Tool calls land all at once on the final chunk because * that is how real providers (OpenAI, Anthropic) deliver them too. */ stream(req: LLMRequest): AsyncIterable; private buildResponse; /** * Resolve the next reply source for one `complete()` / `stream()` call. * Priority: `replies` (scripted, throws on exhaustion) → `reply` (single * fixed string) → `respond` (callback default). Replies are consumed * in order; the cursor is per-instance, not per-request. */ private consumeNextReply; } /** * Lowercase factory for `MockProvider` — matches the v1 `mock()` import * shape so docs and quick-starts stay copy-pasteable. Equivalent to * `new MockProvider(options)`. * * @example * import { Agent, defineTool } from 'agentfootprint' import { mock } from 'agentfootprint/llm-providers'; * * const agent = Agent.create({ provider: mock({ reply: 'hello' }) }) * .tool(defineTool({ name: 'echo', ... })) * .build(); */ export declare function mock(options?: MockProviderOptions): MockProvider; //# sourceMappingURL=MockProvider.d.ts.map