Rails & AI
ruby_llm retries a failing provider three times before your rescue ever runs. Here is what is left for you to handle, why regenerating uses complete rather than ask, and the one case the gem refuses to retry.
ruby_llm normalises the chat call across seventeen providers and stops short of normalising what they need to answer at all. Bedrock asks for a region, Vertex AI for a project and a location, Ollama for an address.
One line on a model mints three associations and expects six tables. Here is which two are yours, which four belong to the gem, why token counts moved in 2.0, and the load-order rule that decides whether your app boots in production.
Putting an LLM in a Rails app is mostly not about prompts. A Rails LLM feature is an HTTP call that takes eight seconds, fails in four distinguishable ways, costs money per token, and returns its answer in fragments.
ruby_llmhandles the provider differences. Everything else is still yours.What follows is what we learned wiring it into a production Rails 8 application, including the parts that were wrong the first time.
What ruby_llm actually is
A single chat interface over seventeen providers, plus an ActiveRecord layer that persists the conversation. Most Rails AI work that is not prompt writing is one of the two. The interface is small enough to fit in a paragraph:
RubyLLM.chatgives you a chat,asksends a message and returns the reply, passing a block streams it in chunks, andwith_modelswitches which model answers.The ActiveRecord layer is where the surprises are. One line on a model,
acts_as_chat, and your application has three new associations and six tables. What acts_as_chat puts behind one line walks the schema: which tables you will query, which ones sit empty until you use a feature you may never use, and the load-order rule that decides whether your app boots in production.Version numbers matter more than usual here. Token accounting lived on messages before 2.0 and lives in
ruby_llm_usagesafter it, and the old columns do not raise when you sum them.The failure modes are the feature
An LLM provider is a dependency you cannot test into reliability. It rate limits, it overloads, it returns 503, and it does all three more often than a database does.
ruby_llmmaps those onto an exception hierarchy worth reading before you write a rescue, and it also retries four of those classes three times before you ever see one. That reorders the problem: by the time your rescue runs, "try again" is finished, and what is left to decide is whether to try somewhere else.UnauthorizedErroris the class to keep out of that decision. Catching it alongside the transient ones turns a wrong API key into a feature that silently runs on your backup model and a bill nobody expected. Failing over without asking twice covers the split, the retries that already happened, and the detail that decides whether failover works at all: regenerating withcompleteinstead ofask.One interface, seventeen providers, and the seam
The pitch for a gem like this is that providers become interchangeable. In the chat call they genuinely are: the same
askreaches Anthropic, OpenAI, Gemini, Bedrock, Vertex AI or a local Ollama, and the reply comes back in the same shape.The seam is one layer down. Seventeen providers and the credentials that refuse to be uniform reads
configuration_requirementsoff each provider class rather than guessing: twelve want one API key, Azure and Ollama want an endpoint, Bedrock wants a region, and Vertex AI wants a project and a location. The two big clouds require the least of anybody, because both already know who you are and only need to be told where. A settings screen with one password field per provider cannot express five of the seventeen.What these pages do not cover
Not prompt engineering. Nothing here is about what to write in the prompt, which is a skill these pages have no claim to teach and which changes faster than any page can track.
Not what any one application does with the gem. These pages stay at the level of
ruby_llmitself, because that is the level the question is asked at. The AI layer in this Rails boilerplate is the worked example on the other side of that line: the same gem with a job, two quotas and an admin dashboard around it, which is a different question and gets a different page.Not an agents-and-tools reference either.
ruby_llmsupports tool calling and ships aruby_llm_tool_callstable for it, and no feature we run uses one, so writing that page would mean describing code we have not executed. What is here has been run in production, and it stops there.