You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Complete #274 blocks feature across all providers + blocks-only first-class + docs (#285)
## What
Completes the (still-unreleased) #274 `blocks` fixture-ordering feature
so it works **uniformly across every provider**, and makes blocks-only
fixtures **first-class**. Builds on #283 (blocks v1) and #282 (async
video), both already on `main` under `[Unreleased]`.
No version bump — stays `1.34.0`. The dedicated `chore: release v1.35.0`
PR owns the bump.
## Why
#283 shipped `blocks` for a subset of providers and left the record-side
capture + several provider builders out of scope, with blocks-only
fixtures treated as by-design-unsupported. To ship the feature as
*complete* for 1.35.0, it needs to: capture/replay block order on the
remaining providers, accept a fixture that is *just* `{ blocks: [...]
}`, and document the feature where authors actually look.
## Changes
**Feature completion**
- **First-class blocks-only fixtures** — a response may be `{ blocks:
[...] }` with no `content`/`toolCalls`; the shared recognizer was
relaxed *additively* (legacy recognition byte-identical).
- **Record side** — block-order capture added to the Cohere, Bedrock
(event-stream), and Gemini-Interactions collapsers;
`normalizeToolArguments` zero-arg ("{}") fix. Gemini-Interactions is
args-only by design (step-index can't reconcile arrival-order blocks).
- **Replay builders** — Cohere, Bedrock (invoke), Bedrock-Converse, and
Gemini-Interactions now honor `blocks` ordering (streaming +
non-streaming where the wire allows).
- **toolCall `arguments`** may be a JSON object or a string (objects
auto-stringify), consistently across top-level toolCalls and block
toolCalls.
- **Validation hardening** — `validateBlocks` rejects empty-text blocks
and warns on blocks/content divergence.
**Completion gaps found + fixed during code review** (each with a local
red→green on the real surface)
- Ollama **non-streaming** dropped the entire payload for a blocks-only
fixture (F0 made it newly reachable) — now backfills content/tool_calls
from blocks.
- Realtime + Gemini-Live WS surfaces silently dropped blocks-only
payloads — now honor blocks.
- Programmatic `addFixture` with object block args returned HTTP 500
(bypassed normalize) — `resolveFixtureBlocks` now tolerates object args.
- A valid `{ content: "", blocks: [...] }` fixture spuriously
hard-errored at load — suppressed when non-empty blocks drive output.
- Gemini-Live empty-text block leaked past `truncateAfterChunks` and
shifted recorded-timing indices — guard now skips emission cleanly.
**Docs**
- `docs/fixtures/index.html`: per-provider observability matrix,
blocks-only authoring, reasoning cells.
- Authoring on-ramps: `write-fixtures` skill/command, `docs/examples`
(worked **tool-first** example), `docs/record-replay`, root + pytest
READMEs.
- A runnable `fixtures/examples/llm/blocks-tool-first.json` with a
permanent loadability test.
## Wire limitations (documented, not gaps)
OpenAI-chat, Ollama, and Cohere **non-streaming** expose separate
content/tool_calls fields, so order is not observable on the wire —
payload is delivered, order is a no-op. Captured in the observability
matrix.
## Test plan
- Full suite: **144 files / 4244 tests** pass
- `tsc --noEmit`, `eslint`, `prettier --check`, `build`, `test:exports`
(node10/node16-CJS/ESM/bundler) all green
- Every behavioral fix verified red→green on the real surface (live
endpoint / real WS / real loader), independently re-confirmed in review
## Known non-blocking follow-ups
Pre-existing items (predate this branch) and minor polish are tracked
but intentionally out of scope: a shared stringify helper to de-dup the
three normalize sites, JSON-serializability check for object args at
load time, and assorted pre-existing collapser/request-converter edge
cases.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,10 @@
11
11
- Optional `blocks` array on the combined `content` + `toolCalls` fixture shape lets a fixture express ordered text/tool-call blocks (`{type:"text",text}` | `{type:"toolCall",name,arguments,id?}`); when present it takes precedence over `{content, toolCalls}` for stream order, enabling tool-first and interleaved ordering. Legacy `{content, toolCalls}` fixtures are unchanged (#274)
12
12
- All five providers stream combined responses in fixture block order: Anthropic, OpenAI Responses, and Gemini are fully observable; Ollama is best-effort (clients may reassemble positionally); OpenAI chat-completions emits in order but is degenerate (`delta.content`/`delta.tool_calls` are separate channels the client merges) (#274)
13
13
- Recorder captures block order and persists `blocks` only when the recorded upstream stream was genuinely tool-first or interleaved; text-first streams keep the legacy `{content, toolCalls}` shape so golden recordings round-trip byte-identically (#274)
14
+
- Blocks-only fixtures are first-class: a non-empty `blocks` array is a complete response shape on its own, with no `content`/`toolCalls` required — builders derive the aggregate from the blocks and `validateFixtures()` accepts the shape (#274)
15
+
- Block ordering is now honored on replay across the remaining providers — Cohere (streaming), Bedrock invoke, Bedrock Converse, and Gemini Interactions — so a tool-first or interleaved fixture streams its tool call ahead of its text wherever the wire protocol can express it (#274)
16
+
- Record-side block capture extends to the Cohere and Bedrock collapsers; Gemini Interactions normalizes tool-call arguments only and does not reorder blocks on capture (its step-index protocol can't reconcile arrival-order blocks at record time), while replay still honors a hand-authored `blocks` array (#274)
17
+
-`validateBlocks` rejects a malformed `blocks` array at load time — non-array, non-object entries, a `type` other than `text`/`toolCall`, a non-string or empty-string text block, or a `toolCall` block missing a name or carrying non-JSON arguments — and warns when a fixture carries both `blocks` and divergent `content`/`toolCalls`, so a bad array never reaches a builder mid-dispatch (#274)
Copy file name to clipboardExpand all lines: README.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -64,6 +64,7 @@ Run them all on one port with `npx @copilotkit/aimock --config aimock.json`, or
64
64
-**[Docker + Helm](https://aimock.copilotkit.dev/docker)** — Container image and Helm chart for CI/CD
65
65
-**[Vitest & Jest Plugins](https://aimock.copilotkit.dev/test-plugins)** — Zero-config `useAimock()` with auto lifecycle and env patching
66
66
-**[Response Overrides](https://aimock.copilotkit.dev/fixtures)** — Control `id`, `model`, `usage`, `finishReason` in fixture responses
67
+
-**[Ordered Blocks](https://aimock.copilotkit.dev/fixtures#ordered-blocks)** — A `blocks` array streams text and tool calls in any order (tool-first or interleaved); blocks-only fixtures are first-class, and the recorder captures order from genuinely tool-first/interleaved streams
67
68
-**[Streaming Usage Chunks](https://aimock.copilotkit.dev/streaming-physics)** — `stream_options.include_usage` support emits a final chunk with token counts, matching OpenAI's streaming usage protocol
68
69
-**[Rate Limiting Headers](https://aimock.copilotkit.dev/chaos-testing)** — `x-ratelimit-*` headers on every response and `Retry-After` on 429 errors for testing retry/backoff logic
69
70
-**Zero dependencies** — Everything from Node.js builtins
{ <spanclass="key">"type"</span>: <spanclass="str">"text"</span>, <spanclass="key">"text"</span>: <spanclass="str">"Let me check the weather in NYC for you."</span> }
0 commit comments