From 16bc9fd5e4c5b9344c750d9bc4a2bbf4a9c36c6f Mon Sep 17 00:00:00 2001 From: kris Date: Mon, 6 Apr 2026 09:11:39 +0800 Subject: [PATCH] feat: prefer direct benchmark actions when context exists --- CHANGELOG.md | 16 ++++++++++ web/storyforge-web-v4/assets/app.js | 29 +++++++++++++++++++ .../tests/workbench-pages.test.mjs | 10 +++++++ 3 files changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9685413..a5247cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ 这个文件用于给 Gitea 仓库保留阶段性版本更新记录,方便直接查看每一轮里程碑,不用只依赖零散 commit。 +## 2026-04-06 + +### 查相似与保存对标关系入口开始优先直执行 + +- `查相似` 入口在当前已经选中账号时,会优先直接触发相似账号搜索,而不是先打开旧表单。 +- `保存对标关系` 入口在当前已有相似候选时,也会优先直接保存首个候选关系;只有缺少上下文时才回退到旧表单。 +- 这样 `找对标` 这条主链进一步从“先开表单再继续”收成了“有上下文就直接执行、没上下文才补信息”。 + ## 2026-04-05 ### 直播录制表单开始按当前项目和平台推荐默认标题与导入样例 @@ -528,3 +536,11 @@ - 修复 fnOS `live_recorder` 部署链,改成同步 `DouyinLiveRecorder-main` 源码到 NAS 并在 NAS 构建,避免错误预构建镜像里缺少 `webui.py` 导致容器启动即失败。 - 新增 `scripts/deploy_fnos_storyforge_live_recorder.sh`,并把 live recorder 并入 `deploy_fnos_storyforge_lan_stack.sh`。 - `smoke_fnos_storyforge_lan.sh` 新增 `live_recorder` 健康检查,后续 NAS 重启或版本更新后能直接发现录制服务回退。 +# 2026-04-06 + +- Added fnOS-native deployment assets for StoryForge local dependencies: + - `cli-proxy-api` model gateway on `:8317` + - `n8n` on `:5670` + - `huobao-drama` on `:5678` +- Extended `deploy_fnos_storyforge_lan_stack.sh` so the NAS LAN stack can recreate model gateway, n8n, huobao, live recorder, collector and web from repo-managed assets. +- Switched collector fnOS defaults away from the Mac host for `LOCAL_OPENAI_BASE_URL`, `N8N_BASE_URL`, and `HUOBAO_BASE_URL`, so the NAS stack no longer depends on local disk-hosted services for those routes. diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index 23e5982..d9656af 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -12648,6 +12648,19 @@ document.addEventListener("click", async (event) => { return; } if (name === "open-similar-search") { + const account = getSelectedAccount(); + if (account?.id) { + await runDirectDiscoveryAction("search-similar-accounts", { + target_account_id: account.id, + max_candidates: 8, + extra_requirements: "" + }, { + busyLabel: "正在查找相似账号...", + errorTitle: "查相似失败", + discoveryFocus: "relations" + }); + return; + } openSimilaritySearchAction(); return; } @@ -12665,6 +12678,22 @@ document.addEventListener("click", async (event) => { return; } if (name === "open-benchmark-link") { + const account = getSelectedAccount(); + const candidate = safeArray(appState.lastSimilaritySearch?.candidates)[0] || null; + if (account?.id && candidate) { + await runDirectDiscoveryAction("save-benchmark-link", { + source_account_id: account.id, + target_account_id: candidate.candidate_account_id || "", + target_profile_url: candidate.candidate_account_id ? "" : (candidate.candidate_profile_url || ""), + search_id: appState.lastSimilaritySearch?.id || "", + note: brief(candidate.rationale_text || "由对标工作台直接加入对标库。", 120) + }, { + busyLabel: "正在保存对标关系...", + errorTitle: "保存对标失败", + discoveryFocus: "relations" + }); + return; + } openBenchmarkLinkAction(); return; } diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs index 2eaada1..b25ae78 100644 --- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs +++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs @@ -1450,6 +1450,16 @@ test("live-first workbench flows no longer advertise stale missing-capability pl assert.match(APP, /暂未识别当前动作/); }); +test("smart discovery entrypoints prefer direct execute before falling back to forms", () => { + const clicks = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {"); + assert.match(clicks, /name === "open-similar-search"[\s\S]*const account = getSelectedAccount\(\);/); + assert.match(clicks, /name === "open-similar-search"[\s\S]*runDirectDiscoveryAction\("search-similar-accounts"/); + assert.match(clicks, /name === "open-similar-search"[\s\S]*openSimilaritySearchAction\(\);/); + assert.match(clicks, /name === "open-benchmark-link"[\s\S]*const candidate = safeArray\(appState\.lastSimilaritySearch\?\.candidates\)\[0\] \|\| null;/); + assert.match(clicks, /name === "open-benchmark-link"[\s\S]*runDirectDiscoveryAction\("save-benchmark-link"/); + assert.match(clicks, /name === "open-benchmark-link"[\s\S]*openBenchmarkLinkAction\(\);/); +}); + test("declared static workbench actions are wired into explicit handlers", () => { const declared = new Set([...APP.matchAll(/data-action="([a-zA-Z0-9_-]+)"/g)].map((match) => match[1])); const clickHandled = new Set([...APP.matchAll(/if \(name === "([a-zA-Z0-9_-]+)"\)/g)].map((match) => match[1]));