Skip to content

AI Pipeline

Everything AI on Ediky flows through one module: functions/_ai-providers.js. Grading, tutoring, optimization review, aptitude coaching, SQL review, and this docs assistant all share the same provider resolution, the same JSON coercion, and the same failure posture. That's deliberate — reliability work done once benefits every feature.

The nine-provider fallback chain

diagram100%
rendering diagram…

drag to pan · ctrl / ⌘ + scroll to zoom

Every provider in the chain has a free tier; the chain exists because free tiers fail constantly — rate limits, deprecated model IDs, regional outages. A student mid-exam should never see that. A request walks the chain until one provider answers; only providers with a configured key participate.

What the shared module exports

ExportRole
DEFAULT_MODELSper-provider default model id
KEY_FOR / MODEL_ENV_FORmaps provider → its key/model env var names
providerHasKey(env, p)participation check
REAL_PROVIDERSthe chain, in order
resolveProviderChain(env, preferred?)final ordered chain for this deployment
coerceJson / firstBalancedextract strict JSON from chatty model output
json(status, body)uniform response helper

Configuration

Env varEffect
<PROVIDER>_API_KEY (e.g. GEMINI_API_KEY, GROQ_API_KEY)enables that provider
<PROVIDER>_MODELoverrides its default model id
AI_PROVIDERpins the first provider to try
AI_FALLBACKScomma-list overriding chain order

No keys at all → the echo stub answers, prefixed [echo provider] No real model was called…. It keeps dev environments working and makes the degraded state impossible to mistake for a real answer.

Hard-won lessons encoded here

The chain wasn't speculative armor — it was built after cascading production failures: a deprecated Gemini model id, Groq rate-limit storms, and an invalid model string each took down grading at different times. Diagnosis got its own tooling: every AI route's GET is a health check reporting the resolved chain and picked model names (never key values).

coerceJson exists because free-tier models decorate JSON with markdown fences and commentary; firstBalanced pulls the first balanced {…} out of whatever came back. Prompts additionally demand JSON-only — belt and suspenders, and both have caught real failures.

Who calls it

CallerRouteMode
AskAiPopover (tutor)ai-code-reviewask
AI Help on a problemai-code-reviewhelp / chat
Optimization reviewai-code-reviewoptimize
CS concept gradinggrade-conceptrubric grading
Aptitude coachingevaluate-aptitudeexplanation only
SQL reviewgrade-sql-practicequality review
OA "Regrade with AI"grade-conceptre-run after the fact
This docs siteapi/ask-docsretrieval-grounded Q&A

The Python authoring pipelines mirror the same abstraction (cs_eval/ai_service.py) so authoring-time and runtime grading agree on one contract — see CS Pipeline.

The docs assistant

The widget on this site is the same architecture with one addition: retrieval grounding.

  1. scripts/build-ai-index.mjs runs before every build, indexing every docs page into /ai/docs-map.json (titles, headings, excerpts) + per-page text files.
  2. POST /api/ask-docs keyword-ranks pages against the question (title > path > headings > excerpt, small boost for the page the reader is on), stuffs the top 4 into the prompt, and demands JSON { reply, sources }.
  3. Returned sources are filtered against the real index — the assistant can cite only pages that exist.
  4. Same chain, same echo stub, same health check on GET.

Grounding means the assistant answers from the documentation instead of hallucinating an architecture — and tells you where it read the answer.

Ediky Workflow — internal engineering documentation.