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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## [Unreleased]

## [1.36.0] - 2026-07-13

### Added

- On a fixture-miss passthrough (record/proxy mode), aimock can inject its own configured upstream provider key instead of forwarding a caller's dummy placeholder key. Every static-key provider aimock proxies is wired end-to-end, each with an independent `AIMOCK_PROVIDER_<PROVIDER>_KEY` env var applied with the provider-correct wire scheme: `Authorization: Bearer` for OpenAI (`_OPENAI_KEY`), OpenRouter (`_OPENROUTER_KEY`), Cohere (`_COHERE_KEY`), Grok/xAI (`_GROK_KEY`), and Ollama (`_OLLAMA_KEY`); `x-api-key` for Anthropic (`_ANTHROPIC_KEY`); `x-goog-api-key` for Gemini (`_GEMINI_KEY`, also used for Gemini Interactions) and Veo (`_VEO_KEY`); `api-key` for Azure OpenAI (`_AZURE_KEY`); `xi-api-key` for ElevenLabs (`_ELEVENLABS_KEY`); and `Authorization: Key` for fal.ai (`_FAL_KEY`). Injection fires only when the caller credential is absent or dummy-prefixed (`sk-aimock-`, overridable via `AIMOCK_DUMMY_KEY_MARKER`); a real caller key is always forwarded unchanged, an empty-string env var is treated as unset, and with no built-in key configured the feature is inert. Signed/exchanged credentials — AWS Bedrock (SigV4) and Vertex AI (OAuth) — are never rewritten (#293)

## [1.35.1] - 2026-07-06

### Fixed
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,28 @@ Private and link-local addresses (loopback, RFC1918, CGNAT, cloud metadata, ULA,

On replay, `turnIndex` is a non-fatal disambiguator, not a hard reject gate: a content-matching fixture is served even when its scripted `turnIndex` differs from the request's assistant-message count. This kills false "no fixture matched" misses for multi-bubble agent runs (multi-step agents emit several assistant bubbles per logical turn). When a served fixture diverges from its scripted `turnIndex`, the match diagnostic carries `turnIndexRelaxed: true` and aimock logs a one-shot warning (at the `warn` log level — silent by default). To restore the legacy strict behavior where a defined `turnIndex` must equal the assistant count exactly, set `AIMOCK_STRICT_TURN_INDEX=1`. The record path is always strict regardless of this flag.

### aimock-owned upstream keys — `AIMOCK_PROVIDER_*_KEY`

In record or `--proxy-only` mode, aimock forwards the caller's auth header to the real provider unchanged. If your tests can only send a dummy placeholder key (e.g. an SDK that refuses to start without a non-empty API key), aimock can inject its own configured upstream key on a fixture-miss passthrough so the proxied call actually authenticates. Each provider has an independent env var, and the key is applied with the provider-correct wire scheme:

| Env var | Provider | Injected header |
| -------------------------------- | -------------------------------- | ----------------------------- |
| `AIMOCK_PROVIDER_OPENAI_KEY` | OpenAI | `Authorization: Bearer <key>` |
| `AIMOCK_PROVIDER_OPENROUTER_KEY` | OpenRouter | `Authorization: Bearer <key>` |
| `AIMOCK_PROVIDER_COHERE_KEY` | Cohere | `Authorization: Bearer <key>` |
| `AIMOCK_PROVIDER_GROK_KEY` | Grok (xAI) | `Authorization: Bearer <key>` |
| `AIMOCK_PROVIDER_OLLAMA_KEY` | Ollama (Cloud / bearer-gated) | `Authorization: Bearer <key>` |
| `AIMOCK_PROVIDER_ANTHROPIC_KEY` | Anthropic | `x-api-key: <key>` |
| `AIMOCK_PROVIDER_GEMINI_KEY` | Gemini (and Gemini Interactions) | `x-goog-api-key: <key>` |
| `AIMOCK_PROVIDER_VEO_KEY` | Veo | `x-goog-api-key: <key>` |
| `AIMOCK_PROVIDER_AZURE_KEY` | Azure OpenAI | `api-key: <key>` |
| `AIMOCK_PROVIDER_ELEVENLABS_KEY` | ElevenLabs | `xi-api-key: <key>` |
| `AIMOCK_PROVIDER_FAL_KEY` | fal.ai | `Authorization: Key <key>` |

`gemini-interactions` reuses `AIMOCK_PROVIDER_GEMINI_KEY` (same upstream API as Gemini). An empty-string value is treated as unset.

This is opt-in and backward-compatible: with no key configured the feature is inert and the caller's header passes through as-is. Injection fires only when the caller sent no credential **or** a dummy credential prefixed with `sk-aimock-` (overridable via `AIMOCK_DUMMY_KEY_MARKER`); a real caller key never starting with that marker is always forwarded verbatim, so the caller overrides aimock. Signed and exchanged credentials — AWS Bedrock (SigV4) and Vertex AI (OAuth) — are never rewritten and always forwarded unchanged. (Azure's static `api-key` is injected; a real Microsoft Entra ID `Authorization: Bearer` token from the caller is never dummy-prefixed, so it too passes through verbatim.)

