mirror of
https://github.com/indentlabs/notebook.git
synced 2025-10-26 11:19:22 +00:00
allow setting per-field weights for basil
This commit is contained in:
parent
f84383623e
commit
4e42b055f7
@ -22,11 +22,11 @@ class BasilController < ApplicationController
|
||||
entity_type: 'Character'
|
||||
)
|
||||
|
||||
gender_field = @character.overview_field('Gender')
|
||||
@gender_value = Attribute.find_by(attribute_field_id: gender_field.id, entity: @character).try(:value)
|
||||
@gender_field = @character.overview_field('Gender')
|
||||
@gender_value = Attribute.find_by(attribute_field_id: @gender_field.id, entity: @character).try(:value)
|
||||
|
||||
age_field = @character.overview_field('Age')
|
||||
@age_value = Attribute.find_by(attribute_field_id: age_field.id, entity: @character).try(:value)
|
||||
@age_field = @character.overview_field('Age')
|
||||
@age_value = Attribute.find_by(attribute_field_id: @age_field.id, entity: @character).try(:value)
|
||||
|
||||
@commissions = BasilCommission.where(entity_type: 'Character', entity_id: @character.id).order('id DESC')
|
||||
@can_request_another = @commissions.all? { |c| c.complete? }
|
||||
@ -44,42 +44,97 @@ class BasilController < ApplicationController
|
||||
@recent_commissions = BasilCommission.all.includes(:entity, :user).order('id DESC').limit(500)
|
||||
end
|
||||
|
||||
def commission_character
|
||||
def commission
|
||||
# TODO: when we support multiple page types, we'll want to grab params[:entity_type] and params[:entity_id]
|
||||
# and constantize the former, then find the entity from the user's content
|
||||
@character = current_user.characters.find(params[:id])
|
||||
|
||||
# Build the prompt
|
||||
category_ids = AttributeCategory.where(
|
||||
user_id: current_user.id, entity_type: 'character', label: ['Looks', 'Appearance']
|
||||
).pluck(:id)
|
||||
# Build the prompt from the character's attributes
|
||||
category_ids = AttributeCategory.where(user: current_user, entity_type: 'character', label: ['Looks', 'Appearance'])
|
||||
.pluck(:id)
|
||||
appearance_fields = AttributeField.where(attribute_category_id: category_ids)
|
||||
attributes = Attribute.where(
|
||||
attribute_field_id: appearance_fields.pluck(:id),
|
||||
entity_id: @character.id,
|
||||
entity_type: 'Character'
|
||||
)
|
||||
attributes = Attribute.where(attribute_field_id: appearance_fields.pluck(:id),
|
||||
entity_id: @character.id,
|
||||
entity_type: 'Character')
|
||||
|
||||
prompt_components = []
|
||||
|
||||
# Step 1. Gender
|
||||
gender_field = @character.overview_field('Gender')
|
||||
gender_value = Attribute.find_by(attribute_field_id: gender_field.id, entity: @character).try(:value)
|
||||
if gender_value.present?
|
||||
gender_importance = params.dig(:field, gender_field.id.to_s)
|
||||
|
||||
# Add 1 because we present weight to the user as -1 to 1, but it's really 0 to 2 for Stable Diffusion.
|
||||
if gender_importance.present?
|
||||
gender_importance = gender_importance.to_f + 1
|
||||
end
|
||||
|
||||
if gender_importance == 1
|
||||
# If the importance is exactly 1, we can omit the parentheses and save a few tokens, since the
|
||||
# default attention importance is 1.
|
||||
prompt_components.push gender_value
|
||||
elsif gender_importance != 0
|
||||
# We also want to skip adding gender to the prompt at all if the user marked it as completely unimportant (-1 + 1 = 0)
|
||||
prompt_components.push "(#{gender_value}:#{gender_importance})"
|
||||
end
|
||||
end
|
||||
|
||||
# Step 2. Age
|
||||
age_field = @character.overview_field('Age')
|
||||
age_value = Attribute.find_by(attribute_field_id: age_field.id, entity: @character).try(:value)
|
||||
|
||||
if age_value.present? && age_value.to_i.to_s == age_value
|
||||
age_value = "#{age_value} years old"
|
||||
if age_value.present?
|
||||
# If the user simply entered a number in for an age field, we want to help SD along by
|
||||
# giving it some context. Otherwise, we'll just use the value as-is.
|
||||
if age_value.to_i.to_s == age_value
|
||||
age_value = "#{age_value} years old"
|
||||
end
|
||||
|
||||
age_importance = params.dig(:field, age_field.id.to_s)
|
||||
|
||||
# Add 1 because we present weight to the user as -1 to 1, but it's really 0 to 2 for Stable Diffusion.
|
||||
if age_importance.present?
|
||||
age_importance = age_importance.to_f + 1
|
||||
end
|
||||
|
||||
if age_importance == 1
|
||||
prompt_components.push age_value
|
||||
elsif age_importance != 0
|
||||
# We also want to skip adding gender to the prompt at all if the user marked it as completely unimportant (-1 + 1 = 0)
|
||||
prompt_components.push "(#{age_value}:#{age_importance})"
|
||||
end
|
||||
end
|
||||
|
||||
# Step 3. Do it all again for every other field, too
|
||||
formatted_field_values = appearance_fields.map do |field|
|
||||
value = attributes.detect { |a| a.attribute_field_id == field.id }.try(:value)
|
||||
|
||||
# If there is no value to this field (or looks like it doesn't apply), skip it.
|
||||
next if value.nil? || value.blank? || ['none', 'n/a', 'no', '.', '-', ' '].include?(value.try(:downcase))
|
||||
|
||||
# If the field is something implied like a "Human" answer on "Race", skip it.
|
||||
next if field.label.downcase == 'race' && value.downcase == 'human'
|
||||
|
||||
"(#{value.gsub(',', '').gsub("\r", "").gsub("\n", " ")} #{field.label})"
|
||||
# Get the importance of this field and add 1 to get back to our SD version
|
||||
importance = params.dig(:field, field.id.to_s)
|
||||
importance = importance.to_f + 1 if importance.present?
|
||||
|
||||
# If the importance is exactly 1, we can omit the parentheses and save a few tokens, since the
|
||||
# default attention importance is 1.
|
||||
if importance == 1
|
||||
"#{value.gsub(',', '').gsub("\r", "").gsub("\n", " ")} #{field.label}"
|
||||
elsif importance != 0
|
||||
# We also want to skip adding gender to the prompt at all if the user marked it as completely unimportant (-1 + 1 = 0)
|
||||
"(#{value.gsub(',', '').gsub("\r", "").gsub("\n", " ")} #{field.label}:#{importance})"
|
||||
else
|
||||
# For 0-importance fields, we'll compact them out of the list in a moment
|
||||
nil
|
||||
end
|
||||
end
|
||||
prompt = [
|
||||
gender_value,
|
||||
age_value,
|
||||
formatted_field_values.compact.join(', '),
|
||||
].compact.join(', ')
|
||||
|
||||
prompt_components.concat formatted_field_values.compact
|
||||
prompt = prompt_components.join(', ')
|
||||
|
||||
BasilCommission.create!(
|
||||
user: current_user,
|
||||
|
||||
3
app/services/basil_service.rb
Normal file
3
app/services/basil_service.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class BasilService < Service
|
||||
# TODO
|
||||
end
|
||||
@ -1,180 +1,212 @@
|
||||
<div class="row">
|
||||
<div class="col s5">
|
||||
<div>
|
||||
<%= image_tag @character.random_image_including_private(format: :medium), style: 'width: 100%' %>
|
||||
<h1 style="font-size: 2rem"><%= @character.name %></h1>
|
||||
</div>
|
||||
<%= form_for BasilCommission.new, url: basil_character_path(@character) do |f| %>
|
||||
<%= f.hidden_field :style, value: 'realistic' %>
|
||||
<%= f.hidden_field :entity_type, value: 'Character' %>
|
||||
<%= f.hidden_field :entity_id, value: @character.id %>
|
||||
|
||||
<ul>
|
||||
<li style="margin-bottom: 1em">
|
||||
<div class="grey-text text-darken-3" style="font-weight: bold; font-size: 0.8em">
|
||||
Gender
|
||||
</div>
|
||||
<div>
|
||||
<%= @gender_value %>
|
||||
</div>
|
||||
</li>
|
||||
<div class="row">
|
||||
<div class="col s5">
|
||||
<div>
|
||||
<%= image_tag @character.random_image_including_private(format: :medium), style: 'width: 100%' %>
|
||||
<h1 style="font-size: 2rem"><%= @character.name %></h1>
|
||||
</div>
|
||||
|
||||
<li style="margin-bottom: 1em">
|
||||
<div class="grey-text text-darken-3" style="font-weight: bold; font-size: 0.8em">
|
||||
Age
|
||||
</div>
|
||||
<div>
|
||||
<%= @age_value %>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<% shown_any_value = false %>
|
||||
<% @appearance_fields.each do |field| %>
|
||||
<%
|
||||
value = @attributes.detect { |attr| attr.attribute_field_id == field.id }.try(:value)
|
||||
next if value.nil? || value.blank?
|
||||
shown_any_value = true
|
||||
%>
|
||||
<li style="margin-bottom: 1em;">
|
||||
<ul>
|
||||
<li style="margin-bottom: 1em">
|
||||
<div class="grey-text text-darken-3" style="font-weight: bold; font-size: 0.8em">
|
||||
<%= field.label %>
|
||||
Gender
|
||||
<%= range_field_tag "field[#{@gender_field.id}]", 0, { min: -1, max: 1, step: 0.25, style: 'width: 50%', class: 'js-importance-slider hide' } %>
|
||||
</div>
|
||||
<div>
|
||||
<%= value %>
|
||||
<%= @gender_value %>
|
||||
</div>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<% if !shown_any_value %>
|
||||
<div class="red card-panel lighten-3">
|
||||
Basil works best with guidance! Please fill out some fields in the "Looks" category for this character
|
||||
before requesting an image.
|
||||
</div>
|
||||
<% end %>
|
||||
<li style="margin-bottom: 1em">
|
||||
<div class="grey-text text-darken-3" style="font-weight: bold; font-size: 0.8em">
|
||||
Age
|
||||
<%= range_field_tag "field[#{@age_field.id}]", 0, { min: -1, max: 1, step: 0.25, style: 'width: 50%', class: 'js-importance-slider hide' } %>
|
||||
</div>
|
||||
<div>
|
||||
<%= @age_value %>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<div>
|
||||
<%= link_to 'Edit this character page', edit_polymorphic_path(@character), class: 'grey-text text-darken-2' %>
|
||||
</div>
|
||||
<div>
|
||||
<%= link_to 'Back to my other characters', basil_path, class: 'grey-text text-darken-2' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s7">
|
||||
<div class="card-panel" style="margin-top: 2em">
|
||||
<p>
|
||||
Basil is learning as fast as he can. Here are some tips and guidelines to get the most out of
|
||||
him right now:
|
||||
</p>
|
||||
<p>
|
||||
This is still a work in progress and very much a beta that will change a lot before releasing publicly,
|
||||
but feel free to use it as much as you'd like to provide feedback!
|
||||
</p>
|
||||
<p>
|
||||
If you run into any issues, please let me know <%# in the %>
|
||||
<%# link_to 'Site Support forums', 'https://www.notebook.ai/forum/site-support' %>
|
||||
<%# or %> <%= link_to 'on Discord', 'https://discord.gg/7WCuGxY3AW' %>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<% if @can_request_another && shown_any_value %>
|
||||
<div class="center">
|
||||
To request Basil create an image of <%= @character.name %>, choose your desired style.
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to basil_commission_character_path(@character, style: 'realistic') do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">photo</i>
|
||||
Photograph
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to basil_commission_character_path(@character, style: 'painting') do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">palette</i>
|
||||
Painting
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to basil_commission_character_path(@character, style: 'sketch') do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">edit</i>
|
||||
Sketch
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to basil_commission_character_path(@character, style: 'digital') do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">brush</i>
|
||||
Digital art
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to basil_commission_character_path(@character, style: 'anime') do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">face</i>
|
||||
Anime
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to basil_commission_character_path(@character, style: 'abstract') do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">supervised_user_circle</i>
|
||||
Abstract
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<% end %>
|
||||
|
||||
<% @commissions.each do |commission| %>
|
||||
<div>
|
||||
<% if commission.complete? %>
|
||||
<%# image_tag commission.image, style: 'width: 100%' %>
|
||||
<% shown_any_value = false %>
|
||||
<% @appearance_fields.each do |field| %>
|
||||
<%
|
||||
s3 = Aws::S3::Resource.new(region: "us-east-1")
|
||||
obj = s3.bucket("basil-characters").object("job-#{commission.job_id}.png")
|
||||
value = @attributes.detect { |attr| attr.attribute_field_id == field.id }.try(:value)
|
||||
next if value.nil? || value.blank?
|
||||
shown_any_value = true
|
||||
%>
|
||||
<div class="card horizontal">
|
||||
<div class="card-image">
|
||||
<%= link_to obj.presigned_url(:get) do %>
|
||||
<%= image_tag obj.presigned_url(:get) %>
|
||||
<% end %>
|
||||
<li style="margin-bottom: 1em;">
|
||||
<div class="grey-text text-darken-3" style="font-weight: bold; font-size: 0.8em">
|
||||
<%= field.label %>
|
||||
<%= range_field_tag "field[#{field.id}]", 0, { min: -1, max: 1, step: 0.25, style: 'width: 50%', class: 'js-importance-slider hide' } %>
|
||||
</div>
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<strong><%= @character.name %></strong>
|
||||
<ul>
|
||||
<li>
|
||||
Completed <%= time_ago_in_words commission.completed_at %> ago
|
||||
</li>
|
||||
<li>
|
||||
Took <%= distance_of_time_in_words commission.completed_at - commission.created_at %>
|
||||
</li>
|
||||
<hr />
|
||||
<li>
|
||||
You can click on the image to download it. Congratulations, it's yours now!
|
||||
Feel free to upload it to your character's page if you want to keep and/or use it.
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
<%= value %>
|
||||
</div>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<% if !shown_any_value %>
|
||||
<div class="red card-panel lighten-3">
|
||||
Basil works best with guidance! Please fill out some fields in the "Looks" category for this character
|
||||
before requesting an image.
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div>
|
||||
<%= link_to 'Customize per-field importance', "javascript:var sliders = document.getElementsByClassName('js-importance-slider'); for(var i = 0; i < sliders.length; i++) sliders.item(i).classList.remove('hide')" %>
|
||||
|
||||
<div class="card-panel js-importance-slider hide" style="margin-right: 1em">
|
||||
<strong>How to customize per-field importance</strong>
|
||||
<br /><br />
|
||||
|
||||
This allows you to tell Basil which fields are more or less important to you. For example, if Basil isn't
|
||||
getting a character's hair color right, you can increase the importance of the "Hair Color" field.
|
||||
<br /><br />
|
||||
You can also tell Basil to ignore a field entirely by dragging the slider all the way to the left.
|
||||
</div>
|
||||
</div>
|
||||
<br /><br />
|
||||
<div>
|
||||
<%= link_to 'Edit this character page', edit_polymorphic_path(@character), class: 'grey-text text-darken-2' %>
|
||||
</div>
|
||||
<div>
|
||||
<%= link_to 'Back to my other characters', basil_path, class: 'grey-text text-darken-2' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s7">
|
||||
<div class="card-panel" style="margin-top: 2em">
|
||||
<p>
|
||||
Basil is learning as fast as he can. Here are some tips and guidelines to get the most out of
|
||||
him right now:
|
||||
</p>
|
||||
<p>
|
||||
This is still a work in progress and very much a beta that will change a lot before releasing publicly,
|
||||
but feel free to use it as much as you'd like to provide feedback!
|
||||
</p>
|
||||
<p>
|
||||
If you run into any issues, please let me know <%# in the %>
|
||||
<%# link_to 'Site Support forums', 'https://www.notebook.ai/forum/site-support' %>
|
||||
<%# or %> <%= link_to 'on Discord', 'https://discord.gg/7WCuGxY3AW' %>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<% if @can_request_another && shown_any_value %>
|
||||
<div class="center">
|
||||
To request Basil create an image of <%= @character.name %>, choose your desired style.
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to 'javascript:commission_basil("realistic")' do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">photo</i>
|
||||
Photograph
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to 'javascript:commission_basil("painting")' do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">palette</i>
|
||||
Painting
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to 'javascript:commission_basil("sketch")' do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">edit</i>
|
||||
Sketch
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to 'javascript:commission_basil("digital")' do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">brush</i>
|
||||
Digital art
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to 'javascript:commission_basil("anime")' do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">face</i>
|
||||
Anime
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col s12 m4 l4">
|
||||
<%= link_to 'javascript:commission_basil("abstract")' do %>
|
||||
<div class="hoverable card-panel blue white-text">
|
||||
<i class="material-icons left">supervised_user_circle</i>
|
||||
Abstract
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<% end %>
|
||||
|
||||
<% @commissions.each do |commission| %>
|
||||
<div>
|
||||
<% if commission.complete? %>
|
||||
<%# image_tag commission.image, style: 'width: 100%' %>
|
||||
<%
|
||||
s3 = Aws::S3::Resource.new(region: "us-east-1")
|
||||
obj = s3.bucket("basil-characters").object("job-#{commission.job_id}.png")
|
||||
%>
|
||||
<div class="card horizontal">
|
||||
<div class="card-image">
|
||||
<%= link_to obj.presigned_url(:get) do %>
|
||||
<%= image_tag obj.presigned_url(:get) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<strong><%= @character.name %></strong>
|
||||
<ul>
|
||||
<li>
|
||||
Completed <%= time_ago_in_words commission.completed_at %> ago
|
||||
</li>
|
||||
<li>
|
||||
Took <%= distance_of_time_in_words commission.completed_at - commission.created_at %>
|
||||
</li>
|
||||
<hr />
|
||||
<li>
|
||||
You can click on the image to download it. Congratulations, it's yours now!
|
||||
Feel free to upload it to your character's page if you want to keep and/or use it.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="card-panel green white-text darken-4">
|
||||
Basil is still working on this commission...
|
||||
<div style="font-size: 0.8em">
|
||||
(Requested <%= time_ago_in_words(commission.created_at) %> ago)
|
||||
<% else %>
|
||||
<div class="card-panel green white-text darken-4">
|
||||
Basil is still working on this commission...
|
||||
<div style="font-size: 0.8em">
|
||||
(Requested <%= time_ago_in_words(commission.created_at) %> ago)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<script type="text/javascript">
|
||||
function commission_basil(style) {
|
||||
// Set style hidden value to our selected style
|
||||
$('#basil_commission_style').val(style);
|
||||
|
||||
// Submit form to start the commission
|
||||
$('#new_basil_commission').submit();
|
||||
}
|
||||
</script>
|
||||
@ -15,6 +15,7 @@
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<div>
|
||||
<%= commission.id %>.
|
||||
<strong><%= link_to commission.entity.name, commission.entity %></strong>
|
||||
<% if commission.style? %>
|
||||
<em>(<%= commission.style.humanize %>)</em>
|
||||
|
||||
@ -5,9 +5,12 @@ Rails.application.routes.draw do
|
||||
scope :ai, path: '/ai' do
|
||||
scope :basil do
|
||||
get '/', to: 'basil#index', as: :basil
|
||||
|
||||
get '/character/:id', to: 'basil#character', as: :basil_character
|
||||
get '/character/:id/commission', to: 'basil#commission_character', as: :basil_commission_character
|
||||
post '/character/:id', to: 'basil#commission'
|
||||
#get '/character/:id/commission', to: 'basil#commission_character', as: :basil_commission_character
|
||||
|
||||
# TODO this should also be a POST
|
||||
get '/complete/:jobid', to: 'basil#complete_commission'
|
||||
|
||||
get '/info', to: 'basil#info'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user