Skip to content

Commit 81edd4f

Browse files
committed
basil reset
1 parent d5eb263 commit 81edd4f

File tree

3 files changed

+163
-37
lines changed

3 files changed

+163
-37
lines changed

app/controllers/basil_controller.rb

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,9 @@ def commission
457457
return
458458
end
459459

460+
# At this point, the content is valid, and the user is allowed to commission it!
461+
# We can now create the prompt, and save the commission.
462+
460463
# Before creating the prompt, do a little config to tweak things to work well :)
461464
labels_to_omit_label_text = [
462465
"Name",
@@ -468,24 +471,24 @@ def commission
468471
"Type of food"
469472
].map(&:downcase)
470473
field_importance_multipliers = {
471-
'hair': 1.15,
472-
'hair color': 1.55,
473-
'hair style': 1.10,
474-
'skin tone': 1.05,
475-
'race': 1.10,
476-
'eye color': 1.05,
477-
'gender': 1.15,
474+
'hair': 1.00,
475+
'hair color': 1.00,
476+
'hair style': 1.00,
477+
'skin tone': 1.00,
478+
'race': 1.00,
479+
'eye color': 1.00,
480+
'gender': 1.00,
478481
'description': 1.00,
479-
'item type': 1.55,
480-
'type': 1.15,
481-
'type of building': 1.25,
482-
'type of condition': 1.25,
483-
'type of food': 1.25,
484-
'type of landmark': 1.25,
485-
'type of magic': 1.25,
486-
'type of school': 1.25,
487-
'type of vehicle': 1.25,
488-
'type of creature': 1.25
482+
'item type': 1.00,
483+
'type': 1.00,
484+
'type of building': 1.00,
485+
'type of condition': 1.00,
486+
'type of food': 1.00,
487+
'type of landmark': 1.00,
488+
'type of magic': 1.00,
489+
'type of school': 1.00,
490+
'type of vehicle': 1.00,
491+
'type of creature': 1.00
489492
}
490493
label_value_pairs_to_skip_entirely = [
491494
['race', 'human']
@@ -540,6 +543,7 @@ def commission
540543
.to_h
541544
guidance.update(guidance: guidance_data)
542545

546+
# Finally, create the commission!
543547
BasilCommission.create!(
544548
user: current_user,
545549
entity_type: @content.page_type,
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
require 'net/http'
2+
require 'uri'
3+
require 'json'
4+
require 'base64'
5+
require 'tempfile'
6+
require 'aws-sdk-s3'
7+
8+
class GenerateBasilImageJob < ApplicationJob
9+
queue_as :default
10+
11+
# Define potential errors for rescue
12+
class ApiError < StandardError; end
13+
14+
def perform(basil_commission_id)
15+
# Find the BasilCommission record
16+
commission = BasilCommission.find(basil_commission_id)
17+
18+
# Skip if already completed (image attached)
19+
return if commission.image.attached?
20+
21+
# Details we can use:
22+
# commission.prompt - the prompt for the image
23+
# commission.style - the style of the image
24+
# commission.entity_type - the type of entity the image is for (e.g. Character, Location, etc)
25+
prompt_to_send = "(#{commission.style} style), #{commission.entity_type}, #{commission.prompt}"
26+
27+
# Make the API request to generate the image
28+
endpoint_url = ENV.fetch("BASIL_ENDPOINT")
29+
api_url = URI.join(endpoint_url, '/sdapi/v1/txt2img')
30+
31+
puts "*" * 100
32+
puts "Prompt: #{prompt_to_send}"
33+
puts "*" * 100
34+
35+
payload = {
36+
prompt: prompt_to_send,
37+
steps: 20,
38+
# Add other parameters like negative_prompt, width, height, sampler_index, etc. as needed
39+
# Example:
40+
negative_prompt: "nudity, nsfw, nude, xxx, low quality, blurry, worst quality, diptych, triptych, multiple images, multiple subjects",
41+
width: 512,
42+
height: 512,
43+
sd_model_checkpoint: "openxl"
44+
}.to_json
45+
46+
begin
47+
response = Net::HTTP.post(api_url, payload, "Content-Type" => "application/json")
48+
response.value # Raises an HTTPError if the response is not 2xx
49+
50+
response_data = JSON.parse(response.body)
51+
image_data_base64 = response_data['images']&.first
52+
53+
raise ApiError, "No image data found in API response" unless image_data_base64
54+
55+
# Decode the base64 image data
56+
image_data_binary = Base64.decode64(image_data_base64)
57+
58+
# --- Manual S3 Upload and ActiveStorage Blob Creation ---
59+
begin
60+
s3_client = Aws::S3::Client.new(
61+
region: ENV.fetch('AWS_REGION', 'us-east-1'),
62+
access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'),
63+
secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY')
64+
)
65+
bucket_name = ENV.fetch('S3_BASIL_BUCKET_NAME', 'basil-commissions')
66+
s3_key = "job-#{commission.job_id || SecureRandom.uuid}.png" # Use job_id for the key
67+
filename = s3_key # Use the same for the filename
68+
69+
# 1. Upload directly to S3
70+
Rails.logger.info "Uploading key '#{s3_key}' to bucket '#{bucket_name}'"
71+
upload_response = s3_client.put_object(
72+
bucket: bucket_name,
73+
key: s3_key,
74+
body: image_data_binary,
75+
content_type: 'image/png'
76+
# acl: 'public-read' # Only if you wanted public files, which we don't
77+
)
78+
79+
# 2. Create the ActiveStorage Blob record manually
80+
checksum = upload_response.etag.gsub('"','') # ETag comes with quotes
81+
byte_size = image_data_binary.size
82+
83+
blob = ActiveStorage::Blob.create!(
84+
key: s3_key,
85+
filename: filename,
86+
content_type: 'image/png',
87+
byte_size: byte_size,
88+
checksum: checksum,
89+
service_name: :amazon_basil # Crucial: Specify the service!
90+
)
91+
92+
# 3. Associate the blob with the commission
93+
# Note: We use update! which saves immediately. No separate save! needed.
94+
commission.update!(image: blob)
95+
96+
# 4. Update completed_at timestamp
97+
commission.update!(completed_at: Time.current)
98+
99+
rescue Aws::S3::Errors::ServiceError => e
100+
Rails.logger.error "Manual S3 Upload/Blob Creation Failed: #{e.class} - #{e.message}"
101+
# Re-raise to let the job runner handle retries/failure
102+
raise e
103+
rescue ActiveRecord::RecordInvalid => e
104+
Rails.logger.error "Manual Blob Creation/Association Failed: #{e.class} - #{e.message}"
105+
# Re-raise
106+
raise e
107+
end
108+
109+
rescue Net::HTTPError, Net::OpenTimeout, Net::ReadTimeout, ApiError, JSON::ParserError => e
110+
# Handle API errors, timeouts, or decoding issues
111+
# Log the error, potentially retry the job, or mark the commission as failed
112+
Rails.logger.error("Basil Image Generation Failed for commission #{commission.id}: #{e.message}")
113+
# Example: Mark as failed (requires adding a status field to BasilCommission)
114+
# commission.update(status: 'failed', error_message: e.message)
115+
# Or re-raise to let the job runner handle retries/failure
116+
raise e
117+
end
118+
end
119+
end

app/models/basil_commission.rb

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,29 @@ class BasilCommission < ApplicationRecord
1212

1313
after_create :submit_to_job_queue!
1414
def submit_to_job_queue!
15-
# TODO clean this up and put it in a config
16-
region = 'us-east-1'
17-
queue_name = 'basil-commissions'
18-
19-
# TODO clean this up and put it in a service
20-
sts_client = Aws::STS::Client.new(region: region)
21-
queue_url = 'https://sqs.' + region + '.amazonaws.com/' + sts_client.get_caller_identity.account + '/' + queue_name
22-
sqs_client = Aws::SQS::Client.new(region: region)
23-
24-
message_body = {
25-
job_id: job_id,
26-
prompt: prompt,
27-
style: style,
28-
page_type: entity_type
29-
}.to_json
30-
31-
sqs_client.send_message(
32-
queue_url: queue_url,
33-
message_body: message_body
34-
)
15+
# # TODO clean this up and put it in a config
16+
# region = 'us-east-1'
17+
# queue_name = 'basil-commissions'
18+
19+
# # TODO clean this up and put it in a service
20+
# sts_client = Aws::STS::Client.new(region: region)
21+
# queue_url = 'https://sqs.' + region + '.amazonaws.com/' + sts_client.get_caller_identity.account + '/' + queue_name
22+
# sqs_client = Aws::SQS::Client.new(region: region)
23+
24+
# message_body = {
25+
# job_id: job_id,
26+
# prompt: prompt,
27+
# style: style,
28+
# page_type: entity_type
29+
# }.to_json
30+
31+
# sqs_client.send_message(
32+
# queue_url: queue_url,
33+
# message_body: message_body
34+
# )
35+
36+
# Enqueue the background job to generate the image
37+
GenerateBasilImageJob.perform_now(self.id)
3538
end
3639

3740
def cache_after_complete!

0 commit comments

Comments
 (0)