638 lines
22 KiB
Rust
638 lines
22 KiB
Rust
use super::*;
|
|
|
|
pub(crate) fn map_puzzle_generation_endpoint_error(error: AppError) -> AppError {
|
|
if error.code() == "UPSTREAM_ERROR" {
|
|
let body_text = error.body_text();
|
|
return AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
|
"provider": PUZZLE_AGENT_API_BASE_PROVIDER,
|
|
"message": format!("拼图图片生成失败:{body_text}"),
|
|
}));
|
|
}
|
|
|
|
error
|
|
}
|
|
|
|
pub(crate) fn should_use_uploaded_puzzle_image_directly(
|
|
reference_image_src: Option<&str>,
|
|
ai_redraw: bool,
|
|
) -> bool {
|
|
!ai_redraw
|
|
&& reference_image_src
|
|
.map(str::trim)
|
|
.is_some_and(|value| !value.is_empty())
|
|
}
|
|
|
|
pub(crate) async fn create_uploaded_puzzle_image_candidate(
|
|
state: &PuzzleApiState,
|
|
owner_user_id: &str,
|
|
session_id: &str,
|
|
level_name: &str,
|
|
prompt: &str,
|
|
reference_image_src: &str,
|
|
candidate_start_index: usize,
|
|
) -> Result<GeneratedPuzzleImageCandidate, AppError> {
|
|
let http_client = reqwest::Client::new();
|
|
let downloaded_image =
|
|
resolve_puzzle_reference_image_as_data_url(state, &http_client, reference_image_src)
|
|
.await
|
|
.map(PuzzleDownloadedImage::from_resolved_reference_image)
|
|
.map_err(|error| {
|
|
if error.status_code() == StatusCode::BAD_REQUEST {
|
|
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
|
"provider": PUZZLE_AGENT_API_BASE_PROVIDER,
|
|
"field": "referenceImageSrc",
|
|
"message": "关闭 AI 重绘时上传图必须是图片 Data URL 或历史生成图片路径。",
|
|
}))
|
|
} else {
|
|
error
|
|
}
|
|
})?;
|
|
let candidate_id = format!("{session_id}-candidate-{}", candidate_start_index + 1);
|
|
let asset = persist_puzzle_generated_asset(
|
|
state,
|
|
owner_user_id,
|
|
session_id,
|
|
level_name,
|
|
candidate_id.as_str(),
|
|
"uploaded-direct",
|
|
downloaded_image.clone(),
|
|
current_utc_micros(),
|
|
)
|
|
.await
|
|
.map_err(map_puzzle_generation_endpoint_error)?;
|
|
|
|
Ok(GeneratedPuzzleImageCandidate {
|
|
record: PuzzleGeneratedImageCandidateRecord {
|
|
candidate_id,
|
|
image_src: asset.image_src,
|
|
asset_id: asset.asset_id,
|
|
prompt: prompt.to_string(),
|
|
actual_prompt: None,
|
|
source_type: "uploaded".to_string(),
|
|
selected: true,
|
|
},
|
|
downloaded_image,
|
|
})
|
|
}
|
|
|
|
pub(crate) async fn generate_puzzle_image_candidates(
|
|
state: &PuzzleApiState,
|
|
owner_user_id: &str,
|
|
profile_id: Option<&str>,
|
|
session_id: &str,
|
|
level_name: &str,
|
|
prompt: &str,
|
|
reference_image_src: Option<&str>,
|
|
use_reference_image_generation: bool,
|
|
image_model: Option<&str>,
|
|
candidate_count: u32,
|
|
candidate_start_index: usize,
|
|
) -> Result<Vec<GeneratedPuzzleImageCandidate>, AppError> {
|
|
let total_started_at = Instant::now();
|
|
let count = candidate_count.clamp(1, 1);
|
|
let resolved_model = resolve_puzzle_image_model(image_model);
|
|
let http_client = build_puzzle_image_http_client(state, resolved_model)?;
|
|
let has_reference_image = has_puzzle_reference_image(reference_image_src);
|
|
let should_use_reference_image_generation = should_use_puzzle_reference_image_generation(
|
|
reference_image_src,
|
|
use_reference_image_generation,
|
|
);
|
|
let actual_prompt = build_puzzle_vector_engine_generation_prompt(
|
|
build_puzzle_image_prompt(level_name, prompt).as_str(),
|
|
should_use_reference_image_generation,
|
|
);
|
|
tracing::info!(
|
|
provider = resolved_model.provider_name(),
|
|
image_model = resolved_model.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
prompt_chars = prompt.chars().count(),
|
|
actual_prompt_chars = actual_prompt.chars().count(),
|
|
has_reference_image,
|
|
use_reference_image_generation = should_use_reference_image_generation,
|
|
"拼图图片生成请求已准备"
|
|
);
|
|
let reference_image_started_at = Instant::now();
|
|
let reference_image = match reference_image_src
|
|
.map(str::trim)
|
|
.filter(|value| !value.is_empty())
|
|
.filter(|_| should_use_reference_image_generation)
|
|
{
|
|
Some(source) => {
|
|
let resolved =
|
|
resolve_puzzle_reference_image(state, &http_client, source, Some(owner_user_id))
|
|
.await?;
|
|
tracing::info!(
|
|
provider = resolved_model.provider_name(),
|
|
image_model = resolved_model.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
reference_mime = %resolved.mime_type,
|
|
reference_bytes = resolved.bytes_len,
|
|
elapsed_ms = reference_image_started_at.elapsed().as_millis() as u64,
|
|
"拼图参考图解析完成"
|
|
);
|
|
Some(resolved)
|
|
}
|
|
None => None,
|
|
};
|
|
if !should_use_reference_image_generation {
|
|
tracing::info!(
|
|
provider = resolved_model.provider_name(),
|
|
image_model = resolved_model.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
has_reference_image,
|
|
use_reference_image_generation = should_use_reference_image_generation,
|
|
elapsed_ms = reference_image_started_at.elapsed().as_millis() as u64,
|
|
"拼图参考图解析跳过"
|
|
);
|
|
}
|
|
// 中文注释:SpacetimeDB reducer 不能做外部 I/O,参考图读取与外部生图都必须停留在 api-server。
|
|
// 中文注释:拼图作品资产统一按 1:1 正方形生成,前端运行时也按正方形棋盘切块承载。
|
|
let settings = require_puzzle_vector_engine_settings(state)?;
|
|
let settings = PuzzleVectorEngineSettings {
|
|
external_api_audit_user_id: Some(owner_user_id.to_string()),
|
|
external_api_audit_profile_id: profile_id.map(ToOwned::to_owned),
|
|
..settings
|
|
};
|
|
let vector_engine_started_at = Instant::now();
|
|
let generated = if should_use_reference_image_generation {
|
|
let reference_image = reference_image.as_ref().ok_or_else(|| {
|
|
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
|
"provider": "puzzle",
|
|
"field": "referenceImageSrc",
|
|
"message": "AI 重绘需要提供参考图。",
|
|
}))
|
|
})?;
|
|
create_puzzle_vector_engine_image_generation(
|
|
&http_client,
|
|
&settings,
|
|
resolved_model,
|
|
actual_prompt.as_str(),
|
|
PUZZLE_DEFAULT_NEGATIVE_PROMPT,
|
|
PUZZLE_VECTOR_ENGINE_GENERATED_IMAGE_SIZE,
|
|
count,
|
|
Some(reference_image),
|
|
)
|
|
.await
|
|
} else {
|
|
create_puzzle_vector_engine_image_generation(
|
|
&http_client,
|
|
&settings,
|
|
resolved_model,
|
|
actual_prompt.as_str(),
|
|
PUZZLE_DEFAULT_NEGATIVE_PROMPT,
|
|
PUZZLE_VECTOR_ENGINE_GENERATED_IMAGE_SIZE,
|
|
count,
|
|
None,
|
|
)
|
|
.await
|
|
}
|
|
.map_err(map_puzzle_generation_endpoint_error)?;
|
|
tracing::info!(
|
|
provider = resolved_model.provider_name(),
|
|
image_model = resolved_model.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
generated_image_count = generated.images.len(),
|
|
elapsed_ms = vector_engine_started_at.elapsed().as_millis() as u64,
|
|
"拼图 VectorEngine 生图与下载完成"
|
|
);
|
|
let mut items = Vec::with_capacity(generated.images.len());
|
|
|
|
for (index, image) in generated.images.into_iter().enumerate() {
|
|
let candidate_id = format!(
|
|
"{session_id}-candidate-{}",
|
|
candidate_start_index + index + 1
|
|
);
|
|
let downloaded_image = image.clone();
|
|
let persist_started_at = Instant::now();
|
|
let asset = persist_puzzle_generated_asset(
|
|
state,
|
|
owner_user_id,
|
|
session_id,
|
|
level_name,
|
|
candidate_id.as_str(),
|
|
generated.task_id.as_str(),
|
|
image,
|
|
current_utc_micros(),
|
|
)
|
|
.await
|
|
.map_err(map_puzzle_generation_endpoint_error)?;
|
|
tracing::info!(
|
|
provider = resolved_model.provider_name(),
|
|
image_model = resolved_model.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
candidate_id = %candidate_id,
|
|
image_bytes = downloaded_image.bytes.len(),
|
|
image_mime = %downloaded_image.mime_type,
|
|
elapsed_ms = persist_started_at.elapsed().as_millis() as u64,
|
|
"拼图生成图片已写入 OSS 与资产索引"
|
|
);
|
|
items.push(GeneratedPuzzleImageCandidate {
|
|
record: PuzzleGeneratedImageCandidateRecord {
|
|
candidate_id,
|
|
image_src: asset.image_src,
|
|
asset_id: asset.asset_id,
|
|
prompt: prompt.to_string(),
|
|
actual_prompt: Some(actual_prompt.clone()),
|
|
source_type: resolved_model.candidate_source_type().to_string(),
|
|
// 单图生成结果总是直接成为当前正式图。
|
|
selected: index == 0,
|
|
},
|
|
downloaded_image,
|
|
});
|
|
}
|
|
|
|
tracing::info!(
|
|
provider = resolved_model.provider_name(),
|
|
image_model = resolved_model.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
candidate_count = items.len(),
|
|
has_reference_image,
|
|
elapsed_ms = total_started_at.elapsed().as_millis() as u64,
|
|
"拼图图片候选生成完成"
|
|
);
|
|
Ok(items)
|
|
}
|
|
|
|
pub(crate) async fn generate_puzzle_ui_background_image(
|
|
state: &PuzzleApiState,
|
|
request_context: &RequestContext,
|
|
owner_user_id: &str,
|
|
session_id: &str,
|
|
level_name: &str,
|
|
prompt: &str,
|
|
) -> Result<GeneratedPuzzleUiBackgroundResponse, AppError> {
|
|
let settings = require_openai_image_settings(state.root_state())?
|
|
.with_external_api_audit_context(
|
|
request_context,
|
|
Some(owner_user_id.to_string()),
|
|
Some(session_id.to_string()),
|
|
);
|
|
let http_client = build_openai_image_http_client(&settings)?;
|
|
let generated = create_openai_image_generation(
|
|
&http_client,
|
|
&settings,
|
|
build_puzzle_ui_background_generation_prompt(level_name, prompt).as_str(),
|
|
Some("文字、水印、按钮文字、数字、教程浮层、拼图碎片、完整拼图图像、拼图槽、棋盘、拼图区边框、物品槽、HUD、角色手指"),
|
|
"9:16",
|
|
1,
|
|
&[],
|
|
"拼图 UI 背景图生成失败",
|
|
)
|
|
.await?;
|
|
let image = generated.images.into_iter().next().ok_or_else(|| {
|
|
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
|
"provider": VECTOR_ENGINE_PROVIDER,
|
|
"message": "拼图 UI 背景图生成失败:未返回图片",
|
|
}))
|
|
})?;
|
|
persist_puzzle_ui_background_image(
|
|
state,
|
|
owner_user_id,
|
|
session_id,
|
|
level_name,
|
|
generated.task_id.as_str(),
|
|
image,
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub(crate) async fn generate_puzzle_level_asset_bundle(
|
|
state: &PuzzleApiState,
|
|
request_context: &RequestContext,
|
|
owner_user_id: &str,
|
|
session_id: &str,
|
|
level_name: &str,
|
|
puzzle_image: &PuzzleDownloadedImage,
|
|
) -> Result<GeneratedPuzzleLevelAssetBundle, AppError> {
|
|
let settings = require_puzzle_vector_engine_settings(state)?.with_external_api_audit_context(
|
|
request_context,
|
|
Some(owner_user_id.to_string()),
|
|
Some(session_id.to_string()),
|
|
);
|
|
let http_client = build_puzzle_image_http_client(state, PuzzleImageModel::GptImage2)?;
|
|
let puzzle_reference = build_puzzle_downloaded_image_reference(puzzle_image);
|
|
let bundle_started_at = Instant::now();
|
|
tracing::info!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
"拼图关卡资产包生成开始"
|
|
);
|
|
let scene_started_at = Instant::now();
|
|
let scene_generated = match create_puzzle_vector_engine_image_generation(
|
|
&http_client,
|
|
&settings,
|
|
PuzzleImageModel::GptImage2,
|
|
PUZZLE_LEVEL_SCENE_IMAGE_PROMPT,
|
|
"",
|
|
PUZZLE_VECTOR_ENGINE_PORTRAIT_IMAGE_SIZE,
|
|
1,
|
|
Some(&puzzle_reference),
|
|
)
|
|
.await
|
|
.map_err(map_puzzle_generation_endpoint_error)
|
|
{
|
|
Ok(generated) => {
|
|
tracing::info!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
slot = "level_scene",
|
|
elapsed_ms = scene_started_at.elapsed().as_millis() as u64,
|
|
"拼图关卡场景图生成完成"
|
|
);
|
|
generated
|
|
}
|
|
Err(error) => {
|
|
tracing::warn!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
slot = "level_scene",
|
|
elapsed_ms = scene_started_at.elapsed().as_millis() as u64,
|
|
error = %error,
|
|
"拼图关卡场景图生成失败"
|
|
);
|
|
return Err(error);
|
|
}
|
|
};
|
|
let scene_image = scene_generated.images.into_iter().next().ok_or_else(|| {
|
|
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
|
"provider": VECTOR_ENGINE_PROVIDER,
|
|
"message": "拼图关卡画面图生成失败:未返回图片",
|
|
}))
|
|
})?;
|
|
let scene_reference = build_puzzle_downloaded_image_reference(&scene_image);
|
|
let scene_persist_started_at = Instant::now();
|
|
let level_scene = persist_puzzle_level_asset_image(
|
|
state,
|
|
owner_user_id,
|
|
session_id,
|
|
level_name,
|
|
scene_generated.task_id.as_str(),
|
|
"level-scene",
|
|
"puzzle_level_scene_image",
|
|
"level_scene",
|
|
"scene",
|
|
scene_image,
|
|
)
|
|
.await?;
|
|
tracing::info!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
slot = "level_scene",
|
|
elapsed_ms = scene_persist_started_at.elapsed().as_millis() as u64,
|
|
"拼图关卡场景图持久化完成"
|
|
);
|
|
let ui_spritesheet = generate_and_persist_puzzle_level_asset(
|
|
state,
|
|
&http_client,
|
|
&settings,
|
|
owner_user_id,
|
|
session_id,
|
|
level_name,
|
|
PUZZLE_UI_SPRITESHEET_IMAGE_PROMPT,
|
|
PUZZLE_VECTOR_ENGINE_SQUARE_IMAGE_SIZE,
|
|
&scene_reference,
|
|
"ui-spritesheet",
|
|
"puzzle_ui_spritesheet_image",
|
|
"ui_spritesheet",
|
|
"spritesheet",
|
|
)
|
|
.await?;
|
|
let level_background = generate_and_persist_puzzle_level_asset(
|
|
state,
|
|
&http_client,
|
|
&settings,
|
|
owner_user_id,
|
|
session_id,
|
|
level_name,
|
|
PUZZLE_LEVEL_BACKGROUND_IMAGE_PROMPT,
|
|
PUZZLE_VECTOR_ENGINE_PORTRAIT_IMAGE_SIZE,
|
|
&scene_reference,
|
|
"level-background",
|
|
"puzzle_level_background_image",
|
|
"level_background",
|
|
"background",
|
|
)
|
|
.await?;
|
|
|
|
tracing::info!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
elapsed_ms = bundle_started_at.elapsed().as_millis() as u64,
|
|
"拼图关卡资产包生成完成"
|
|
);
|
|
Ok(GeneratedPuzzleLevelAssetBundle {
|
|
level_scene,
|
|
ui_spritesheet,
|
|
level_background,
|
|
})
|
|
}
|
|
|
|
async fn generate_and_persist_puzzle_level_asset(
|
|
state: &PuzzleApiState,
|
|
http_client: &reqwest::Client,
|
|
settings: &PuzzleVectorEngineSettings,
|
|
owner_user_id: &str,
|
|
session_id: &str,
|
|
level_name: &str,
|
|
prompt: &str,
|
|
size: &str,
|
|
reference_image: &PuzzleResolvedReferenceImage,
|
|
path_segment: &str,
|
|
asset_kind: &str,
|
|
slot: &str,
|
|
file_stem: &str,
|
|
) -> Result<GeneratedPuzzleLevelAssetResponse, AppError> {
|
|
let started_at = Instant::now();
|
|
tracing::info!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
slot,
|
|
asset_kind,
|
|
size,
|
|
prompt_chars = prompt.chars().count(),
|
|
reference_image_bytes = reference_image.bytes_len,
|
|
"拼图关卡资产生成请求开始"
|
|
);
|
|
let generated = match create_puzzle_vector_engine_image_generation(
|
|
http_client,
|
|
settings,
|
|
PuzzleImageModel::GptImage2,
|
|
prompt,
|
|
"",
|
|
size,
|
|
1,
|
|
Some(reference_image),
|
|
)
|
|
.await
|
|
.map_err(map_puzzle_generation_endpoint_error)
|
|
{
|
|
Ok(generated) => {
|
|
tracing::info!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
slot,
|
|
asset_kind,
|
|
elapsed_ms = started_at.elapsed().as_millis() as u64,
|
|
"拼图关卡资产生成请求完成"
|
|
);
|
|
generated
|
|
}
|
|
Err(error) => {
|
|
tracing::warn!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
slot,
|
|
asset_kind,
|
|
elapsed_ms = started_at.elapsed().as_millis() as u64,
|
|
error = %error,
|
|
"拼图关卡资产生成请求失败"
|
|
);
|
|
return Err(error);
|
|
}
|
|
};
|
|
let image = generated.images.into_iter().next().ok_or_else(|| {
|
|
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
|
"provider": VECTOR_ENGINE_PROVIDER,
|
|
"message": format!("拼图关卡资产生成失败:{asset_kind} 未返回图片"),
|
|
}))
|
|
})?;
|
|
let image = if slot == "ui_spritesheet" {
|
|
make_puzzle_ui_spritesheet_image_transparent(image)?
|
|
} else {
|
|
image
|
|
};
|
|
|
|
let persist_started_at = Instant::now();
|
|
let persisted = persist_puzzle_level_asset_image(
|
|
state,
|
|
owner_user_id,
|
|
session_id,
|
|
level_name,
|
|
generated.task_id.as_str(),
|
|
path_segment,
|
|
asset_kind,
|
|
slot,
|
|
file_stem,
|
|
image,
|
|
)
|
|
.await?;
|
|
tracing::info!(
|
|
provider = VECTOR_ENGINE_PROVIDER,
|
|
image_model = PuzzleImageModel::GptImage2.request_model_name(),
|
|
session_id,
|
|
level_name,
|
|
slot,
|
|
asset_kind,
|
|
elapsed_ms = persist_started_at.elapsed().as_millis() as u64,
|
|
"拼图关卡资产持久化完成"
|
|
);
|
|
|
|
Ok(persisted)
|
|
}
|
|
|
|
pub(crate) fn make_puzzle_ui_spritesheet_image_transparent(
|
|
image: PuzzleDownloadedImage,
|
|
) -> Result<PuzzleDownloadedImage, AppError> {
|
|
let source = image::load_from_memory(image.bytes.as_slice()).map_err(|error| {
|
|
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
|
"provider": VECTOR_ENGINE_PROVIDER,
|
|
"message": format!("拼图 UI spritesheet 图解码失败:{error}"),
|
|
}))
|
|
})?;
|
|
|
|
let mut encoded = std::io::Cursor::new(Vec::new());
|
|
apply_generated_asset_sheet_green_screen_alpha(source)
|
|
.write_to(&mut encoded, ImageFormat::Png)
|
|
.map_err(|error| {
|
|
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
|
"provider": VECTOR_ENGINE_PROVIDER,
|
|
"message": format!("拼图 UI spritesheet 图透明化失败:{error}"),
|
|
}))
|
|
})?;
|
|
|
|
Ok(PuzzleDownloadedImage {
|
|
extension: "png".to_string(),
|
|
mime_type: "image/png".to_string(),
|
|
bytes: encoded.into_inner(),
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn make_puzzle_ui_spritesheet_image_transparent_for_test(
|
|
image: PuzzleDownloadedImage,
|
|
) -> Result<PuzzleDownloadedImage, AppError> {
|
|
make_puzzle_ui_spritesheet_image_transparent(image)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn build_puzzle_ui_background_request_prompt_for_test(
|
|
level_name: &str,
|
|
prompt: &str,
|
|
) -> String {
|
|
build_puzzle_ui_background_generation_prompt(level_name, prompt)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn build_puzzle_level_scene_image_request_body_for_test(
|
|
reference_image: &PuzzleDownloadedImage,
|
|
) -> Result<Value, AppError> {
|
|
Ok(build_puzzle_vector_engine_image_request_body(
|
|
PuzzleImageModel::GptImage2,
|
|
PUZZLE_LEVEL_SCENE_IMAGE_PROMPT,
|
|
"",
|
|
PUZZLE_VECTOR_ENGINE_PORTRAIT_IMAGE_SIZE,
|
|
1,
|
|
Some(&build_puzzle_downloaded_image_reference(reference_image)),
|
|
))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn build_puzzle_ui_spritesheet_request_body_for_test(
|
|
reference_image: &PuzzleDownloadedImage,
|
|
) -> Result<Value, AppError> {
|
|
Ok(build_puzzle_vector_engine_image_request_body(
|
|
PuzzleImageModel::GptImage2,
|
|
PUZZLE_UI_SPRITESHEET_IMAGE_PROMPT,
|
|
"",
|
|
PUZZLE_VECTOR_ENGINE_SQUARE_IMAGE_SIZE,
|
|
1,
|
|
Some(&build_puzzle_downloaded_image_reference(reference_image)),
|
|
))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn build_puzzle_level_background_request_body_for_test(
|
|
reference_image: &PuzzleDownloadedImage,
|
|
) -> Result<Value, AppError> {
|
|
Ok(build_puzzle_vector_engine_image_request_body(
|
|
PuzzleImageModel::GptImage2,
|
|
PUZZLE_LEVEL_BACKGROUND_IMAGE_PROMPT,
|
|
"",
|
|
PUZZLE_VECTOR_ENGINE_PORTRAIT_IMAGE_SIZE,
|
|
1,
|
|
Some(&build_puzzle_downloaded_image_reference(reference_image)),
|
|
))
|
|
}
|