Skip to content

Real-inference model gateway: whisper.cpp, Python Whisper, and OpenAI backends - #2

Open
dedmonwalkin wants to merge 1 commit into
claude/launch-readinessfrom
claude/whisper-gateway
Open

Real-inference model gateway: whisper.cpp, Python Whisper, and OpenAI backends#2
dedmonwalkin wants to merge 1 commit into
claude/launch-readinessfrom
claude/whisper-gateway

Conversation

@dedmonwalkin

Copy link
Copy Markdown
Owner

Summary

Stacked on #1. Replaces the mock-only inference story with a real, self-contained model gateway — the first milestone of the local-government demo plan.

Both accessibility-lite and accessibility-broadcast already call MODEL_HTTP_BASE_URL with the same contract, so this one service gives both products real transcription.

What's here

  • model-gateway/ — zero-dependency Node service with four backends selected by WHISPER_BACKEND:
    • whispercpp — local static binary, audio never leaves the machine (the public-sector privacy story)
    • python-whisper — the reference openai-whisper CLI (pip/brew), zero build step
    • openai — hosted API, pay per minute
    • mock — clearly labelled placeholder, confidence pinned to 0
  • Honesty by design: only transcription runs a model; scene is always empty (no fabricated visual descriptions); every response carries _backend so mock output can never pass as real
  • Docker: multi-stage static whisper.cpp build with the ggml model baked in; non-root user; compose service with no host port exposure
  • SSRF opt-in: MODEL_HTTP_ALLOW_PRIVATE=true lets the guard reach the operator-configured gateway on the compose-internal network — scoped to MODEL_HTTP_BASE_URL only, never user-supplied URLs
  • Optional GATEWAY_API_KEY bearer auth (timing-safe compare), 32MB body cap, /v1/translate proxy to LibreTranslate or honest 501

Verification

End-to-end on real audio: synthesized council-meeting speech → upload → ffmpeg chunk → HTTP provider → gateway (python-whisper) → WebVTT:

WEBVTT

00:00:00.000 --> 00:00:08.240
The motion to approve the sidewalk repair budget passes five votes to two. The next item on the agenda is the public library renovation.

Word-for-word correct, confidence 0.84.

  • 84 tests passing (9 new: contract shape, mock labelling, auth, pass-through honesty, 501 translate, 404)
  • Full-stack smoke test with real Whisper (above)
  • Docker image build (whisper.cpp compile ~5 min; not run in this session)
  • Broadcast repo smoke against the same gateway

🤖 Generated with Claude Code

New model-gateway/ service implementing the HTTP contract both
accessibility-lite and accessibility-broadcast already speak
(MODEL_HTTP_BASE_URL) — run one gateway, both products get real
transcription.

Gateway
- Backends: whispercpp (local, static binary), python-whisper
  (pip/brew openai-whisper), openai (API), mock (labelled, confidence 0)
- Only transcription runs a model; perception/accessibility endpoints are
  structural transforms of caller data, scene stays empty — the gateway
  never fabricates content. Every response tagged _backend.
- Confidence semantics per backend: mean token p (whisper.cpp),
  exp(mean avg_logprob) (python/OpenAI), 0 (mock)
- Optional GATEWAY_API_KEY bearer auth (timing-safe), 32MB body cap,
  /v1/translate proxies to LIBRETRANSLATE_URL or returns 501
- Dockerfile: multi-stage static whisper.cpp build with ggml model baked
  in, non-root user

App integration
- MODEL_HTTP_ALLOW_PRIVATE opt-in lets the SSRF guard reach an
  operator-configured gateway on a private network (docker-compose);
  scoped to MODEL_HTTP_BASE_URL, never user-supplied URLs
- docker-compose: model-gateway service wired in, no host port exposure
- npm run start:gateway; README + .env.example updated

Verified end to end on real audio: synthesized council-meeting speech →
upload → ffmpeg chunking → HTTP provider → gateway (python-whisper) →
word-for-word correct WebVTT captions.

9 new gateway tests; suite now 84 passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 645ce8ef12

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread model-gateway/server.js
return sendJson(res, 404, { error: 'Not found' });
}
} catch (err) {
const status = err.statusCode || (err instanceof SyntaxError ? 400 : 500);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep backend parse failures as server errors

When a valid request reaches a backend that then throws a SyntaxError while parsing its own JSON output (for example malformed whisper.cpp output, an invalid OpenAI JSON response, or a bad LibreTranslate response), this maps it to HTTP 400 even though the client body was not invalid. That misreports real inference/upstream failures as bad requests, so clients and logs cannot distinguish operator/backend failures from caller mistakes; only JSON body parsing should set an explicit 400 status.

Useful? React with 👍 / 👎.

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