Hey all,
I had a project that needed a proper AI-assistant layer, and after digging through the shards ecosystem I couldn’t find anything that fit, so I built my own: agent.cr.
It’s heavily inspired by the excellent RubyLLM gem (kudos to them! A lot of the ergonomics are stolen with love
).
It wraps any OpenAI-compatible streaming endpoint behind a fiber-based async interface, so #ask never blocks your code.
What it does today:
- Tools / function calling with automatic resolution (no manual while-loop), plus enable/disable mid-conversation
- Attachments — images, audio, text files, auto-detected by extension
- Streamed content with typed chunks (content, reasoning tokens, tool-call deltas)
- Multi-turn agentic loop with automatic history tracking + trimming
- Dump / restore a session (history, tool state, and prompt-cache affinity preserved)
- A middleware-style handler chain that wraps each pipeline stage: turn, streamed chunk, tool call, error… so you can inspect, mutate, or short-circuit. Handy for gating tool calls behind user confirmation, enforcing a token budget, or rewriting content in-flight.
- Pluggable provider abstraction (anything OpenAI-compatible: OpenAI, OpenRouter, local llama.cpp, Ollama…). The architecture is open for non-OpenAI protocols (Anthropic) but I’m not planning to implement them in the near future.
What it doesn’t do: MCP. No plan in the near term; I don’t run MCP servers for this project. Maybe later for customer integrations, but not a priority.
Next up: embedding and STT endpoints as “extra” features, then I’ll tag 0.1.
There are a couple of self-contained examples in examples/. The fun one is a stupid little text-based RPG where the LLM is your Dungeon Master and Crystal tools enforce the rules. No cheating, not even for the DM:
crystal run examples/rpg.cr # Note: Setup in .env.local first your ENV variable to your LLM
There’s also examples/handlers.cr if you want to see the middleware chain in action (token budgeting, tool confirmation, in-flight word replacement).
Tested both locally (Strix Halo box running Qwen3.6) and over OpenRouter (Deepseek v4 & GLM5.2).
Repo: GitHub - anykeyh/agent.cr: Agentic in Crystal Lang · GitHub
The code was written with AI-assistance, but not vibe-coded (e.g. control on each steps). For those interested, globally AI is working well with Crystal Lang, except some confusions with Ruby methods.
Would love to hear what you think !