完善UI自动分离批次处理
复用资源ID安全路径并修正像素坐标 加入标记图、Raw编辑、视觉绑定修复与问题节点继续处理
This commit is contained in:
@@ -16,7 +16,6 @@ use platform_llm::{
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use ts_rs::TS;
|
||||
@@ -331,18 +330,10 @@ pub fn separation_sidecar_dir(root: &Path, asset_id: &str) -> Result<PathBuf, St
|
||||
if asset_id.trim().is_empty() || asset_id.trim() != asset_id {
|
||||
return Err("UI 资源 ID 无效".to_string());
|
||||
}
|
||||
let mut stem = String::new();
|
||||
for character in asset_id.chars() {
|
||||
if character.is_ascii_alphanumeric() || matches!(character, '_' | '-') {
|
||||
stem.push(character);
|
||||
} else {
|
||||
stem.push('_');
|
||||
}
|
||||
}
|
||||
let digest = format!("{:x}", Sha256::digest(asset_id.as_bytes()));
|
||||
let dir = root
|
||||
.join("ui")
|
||||
.join(format!(".{stem}-{}-separation", &digest[..16]));
|
||||
let dir = root.join("ui").join(format!(
|
||||
".{}-separation",
|
||||
crate::ui_editor::persistence::generated_file_stem(asset_id)
|
||||
));
|
||||
if !dir.starts_with(root) {
|
||||
return Err("separation sidecar 路径越界".to_string());
|
||||
}
|
||||
@@ -437,6 +428,28 @@ pub fn apply_batch_patch(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mark_batch_problematic(
|
||||
state: &mut SeparationState,
|
||||
tree_index: usize,
|
||||
batch: &[&SeparationNode],
|
||||
error: String,
|
||||
) {
|
||||
let ids = batch
|
||||
.iter()
|
||||
.map(|node| node.id.clone())
|
||||
.collect::<std::collections::HashSet<_>>();
|
||||
for node in batch {
|
||||
state.problematic_nodes.push(ProblematicNode {
|
||||
node_id: node.id.clone(),
|
||||
problem_description: error.clone(),
|
||||
rework_count: MAX_REWORK_COUNT,
|
||||
});
|
||||
}
|
||||
if let Some(tree) = state.unprocessed_trees.get_mut(tree_index) {
|
||||
remove_ids(&mut tree.root, &ids);
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_rework_count(node: &mut SeparationNode, id: &NodeId, count: u32) {
|
||||
if node.id == *id {
|
||||
node.rework_count = count;
|
||||
@@ -662,22 +675,41 @@ pub(crate) async fn separate_ui_impl(
|
||||
let Some(current_tree) = separation.unprocessed_trees.get(tree_index) else {
|
||||
break;
|
||||
};
|
||||
let batch = next_leaf_batch(current_tree);
|
||||
if batch.is_empty() {
|
||||
let batch_nodes = next_leaf_batch(current_tree)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if batch_nodes.is_empty() {
|
||||
break;
|
||||
}
|
||||
let batch = batch_nodes.iter().collect::<Vec<_>>();
|
||||
let prompt =
|
||||
gen_extract_prompt(&batch.iter().map(|n| n.note.clone()).collect::<Vec<_>>());
|
||||
let marker_path = sidecar.join(format!("marked-{}.png", separation.bound.len()));
|
||||
let marked_url = build_marked_image(&source_url, &batch, &marker_path)?;
|
||||
let processed_url = raw_image_edit(
|
||||
let marked_url = match build_marked_image(&source_url, &batch, &marker_path) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
mark_batch_problematic(&mut separation, tree_index, &batch, error);
|
||||
write_separation_state(&state_path, &separation)?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let processed_url = match raw_image_edit(
|
||||
&session,
|
||||
&marked_url,
|
||||
&prompt,
|
||||
image.pixel_size.x as u32,
|
||||
image.pixel_size.y as u32,
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
mark_batch_problematic(&mut separation, tree_index, &batch, error);
|
||||
write_separation_state(&state_path, &separation)?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let processed_path = sidecar.join(format!("processed-{}.png", separation.bound.len()));
|
||||
let processed_bytes = base64::engine::general_purpose::STANDARD
|
||||
.decode(
|
||||
@@ -688,7 +720,14 @@ pub(crate) async fn separate_ui_impl(
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
fs::write(&processed_path, processed_bytes).map_err(|e| e.to_string())?;
|
||||
let binding = visual_binding(source_url.clone(), processed_url, &batch).await?;
|
||||
let binding = match visual_binding(source_url.clone(), processed_url, &batch).await {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
mark_batch_problematic(&mut separation, tree_index, &batch, error);
|
||||
write_separation_state(&state_path, &separation)?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut cut_paths = std::collections::HashMap::new();
|
||||
for decision in &binding.decisions {
|
||||
if let BindingDecision::Ok {
|
||||
|
||||
Reference in New Issue
Block a user