- Implement method to get multilingual city descriptions - Create worker for generating descriptions in the background - Update database schema to include description translations - Update AiService to fetch descriptions from City model This commit introduces a feature to generate city descriptions in multiple languages using AI. It includes methods for caching descriptions and a background job to handle generation to improve performance and user experience.
156 lines
6.3 KiB
Ruby
156 lines
6.3 KiB
Ruby
class AiService
|
|
MODELS = {
|
|
dalle: "dall-e-3",
|
|
flux: "black-forest-labs/FLUX.1-schnell"
|
|
}.freeze
|
|
|
|
def initialize
|
|
@client = OpenAI::Client.new(
|
|
access_token: Rails.application.credentials.openai.token,
|
|
uri_base: Rails.application.credentials.openai.uri,
|
|
log_errors: Rails.env.development?, # 只在开发环境下启用
|
|
request_timeout: 240
|
|
)
|
|
|
|
# 这里假设你已经在credentials中添加了siliconflow相关配置
|
|
@flux_token = Rails.application.credentials.siliconflow.token
|
|
@flux_api_url = Rails.application.credentials.siliconflow.url
|
|
end
|
|
|
|
def generate_prompt(city, weather_data)
|
|
# city_desc = generate_location_desc(city)
|
|
city_desc = city.get_description('en')
|
|
|
|
system_message =
|
|
"You are a professional artist creating prompts for image generation. Create realistic, artistic weather scenes featuring iconic landmarks."
|
|
user_message = generate_image_prompt_request(city, weather_data, city_desc)
|
|
ask_ai(system_message, user_message)
|
|
end
|
|
|
|
# def generate_location_desc(city)
|
|
# region = city.country.region&.name || ""
|
|
# country = city.country.name || ""
|
|
# state = city.state&.name || ""
|
|
|
|
# system_message =
|
|
# "You are a global geography master, you understand the culture, geography, architecture, customs and other information of all cities around the world. Describe this city based on the city I gave you. Include details about its culture, climate, landmarks, and any unique features that make this place special. Condense the keyword into a description of about 50 words"
|
|
# user_message =
|
|
# "Describe the characteristics of the city of #{city.name}, located in the #{state}, #{country}, #{region}"
|
|
# ask_ai(system_message, user_message)
|
|
# end
|
|
|
|
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(
|
|
parameters: {
|
|
model: MODELS[:dalle],
|
|
prompt: prompt,
|
|
size: "1792x1024",
|
|
quality: "hd",
|
|
n: 1
|
|
}
|
|
)
|
|
|
|
response.dig("data", 0, "url")
|
|
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
|
|
|
|
def ask_ai(system_message, user_message)
|
|
response = @client.chat(
|
|
parameters: {
|
|
model: "gpt-4o-mini",
|
|
messages:
|
|
[ {
|
|
role: "system",
|
|
content: system_message
|
|
}, {
|
|
role: "user",
|
|
content: user_message
|
|
} ],
|
|
temperature: 0.7,
|
|
max_tokens: 300
|
|
}
|
|
)
|
|
response.dig("choices", 0, "message", "content")
|
|
end
|
|
|
|
private
|
|
|
|
def generate_image_prompt_request(city, weather_data, city_desc)
|
|
region = city.country.region&.name || ""
|
|
country = city.country.name || ""
|
|
state = city.state&.name || ""
|
|
|
|
<<~PROMPT
|
|
Generate a highly detailed and photorealistic image prompt capturing a specific weather moment in #{city.name}, #{state}, #{country}, #{region}.
|
|
|
|
**Scene Inspiration:**
|
|
#{city_desc}
|
|
(Use this description to inform the specific elements and mood of the scene).
|
|
|
|
**Weather Conditions to Depict:**
|
|
- Temperature Feel: Convey the atmosphere associated with #{weather_data[:temperature]}°C.
|
|
- Visible Weather: #{weather_data[:description]}. Show its effects on the environment (e.g., rain slicking streets, sun casting sharp shadows, fog softening distant objects).
|
|
- Sky & Light: Sky is #{weather_data[:cloud]}% cloud covered. Render the lighting accurately for the time: #{weather_data[:time]} (e.g., warm morning light, bright midday sun, soft afternoon light, dramatic sunset colors, twilight ambiance, or night scene lighting like streetlights).
|
|
|
|
**Mandatory Requirements for Realism and Aesthetics:**
|
|
- **Style:** Photorealistic photograph, hyperrealistic, cinematic quality. Capture fine details, textures (e.g., wet pavement, brickwork, foliage), and accurate lighting.
|
|
- **Composition:** Atmospheric and artistic composition (e.g., rule of thirds, leading lines), creating depth and visual interest. Consider a natural eye-level perspective or a slightly elevated viewpoint for landscape/cityscape.
|
|
- **Landmarks/Architecture:** Feature iconic landmarks or characteristic architecture from #{city.name} as naturally integrated elements within the scene.
|
|
- **Clarity & Brightness:** Ensure the image is sharp, clear, and well-exposed. Avoid excessive blur or motion artifacts unless intentionally used for artistic effect (like long exposure for rain).
|
|
- **Lighting Enhancement:**
|
|
- Even in overcast conditions (high #{weather_data[:cloud]}), maintain visual appeal with soft, diffused light, ensuring details in shadows are visible and colors remain natural yet engaging.
|
|
- 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.
|
|
|
|
**Output Format:** Directly output the complete, detailed image generation prompt string only. No introductory text, labels, or explanations.
|
|
PROMPT
|
|
end
|
|
end
|