Skip to content

Rails Generator for RubyLLM Models #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 36 commits into
base: main
Choose a base branch
from

Conversation

kieranklaassen
Copy link
Contributor

@kieranklaassen kieranklaassen commented Mar 27, 2025

This PR adds a Rails generator for RubyLLM models, making it easier to integrate RubyLLM with Rails applications.

Changes in this PR

  • Added generator that creates Chat, Message, and ToolCall models with migrations
  • Updated Rails integration guide with generator instructions
  • Added support for customizing model names to avoid namespace collisions
  • Added cross-database support for JSON columns

Recent updates to address PR comments

  1. Renamed README.md.tt to INSTALL_INFO.md.tt to avoid confusion with the project README, and updated the reference in the install_generator.rb file.

  2. Fixed migration order by using timestamps in the migration filenames in the install_generator.rb file. This ensures that migrations run in the correct order (chats first, then tool_calls, then messages).

  3. Updated railtie.rb to include rake tasks from the lib/ruby_llm/tasks directory, which addresses the issue mentioned in the comments about rake tasks not showing up in Rails applications.

  4. Reverted several changes in docs/guides/rails.md as requested in the comments, including fixing formatting issues and removing unnecessary content.

  5. Removed the comment about migration order from the create_messages_migration.rb.tt file since we're now enforcing it with timestamps.

These changes should address all the issues mentioned in the PR comments.

kieranklaassen and others added 4 commits March 27, 2025 09:50
The generator creates Chat, Message, and ToolCall models with migrations
for seamless Rails integration. Users can run 'rails generate ruby_llm:install'
to automatically set up all required models and database tables.

- Add install_generator with templates for models and migrations
- Update Rails integration guide with generator instructions
- Update README with two integration options
- Update Railtie to register the generator
- Add CLAUDE.md for agent assistance

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Added tests for the Rails generator template files to ensure they contain the expected content. Fixed a Zeitwerk warning by ignoring the generators directory in the loader.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Updated the tool_calls migration to detect and use the appropriate JSON
column type based on the database adapter:
- Uses jsonb for PostgreSQL databases (better performance and indexing)
- Falls back to standard json type for other databases (MySQL, SQLite, etc.)
- Added postgresql? detection method with proper error handling
- Added comprehensive tests for database adapter detection

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
kieranklaassen and others added 8 commits March 27, 2025 09:55
This adds a helpful README template that's displayed after successful generator installation, providing users with clear next steps and examples of how to use the generated models.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Addresses PR feedback to remove redundant comments from the migration template.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Renamed all template files to use .tt extension following Rails convention
- Updated generator code and tests to reference the new template files
- Fixed style issues in the generator code according to Rubocop

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Split large tests into smaller focused tests
- Fix string quotes to follow style guidelines
- Remove trailing whitespace
- Improve test readability and organization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Removed unnecessary whitespace in railtie.rb for cleaner code.
- Added comprehensive tests for the RubyLLM::InstallGenerator to ensure migration and model templates are correctly defined and structured.
- Deleted outdated template files spec to streamline test organization.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jamster
Copy link

jamster commented Mar 29, 2025

I created a PR on this PR (not sure where to put it so commenting here). This PR adds to this one by allowing the user to pass in the class (and hence table) names for the Chat, Message and ToolCall classes and tables. I had an existing set of tables that did have conflicts, and my new use case was better suited with a different taxonomy.

PR for Dynamic Names

Here is an example:

rails generate ruby_llm:install --chat-model-name=Review --message-model-name=ReviewMessage --force

@kieranklaassen
Copy link
Contributor Author

kieranklaassen commented Mar 30, 2025

Amazing! Thanks @jamster once merge conflicts are resolved I'll merge it

jamster and others added 3 commits March 31, 2025 11:40
readme as template, migration # handled by rails, remove `frozen_literal` from models and migrations

forcing timestamp order for migrations

make act_as methods dynamic, cleaned ordering/references
Making class and table names optional
@kieranklaassen
Copy link
Contributor Author

