Skip to content

Instantly share code, notes, and snippets.

@alexrudall
Last active September 25, 2025 02:25
Show Gist options
  • Save alexrudall/cb5ee1e109353ef358adb4e66631799d to your computer and use it in GitHub Desktop.
Save alexrudall/cb5ee1e109353ef358adb4e66631799d to your computer and use it in GitHub Desktop.

Revisions

  1. alexrudall revised this gist Jun 18, 2024. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,8 @@

    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app using ruby-openai, Rails 7, Hotwire, Turbostream, Sidekiq and Tailwind. All code included below!

    Want more content like this, for free? Check out my free book, [RailsAI](https://railsai.com)!

    ![Alt Text](https://media.giphy.com/media/5Lv6gU6jACtpn2KYNd/giphy.gif)

    - Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
  2. alexrudall revised this gist Oct 18, 2023. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions get_ai_response.rb
    Original file line number Diff line number Diff line change
    @@ -21,8 +21,6 @@ def call_openai(chat:)
    )
    end

    private

    def create_messages(chat:)
    messages = []
    RESPONSES_PER_MESSAGE.times do |i|
  3. alexrudall revised this gist Oct 18, 2023. 2 changed files with 21 additions and 6 deletions.
    1 change: 1 addition & 0 deletions 20230427131900_create_messages.rb
    Original file line number Diff line number Diff line change
    @@ -6,6 +6,7 @@ def change
    t.references :chat, foreign_key: true
    t.integer :role, null: false, default: 0
    t.string :content, null: false
    t.integer :response_number, null: false, default: 0

    t.timestamps
    end
    26 changes: 20 additions & 6 deletions get_ai_response.rb
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    # app/jobs/get_ai_response.rb
    class GetAiResponse < SidekiqJob
    RESPONSES_PER_MESSAGE = 1

    def perform(chat_id)
    chat = Chat.find(chat_id)
    call_openai(chat: chat)
    @@ -8,22 +10,34 @@ def perform(chat_id)
    private

    def call_openai(chat:)
    message = chat.messages.create(role: "assistant", content: "")
    message.broadcast_created

    OpenAI::Client.new.chat(
    parameters: {
    model: "gpt-3.5-turbo",
    messages: Message.for_openai(chat.messages),
    temperature: 0.1,
    stream: stream_proc(message: message)
    temperature: 0.8,
    stream: stream_proc(chat: chat),
    n: RESPONSES_PER_MESSAGE
    }
    )
    end

    private

    def create_messages(chat:)
    messages = []
    RESPONSES_PER_MESSAGE.times do |i|
    message = chat.messages.create(role: "assistant", content: "", response_number: i)
    message.broadcast_created
    messages << message
    end
    messages
    end

    def stream_proc(message:)
    def stream_proc(chat:)
    messages = create_messages(chat: chat)
    proc do |chunk, _bytesize|
    new_content = chunk.dig("choices", 0, "delta", "content")
    message = messages.find { |m| m.response_number == chunk.dig("choices", 0, "index") }
    message.update(content: message.content + new_content) if new_content
    end
    end
  4. alexrudall revised this gist Apr 30, 2023. 2 changed files with 9 additions and 12 deletions.
    1 change: 0 additions & 1 deletion #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,6 @@ This guide will walk you through adding a ChatGPT-like messaging stream to your
    ![Alt Text](https://media.giphy.com/media/5Lv6gU6jACtpn2KYNd/giphy.gif)

    - Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    - Would really appreciate public Tweets/social media linking to [this gist](https://gist.github.com/alexrudall/cb5ee1e109353ef358adb4e66631799d) if you've found it useful!
    - Released under the [MIT License](https://opensource.org/license/mit/) - use as you wish :)

    First, add the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem! Needs to be at least version 4. Add Sidekiq too.
    20 changes: 9 additions & 11 deletions views_message_message.html.erb
    Original file line number Diff line number Diff line change
    @@ -1,15 +1,13 @@
    # app/views/messages/_message.html.erb
    <%= turbo_frame_tag "#{dom_id(chat)}_message_form" do %>
    <%= form_with(model: Message.new, url: [chat, chat.messages.new]) do |form| %>
    <div class="my-5">
    <%= form.text_area :content, rows: 4, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full", autofocus: true, "x-on:keydown.cmd.enter" => "$event.target.form.requestSubmit();" %>
    # Thanks to github.com/fanahova for this template!
    <div id="<%= dom_id(message) %>_messages">
    <% if message.user? %>
    <div class="bg-sky-400 rounded-lg m-8 text-white p-4">
    <%= message.content %>
    </div>

    <div class="grid justify-items-end">
    <%= form.button type: :submit, class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" do %>
    <i class="fas fa-paper-plane"></i>
    <span class="pl-2">Send</span>
    <% end %>
    <% else %>
    <div class="bg-gray-200 rounded-lg m-8 p-4">
    <%= message.content %>
    </div>
    <% end %>
    <% end %>
    </div>
  5. alexrudall revised this gist Apr 28, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    # How to add ChatGPT streaming to your Ruby on Rails 7 app!

    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app ruby-openai, Rails 7, Hotwire, Turbostream, Sidekiq and Tailwind. All code included below!
    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app using ruby-openai, Rails 7, Hotwire, Turbostream, Sidekiq and Tailwind. All code included below!

    ![Alt Text](https://media.giphy.com/media/5Lv6gU6jACtpn2KYNd/giphy.gif)

  6. alexrudall revised this gist Apr 27, 2023. 3 changed files with 11 additions and 3 deletions.
    6 changes: 6 additions & 0 deletions #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -46,6 +46,12 @@ resources :chats, only: %i[create show] do
    end
    ```

    Generate the migrations:
    ```
    bin/rails generate migration CreateChats user:references
    bin/rails generate migration CreateMessages chat:references role:integer content:string
    ```

    Add the rest of the code, full example files below!

    ```
    5 changes: 3 additions & 2 deletions 20230427131800_create_chats.rb
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,11 @@
    # db/migrate/20230427131800_create_chats.rb
    # bin/rails generate migration CreateChats user:references
    class CreateChats < ActiveRecord::Migration[7.0]
    def change
    create_table :chats do |t|
    t.belongs_to :user, null: false, foreign_key: true
    t.references :user, null: false, foreign_key: true

    t.timestamps
    end
    end
    end
    end
    3 changes: 2 additions & 1 deletion 20230427131900_create_messages.rb
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,9 @@
    # db/migrate/20230427131900_create_messages.rb
    # bin/rails generate migration CreateMessages chat:references role:integer content:string
    class CreateMessages < ActiveRecord::Migration[7.0]
    def change
    create_table :messages do |t|
    t.references :chat, null: false, foreign_key: true
    t.references :chat, foreign_key: true
    t.integer :role, null: false, default: 0
    t.string :content, null: false

  7. alexrudall revised this gist Apr 27, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    # How to add ChatGPT streaming to your Ruby on Rails 7 app!

    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app using the `ruby-openai` gem. All code included below!
    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app ruby-openai, Rails 7, Hotwire, Turbostream, Sidekiq and Tailwind. All code included below!

    ![Alt Text](https://media.giphy.com/media/5Lv6gU6jACtpn2KYNd/giphy.gif)

  8. alexrudall revised this gist Apr 27, 2023. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,8 @@

    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app using the `ruby-openai` gem. All code included below!

    ![Alt Text](https://media.giphy.com/media/5Lv6gU6jACtpn2KYNd/giphy.gif)

    - Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    - Would really appreciate public Tweets/social media linking to [this gist](https://gist.github.com/alexrudall/cb5ee1e109353ef358adb4e66631799d) if you've found it useful!
    - Released under the [MIT License](https://opensource.org/license/mit/) - use as you wish :)
  9. alexrudall revised this gist Apr 27, 2023. 1 changed file with 8 additions and 0 deletions.
    8 changes: 8 additions & 0 deletions #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -36,6 +36,14 @@ Add your secret OpenAI token to your `.env` file. Get one from OpenAI [here](htt
    OPENAI_ACCESS_TOKEN=abc123
    ```

    Add the new routes:
    ```
    # config/routes.rb
    resources :chats, only: %i[create show] do
    resources :messages, only: %i[create]
    end
    ```

    Add the rest of the code, full example files below!

    ```
  10. alexrudall revised this gist Apr 27, 2023. No changes.
  11. alexrudall revised this gist Apr 27, 2023. 3 changed files with 37 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions views_message_create.turbo_stream.erb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    # app/views/messages/create.turbo_stream.erb
    <%= turbo_stream.append "#{dom_id(@message.chat)}_messages" do %>
    <%= render "message", message: @message, scroll_to: true %>
    <% end %>
    <%= turbo_stream.replace "#{dom_id(@message.chat)}_message_form" do %>
    <%= render "form", chat: @message.chat %>
    <% end %>
    15 changes: 15 additions & 0 deletions views_message_form.html.erb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    # app/views/messages/_form.html.erb
    <%= turbo_frame_tag "#{dom_id(chat)}_message_form" do %>
    <%= form_with(model: Message.new, url: [chat, chat.messages.new]) do |form| %>
    <div class="my-5">
    <%= form.text_area :content, rows: 4, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full", autofocus: true, "x-on:keydown.cmd.enter" => "$event.target.form.requestSubmit();" %>
    </div>

    <div class="grid justify-items-end">
    <%= form.button type: :submit, class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" do %>
    <i class="fas fa-paper-plane"></i>
    <span class="pl-2">Send</span>
    <% end %>
    </div>
    <% end %>
    <% end %>
    15 changes: 15 additions & 0 deletions views_message_message.html.erb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    # app/views/messages/_message.html.erb
    <%= turbo_frame_tag "#{dom_id(chat)}_message_form" do %>
    <%= form_with(model: Message.new, url: [chat, chat.messages.new]) do |form| %>
    <div class="my-5">
    <%= form.text_area :content, rows: 4, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full", autofocus: true, "x-on:keydown.cmd.enter" => "$event.target.form.requestSubmit();" %>
    </div>

    <div class="grid justify-items-end">
    <%= form.button type: :submit, class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" do %>
    <i class="fas fa-paper-plane"></i>
    <span class="pl-2">Send</span>
    <% end %>
    </div>
    <% end %>
    <% end %>
  12. alexrudall revised this gist Apr 27, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -25,7 +25,7 @@ brew install redis
    Add Redis and Sidekiq to your Procfile so they run when you run `bin/dev`.
    ```
    # Procfile.dev
    web: bin/rails db:seed && bin/rails server -p 3000
    web: bin/rails server -p 3000
    css: bin/rails tailwindcss:watch
    sidekiq: bundle exec sidekiq -c 2
    queue: redis-server
  13. alexrudall revised this gist Apr 27, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@ This guide will walk you through adding a ChatGPT-like messaging stream to your

    - Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    - Would really appreciate public Tweets/social media linking to [this gist](https://gist.github.com/alexrudall/cb5ee1e109353ef358adb4e66631799d) if you've found it useful!
    - Released under the MIT License - use as you wish :)
    - Released under the [MIT License](https://opensource.org/license/mit/) - use as you wish :)

    First, add the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem! Needs to be at least version 4. Add Sidekiq too.

  14. alexrudall revised this gist Apr 27, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app using the `ruby-openai` gem. All code included below!

    - Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    - Would really appreciate Tweets linking to this gist if you've found it useful!
    - Would really appreciate public Tweets/social media linking to [this gist](https://gist.github.com/alexrudall/cb5ee1e109353ef358adb4e66631799d) if you've found it useful!
    - Released under the MIT License - use as you wish :)

    First, add the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem! Needs to be at least version 4. Add Sidekiq too.
  15. alexrudall revised this gist Apr 27, 2023. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -2,9 +2,9 @@

    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app using the `ruby-openai` gem. All code included below!

    Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    Would really appreciate Tweets linking to this gist if you've found it useful!
    Released under the MIT License - use as you wish :)
    - Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    - Would really appreciate Tweets linking to this gist if you've found it useful!
    - Released under the MIT License - use as you wish :)

    First, add the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem! Needs to be at least version 4. Add Sidekiq too.

  16. alexrudall revised this gist Apr 27, 2023. 1 changed file with 8 additions and 2 deletions.
    10 changes: 8 additions & 2 deletions #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,10 @@

    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app using the `ruby-openai` gem. All code included below!

    Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    Would really appreciate Tweets linking to this gist if you've found it useful!
    Released under the MIT License - use as you wish :)

    First, add the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem! Needs to be at least version 4. Add Sidekiq too.

    ```
    @@ -51,7 +55,9 @@ app/models/chat.rb
    app/models/message.rb
    # Views
    app/views/chats/show.html.erb
    app/views/messages/_form.html.erb
    app/views/messages/_message.html.erb
    app/views/messages/create.turbo_stream.erb
    ```

    Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    Released under the MIT License - use as you wish :)
  17. alexrudall revised this gist Apr 27, 2023. 2 changed files with 21 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    # How to add ChatGPT streaming to your Ruby on Rails 7 app!

    This guide will walk you through adding a ChatGPT-like messaging stream to your Ruby on Rails 7 app using the `ruby-openai` gem. All code included below!

    First, add the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem! Needs to be at least version 4. Add Sidekiq too.

    ```
    19 changes: 19 additions & 0 deletions views_chat_show.html.erb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    # app/views/chats/show.html.erb
    <div class="mx-auto w-full flex">
    <div class="mx-auto">

    <div class="bg-white py-8">
    <div class="mx-auto max-w-lg px-6 ">
    <ul role="list" class="overflow-y-auto max-h-[48vh] flex flex-col-reverse">
    <%= turbo_stream_from "#{dom_id(@chat)}_messages" %>
    <div id="<%= dom_id(@chat) %>_messages">
    <%= render @chat.messages %>
    </div>
    </ul>

    <%= render partial: "messages/form", locals: { chat: @chat } %>
    </div>
    </div>

    </div>
    </div>
  18. alexrudall revised this gist Apr 27, 2023. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions #ChatGPT Streaming.md
    Original file line number Diff line number Diff line change
    @@ -50,3 +50,6 @@ app/models/message.rb
    # Views
    ```

    Follow me on Twitter for more Ruby AI at [https://twitter.com/alexrudall](https://twitter.com/alexrudall)
    Released under the MIT License - use as you wish :)
  19. alexrudall renamed this gist Apr 27, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  20. alexrudall revised this gist Apr 27, 2023. 3 changed files with 42 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions #instructions.md
    Original file line number Diff line number Diff line change
    @@ -41,8 +41,12 @@ app/controllers/messages_controller.rb
    app/jobs/get_ai_response.rb
    # Migrations
    db/migrate/20230427131800_create_chats.rb
    db/migrate/20230427131900_create_messages.rb
    # Models
    app/models/chat.rb
    app/models/message.rb
    # Views
    ```
    5 changes: 5 additions & 0 deletions chat.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    # app/models/chat.rb
    class Chat < ApplicationRecord
    belongs_to :user
    has_many :messages, dependent: :destroy
    end
    33 changes: 33 additions & 0 deletions message.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,33 @@
    # app/models/message.rb
    class Message < ApplicationRecord
    include ActionView::RecordIdentifier

    enum role: { system: 0, assistant: 10, user: 20 }

    belongs_to :chat

    after_create_commit -> { broadcast_created }
    after_update_commit -> { broadcast_updated }

    def broadcast_created
    broadcast_append_later_to(
    "#{dom_id(chat)}_messages",
    partial: "messages/message",
    locals: { message: self, scroll_to: true },
    target: "#{dom_id(chat)}_messages"
    )
    end

    def broadcast_updated
    broadcast_append_to(
    "#{dom_id(chat)}_messages",
    partial: "messages/message",
    locals: { message: self, scroll_to: true },
    target: "#{dom_id(chat)}_messages"
    )
    end

    def self.for_openai(messages)
    messages.map { |message| { role: message.role, content: message.content } }
    end
    end
  21. alexrudall renamed this gist Apr 27, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  22. alexrudall revised this gist Apr 27, 2023. 2 changed files with 0 additions and 0 deletions.
    File renamed without changes.
  23. alexrudall revised this gist Apr 27, 2023. 3 changed files with 24 additions and 2 deletions.
    10 changes: 10 additions & 0 deletions 20230427000000_create_chats.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    # db/migrate/20230427131800_create_chats.rb
    class CreateChats < ActiveRecord::Migration[7.0]
    def change
    create_table :chats do |t|
    t.belongs_to :user, null: false, foreign_key: true

    t.timestamps
    end
    end
    end
    4 changes: 2 additions & 2 deletions _instructions.md
    Original file line number Diff line number Diff line change
    @@ -35,10 +35,10 @@ Add the rest of the code, full example files below!
    ```
    # Controllers.
    app/controllers/chats_controller.rb
    app/controllers/messages_controller.rb
    app/controllers/messages_controller.rb
    # Sidekiq job to stream the data from the OpenAI API.
    app/jobs/get_ai_response.rb
    app/jobs/get_ai_response.rb
    # Migrations
    12 changes: 12 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    # db/migrate/20230427131900_create_messages.rb
    class CreateMessages < ActiveRecord::Migration[7.0]
    def change
    create_table :messages do |t|
    t.references :chat, null: false, foreign_key: true
    t.integer :role, null: false, default: 0
    t.string :content, null: false

    t.timestamps
    end
    end
    end
  24. alexrudall renamed this gist Apr 27, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  25. alexrudall revised this gist Apr 27, 2023. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions chats_controller.rb
    Original file line number Diff line number Diff line change
    @@ -9,8 +9,6 @@ def show

    def create
    @chat = Chat.create(user: current_user)
    @chat.messages.create(role: "system", content: @prompt.content)
    GetAiResponse.perform_async(@chat.id)
    respond_with(@chat)
    end

  26. alexrudall created this gist Apr 27, 2023.
    48 changes: 48 additions & 0 deletions Instructions.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,48 @@
    # How to add ChatGPT streaming to your Ruby on Rails 7 app!

    First, add the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem! Needs to be at least version 4. Add Sidekiq too.

    ```
    # Gemfile
    gem "ruby-openai", "~> 4.0.0"
    # Simple, efficient background processing using Redis.
    # https://github.com/sidekiq/sidekiq
    gem "sidekiq", "~> 7.0.9"
    ```

    Install Redis on your machine.
    ```
    brew install redis
    ```

    Add Redis and Sidekiq to your Procfile so they run when you run `bin/dev`.
    ```
    # Procfile.dev
    web: bin/rails db:seed && bin/rails server -p 3000
    css: bin/rails tailwindcss:watch
    sidekiq: bundle exec sidekiq -c 2
    queue: redis-server
    ```

    Add your secret OpenAI token to your `.env` file. Get one from OpenAI [here](https://platform.openai.com/account/api-keys).
    ```
    OPENAI_ACCESS_TOKEN=abc123
    ```

    Add the rest of the code, full example files below!

    ```
    # Controllers.
    app/controllers/chats_controller.rb
    # app/controllers/messages_controller.rb
    # Sidekiq job to stream the data from the OpenAI API.
    # app/jobs/get_ai_response.rb
    # Migrations
    # Models
    # Views
    ```
    22 changes: 22 additions & 0 deletions chats_controller.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    # app/controllers/chats_controller.rb
    class ChatsController < ApplicationController
    before_action :authenticate_user!
    before_action :set_chat, only: %i[show]

    def show
    respond_with(@chat)
    end

    def create
    @chat = Chat.create(user: current_user)
    @chat.messages.create(role: "system", content: @prompt.content)
    GetAiResponse.perform_async(@chat.id)
    respond_with(@chat)
    end

    private

    def set_chat
    @chat = Chat.find(params[:id])
    end
    end
    30 changes: 30 additions & 0 deletions get_ai_response.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    # app/jobs/get_ai_response.rb
    class GetAiResponse < SidekiqJob
    def perform(chat_id)
    chat = Chat.find(chat_id)
    call_openai(chat: chat)
    end

    private

    def call_openai(chat:)
    message = chat.messages.create(role: "assistant", content: "")
    message.broadcast_created

    OpenAI::Client.new.chat(
    parameters: {
    model: "gpt-3.5-turbo",
    messages: Message.for_openai(chat.messages),
    temperature: 0.1,
    stream: stream_proc(message: message)
    }
    )
    end

    def stream_proc(message:)
    proc do |chunk, _bytesize|
    new_content = chunk.dig("choices", 0, "delta", "content")
    message.update(content: message.content + new_content) if new_content
    end
    end
    end
    22 changes: 22 additions & 0 deletions messages_controller.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    # app/controllers/messages_controller.rb
    class MessagesController < ApplicationController
    include ActionView::RecordIdentifier

    before_action :authenticate_user!

    def create
    @message = Message.create(message_params.merge(chat_id: params[:chat_id], role: "user"))

    GetAiResponse.perform_async(@message.chat_id)

    respond_to do |format|
    format.turbo_stream
    end
    end

    private

    def message_params
    params.require(:message).permit(:content)
    end
    end