统一 VectorEngine LLM 客户端命名
将 editor_agent_llm_client 重命名为 vector_engine_llm_client 更新图标生成、角色动画、美术 Agent 与应用状态调用 同步排障文档中的客户端名称
This commit is contained in:
@@ -3833,7 +3833,7 @@
|
||||
## 画布 Agent 的规划请求不能关闭瞬时失败重试
|
||||
|
||||
- 现象:美术 Agent 对话返回红色错误气泡 `completion error: LLM 请求超时,累计尝试 1 次`;HTTP 本身仍返回 200,前端 20 分钟 transport timeout 没有触发。
|
||||
- 原因:规划请求虽然有 Agent 专用单次 timeout,但 `editor_agent_llm_client` 把 `max_retries` 硬编码为 0;VectorEngine `gpt-5.4-mini` 的偶发长尾、连接超时或可重试上游状态会在第一次失败后直接持久化成 system error。framework 的英文 `completion error` 前缀也被原样暴露给用户。
|
||||
- 原因:规划请求虽然有 Agent 专用单次 timeout,但 `vector_engine_llm_client` 把 `max_retries` 硬编码为 0;VectorEngine `gpt-5.4-mini` 的偶发长尾、连接超时或可重试上游状态会在第一次失败后直接持久化成 system error。framework 的英文 `completion error` 前缀也被原样暴露给用户。
|
||||
- 处理:120 秒改为前端软提示阈值:POST 仍 pending 时显示不入库的“仍在处理中,请耐心等待”;provider 明确断开/失败才写正式错误。专用 provider 单 attempt 使用 8 分钟 hard timeout,请求发起阶段读取 `GENARRATIVE_LLM_MAX_RETRIES`,但画布 Agent 最多重试 1 次且重试退避最多 60 秒。不要只计算单次 complete 的最坏时间:runner 还可因非法 JSON/工具校验失败进入后续轮次,必须从 handler 入口开始计算 18 分钟总 deadline,进入 `agent.prompt(...)` 时扣除会话锁/上下文准备已用时间,为持久化和前端 20 分钟 timeout 留出余量。响应头后的体读取/解析错误按明确失败收口,必须使用真实 attempt 计数;规划、配置和定价错误对用户统一为中文,原始诊断只记后端日志。重试发生在任何生成工具执行前,不会重复提交生成任务或扣费,不要通过提高前端 timeout 或 runner `max_turns` 掩盖 provider 重试缺失。
|
||||
- 验证:`platform-editor-agent` 测试锁定 8 分钟 hard timeout 与中文错误;前端 fake timer 用例锁定 120 秒前只显示思考动画、到点后显示耐心等待、成功/失败后移除;`platform-llm` 回归用例锁定第二次 attempt 成功响应头后的 body timeout 仍报累计 2 次;`api-server` 测试锁定专用 client retry、18 分钟整体 deadline 与中文直达错误。运行态排障按同一 request id 对齐 `platform_llm` failure stage 与 `/messages` 总耗时,并确认仍 pending 的请求不再在 120 秒形成错误气泡。
|
||||
- 关联:`server-rs/crates/platform-editor-agent/src/agent/agent.rs`、`server-rs/crates/platform-agent-harness/src/error.rs`、`server-rs/crates/api-server/src/state.rs`、`src/components/image-editor/EditorAgentConversation/useEditorAgentConversation.ts`、`src/components/image-editor/EditorAgentConversation/MessageBubble.tsx`、`src/services/image-editor/editorAgentClient.ts`。
|
||||
|
||||
@@ -744,9 +744,9 @@ pub(crate) async fn generate_editor_character_animation_for_owner(
|
||||
// 背景色决策:预扣泥点之后才发起(显式传入 → 视觉 LLM 按源图主体配色自动决策 → 默认色)。
|
||||
// 决策失败会让本 future 返回 Err,走 execute_billable 的失败退款,用户不损失泥点。
|
||||
let screen_background_decision = resolve_editor_screen_background_color(
|
||||
state.llm_client(),
|
||||
state.editor_agent_llm_client(),
|
||||
EditorScreenBackgroundDecisionInput {
|
||||
state.llm_client(),
|
||||
state.vector_engine_llm_client(),
|
||||
EditorScreenBackgroundDecisionInput {
|
||||
kind: EditorScreenBackgroundDecisionKind::CharacterAnimation,
|
||||
screen_color: payload.screen_color.clone(),
|
||||
prompt: payload.prompt_text.clone(),
|
||||
@@ -754,7 +754,7 @@ pub(crate) async fn generate_editor_character_animation_for_owner(
|
||||
reference_count: 1,
|
||||
source_image_data_url: Some(source_data_url.clone()),
|
||||
},
|
||||
Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext {
|
||||
Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext {
|
||||
state: &state,
|
||||
context: crate::external_api_audit::ExternalApiAuditContext {
|
||||
user_id: Some(owner_user_id.clone()),
|
||||
|
||||
@@ -201,7 +201,7 @@ pub async fn editor_agent_message(
|
||||
let tool_context = context::build_tool_context(&document);
|
||||
|
||||
// Build and run agent
|
||||
let Some(llm_client) = state.editor_agent_llm_client() else {
|
||||
let Some(llm_client) = state.vector_engine_llm_client() else {
|
||||
tracing::warn!(
|
||||
conversation_id = %conversation.conversation_id,
|
||||
"美术 Agent LLM 客户端未配置"
|
||||
|
||||
@@ -1892,9 +1892,9 @@ pub(crate) async fn generate_editor_image_for_owner(
|
||||
let screen_background_decision = if is_character_generation {
|
||||
Some(
|
||||
resolve_editor_screen_background_color(
|
||||
state.llm_client(),
|
||||
state.editor_agent_llm_client(),
|
||||
EditorScreenBackgroundDecisionInput {
|
||||
state.llm_client(),
|
||||
state.vector_engine_llm_client(),
|
||||
EditorScreenBackgroundDecisionInput {
|
||||
kind: EditorScreenBackgroundDecisionKind::Character,
|
||||
screen_color: requested_screen_color.clone(),
|
||||
prompt: role_setting.clone(),
|
||||
@@ -1902,7 +1902,7 @@ pub(crate) async fn generate_editor_image_for_owner(
|
||||
reference_count,
|
||||
source_image_data_url: None,
|
||||
},
|
||||
Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext {
|
||||
Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext {
|
||||
state,
|
||||
context: crate::external_api_audit::ExternalApiAuditContext {
|
||||
user_id: caller.audit_subject_user_id.clone(),
|
||||
@@ -5072,9 +5072,9 @@ pub(crate) async fn generate_editor_icon_spritesheet_for_owner(
|
||||
async {
|
||||
// 背景色决策:预扣泥点之后才发起(决策失败→future 返回 Err→失败退款)。
|
||||
let screen_background_decision = resolve_editor_screen_background_color(
|
||||
state.llm_client(),
|
||||
state.editor_agent_llm_client(),
|
||||
EditorScreenBackgroundDecisionInput {
|
||||
state.llm_client(),
|
||||
state.vector_engine_llm_client(),
|
||||
EditorScreenBackgroundDecisionInput {
|
||||
kind: EditorScreenBackgroundDecisionKind::IconSpritesheet,
|
||||
screen_color: requested_screen_color.clone(),
|
||||
prompt: user_prompt.clone(),
|
||||
@@ -5082,7 +5082,7 @@ pub(crate) async fn generate_editor_icon_spritesheet_for_owner(
|
||||
reference_count,
|
||||
source_image_data_url: None,
|
||||
},
|
||||
Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext {
|
||||
Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext {
|
||||
state,
|
||||
context: crate::external_api_audit::ExternalApiAuditContext {
|
||||
user_id: caller.audit_subject_user_id.clone(),
|
||||
@@ -6156,9 +6156,9 @@ pub(crate) async fn extract_editor_ui_design_assets_for_owner(
|
||||
async {
|
||||
// 背景色决策:预扣泥点之后才发起(决策失败→future 返回 Err→失败退款)。
|
||||
let screen_background_decision = resolve_editor_screen_background_color(
|
||||
state.llm_client(),
|
||||
state.editor_agent_llm_client(),
|
||||
EditorScreenBackgroundDecisionInput {
|
||||
state.llm_client(),
|
||||
state.vector_engine_llm_client(),
|
||||
EditorScreenBackgroundDecisionInput {
|
||||
kind: EditorScreenBackgroundDecisionKind::UiDesignAssetExtraction,
|
||||
screen_color: requested_screen_color.clone(),
|
||||
prompt: decision_prompt.clone(),
|
||||
@@ -6166,7 +6166,7 @@ pub(crate) async fn extract_editor_ui_design_assets_for_owner(
|
||||
reference_count,
|
||||
source_image_data_url: None,
|
||||
},
|
||||
Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext {
|
||||
Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext {
|
||||
state,
|
||||
context: crate::external_api_audit::ExternalApiAuditContext {
|
||||
user_id: caller.audit_subject_user_id.clone(),
|
||||
|
||||
@@ -265,7 +265,7 @@ pub struct AppStateInner {
|
||||
wallet_refund_outbox: Option<Arc<WalletRefundOutbox>>,
|
||||
editor_generation_pricing_store: EditorGenerationPricingStore,
|
||||
llm_client: Option<LlmClient>,
|
||||
editor_agent_llm_client: Option<LlmClient>,
|
||||
vector_engine_llm_client: Option<LlmClient>,
|
||||
matting_client: Option<MattingClient>,
|
||||
bgfilter_provider_http_client: reqwest::Client,
|
||||
bgfilter_worker_http_client: reqwest::Client,
|
||||
@@ -513,7 +513,7 @@ impl AppState {
|
||||
)
|
||||
.map_err(|error| AppStateInitError::DependencyUnavailable(error.to_string()))?;
|
||||
let llm_client = build_llm_client(&config)?;
|
||||
let editor_agent_llm_client = build_editor_agent_llm_client(&config)?;
|
||||
let vector_engine_llm_client = build_vector_engine_llm_client(&config)?;
|
||||
let matting_client = build_matting_client(&config)?;
|
||||
let bgfilter_provider_http_client = build_bgfilter_provider_http_client(&config)?;
|
||||
let bgfilter_worker_http_client = build_bgfilter_worker_http_client(&config)?;
|
||||
@@ -570,7 +570,7 @@ impl AppState {
|
||||
wallet_refund_outbox,
|
||||
editor_generation_pricing_store,
|
||||
llm_client,
|
||||
editor_agent_llm_client,
|
||||
vector_engine_llm_client,
|
||||
matting_client,
|
||||
bgfilter_provider_http_client,
|
||||
bgfilter_worker_http_client,
|
||||
@@ -1281,8 +1281,8 @@ impl AppState {
|
||||
self.llm_client.as_ref()
|
||||
}
|
||||
|
||||
pub fn editor_agent_llm_client(&self) -> Option<&LlmClient> {
|
||||
self.editor_agent_llm_client.as_ref()
|
||||
pub fn vector_engine_llm_client(&self) -> Option<&LlmClient> {
|
||||
self.vector_engine_llm_client.as_ref()
|
||||
}
|
||||
|
||||
pub fn matting_client(&self) -> Option<&MattingClient> {
|
||||
@@ -2126,7 +2126,7 @@ fn build_llm_client(config: &AppConfig) -> Result<Option<LlmClient>, AppStateIni
|
||||
Ok(Some(LlmClient::new(llm_config)?))
|
||||
}
|
||||
|
||||
fn build_editor_agent_llm_client(
|
||||
fn build_vector_engine_llm_client(
|
||||
config: &AppConfig,
|
||||
) -> Result<Option<LlmClient>, AppStateInitError> {
|
||||
// 中文注释:Apimart 已于 2026-06 弃用,LLM 文本调用统一迁移到 VectorEngine。
|
||||
@@ -2429,7 +2429,7 @@ mod tests {
|
||||
let state = AppState::new(AppConfig::default()).expect("state should build");
|
||||
|
||||
assert!(state.llm_client().is_none());
|
||||
assert!(state.editor_agent_llm_client().is_none());
|
||||
assert!(state.vector_engine_llm_client().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2454,7 +2454,7 @@ mod tests {
|
||||
|
||||
let state = AppState::new(config).expect("state should build");
|
||||
let client = state
|
||||
.editor_agent_llm_client()
|
||||
.vector_engine_llm_client()
|
||||
.expect("editor agent LLM client should exist");
|
||||
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user