Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/fixtures/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@ <h2>Match Fields</h2>
<td>hasToolResult</td>
<td>boolean</td>
<td>
<code>true</code> when at least one <code>role: "tool"</code> message is present;
<code>false</code> when none are. Stateless alternative to ordering fixtures by
<code>true</code> when the current turn (the messages after the last
<code>role: "user"</code> message) contains a <code>role: "tool"</code> result;
<code>false</code> when it does not. Stateless alternative to ordering fixtures by
<code>toolCallId</code>. See <a href="/multi-turn">Multi-Turn Conversations</a>
</td>
</tr>
Expand Down
26 changes: 17 additions & 9 deletions docs/multi-turn/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ <h2>How matching works across turns</h2>
</p>

<p>
Two additional fields inspect the <em>full</em> message array rather than just the tail:
Two additional fields derive their value from the request shape rather than from a single
tail message:
</p>
<ul>
<li>
Expand All @@ -100,9 +101,16 @@ <h2>How matching works across turns</h2>
entirely from the request content, not from a server-side counter.
</li>
<li>
<code>hasToolResult</code> checks whether <em>any</em> <code>role: "tool"</code> message
exists in the request. <code>true</code> means &ldquo;this request carries tool
results&rdquo;; <code>false</code> means &ldquo;no tools have executed yet.&rdquo;
<code>hasToolResult</code> checks whether the <strong>current turn</strong> &mdash; the
messages after the last <code>role: "user"</code> message &mdash; contains a
<code>role: "tool"</code> result. <code>true</code> means &ldquo;a tool has executed in
this turn&rdquo;; <code>false</code> means &ldquo;no tool result yet this turn.&rdquo;
It is scoped to the current turn, not the whole conversation &mdash; that is what keeps
the leg-1 tool-call fixture (<code>false</code>) and the leg-2 narration fixture
(<code>true</code>) working on <em>every</em> turn of a multi-turn session, because
earlier turns&rsquo; tool results no longer force <code>true</code> forever. (If the
request carries no <code>user</code> message at all, it falls back to scanning the whole
conversation.)
</li>
</ul>

Expand Down Expand Up @@ -166,9 +174,9 @@ <h3>turnIndex &mdash; match by conversation depth</h3>

<h3>hasToolResult &mdash; match by tool execution state</h3>
<p>
<code>hasToolResult</code> checks whether the request contains any
<code>role: "tool"</code> messages. For a simple two-step tool round, this is often
simpler than <code>turnIndex</code>:
<code>hasToolResult</code> checks whether the <em>current turn</em> (the messages after
the last <code>role: "user"</code> message) contains a <code>role: "tool"</code> result.
For a simple two-step tool round, this is often simpler than <code>turnIndex</code>:
</p>

<div class="code-block">
Expand Down Expand Up @@ -365,8 +373,8 @@ <h2>
</td>
<td><code>hasToolResult</code></td>
<td>
Boolean check for <code>role: "tool"</code> presence. Stateless. Does not require
pinning a specific <code>tool_call_id</code>.
Boolean check for <code>role: "tool"</code> presence in the current turn. Stateless.
Does not require pinning a specific <code>tool_call_id</code>.
</td>
</tr>
<tr>
Expand Down
20 changes: 11 additions & 9 deletions docs/record-replay/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -636,9 +636,9 @@ <h2>Fixture Auto-Generation</h2>
<code>inputText</code>), the normalized model becomes <code>model</code>, and for chat
requests the recorder also writes the multi-turn disambiguators
<code>turnIndex</code> (the number of assistant messages already in the conversation) and
<code>hasToolResult</code> (whether a tool-result message is present). If no match
criteria can be derived (e.g., empty messages), the fixture is saved to disk with a
warning but not registered in memory.
<code>hasToolResult</code> (whether the current turn &mdash; the messages after the last
user message &mdash; contains a tool result). If no match criteria can be derived (e.g.,
empty messages), the fixture is saved to disk with a warning but not registered in memory.
</p>

<h2 id="model-aware-recording">Model-Aware Recording</h2>
Expand Down Expand Up @@ -1040,11 +1040,11 @@ <h2 id="recording-multi-turn-conversations">Recording Multi-Turn Conversations</
<strong>last user message</strong> (<code>match.userMessage</code>), the normalized
<code>model</code>, and two shape disambiguators read off the request itself:
<code>turnIndex</code> (how many assistant messages the conversation already contains) and
<code>hasToolResult</code> (whether a tool-result message is present). Embedding requests
key on the input text instead. There is no history-based fingerprinting of the full
request body &mdash; but because a growing conversation carries a growing
<code>turnIndex</code>, successive turns that happen to end with the same user message
still record as <em>distinct</em> fixtures.
<code>hasToolResult</code> (whether the current turn &mdash; the messages after the last
user message &mdash; contains a tool result). Embedding requests key on the input text
instead. There is no history-based fingerprinting of the full request body &mdash; but
because a growing conversation carries a growing <code>turnIndex</code>, successive turns
that happen to end with the same user message still record as <em>distinct</em> fixtures.
</p>

<div class="code-block">
Expand All @@ -1063,7 +1063,9 @@ <h2 id="recording-multi-turn-conversations">Recording Multi-Turn Conversations</
<span class="prop">userMessage</span>: <span class="fn">getTextContent</span>(lastUser.content),
<span class="prop">model</span>: <span class="fn">normalizeModelName</span>(request.model),
<span class="prop">turnIndex</span>: request.messages.filter((m) => m.role === <span class="str">"assistant"</span>).length,
<span class="prop">hasToolResult</span>: request.messages.some((m) => m.role === <span class="str">"tool"</span>),
<span class="cm">// Scoped to the current turn (messages after the last user message), so it</span>
<span class="cm">// shares the exact predicate the matcher uses &mdash; see router.ts.</span>
<span class="prop">hasToolResult</span>: <span class="fn">currentTurnHasToolResult</span>(request.messages),
};
<span class="cm">// Capture context from X-AIMock-Context header if present</span>
<span class="kw">if</span> (request._context) match.<span class="prop">context</span> = request._context;
Expand Down
Loading