Ok ready for review!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be annoying to have a new README? I would delete this

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name "README" is a bit misleading here. This is the text generated AFTER you run the generator to explain what you did, whereas the readme for RubyLLM remains the same and untouched. Originally, this did overwrite the README, but has been corrected to be more of an ephemeral display post install.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you can see it in the test added too. It's to give direction after using the generator.

@@ -0,0 +1,16 @@
# This migration must be run AFTER create_<%= options[:chat_model_name].tableize %> and create_<%= options[:tool_call_model_name].tableize %> migrations
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to enforce this constraint with the timestamp of the migration? Disclaimer: I'm new to making templates for migrations.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments on latest PR below

def create_migration_files
# Use a fixed timestamp for testing and to ensure they're sequential
# @migration_number = Time.now.utc.strftime('%Y%m%d%H%M%S')
migration_template 'create_chats_migration.rb.tt', "db/migrate/create_#{options[:chat_model_name].tableize}.rb"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah there you go that's where we can enforce the order.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kieranklaassen can you enforce the order by using timestamps in the name just like rails does?

ruby_llm.gemspec Outdated
Comment on lines 32 to 34
# Add generator files
spec.files += Dir.glob('lib/generators/**/*')

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be already covered by the lines above

ruby_llm.gemspec Outdated
Comment on lines 42 to 44

# Development dependencies
spec.add_development_dependency 'rails', '>= 7.0.0'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't add dev dependencies here, we do it in the Gemfile

Comment on lines 9 to 10
let(:template_dir) { File.join(File.dirname(__FILE__), '../../../../lib/generators/ruby_llm/install/templates') }
let(:generator_file) { File.join(File.dirname(__FILE__), '../../../../lib/generators/ruby_llm/install_generator.rb') }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File.dirname(__FILE__) can be __dir__ right?

@crmne crmne added the enhancement New feature or request label Apr 2, 2025
kieranklaassen and others added 2 commits April 2, 2025 13:39
Address PR feedback and cleanup

- Replace File.dirname(__FILE__) with __dir__ in specs for better Ruby
idioms
- Remove redundant spec.files line for generators (already covered by
lib/**/*)
- Move rails development dependency to Gemfile from gemspec
- Update example in README to use ActiveJob instead of controller for
better practices

Note on migration timestamps: The current implementation has no table
dependencies,
so migration order is not critical. Rails generators automatically
handle
timestamps for migrations, making manual timestamp management
unnecessary.
@jamster
Copy link

jamster commented Apr 8, 2025

I've made the updates you requested and created this PR: kieranklaassen#2
Not sure the best workflow here with PRs to PRs, etc. Happy to do this better with any direction.

for context, the PR addresses:


Address PR feedback and cleanup

  • Replace File.dirname(FILE) with dir in specs for better Ruby idioms
  • Remove redundant spec.files line for generators (already covered by lib/**/*)
  • Move rails development dependency to Gemfile from gemspec
  • Update example in README to use ActiveJob instead of controller for better practices

Note on migration timestamps: The current implementation has no table dependencies,
so migration order is not critical. Rails generators automatically handle
timestamps for migrations, making manual timestamp management unnecessary.

@kieranklaassen kieranklaassen requested a review from crmne April 10, 2025 16:31
@kieranklaassen
Copy link
Contributor Author

Thanks @jamster !! I've been really busy so appreciate doing the work!!

@kieranklaassen
Copy link
Contributor Author

@crmne Let me know if you need any more chnages, waiting for review

@kieranklaassen kieranklaassen requested a review from jamster April 18, 2025 16:15
kieranklaassen and others added 6 commits April 18, 2025 14:33
…mprove formatting

- Added a section on using the generator for setting up RubyLLM in Rails applications.
- Improved formatting for better readability, including table of contents and notes.
- Updated examples to reflect changes in model creation and button usage in forms.
…cord creation

- Modified examples in the Rails integration guide to reflect the inclusion of the `user` parameter when creating a new chat record.
- Updated model_id in examples to 'gpt-4.1-nano' for consistency.
- Introduced the `ChatStreamJob` class to the Rails integration guide, detailing its purpose and functionality in handling chat streaming.
- Included comments within the job to explain the process of saving user messages and broadcasting assistant message updates.
Copy link
Owner

