Skip to content

feat: add OpenAI-compatible API mock with SDK verification harness - #13

Merged
outofcoffee merged 2 commits into
mainfrom
worktree-openai-mock
Jul 25, 2026
Merged

feat: add OpenAI-compatible API mock with SDK verification harness#13
outofcoffee merged 2 commits into
mainfrom
worktree-openai-mock

Conversation

@outofcoffee

Copy link
Copy Markdown
Contributor

Summary

Adds a rest/openai example: a drop-in mock of the common OpenAI API endpoints, so any "OpenAI compatible" AI harness (chat, tool calling, streaming) can be pointed at Imposter instead of the real API.

OpenAI's API is REST + Server-Sent Events (no websockets), so this is a plain rest plugin mock with externalised response files.

What it covers

Method & path Behaviour
GET /v1/models list models
GET /v1/models/{model} single model (echoes the id)
POST /v1/chat/completions routes by request body → streaming (SSE) when stream: true, a get_weather tool call when the body mentions the weather, otherwise a plain assistant message
POST /v1/completions legacy text completion
POST /v1/embeddings embedding vector

The requested model is captured and templated back into every response.

Verification harness

rest/openai/harness/ drives the mock with the real openai Python SDK and asserts every endpoint round-trips (models, chat, tool calls, streaming reassembly, embeddings). Handy as a smoke test or in CI, and it doubles as proof the mock is genuinely OpenAI-compatible. Verified locally against openai-python 2.48.0 — all checks pass.

Notes

  • The mock ignores the Authorization header, so any API key works.
  • Streaming replies are complete, correctly-framed SSE that the SDKs parse and reassemble; the whole stream is sent at once rather than dribbled token-by-token over time (fine for wiring up a client, not a substitute for real time-to-first-token behaviour).

https://claude.ai/code/session_01NGFQaVnKN4wv322agMownn

Add a `rest/openai` example: a drop-in mock of the common OpenAI API
endpoints for any "OpenAI compatible" AI harness to run against.

It's a plain rest plugin mock with externalised response files. The one
`POST /v1/chat/completions` endpoint routes to three response files by
matching the request body: streaming (Server-Sent Events) when
`stream: true`, a `get_weather` tool call when the body mentions the
weather, and a plain assistant message otherwise. The requested model is
captured and templated back into each response. Also covers
`GET /v1/models`, `GET /v1/models/{model}`, legacy `POST /v1/completions`
and `POST /v1/embeddings`.

A `harness/` subdir drives the mock with the real `openai` Python SDK and
asserts every endpoint round-trips, so users can verify the mock (and use
it in CI). Verified against openai-python 2.48.0.

Claude-Session: https://claude.ai/code/session_01NGFQaVnKN4wv322agMownn
The three POST /v1/chat/completions resources shared a path but their body
conditions overlapped, so a single request could match more than one. Clients
that stream by default hit this: a "weather" message is both `stream: true`
and contains "weather", matching the streaming and tool-call resources
equally. Imposter then warns ("more than one resource matched") and picks one
arbitrarily — returning the stream instead of the tool call.

Make the conditions disjoint: streaming matches `$.stream == "true"`; the tool
and plain resources additionally require `$.stream != "true"`, and plain
requires the body not contain "weather". Verified on both the native and JVM
engines: all six stream/weather combinations route to exactly one resource with
no match warnings, and the SDK harness still passes.

Also quote `value: "true"` — unquoted it is a YAML boolean, which the string
body matcher never equals (a cross-engine footgun).

Claude-Session: https://claude.ai/code/session_01NGFQaVnKN4wv322agMownn
@outofcoffee
outofcoffee marked this pull request as ready for review July 25, 2026 23:37
@outofcoffee
outofcoffee merged commit da19040 into main Jul 25, 2026
2 checks passed
@outofcoffee
outofcoffee deleted the worktree-openai-mock branch July 25, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant