API reference

The complete public surface of ghostrun — everything in ghostrun.__all__. Anything not listed here is a private implementation detail and may change without notice.

Recording

ghostrun.record(func=None, *, model=None, mode=None, cache_dir=None)

Decorator. Intercepts LLM HTTP calls made inside the decorated test and records/replays them. Works on sync and async test functions.

@ghostrun.record(model="gpt-4o-mini")
def test_reply():
    ...

Usable bare (@ghostrun.record) or parameterized. model is accepted for documentation purposes and does not change interception behavior — it's not sent anywhere. mode and cache_dir override the resolved config for this test only.

ghostrun.recording(*, model=None, mode=None, cache_dir=None)

Context-manager form of record, for intercepting only part of a test:

def test_reply():
    with ghostrun.recording():
        reply = generate_reply(...)
    ghostrun.expect(reply).contains_intent("apology")

Assertions

ghostrun.expect(text, *, judge=None) -> Expectation

Entry point for semantic and deterministic assertions on a string. Raises TypeError if text isn't a str. Pass judge= to use a specific judge instance instead of the configured default (mainly useful in tests of your own code).

Expectation methods (all chainable, all raise SemanticAssertionError — a subclass of AssertionError — on failure):

MethodJudge-backed?Description
.contains_intent(intent)yestext expresses the given intent/content
.does_not_contain_intent(intent)yestext does not express it
.tone_is(tone)yesoverall tone matches
.matches(criterion)yesfree-form judge criterion escape hatch
.contains(substring)noexact substring match
.does_not_contain(substring)noexact substring absence
.is_valid_json()nojson.loads succeeds

ghostrun.expect_tool_calls(calls) -> ToolCallExpectation

Entry point for tool/function-call assertions. Normalizes OpenAI (function.arguments as a JSON string), Anthropic (input), and plain {"name", "arguments"} shapes automatically. calls may be None or empty.

ToolCallExpectation methods (all chainable):

MethodDescription
.called(name)some call matches name
.did_not_call(name)no call matches name
.called_once(name)exactly one call matches name
.call_count(n)exactly n calls total
.called_with(name, **kwargs)some call to name has these argument values (subset match)

.names (property) returns the list of called tool names; len(expectation) returns the total call count.

Configuration

ghostrun.configure(**kwargs) -> Config

Overrides configuration for the current process. Accepts the same field names as Config below. Returns the resulting Config.

ghostrun.configure(judge="echo", judge_model="qwen2.5:7b")

ghostrun.Config

Frozen dataclass holding all resolved settings:

FieldDefaultMeaning
mode"auto"auto | record | replay
cache_dir.ghostrun_cacheWhere recordings and verdicts live
judge"ollama"ollama | echo
judge_modelllama3.2:3bModel name passed to the judge backend
judge_base_urlhttp://localhost:11434Ollama daemon URL
judge_timeout60.0Seconds before a judge call times out
judge_cacheTrueCache judge verdicts (see Recording and replay)
judge_votes1Majority-vote grading count (see Semantic assertions)

ghostrun.get_config() -> Config

Returns the active, resolved configuration (defaults → .ghostrun.yaml → environment → configure() overrides), computing it once per process.

ghostrun.load_config(start=None) -> Config

Re-resolves configuration from scratch (defaults + file + env), ignoring any prior configure() call. Mainly useful in tests of your own configuration.

ghostrun.set_config(cfg) / ghostrun.reset_config()

Set the active config directly, or clear it so the next get_config() call re-resolves from defaults/file/env. Used internally by the pytest plugin's CLI-flag handling; rarely needed directly.

Exceptions

ExceptionRaised when
ghostrun.SemanticAssertionErrorAny Expectation/ToolCallExpectation assertion fails. Subclasses AssertionError.
ghostrun.CacheMissmode="replay" and no cached HTTP response or judge verdict exists for the request.
ghostrun.UnsupportedHttpxThe installed httpx version doesn't expose the hook the interceptor needs.
ghostrun.judge.ollama.JudgeUnavailableThe Ollama daemon is unreachable, the model isn't pulled, or it returned an error — not exported at the top level, imported from ghostrun.judge.ollama if you need to catch it specifically.

CLI

See Prompt regression tracking for ghostrun list / show / diff and Configuration for ghostrun doctor and ghostrun init.

Pytest plugin

Auto-registered via the pytest11 entry point — no conftest wiring needed. Flags: --ghostrun-record, --ghostrun-replay, --ghostrun-judge NAME, --ghostrun-snapshot NAME, --ghostrun-label TEXT. A ghostrun_record fixture is also available as a factory form of the decorator for tests that prefer explicit setup over @ghostrun.record.