summaryrefslogtreecommitdiffhomepage
path: root/README.md
blob: 47f228bfb8bb814194e1db8b51708e7d8f4f02aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# dispatch-adapter-tester

A deterministic playbook adapter for integration testing Dispatch agent flows.

`dispatch-adapter-tester` is a drop-in replacement for `dispatch-adapter-copilot`
that replays a scripted JSON sequence of AI responses, enabling deterministic
end-to-end testing of the full agent loop without calling any real LLM API.

## Installation

Add to your Gemfile (test group):

```ruby
group :test do
  gem "dispatch-adapter-tester", path: "reference/dispatch-adapter-tester"
end
```

## Usage

### Define a Playbook

A playbook is a JSON array of steps. Each step represents one response from the
fake AI:

```json
[
  {
    "step": 1,
    "type": "message",
    "content": "I will read the file now."
  },
  {
    "step": 2,
    "type": "tool_calls",
    "content": null,
    "tool_calls": [
      {
        "id": "tc_read_001",
        "name": "read_file",
        "arguments": { "path": "/some/file.rb" }
      }
    ]
  },
  {
    "step": 3,
    "type": "message",
    "content": "The file contains a Ruby class."
  }
]
```

### Step Schema

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `step` | integer | yes | Unique step identifier for debug output |
| `type` | string | yes | `"message"` or `"tool_calls"` |
| `content` | string/null | yes for message, optional for tool_calls | Text content of the response |
| `tool_calls` | array | yes for tool_calls | Array of tool call objects |

Each tool call object:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | yes | Tool call ID (for tracing/debugging) |
| `name` | string | yes | Name of the tool to invoke |
| `arguments` | object | yes | Arguments to pass to the tool |

### Create the Adapter

```ruby
adapter = Dispatch::Adapter::Tester::Playbook.new(
  steps_json: steps_json,  # JSON string or Ruby array
  model: "gpt-4",          # optional, for interface compat
  max_tokens: 200_000,     # optional, absorbed but unused
  min_request_interval: 0, # optional, absorbed but unused
  rate_limit: nil           # optional, absorbed but unused
)
```

### Use in Tests

```ruby
# Each call to chat consumes the next step
response = adapter.chat(messages, system: system_prompt, tools: registry.to_a)

# After the test, verify all steps were consumed
adapter.verify_all_consumed!

# Inspect what was passed to each chat call
adapter.call_log.each do |entry|
  puts entry[:step]     # the Step object
  puts entry[:system]   # system prompt passed
  puts entry[:messages] # messages array passed
  puts entry[:tools]    # tools array passed
end
```

### Test Helpers

| Method | Description |
|--------|-------------|
| `finished?` | Returns true when all steps have been consumed |
| `remaining_steps` | Number of unconsumed steps |
| `verify_all_consumed!` | Raises `UnconsumedStepsError` if steps remain |
| `reset!` | Resets to step 0 and clears the call log |
| `call_log` | Array of recorded chat call details |
| `current_index` | Current position in the playbook |

### Error Types

| Error | When |
|-------|------|
| `InvalidPlaybookError` | Malformed JSON, missing fields, invalid types |
| `PlaybookExhaustedError` | `chat` called after all steps consumed |
| `UnconsumedStepsError` | `verify_all_consumed!` called with steps remaining |

All errors include step IDs for debugging.

## See Also

- `APP_INTEGRATION.md` — Required changes in the host application
- `dispatch-adapter-copilot` — The real adapter this replaces