From 8d1ea736efd1aaf054d7f8ea01e7824a7b39279a Mon Sep 17 00:00:00 2001 From: Jordan Ritter Date: Tue, 3 Mar 2026 09:06:45 -0800 Subject: [PATCH 1/2] Add project website in docs/ for GitHub Pages Dark terminal-inspired single-page site. Two-panel hero demo shows the fixture JSON on the left and an animated terminal on the right typing out the server startup, request matching, and streamed response. Includes features grid, real-world code examples from E2E tests, MSW comparison table with architecture diagram, and CNAME for mock-openai.copilotkit.dev. --- docs/CNAME | 1 + docs/index.html | 1226 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1227 insertions(+) create mode 100644 docs/CNAME create mode 100644 docs/index.html diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 00000000..3ce79fb7 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +mock-openai.copilotkit.dev diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..69d22dd5 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,1226 @@ + + + + + + mock-openai — Deterministic OpenAI mock server for testing + + + + + + + + + + + + + + + +
+
+
+ + Zero dependencies · Node.js builtins only +
+ +

+ Deterministic OpenAI mock server for testing +

+ +

+ Real HTTP server. Real SSE streams. Fixture-driven responses. + Drop-in replacement for OpenAI — any process on the machine can reach it. +

+ + + +
+ $ + npm install @copilotkit/mock-openai + +
+ + +
+
+ +
+
+
+ fixtures/chat.json +
+
+
{
+  "fixtures": [
+    {
+      "match": {
+        "userMessage": "capital of France"
+      },
+      "response": {
+        "content": "The capital of France is Paris."
+      }
+    }
+  ]
+}
+
+ + +
+
+
+ Terminal +
+
+
+
+
+
+
+
+ + +
+
+ +

Everything you need to test AI integrations

+

+ Built for E2E test suites where multiple processes — your app, agent workers, + framework runtimes — all need to hit the same mock endpoint. +

+ +
+
+
+

Real HTTP Server

+

Runs on an actual port. Any process on the machine can reach it — Next.js, Mastra, LangGraph, Agno, anything that speaks HTTP.

+
+
+
📡
+

Authentic SSE Streams

+

Chat Completions API and Responses API — byte-for-byte identical to real OpenAI. Streaming and non-streaming modes.

+
+
+
📁
+

JSON Fixture Files

+

Define responses as JSON — one file per feature. Load a directory, load a file, or register fixtures programmatically.

+
+
+
🔧
+

Tool Call Support

+

Return tool calls with structured arguments. Match on tool names, tool result IDs, or write custom predicates.

+
+
+
💥
+

Error Injection

+

Queue one-shot errors — 429 rate limits, 503 outages, whatever. Fires once, then auto-removes itself.

+
+
+
📋
+

Request Journal

+

Every request recorded. Inspect messages, verify tool calls, assert on conversation history. HTTP and programmatic access.

+
+
+
+
+ + +
+
+ +

Fixture-driven. Zero boilerplate.

+ + +
+
+

Simple text responses

+

+ Match on the last user message — substring or regex. The fixture fires + when it matches, streaming SSE chunks just like the real API. +

+
    +
  • First-match-wins routing
  • +
  • Substring and RegExp matching
  • +
  • Configurable chunk size and latency
  • +
+
+
+
+ fixtures/chat.json + json +
+
{
+  "fixtures": [
+    {
+      "match": { "userMessage": "stock price of AAPL" },
+      "response": {
+        "content": "The current stock price of Apple Inc. (AAPL) is $150.25."
+      }
+    },
+    {
+      "match": { "userMessage": "capital of France" },
+      "response": {
+        "content": "The capital of France is Paris."
+      }
+    }
+  ]
+}
+
+
+ + +
+
+
+ fixtures/tools.json + json +
+
{
+  "fixtures": [
+    {
+      "match": { "userMessage": "one step with eggs" },
+      "response": {
+        "toolCalls": [{
+          "name": "generate_task_steps",
+          "arguments": "{\"steps\":[{\"description\":\"Crack eggs\"},{\"description\":\"Preheat oven\"}]}"
+        }]
+      }
+    },
+    {
+      "match": { "userMessage": "background color to blue" },
+      "response": {
+        "toolCalls": [{
+          "name": "change_background",
+          "arguments": "{\"background\":\"blue\"}"
+        }]
+      }
+    }
+  ]
+}
+
+
+

