Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Thermoplus: Commercial Email Intake Assistant

Deploy backend Python FastAPI LangGraph Next.js Live app

A commercial email intake assistant for a French SME that sells and installs heat pumps, sells spare parts, and runs installer trainings. It reads incoming emails, matches requests against a product catalog, and drafts replies that a human always reviews before sending.

Review queue

Problem

Commercial staff spend time reading emails, looking up the right product reference in the catalog, checking availability, and writing a reply, several times a day, across three product families, on messages that are sometimes ambiguous or mix several requests together.

Design principles

  • One LLM call reads raw email text. Everything after that (catalog lookup, stock check, the review gate) is plain Python, not another model call.
  • Never fabricate. No price is ever calculated (the catalog has none). No reference or stock status is trusted from the model alone: every claim is cross-checked against the real catalog and a stock snapshot before anything reaches a human.
  • Human in the loop, always. No email is sent automatically. Every draft is reviewed, editable, and sent only on an explicit action.

Stack

  • FastAPI, async throughout, for an I/O bound service that mostly waits on the Anthropic API and Resend.
  • LangGraph for Agent Intake: the pipeline has a real branch (draftable vs human review), so modeling it as a graph keeps that branch explicit and testable.
  • Claude Haiku for extraction and reply drafting: both are closer to structured parsing and templated writing than open ended reasoning, so a small model is enough. Extraction uses forced tool calling instead of free text plus parsing.
  • In-memory store, no database: the app is single-process and stateless between requests. Swapping in Postgres later would only touch store.py.
  • Next.js for the frontend, fully client-side once loaded, talking to the API directly.
  • Azure Container Apps for the backend, built from source through ACR, minimum one replica to avoid cold starts.
  • Vercel for the frontend, matching the Next.js deployment model with free preview deployments.
  • Resend for both inbound and outbound email, one provider instead of two.

Architecture

Architecture

Agent Intake

A three-node LangGraph pipeline. The review gate is a graph branch, not a side check: if reconcile doesn't clear every item, the graph never reaches draft.

Agent Intake pipeline

Project layout

app/
  agents/
    extraction.py   # the LLM call, catalog/stock reconciliation, the review gate
    intake.py       # the LangGraph pipeline (Agent Intake)
  routes/
    webhook.py      # Resend inbound webhook + manual test endpoint
    requests.py     # review queue listing + send action
    health.py
  reply.py          # per-item reply drafting, grounded in already-verified fields
  resend_client.py  # inbound signature verification, outbound send
  catalog.py, stock.py, store.py, schemas.py, config.py
data/
  catalog.yaml      # product reference data (no pricing)
  stock.yaml        # simulated stock snapshot, no ERP connection
frontend/
  app/
    components/       # RequestList, RequestDetail, ReplyAccordion, OnboardingTour
    hooks/            # useOnboarding
    exampleEmails.ts  # sample messages for quick testing
    page.tsx          # master-detail layout: list on the left, detail on the right
tests/
  test_intake_live.py  # integration tests against a deployed instance

Running the backend

pip install -e ".[dev]"
uvicorn app.main:app --reload

Required environment variables (see .env.example):

  • ANTHROPIC_API_KEY
  • RESEND_API_KEY, RESEND_WEBHOOK_SECRET (only needed for the real inbound webhook)

Running the frontend

cd frontend
npm install
npm run dev

Set NEXT_PUBLIC_API_URL to the backend's URL.

Master-detail layout: a sortable list of processed emails on the left, full detail (items, original message, a reply-per-category accordion) on the right. A few example emails are built in for quick testing, and a short onboarding tour runs on first visit (replayable from the ? button). Responsive down to a single column on mobile.

There are two ways to feed the assistant a message: paste text into the "Nouveau message" form (calls /intake/manual directly), or send a real email to thermoplus@feraikvra.resend.app. The second option goes through the actual Resend inbound webhook and shows up in the queue automatically, no form needed.

API

Endpoint Purpose
GET /health Liveness check
POST /webhook/resend Resend inbound webhook (email.received)
POST /intake/manual Process an email without going through Resend
GET /requests List processed emails and their drafts
POST /requests/{id}/send Send an approved (possibly edited) reply
DELETE /requests Clear all processed emails

Testing

Integration tests run against a deployed instance rather than with local secrets:

BASE_URL=https://<deployed-url> pytest tests/test_intake_live.py

Deployment and CI/CD

Every push to main deploys both sides independently.

  • Backend: a GitHub Actions workflow (.github/workflows/backend.yml) builds the image through Azure Container Registry and updates the Azure Container App. No separate build server needed, ACR does the build. One replica is always kept warm to avoid cold starts.
  • Frontend: Vercel's Git integration builds and deploys the Next.js app automatically, no workflow file needed on this side.
  • Email in/out: Resend, with a dedicated inbound address.

CI/CD and deployment

Known limitations

  • No real ERP connection. Stock is a simulated snapshot (data/stock.yaml).
  • No pricing engine. Any request needing a price is routed to a human.
  • Training requests are matched against the catalog but session scheduling isn't modeled.
  • Storage is in-memory and resets on restart.
  • No authentication on any endpoint, including sending a reply or clearing the queue.
  • CORS is wide open (allow_origins=["*"]), needed since frontend and backend are on separate domains, but not a production policy.
  • No rate limiting, so /intake/manual and /requests/{id}/send could be hit repeatedly and run up API and email costs.

About

AI email intake assistant for a heat pump and spare parts SME. LangGraph agent extracts and verifies requests against a catalog, drafts replies, human always approves before sending.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages