Adaptive Python AI Tutor with FastAPI and SQLite

Share:

Adaptive Python AI Tutor with FastAPI and SQLite

Build a focused API that accepts a Python exercise submission, requests structured tutoring feedback, stores topic mastery in SQLite, and returns a validated response to a client.

What You Will Build

This tutorial creates PyMentor, a small adaptive tutoring API for Python practice. A client sends a learner identifier, a topic, an exercise, and a code submission. The API reads the learner’s previous mastery score for that topic, asks a configured OpenAI model for teaching-oriented feedback, validates the returned JSON, calculates a bounded new mastery score, and records the attempt in SQLite.

The goal is deliberately narrow. The service does not execute learner code, decide whether a learner has passed a course, or replace an instructor. It provides a repeatable feedback workflow: identify one likely issue, recognize a useful part of the attempt, offer a next hint, ask a question, and keep a small progress record. Those boundaries keep the example understandable and prevent the API process from treating arbitrary submitted Python as executable input.

The supplied research context does not include official compatibility documentation for FastAPI, Pydantic, SQLite, or the OpenAI SDK. For that reason, this guide avoids claiming that a particular model or package release is universally available. Configure the model name through an environment variable, then verify package and API compatibility against the official documentation for the versions you install.

Prerequisites

  • Python 3.10 or later.
  • An OpenAI API key supplied through an environment variable or local development file.
  • A terminal and an HTTP client such as curl.
  • Basic familiarity with Python functions, JSON, and HTTP requests.

Create a project directory and a virtual environment. The line-continuation characters below are intentional, so the install command remains valid in a POSIX shell.

mkdir pymentor
cd pymentor
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install fastapi "uvicorn[standard]" openai pydantic-settings
mkdir -p app

In Windows PowerShell, activate the environment with ..venvScriptsActivate.ps1. Create a file named .env in the project root:

OPENAI_API_KEY=replace-with-your-api-key
OPENAI_MODEL=replace-with-a-model-available-to-your-account
DATABASE_PATH=pymentor.db
MAX_CODE_CHARACTERS=12000

Do not commit this file. Add the following entries to .gitignore before you begin:

.venv/
__pycache__/
*.pyc
.env
pymentor.db
.pytest_cache/

Step 1: Create the Application Module

For clarity, this tutorial keeps the complete application in one file. A production project can later separate settings, schemas, persistence, prompting, and routes into dedicated modules. The important design decision is already present: request data, model feedback, and stored data each have explicit structures.

Create app/main.py and paste the following code. The request model limits the data accepted from the client. The feedback model is the application contract for model output. SQLite writes use parameters rather than string interpolation, so learner-provided values are not inserted into SQL text.

import json
import sqlite3
from contextlib import asynccontextmanager
from datetime import UTC, datetime
from functools import lru_cache
from pathlib import Path
from uuid import uuid4from fastapi import FastAPI, HTTPException, Request, status
from fastapi.concurrency import run_in_threadpool
from openai import OpenAI
from pydantic import BaseModel, Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDictclass Settings(BaseSettings):
openai_api_key: str = Field(min_length=1)
openai_model: str = Field(min_length=1)
database_path: Path = Path("pymentor.db")
max_code_characters: int = Field(default=12000, ge=500, le=50000)model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)@lru_cache
def get_settings() -> Settings:
return Settings()class TutorRequest(BaseModel):
learner_id: str = Field(
min_length=3,
max_length=80,
pattern=r"^[A-Za-z0-9_-]+$",
)
topic: str = Field(min_length=2, max_length=80)
exercise: str = Field(min_length=10, max_length=3000)
code: str = Field(min_length=1, max_length=50000)
learner_question: str | None = Field(default=None, max_length=1500)
allow_solution: bool = False@field_validator("code")
@classmethod
def reject_null_bytes(cls, value: str) -> str:
if "x00" in value:
raise ValueError("code must not contain null bytes")
return valueclass ModelFeedback(BaseModel):
summary: str = Field(min_length=1, max_length=600)
strengths: list[str] = Field(min_length=1, max_length=4)
misconceptions: list[str] = Field(min_length=1, max_length=3)
next_hint: str = Field(min_length=1, max_length=700)
socratic_question: str = Field(min_length=1, max_length=400)
suggested_concepts: list[str] = Field(min_length=1, max_length=4)
mastery_delta: int = Field(ge=-20, le=20)
needs_human_review: boolclass TutorResponse(BaseModel):
attempt_id: str
topic: str
previous_mastery: int = Field(ge=0, le=100)
current_mastery: int = Field(ge=0, le=100)
feedback: ModelFeedbackclass ProgressStore:
def __init__(self, database_path: Path) -> None:
self.database_path = database_pathdef connect(self) -> sqlite3.Connection:
connection = sqlite3.connect(self.database_path)
connection.row_factory = sqlite3.Row
return connectiondef initialize(self) -> None:
with self.connect() as connection:
connection.executescript(
"""
CREATE TABLE IF NOT EXISTS learner_progress (
learner_id TEXT NOT NULL,
topic TEXT NOT NULL,
mastery INTEGER NOT NULL CHECK (mastery BETWEEN 0 AND 100),
updated_at TEXT NOT NULL,
PRIMARY KEY (learner_id, topic)
);CREATE TABLE IF NOT EXISTS tutor_attempts (
attempt_id TEXT PRIMARY KEY,
learner_id TEXT NOT NULL,
topic TEXT NOT NULL,
exercise TEXT NOT NULL,
submitted_code TEXT NOT NULL,
feedback_json TEXT NOT NULL,
created_at TEXT NOT NULL
);
"""
)def get_mastery(self, learner_id:...

Continue Reading

Log in for free to read the rest of this article and access exclusive AI tools.

Log in / Register

Was this tutorial helpful?

GateOfAI AI Guide
Online
Hello! Welcome to GateOfAI. I am your guide copilot. I can answer questions about our SaaS tools, pricing, vetted developers, and escrow safety. How can I help you today?