ruby_llm_community 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +21 -2
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +127 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +39 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +24 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +12 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +29 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +11 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +23 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +10 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +30 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +18 -0
- data/lib/generators/ruby_llm/generator_helpers.rb +129 -0
- data/lib/generators/ruby_llm/install/install_generator.rb +104 -0
- data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +2 -2
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +4 -4
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +8 -7
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +9 -3
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +6 -5
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +9 -8
- data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +4 -3
- data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +2 -5
- data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +2 -2
- data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +145 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +121 -0
- data/lib/ruby_llm/active_record/acts_as.rb +108 -467
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +403 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +336 -0
- data/lib/ruby_llm/active_record/message_methods.rb +72 -0
- data/lib/ruby_llm/active_record/model_methods.rb +84 -0
- data/lib/ruby_llm/aliases.json +72 -6
- data/lib/ruby_llm/attachment.rb +27 -0
- data/lib/ruby_llm/configuration.rb +6 -0
- data/lib/ruby_llm/image_attachment.rb +12 -3
- data/lib/ruby_llm/message.rb +1 -1
- data/lib/ruby_llm/mime_type.rb +4 -0
- data/lib/ruby_llm/model/info.rb +4 -0
- data/lib/ruby_llm/models.json +2640 -1756
- data/lib/ruby_llm/models.rb +5 -15
- data/lib/ruby_llm/provider.rb +6 -4
- data/lib/ruby_llm/providers/anthropic/media.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/models.rb +19 -1
- data/lib/ruby_llm/providers/gemini/capabilities.rb +5 -0
- data/lib/ruby_llm/providers/gemini/media.rb +1 -1
- data/lib/ruby_llm/providers/gpustack/media.rb +1 -1
- data/lib/ruby_llm/providers/ollama/media.rb +1 -1
- data/lib/ruby_llm/providers/openai/media.rb +4 -4
- data/lib/ruby_llm/providers/openai/response.rb +7 -6
- data/lib/ruby_llm/providers/openai/response_media.rb +1 -1
- data/lib/ruby_llm/providers/openai/streaming.rb +14 -11
- data/lib/ruby_llm/providers/openai/tools.rb +11 -6
- data/lib/ruby_llm/providers/vertexai.rb +1 -1
- data/lib/ruby_llm/providers/xai/capabilities.rb +166 -0
- data/lib/ruby_llm/providers/xai/chat.rb +15 -0
- data/lib/ruby_llm/providers/xai/models.rb +48 -0
- data/lib/ruby_llm/providers/xai.rb +46 -0
- data/lib/ruby_llm/railtie.rb +20 -3
- data/lib/ruby_llm/stream_accumulator.rb +0 -4
- data/lib/ruby_llm/utils.rb +5 -9
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm_community.rb +4 -3
- data/lib/tasks/models.rake +29 -5
- data/lib/tasks/ruby_llm.rake +15 -0
- data/lib/tasks/vcr.rake +2 -2
- metadata +33 -3
- data/lib/generators/ruby_llm/install/templates/INSTALL_INFO.md.tt +0 -108
- data/lib/generators/ruby_llm/install_generator.rb +0 -146
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91657c36438fabeadbfa5bc881be1d9c0750916511e54624627c11478a7c8603
|
4
|
+
data.tar.gz: 2cf908a30159a970a4a973356ae60ef3844ffb507fe0bf1a567c11d1ecfa1e46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c1cd8a73e001af382db8868d2c1037efe9e0bc411a559fdc36daa383897f2ba33aa42c6a4630e619c152f6879fd1e4a62a10e1c9c3a89a6745934db4c4b080d
|
7
|
+
data.tar.gz: 9a4391b67d16d171c35e9def92c0fd42d059c072ad03f148c14d0238cb5c59e00775fd42133417f086203bad228fd7d7b4b149f4190e93d1afd9e2bf78179531
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
|
10
10
|
Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="https://chatwithwork.com/logotype-dark.svg"><img src="https://chatwithwork.com/logotype.svg" alt="Chat with Work" height="30" align="absmiddle"></picture>](https://chatwithwork.com) — *Claude Code for your documents*
|
11
11
|
|
12
|
-
[](https://badge.fury.io/rb/ruby_llm)
|
13
13
|
[](https://github.com/testdouble/standard)
|
14
14
|
[](https://rubygems.org/gems/ruby_llm)
|
15
15
|
[](https://codecov.io/gh/crmne/ruby_llm)
|
@@ -17,6 +17,9 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
|
|
17
17
|
<a href="https://trendshift.io/repositories/13640" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13640" alt="crmne%2Fruby_llm | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
18
18
|
</div>
|
19
19
|
|
20
|
+
> [!NOTE]
|
21
|
+
> Using RubyLLM in production? [Share your story](https://tally.so/r/3Na02p)! Takes 5 minutes.
|
22
|
+
|
20
23
|
---
|
21
24
|
|
22
25
|
Build chatbots, AI agents, RAG applications. Works with OpenAI, Anthropic, Google, AWS, local models, and any OpenAI-compatible API.
|
@@ -27,6 +30,21 @@ Every AI provider ships their own bloated client. Different APIs. Different resp
|
|
27
30
|
|
28
31
|
RubyLLM gives you one beautiful API for all of them. Same interface whether you're using GPT, Claude, or your local Ollama. Just three dependencies: [Faraday](https://github.com/lostisland/faraday), [Zeitwerk](https://github.com/fxn/zeitwerk), and [Marcel](https://github.com/rails/marcel). That's it.
|
29
32
|
|
33
|
+
## How does RubyLLM Community differ?
|
34
|
+
|
35
|
+
Use this gem to get early access to features currently in PR at the main gem.
|
36
|
+
|
37
|
+
Some examples --
|
38
|
+
|
39
|
+
- Prompt caching for Anthropic
|
40
|
+
- Image editing for Gemini and OpenAI
|
41
|
+
- Responses API for OpenAI
|
42
|
+
- xAI provider that supports Grok 2, Grok 3, Grok 4, and Grok Code
|
43
|
+
|
44
|
+
This project is intended to be compatible with RubyLLM. I will attempt to keep it up to date as Carmine pushes commits often.
|
45
|
+
|
46
|
+
I may have to bump the major version if I have to break compatibility due to a change in direction on one of the PRs. I will provide instructions on how to upgrade in the release notes.
|
47
|
+
|
30
48
|
## Show me the code
|
31
49
|
|
32
50
|
```ruby
|
@@ -38,6 +56,7 @@ chat.ask "What's the best way to learn Ruby?"
|
|
38
56
|
```ruby
|
39
57
|
# Analyze any file type
|
40
58
|
chat.ask "What's in this image?", with: "ruby_conf.jpg"
|
59
|
+
chat.ask "What's happening in this video?", with: "video.mp4"
|
41
60
|
chat.ask "Describe this meeting", with: "meeting.wav"
|
42
61
|
chat.ask "Summarize this document", with: "contract.pdf"
|
43
62
|
chat.ask "Explain this code", with: "app.rb"
|
@@ -97,7 +116,7 @@ response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "pr
|
|
97
116
|
## Features
|
98
117
|
|
99
118
|
* **Chat:** Conversational AI with `RubyLLM.chat`
|
100
|
-
* **Vision:** Analyze images and
|
119
|
+
* **Vision:** Analyze images and videos
|
101
120
|
* **Audio:** Transcribe and understand speech
|
102
121
|
* **Documents:** Extract from PDFs, CSVs, JSON, any file type
|
103
122
|
* **Image generation:** Create images with `RubyLLM.paint`
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
module RubyLLM
|
6
|
+
module Generators
|
7
|
+
# Generates a simple chat UI scaffold for RubyLLM
|
8
|
+
class ChatUIGenerator < Rails::Generators::Base
|
9
|
+
source_root File.expand_path('templates', __dir__)
|
10
|
+
|
11
|
+
namespace 'ruby_llm:chat_ui'
|
12
|
+
|
13
|
+
argument :model_mappings, type: :array, default: [], banner: 'chat:ChatName message:MessageName ...'
|
14
|
+
|
15
|
+
desc 'Creates a chat UI scaffold with Turbo streaming\n' \
|
16
|
+
'Usage: rails g ruby_llm:chat_ui [chat:ChatName] [message:MessageName] ...'
|
17
|
+
|
18
|
+
def parse_model_mappings
|
19
|
+
@model_names = {
|
20
|
+
chat: 'Chat',
|
21
|
+
message: 'Message',
|
22
|
+
tool_call: 'ToolCall',
|
23
|
+
model: 'Model'
|
24
|
+
}
|
25
|
+
|
26
|
+
model_mappings.each do |mapping|
|
27
|
+
if mapping.include?(':')
|
28
|
+
key, value = mapping.split(':', 2)
|
29
|
+
@model_names[key.to_sym] = value.classify
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@model_names
|
34
|
+
end
|
35
|
+
|
36
|
+
%i[chat message model].each do |type|
|
37
|
+
define_method("#{type}_model_name") do
|
38
|
+
@model_names ||= parse_model_mappings
|
39
|
+
@model_names[type]
|
40
|
+
end
|
41
|
+
|
42
|
+
define_method("#{type}_table_name") do
|
43
|
+
table_name_for(send("#{type}_model_name"))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_views
|
48
|
+
# Chat views
|
49
|
+
template 'views/chats/index.html.erb', "app/views/#{chat_table_name}/index.html.erb"
|
50
|
+
template 'views/chats/new.html.erb', "app/views/#{chat_table_name}/new.html.erb"
|
51
|
+
template 'views/chats/show.html.erb', "app/views/#{chat_table_name}/show.html.erb"
|
52
|
+
template 'views/chats/_chat.html.erb',
|
53
|
+
"app/views/#{chat_table_name}/_#{chat_model_name.underscore}.html.erb"
|
54
|
+
template 'views/chats/_form.html.erb', "app/views/#{chat_table_name}/_form.html.erb"
|
55
|
+
|
56
|
+
# Message views
|
57
|
+
template 'views/messages/_message.html.erb',
|
58
|
+
"app/views/#{message_table_name}/_#{message_model_name.underscore}.html.erb"
|
59
|
+
template 'views/messages/_form.html.erb', "app/views/#{message_table_name}/_form.html.erb"
|
60
|
+
template 'views/messages/create.turbo_stream.erb',
|
61
|
+
"app/views/#{message_table_name}/create.turbo_stream.erb"
|
62
|
+
|
63
|
+
# Model views
|
64
|
+
template 'views/models/index.html.erb', "app/views/#{model_table_name}/index.html.erb"
|
65
|
+
template 'views/models/show.html.erb', "app/views/#{model_table_name}/show.html.erb"
|
66
|
+
template 'views/models/_model.html.erb',
|
67
|
+
"app/views/#{model_table_name}/_#{model_model_name.underscore}.html.erb"
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_controllers
|
71
|
+
template 'controllers/chats_controller.rb', "app/controllers/#{chat_table_name}_controller.rb"
|
72
|
+
template 'controllers/messages_controller.rb', "app/controllers/#{message_table_name}_controller.rb"
|
73
|
+
template 'controllers/models_controller.rb', "app/controllers/#{model_table_name}_controller.rb"
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_jobs
|
77
|
+
template 'jobs/chat_response_job.rb', "app/jobs/#{chat_model_name.underscore}_response_job.rb"
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_routes
|
81
|
+
model_routes = <<~ROUTES.strip
|
82
|
+
resources :#{model_table_name}, only: [:index, :show] do
|
83
|
+
collection do
|
84
|
+
post :refresh
|
85
|
+
end
|
86
|
+
end
|
87
|
+
ROUTES
|
88
|
+
route model_routes
|
89
|
+
chat_routes = <<~ROUTES.strip
|
90
|
+
resources :#{chat_table_name} do
|
91
|
+
resources :#{message_table_name}, only: [:create]
|
92
|
+
end
|
93
|
+
ROUTES
|
94
|
+
route chat_routes
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_broadcasting_to_message_model
|
98
|
+
msg_var = message_model_name.underscore
|
99
|
+
chat_var = chat_model_name.underscore
|
100
|
+
broadcasting_code = "broadcasts_to ->(#{msg_var}) { \"#{chat_var}_\#{#{msg_var}.#{chat_var}_id}\" }"
|
101
|
+
|
102
|
+
inject_into_class "app/models/#{msg_var}.rb", message_model_name do
|
103
|
+
"\n #{broadcasting_code}\n"
|
104
|
+
end
|
105
|
+
rescue Errno::ENOENT
|
106
|
+
say "#{message_model_name} model not found. Add broadcasting code to your model.", :yellow
|
107
|
+
say " #{broadcasting_code}", :yellow
|
108
|
+
end
|
109
|
+
|
110
|
+
def display_post_install_message
|
111
|
+
return unless behavior == :invoke
|
112
|
+
|
113
|
+
say "\n ✅ Chat UI installed!", :green
|
114
|
+
say "\n Start your server and visit http://localhost:3000/#{chat_table_name}", :cyan
|
115
|
+
say "\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def table_name_for(model_name)
|
121
|
+
# Convert namespaced model names to proper table names
|
122
|
+
# e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
|
123
|
+
model_name.underscore.pluralize.tr('/', '_')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class <%= chat_model_name.pluralize %>Controller < ApplicationController
|
2
|
+
before_action :set_<%= chat_model_name.underscore %>, only: [:show]
|
3
|
+
|
4
|
+
def index
|
5
|
+
@<%= chat_model_name.tableize %> = <%= chat_model_name %>.order(created_at: :desc)
|
6
|
+
end
|
7
|
+
|
8
|
+
def new
|
9
|
+
@<%= chat_model_name.underscore %> = <%= chat_model_name %>.new
|
10
|
+
@selected_model = params[:model]
|
11
|
+
end
|
12
|
+
|
13
|
+
def create
|
14
|
+
return unless prompt.present?
|
15
|
+
|
16
|
+
@<%= chat_model_name.underscore %> = <%= chat_model_name %>.create!(model: model)
|
17
|
+
<%= chat_model_name %>ResponseJob.perform_later(@<%= chat_model_name.underscore %>.id, prompt)
|
18
|
+
|
19
|
+
redirect_to @<%= chat_model_name.underscore %>, notice: '<%= chat_model_name.humanize %> was successfully created.'
|
20
|
+
end
|
21
|
+
|
22
|
+
def show
|
23
|
+
@<%= message_model_name.underscore %> = @<%= chat_model_name.underscore %>.<%= message_model_name.tableize %>.build
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def set_<%= chat_model_name.underscore %>
|
29
|
+
@<%= chat_model_name.underscore %> = <%= chat_model_name %>.find(params[:id])
|
30
|
+
end
|
31
|
+
|
32
|
+
def model
|
33
|
+
params[:<%= chat_model_name.underscore %>][:model].presence
|
34
|
+
end
|
35
|
+
|
36
|
+
def prompt
|
37
|
+
params[:<%= chat_model_name.underscore %>][:prompt]
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class <%= message_model_name.pluralize %>Controller < ApplicationController
|
2
|
+
before_action :set_<%= chat_model_name.underscore %>
|
3
|
+
|
4
|
+
def create
|
5
|
+
return unless content.present?
|
6
|
+
|
7
|
+
<%= chat_model_name %>ResponseJob.perform_later(@<%= chat_model_name.underscore %>.id, content)
|
8
|
+
|
9
|
+
respond_to do |format|
|
10
|
+
format.turbo_stream
|
11
|
+
format.html { redirect_to @<%= chat_model_name.underscore %> }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def set_<%= chat_model_name.underscore %>
|
18
|
+
@<%= chat_model_name.underscore %> = <%= chat_model_name %>.find(params[:<%= chat_model_name.underscore %>_id])
|
19
|
+
end
|
20
|
+
|
21
|
+
def content
|
22
|
+
params[:<%= message_model_name.underscore %>][:content]
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class <%= model_model_name.pluralize %>Controller < ApplicationController
|
2
|
+
def index
|
3
|
+
@<%= model_model_name.tableize %> = <%= model_model_name %>.all.group_by(&:provider)
|
4
|
+
end
|
5
|
+
|
6
|
+
def show
|
7
|
+
@<%= model_model_name.underscore %> = <%= model_model_name %>.find(params[:id])
|
8
|
+
end
|
9
|
+
|
10
|
+
def refresh
|
11
|
+
<%= model_model_name %>.refresh!
|
12
|
+
redirect_to <%= model_model_name.tableize %>_path, notice: "<%= model_model_name.pluralize %> refreshed successfully"
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class <%= chat_model_name %>ResponseJob < ApplicationJob
|
2
|
+
def perform(<%= chat_model_name.underscore %>_id, content)
|
3
|
+
<%= chat_model_name.underscore %> = <%= chat_model_name %>.find(<%= chat_model_name.underscore %>_id)
|
4
|
+
|
5
|
+
<%= chat_model_name.underscore %>.ask(content) do |chunk|
|
6
|
+
if chunk.content && !chunk.content.blank?
|
7
|
+
<%= message_model_name.underscore %> = <%= chat_model_name.underscore %>.<%= message_model_name.tableize %>.last
|
8
|
+
<%= message_model_name.underscore %>.update!(content: <%= message_model_name.underscore %>.content + chunk.content)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div id="<%%= dom_id <%= chat_model_name.underscore %> %>">
|
2
|
+
<div>
|
3
|
+
<strong>Model:</strong>
|
4
|
+
<%%= <%= chat_model_name.underscore %>.model_association.name %>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div>
|
8
|
+
<strong>Messages:</strong>
|
9
|
+
<%%= <%= chat_model_name.underscore %>.messages_association.count %>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div>
|
13
|
+
<strong>Created:</strong>
|
14
|
+
<%%= <%= chat_model_name.underscore %>.created_at.strftime("%B %d, %Y at %I:%M %p") %>
|
15
|
+
</div>
|
16
|
+
</div>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<%%= form_with(model: <%= chat_model_name.underscore %>, url: <%= chat_model_name.tableize %>_path) do |form| %>
|
2
|
+
<%% if <%= chat_model_name.underscore %>.errors.any? %>
|
3
|
+
<div style="color: red">
|
4
|
+
<h2><%%= pluralize(<%= chat_model_name.underscore %>.errors.count, "error") %> prohibited this <%= chat_model_name.underscore.humanize.downcase %> from being saved:</h2>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<%% <%= chat_model_name.underscore %>.errors.each do |error| %>
|
8
|
+
<li><%%= error.full_message %></li>
|
9
|
+
<%% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<%% end %>
|
13
|
+
|
14
|
+
<div>
|
15
|
+
<%%= form.label :model, "Select AI model:", style: "display: block" %>
|
16
|
+
<%%= form.select :model,
|
17
|
+
options_for_select(<%= model_model_name %>.pluck(:name, :model_id).unshift(["Default (#{RubyLLM.config.default_model})", nil]), @selected_model),
|
18
|
+
{},
|
19
|
+
style: "width: 100%; max-width: 600px; padding: 5px;" %>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div style="margin-top: 15px;">
|
23
|
+
<%%= form.text_field :prompt, style: "width: 100%; max-width: 600px;", placeholder: "What would you like to discuss?", autofocus: true %>
|
24
|
+
</div>
|
25
|
+
|
26
|
+
<div>
|
27
|
+
<%%= form.submit "Start new <%= chat_model_name.underscore.humanize.downcase %>" %>
|
28
|
+
</div>
|
29
|
+
<%% end %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
2
|
+
|
3
|
+
<%% content_for :title, "<%= chat_model_name.pluralize %>" %>
|
4
|
+
|
5
|
+
<h1><%= chat_model_name.pluralize %></h1>
|
6
|
+
|
7
|
+
<div id="<%= chat_model_name.tableize %>">
|
8
|
+
<%% @<%= chat_model_name.tableize %>.each do |<%= chat_model_name.underscore %>| %>
|
9
|
+
<%%= render <%= chat_model_name.underscore %> %>
|
10
|
+
<p>
|
11
|
+
<%%= link_to "Show this <%= chat_model_name.underscore.humanize.downcase %>", <%= chat_model_name.underscore %> %>
|
12
|
+
</p>
|
13
|
+
<%% end %>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<%%= link_to "New <%= chat_model_name.underscore.humanize.downcase %>", new_<%= chat_model_name.underscore %>_path %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%% content_for :title, "New <%= chat_model_name.underscore.humanize.downcase %>" %>
|
2
|
+
|
3
|
+
<h1>New <%= chat_model_name.underscore.humanize.downcase %></h1>
|
4
|
+
|
5
|
+
<%%= render "form", <%= chat_model_name.underscore %>: @<%= chat_model_name.underscore %> %>
|
6
|
+
|
7
|
+
<br>
|
8
|
+
|
9
|
+
<div>
|
10
|
+
<%%= link_to "Back to <%= chat_model_name.tableize.humanize.downcase %>", <%= chat_model_name.tableize %>_path %>
|
11
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
2
|
+
|
3
|
+
<%%= turbo_stream_from "<%= chat_model_name.underscore %>_#{@<%= chat_model_name.underscore %>.id}" %>
|
4
|
+
|
5
|
+
<%% content_for :title, "<%= chat_model_name %>" %>
|
6
|
+
|
7
|
+
<h1><%= chat_model_name %> <%%= @<%= chat_model_name.underscore %>.id %></h1>
|
8
|
+
|
9
|
+
<p>Using <strong><%%= @<%= chat_model_name.underscore %>.model_association.name %></strong></p>
|
10
|
+
|
11
|
+
<div id="<%= message_model_name.tableize %>">
|
12
|
+
<%% @<%= chat_model_name.underscore %>.messages_association.where.not(id: nil).each do |<%= message_model_name.underscore %>| %>
|
13
|
+
<%%= render "<%= message_model_name.tableize %>/<%= message_model_name.underscore %>", <%= message_model_name.underscore %>: <%= message_model_name.underscore %> %>
|
14
|
+
<%% end %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div style="margin-top: 30px;">
|
18
|
+
<%%= render "<%= message_model_name.tableize %>/form", <%= chat_model_name.underscore %>: @<%= chat_model_name.underscore %>, <%= message_model_name.underscore %>: @<%= message_model_name.underscore %> %>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div style="margin-top: 20px;">
|
22
|
+
<%%= link_to "Back to <%= chat_model_name.tableize.humanize.downcase %>", <%= chat_model_name.tableize %>_path %>
|
23
|
+
</div>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<%%= form_with(model: [<%= chat_model_name.underscore %>, <%= message_model_name.underscore %>], id: "new_<%= message_model_name.underscore %>") do |form| %>
|
2
|
+
<%% if <%= message_model_name.underscore %>.errors.any? %>
|
3
|
+
<div style="color: red">
|
4
|
+
<h2><%%= pluralize(<%= message_model_name.underscore %>.errors.count, "error") %> prohibited this <%= message_model_name.underscore.humanize.downcase %> from being saved:</h2>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<%% <%= message_model_name.underscore %>.errors.each do |error| %>
|
8
|
+
<li><%%= error.full_message %></li>
|
9
|
+
<%% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<%% end %>
|
13
|
+
|
14
|
+
<div>
|
15
|
+
<%%= form.text_field :content, style: "width: 100%; max-width: 600px;", placeholder: "Message...", autofocus: true %>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div>
|
19
|
+
<%%= form.submit "Send <%= message_model_name.underscore.humanize.downcase %>" %>
|
20
|
+
</div>
|
21
|
+
<%% end %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%%= tag.div id: dom_id(<%= message_model_name.underscore %>), class: "<%= message_model_name.underscore %>",
|
2
|
+
style: "margin-bottom: 20px; padding: 10px; border-left: 3px solid #{<%= message_model_name.underscore %>.role == 'user' ? '#007bff' : '#28a745'};" do %>
|
3
|
+
<div style="font-weight: bold; margin-bottom: 5px;">
|
4
|
+
<%%= <%= message_model_name.underscore %>.role&.capitalize %>
|
5
|
+
</div>
|
6
|
+
<div style="white-space: pre-wrap;"><%%= <%= message_model_name.underscore %>.content %></div>
|
7
|
+
<div style="font-size: 0.85em; color: #666; margin-top: 5px;">
|
8
|
+
<%%= <%= message_model_name.underscore %>.created_at&.strftime("%I:%M %p") %>
|
9
|
+
</div>
|
10
|
+
<%% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%%= turbo_stream.append "<%= message_model_name.tableize %>" do %>
|
2
|
+
<%% @<%= chat_model_name.underscore %>.messages_association.last(2).each do |<%= message_model_name.underscore %>| %>
|
3
|
+
<%%= render "<%= message_model_name.tableize %>/<%= message_model_name.underscore %>", <%= message_model_name.underscore %>: <%= message_model_name.underscore %> %>
|
4
|
+
<%% end %>
|
5
|
+
<%% end %>
|
6
|
+
|
7
|
+
<%%= turbo_stream.replace "new_<%= message_model_name.underscore %>" do %>
|
8
|
+
<%%= render "<%= message_model_name.tableize %>/form", <%= chat_model_name.underscore %>: @<%= chat_model_name.underscore %>, <%= message_model_name.underscore %>: @<%= chat_model_name.underscore %>.messages_association.build %>
|
9
|
+
<%% end %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<tr id="<%%= dom_id <%= model_model_name.underscore %> %>">
|
2
|
+
<td><%%= <%= model_model_name.underscore %>.provider %></td>
|
3
|
+
<td><%%= <%= model_model_name.underscore %>.name %></td>
|
4
|
+
<td><%%= number_with_delimiter(<%= model_model_name.underscore %>.context_window) if <%= model_model_name.underscore %>.context_window %></td>
|
5
|
+
<td>
|
6
|
+
<%% if <%= model_model_name.underscore %>.pricing && <%= model_model_name.underscore %>.pricing['text_tokens'] && <%= model_model_name.underscore %>.pricing['text_tokens']['standard'] %>
|
7
|
+
<%% input = <%= model_model_name.underscore %>.pricing['text_tokens']['standard']['input_per_million'] %>
|
8
|
+
<%% output = <%= model_model_name.underscore %>.pricing['text_tokens']['standard']['output_per_million'] %>
|
9
|
+
<%% if input && output %>
|
10
|
+
$<%%= "%.2f" % input %> / $<%%= "%.2f" % output %>
|
11
|
+
<%% end %>
|
12
|
+
<%% end %>
|
13
|
+
</td>
|
14
|
+
<td><%%= link_to "Show", <%= model_model_name.underscore %> %></td>
|
15
|
+
<td><%%= link_to "Start <%= chat_model_name.underscore.humanize.downcase %>", new_<%= chat_model_name.underscore %>_path(model: <%= model_model_name.underscore %>.model_id) %></td>
|
16
|
+
</tr>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
2
|
+
|
3
|
+
<%% content_for :title, "<%= model_model_name.pluralize %>" %>
|
4
|
+
|
5
|
+
<h1><%= model_model_name.pluralize %></h1>
|
6
|
+
|
7
|
+
<p>
|
8
|
+
<%%= button_to "Refresh <%= model_model_name.pluralize %>", refresh_<%= model_model_name.tableize %>_path, method: :post %>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<div id="<%= model_model_name.tableize %>">
|
12
|
+
<table>
|
13
|
+
<thead>
|
14
|
+
<tr>
|
15
|
+
<th>Provider</th>
|
16
|
+
<th>Model</th>
|
17
|
+
<th>Context Window</th>
|
18
|
+
<th>$/1M tokens (In/Out)</th>
|
19
|
+
<th colspan="2"></th>
|
20
|
+
</tr>
|
21
|
+
</thead>
|
22
|
+
<tbody>
|
23
|
+
<%% @<%= model_model_name.tableize %>.values.flatten.each do |<%= model_model_name.underscore %>| %>
|
24
|
+
<%%= render <%= model_model_name.underscore %> %>
|
25
|
+
<%% end %>
|
26
|
+
</tbody>
|
27
|
+
</table>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<%%= link_to "Back to <%= chat_model_name.tableize.humanize.downcase %>", <%= chat_model_name.tableize %>_path %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%% content_for :title, @model.name %>
|
2
|
+
|
3
|
+
<h1><%%= @model.name %></h1>
|
4
|
+
|
5
|
+
<p><strong>ID:</strong> <%%= @model.model_id %></p>
|
6
|
+
<p><strong>Provider:</strong> <%%= @model.provider %></p>
|
7
|
+
<p><strong>Context Window:</strong> <%%= number_with_delimiter(@model.context_window) %> tokens</p>
|
8
|
+
<p><strong>Max Output:</strong> <%%= number_with_delimiter(@model.max_output_tokens) %> tokens</p>
|
9
|
+
|
10
|
+
<%% if @model.capabilities.any? %>
|
11
|
+
<p><strong>Capabilities:</strong> <%%= @model.capabilities.join(", ") %></p>
|
12
|
+
<%% end %>
|
13
|
+
|
14
|
+
<p>
|
15
|
+
<%%= link_to "Start chat with this model", new_chat_path(model: @model.model_id) %> |
|
16
|
+
<%%= link_to "All models", models_path %> |
|
17
|
+
<%%= link_to "Back to chats", chats_path %>
|
18
|
+
</p>
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
# Shared helpers for RubyLLM generators
|
5
|
+
module GeneratorHelpers
|
6
|
+
def parse_model_mappings
|
7
|
+
@model_names = {
|
8
|
+
chat: 'Chat',
|
9
|
+
message: 'Message',
|
10
|
+
tool_call: 'ToolCall',
|
11
|
+
model: 'Model'
|
12
|
+
}
|
13
|
+
|
14
|
+
model_mappings.each do |mapping|
|
15
|
+
if mapping.include?(':')
|
16
|
+
key, value = mapping.split(':', 2)
|
17
|
+
@model_names[key.to_sym] = value.classify
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
@model_names
|
22
|
+
end
|
23
|
+
|
24
|
+
%i[chat message tool_call model].each do |type|
|
25
|
+
define_method("#{type}_model_name") do
|
26
|
+
@model_names ||= parse_model_mappings
|
27
|
+
@model_names[type]
|
28
|
+
end
|
29
|
+
|
30
|
+
define_method("#{type}_table_name") do
|
31
|
+
table_name_for(send("#{type}_model_name"))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def acts_as_chat_declaration
|
36
|
+
params = []
|
37
|
+
|
38
|
+
add_association_params(params, :messages, message_table_name, message_model_name, plural: true)
|
39
|
+
add_association_params(params, :model, model_table_name, model_model_name)
|
40
|
+
|
41
|
+
"acts_as_chat#{" #{params.join(', ')}" if params.any?}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def acts_as_message_declaration
|
45
|
+
params = []
|
46
|
+
|
47
|
+
add_association_params(params, :chat, chat_table_name, chat_model_name)
|
48
|
+
add_association_params(params, :tool_calls, tool_call_table_name, tool_call_model_name, plural: true)
|
49
|
+
add_association_params(params, :model, model_table_name, model_model_name)
|
50
|
+
|
51
|
+
"acts_as_message#{" #{params.join(', ')}" if params.any?}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def acts_as_model_declaration
|
55
|
+
params = []
|
56
|
+
|
57
|
+
add_association_params(params, :chats, chat_table_name, chat_model_name, plural: true)
|
58
|
+
|
59
|
+
"acts_as_model#{" #{params.join(', ')}" if params.any?}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def acts_as_tool_call_declaration
|
63
|
+
params = []
|
64
|
+
|
65
|
+
add_association_params(params, :message, message_table_name, message_model_name)
|
66
|
+
|
67
|
+
"acts_as_tool_call#{" #{params.join(', ')}" if params.any?}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_namespace_modules
|
71
|
+
namespaces = []
|
72
|
+
|
73
|
+
[chat_model_name, message_model_name, tool_call_model_name, model_model_name].each do |model_name|
|
74
|
+
if model_name.include?('::')
|
75
|
+
namespace = model_name.split('::').first
|
76
|
+
namespaces << namespace unless namespaces.include?(namespace)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
namespaces.each do |namespace|
|
81
|
+
module_path = "app/models/#{namespace.underscore}.rb"
|
82
|
+
next if File.exist?(Rails.root.join(module_path))
|
83
|
+
|
84
|
+
create_file module_path do
|
85
|
+
<<~RUBY
|
86
|
+
module #{namespace}
|
87
|
+
def self.table_name_prefix
|
88
|
+
"#{namespace.underscore}_"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
RUBY
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def migration_version
|
97
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
98
|
+
end
|
99
|
+
|
100
|
+
def postgresql?
|
101
|
+
::ActiveRecord::Base.connection.adapter_name.downcase.include?('postgresql')
|
102
|
+
rescue StandardError
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
def table_exists?(table_name)
|
107
|
+
::ActiveRecord::Base.connection.table_exists?(table_name)
|
108
|
+
rescue StandardError
|
109
|
+
false
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def add_association_params(params, default_assoc, table_name, model_name, plural: false)
|
115
|
+
assoc = plural ? table_name.to_sym : table_name.singularize.to_sym
|
116
|
+
|
117
|
+
return if assoc == default_assoc
|
118
|
+
|
119
|
+
params << "#{default_assoc}: :#{assoc}"
|
120
|
+
params << "#{default_assoc.to_s.singularize}_class: '#{model_name}'" if model_name != assoc.to_s.classify
|
121
|
+
end
|
122
|
+
|
123
|
+
def table_name_for(model_name)
|
124
|
+
# Convert namespaced model names to proper table names
|
125
|
+
# e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
|
126
|
+
model_name.underscore.pluralize.tr('/', '_')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|