Sequential / Stateful Responses

Use sequenceIndex in fixture match criteria to return different responses for the same query on each successive call. This enables testing multi-step agent conversations and retry logic.

How It Works

Unit Test: 2-Step Sequence

sequence.test.ts ts
const mock = new LLMock();
await mock.start();

mock.on({ userMessage: "plan", sequenceIndex: 0 }, { content: "Step 1: planning..." });
mock.on({ userMessage: "plan", sequenceIndex: 1 }, { content: "Step 2: done!" });

// First request → first response
const res1 = await fetch(`${mock.url}/v1/chat/completions`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "gpt-4",
    messages: [{ role: "user", content: "plan" }],
    stream: false,
  }),
});
const body1 = await res1.json();
expect(body1.choices[0].message.content).toBe("Step 1: planning...");

// Second request → second response
const res2 = await fetch(`${mock.url}/v1/chat/completions`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "gpt-4",
    messages: [{ role: "user", content: "plan" }],
    stream: false,
  }),
});
const body2 = await res2.json();
expect(body2.choices[0].message.content).toBe("Step 2: done!");

Fallback After Sequence Exhaustion

sequence-fallback.test.ts ts
// First call matches sequenceIndex 0, subsequent calls fall through to fallback
mock.on({ userMessage: "once", sequenceIndex: 0 }, { content: "only-first-time" });
mock.on({ userMessage: "once" }, { content: "fallback" });

// Request 1 → "only-first-time" (sequenceIndex 0 matches)
// Request 2 → "fallback" (sequenceIndex 0 won't match, falls through)

JSON Fixture

fixtures/sequence.json json
{
  "fixtures": [
    {
      "match": { "userMessage": "plan", "sequenceIndex": 0 },
      "response": { "content": "Step 1: planning..." }
    },
    {
      "match": { "userMessage": "plan", "sequenceIndex": 1 },
      "response": { "content": "Step 2: done!" }
    }
  ]
}

Sequence counters are per-fixture-match, not global. If you have fixtures matching "alpha" and "beta", their counters are tracked independently.