{"id":31250540,"url":"https://github.com/pnvang/llm-agent-rails","last_synced_at":"2026-05-19T07:34:04.431Z","repository":{"id":312969703,"uuid":"1049488649","full_name":"pnvang/llm-agent-rails","owner":"pnvang","description":"Rails engine for LLM-powered agents — slot filling, tool orchestration, and safe backend execution.","archived":false,"fork":false,"pushed_at":"2026-05-07T06:58:03.000Z","size":48,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-07T08:40:09.487Z","etag":null,"topics":["agents","ai","gem","llm","nlp","rails","ruby"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/llm-agent-rails","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pnvang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-03T04:00:56.000Z","updated_at":"2025-09-11T00:29:40.000Z","dependencies_parsed_at":"2025-09-03T06:18:20.642Z","dependency_job_id":null,"html_url":"https://github.com/pnvang/llm-agent-rails","commit_stats":null,"previous_names":["pnvang/llm-agent-rails"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/pnvang/llm-agent-rails","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-agent-rails","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-agent-rails/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-agent-rails/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-agent-rails/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pnvang","download_url":"https://codeload.github.com/pnvang/llm-agent-rails/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pnvang%2Fllm-agent-rails/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33206320,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-19T07:16:55.748Z","status":"ssl_error","status_checked_at":"2026-05-19T07:16:54.366Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","ai","gem","llm","nlp","rails","ruby"],"created_at":"2025-09-23T05:25:52.245Z","updated_at":"2026-05-19T07:34:04.425Z","avatar_url":"https://github.com/pnvang.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# llm-agent-rails\n\nAI intake forms for Rails.\n\n`llm-agent-rails` is a Rails-native layer on top of `llm-fillin` for slot-filling workflows: collect missing fields through conversation, validate before execution, confirm before submit, and run safe Rails backend actions exactly once.\n\nIt is built for booking leads, quote requests, onboarding forms, support tickets, and internal admin workflows. It is not a broad agent framework.\n\n## Installation\n\n```ruby\ngem \"llm-agent-rails\"\n```\n\nThen:\n\n```bash\nbundle install\nbin/rails generate llm_agent_rails:install\nbin/rails db:migrate\n```\n\nThe install generator creates:\n\n- `config/initializers/llm_agent_rails.rb`\n- `app/llm_intakes/.keep`\n- database migrations for intake threads, messages, slot values, and executions\n- a route mount example\n\nMount the engine at `/llm`:\n\n```ruby\nmount Llm::Agent::Rails::Engine =\u003e \"/llm\"\n```\n\n## Create An Intake\n\n```ruby\n# app/llm_intakes/booking_lead_intake.rb\nclass BookingLeadIntake \u003c LlmAgentRails::Intake\n  description \"Collect event details for a booking lead\"\n\n  slot :name, type: :string, required: true\n  slot :email, type: :string, required: true, format: :email\n  slot :event_date, type: :date, required: true\n  slot :start_time, type: :string, required: true\n  slot :end_time, type: :string, required: true\n  slot :location, type: :string, required: true\n  slot :guest_count, type: :integer, required: false\n  slot :package, type: :string, enum: [\"Gold\", \"Platinum\", \"Emerald\"], required: false\n\n  confirm_before_submit true\n\n  def submit(values, context:)\n    BookingLead.create!(values)\n  end\nend\n```\n\nOr generate one:\n\n```bash\nbin/rails generate llm_agent_rails:intake BookingLead name:string email:string event_date:date location:string\n```\n\n## Intake Endpoint\n\n`POST /llm/intakes/:id/step`\n\nInput:\n\n```json\n{\n  \"thread_id\": \"booking-123\",\n  \"message\": \"name: Mina Park email: mina@example.com\",\n  \"context\": { \"tenant_id\": \"acct_1\", \"actor_id\": \"user_1\" }\n}\n```\n\nExample response when fields are missing:\n\n```json\n{\n  \"status\": \"needs_clarification\",\n  \"assistant_message\": \"What is the event date?\",\n  \"slots\": {\n    \"name\": \"Mina Park\",\n    \"email\": \"mina@example.com\"\n  },\n  \"missing_slots\": [\"event_date\", \"start_time\", \"end_time\", \"location\"],\n  \"invalid_slots\": {},\n  \"ready_to_confirm\": false,\n  \"ready_to_execute\": false,\n  \"executed\": false,\n  \"execution_result\": null,\n  \"idempotency_key\": \"intake-...\",\n  \"thread_id\": \"booking-123\"\n}\n```\n\n## Confirmation Flow\n\nWhen all required slots are valid and `confirm_before_submit true` is set, the engine asks for confirmation:\n\n```json\n{\n  \"status\": \"needs_confirmation\",\n  \"assistant_message\": \"Please confirm: name: Mina Park, email: mina@example.com, event date: 2026-06-20. Should I submit this?\",\n  \"ready_to_confirm\": true,\n  \"executed\": false\n}\n```\n\nConfirm with the same `thread_id`:\n\n```bash\ncurl -X POST http://localhost:3000/llm/intakes/booking_lead/step \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"thread_id\":\"booking-123\",\"message\":\"yes\"}'\n```\n\nThe response includes the Rails return value:\n\n```json\n{\n  \"status\": \"executed\",\n  \"assistant_message\": \"Submitted.\",\n  \"executed\": true,\n  \"execution_result\": {\n    \"id\": 42,\n    \"name\": \"Mina Park\"\n  },\n  \"thread_id\": \"booking-123\"\n}\n```\n\n## Idempotency\n\nEach submit has a stable idempotency key derived by `llm-fillin`. The engine stores execution records in ActiveRecord and replays completed results when a browser retries the same confirmed thread. Required slots must be valid and confirmation must pass before Rails application code runs.\n\nThe generated migrations create:\n\n- `LlmAgentRails::Thread`\n- `LlmAgentRails::Message`\n- `LlmAgentRails::SlotValue`\n- `LlmAgentRails::Execution`\n\n## Provider Configuration\n\nAPI keys are server-side only and are never exposed to the browser.\n\nThe default generated initializer uses the fake adapter:\n\n```ruby\nLlmAgentRails.configure do |config|\n  config.provider = :fake\n  config.adapter = LlmAgentRails::Adapters::Fake.new\nend\n```\n\nFor OpenAI, add `gem \"openai\"` to your Rails app and configure through `llm-fillin`:\n\n```ruby\nLlmAgentRails.configure do |config|\n  config.provider = :openai\n  config.model = \"gpt-4.1-mini\"\n  config.temperature = 0\n  config.adapter = -\u003e(c) {\n    LlmFillin::Adapters::OpenAI.new(\n      api_key: ENV.fetch(\"OPENAI_API_KEY\"),\n      model: c.model,\n      temperature: c.temperature\n    )\n  }\nend\n```\n\nAny object that implements `#extract(workflow:, message:, slots:, context:)` can be used as an adapter.\n\n## Testing With Fake Adapter\n\nTests should not make real API calls:\n\n```ruby\nLlmAgentRails.configure do |config|\n  config.adapter = LlmAgentRails::Adapters::Fake.new\nend\n```\n\nThe fake adapter understands simple `field: value` messages, which makes request specs deterministic.\n\n## Compatibility\n\nThe gem name remains `llm-agent-rails`. The old `Llm::Agent::Rails` namespace and `POST /step` endpoint are still present where reasonable for 0.1 compatibility, but the preferred API is intake-oriented:\n\n```ruby\nLlmAgentRails::Intake\nPOST /llm/intakes/:id/step\n```\n\n## How This Relates To llm-fillin\n\n`llm-fillin` is the framework-light Ruby core: workflow definitions, slot validation, confirmation, result objects, provider adapters, and idempotent handler execution.\n\n`llm-agent-rails` adds the Rails-native layer: autoloaded intake classes, ActiveRecord persistence, JSON endpoints, Rails generators, and dummy/demo app patterns.\n\n## Migration From 0.1 Agent API\n\nThe old agent/tool-call route remains available as `POST /llm/step` when mounted at `/llm`, and the old `Llm::Agent::Rails` namespace still delegates to the new configuration object.\n\nFor new work, move JSON-schema tools into intake classes:\n\n```ruby\nclass SupportTicketIntake \u003c LlmAgentRails::Intake\n  slot :email, type: :string, required: true, format: :email\n  slot :summary, type: :string, required: true\n\n  confirm_before_submit true\n\n  def submit(values, context:)\n    SupportTicket.create!(values.merge(created_by_id: context[:actor_id]))\n  end\nend\n```\n\nThe engine persists the thread, slot state, messages, and execution result for you. Provider calls still go through `llm-fillin` adapters.\n\n## Commands\n\n```bash\nbundle install\nbundle exec rake test\n```\n\nDummy app boot smoke test:\n\n```bash\nbundle exec ruby -e 'require_relative \"test/dummy/config/environment\"; puts Rails.application.class.name'\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpnvang%2Fllm-agent-rails","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpnvang%2Fllm-agent-rails","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpnvang%2Fllm-agent-rails/lists"}