Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Invoice processor

Pull structured data out of invoices, in whatever shape you ask for.

Upload an invoice, describe the JSON you want with a visual schema editor, and get exactly that back. Bring your own AI provider and your own API key — they stay in your browser.

flowchart LR
    invoice[Invoice PDF or image] --> processor
    schema[Your JSON Schema] --> processor
    processor -- uses --> AI[(Gemini / GPT / Claude)]
    AI -- extracted JSON --> consumer[Consumer: stdout, a file, your datastore]
Loading

Want the store name and a line item for every product on a laptop receipt? Build that shape in the editor and it comes back as JSON, ready to feed wherever you need it.

Requirements

  • Go 1.26+ (developed against 1.26.4)
  • Node 20+ (developed against 25.9) — only to build the frontend
  • An API key for one of the supported providers

Quick start

git clone <your-repo-url>
cd invoices-processor-golang
make

That one command runs npm install, builds the UI, and serves it on http://127.0.0.1:8080. Open it, pick a provider, paste your API key, and go.

No API key is needed to start the server — you supply it in the browser.

Make targets

The Makefile is the entry point for everything; you shouldn't need to run go or npm by hand.

Target What it does
make or make run Rebuild the UI if it's stale, then serve on 127.0.0.1:8080
make dev Vite on :5173 with hot reload, proxying /api to the Go server on :8080
make test go test ./... plus the frontend tests
make clean Remove cmd/webapp/web/{dist,node_modules}

Three things it handles so you don't have to:

  • node_modules and dist are real file targets. npm install runs only when package.json changes, and the UI rebuilds only when a frontend source file does — so a repeat make starts the server immediately.
  • .env is sourced into the recipe shell if present. It's read as shell, not Makefile syntax, so quotes and export prefixes behave the way you'd expect.
  • One Ctrl-C is enough under make dev — it stops Vite and the Go server together.

Without make

cd cmd/webapp/web && npm install && npm run build && cd -
go run ./cmd/webapp

Run it from the repo root, or point -web at the built assets.

Providers

Provider Value Default model Get a key
Google Gemini googleai gemini-3.1-flash-lite https://ai.google.dev
OpenAI openai gpt-4.1 https://platform.openai.com/api-keys
Anthropic Claude anthropic claude-haiku-4-5 https://console.anthropic.com

Leave the model field blank to use the default, or type any model name the provider supports.

Where your API key goes

Your key is stored in your browser's localStorage and sent with each extraction request. The server holds a client in memory and never writes your key to disk — there is no key database to leak.

Two consequences worth understanding:

  • The server binds to 127.0.0.1 by default. If you change -addr to expose it on a network, your key crosses that connection. Put it behind TLS if you do.
  • Errors from a provider are passed back to you, and keys are stripped from them. That strip is verbatim-only: OpenAI echoes a self-masked form (sk-abcd****wxyz) that won't string-match, so fragments can still appear in the server log and the browser. Both are yours, on your machine, but it's worth knowing.

Schemas and templates

The schema editor is jsonjoy-builder (MIT) — the same tool behind https://json.ophir.dev. Edit visually or drop into the JSON source view.

  • The schema you're editing is remembered across reloads.
  • Save named templates to switch between extraction shapes — one for hardware receipts, one for fuel, one for supplier invoices.
  • Reset to example puts the starter schema back.

Templates live in your browser too. Nothing is stored server-side.

Configuration

go run ./cmd/webapp [flags]

  -addr string
        address to listen on (default "127.0.0.1:8080")
  -web string
        directory of built frontend assets (default "cmd/webapp/web/dist")

A .env file in the repo root is sourced by make if present, so you can put anything the Go process should see there.

API

The UI is a client of one endpoint, so you can script against it directly:

curl -X POST http://127.0.0.1:8080/api/extract \
  -F 'invoice=@receipt.pdf' \
  -F 'schema={"type":"object","properties":{"total":{"type":"number"}}}' \
  -F 'provider=googleai' \
  -F 'apiKey=YOUR_KEY' \
  -F 'model='            # optional

Returns the extracted JSON. Uploads are capped at 20 MiB.

Status Meaning
200 Extracted JSON
400 Bad input: missing file, unusable schema, unknown provider, rejected credentials
502 The model call itself failed

Development

make dev for frontend work, make test before you push — see Make targets.

Tests are split by language. Go uses the standard library; the frontend uses Node's built-in test runner (node --test), so there's no test framework to install. Run the frontend suite alone with npm test inside cmd/webapp/web.

Project layout

cmd/webapp/          HTTP server and the React app
  main.go            routes, upload limits, credential plumbing
  web/src/
    main.jsx         the whole UI
    storage.js       localStorage read/write
    templates.js     named-schema list operations
internal/
  AI.go              genkit client cache, extraction
  provider.go        provider credentials, model names, redaction
  processor.go       ties AI output to a consumer
  consumer.go        where extracted data goes (io.Writer)

Processor writes to a Consumer, which is just an io.Writer — so os.Stdout, a file, or an HTTP response all work. The web app buffers into one; a CLI could write straight to stdout.

Known limitations

  • OpenAI output is less strictly constrained. Genkit's Gemini and Anthropic plugins support native constrained decoding; the OpenAI plugin doesn't, so the schema becomes a prompt instruction rather than an enforced grammar. Responses are unwrapped from markdown fences before being returned, but expect more variance.
  • A bad Anthropic key looks like a missing model. Anthropic resolves model names by calling its API, so an invalid key surfaces as model "anthropic/..." not found. The real 401 is in the server log.
  • GENKIT_ENV=dev doesn't work here. Genkit starts a reflection server per client instance, and this app creates one per credential set, so the second provider fails on the busy port. The server warns at startup if you set it.
  • In-memory client cache is capped at 16. Genkit leaks a signal registration per instance, so the cache evicts oldest-first rather than growing without bound.
  • Single user, no accounts, no server-side persistence.

Built with

License

TBD

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages