生成带参考图统一按编辑 concrete model 执行与计价
- 新增 editor_image_generation_concrete_model 作为生成类任务选择 concrete model 的唯一入口,dispatch 与计价共用同一判据 - image_generation_mud_points 增加 has_reference_images,命中编辑 concrete model 时改走 image_edit_model_mud_points - editor_project/editor_project_icon 的 provider_model 改用共享解析函数,替换三处重复的带参考图 if 表达式 - character_visual_assets 的角色主形象生成改用共享解析函数 - 图标素材图集、UI 素材提取、图标规范带 referenceId、Agent 生成类工具带 referenceImageIds 的计价同步切到编辑档 - EditImageTool 计价从生成档改为编辑档,与直连快速编辑、raw edit 对齐 - normalize_editor_generation_options 的具体 provider key 改用常量替换字符串字面量 - 补充参考图档位路由与 Agent 工具档位路由单测
This commit is contained in:
@@ -34,11 +34,12 @@ use crate::{
|
||||
build_character_visual_negative_prompt, build_character_visual_prompt,
|
||||
build_fallback_moderation_safe_character_visual_prompt,
|
||||
},
|
||||
editor_generation_config::editor_image_generation_concrete_model,
|
||||
http_error::AppError,
|
||||
openai_image_generation::{
|
||||
DownloadedOpenAiImage, GPT_IMAGE_2_5_BUSINESS_NAME, GPT_IMAGE_2_5_EDIT_MODEL,
|
||||
GPT_IMAGE_2_5_GENERATION_MODEL, OpenAiImageSettings, build_openai_image_http_client,
|
||||
create_openai_image_generation_with_model, require_openai_image_settings,
|
||||
DownloadedOpenAiImage, GPT_IMAGE_2_5_BUSINESS_NAME, OpenAiImageSettings,
|
||||
build_openai_image_http_client, create_openai_image_generation_with_model,
|
||||
require_openai_image_settings,
|
||||
},
|
||||
platform_errors::map_oss_error,
|
||||
request_context::RequestContext,
|
||||
@@ -940,18 +941,15 @@ async fn create_character_visual_generation(
|
||||
async fn create_character_visual_generation_once(
|
||||
http_client: &reqwest::Client,
|
||||
settings: &OpenAiImageSettings,
|
||||
_model: &str,
|
||||
model: &str,
|
||||
prompt: &str,
|
||||
size: &str,
|
||||
candidate_count: u32,
|
||||
reference_images: &[String],
|
||||
) -> Result<GeneratedCharacterVisuals, AppError> {
|
||||
// 中文注释:参考图存在时后端会走编辑端点,因此必须提交编辑模型而不是生成模型。
|
||||
let provider_model = if reference_images.is_empty() {
|
||||
GPT_IMAGE_2_5_GENERATION_MODEL
|
||||
} else {
|
||||
GPT_IMAGE_2_5_EDIT_MODEL
|
||||
};
|
||||
// 中文注释:参考图存在时后端会走编辑端点,因此必须提交编辑 concrete model 而不是生成模型。
|
||||
let provider_model =
|
||||
editor_image_generation_concrete_model(Some(model), !reference_images.is_empty());
|
||||
let generated = create_openai_image_generation_with_model(
|
||||
http_client,
|
||||
settings,
|
||||
|
||||
@@ -347,8 +347,9 @@ fn editor_agent_image_mud_points(
|
||||
kind: Option<&str>,
|
||||
model: &str,
|
||||
image_size: Option<&str>,
|
||||
has_reference_images: bool,
|
||||
) -> u32 {
|
||||
pricing.image_generation_mud_points(kind, Some(model), image_size)
|
||||
pricing.image_generation_mud_points(kind, Some(model), image_size, has_reference_images)
|
||||
}
|
||||
|
||||
impl EditorAgentTool for GenerateImageTool {
|
||||
@@ -370,6 +371,7 @@ impl EditorAgentTool for GenerateImageTool {
|
||||
None,
|
||||
args.model.as_str(),
|
||||
Some(args.image_size.as_str()),
|
||||
!args.reference_image_ids.is_empty(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -479,6 +481,7 @@ impl EditorAgentTool for GenerateCharacterTool {
|
||||
Some("character"),
|
||||
args.model.as_str(),
|
||||
Some(args.image_size.as_str()),
|
||||
!args.reference_image_ids.is_empty(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -591,6 +594,7 @@ impl EditorAgentTool for GenerateUiDesignTool {
|
||||
Some("ui-design"),
|
||||
args.model.as_str(),
|
||||
Some(args.image_size.as_str()),
|
||||
!args.reference_image_ids.is_empty(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -697,13 +701,10 @@ impl EditorAgentTool for EditImageTool {
|
||||
pricing: &EditorGenerationPricingConfig,
|
||||
args: &Value,
|
||||
) -> Result<u32, EditorAgentToolError> {
|
||||
let _: EditImageToolArgs = parse_internal_image_args("edit image args", args)?;
|
||||
Ok(editor_agent_image_mud_points(
|
||||
pricing,
|
||||
Some("quick-edit"),
|
||||
GPT_IMAGE_2_5_BUSINESS_NAME,
|
||||
Some("1K"),
|
||||
))
|
||||
let args: EditImageToolArgs = parse_internal_image_args("edit image args", args)?;
|
||||
// 中文注释:修改图片按编辑 concrete model 执行,计价必须走编辑档,
|
||||
// 不能沿用生成档(admin 分开调价时生成档价格即错价)。
|
||||
Ok(pricing.image_edit_model_mud_points(Some(args.model.as_str()), Some("1K")))
|
||||
}
|
||||
|
||||
fn build_display_args(
|
||||
@@ -851,6 +852,7 @@ impl EditorAgentTool for GenerateIconSpritesheetTool {
|
||||
Some("icon"),
|
||||
args.model.as_str(),
|
||||
Some(args.image_size.as_str()),
|
||||
!args.reference_image_ids.is_empty(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1298,7 +1300,8 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::editor_generation_config::{
|
||||
EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_GENERATION, load_editor_generation_pricing_from_paths,
|
||||
EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_EDIT, EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_GENERATION,
|
||||
load_editor_generation_pricing_from_paths,
|
||||
};
|
||||
|
||||
fn pricing() -> EditorGenerationPricingConfig {
|
||||
@@ -1626,6 +1629,61 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_pricing_routes_edit_tools_and_referenced_generation_to_the_edit_tier() {
|
||||
let mut pricing = pricing();
|
||||
pricing
|
||||
.models
|
||||
.get_mut(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_EDIT)
|
||||
.expect("edit tier pricing should exist")
|
||||
.prices
|
||||
.insert("1K".to_string(), 41);
|
||||
pricing
|
||||
.models
|
||||
.get_mut(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_GENERATION)
|
||||
.expect("generation tier pricing should exist")
|
||||
.prices
|
||||
.insert("1K".to_string(), 7);
|
||||
|
||||
let context = context_with_image("image-1");
|
||||
let edit =
|
||||
editor_agent_tool(EditImageTool::NAME, &context).expect("edit tool should resolve");
|
||||
let edit_args = edit
|
||||
.validate_args(&json!({ "object_image_id": "image-1", "prompt": "修改" }))
|
||||
.expect("edit args should validate");
|
||||
assert_eq!(edit.pricing(&pricing, &edit_args).expect("pricing"), 41);
|
||||
|
||||
let generate = editor_agent_tool(GenerateImageTool::NAME, &context)
|
||||
.expect("image tool should resolve");
|
||||
let without_reference = generate
|
||||
.validate_args(&json!({
|
||||
"prompt": "生成图片",
|
||||
"model": GPT_IMAGE_2_5_BUSINESS_NAME,
|
||||
"image_size": "1K"
|
||||
}))
|
||||
.expect("generation args should validate");
|
||||
assert_eq!(
|
||||
generate
|
||||
.pricing(&pricing, &without_reference)
|
||||
.expect("pricing"),
|
||||
7
|
||||
);
|
||||
let with_reference = generate
|
||||
.validate_args(&json!({
|
||||
"prompt": "生成图片",
|
||||
"model": GPT_IMAGE_2_5_BUSINESS_NAME,
|
||||
"image_size": "1K",
|
||||
"reference_image_ids": ["image-1"]
|
||||
}))
|
||||
.expect("referenced generation args should validate");
|
||||
assert_eq!(
|
||||
generate
|
||||
.pricing(&pricing, &with_reference)
|
||||
.expect("pricing"),
|
||||
41
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_pricing_uses_the_supplied_runtime_snapshot() {
|
||||
let mut pricing = pricing();
|
||||
|
||||
@@ -180,10 +180,18 @@ impl EditorGenerationPricingConfig {
|
||||
kind: Option<&str>,
|
||||
model: Option<&str>,
|
||||
image_size: Option<&str>,
|
||||
has_reference_images: bool,
|
||||
) -> u32 {
|
||||
if let Some(price_mud_points) = current_external_generation_billing_price_mud_points() {
|
||||
return price_mud_points;
|
||||
}
|
||||
// 中文注释:生成任务带参考图时执行端改走编辑 concrete model,计价必须同步切到编辑档,
|
||||
// 否则会出现「生成档扣费 + 编辑档执行/审计」的分裂。
|
||||
if editor_image_generation_concrete_model(model, has_reference_images)
|
||||
== EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_EDIT
|
||||
{
|
||||
return self.image_edit_model_mud_points(model, image_size);
|
||||
}
|
||||
match kind.map(str::trim) {
|
||||
Some("spec") => self.spec_model_mud_points(model),
|
||||
Some("character" | "icon" | "ui-design" | "publication-material" | "scene") => {
|
||||
@@ -490,8 +498,14 @@ pub(crate) fn editor_image_generation_mud_points(
|
||||
kind: Option<&str>,
|
||||
model: Option<&str>,
|
||||
image_size: Option<&str>,
|
||||
has_reference_images: bool,
|
||||
) -> u32 {
|
||||
default_runtime_pricing().image_generation_mud_points(kind, model, image_size)
|
||||
default_runtime_pricing().image_generation_mud_points(
|
||||
kind,
|
||||
model,
|
||||
image_size,
|
||||
has_reference_images,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -558,6 +572,27 @@ fn normalize_editor_image_edit_model(model: Option<&str>) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// 生成类图片任务最终发送给 provider 的 concrete model。
|
||||
///
|
||||
/// 中文注释:GPT Image 2.5 生成任务只要带参考图,执行端就必须改走 edits multipart,
|
||||
/// 因此执行、审计与计价统一使用编辑 concrete model `gpt-image-2.5-sunburst-c`;
|
||||
/// nanobanana 的生成与编辑共用同一个 concrete model,不受参考图影响。
|
||||
/// 这里是生成类任务选择 concrete model 的唯一入口,dispatch 与计价都不得再各写一份判断。
|
||||
pub(crate) fn editor_image_generation_concrete_model(
|
||||
model: Option<&str>,
|
||||
has_reference_images: bool,
|
||||
) -> &'static str {
|
||||
match model.map(str::trim).filter(|value| !value.is_empty()) {
|
||||
Some(
|
||||
EDITOR_IMAGE_MODEL_NANOBANANA2
|
||||
| EDITOR_IMAGE_MODEL_NANOBANANA2_DISPLAY_ALIAS
|
||||
| EDITOR_IMAGE_MODEL_NANOBANANA_LEGACY_ALIAS,
|
||||
) => EDITOR_IMAGE_MODEL_NANOBANANA2,
|
||||
_ if has_reference_images => EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_EDIT,
|
||||
_ => EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_GENERATION,
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_editor_generation_image_price_size(
|
||||
model: &str,
|
||||
image_size: Option<&str>,
|
||||
@@ -808,7 +843,8 @@ mod tests {
|
||||
config.image_generation_mud_points(
|
||||
Some("character"),
|
||||
Some("gpt-image-2"),
|
||||
Some("2K")
|
||||
Some("2K"),
|
||||
false
|
||||
),
|
||||
queued_price
|
||||
);
|
||||
@@ -955,7 +991,8 @@ mod tests {
|
||||
editor_image_generation_mud_points(
|
||||
Some("image"),
|
||||
Some("gemini-3.1-flash-image-preview"),
|
||||
Some("0.5K")
|
||||
Some("0.5K"),
|
||||
false
|
||||
),
|
||||
8
|
||||
);
|
||||
@@ -963,7 +1000,8 @@ mod tests {
|
||||
editor_image_generation_mud_points(
|
||||
Some("character"),
|
||||
Some("gemini-3.1-flash-image-preview"),
|
||||
Some("1K")
|
||||
Some("1K"),
|
||||
false
|
||||
),
|
||||
12
|
||||
);
|
||||
@@ -971,24 +1009,124 @@ mod tests {
|
||||
editor_image_generation_mud_points(
|
||||
Some("scene"),
|
||||
Some("gemini-3.1-flash-image-preview"),
|
||||
Some("1K")
|
||||
Some("1K"),
|
||||
false
|
||||
),
|
||||
12
|
||||
);
|
||||
assert_eq!(
|
||||
editor_image_generation_mud_points(Some("ui-design"), Some("gpt-image-2"), Some("1K")),
|
||||
editor_image_generation_mud_points(
|
||||
Some("ui-design"),
|
||||
Some("gpt-image-2"),
|
||||
Some("1K"),
|
||||
false
|
||||
),
|
||||
3
|
||||
);
|
||||
assert_eq!(
|
||||
editor_image_generation_mud_points(Some("ui-design"), Some("gpt-image-2"), Some("2K")),
|
||||
editor_image_generation_mud_points(
|
||||
Some("ui-design"),
|
||||
Some("gpt-image-2"),
|
||||
Some("2K"),
|
||||
false
|
||||
),
|
||||
5
|
||||
);
|
||||
assert_eq!(
|
||||
editor_image_generation_mud_points(Some("spec"), Some("gpt-image-2"), Some("2K")),
|
||||
editor_image_generation_mud_points(
|
||||
Some("spec"),
|
||||
Some("gpt-image-2"),
|
||||
Some("2K"),
|
||||
false
|
||||
),
|
||||
5
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_image_generation_with_references_prices_at_the_edit_tier() {
|
||||
let mut config = default_runtime_pricing();
|
||||
for (model, price_1k, price_2k) in [
|
||||
(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_GENERATION, 7, 9),
|
||||
(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_EDIT, 41, 43),
|
||||
] {
|
||||
let prices = &mut config
|
||||
.models
|
||||
.get_mut(model)
|
||||
.expect("gpt image pricing should exist")
|
||||
.prices;
|
||||
prices.insert("1K".to_string(), price_1k);
|
||||
prices.insert("2K".to_string(), price_2k);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
editor_image_generation_concrete_model(
|
||||
Some(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_BUSINESS),
|
||||
false
|
||||
),
|
||||
EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_GENERATION
|
||||
);
|
||||
assert_eq!(
|
||||
editor_image_generation_concrete_model(
|
||||
Some(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_BUSINESS),
|
||||
true
|
||||
),
|
||||
EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_EDIT
|
||||
);
|
||||
assert_eq!(
|
||||
editor_image_generation_concrete_model(Some(EDITOR_IMAGE_MODEL_NANOBANANA2), true),
|
||||
EDITOR_IMAGE_MODEL_NANOBANANA2
|
||||
);
|
||||
|
||||
// GPT Image 2.5 生成带参考图按编辑 concrete model 执行,计价必须同步切到编辑档。
|
||||
assert_eq!(
|
||||
config.image_generation_mud_points(
|
||||
Some("character"),
|
||||
Some(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_BUSINESS),
|
||||
Some("1K"),
|
||||
true
|
||||
),
|
||||
41
|
||||
);
|
||||
assert_eq!(
|
||||
config.image_generation_mud_points(
|
||||
Some("character"),
|
||||
Some(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_BUSINESS),
|
||||
Some("1K"),
|
||||
false
|
||||
),
|
||||
7
|
||||
);
|
||||
assert_eq!(
|
||||
config.image_generation_mud_points(
|
||||
Some("spec"),
|
||||
Some(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_BUSINESS),
|
||||
Some("2K"),
|
||||
true
|
||||
),
|
||||
43
|
||||
);
|
||||
assert_eq!(
|
||||
config.image_generation_mud_points(
|
||||
Some("spec"),
|
||||
Some(EDITOR_IMAGE_MODEL_GPT_IMAGE_2_5_BUSINESS),
|
||||
Some("2K"),
|
||||
false
|
||||
),
|
||||
9
|
||||
);
|
||||
// nanobanana 生成与编辑共用一个 concrete model,参考图不改变计价档位。
|
||||
assert_eq!(
|
||||
config.image_generation_mud_points(
|
||||
None,
|
||||
Some(EDITOR_IMAGE_MODEL_NANOBANANA2),
|
||||
Some("1K"),
|
||||
true
|
||||
),
|
||||
12
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_video_and_character_animation_share_model_resolution_rates() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -77,6 +77,7 @@ use crate::{
|
||||
with_editor_generation_durable_billing_boundary,
|
||||
},
|
||||
auth::{AuthenticatedAccessToken, optional_access_token_from_headers},
|
||||
editor_generation_config::editor_image_generation_concrete_model,
|
||||
editor_generation_queue::{
|
||||
EDITOR_BACKGROUND_REMOVAL_JOB_KIND, EDITOR_ICON_SPRITESHEET_GENERATION_JOB_KIND,
|
||||
EDITOR_IMAGE_EDIT_JOB_KIND, EDITOR_IMAGE_GENERATION_JOB_KIND,
|
||||
@@ -2920,6 +2921,7 @@ pub(crate) async fn enqueue_editor_image_generation_for_owner(
|
||||
normalized_kind,
|
||||
Some(generation_options.model),
|
||||
Some(generation_options.image_size),
|
||||
editor_generation_has_reference_images(payload.reference_image_srcs.as_deref()),
|
||||
),
|
||||
);
|
||||
let source_entity_id = editor_generation_source_entity_id(
|
||||
@@ -3011,6 +3013,7 @@ pub(crate) async fn validate_editor_image_generation_parameters_for_owner(
|
||||
normalized_kind,
|
||||
Some(generation_options.model),
|
||||
Some(generation_options.image_size),
|
||||
editor_generation_has_reference_images(payload.reference_image_srcs.as_deref()),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -3222,6 +3225,7 @@ where
|
||||
normalized_kind,
|
||||
Some(generation_options.model),
|
||||
Some(generation_options.image_size),
|
||||
!reference_images.is_empty(),
|
||||
)
|
||||
};
|
||||
let prompt_generation_inputs = payload.generation_inputs.take();
|
||||
@@ -3291,13 +3295,10 @@ where
|
||||
&settings,
|
||||
EditorImageProviderRequest {
|
||||
model: generation_options.model,
|
||||
provider_model: if generation_options.model == EDITOR_IMAGE_MODEL_NANOBANANA2 {
|
||||
generation_options.model
|
||||
} else if reference_images.is_empty() {
|
||||
GPT_IMAGE_2_5_GENERATION_MODEL
|
||||
} else {
|
||||
GPT_IMAGE_2_5_EDIT_MODEL
|
||||
},
|
||||
provider_model: editor_image_generation_concrete_model(
|
||||
Some(generation_options.model),
|
||||
!reference_images.is_empty(),
|
||||
),
|
||||
prompt: submitted_prompt.as_str(),
|
||||
negative_prompt,
|
||||
size: provider_request_size.as_ref(),
|
||||
@@ -4098,8 +4099,8 @@ pub(crate) fn normalize_editor_generation_options(
|
||||
Some(
|
||||
GPT_IMAGE_2_MODEL
|
||||
| GPT_IMAGE_2_5_BUSINESS_NAME
|
||||
| "gpt-image-2.5-flare-c"
|
||||
| "gpt-image-2.5-sunburst-c",
|
||||
| GPT_IMAGE_2_5_GENERATION_MODEL
|
||||
| GPT_IMAGE_2_5_EDIT_MODEL,
|
||||
) => GPT_IMAGE_2_5_BUSINESS_NAME,
|
||||
Some(
|
||||
EDITOR_IMAGE_MODEL_NANOBANANA2
|
||||
@@ -12082,7 +12083,8 @@ async fn resolve_editor_ui_design_asset_extraction_price(
|
||||
"message": error.to_string(),
|
||||
}))
|
||||
})?
|
||||
.image_generation_mud_points(Some("icon"), model, image_size);
|
||||
// 中文注释:UI 素材提取始终以 UI 设计图作为来源图,GPT Image 2.5 固定走编辑 concrete model。
|
||||
.image_generation_mud_points(Some("icon"), model, image_size, true);
|
||||
Ok(expected_price_mud_points)
|
||||
}
|
||||
|
||||
@@ -13278,6 +13280,14 @@ pub(crate) fn normalize_editor_reference_image_sources(sources: Option<&[String]
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 生成类任务是否带参考图。
|
||||
///
|
||||
/// 中文注释:dispatch、审计与计价必须共用同一个判据,否则又会出现
|
||||
/// 「生成档计价 + 编辑档执行」的分裂。
|
||||
pub(crate) fn editor_generation_has_reference_images(sources: Option<&[String]>) -> bool {
|
||||
!normalize_editor_reference_image_sources(sources).is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_editor_reference_image_source_limit(
|
||||
sources: Option<&[String]>,
|
||||
limit: usize,
|
||||
@@ -20058,6 +20068,7 @@ mod tests {
|
||||
Some("icon"),
|
||||
None,
|
||||
Some("1K"),
|
||||
true,
|
||||
),
|
||||
spritesheet_resource: None,
|
||||
spritesheet_asset: None,
|
||||
|
||||
@@ -42,6 +42,7 @@ use crate::{
|
||||
execute_billable_asset_operation_with_cost, with_editor_generation_durable_billing_boundary,
|
||||
},
|
||||
auth::AuthenticatedAccessToken,
|
||||
editor_generation_config::editor_image_generation_concrete_model,
|
||||
editor_generation_queue::{
|
||||
EDITOR_ICON_SPEC_GENERATION_JOB_KIND, EDITOR_ICON_SPRITESHEET_GENERATION_JOB_KIND,
|
||||
EditorGenerationQueuedResponse, editor_generation_queue_state,
|
||||
@@ -246,7 +247,8 @@ pub(crate) async fn resolve_editor_icon_spritesheet_price(
|
||||
"message": error.to_string(),
|
||||
}))
|
||||
})?
|
||||
.image_generation_mud_points(Some("icon"), model, image_size))
|
||||
// 中文注释:图标素材图集始终以图标规范图为参考图,GPT Image 2.5 固定走编辑 concrete model。
|
||||
.image_generation_mud_points(Some("icon"), model, image_size, true))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
@@ -660,7 +662,7 @@ pub(crate) async fn enqueue_icon_spec_generation_for_owner(
|
||||
payload.project_id = target.project_id;
|
||||
payload.asset_folder_id = target.asset_folder_id;
|
||||
validate_icon_spec_reference_for_owner(state, caller, &payload).await?;
|
||||
let price_mud_points = resolve_icon_spec_price(state).await?;
|
||||
let price_mud_points = resolve_icon_spec_price(state, payload.reference_id.is_some()).await?;
|
||||
enqueue_editor_generation_job_for_caller(
|
||||
state,
|
||||
request_context,
|
||||
@@ -740,7 +742,10 @@ async fn validate_icon_spec_reference_for_owner(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn resolve_icon_spec_price(state: &AppState) -> Result<u32, AppError> {
|
||||
async fn resolve_icon_spec_price(
|
||||
state: &AppState,
|
||||
has_reference_images: bool,
|
||||
) -> Result<u32, AppError> {
|
||||
Ok(state
|
||||
.editor_generation_pricing()
|
||||
.await
|
||||
@@ -753,6 +758,7 @@ async fn resolve_icon_spec_price(state: &AppState) -> Result<u32, AppError> {
|
||||
Some("spec"),
|
||||
Some(ICON_SPEC_MODEL),
|
||||
Some(ICON_SPEC_IMAGE_SIZE),
|
||||
has_reference_images,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1774,15 +1780,10 @@ pub(crate) async fn generate_editor_icon_spritesheet_for_owner(
|
||||
&settings,
|
||||
EditorImageProviderRequest {
|
||||
model: generation_options.model,
|
||||
provider_model: if generation_options.model
|
||||
== EDITOR_IMAGE_MODEL_NANOBANANA2
|
||||
{
|
||||
generation_options.model
|
||||
} else if reference_images.is_empty() {
|
||||
GPT_IMAGE_2_5_GENERATION_MODEL
|
||||
} else {
|
||||
GPT_IMAGE_2_5_EDIT_MODEL
|
||||
},
|
||||
provider_model: editor_image_generation_concrete_model(
|
||||
Some(generation_options.model),
|
||||
!reference_images.is_empty(),
|
||||
),
|
||||
prompt: prompt.as_str(),
|
||||
negative_prompt: None,
|
||||
size,
|
||||
|
||||
Reference in New Issue
Block a user