## Framework Guides

Test your AI agents with aimock — no API keys, no network calls: [LangChain](https://aimock.copilotkit.dev/integrate-langchain) · [CrewAI](https://aimock.copilotkit.dev/integrate-crewai) · [PydanticAI](https://aimock.copilotkit.dev/integrate-pydanticai) · [LlamaIndex](https://aimock.copilotkit.dev/integrate-llamaindex) · [Mastra](https://aimock.copilotkit.dev/integrate-mastra) · [Google ADK](https://aimock.copilotkit.dev/integrate-adk) · [Microsoft Agent Framework](https://aimock.copilotkit.dev/integrate-maf)
Expand Down
108 changes: 108 additions & 0 deletions docs/record-replay/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,114 @@ <h2>Header Forwarding</h2>
<code>recordedTimings</code> block (streaming frame timings) — never the request headers.
</p>

<h2 id="own-provider-keys">aimock-Owned Upstream Keys</h2>
<p>
By default, aimock forwards the caller's auth header to the upstream provider unchanged.
If your tests can only send a dummy placeholder key — for example, an SDK that refuses to
start without a non-empty API key — aimock can inject its <em>own</em> configured upstream
key on a fixture-miss passthrough so the recorded/proxied call actually authenticates.
This is <strong>opt-in and backward-compatible</strong>: with no key configured the
feature is fully inert and the caller's header is forwarded as-is.
</p>
<p>
Set one or more of these env vars to aimock's real upstream keys. Each is independent —
configure only the providers you record against:
</p>
<table class="endpoint-table">
<thead>
<tr>
<th>Env var</th>
<th>Provider</th>
<th>Injected header</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>AIMOCK_PROVIDER_OPENAI_KEY</code></td>
<td>OpenAI</td>
<td><code>Authorization: Bearer &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_OPENROUTER_KEY</code></td>
<td>OpenRouter</td>
<td><code>Authorization: Bearer &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_COHERE_KEY</code></td>
<td>Cohere</td>
<td><code>Authorization: Bearer &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_GROK_KEY</code></td>
<td>Grok (xAI)</td>
<td><code>Authorization: Bearer &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_OLLAMA_KEY</code></td>
<td>Ollama (Cloud / bearer-gated)</td>
<td><code>Authorization: Bearer &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_ANTHROPIC_KEY</code></td>
<td>Anthropic</td>
<td><code>x-api-key: &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_GEMINI_KEY</code></td>
<td>Gemini (and Gemini Interactions)</td>
<td><code>x-goog-api-key: &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_VEO_KEY</code></td>
<td>Veo</td>
<td><code>x-goog-api-key: &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_AZURE_KEY</code></td>
<td>Azure OpenAI</td>
<td><code>api-key: &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_ELEVENLABS_KEY</code></td>
<td>ElevenLabs</td>
<td><code>xi-api-key: &lt;key&gt;</code></td>
</tr>
<tr>
<td><code>AIMOCK_PROVIDER_FAL_KEY</code></td>
<td>fal.ai</td>
<td><code>Authorization: Key &lt;key&gt;</code></td>
</tr>
</tbody>
</table>
<p>
<code>gemini-interactions</code> reuses <code>AIMOCK_PROVIDER_GEMINI_KEY</code> (same
upstream API as Gemini). An empty-string value is treated as unset.
</p>
<p>Injection fires only when both of these hold:</p>
<ul>
<li>
The request is a fixture-<em>miss</em> passthrough (record mode or
<code>--proxy-only</code>).
</li>
<li>
The caller sent <strong>no</strong> credential for that provider,
<strong>or</strong> sent a <em>dummy</em> credential prefixed with
<code>sk-aimock-</code>. A caller credential that does not start with the marker is
treated as a real key and forwarded verbatim — the caller always overrides aimock.
</li>
</ul>
<p>
The dummy marker prefix is overridable via <code>AIMOCK_DUMMY_KEY_MARKER</code> for setups
that mint placeholder keys under a different prefix. Gemini is injected as an
<code>x-goog-api-key</code> header only (no query-param <code>?key=</code> rewrite).
</p>
<p>
<strong>Signed and exchanged credentials are never rewritten.</strong> AWS Bedrock
(SigV4), Vertex AI, and Azure AD carry OAuth/signed auth rather than a simple bearer or
api-key header, so aimock always forwards their credentials unchanged regardless of these
env vars.
</p>

<h2>Strict Mode</h2>
<p>
When <code>--strict</code> is enabled, an unmatched request returns
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@copilotkit/aimock",
"version": "1.35.1",
"version": "1.36.0",
"description": "Mock infrastructure for AI application testing — LLM APIs, image generation, image editing, text-to-speech, transcription, audio translation, audio generation, video generation, embeddings, MCP tools, A2A agents, AG-UI event streams, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.",
"license": "MIT",
"keywords": [
Expand Down
Loading
Loading