0.0
The project is in a healthy, maintained state
Drop-in Rails engine to register JSON-schema tools, let an Llm fill missing fields, validate input, and execute handlers safely.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 0.21
>= 7.0
 Project Readme

llm-agent-rails

Rails engine for LLM‑powered agents — slot filling, tool orchestration, and safe backend execution.
Turn chat into validated function calls using JSON‑Schema, then run your Ruby handlers with idempotency.


What this gem gives you

  • A mountable endpoint: POST /llm/agent/step that talks to an LLM.
  • Slot filling: the model asks for missing fields until a tool call is ready.
  • Tool registry: register functions (name+version+schema+handler) the LLM can call.
  • Validation: arguments are validated with json_schemer before your handler runs.
  • Idempotency: unique per‑thread keys for safe “create” operations.
  • Adapter: OpenAI adapter using the openai ~> 0.21 Ruby SDK.
  • Memory store: persists tool results per thread_id (swap for Redis in prod).
  • Generators:
    • rails g llm:agent:install (initializer + mount routes)
    • rails g llm:agent:tickets_demo (optional demo tool + migration)

Install

Add to your Gemfile:

gem "llm-agent-rails", "~> 0.1"

Then:

bundle install
bin/rails g llm:agent:install
# mounts the engine at /llm/agent and creates config/initializers/llm_agent.rb

Optional demo (creates a Ticket model/tool so you can see tool-calling end‑to‑end):

bin/rails g llm:agent:tickets_demo
bin/rails db:migrate

Configure your API key:

export OPENAI_API_KEY=sk-...

Run the app:

bin/rails s

API — talk to the agent

POST /llm/agent/step

Body

{
  "thread_id": "demo-123",
  "messages": [
    { "role": "user", "content": "Open a ticket for checkout failing on Apple Pay" }
  ]
}
  • thread_id keeps tool state & idempotency consistent across turns.
  • messages is the chat transcript (array of {role, content}).

Response

One of:

{ "type": "assistant", "text": "a clarifying question or confirmation..." }

or

{ "type": "tool_ran", "tool_name": "create_ticket", "result": { "id": 42, "title": "..." } }

Errors look like:

{ "error": "BadRequest", "message": "messages is required (array of {role, content})" }

Example conversations

A. Client-managed transcript (simple)

Send the entire transcript each POST with the same thread_id.

curl -X POST http://localhost:3000/llm/agent/step   -H "Content-Type: application/json"   -d '{
    "thread_id": "demo-123",
    "messages": [
      { "role": "user", "content": "Open a ticket for checkout failing on Apple Pay" },
      { "role": "assistant", "content": "I can help with that! What is the description?" },
      { "role": "user", "content": "Description: iOS 17 checkout fails with tokenization error. Priority high." },
      { "role": "assistant", "content": "Ill create a ticket with the following details:\n\n- **Title**: Checkout failing on Apple Pay\n- **Description**: iOS 17 checkout fails with tokenization error.\n- **Priority**: High\n\nIs there a specific category or team this ticket should be assigned to?"},
      { "role": "user", "content": "Assign to the mobile team." }
    ]
  }'

If you haven’t registered a tool, you’ll receive {"type":"assistant","text":"..."} — a helpful structured summary.

B. With a tool registered (end‑to‑end create)

  1. Use the demo generator:
bin/rails g llm:agent:tickets_demo
bin/rails db:migrate

This adds app/llm_tools/tickets.rb and registers a create_ticket_v1 tool with JSON Schema validation and idempotency.

  1. Try again. Once all required fields are collected, the response includes the tool result:
{
  "type": "tool_ran",
  "tool_name": "create_ticket",
  "result": { "id": 123, "title": "Checkout failing on Apple Pay", "priority": "high", "key": "chat-demo-123-7a3c9e" }
}

How it works

  • Orchestrator: supplies tools to the model, loops until enough data is collected, then chooses exactly one tool to run.
  • Registry: your functions (name+version+schema+handler).
  • Validators: rejects bad inputs before your code runs.
  • Idempotency: generates chat-<thread>-<hex>, pass to your creates.
  • Store: remembers prior tool results by thread_id (swap to Redis).

Environment

  • OPENAI_API_KEY must be set.
  • Ruby ≥ 3.1, Rails ≥ 7.0.

License

MIT