@crmne crmne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! there are a couple things to change and then I'm ready to merge it

@@ -7,31 +7,67 @@ permalink: /guides/rails
---

# Rails Integration

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

Comment on lines 12 to 13
RubyLLM offers seamless integration with Ruby on Rails applications through helpers for ActiveRecord models. This allows you to easily persist chat conversations, including messages and tool interactions, directly in your database.
{: .fs-6 .fw-300 }
RubyLLM offers seamless integration with Ruby on Rails applications through helpers for ActiveRecord models. This allows you to easily persist chat conversations, including messages and tool interactions, directly in your database. {: .fs-6 .fw-300 }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stick to previous version


## Table of contents

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

Comment on lines 18 to 19
1. TOC
{:toc}
1. TOC {:toc}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stick to previous version

Comment on lines 25 to 28
* How to set up ActiveRecord models for persisting chats and messages.
* How to use `acts_as_chat` and `acts_as_message`.
* How chat interactions automatically persist data.
* A basic approach for integrating streaming responses with Hotwire/Turbo Streams.
- How to set up ActiveRecord models for persisting chats and messages.
- How to use `acts_as_chat` and `acts_as_message`.
- How chat interactions automatically persist data.
- A basic approach for integrating streaming responses with Hotwire/Turbo Streams.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary change

Comment on lines 124 to 160
{: .note }
The `acts_as` helpers primarily handle loading history and saving messages/tool calls related to the chat interaction. Add your application-specific logic (associations, validations, scopes, callbacks) as usual.
{: .note } The `acts_as` helpers primarily handle loading history and saving messages/tool calls related to the chat interaction. Add your application-specific logic (associations, validations, scopes, callbacks) as usual.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stick to previous version

Comment on lines 166 to 174
```ruby
# config/initializers/ruby_llm.rb
RubyLLM.configure do |config|
config.openai_api_key = ENV["OPENAI_API_KEY"]
config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
# Add other provider keys as needed
end
```

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for this, we have the configuration guide for it

Comment on lines 254 to 310
{: .note }
This example shows the core idea. You'll need to adapt the broadcasting, targets, and partials for your specific UI needs (e.g., handling Markdown rendering, adding styling, showing typing indicators). See the [Streaming Responses Guide]({% link guides/streaming.md %}) for more on streaming itself.
{: .note } This example shows the core idea. You'll need to adapt the broadcasting, targets, and partials for your specific UI needs (e.g., handling Markdown rendering, adding styling, showing typing indicators). See the [Streaming Responses Guide]({% link guides/streaming.md %}) for more on streaming itself.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stick to previous version

Comment on lines 317 to 322
* [Chatting with AI Models]({% link guides/chat.md %})
* [Using Tools]({% link guides/tools.md %})
* [Streaming Responses]({% link guides/streaming.md %})
* [Working with Models]({% link guides/models.md %})
* [Error Handling]({% link guides/error-handling.md %}) No newline at end of file
- [Chatting with AI Models]({% link guides/chat.md %})
- [Using Tools]({% link guides/tools.md %})
- [Streaming Responses]({% link guides/streaming.md %})
- [Working with Models]({% link guides/models.md %})
- [Error Handling]({% link guides/error-handling.md %})
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stick to previous version

def create_migration_files
# Use a fixed timestamp for testing and to ensure they're sequential
# @migration_number = Time.now.utc.strftime('%Y%m%d%H%M%S')
migration_template 'create_chats_migration.rb.tt', "db/migrate/create_#{options[:chat_model_name].tableize}.rb"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kieranklaassen can you enforce the order by using timestamps in the name just like rails does?

@@ -8,5 +8,15 @@ class Railtie < Rails::Railtie
include RubyLLM::ActiveRecord::ActsAs
end
end

# Include rake tasks if applicable

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should include the ruby_llm rake tasks in a Rails application:

    rake_tasks do
      path = File.expand_path(__dir__)
      Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
    end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an issue for the rake task not show up in a Rails application: #136

This is making it harder for me to use the latest Gemini models.

@kieranklaassen kieranklaassen changed the title Rails Generators Rails Generator for RubyLLM Models Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants