feat: add support for multiple image generation models

- Introduced constant MODELS for managing model names.
- Updated the `generate_image` method to switch between FLUX and DALL-E 3.
- Added `generate_image_dalle` and `generate_image_flux` methods for respective API calls.
- Enhanced prompts for image generation across both models, ensuring correct message construction.

This update enables users to generate images using different AI models dynamically, allowing for flexible integration and improved user experience. The handling of errors now falls back to the DALL-E model if the FLUX API fails.
This commit is contained in:
songtianlun 2025-04-10 12:00:57 +08:00
parent 719a523cc9
commit ab50ed3036
2 changed files with 66 additions and 10 deletions

View File

@ -1,4 +1,9 @@
class AiService class AiService
MODELS = {
dalle: "dall-e-3",
flux: "black-forest-labs/FLUX.1-schnell"
}.freeze
def initialize def initialize
@client = OpenAI::Client.new( @client = OpenAI::Client.new(
access_token: Rails.application.credentials.openai.token, access_token: Rails.application.credentials.openai.token,
@ -6,14 +11,18 @@ class AiService
log_errors: Rails.env.development?, # 只在开发环境下启用 log_errors: Rails.env.development?, # 只在开发环境下启用
request_timeout: 240 request_timeout: 240
) )
# 这里假设你已经在credentials中添加了siliconflow相关配置
@flux_token = Rails.application.credentials.siliconflow.token
@flux_api_url = Rails.application.credentials.siliconflow.url
end end
def generate_prompt(city, weather_data) def generate_prompt(city, weather_data)
city_desc = generate_location_desc(city) city_desc = generate_location_desc(city)
system_message = system_message =
"You are a professional artist creating prompts for DALL-E 3. Create realistic, artistic weather scenes featuring iconic landmarks." "You are a professional artist creating prompts for image generation. Create realistic, artistic weather scenes featuring iconic landmarks."
user_message = generate_dall_e_3_prompt_request(city, weather_data, city_desc) user_message = generate_image_prompt_request(city, weather_data, city_desc)
ask_ai(system_message, user_message) ask_ai(system_message, user_message)
end end
@ -29,13 +38,23 @@ class AiService
ask_ai(system_message, user_message) ask_ai(system_message, user_message)
end end
def generate_image(prompt) def generate_image(prompt, model_type = :flux)
case model_type
when :flux
generate_image_flux(prompt)
when :dalle
generate_image_dalle(prompt)
else
generate_image_flux(prompt) # 默认使用FLUX
end
end
def generate_image_dalle(prompt)
response = @client.images.generate( response = @client.images.generate(
parameters: { parameters: {
model: "dall-e-3", model: MODELS[:dalle],
prompt: prompt, prompt: prompt,
size: "1792x1024", size: "1792x1024",
# quality: "standard",
quality: "hd", quality: "hd",
n: 1 n: 1
} }
@ -44,6 +63,43 @@ class AiService
response.dig("data", 0, "url") response.dig("data", 0, "url")
end end
def generate_image_flux(prompt)
uri = URI(@flux_api_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{@flux_token}"
request["Content-Type"] = "application/json"
# 生成随机种子
seed = rand(10000000000)
request.body = {
model: MODELS[:flux],
prompt: prompt,
# image_size: "1024x576",
image_size: "1792x1024",
seed: seed,
prompt_enhancement: false
}.to_json
response = http.request(request)
if response.is_a?(Net::HTTPSuccess)
result = JSON.parse(response.body)
result.dig("data", 0, "url")
else
Rails.logger.error("FLUX API error: #{response.code} - #{response.body}")
# 异常时回退到DALL-E
# generate_image_dalle(prompt)
end
rescue => e
Rails.logger.error("Error generating image with FLUX: #{e.message}")
# 异常时回退到DALL-E
# generate_image_dalle(prompt)
end
private private
def ask_ai(system_message, user_message) def ask_ai(system_message, user_message)
@ -65,13 +121,13 @@ class AiService
response.dig("choices", 0, "message", "content") response.dig("choices", 0, "message", "content")
end end
def generate_dall_e_3_prompt_request(city, weather_data, city_desc) def generate_image_prompt_request(city, weather_data, city_desc)
region = city.country.region&.name || "" region = city.country.region&.name || ""
country = city.country.name || "" country = city.country.name || ""
state = city.state&.name || "" state = city.state&.name || ""
<<~PROMPT <<~PROMPT
Generate a highly detailed and photorealistic DALL-E 3 prompt capturing a specific weather moment in #{city.name}, #{state}, #{country}, #{region}. Generate a highly detailed and photorealistic image prompt capturing a specific weather moment in #{city.name}, #{state}, #{country}, #{region}.
**Scene Inspiration:** **Scene Inspiration:**
#{city_desc} #{city_desc}
@ -92,7 +148,7 @@ class AiService
- On sunny days (low #{weather_data[:cloud]}%), emphasize the brightness, warmth, contrast, and potentially lens flare for added realism. - On sunny days (low #{weather_data[:cloud]}%), emphasize the brightness, warmth, contrast, and potentially lens flare for added realism.
- **Avoid:** Do NOT produce illustrations, paintings, sketches, anime, cartoons, or overly stylized digital art. The final image must look like a real photograph. - **Avoid:** Do NOT produce illustrations, paintings, sketches, anime, cartoons, or overly stylized digital art. The final image must look like a real photograph.
**Output Format:** Directly output the complete, detailed DALL-E prompt string only. No introductory text, labels, or explanations. **Output Format:** Directly output the complete, detailed image generation prompt string only. No introductory text, labels, or explanations.
PROMPT PROMPT
end end
end end

View File

@ -1 +1 @@
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--NoWqPB3RsXQtcokp--t2ygZml0sjj/3J3lrKsguQ== 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--d7o8roTgR6J0L8GM--7Im0i3w2N7jwl89+qBJApg==