收敛UI编辑器结构化LLM动作适配器
统一必需 tool-call 定位与有界 JSON 参数解析 让语义建议、结构识别、组件绑定和多树合并复用稳定机械 保留各操作的 prompt、schema、领域校验与 materializer
This commit is contained in:
@@ -2,7 +2,7 @@ use crate::config::build_game_creator_llm_client_from_llm_config;
|
||||
use crate::config::load_game_creator_app_config;
|
||||
use crate::ui_editor::commands::utils::{
|
||||
parse_limited_llm_tool_arguments, read_ui_reference_image_data_url, request_ui_editor_llm,
|
||||
strict_json_schema,
|
||||
required_tool_call_arguments, strict_json_schema,
|
||||
};
|
||||
use crate::ui_editor::component::text::FontSource;
|
||||
use crate::ui_editor::component::{Component, NodeComponent};
|
||||
@@ -406,12 +406,8 @@ pub(crate) async fn bind_components_impl_with_provider(
|
||||
.await
|
||||
}
|
||||
.map_err(|error| format!("组件绑定失败:{error}"))?;
|
||||
let call = response
|
||||
.tool_calls
|
||||
.iter()
|
||||
.find(|call| call.name == "bind_ui_components")
|
||||
.ok_or_else(|| "LLM 未返回 bind_ui_components 工具调用".to_string())?;
|
||||
let parsed = parse_binding_response(&call.arguments, editable_nodes.len())?;
|
||||
let arguments = required_tool_call_arguments(&response, "bind_ui_components")?;
|
||||
let parsed = parse_binding_response(arguments, editable_nodes.len())?;
|
||||
let known_sprite_ids = state.sprite_assets.keys().cloned().collect::<HashSet<_>>();
|
||||
let known_font_ids = state.font_assets.keys().cloned().collect::<HashSet<_>>();
|
||||
let result = validate_and_materialize(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::config::build_game_creator_llm_client_from_llm_config;
|
||||
use crate::config::load_game_creator_app_config;
|
||||
use crate::ui_editor::commands::utils::{
|
||||
parse_limited_llm_tool_arguments, request_ui_editor_llm, strict_json_schema,
|
||||
request_ui_editor_llm, required_tool_arguments, strict_json_schema,
|
||||
};
|
||||
use crate::ui_editor::state::{State, UITree};
|
||||
use platform_llm::{LlmFunctionTool, LlmMessage, LlmRunRequest, LlmToolChoice};
|
||||
@@ -487,15 +487,7 @@ pub(crate) async fn merge_ui_impl_with_provider(
|
||||
app_log!("ui_merge.error stage=llm_request error={error}");
|
||||
format!("UI 树合并失败:{error}")
|
||||
})?;
|
||||
let call = response
|
||||
.tool_calls
|
||||
.iter()
|
||||
.find(|call| call.name == MERGE_TOOL_NAME)
|
||||
.ok_or_else(|| {
|
||||
app_log!("ui_merge.error stage=parse_tool_call reason=missing_tool_call");
|
||||
format!("LLM 未返回 {MERGE_TOOL_NAME} 工具调用")
|
||||
})?;
|
||||
let arguments = parse_limited_llm_tool_arguments(&call.arguments).map_err(|error| {
|
||||
let arguments = required_tool_arguments(&response, MERGE_TOOL_NAME).map_err(|error| {
|
||||
app_log!("ui_merge.error stage=parse_arguments error={error}");
|
||||
format!("UI 合并工具参数无效:{error}")
|
||||
})?;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::config::build_game_creator_llm_client_from_llm_config;
|
||||
use crate::config::load_game_creator_app_config;
|
||||
use crate::ui_editor::commands::utils::{
|
||||
parse_limited_llm_tool_arguments, read_ui_reference_image_data_url, request_ui_editor_llm,
|
||||
read_ui_reference_image_data_url, request_ui_editor_llm, required_tool_arguments,
|
||||
strict_json_schema,
|
||||
};
|
||||
use crate::ui_editor::component::{Component, NodeComponent};
|
||||
@@ -13,7 +13,7 @@ use crate::ui_editor::layout::offset::NodeOffset;
|
||||
use crate::ui_editor::layout::transform::Transform;
|
||||
use crate::ui_editor::resource::ui_design_image::UIDesignImage;
|
||||
use crate::ui_editor::state::{State, UITree};
|
||||
use crate::ui_editor::utils::{random_node_id, NodeId, UIDesignImageId};
|
||||
use crate::ui_editor::utils::{random_node_id, UIDesignImageId};
|
||||
use nalgebra::{Point2, Vector2};
|
||||
use platform_llm::{
|
||||
LlmFunctionTool, LlmMessage, LlmMessageContentPart, LlmRunRequest, LlmToolChoice,
|
||||
@@ -770,27 +770,14 @@ pub(crate) async fn recognize_ui_impl_with_provider(
|
||||
!response.text.trim().is_empty(),
|
||||
response.tool_calls.len()
|
||||
);
|
||||
let call = response
|
||||
.tool_calls
|
||||
.iter()
|
||||
.find(|call| call.name == "recognize_ui_structure")
|
||||
.ok_or_else(|| {
|
||||
let arguments =
|
||||
required_tool_arguments(&response, "recognize_ui_structure").map_err(|error| {
|
||||
app_log!(
|
||||
"ui_recognition.error stage=parse_tool_call root={} reason=missing_tool_call",
|
||||
"ui_recognition.error stage=parse_arguments root={} error={error}",
|
||||
root_id.as_str()
|
||||
);
|
||||
format!(
|
||||
"根界面图 {} 的 LLM 未返回 recognize_ui_structure 工具调用",
|
||||
root_id.as_str()
|
||||
)
|
||||
format!("根界面图 {} 的识别工具参数无效:{error}", root_id.as_str())
|
||||
})?;
|
||||
let arguments = parse_limited_llm_tool_arguments(&call.arguments).map_err(|error| {
|
||||
app_log!(
|
||||
"ui_recognition.error stage=parse_arguments root={} error={error}",
|
||||
root_id.as_str()
|
||||
);
|
||||
format!("根界面图 {} 的识别工具参数无效:{error}", root_id.as_str())
|
||||
})?;
|
||||
validate_recognition_response_shape(&arguments).map_err(|error| {
|
||||
app_log!(
|
||||
"ui_recognition.error stage=validate_arguments root={} error={error}",
|
||||
|
||||
+5
-12
@@ -1,7 +1,7 @@
|
||||
use crate::config::build_game_creator_llm_client_from_llm_config;
|
||||
use crate::config::load_game_creator_app_config;
|
||||
use crate::ui_editor::commands::utils::{
|
||||
parse_limited_llm_tool_arguments, read_ui_reference_image_data_url, request_ui_editor_llm,
|
||||
read_ui_reference_image_data_url, request_ui_editor_llm, required_tool_arguments,
|
||||
strict_json_schema,
|
||||
};
|
||||
use crate::ui_editor::resource::ui_design_image::UIDesignImageRole;
|
||||
@@ -214,18 +214,11 @@ pub(crate) async fn suggest_ui_design_semantic_impl(
|
||||
!response.text.trim().is_empty(),
|
||||
response.tool_calls.len()
|
||||
);
|
||||
let call = response
|
||||
.tool_calls
|
||||
.iter()
|
||||
.find(|call| call.name == "suggest_ui_design_semantics")
|
||||
.ok_or_else(|| {
|
||||
app_log!("ui_design_suggestion.error stage=parse_tool_call reason=missing_tool_call");
|
||||
"LLM 响应无效(详情:未返回 suggest_ui_design_semantics 工具调用)".to_string()
|
||||
let arguments =
|
||||
required_tool_arguments(&response, "suggest_ui_design_semantics").map_err(|error| {
|
||||
app_log!("ui_design_suggestion.error stage=parse_arguments error={error}");
|
||||
format!("LLM 响应无效(详情:UI 语义建议工具参数无效:{error})")
|
||||
})?;
|
||||
let arguments = parse_limited_llm_tool_arguments(&call.arguments).map_err(|error| {
|
||||
app_log!("ui_design_suggestion.error stage=parse_arguments error={error}");
|
||||
format!("LLM 响应无效(详情:UI 语义建议工具参数无效:{error})")
|
||||
})?;
|
||||
validate_suggestion_response_shape(&arguments).map_err(|error| {
|
||||
app_log!("ui_design_suggestion.error stage=validate_arguments error={error}");
|
||||
format!("LLM 响应无效(详情:{error})")
|
||||
|
||||
@@ -95,6 +95,29 @@ pub(crate) fn parse_limited_llm_tool_arguments(
|
||||
serde_json::from_str(arguments).map_err(|error| format!("LLM 工具参数不是有效 JSON:{error}"))
|
||||
}
|
||||
|
||||
/// Stable structured-action adapter: locate the required tool call and parse its
|
||||
/// bounded JSON arguments. Prompt, schema and materialization stay in each
|
||||
/// operation-specific command.
|
||||
pub(crate) fn required_tool_arguments(
|
||||
response: &LlmRunResponse,
|
||||
tool_name: &str,
|
||||
) -> Result<serde_json::Value, String> {
|
||||
let arguments = required_tool_call_arguments(response, tool_name)?;
|
||||
parse_limited_llm_tool_arguments(arguments)
|
||||
}
|
||||
|
||||
pub(crate) fn required_tool_call_arguments<'a>(
|
||||
response: &'a LlmRunResponse,
|
||||
tool_name: &str,
|
||||
) -> Result<&'a str, String> {
|
||||
response
|
||||
.tool_calls
|
||||
.iter()
|
||||
.find(|call| call.name == tool_name)
|
||||
.map(|call| call.arguments.as_str())
|
||||
.ok_or_else(|| format!("LLM 未返回 {tool_name} 工具调用"))
|
||||
}
|
||||
|
||||
pub(crate) async fn read_ui_reference_image_data_url(path: PathBuf) -> Result<String, String> {
|
||||
tokio::task::spawn_blocking(move || read_ui_reference_image_data_url_blocking(&path))
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user