Radar‑inspired AI Agent Monitor (Python)

A minimal clone to track your AI agents locally

Welcome to another official UVF IT guide. In the next 45 minutes you’ll spin up a tiny dashboard that logs and displays health pings from any AI agent, echoing the spirit of Radar without any paid services.

We’ll use Python, FastAPI, and SQLite – all free‑tier, open‑source tools – to build a working prototype you can run on your laptop and extend as you wish.

Before you start

Minimal Radar Clone Architecture
LOCAL STACK CRUD HTTP API FastAPI Server Python + SQLite SQLite DB Simple HTML UI
1

Phase 1: Scaffold the project

First we isolate our dependencies so the guide never interferes with your global Python install. A virtual environment gives us a clean sandbox, and installing FastAPI plus an ASGI server (uvicorn) gives us a lightweight web stack ready to serve JSON and HTML.

Once the environment is active, a single pip install line pulls in everything we need. This step is the foundation; later code will run inside this isolated context.

python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install fastapi uvicorn
2

Phase 2: Define the data model

We keep persistence simple by using the standard library sqlite3 module – no external ORM required. The model file creates the database file if it doesn't exist, defines a pings table, and provides two helper functions: one to record a ping and another to fetch the latest entries for the UI.

These helpers will be imported by the FastAPI routes later, keeping the API layer thin and focused on request handling.

model.py

import sqlite3
from pathlib import Path

DB_PATH = Path('radar_clone.db')

def _get_conn():
    return sqlite3.connect(DB_PATH)

def init_db():
    with _get_conn() as conn:
        conn.execute('''
            CREATE TABLE IF NOT EXISTS pings (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                agent_name TEXT NOT NULL,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                status TEXT NOT NULL
            )
        ''')
        conn.commit()

def add_ping(agent_name: str, status: str):
    with _get_conn() as conn:
        conn.execute('INSERT INTO pings (agent_name, status) VALUES (?, ?)', (agent_name, status))
        conn.commit()

def list_pings(limit: int = 20):
    with _get_conn() as conn:
        cur = conn.execute('SELECT id, agent_name, timestamp, status FROM pings ORDER BY timestamp DESC LIMIT ?', (limit,))
        return [dict(id=row[0], agent_name=row[1], timestamp=row[2], status=row[3]) for row in cur]

# Initialize DB on import so later modules can assume the table exists
init_db()
3

Phase 3: Build the API and UI

We’ll create a tiny FastAPI service that accepts a POST from any AI agent reporting its status. Each ping is stored in a simple in‑memory list so we can show recent activity without a database. The same app also serves a GET route that renders an HTML table, giving you a quick dashboard you can open in a browser.

The code below defines two endpoints: /ping receives JSON like {"agent":"name","status":"ok"} and returns a confirmation, while / builds an HTML page listing the last 20 pings. Keeping everything in one file makes the clone easy to understand and run.

main.py

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, JSONResponse
from pydantic import BaseModel
from typing import List
import uvicorn

app = FastAPI()

# In‑memory store for recent pings (max 20)
ping_log: List[dict] = []

class Ping(BaseModel):
    agent: str
    status: str

@app.post("/ping")
async def receive_ping(ping: Ping):
    # Record the ping with a timestamp
    entry = {"agent": ping.agent, "status": ping.status, "time": __import__('datetime').datetime.utcnow().isoformat()}
    ping_log.append(entry)
    if len(ping_log) > 20:
        ping_log.pop(0)
    return JSONResponse({"msg": "recorded", "entry": entry})

@app.get("/", response_class=HTMLResponse)
async def dashboard(request: Request):
    rows = "".join(
        f"<tr><td>{e['time'][:19].replace('T',' ')}</td><td>{e['agent']}</td><td>{e['status']}</td></tr>"
        for e in reversed(ping_log)
    )
    html = f"""
    <html><head><title>Radar Dashboard</title></head>
    <body><h2>Recent Agent Pings</h2>
    <table border='1' cellpadding='5'>
    <tr><th>Time (UTC)</th><th>Agent</th><th>Status</th></tr>
    {rows or '<tr><td colspan=3>No pings yet</td></tr>'}
    </table></body></html>
    """
    return HTMLResponse(content=html)

if __name__ == "__main__":
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
4

Phase 4: Run locally

Now that the FastAPI app is ready, start it with Uvicorn. The --reload flag watches for file changes, which is handy while you experiment. Once the server is up, it will listen on http://127.0.0.1:8000 – the same address you’ll use for testing and viewing the dashboard.

uvicorn main:app --reload
5

Phase 5: Verify and extend

With the server running, send a test ping using curl. The endpoint should reply with a JSON confirming the record was saved. Then open your browser to http://127.0.0.1:8000 – you should see a table showing the ping you just sent. From here you can iterate: add simple token‑based auth, store data in SQLite, or hook the endpoint into Grafana for richer visualisation.

curl -X POST http://127.0.0.1:8000/ping -H 'Content-Type: application/json' -d '{"agent":"demo-agent","status":"ok"}'

Verifying the Stack

CheckCommand / Why it matters
Server starts without errorsuvicorn main:app --reload shows 'Uvicorn running on http://127.0.0.1:8000'
Test ping returns JSON with msg='recorded'curl -X POST http://127.0.0.1:8000/ping -H 'Content-Type: application/json' -d '{"agent":"demo-agent","status":"ok"}' prints a JSON response containing msg='recorded'
Dashboard displays a table with the submitted pingOpening http://127.0.0.1:8000 in a browser shows an HTML table row for the ping just sent

You did it!

Your minimal Radar clone is up and running. Feel free to iterate – add charts, switch to Postgres, or containerize with Docker for production use.