description: "Scriptable OpenAI-compatible fault server for testing LLM adapters and recovery policy without a provider key, for test authors and demos."
English | 中文
This package gives tests and demos a scriptable OpenAI-compatible HTTP/SSE endpoint, so they can exercise model-provider failures and successes without a provider key. Each accepted /chat/completions request consumes the next scripted behavior, including resets, stalls, malformed chunks, rate limits, server errors, completions, and tool calls. Test authors can run it with pnpm run mock:llm or call startMockLlmServer, which returns captured requests for assertions. Seeded random behavior supports reproducible mixed-failure stress runs.
This package lets a test or demo speak the provider protocol without a provider: start the server, script the wire behaviors you want to exercise, and point a real LLM adapter at its base URL.
Run the source entry from this repository:
pnpm run mock:llm \
--port 8000 \
--api-key mock-key \
--sequence partial_disconnect,success \
--partial-text "discard this half"
Point the shipping DeepSeek adapter at the server; it appends /chat/completions to the configured base:
DEEPSEEK_BASE_URL=http://127.0.0.1:8000/v1 \
DEEPSEEK_API_KEY=mock-key \
pnpm dsh --profile headless "test provider recovery"
The repository script writes JSONL to stdout: a ready record carries the /v1 base URL and random seed, followed by request/result records that name both the scripted behavior and the concrete behavior selected. The package exposes no installable binary.
--sequence is a comma-separated FIFO. Exhaustion returns a structured HTTP 500; --repeat-last explicitly reuses the last entry.
| Behavior | Wire result |
|---|---|
connection_reset |
Destroy the socket before HTTP headers |
stream_disconnect |
Send SSE headers, then reset before the first event |
partial_disconnect |
Send text deltas, then reset the socket |
stall |
Send SSE headers and remain idle until client/server cancellation |
empty |
Send a valid content-less stop and [DONE] |
empty_body / stream_eof / partial_eof |
End cleanly without the required [DONE] boundary |
malformed_json / malformed_event |
Send invalid SSE JSON or an invalid provider chunk shape |
rate_limit / server_error / service_unavailable |
Return retry-oriented 429/500/503 JSON errors |
auth_error / invalid_request / context_overflow / quota_exceeded |
Return terminal or separately recovered provider errors |
success / slow_success / reasoning_success |
Stream a complete text response, optionally delayed or preceded by reasoning |
tool_call_success / max_tokens |
Complete with a tool call or length finish |
wrong_content_type |
Send a valid SSE body under application/json |
random |
Select a concrete request behavior from weighted seeded randomness |
connection_refused is CLI-only and must be the first entry. It delays binding a caller-specified nonzero port, so requests during --listen-delay-ms receive a real TCP refusal; the remaining entries begin after the listener starts.
Use a repeating random entry for an open-ended mixed run:
pnpm run mock:llm \
--port 8000 \
--sequence random \
--repeat-last \
--seed 42 \
--random-weights 'success=60,slow_success=10,connection_reset=5,stream_disconnect=5,partial_disconnect=10,empty=5,server_error=5'
Omitting --seed generates one and prints it in the ready record. --random-weights accepts non-negative relative behavior=weight entries and requires at least one positive concrete behavior. The exported default is a success-heavy stress profile containing reset, disconnect, partial output, empty completion, stall, 429/5xx, clean truncation, and malformed JSON; it is test pressure, not an estimate of production incident frequency. connection_refused is excluded because a bound request handler cannot produce a true refusal. When random weights include stall, configure the client under test with a short stream-idle timeout so the scenario terminates promptly.
The CLI exposes --success-text, --partial-text, --reasoning-text, --chunk-size, --chunk-delay-ms, --disconnect-delay-ms, --retry-after-ms, --request-id, --tool-name, and --tool-arguments. Millisecond delays are bounded integers within Node's timer range; retryAfterMs must also be positive. The library accepts the same camel-case options. An optional exact apiKey validates Authorization: Bearer <token>; omission accepts any token.
--repeat-last or lengthen the sequence when a run needs more requests.Read these pages when the package-level contract is not enough. They move from the fault server to the adapter contract it exercises and the keyless alternative for recorded success transcripts.
None, as this test server substitutes provider wire behavior without invoking a real model.
None; requests terminate locally and never reach a provider cache.
These limits define when the server needs special care. They are current package constraints, not a task backlog.