From 11afc659057dc5e48b661848122f5cdd9163128a Mon Sep 17 00:00:00 2001 From: Linghong Date: Thu, 17 Sep 2026 09:22:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAGC=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E6=8A=A0=E5=9B=BE=E8=AE=A4=E8=AF=81=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 普通登录态抠图请求改走站内账号鉴权路由 保留 ExternalDeveloper 模式的 External v1 API Key 路由 透传站内 Idempotency-Key 并兼容两种队列响应结构 同步 AGC 抠图契约、技术方案与决策记录 --- .../references/projection-contract.md | 2 +- .../src-tauri/src/agent/direct_tool_bridge.rs | 58 +++++++++++++++++-- .../shared-memory/decision-log.md | 4 +- ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 2 +- .../crates/api-server/src/editor_project.rs | 4 +- 5 files changed, 60 insertions(+), 10 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/agc-client-projection/references/projection-contract.md b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/agc-client-projection/references/projection-contract.md index 87cba6b1a..9e2f9413a 100644 --- a/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/agc-client-projection/references/projection-contract.md +++ b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/agc-client-projection/references/projection-contract.md @@ -14,4 +14,4 @@ Read scopes remain separate: `asset.list` is the current project's local manifes `agc_create_or_derive_resource` accepts only semantic intent. The client resolves `sourceLocalAssetId`, creates stable request identities, recovers matching pending operations, serializes paid submissions, writes supported media into the current canvas and same-name asset folder, validates downloaded bytes, commits the local manifest transaction, and returns redacted warnings. A tool error or timeout is not permission to generate again with a new identity. -`agc_remove_background` accepts a registered image `sourceLocalAssetId`, `assetName`, and optional `backgroundMode` and `screenColor`. `complex` uses semantic segmentation to identify the foreground; `flat` removes a solid-colour background. Prefer `flat` when the background is known to be solid; omitting the mode selects `complex`. Only `flat` accepts a colour: `auto`, `#RRGGBB`, or omitted for automatic service detection. Never infer a concrete colour for `auto`. Empty or invalid values and colour without `flat` are rejected. The client resolves the formal source resource, canvas/folder context, stable operation identity, idempotency key, and authenticated External v1 `/api/external/v1/editor/images/background-removals` call. Mode and colour are part of request identity. Its result is bounded queue state; Codex must not poll internal workers, construct source URLs, or retry with a new identity after an uncertain response. +`agc_remove_background` accepts a registered image `sourceLocalAssetId`, `assetName`, and optional `backgroundMode` and `screenColor`. `complex` uses semantic segmentation to identify the foreground; `flat` removes a solid-colour background. Prefer `flat` when the background is known to be solid; omitting the mode selects `complex`. Only `flat` accepts a colour: `auto`, `#RRGGBB`, or omitted for automatic service detection. Never infer a concrete colour for `auto`. Empty or invalid values and colour without `flat` are rejected. The client resolves the formal source resource, canvas/folder context, stable operation identity, idempotency key, and authenticated request. Ordinary account mode maps the External v1 shaped route to `/api/editor/images/background-removals`; ExternalDeveloper mode uses `/api/external/v1/editor/images/background-removals`. Mode and colour are part of request identity. Its result is bounded queue state; Codex must not poll internal workers, construct source URLs, or retry with a new identity after an uncertain response. diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs index 705da3ecb..399bc45cd 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs @@ -1886,7 +1886,7 @@ async fn bridge_create_or_derive_resource( } async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Value) -> Value { - let result = async { + let result = with_direct_editor_api_credentials(async { super::direct_tools_mcp::validate_remove_background_arguments(arguments)?; enforce_project_permission_policy(&state.root, "canvas.asset_generate")?; enforce_project_permission_policy(&state.root, "asset.register")?; @@ -1929,7 +1929,7 @@ async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Val screen_color, ); let (_operation_id, idempotency_key) = state.resource_request_ids(&fingerprint)?; - let route = "/api/external/v1/editor/images/background-removals"; + let route = access.api_route("/api/external/v1/editor/images/background-removals"); let mut request_body = json!({ "sourceImageSrc": source_resource_id, "projectId": manifest.project_id, @@ -1947,7 +1947,7 @@ async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Val let response = crate::http_client::with_agc_main_site_marker( client .post(format!("{}{}", api_base_url, route)) - .bearer_auth(api_key) + .bearer_auth(access.bearer_token()) .header("Idempotency-Key", idempotency_key) .json(&request_body), ) @@ -1965,7 +1965,8 @@ async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Val } return Err(format!("抠图服务提交失败:HTTP {}", status.as_u16())); } - let queue_state = external_editor_response_data(&payload).clone(); + let queue_state = + background_removal_queue_state(&payload, access.frozen_platform_session().is_some())?; Ok::<_, String>(json!({ "status": "queued", "sourceLocalAssetId": source_asset_id, @@ -1974,7 +1975,7 @@ async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Val "assetFolderId": context.asset_folder_id, "queueState": bridge_safe_queue_state(queue_state), })) - } + }) .await; match result { Ok(value) => bridge_tool_result(value.to_string(), Vec::new(), false), @@ -2000,6 +2001,20 @@ fn background_removal_request_fingerprint( } } +fn background_removal_queue_state( + payload: &Value, + platform_account_route: bool, +) -> Result { + let data = external_editor_response_data(payload); + if platform_account_route { + return data + .get("queueState") + .cloned() + .ok_or_else(|| "抠图服务响应缺少 queueState".to_string()); + } + Ok(data.clone()) +} + fn bridge_safe_queue_state(value: Value) -> Value { let object = value.as_object(); json!({ @@ -3604,4 +3619,37 @@ mod tests { assert!(projection.get("error").is_none()); assert!(projection.get("signedUrl").is_none()); } + + #[test] + fn background_removal_queue_state_matches_authentication_route_envelope() { + let platform_payload = json!({ + "data": { + "queueState": { + "operationId": "platform-operation-1", + "status": "queued" + } + } + }); + assert_eq!( + background_removal_queue_state(&platform_payload, true).expect("platform queue state"), + json!({ + "operationId": "platform-operation-1", + "status": "queued" + }) + ); + + let external_payload = json!({ + "data": { + "operationId": "external-operation-1", + "status": "queued" + } + }); + assert_eq!( + background_removal_queue_state(&external_payload, false).expect("external queue state"), + json!({ + "operationId": "external-operation-1", + "status": "queued" + }) + ); + } } diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index a4ca70a6f..598f4a991 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -8176,8 +8176,8 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 ## 2026-08-24 AGC Direct 抠图语义工具 -- 决策:将 External v1 `/api/external/v1/editor/images/background-removals` 通过 `agc_remove_background` 加入受控 `agc_tools`。工具只接受当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端负责正式 resourceId、画布/素材目录、稳定 operation/idempotency 身份、权限和错误脱敏,不向 Codex 暴露内部 BgFilter worker、凭据或任意 API。 -- 约束:异步结果只投影有界队列状态,不允许模型自行构造源 URL 或在不确定提交后更换请求身份;External v1 负责 API Key、幂等接收与统一 operation 查询,客户端不得绕过该契约。 +- 决策:将抠图能力通过 `agc_remove_background` 加入受控 `agc_tools`。工具只接受当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;普通登录态使用账号鉴权的 `/api/editor/images/background-removals`,ExternalDeveloper 模式使用 External v1 `/api/external/v1/editor/images/background-removals`。客户端负责正式 resourceId、画布/素材目录、稳定 operation/idempotency 身份、权限和错误脱敏,不向 Codex 暴露内部 BgFilter worker、凭据或任意 API。 +- 约束:异步结果只投影有界队列状态,不允许模型自行构造源 URL 或在不确定提交后更换请求身份;两种路由都接收客户端稳定幂等身份,External v1 继续负责 API Key、幂等接收与统一 operation 查询,客户端不得绕过该契约。 ## 2026-08-24 资源详情动作、空态滚动与最终图多步恢复 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 43706740d..36cf92fc5 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -136,7 +136,7 @@ npm 游戏的可预览产物固定为对应 package 目录下的 `dist/index.htm - 项目路径、projectId、当前 revision、源文件路径与媒体类型、operationId、Idempotency-Key、登录态、项目锁、付费提交、轮询恢复、下载校验与 manifest 事务全部由客户端持有。模型不能提交或覆盖这些字段。同一 Direct `clientTurnId + 规范语义参数` 生成稳定 UUID v4 身份;单回合同参重试复用原 operation,不同请求串行且最多四项。跨回合存在完全匹配的 pending 账本时优先恢复原 operation,不能换键重发。 - 资源查询同时投影未完成 operation 的安全状态。媒体工具成功只返回 operation、本地相对路径、资源类型、Canvas/resource/asset/task 身份、正式序列帧以及脱敏后的 `warnings / sliceWarnings`;错误继续使用统一脱敏边界。客户端资源账本持久化 completed 结果的两类告警,committed replay 不能把历史告警伪装成空集合。 - 角色动画、视频、音效和背景音乐在构造新的远端请求前统一准备当前项目同名画布与素材目录上下文,并在端点支持时携带 `projectId / assetFolderId / canvasCompletion`。角色动画 placeholder 使用源图片真实宽高,避免非方形角色进入画布时失真;正式 resource/asset 与序列帧继续直接复用 External 返回身份,不从首帧伪造重复资源。已有冻结 request body 或已受理 operation 保持不变,不因本次升级重建请求或重复扣费。 -- 抠图通过新增 `agc_remove_background` 语义工具开放:模型只提交当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端解析稳定 `resourceId`,准备同名画布/素材目录并生成稳定 operation/idempotency 身份,调用 External v1 `/api/external/v1/editor/images/background-removals` 后只返回有界队列状态。抠图服务仍由客户端和服务端负责源校验、BgFilter、素材登记与画布事务,Codex 不获得内部 worker、凭据或任意 API 调用权。 +- 抠图通过新增 `agc_remove_background` 语义工具开放:模型只提交当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端解析稳定 `resourceId`,准备同名画布/素材目录并生成稳定 operation/idempotency 身份。普通登录态使用账号鉴权的 `/api/editor/images/background-removals`,ExternalDeveloper 模式使用 External v1 `/api/external/v1/editor/images/background-removals`;两者都只返回有界队列状态。抠图服务仍由客户端和服务端负责源校验、BgFilter、素材登记与画布事务,Codex 不获得内部 worker、凭据或任意 API 调用权。 ## 2026-08-23 AGC 资源生成补齐(视频 / 动画 / 音效 / 背景音乐) diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index 5f92b841a..bc3f69744 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -6266,17 +6266,19 @@ pub(crate) async fn edit_editor_image_for_owner_with_source_snapshot( pub async fn remove_editor_image_background( State(state): State, + headers: HeaderMap, Extension(request_context): Extension, Extension(authenticated): Extension, Json(payload): Json, ) -> Result, AppError> { let caller = EditorGenerationCaller::from_authenticated(&authenticated); + let idempotency_key = optional_editor_idempotency_key(&headers)?; let queue_job = enqueue_editor_background_removal_for_owner( &state, &request_context, &caller, payload, - None, + idempotency_key, ) .await?; Ok(json_success_body(