ruby_llm_community 1.0.0 → 1.1.0

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -1
  3. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +127 -0
  4. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +39 -0
  5. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +24 -0
  6. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +14 -0
  7. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +12 -0
  8. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +16 -0
  9. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +29 -0
  10. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +16 -0
  11. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +11 -0
  12. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +23 -0
  13. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +21 -0
  14. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +10 -0
  15. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +9 -0
  16. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +16 -0
  17. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +30 -0
  18. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +18 -0
  19. data/lib/generators/ruby_llm/install/install_generator.rb +227 -0
  20. data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +2 -2
  21. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +4 -4
  22. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +8 -7
  23. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +12 -3
  24. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +6 -5
  25. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +9 -8
  26. data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +4 -3
  27. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +2 -5
  28. data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +2 -2
  29. data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +137 -0
  30. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +170 -0
  31. data/lib/ruby_llm/active_record/acts_as.rb +108 -467
  32. data/lib/ruby_llm/active_record/acts_as_legacy.rb +403 -0
  33. data/lib/ruby_llm/active_record/chat_methods.rb +336 -0
  34. data/lib/ruby_llm/active_record/message_methods.rb +72 -0
  35. data/lib/ruby_llm/active_record/model_methods.rb +84 -0
  36. data/lib/ruby_llm/aliases.json +72 -6
  37. data/lib/ruby_llm/attachment.rb +22 -0
  38. data/lib/ruby_llm/configuration.rb +6 -0
  39. data/lib/ruby_llm/image_attachment.rb +12 -3
  40. data/lib/ruby_llm/message.rb +1 -1
  41. data/lib/ruby_llm/models.json +2640 -1756
  42. data/lib/ruby_llm/models.rb +5 -15
  43. data/lib/ruby_llm/provider.rb +6 -4
  44. data/lib/ruby_llm/providers/anthropic/media.rb +1 -1
  45. data/lib/ruby_llm/providers/bedrock/models.rb +19 -1
  46. data/lib/ruby_llm/providers/gemini/media.rb +1 -1
  47. data/lib/ruby_llm/providers/gpustack/media.rb +1 -1
  48. data/lib/ruby_llm/providers/ollama/media.rb +1 -1
  49. data/lib/ruby_llm/providers/openai/media.rb +4 -4
  50. data/lib/ruby_llm/providers/openai/response.rb +7 -6
  51. data/lib/ruby_llm/providers/openai/response_media.rb +1 -1
  52. data/lib/ruby_llm/providers/openai/streaming.rb +14 -11
  53. data/lib/ruby_llm/providers/openai/tools.rb +11 -6
  54. data/lib/ruby_llm/providers/vertexai.rb +1 -1
  55. data/lib/ruby_llm/providers/xai/capabilities.rb +166 -0
  56. data/lib/ruby_llm/providers/xai/chat.rb +15 -0
  57. data/lib/ruby_llm/providers/xai/models.rb +48 -0
  58. data/lib/ruby_llm/providers/xai.rb +46 -0
  59. data/lib/ruby_llm/railtie.rb +20 -3
  60. data/lib/ruby_llm/stream_accumulator.rb +0 -4
  61. data/lib/ruby_llm/utils.rb +5 -9
  62. data/lib/ruby_llm/version.rb +1 -1
  63. data/lib/ruby_llm_community.rb +4 -3
  64. data/lib/tasks/models.rake +29 -5
  65. data/lib/tasks/ruby_llm.rake +15 -0
  66. data/lib/tasks/vcr.rake +2 -2
  67. metadata +32 -3
  68. data/lib/generators/ruby_llm/install/templates/INSTALL_INFO.md.tt +0 -108
  69. 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: 0d28b384f53784c1954147d9f217caff1325893f1853e6be63c60158d9c37b79
4
- data.tar.gz: 3c6e66e260c1300b3b20ae730ec3755fcca19be8afc8dee754e26cc82cbee7f4
3
+ metadata.gz: 82545649470ef24d251c3cf4a5f7eceff777ea2d3939e48eb76fc5109e8a1b5a
4
+ data.tar.gz: 378d2d26c2cc08362b5b552ca42ee397c9ab1e96cb394223cf8df2dae2548aa6
5
5
  SHA512:
6
- metadata.gz: 568f6c9a3facf5e5f721ec6e3d44862eb0988529cf72625a88220fe80f00ba89f5602f0a178d0ead3cfc3c4accca3a4b56321a3a24f84883df16bd1c72432458
7
- data.tar.gz: e6336936468acfc005284a1e6f7cbffe58e55e85c760082af71569d9555371ed124b26fe0cc70d274124f4b80530881c012d10079145f0c90383f2d6e2c51ff7
6
+ metadata.gz: 6340051367db4bd6401e2d8c5468eefded5bbf5a72daa94b3bf930ab8b3601b7d3c3cc26e6e878a45e11d3fce15f99747e09eee7393d966f59ce8f968b7d3640
7
+ data.tar.gz: 1c7de7980a77d5340325ef1db12c4e6b8e6347639f956a925b1ec8dffeb3ae819086159e35aeb3d7964cc4c60133f09657326dc0eb894c28a2c81efda1a4ddb4
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
- [![Gem Version](https://badge.fury.io/rb/ruby_llm.svg?a=7)](https://badge.fury.io/rb/ruby_llm)
12
+ [![Gem Version](https://badge.fury.io/rb/ruby_llm.svg?a=8)](https://badge.fury.io/rb/ruby_llm)
13
13
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
14
14
  [![Gem Downloads](https://img.shields.io/gem/dt/ruby_llm)](https://rubygems.org/gems/ruby_llm)
15
15
  [![codecov](https://codecov.io/gh/crmne/ruby_llm/branch/main/graph/badge.svg?a=2)](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,20 @@ 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
+ Initial examples --
38
+
39
+ - Prompt caching for Anthropic
40
+ - Image editing for Gemini and OpenAI
41
+ - Responses API for OpenAI
42
+
43
+ This project is intended to be compatible with RubyLLM. I will attempt to keep it up to date as Carmine pushes commits often.
44
+
45
+ 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.
46
+
30
47
  ## Show me the code
31
48
 
32
49
  ```ruby
@@ -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>