Skip to content
Adam Probert

For myself

Grimoire ML

Describe the Magic card you're after and turn that into a Scryfall search.

Sheet
4 of 25
Period
2025
Kind
AI / Web
Org
Personal

What it was

You can know what you want a Magic card to do and still have no idea what it's called. Grimoire ML lets you describe it in ordinary language, then turns that description into a Scryfall query. The model produces a structured representation first. I can look at that to see what it understood, then build and test the actual search separately. There's a Next.js frontend, a FastAPI backend and a separate card-image proxy, with Redis caches between the services. It's a prototype.

The Grimoire ML mark: a burning spellbook with ML in the flames.Grimoire ML
Grimoire ML Logo
Grimoire ML Search Results
Grimoire ML Search Bar
grimoire ml: project diagram

Build

Fig. 1
PROMETHEUS · /metricsIR CACHEREDISPOSTGRESBROWSERWEBAPP:3000 · NEXT.JSQUERY:8080 · NL → IRCARD-DB:8081 · IMAGESBACKEND:8000 · DECKSOPENAIGPT-5-MINISCRYFALLSEARCH + IMAGES
  • The query service turns natural language into a structured intermediate representation, then builds the Scryfall query from that. Redis caches the stages.
  • A card-image proxy has its own cache and metrics, keeping image requests off the main backend.
  • The frontend uses Next.js and TypeScript, with Tailwind and Mantine for the UI.
  • Docker Compose runs the services, Postgres and Redis locally. Prometheus and Grafana show what each service is doing.

Key decisions

  • The query representation

    I wanted to inspect the model's interpretation before it became search syntax. The structured format also means the query code can be tested on its own.

  • Caching

    Card data changes slowly, while model calls cost time and money. Redis means repeating a search doesn't need to repeat all that work.

  • Seeing inside the services

    I wired in Prometheus and Grafana one afternoon. They were useful whenever Claude gave crappy responses to my prompts, because I could see which service had started doing something odd.

Lesson learned

I needed to see what the model thought I'd asked for.the rest of it ↓

Having the model write search syntax directly made a bad result difficult to work out. Had it misunderstood the request, or was the query wrong? Putting a structured representation in the middle let me look at those separately. I could also test the whole query layer without making a model call. I put that in late, I'd start there if I built it again.

Contact

Say hello.

If you’re stuck on something or thinking about starting a project, I’m happy to chat it through.

Get in touch
  • Something you're building, or thinking about building
  • A problem you're stuck on and want another pair of eyes on
  • Anything on this site you want to know more about
  • A role or piece of work worth a conversation