Show HN: LLM-mock – Record and replay OpenAI/Anthropic calls in pytest (v1.0) LLM-mock, a pytest plugin that records and replays OpenAI and Anthropic API calls, has been released in version 1.0. The tool intercepts HTTP calls to LLM APIs, allowing developers to record real responses once and replay them in tests without API keys, costs, or non-deterministic flakiness. It works with the Anthropic and OpenAI SDKs without requiring changes to application code. pytest plugin to mock OpenAI and Anthropic API calls — record real responses once, replay them in tests forever. No API key needed in CI, no cost per run, no flaky non-determinism. Record once against the real API run locally with your API key with llm mock mode="record", fixture="tests/fixtures/summarize" : result = my pipeline "Summarize this document..." Replay in tests — no API key, no cost, deterministic @pytest.mark.llm replay fixture="summarize" def test summarize : result = my pipeline "Summarize this document..." assert "key points" in result Works with the Anthropic SDK claude- models and the OpenAI SDK gpt- models out of the box — no changes to your application code required. Cost. A CI pipeline hitting real LLM APIs can cost dollars per run at scale. Flakiness. LLM outputs are non-deterministic — even temperature=0 varies across model versions. Speed. Replayed fixtures return instantly; no network round-trip. Offline. Tests run without credentials in CI, on a plane, in a container. llm-mock intercepts at the HTTP transport layer via httpx / respx — your production code is never touched. Fixtures are plain JSON files you commit to git, diff in PRs, and refresh on demand. pip install llm-mock Or install from source: git clone https://github.com/autopost/llm-mock.git cd llm-mock pip install -e . Runtime dependencies: httpx , respx , pydantic python my app/pipeline.py import anthropic client = anthropic.Anthropic def summarize text: str - str: message = client.messages.create model="claude-sonnet-4-6", max tokens=100, messages= {"role": "user", "content": f"Summarize: {text}"} , return message.content 0 .text pipeline.py has zero knowledge of llm-mock. No imports, no changes needed. Create a small script or a dedicated test that runs with mode="record" . You need a real API key for this step. python record fixtures.py from llm mock import llm mock from my app.pipeline import summarize with llm mock mode="record", fixture="tests/fixtures/summarize" : result = summarize "Long article about climate change..." print result real response from the API ANTHROPIC API KEY=sk-... python record fixtures.py This creates tests/fixtures/summarize.json . Commit this file to git. Use the pytest decorator — no with block needed inside the test: python tests/test pipeline.py import pytest from my app.pipeline import summarize @pytest.mark.llm replay fixture="summarize" def test summarize : result = summarize "Long article about climate change..." assert "climate" in result pytest no API key needed, runs offline, instant The decorator auto-discovers the fixture path relative to the test file — fixture="summarize" looks for tests/fixtures/summarize.json when the test lives in tests/ . llm-mock intercepts the httpx call the Anthropic SDK makes internally and returns the saved response — your test code calls summarize exactly as it would in production. Alternative: use the context manager directly if you need more control: python from llm mock import llm mock def test summarize : with llm mock mode="replay", fixture="tests/fixtures/summarize" : result = summarize "Long article about climate change..." assert "climate" in result If you change the prompt, update the model, or want to refresh fixtures: ANTHROPIC API KEY=sk-... python record fixtures.py overwrites old fixture git add tests/fixtures/summarize.json git commit -m "refresh summarize fixture" A complete working example from scratch. pip install llm-mock echo 'export ANTHROPIC API KEY=sk-ant-api03-...' .env echo '.env' .gitignore Create try record.py : python import anthropic from llm mock import llm mock client = anthropic.Anthropic with llm mock mode="record", fixture="fixtures/hello" : message = client.messages.create model="claude-haiku-4-5-20251001", max tokens=64, messages= {"role": "user", "content": "Say hello in one sentence."} , print "Response:", message.content 0 .text print "Fixture saved to fixtures/hello.json" source .env && .venv/bin/python try record.py You should see the real response printed and fixtures/hello.json created. llm-mock list tests/fixtures/hello Create try replay.py : python import anthropic from llm mock import llm mock client = anthropic.Anthropic api key="fake-key" key is irrelevant in replay with llm mock mode="replay", fixture="fixtures/hello" : message = client.messages.create model="claude-haiku-4-5-20251001", max tokens=64, messages= {"role": "user", "content": "Say hello in one sentence."} , print "Replayed:", message.content 0 .text .venv/bin/python try replay.py The exact same response is returned instantly — no network call made. python tests/test hello.py import anthropic import pytest client = anthropic.Anthropic api key="fake-key" @pytest.mark.llm replay fixture="hello" def test hello : message = client.messages.create model="claude-haiku-4-5-20251001", max tokens=64, messages= {"role": "user", "content": "Say hello in one sentence."} , assert message.content 0 .text replayed from fixtures/hello.json .venv/bin/pytest tests/test hello.py -v Inspect and manage fixture files from the terminal. Note:activate your virtual environment first so llm-mock is on your PATH: source .venv/bin/activate Or run it directly with .venv/bin/llm-mock