kp// projects/ TaskIQ

~/projects/TaskIQ · README.md

TaskIQ — AI Project Manager

Turns unstructured meeting notes and chat logs into structured, prioritized tasks — a chain-of-thought Gemini pipeline with schema-constrained JSON extraction, a persistence layer, and a live drag-and-drop Kanban board on top.

6 REST API Endpoints
Exponential Backoff Retries
5 pytest Test Functions
200w Max AI Summary Length

01 · overview

The problem

Meeting notes and chat logs are full of tasks that never make it onto anyone's board — buried in a sentence, missing an owner, with a deadline stated as "by EOD" instead of a date. Someone has to read the whole thing and manually translate it into tickets. TaskIQ does that translation step: paste in raw, messy text and get back structured tasks — description, owner, due date, priority — ready to drop onto a Kanban board.

The core design problem is reliability of extraction. A plain "summarize this into tasks" prompt tends to invent fields, skip implicit deadlines, or return inconsistent JSON shapes between calls. TaskIQ constrains the model with a Pydantic response schema and forces it to reason before it extracts, rather than trusting free-form output.

02 · architecture

System architecture

A vanilla JS/HTML/CSS frontend talking to a small FastAPI service, backed by SQLite and Google's Gemini API.

Extraction flow
Raw NotesPasted meeting text
or chat log
POST /api/tasks/extractFastAPI route
Gemini 2.5 FlashCoT reasoning +
schema-locked JSON
Parsed Tasksdescription, owner,
due date, priority
SQLitetasks.db
Kanban UIDrag-and-drop
status columns
🧠 chain-of-thought first

The schema's first field is reasoning, not the task itself — the model has to write out its inference about priority and ownership before it's allowed to produce the structured fields.

↺ resilient by default

Every LLM call is wrapped in Tenacity's retry with exponential backoff (up to 3 attempts), so a transient API hiccup doesn't surface as a failed extraction.

📝 executive summary

A second, independent flow reads every task row back out of SQLite and asks Gemini for a stakeholder-ready status summary — on-track / at-risk / behind, highlights, blockers, next steps.

03 · how it works

How extraction works

01

Schema-constrained output, not free-form JSON

Extraction targets a Pydantic model — reasoning, description, due_date, owner, priority — passed to the Gemini API as a native response_schema with response_mime_type: application/json. The model literally cannot return a shape other than the one TaskIQ expects; there's no regex-and-hope JSON parsing on the way back.

02

Reasoning before extraction

reasoning is deliberately the first field in the schema. Because Gemini's structured output is generated field-by-field in order, putting reasoning first forces the model to think through who owns the task and how urgent it is before it commits to the priority and owner values — a lightweight chain-of-thought trick that costs one extra field and meaningfully improves consistency.

03

A one-shot example baked into the prompt

The system prompt includes one worked example directly, anchoring the model's sense of "correct" before it ever sees real input — the same example shown in the walkthrough below. At temperature = 0.1, generation stays close to that anchor rather than drifting stylistically between calls.

04

Resilience via exponential backoff

Both the extraction call and the summary call are wrapped in Tenacity's @retry decorator: up to 3 attempts, exponential wait starting at 2 seconds and capping at 10. A transient rate limit or network blip retries silently instead of surfacing as a broken request.

05

Persistence & the AI summary

Extracted tasks land in a SQLite tasks table (id, description, due_date, owner, priority, status). A separate /api/tasks/summary endpoint re-reads the full task list and asks Gemini — at a looser temperature = 0.3, since this call is prose rather than structured data — for a markdown status report capped at 200 words: overall status, key highlights, blockers, and next steps.

04 · worked example

The example baked into the prompt

This exact input/output pair is embedded in llm.py as the model's one-shot anchor — reproduced here verbatim from the source.

Raw input text

"We need to fix the login bug immediately, it's crashing production. Sarah, please handle this by EOD."

Endpoint

POST /api/tasks/extract

extracted_task.jsonGemini 2.5 Flash
{
  "reasoning": "The login bug is crashing production, which indicates
    critical urgency. Sarah is explicitly assigned. Deadline is EOD.",
  "description": "Fix production crash caused by login bug",
  "due_date": "Today",
  "owner": "Sarah",
  "priority": "High"
}

Notice the order: reasoning is written first and does the actual work — it names the urgency signal, the assignee, and the deadline before those become structured fields. priority: "High" isn't a keyword match on "immediately"; it's downstream of the model already having reasoned that a production outage is urgent.

05 · kanban & reliability

From extraction to board

— Kanban board

  • Native HTML5 drag-and-drop across To Do / In Progress / Done columns — dragging a card fires a PUT /api/tasks/{task_id} to persist the new status immediately.
  • CSV export of the current task list for sharing outside the app.
  • Automatic dark mode via prefers-color-scheme, matching the user's OS setting with no manual toggle needed.
  • One-click AI summary panel that calls /api/tasks/summary and renders the markdown report inline.

— Test suite

  • Isolated per-test database — a fixture creates a fresh SQLite file before each test and deletes it after, so tests never share state.
  • Full CRUD coverage — create, list, update, and delete are each exercised through FastAPI's TestClient against real HTTP routes.
  • Mocked LLM calls — the extraction test monkeypatches llm.extract_tasks_from_text, so the suite runs fast, free, and deterministically without calling the real Gemini API.
  • Static route smoke test — confirms the frontend's index.html is actually served at /.

06 · tech stack

Built with

FastAPI Google Gemini 2.5 Flash Pydantic SQLite Tenacity Vanilla JS / HTML5 / CSS pytest Uvicorn
MethodEndpointPurpose
POST/api/tasks/extractExtract structured tasks from raw notes
GET/api/tasksList all tasks
GET/api/tasks/summaryGenerate the AI executive summary
GET/api/tasks/{task_id}Get a single task
PUT/api/tasks/{task_id}Update a task (e.g. on Kanban drag)
DELETE/api/tasks/{task_id}Delete a task