One method, response back. Laravel 13 makes calling a model feel like calling a mailer, and the demo level is now twenty minutes of work. Which is exactly when architecture starts to matter, because everybody will ship the demo.

An LLM call is IO. Slow IO, expensive IO, IO that sometimes fails and sometimes lies. We know how to handle IO like that. We forget, because the response text looks smart and the API looks native to the framework.

We shipped one small feature like this recently. Text goes in, structured summary comes out. The shape that survived code review:

The HTTP layer validates input and pushes a job. Nothing waits for the model inside a request. The job makes the call with a hard timeout, retries with backoff, and an idempotency key, because a retry that bills twice and writes twice is a bug. The provider sits behind our own small interface. The prompt lives in the adapter, not in the domain. Business rules that must hold are checked in code after the response, never delegated to the prompt. “Please respond with valid JSON” is a wish.

And metrics from day one. Cost per call, latency, failure rate. The invoice arrives monthly. The surprise should not.

The provider is infrastructure, like the database driver, except worse: non-deterministic and priced per use. Keep it replaceable. When domain code starts importing the provider SDK, stop and refactor. The framework making something convenient does not make it central.

The first version of that feature called the model straight from the controller. Mine. It worked in the demo, which is the whole problem with demos.