RubyLLM: A delightful Ruby way to work with AI
181 comments
·March 11, 2025kyledrake
bradgessler
There’s a whole world of async IO in Ruby that doesn’t get enough attention.
Checkout the async gem, including async-http, async-websockets, and the Falcon web server.
earcar
Thank you for your kind words!
Valid point. I'm actually already working on testing better streaming using async-http-faraday, which configures the default adapter to use async_http with falcon and async-job instead of thread-based approaches like puma and SolidQueue. This should significantly improve resource efficiency for AI workloads in Ruby - something I'm not aware is implemented by other major Ruby LLM libraries. The current approach with blocks is idiomatic Ruby, but the upcoming async support will make the library even better for production use cases. Stay tuned!
joevandyk
From https://rubyllm.com/#have-great-conversations
# Stream responses in real-time
chat.ask "Tell me a story about a Ruby programmer" do |chunk|
print chunk.content
end
jupp0r
This will synchronously block until ‘chat.ask’ returns though. Be prepared to be paying for the memory of your whole app tens/low hundreds of MB of memory being held alive doing nothing (other than handling new chunks) until whatever streaming API this is using under the hood is finished streaming.
andrewmutz
Threads?
kyledrake
That looks good, I didn't see that earlier.
jatins
Such a breath of fresh air compared to poor DX libraries like langchain
nullpoint420
I’ve found the Ruby community really cares about DUX. Not sure why it’s not in other language communities
toasterlovin
I don’t really mean this to be derogatory toward people who enjoy other things, but Ruby is a language and ecosystem by and for people who have taste.
continuational
Certainly a taste for global state, it seems.
madeofpalk
For a certain type of taste.
choxi
Matz said he designed Ruby to optimize for developer happiness, it’s just a core principle of the language since it was created
kuboble
Happiness of a developer writing code can be a misery of a one having to read / debug it. I worked in ruby for a couple years around 2009 and having to deal with a code that implemented most of its logic via method missing is still one of the strongest negative memories I have about coding.
RangerScience
Every language prioritizes something (or somethings) because every language was made by a person (or people) with a reason; python and correctness; Java and splitting up work; Go and something like "simplicity" (not that these are the only priorities for each language). As another comment points out, Matz prioritized developer happiness.
My favorite example of this is the amazing useful and amazing whack Ruby array arithmetic; subtraction (`arr1 - arr2`) is element-wise removal, but addition (`arr1 + arr2`) is a simple append. These are almost always exactly what you want to do when you reach for them, but they're completely "incorrect" mathematically.
okeuro49
> python and correctness
I thought it was Python and readability and "one way of doing things".
rochak
Umm, doesn’t Go do so as well? Personally, I’ve had a better experience working with Go tooling.
danenania
I'd say they both optimize for DX, but they come at it from very different angles. Ruby is focused on actually writing the code: making it feel expressive, intuitive, and fun.
Go is more about making it easier to build fast and robust systems. But it really doesn't care if the code itself is ugly and full of boilerplate.
As I've gotten more experience, I've come to really appreciate Go's tradeoffs. It's not as fun up front, but on the other hand, you're less likely to get server alerts at 4am. It really depends what you're building though.
jatins
Go ecosystem is generally good. However, given that Go as a language doesn't have any "fancy" (for the lack of a better word) syntactical features you can't create DSL's like this
though Ruby's expressiveness comes at a cost and I'd personally stick with Go in a team but use something like RubyLLM for personal projects
jasongill
I was an early contributor to Langchain and it was great at first - keep in mind, that's before chat models even existed, not to mention tools, JSON mode, etc.
Langchain really, I think, pushed the LLM makers forward toward adding those features but unfortunately it got left in the dust and became somewhat of a zombie. Simultaneously, the foundational LLM providers kept adding things to turn them more into a walled garden, where you no longer needed to connect multiple things (like scraping websites with one tool, feeding that into the LLM, then storing in a vector datastore - now that's all built in).
I think Langchain has tried to pivot (more than once perhaps) but had they not taken investor $$ early on (and good for them) I suspect that it would have just dried up and the core team would have gone on to work at OpenAI, Anthropic, etc.
ekianjo
langchain and llamaindex are such garbage libraries: not only they never document half of the features they have, but they keep breaking their APIs from one version to the next.
brokegrammer
I was about to mention those. I decided a while ago to build everything myself instead of relying on these libraries. We could use a PythonLLM over here because it seems like nobody cares about developer experience in the Python space.
earcar
Thank you! This is what the Ruby community has always prioritized - developer experience. Making complex things simple and joyful to use isn't just aesthetic preference, it's practical engineering. When your interface matches how developers think about the problem domain, you get fewer bugs and more productivity.
olegp
Would anyone happen to know of a similar library with as good DX but for JavaScript or TypeScript?
kakasoo
[dead]
SkyPuncher
IMO, the samples look great because they're ridiculously simple.
It doesn't deal with any of the hard problems you'll routine face with implementation.
someothherguyy
What about it is a breath of fresh air? What do the other libraries do that this doesn't?
gregmolnar
Be careful with the examples though: https://github.com/crmne/ruby_llm/issues/25
earcar
Thanks for flagging this. The eval was only in the docs and meant only as an example, but we definitely don't want to promote dangerous patterns in the docs. I updated them.
soheil
bobby drop table, still a thing
ketzo
Is this gonna be the thing that finally makes me tried Rails? Ruby syntax really is just nice.
drdaeman
I think it's the very nice-looking and clean high-level API that should be a pleasure to use (when it fits the job, of course).
I'm pretty sure this API semantics (instance builder to configure, and then it's ask/paint/embed with language-native way to handle streaming and declarative tools) would look beautiful and easy to use in many other languages, e.g. I can imagine a similar API - save, of course, for the Rails stuff - in Python, C# or Erlang. While this level of API may be not perfectly sufficient for all possible LLM use cases, it should certainly speed up development time when this level of API is all that's possible needed.
ilrwbwrkhv
Oh just beautiful. Ruby is so expressive and concise.
If you see the typescript options it's like giving yourself a water boarding session through your own volition.
gedy
Is it really Ruby or they just made a nice interface? I don't see why a hypothetical TypeScript example would be all that different.
// Just ask questions
const chat: Chat = LLM.chat;
chat.ask("What's the best way to learn TypeScript?");
// Analyze images
chat.ask("What's in this image?", { image: "ts_conf.jpg" });
// Generate images
LLM.paint("a sunset over mountains in watercolor style");
// Create vector embeddings
LLM.embed("TypeScript is powerful and scalable");
// Let AI use your code
class Calculator {
description = "Performs calculations";
params = {
expression: { type: "string", desc: "Math expression to evaluate" },
};
execute(args: { expression: string }): string {
return eval(args.expression).toString();
}
}
chat.withTool(new Calculator()).ask("What's 123 * 456?");
williamcotton
It's the extra parens, semi-colons, keywords and type annotations. Ruby makes the tradeoff for legibility above all else. Yes, you can obviously read the TypeScript, but there's an argument to be made that it takes more effort to scan the syntax as well as to write the code.
Also:
const chat: Chat = LLM.chat;
...is not instantiating a class, where Ruby is doing so behind the scenes. You'd need yet another pair of parens to make a factory!This is mainly a matter of syntactic style!
drdaeman
> It's the extra parens, semi-colons, keywords and type annotations.
I always thought such minor syntactic differences are unimportant, except for the folks who still learn syntax and haven't seen too many languages out there to stop caring much about it.
YMMV of course, but whenever I need to jump hoops with some API or have things conveniently returned to me in a single call matters a lot for my developer happiness. Whenever my code needs semicolons or indentation or parens feels such a negligibly tiny nuance to me but things like this don't even blip on my mental radar... I always think about what the code does, and don't even see those details (unless I have a typo lol).
Maybe my opinion on this is just the echoes from the ancient C vs Pascal vs BASIC syntax holy wars while I was still a schoolkid, idk. I mean, when I wrote Scheme or Lisp I haven't really "seen" all those parentheses (but then, I just checked some Lisp code and syntax looks off and takes time to get through, since I haven't practiced it in a long while and it's pretty different from anything I've used any recently).
Again, YMMV, but `const chat = new LLM.Chat();` and `chat = RubyLLM.chat` are exactly the same thing to me - I don't remember actual tokens from the screen, I immediately mentally process those both as something like "instantiate a chat object and assign `chat` to it" (without really verbalizing it much, but as an concept/idea). And I don't think a little syntactic noise like `const` or `;` is making things worse or better for me. Although, to be fair, I could be wrong here - I haven't really did any experiments in this regards, with properly defined methodology and metrics, and my subjective perception could be deceptive. Sadly, I'm no scientist and not even sure how to set up one correctly...
maleldil
chat = RubyLLM.chat
Is ambiguous, though. You can't know if it's an assignment or creating a new object. I don't think that's more readable.kakasoo
[dead]
sunrabbit
[dead]
freen
Wow. So thoughtful.
Ruby: late to the party, brought a keg.
wtf242
been using https://github.com/alexrudall/ruby-openai for years with no issues which is a fine gem and works great.
strudey
aw thanks, glad you like it! more good Ruby AI libraries is a good thing IMO
dismalaf
Ruby has a bunch of tools to interact with LLMs already. Also has had bindings to stuff like Torch and Tensorflow for years.
someothherguyy
What about it is so thoughtful?
aguynamedben
Ruby is alive and well!
init0
One of the most concise APIs to interact with an llm!
Keep going! Happy to see ollama support PR in draft.
jiangplus
I am writing some LLM-based app scripts, this feels like a breeze!
soheil
Feels more useful for something like cli where you want to run one-off commands to test something instead of running it in production given how non-deterministic the behavior can be for example for something like
chat.ask "What's being said?", with: { audio: "meeting.wav" }
definitely don't want users to get a valid response only 75% of the times, maybe?tommica
Wow the syntax is beautiful!
nextaccountic
That's my takeaway from Ruby - syntax matters, and good syntax makes programmers happier
dkobia
I support software in many languages and ruby has to be the most syntactically stimulating.
brink
You're confusing beautiful with simple. There's a lot of complexity and magic that's hidden behind the curtains of that "beautiful" syntax. Great for scripts and small programs, and an absolute nightmare on large projects. It's too simple.
the_gastropod
As a general rule of thumb, don’t yuck someone else’s yum. Plenty of people like the trade offs Ruby makes. And plenty of absolutely huge businesses use it quite successfully (e.g., Shopify, GitHub, GitLab, Airbnb, Stripe).
If you don’t like it, don’t use it.
brink
I'll yuck whatever yum I please. I'm not here for your approval. I did 10 years of Ruby. I have some authority on the matter.
desireco42
I am really impressed and delighted how simple this library is.
I agree that waiting for response can be an issue. I don't think this is meant to be for such purposes, but for tools that would process and create artifacts based on inputs.
I love Mistral and local LLMs, so this would probably the thing I would like to add.
This interface needs to have a better relationship with streaming, there is always a lag in response and a lot of people are going to want to stream the response in non blocking threads instead of hanging the process waiting for the response. Its possible this is just a documentation issue, but either way streaming is a first class citizen on anything that takes more than a couple seconds to finish and uses IO.
Aside from that the DSL is quite excellent.