Tool call responses

+

+ Return structured tool calls that agent frameworks execute directly. + Used in production E2E tests for CopilotKit, Mastra, and LangGraph integrations. +

+
    +
  • Tool calls with JSON arguments
  • +
  • Match on tool name or tool result ID
  • +
  • Multi-tool-call responses
  • +
+
+
+ + +
+
+

Predicate-based routing

+

+ When substring matching isn't enough, use predicates. + Inspect the full request — system prompt flags, message history, model name, anything. +

+
    +
  • Inspect system prompt state flags
  • +
  • Route supervisor agents by conversation state
  • +
  • Combine with substring matching (AND logic)
  • +
+
+
+
+ e2e/mock-setup.ts + ts +
+
// Supervisor sees the same user message every time,
+// but system prompt contains state flags
+mock.addFixture({
+  match: {
+    predicate: (req) => {
+      const sys = req.messages
+        .find(m => m.role === "system");
+      return sys?.content
+        ?.includes("Flights found: false");
+    }
+  },
+  response: {
+    toolCalls: [{
+      name: "supervisor_response",
+      arguments: '{"next_agent":"flights_agent"}'
+    }]
+  }
+});
+
+
+ + +
+
+
+ e2e/global-setup.ts + ts +
+
import { MockOpenAI } from "@copilotkit/mock-openai";
+
+const mock = new MockOpenAI({ port: 5555 });
+
+// Load JSON fixture files
+mock.loadFixtureDir("./fixtures/openai");
+
+// Catch-all for tool results
+mock.addFixture({
+  match: {
+    predicate: (req) =>
+      req.messages.at(-1)?.role === "tool"
+  },
+  response: { content: "Done!" }
+});
+
+const url = await mock.start();
+
+// Every process on the machine can reach this
+process.env.OPENAI_BASE_URL = `${url}/v1`;
+process.env.OPENAI_API_KEY = "mock-key";
+
+
+

E2E global setup

+

+ Start the mock server once in Playwright's global setup. + All child processes — Next.js, agent workers, CopilotKit runtime — + inherit OPENAI_BASE_URL and hit the same server. +

+
    +
  • One server, many processes
  • +
  • JSON fixtures loaded from disk
  • +
  • Programmatic catch-alls for tool results
  • +
  • Universal fallback prevents 404 crashes
  • +
+
+
+
+
+ + +
+
+ +

mock-openai vs MSW

+

+ MSW is great for in-process API mocking. mock-openai is for when multiple + processes need to hit the same OpenAI endpoint. +

+ +
+ // MSW: only intercepts in the process that calls server.listen()
+ // mock-openai: real server on a real port — any process can reach it

+ Playwright test runner
+   └─ controls browser Next.js app (separate process)
+                                     └─ OPENAI_BASE_URL mock-openai :5555
+                                         ├─ Mastra agent workers
+                                         ├─ LangGraph workers
+                                         └─ CopilotKit runtime +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Capabilitymock-openaiMSW
Cross-process interceptionReal server ✓In-process only
Chat Completions SSEBuilt-in ✓Manual — build data/[DONE] yourself
Responses API SSEBuilt-in ✓Manual — MSW sse() uses wrong format
Fixture files (JSON)Yes ✓No — handlers are code-only
Request journalYes ✓No — track manually
Non-streaming responsesYes ✓Yes ✓
Error injection (one-shot)Yes ✓Yes (server.use)
CLI serverYes ✓No
DependenciesZero~300KB
+
+
+ + + + + + + + + From c6f019321d9bde141590df39eec43792fa82a2c8 Mon Sep 17 00:00:00 2001 From: Tyler Slaton Date: Tue, 3 Mar 2026 12:30:04 -0500 Subject: [PATCH 2/2] chore: release 0.1.0 Signed-off-by: Tyler Slaton --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a4048b95..ff83c62c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@copilotkit/mock-openai", - "version": "0.0.0", + "version": "0.1.0", "description": "Deterministic mock OpenAI server for testing", "license": "MIT", "packageManager": "pnpm@10.28.2",