langgenius/ruby-sdk: The SDK That Tells You to Use Something Else

A tiny Ruby client for Dify, and a bigger lesson about why AI tooling is drifting from provider-specific wrappers toward universal interfaces.

9 min read • View on GitHub • More from langgenius

A small hand-built skiff moves beside a much larger ferry on a river made of tangled cables and plugs. The scene explains the article's thesis: a narrow provider-specific wrapper can be useful, but a broader interface now looks like the more obvious route.
The repo feels less like a destination and more like a waypoint toward broader Ruby AI abstractions.
Key Takeaways

The most revealing line in langgenius/ruby-sdk is not in the code. It is in the README, where the maintainers tell Ruby programmers to use ruby_llm instead. That turns the repo into a useful client and a quiet admission that the category has moved.

The README says not to use this gem

Ruby programmer should using `ruby_llm`, which currently support all large LLM provider, also including Dify support.

Zigngit, Contributor · langgenius/ruby-sdk README

That sentence is blunt enough to feel odd, but it is also the clearest product statement in the repo. The gem exists to wrap Dify, yet its own README argues that Ruby developers are better served by a wider interface. In practice, that makes this project less of a destination and more of a marker on the road from provider-specific SDKs to universal ones.

One transport, three facades

The implementation is compact on purpose. A base DifyClient handles authentication and request dispatch, while subclasses such as ChatClient and CompletionClient shape the request into Dify's different app types. The point is not polymorphism for its own sake. It is to keep the public API readable while funneling everything through one private request path.

The SDK's real structure is one shared transport with a few typed entry points, not a sprawling class hierarchy.

class DifyClient
  def _send_request(method, endpoint, data = nil, params = nil, _stream: false)
    # prepare headers, serialize JSON, and send the request with Net::HTTP
  end
end

class ChatClient < DifyClient
  def create_chat_message(data)
    _send_request(:post, "/chat-messages", data)
  end
end

That shape matters because it keeps the moving parts visible. The client uses Ruby's standard Net::HTTP, and it reaches for multipart-post only where uploads demand it. You do not see a heavy abstraction stack here. You see a narrow tool that stays close to the wire, which is exactly what a provider wrapper should do when it has no ambition to become a platform of its own.

A close-up view of a brass manifold mounted to a wall, with one thick pipe entering from the left and three narrower pipes leaving in different directions. The image explains how a single request path can feed multiple client facades without turning the SDK into a large framework.
One inlet, several outlets, one shared core.

The comparison that actually matters

ProjectAbstractionStrengthTrade-off
langgenius/ruby-sdkDify-specific clientDirect mapping to Dify app typesOne provider, one surface
ruby_llmUniversal interfaceSwap providers without rewriting app codeLess provider-specific naming
langchainrbFrameworkChains, tools, parsers, and RAG workflowsHeavier mental model
openai-rubyOfficial clientDirect vendor support and idiomatic accessOpenAI only

That table is the real frame. The question is not whether this gem can talk to Dify. It obviously can. The question is whether Ruby AI development wants a small adapter for each vendor or a shared interface that survives the next tool choice. This repo answers that question with a shrug and a recommendation to use something broader.

An archived gem as a snapshot of a shift

The repository is archived and read-only, which makes the design feel even more like a time capsule. It was built as a clean Ruby bridge to Dify's chat, completion, workflow, feedback, conversation, and upload endpoints, and it does that job with very little ceremony. But the archive status changes the meaning of the code. It is no longer trying to win the abstraction war. It is evidence of the moment when Ruby's AI stack started preferring interfaces broad enough to outlast a single provider.