Signposted

October 10, 2026 (2w ago)

Stack: Python, FastAPI, LangGraph, Groq, SQLAlchemy, PostgreSQL, React, TypeScript, Vite, Docker, Vercel, Render · Repo: Axeloooo/Signposted

Problem

Planning a trip means juggling destination research, a day-by-day schedule and a budget, and the pieces constrain each other. You submit a destination, number of days, a USD budget and interests, and four agents produce a plan that a reviewer checks against the budget and interests before it is returned.

The web UI presents the plan as a departure board: time-first itineraries, a live status for each agent, and a flag on the stop where running spend first crosses the budget. The app was deployed once on free tiers and is now offline because of hosting budget.

Architecture diagram

  React SPA (polls GET /trips/{id} every 1.5 s)
      |  /api/*
      v
  nginx or Vercel rewrite (strips /api)
      |
      v
  FastAPI (create_app)
      |  POST /trips: insert status=planning, return 202
      v
  PostgreSQL / SQLite   trips.plan (JSON column)
      ^                          |
      | progress after each      | background task
      | node, then final plan    v
      +---------------  plan_trip (LangGraph)
 
     research_agent --> itinerary_agent --> budget_agent --> reviewer_agent
                             ^                                    |
                             +------ REVISE, reviews < 2 ---------+
                                     APPROVE or 2 reviews --> END
 
  every agent node --> Groq chat model (llama-3.3-70b-versatile)

POST /trips validates the request, stores a trip with status planning and returns 202. A background task streams the compiled graph and writes progress into the plan JSON after each node, which is what the browser polls. The reviewer approves or sends the graph back to the itinerary agent with its feedback; the loop is capped at two review rounds, so at most one revision pass. The itinerary agent is asked for JSON only, which is parsed and validated into typed stops; if parsing fails, the raw text is kept.

Key decisions

Challenges

Results

Links