Skip to content

Commit

Permalink
Attach images to conversations as URLs (AllYourBot#424)
Browse files Browse the repository at this point in the history
Co-authored-by: Justin Vallelonga <jlvallelonga@gmail.com>
  • Loading branch information
krschacht and jlvallelonga authored Nov 5, 2024
1 parent db435e7 commit 5e99b81
Show file tree
Hide file tree
Showing 15 changed files with 71 additions and 34 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/rubyonrails.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
env:
RAILS_ENV: test
DATABASE_URL: "postgres://rails:password@localhost:5432/hostedgpt_test"
APP_URL_PROTOCOL: "http"
APP_URL_HOST: "localhost"
APP_URL_PORT: "3000"
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down Expand Up @@ -68,6 +71,9 @@ jobs:
env:
RAILS_ENV: test
DATABASE_URL: "postgres://rails:password@localhost:5432/hostedgpt_test"
APP_URL_PROTOCOL: "http"
APP_URL_HOST: "localhost"
APP_URL_PORT: "3000"
DISPLAY: "=:99"
CHROME_VERSION: "127.0.6533.119"

Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ RUN bundle exec bootsnap precompile app/ lib/
RUN grep -l '#!/usr/bin/env ruby' /rails/bin/* | xargs sed -i '/^#!/aDir.chdir File.expand_path("..", __dir__)'

# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
RUN SECRET_KEY_BASE_DUMMY=1 VALIDATE_ENV_VARS=0 ./bin/rails assets:precompile


# Final stage for app image
Expand Down Expand Up @@ -134,7 +134,7 @@ COPY . .

# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN bundle exec bootsnap precompile --gemfile app/ lib/
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
RUN SECRET_KEY_BASE_DUMMY=1 VALIDATE_ENV_VARS=0 ./bin/rails assets:precompile

RUN mkdir -p log tmp bin

Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ gem "ffi", "~> 1.15.5" # explicitly requiring 15.5 until this is resolved: https
gem "amatch", "~> 0.4.1" # enables fuzzy comparison of strings, a tool uses this
gem "rails_heroicon", "~> 2.2.0"
gem "ruby-openai", "~> 7.0.1"
gem "anthropic", "~> 0.1.0"
gem "anthropic", "~> 0.1.0" # TODO update to the latest version
gem "tiktoken_ruby", "~> 0.0.9"
gem "solid_queue", "~> 1.0.0"
gem "name_of_person"
gem "actioncable-enhanced-postgresql-adapter" # longer paylaods w/ postgresql actioncable
gem "aws-sdk-s3", require: false
gem "postmark-rails"
gem "ostruct"

gem "omniauth", "~> 2.1"
gem "omniauth-google-oauth2", "~> 1.1"
Expand Down
20 changes: 20 additions & 0 deletions app/models/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@ class Document < ApplicationRecord
validates :purpose, :filename, :bytes, presence: true
validate :file_present

def has_image?(variant = nil)
if variant.present?
return has_file_variant_processed?(variant)
end

file.attached?
end

def image_url(variant, fallback: nil)
return nil unless has_image?

if has_file_variant_processed?(variant)
fully_processed_url(variant)
elsif fallback.nil?
redirect_to_processed_path(variant)
else
fallback
end
end

def file_data_url(variant = :large)
return nil if !file.attached?

Expand Down
4 changes: 1 addition & 3 deletions app/models/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ def finished?
(content_text.present? || content_tool_calls.present?)
end

def not_finished?
!finished?
end
def not_finished? = !finished?

private

Expand Down
17 changes: 3 additions & 14 deletions app/models/message/document_image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,12 @@ module Message::DocumentImage
end

def has_document_image?(variant = nil)
has_image = documents.present? && documents.first.file.attached?
if has_image && variant
has_image = documents.first.has_file_variant_processed?(variant)
end

!!has_image
documents.present? && documents.first.has_image?(variant)
end

def document_image_path(variant, fallback: nil)
def document_image_url(variant, fallback: nil)
return nil unless has_document_image?

if documents.first.has_file_variant_processed?(variant)
documents.first.fully_processed_url(variant)
elsif fallback.nil?
documents.first.redirect_to_processed_path(variant)
else
fallback
end
documents.first.image_url(variant, fallback: fallback)
end
end
2 changes: 1 addition & 1 deletion app/services/ai_backend/open_ai.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def preceding_conversation_messages

content_with_images = [{ type: "text", text: message.content_text }]
content_with_images += message.documents.collect do |document|
{ type: "image_url", image_url: { url: document.file_data_url(:large) }}
{ type: "image_url", image_url: { url: document.image_url(:large) }}
end

{
Expand Down
8 changes: 4 additions & 4 deletions app/views/messages/_message.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ end %>
role: "image-preview",
controller: "image-loader",
image_loader_message_scroller_outlet: "[data-role='inner-message']",
image_loader_url_value: message.document_image_path(:small),
image_loader_url_value: message.document_image_url(:small),
action: "modal#open",
} do %>
<%= image_tag message.document_image_path(:small, fallback: ""),
<%= image_tag message.document_image_url(:small, fallback: ""),
class: %|
my-0
mx-auto
Expand Down Expand Up @@ -254,10 +254,10 @@ end %>
class="flex flex-col md:flex-row justify-center"
data-controller="image-loader"
data-image-loader-message-scroller-outlet="[data-role='inner-message']"
data-image-loader-url-value="<%= message.document_image_path(:large) %>"
data-image-loader-url-value="<%= message.document_image_url(:large) %>"
data-turbo-permanent
>
<%= image_tag message.document_image_path(:large, fallback: ""),
<%= image_tag message.document_image_url(:large, fallback: ""),
class: "w-full h-auto",
data: {
image_loader_target: "image",
Expand Down
8 changes: 8 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ class Application < Rails::Application
config.time_zone = "Central Time (US & Canada)"
config.eager_load_paths << Rails.root.join("lib")

if Setting.validate_env_vars == "1"
Setting.require_keys!(:app_url_protocol, :app_url_host, :app_url_port)
end
config.app_url_protocol = Setting.app_url_protocol
config.app_url_host = Setting.app_url_host
config.app_url_port = Setting.app_url_port
config.app_url = "#{Setting.app_url_protocol}://#{Setting.app_url_host}:#{Setting.app_url_port}"

# Active Storage
if Feature.cloudflare_storage?
config.active_storage.service = :cloudflare
Expand Down
3 changes: 2 additions & 1 deletion config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@
log_polling = ENV["SOLID_QUEUE_LOG_POLLING_ON"] != "false"
config.solid_queue.silence_polling = log_polling # NOTE: this is backwards, true means silence

config.web_console.permissions = ["192.168.0.0/16", "172.17.0.0/16"]
config.web_console.permissions = ["192.168.0.0/16", "172.17.0.0/16", "172.18.0.0/16"]

config.hosts << Setting.app_url_host
config.hosts << ENV["DEV_HOST"] if ENV["DEV_HOST"].present?

stdout_logger = ActiveSupport::Logger.new(STDOUT)
Expand Down
1 change: 1 addition & 0 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
config.hosts = ENV["PRODUCTION_HOST"].split(",").map(&:strip) if ENV["PRODUCTION_HOST"]
# config.hosts << Setting.app_url_host

# Skip DNS rebinding protection for the default health check endpoint.
config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
Expand Down
4 changes: 4 additions & 0 deletions config/options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ shared:
password_reset_email: <%= ENV["PASSWORD_RESET_EMAIL_FEATURE"] || default_to(false, except_env_test: true) %>
settings:
# Be sure to add these ENV to docker-compose.yml
app_url_protocol: <%= ENV["APP_URL_PROTOCOL"] %>
app_url_host: <%= ENV["APP_URL_HOST"] %>
app_url_port: <%= ENV["APP_URL_PORT"] %>
validate_env_vars: <%= ENV["VALIDATE_ENV_VARS"] || "1" %>
product_name: <%= ENV["PRODUCT_NAME"] || "HostedGPT" %>
default_openai_key: <%= ENV["DEFAULT_OPENAI_KEY"] %>
default_anthropic_key: <%= ENV["DEFAULT_ANTHROPIC_KEY"] %>
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ services:
target: development
environment:
# Be sure to add environment variables to config/options.yml
- APP_URL_PROTOCOL
- APP_URL_HOST
- APP_URL_PORT
- VALIDATE_ENV_VARS
- DATABASE_URL=postgres://app:secret@postgres/app_development
- DEV_HOST=${DEV_HOST:-localhost} # Set if you want to use a different hostname
- OVERMIND_COLORS=2,3,5
Expand Down
11 changes: 8 additions & 3 deletions lib/active_storage/service/postgresql_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,15 @@ def url_helpers

def url_options
if ActiveStorage::Current.respond_to?(:url_options)
ActiveStorage::Current.url_options
else
{ host: ActiveStorage::Current.host }
url_opts = ActiveStorage::Current.url_options
return url_opts if url_opts.is_a?(Hash)
end

return {
protocol: Rails.application.config.app_url_protocol,
host: Rails.application.config.app_url_host,
port: Rails.application.config.app_url_port,
}
end
end
end
10 changes: 5 additions & 5 deletions test/models/message/document_image_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ class Message::DocumentImageTest < ActiveSupport::TestCase
refute messages(:examine_this).has_document_image?(:small)
end

test "document_image_path" do
assert messages(:examine_this).document_image_path(:small).is_a?(String)
assert messages(:examine_this).document_image_path(:small).starts_with?("/rails/active_storage/representations/redirect")
test "document_image_url" do
assert messages(:examine_this).document_image_url(:small).is_a?(String)
assert messages(:examine_this).document_image_url(:small).starts_with?("/rails/active_storage/representations/redirect")
end

test "document_image_path with fallback" do
assert_equal "", messages(:examine_this).document_image_path(:small, fallback: "")
test "document_image_url with fallback" do
assert_equal "", messages(:examine_this).document_image_url(:small, fallback: "")
end
end

0 comments on commit 5e99b81

Please sign in to comment.