A lightweight Python clone of OpenMuse using Flask and SQLite
Welcome to another official UVF IT guide! In this tutorial we’ll build a tiny personal‑assistant web service inspired by OpenMuse, using only free, open‑source tools you can run on your laptop.
In about 45 minutes you’ll have a Flask API that stores tasks in SQLite, a minimal HTML UI, and the ability to add and list notes – the core of a personal assistant.
python -m venv venv then venv\Scripts\activate; on macOS/Linux use source venv/bin/activate.Create a virtual environment and install Flask; this gives us an isolated runtime.
We also set up a basic folder structure so later files have a predictable place.
mkdir openmuse_clone && cd openmuse_clone
python -m venv venv
# Activate the environment
# Windows:
# venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate
pip install --upgrade pip
pip install FlaskWe’ll use SQLite via Python’s built‑in sqlite3 module; a single table stores notes with timestamps.
The script creates the database file and the notes table if they don’t already exist.
init_db.py
import sqlite3, os
DB_PATH = os.path.join(os.path.dirname(__file__), 'notes.db')
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
cur.execute('''
CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
conn.close()
print(f'Database initialized at {DB_PATH}')In this step we create a single Flask application that both serves a tiny HTML UI and provides JSON endpoints for managing notes. The UI lets you add a note via a form and displays all saved notes, while the API offers CRUD operations for programmatic access. Storing data in SQLite keeps the clone lightweight and requires no external services. By keeping everything in one file we make the project easy to understand and extend.
app.py
import sqlite3
from flask import Flask, request, jsonify, render_template_string, g
app = Flask(__name__)
DB_PATH = 'notes.db'
# ---------- Database helpers ----------
def get_db():
if 'db' not in g:
g.db = sqlite3.connect(DB_PATH)
g.db.row_factory = sqlite3.Row
return g.db
@app.teardown_appcontext
def close_db(exception):
db = g.pop('db', None)
if db is not None:
db.close()
def init_db():
db = get_db()
db.execute('''CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)''')
db.commit()
# Initialize DB on first request
@app.before_first_request
def setup():
init_db()
# ---------- API routes ----------
@app.route('/api/notes', methods=['GET'])
def list_notes():
db = get_db()
notes = db.execute('SELECT id, content, created_at FROM notes ORDER BY created_at DESC').fetchall()
return jsonify([dict(note) for note in notes])
@app.route('/api/notes', methods=['POST'])
def add_note():
content = request.json.get('content') if request.is_json else request.form.get('content')
if not content:
return jsonify({'error': 'Content required'}), 400
db = get_db()
db.execute('INSERT INTO notes (content) VALUES (?)', (content,))
db.commit()
return jsonify({'status': 'added'}), 201
# ---------- UI route ----------
HTML_TEMPLATE = '''
<!doctype html>
<title>My Notes</title>
<h1>My Notes</h1>
<form method="post" action="/api/notes">
<input name="content" placeholder="Write a note" required>
<button type="submit">Add</button>
</form>
<ul>
{% for note in notes %}
<li>{{ note.content }} <small>({{ note.created_at }})</small></li>
{% else %}
<li>No notes yet.</li>
{% endfor %}
</ul>
'''
@app.route('/')
def index():
db = get_db()
notes = db.execute('SELECT content, created_at FROM notes ORDER BY created_at DESC').fetchall()
return render_template_string(HTML_TEMPLATE, notes=notes)
if __name__ == '__main__':
app.run(debug=True)Now that the code is in place we need to create the SQLite file and launch the Flask development server. The first request will automatically create the database if it doesn't exist, so simply starting the app is enough. Running with `debug=True` gives helpful error messages while you experiment.
Make sure you are inside the virtual environment where Flask is installed before executing the command.
export FLASK_APP=app.py
flask runOpen your browser at http://127.0.0.1:5000 and you should see the "My Notes" page with an empty list. Use the form to add a note; the page will refresh showing the new entry. To confirm the API, request the JSON endpoint and you’ll see the same data in machine‑readable form.
From here you can extend the clone: add tags, implement search, or protect the routes with simple authentication. The structure is intentionally minimal so you can iterate quickly.
curl -s http://127.0.0.1:5000/api/notes | python -m json.tool| Check | Command / Why it matters |
|---|---|
| Virtual environment activated and Flask installed | pip list | grep Flask shows version 2.3.2 |
| Database file created | ls notes.db shows the file after running init_db.py |
| Web UI loads with empty list | Visit http://127.0.0.1:5000 and see 'My Notes' with no items |
| Adding a note updates UI and API | Submit form, then curl http://127.0.0.1:5000/api/notes shows JSON with the new entry |
Your minimal OpenMuse‑style personal assistant is up and running. Feel free to experiment – add authentication, integrate a free LLM API, or containerize with Docker for true self‑hosting.