EchoHire keeps the interview recording in the browser, and the free Render pill still said offline
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady What I Built Interview practice is a chore I keep postponing. I either talk to a blank doc, or I paste an answer int
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
What I Built
Interview practice is a chore I keep postponing. I either talk to a blank doc, or I paste an answer into a chatbot and hope the recording never leaves my laptop.
EchoHire is the lazy version of that. You pick a role, read a behavioral or technical question, and answer out loud. The browser keeps the audio. It fills a transcript, counts filler words, and checks whether the answer has a Situation, Task, Action, and Result. A scorecard comes back without me grading myself.
Who gets to be lazier: a candidate who wants a rehearsal before the real interview, and an interviewer who wants to add a question for that role without editing source code. Added questions stay in that browser.
Scoring order:
- Groq, when an API key is set. Only the transcript text is sent.
-
Gemma 2B through local Ollama (
gemma2:2b), when that model is installed. This is the open-weight path, and it is what runs on my laptop. - Heuristics, if both scorers are down. The page still renders a scorecard. The UI is not allowed to crash because a model is offline.
The hosted demo is on Render's free plan: echohire-web.onrender.com. The API is echohire-api-mzrq.onrender.com. Free instances sleep after about 15 minutes, so the first open can take about a minute.
Demo
Open https://echohire-web.onrender.com/.
- Choose a role and behavioral or technical.
- Read the question, or add one under Add a question. That list is stored in this browser.
- Type an answer, or use Load Sample Candidate Response if the browser has no speech recognition.
- Press Evaluate answer.
The header pill should settle on FastAPI + Groq once the free API has woken up. Health for the API is a JSON body with status, groq_online, and ollama_online. On the free host, ollama_online stays false on purpose: the Gemma weights do not fit in 512 MB.
Repo: github.com/mrerror313coder/echohire
How I Built It
Two folders. frontend is Next.js 14 (App Router, TypeScript, Tailwind). backend is FastAPI. Speech-to-text on a laptop can use faster-whisper (base, int8, CPU). The free Render image does not install that wheel. If the browser cannot transcribe, you type the answer and the scorecard still works.
Gemma is the open model. Ollama serves gemma2:2b on http://127.0.0.1:11434 and is asked for JSON. The API prefers Groq when GROQ_API_KEY is set, then Ollama, then a deterministic STAR and filler-word scorecard. evaluation_source on the response says which path ran: groq:โฆ, gemma2:2b, or heuristic_fallback.
The bug that made a live API look offline
The first free deploy came up. The API /health returned 200. The dashboard pill still said Backend offline.
That pill means the browser fetch failed. It does not mean the model is down. A reached API with both scorers offline would say API up ยท model offline.
Two things stacked:
NEXT_PUBLIC_API_URL is inlined when Next.js builds, not when the page is opened. Ahmed Mahmoud's field notes on that are the short version: a NEXT_PUBLIC_ variable is a build-time replacement, not a runtime read (Environment Variables in Next.js). The first web build ran before that URL was available, so the bundle called http://127.0.0.1:8000. An HTTPS page calling loopback fails immediately. The dashboard remembered that address in localStorage, so a later deploy could still look offline in a tab that had already saved it.
The fix in the repo is a same-origin proxy. On a *.onrender.com host the page talks to its own /echo-api, and Next rewrites that to the public API. A saved laptop URL cannot win.
const hostedApi = "https://echohire-api-mzrq.onrender.com";
const nextConfig = {
async rewrites() {
return [
{ source: "/echo-api/:path*", destination: `${hostedApi}/:path*` },
];
},
};
if (window.location.hostname.endsWith(".onrender.com")) {
storedUrl = `${window.location.origin}/echo-api`;
localStorage.setItem(URL_KEY, storedUrl);
}
The API process only has to listen on Render's $PORT. Free instances that bind the wrong port never become healthy. Ayush Kumar's FastAPI-on-Render write-up is the matching caution: the plan size is a cost decision, and the process has to match the platform (Mastering FastAPI Deployment on Render).
exec uvicorn main:app --host 0.0.0.0 --port "${PORT:-8000}"
I did not put the Groq key in git or in render.yaml. Render stores it on the service. Without it, Evaluate still returns the heuristic scorecard.
Why Open Innovation Matters
The recording is the sensitive part, and it never needs a server. A closed interview product usually wants the audio. EchoHire leaves it in the browser and, on a laptop with Ollama, can score the transcript with Gemma 2B and no external model call.
The free public demo cannot run those weights. A 512 MB instance is not a place for a 1.6 GB model, so the hosted scorecard uses Groq when a key is set and heuristics when it is not. The open path is still the one I use locally, and the UI is written so a missing model is a degraded scorecard rather than a broken page. Swapping the scorer did not require giving the host the microphone.
The Lazy Payoff
A rehearsal used to mean opening a notes doc, reading a question, talking, then trying to remember whether I actually answered it. EchoHire keeps the question, the timer, the transcript, and a STAR pass in one screen. I do not re-grade the answer by hand, and I do not upload the take.
Prize Categories
-
Best Use of Render. The dashboard and the FastAPI scoring API are both on Render's free plan (512 MB). The blueprint is
render.yaml. The API image is slim: no Ollama, no Whisper wheel. Render is the host for the demo, not the machine that runs Gemma. -
Best Use of Gemma. Local scoring uses
gemma2:2bthrough Ollama when Groq is unset or unreachable. The model is asked for JSON, and a bad or missing response falls through to heuristics.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes โ full credit and traffic to the original publisher.