A behavioral-interview simulator and coach that runs in your terminal.
Project description
sotellme
A mock behavioral interviewer that runs in your terminal, built from your CV and the job you're actually chasing.
Why I built it
I built this because behavioral interviews are where good candidates trip up. The questions sound easy, so most people wing them, and the usual prep ("tell me about a time you failed") is too generic to help with the specific job in front of you.
sotellme makes the practice specific. You give it your CV and a job posting, it reads up on the company, and then it interviews you against all three at once, so when it asks why you want the role it can name the product you'd actually be building. At the end it grades every answer and walks you through the weak ones: what went wrong, and what to say instead.
What it does
You give it your CV and the job you're chasing, and it interviews you against both, plus a short brief it builds on the company from a handful of public pages. It runs the session the way a real interviewer would: it opens on who you are, digs into your biggest piece of work, picks the stories that fit the role, and chases the interesting thread in your last answer rather than marching through a checklist. Most sessions run 8 to 14 questions. When you're done, the smart model reads the whole transcript and scores every answer on STAR structure, specificity, and ownership against your target level, then writes you a Markdown report: a scorecard that names what's weak, a fix for each soft answer, and a short study plan. It also tells you what the run cost.
Quickstart
Set one provider key first (see Configuration):
export ANTHROPIC_API_KEY=... # or GOOGLE_API_KEY, or OPENAI_API_KEY
The easiest way in is the local web app. Pull in the web extra and launch it:
uvx --from "sotellme[web]" sotellme web
It opens in your browser: upload your CV, paste a posting or drop in a link, run the interview as a chat, and read the report on the page, with a button to save it as Markdown. Everything runs locally on your own key.
If you'd rather stay in the terminal, run the interview straight from
uvx, no clone needed:
uvx sotellme interview --cv path/to/cv.pdf --job https://jobs.example.com/senior-backend
--job takes a link, a file (PDF, markdown, or text), or pasted posting text, and it's
optional; without it the interview runs on a default competency set with no company
research to ground it. For a link the tool prefers the page's embedded JobPosting data
and falls back to the visible text, and Workable postings are read through their public
API. Pages that only render with JavaScript can't be read, and pasting the text always
works.
Answers are multi-line with real line editing (Home, End, arrow keys, word jumps). Enter
starts a new line; Esc then Enter sends, or put /done on its own line.
Commands
| Command | What it does |
|---|---|
sotellme interview --cv <path> [--job <link|file|text>] |
Start a new interview session. |
sotellme resume |
Pick up the latest interrupted session. |
sotellme reports |
List the coaching reports in this directory, newest first. |
sotellme grade <transcript.json> --level <junior|mid|senior|staff> |
Grade a transcript you already have (a JSON list of {question, answer} pairs) without running a live interview. |
sotellme web |
Launch the local web UI in your browser (needs the web extra). |
interview, resume, and grade also take --provider, --fast-model, and
--smart-model to override the model picks.
Privacy and limits
Your transcripts and session state stay on your machine. The only things that leave it
are API calls to whichever provider you picked, plus plain HTTP GETs to public pages: one
for a --job link, and up to six more for the company brief. Those fetches are capped per
session, truncated per page, and refused for localhost and private addresses. Your API key
is read only by the code that calls the provider and never goes into a prompt, so no
hostile page or posting can talk the model into leaking it (tests/test_fetch.py,
tests/test_secret_isolation.py, tests/test_injection.py).
A cap on questions, a guaranteed closing question, a ceiling on web fetches, and a token budget that ends a long session early are all plain code, and they're unit-tested. The tool also screens what you type before it reaches the interview, so going off-topic nudges you back and a second off-topic reply in a row wraps the session up. Either way the real answers you gave still get graded.
Configuration
There's no account and no server. Pick a provider with SOTELLME_PROVIDER (or
--provider, or the dropdown in the web app) and set its key:
| Provider | Key variable | Default models (fast / smart) |
|---|---|---|
google_genai |
GOOGLE_API_KEY |
gemini-3.5-flash / gemini-3.1-pro-preview |
anthropic |
ANTHROPIC_API_KEY |
claude-sonnet-4-6 / claude-opus-4-8 |
openai |
OPENAI_API_KEY |
gpt-5.4-mini / gpt-5.5 |
The fast slot runs the interview side (CV parser, company researcher, answer assessor,
interviewer); the smart slot runs the director that makes every probe-or-move-on call,
plus the end-of-session grader and coach. In the CLI you set those two slots with
SOTELLME_FAST_MODEL / SOTELLME_SMART_MODEL or the matching flags. The web app goes
finer: its Advanced section pins a model to each step on its own, so you can put a cheap
one on the company research and a stronger one on the questions and the grading, and mix
providers once you've set more than one key. The eval suites run against google_genai
with an anthropic judge, which is the combo I'd reach for.
Both draw their choices from the same catalog, which ships the per-provider defaults in
the table above. To change what's on offer, write a ~/.sotellme/models.toml listing the
models you want and the default for each provider, and that's what the web app's dropdowns
show. The file holds model names plus the per-model prices behind the cost
estimates (including the reduced rate for cached input), so you can correct a rate that's
drifted; your API keys stay in the environment.
The session has a token budget, 400,000 by default, that ends the interview early if a run
goes long and keeps a reserved share back to grade and coach what you gave. Change it with
SOTELLME_TOKEN_BUDGET.
Development
Requires Python 3.12+, managed with uv. The package takes its long description from the
repo's README.md, so stage that and the license into backend/ once before the first
sync:
cd backend
python3 scripts/prepare_package.py
uv sync
uv run ruff check . && uv run mypy && uv run pytest
The deterministic suite runs without any API keys, and it's the whole CI gate.
The judgment agents (grader, coach, assessor, role builder, profile parser) are tuned
separately in Langfuse. Stand up a local instance, export LANGFUSE_PUBLIC_KEY,
LANGFUSE_SECRET_KEY, and LANGFUSE_HOST, then sync the committed cases and run one
agent over its dataset:
uv sync --extra tracing
uv run python scripts/evals.py upload
uv run python scripts/evals.py run grader --limit 2 # small calibration run first
uv run python scripts/evals.py run grader
Each run lands in Langfuse with a deterministic score per case, so you can read the
outputs, edit a prompt, run it again, and compare the two runs side by side. It also
prints the run's token count and estimated cost per model, priced from models.toml, so
you can size a full run from a --limit sample before committing to it. Only the
synthetic evals/*.json cases ever go in, and Langfuse stays off unless its env vars are
set, for evals and for live-session tracing alike.
The questions the system asks get their own eval. scripts/simulate.py runs a full
interview against a synthetic candidate: the real interviewer and director loop ask, while
a candidate-simulator answers in character from a persona under evals/personas/. The
personas span every level from junior to staff and a mix of answering styles, complete
STAR stories, thin answers, blurred ownership, off-topic drift, confident bluffing, and
injection attempts, so a run also exercises the guardrail and how the loop recovers. An
LLM judge on the smart slot scores each question on relevance, whether it probes the
flagged gap, level-appropriateness, whether it leads the candidate, and follow-up
discipline, plus a coverage verdict for the session.
uv run python scripts/simulate.py upload
uv run python scripts/simulate.py run --persona senior-strong --persona junior-thin
uv run python scripts/simulate.py run
Before a run it estimates the cost across the chosen personas and the judge passes and
asks first for anything over $3.50; pass --yes to skip the prompt in a script. Each
persona is a Langfuse dataset item tagged with its level and answer mix, so the
question-quality scores compare run to run and slice by both, and the session transcripts
land under evals/sessions/. The personas are synthetic, the same PII rule as everything
else.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sotellme-0.1.2.tar.gz.
File metadata
- Download URL: sotellme-0.1.2.tar.gz
- Upload date:
- Size: 296.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04fc88dcb0724a85535a882ba19acb3e4a22e09cefcde05b927c8fe45278af08
|
|
| MD5 |
dc7a5bcba3318b2a8f7d108e3f88ac2c
|
|
| BLAKE2b-256 |
0ccb2adf8d9f1d8612c754fd8e03cf7a61140272c5e34c250e5073fc9801821f
|
Provenance
The following attestation bundles were made for sotellme-0.1.2.tar.gz:
Publisher:
release.yml on SrdjanCoric/sotellme
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sotellme-0.1.2.tar.gz -
Subject digest:
04fc88dcb0724a85535a882ba19acb3e4a22e09cefcde05b927c8fe45278af08 - Sigstore transparency entry: 1847433973
- Sigstore integration time:
-
Permalink:
SrdjanCoric/sotellme@6394676a0c8608d9137aec259faf2aecc718bf93 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/SrdjanCoric
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6394676a0c8608d9137aec259faf2aecc718bf93 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sotellme-0.1.2-py3-none-any.whl.
File metadata
- Download URL: sotellme-0.1.2-py3-none-any.whl
- Upload date:
- Size: 77.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0f74338f0ad41657c6aea42d0995266423e6a70cd4e8b6e025b16367275d311
|
|
| MD5 |
c8fe47265ab191efd08fb91ec76b99af
|
|
| BLAKE2b-256 |
860c729f3a863e016bc0b6798673d1b79574b67f9fe3e5caca6abdcd80d4085e
|
Provenance
The following attestation bundles were made for sotellme-0.1.2-py3-none-any.whl:
Publisher:
release.yml on SrdjanCoric/sotellme
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sotellme-0.1.2-py3-none-any.whl -
Subject digest:
e0f74338f0ad41657c6aea42d0995266423e6a70cd4e8b6e025b16367275d311 - Sigstore transparency entry: 1847434227
- Sigstore integration time:
-
Permalink:
SrdjanCoric/sotellme@6394676a0c8608d9137aec259faf2aecc718bf93 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/SrdjanCoric
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6394676a0c8608d9137aec259faf2aecc718bf93 -
Trigger Event:
push
-
Statement type: