# GrillKit – self-hosted AI technical interview trainer

> Source: <https://github.com/GrillKit/grillkit>
> Published: 2026-05-26 14:56:03+00:00

Open-source AI technical interview trainer. Configure an OpenAI-compatible model, practice from YAML question banks, and get real-time scoring with optional voice input and question audio.

[Quick start](#quick-start) · [Changelog](/GrillKit/grillkit/blob/main/CHANGELOG.md) · [Architecture](/GrillKit/grillkit/blob/main/ARCHITECTURE.md)

**Demo video** — full flow from setup to scored feedback

**Dashboard** — recent sessions and quick start

**Interview setup** — question-bank tracks, levels, topics, and session options

**Interview session** — real-time Q&A with AI scoring and final evaluation

**Interviews**— multi-track setup (Python, Database, System Design, …), several topics per session, WebSocket Q&A, AI scoring 1–5, up to 2 follow-ups per question**Timer**— optional per-round time limit; expired rounds score 0 and the session moves on** Voice**— offline Whisper dictation for answers; optional Piper TTS to read questions aloud** Question banks**— Python, Database/SQL, and System Design (`data/questions/{track}/`

), junior / middle / senior**Setup**— model catalog on`/config`

, interview locale (AI feedback language), Whisper/Piper downloads from the UI**Dashboard**— recent interview history on the home page** Persistence**— SQLite (`data/db/grillkit.db`

); Docker Compose on port 8000 with`./data`

volume

**Planned**

- Session-wide time limit (total interview duration)
- More question banks (Go, JavaScript, Java, C++, …)
- Code editor in the interview UI
- Custom question banks, PWA / standalone frontend

[Docker](https://docs.docker.com/get-docker/)and[Docker Compose](https://docs.docker.com/compose/install/)- API key for a cloud provider,
**or** a local OpenAI-compatible server (Ollama, vLLM, …)

```
git clone https://github.com/yourusername/grillkit.git
cd grillkit
docker compose up --build
```

Open [http://localhost:8000](http://localhost:8000).

Optional **question voice** (Piper TTS, same `app`

container):

- Run
`docker compose up`

(or`uv run uvicorn app.main:app`

for development). - Open
`/config`

, enable**Read questions aloud**, save. - On the Configuration page, use
**Download question voice** when prompted (~60 MB per locale voice from Hugging Face). - Start an interview — questions can play aloud; WAV cache lives under
`data/tts-cache/v2/{locale}/`

.

`./data`

on the host holds SQLite, `config.json`

, `llm_models.json`

, Whisper/Piper models, and TTS cache. Question banks, templates, and static files are in the image.

If bind-mounted `data/`

is not writable (Linux UID mismatch):

```
PUID=$(id -u) PGID=$(id -g) docker compose up --build
```

**Configuration**(`/config`

) — add one or more OpenAI-compatible models to the catalog, select an interview model, set interview locale; test connection, then save.**New interview**(`/setup`

) — enable one or more question-bank tracks (level per track), select multiple topics, set total question count (at least one per selected topic; interview locale is read-only from config).**Interview**(`/interview/{id}`

) — page loads history; answers and completion go over WebSocket.

Without saved provider config, `/setup`

redirects to `/config`

.

```
uv sync --extra dev
uv run uvicorn app.main:app --reload
```

Same first-time flow at [http://127.0.0.1:8000](http://127.0.0.1:8000).

Any **OpenAI-compatible** HTTP API works (single adapter in code):

| Provider | Example base URL |
|---|---|
| OpenAI | `https://api.openai.com/v1` |
| Ollama | `http://localhost:11434/v1` |
| vLLM / others | your endpoint + `/v1` |

On `/config`

, use **Add model to catalog** to save OpenAI-compatible providers (base URL, model name, optional API key). Entries are stored in [ data/llm_models.json](/GrillKit/grillkit/blob/main/data/llm_models.json) (gitignored). Select an interview model from the list, run

**Test Connection**, then save.

Application settings and interview **locale** (AI feedback and dictation language) live in `data/config.json`

(gitignored). Do not commit secrets.

After saving configuration, choose a **Whisper** model size (`small`

, `medium`

, or `large`

) and download it from the Configuration page (stored under `data/whisper-models/<size>/`

). Dictation uses the locale snapshot from config. The app loads the model into memory when the download finishes or on the next startup.

**Read questions aloud** (`question_voice_enabled`

) requests synthesized audio for question text only (never code blocks). Download the Piper voice on `/config`

after enabling the option (~60 MB per voice from Hugging Face).

Optional environment variables:

| Variable | Purpose |
|---|---|
`HF_TOKEN` |
Hugging Face read token for faster, more reliable Whisper and Piper model downloads (
`docker compose` when set on the host. |

`WHISPER_DEVICE`

`cpu`

or `cuda`

(default `cpu`

)`WHISPER_COMPUTE_TYPE`

`int8`

or `float16`

(default `int8`

on CPU)

```
data/
├── config.json              # Locale, speech/TTS flags, timer defaults (gitignored)
├── llm_models.json          # Interview model catalog (gitignored)
├── db/grillkit.db           # SQLite (gitignored, created on startup)
├── whisper-models/          # Offline Whisper models per size (gitignored content)
├── piper-voices/            # Piper ONNX voices for question TTS (gitignored content)
├── tts-cache/               # Cached question WAVs per locale (gitignored content)
└── questions/               # YAML banks: {track}/{level}/{category}.yaml
```

Schema and `selection_spec`

data migrations run automatically on startup via **Alembic** (`uv run alembic upgrade head`

). For a clean dev DB, remove `data/db/grillkit.db`

and restart the app.

```
app/
├── main.py              # FastAPI app, routers, lifespan
├── interview/           # Sessions, WebSocket chat, scoring, timer
├── speech/              # Whisper download + dictation
├── question_voice/      # Piper TTS, cache, question audio API
├── platform/            # Provider config, LLM catalog (/config)
├── shared/              # DB, UoW, locales, artifact download helpers
└── ai/                  # OpenAI-compatible + faster-whisper adapters
templates/               # Jinja2 UI
static/                  # CSS, JS (dictation, timer, question voice)
tests/
ARCHITECTURE.md          # Layers, routes, data flows
```

CI runs on every pull request and on pushes to `main`

(ruff, mypy, pytest). See [ .github/workflows/ci.yml](/GrillKit/grillkit/blob/main/.github/workflows/ci.yml).

```
uv sync --frozen --extra dev
uv run pytest
uv run ruff check --fix .
uv run ruff format .
uv run mypy .
```

See [CONTRIBUTING.md](/GrillKit/grillkit/blob/main/CONTRIBUTING.md) for contribution guidelines.

Report vulnerabilities as described in [SECURITY.md](/GrillKit/grillkit/blob/main/SECURITY.md). Do not open public issues for security problems.

[Apache License 2.0](/GrillKit/grillkit/blob/main/LICENSE) (see also [NOTICE](/GrillKit/grillkit/blob/main/NOTICE))
