feat: sync creative agent context with source jobs
Some checks failed
StoryForge CI / Baseline checks (push) Has been cancelled
StoryForge CI / Backend tests (push) Has been cancelled
StoryForge CI / Web tests (push) Has been cancelled

This commit is contained in:
kris
2026-04-07 13:34:12 +08:00
parent 3cf56a4db6
commit 670f631475
3 changed files with 35 additions and 1 deletions

View File

@@ -645,3 +645,4 @@
- 公网 collector 示例配置改为显式禁用 `local_model`,并把 `ASR` 桥接端口切到 `127.0.0.1:28088` - 公网 collector 示例配置改为显式禁用 `local_model`,并把 `ASR` 桥接端口切到 `127.0.0.1:28088`
- 新增 Windows `ASR HTTP` 服务资产,兼容 StoryForge 当前 `/transcribe` 协议,便于把 ASR 迁到 Windows 主机 `192.168.31.18` - 新增 Windows `ASR HTTP` 服务资产,兼容 StoryForge 当前 `/transcribe` 协议,便于把 ASR 迁到 Windows 主机 `192.168.31.18`
- Windows 端新增 `ASR` 启动脚本、云端桥接脚本与计划任务注册脚本,并放通 `8088` 入站,保证局域网和公网都可直连该 `ASR` 服务 - Windows 端新增 `ASR` 启动脚本、云端桥接脚本与计划任务注册脚本,并放通 `8088` 入站,保证局域网和公网都可直连该 `ASR` 服务
- 创作类表单的来源任务联动继续收口:`写复盘` 现在切换来源任务时,会同步推荐更合适的负责 Agent并即时刷新顶部当前上下文摘要避免标题、平台已经切过去了但负责人和上下文还停在旧任务上。

View File

@@ -547,8 +547,12 @@ function bindCreativeSourceJobRecommendations(fields, options = {}) {
const sourceJobSelect = fields.querySelector('[data-action-field="sourceJobId"]'); const sourceJobSelect = fields.querySelector('[data-action-field="sourceJobId"]');
if (!(sourceJobSelect instanceof HTMLSelectElement)) return; if (!(sourceJobSelect instanceof HTMLSelectElement)) return;
const sourceContext = fields.querySelector('[data-action-field="sourceJobContext"] .sheet-html'); const sourceContext = fields.querySelector('[data-action-field="sourceJobContext"] .sheet-html');
const contextHtml = fields.querySelector('[data-action-field="context"] .sheet-html');
const assistantSelect = fields.querySelector('[data-action-field="assistantId"]');
const platformSelect = fields.querySelector(`[data-action-field="${options.platformField || "platform"}"]`); const platformSelect = fields.querySelector(`[data-action-field="${options.platformField || "platform"}"]`);
const defaultPlatform = options.defaultPlatform || "douyin"; const defaultPlatform = options.defaultPlatform || "douyin";
const defaultAssistantId = String(options.defaultAssistantId || "").trim();
const projectId = String(options.projectId || getSelectedProject()?.id || "").trim();
const managedFields = []; const managedFields = [];
const resolveSourceJob = () => getCompletedJobById(sourceJobSelect.value) || options.sourceJob || null; const resolveSourceJob = () => getCompletedJobById(sourceJobSelect.value) || options.sourceJob || null;
const recommendedPlatform = (job) => normalizePlatformValue(job?.platform || getPreferredPlatform(), defaultPlatform); const recommendedPlatform = (job) => normalizePlatformValue(job?.platform || getPreferredPlatform(), defaultPlatform);
@@ -575,6 +579,18 @@ function bindCreativeSourceJobRecommendations(fields, options = {}) {
if (platformSelect instanceof HTMLSelectElement) { if (platformSelect instanceof HTMLSelectElement) {
registerManagedField(platformSelect, (job) => recommendedPlatform(job), "change"); registerManagedField(platformSelect, (job) => recommendedPlatform(job), "change");
} }
if (assistantSelect instanceof HTMLSelectElement) {
registerManagedField(assistantSelect, (job) => {
const preferredAssistantId = String(job?.assistant_id || defaultAssistantId).trim();
if (preferredAssistantId && safeArray(Array.from(assistantSelect.options)).some((item) => item.value === preferredAssistantId)) {
return preferredAssistantId;
}
if (defaultAssistantId && safeArray(Array.from(assistantSelect.options)).some((item) => item.value === defaultAssistantId)) {
return defaultAssistantId;
}
return assistantSelect.value || "";
}, "change");
}
const audienceInput = fields.querySelector('[data-action-field="audience"]'); const audienceInput = fields.querySelector('[data-action-field="audience"]');
if (audienceInput instanceof HTMLInputElement) { if (audienceInput instanceof HTMLInputElement) {
registerManagedField(audienceInput, (job) => { registerManagedField(audienceInput, (job) => {
@@ -619,9 +635,14 @@ function bindCreativeSourceJobRecommendations(fields, options = {}) {
if (field.dataset.recommendationMode === "manual") return; if (field.dataset.recommendationMode === "manual") return;
applyManagedValue(field, compute(sourceJob)); applyManagedValue(field, compute(sourceJob));
}); });
if (contextHtml instanceof HTMLElement) {
const assistantId = assistantSelect instanceof HTMLSelectElement ? assistantSelect.value : defaultAssistantId;
contextHtml.innerHTML = renderIntakeActionContextHtml(projectId, assistantId);
}
}; };
sourceJobSelect.addEventListener("change", sync); sourceJobSelect.addEventListener("change", sync);
platformSelect?.addEventListener("change", sync); platformSelect?.addEventListener("change", sync);
assistantSelect?.addEventListener("change", sync);
sync(); sync();
} }
@@ -12333,7 +12354,13 @@ function openReviewAction(defaults = {}) {
{ name: "notes", label: "备注", type: "textarea", rows: 4, value: existingReview?.notes || "", placeholder: "补充团队讨论、平台环境、发布时间段等信息" } { name: "notes", label: "备注", type: "textarea", rows: 4, value: existingReview?.notes || "", placeholder: "补充团队讨论、平台环境、发布时间段等信息" }
], ],
onOpen: ({ fields }) => { onOpen: ({ fields }) => {
bindCreativeSourceJobRecommendations(fields, { sourceJob, defaultPlatform: normalizePlatformValue(existingReview?.platform || defaults.platform || sourceJob?.platform || "douyin"), titleSuffix: "复盘" }); bindCreativeSourceJobRecommendations(fields, {
sourceJob,
defaultPlatform: normalizePlatformValue(existingReview?.platform || defaults.platform || sourceJob?.platform || "douyin"),
titleSuffix: "复盘",
projectId: project.id,
defaultAssistantId: existingReview?.assistant_id || defaultAssistantId
});
}, },
onSubmit: async (values) => { onSubmit: async (values) => {
if (!values.title?.trim()) throw new Error("请填写复盘标题"); if (!values.title?.trim()) throw new Error("请填写复盘标题");

View File

@@ -1198,6 +1198,10 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
assert.match(APP, /function bindManualIntakeTitleRecommendation\(fields, kind, options = \{\}\)/); assert.match(APP, /function bindManualIntakeTitleRecommendation\(fields, kind, options = \{\}\)/);
assert.match(APP, /function bindActionContextRecommendation\(fields, options = \{\}\)/); assert.match(APP, /function bindActionContextRecommendation\(fields, options = \{\}\)/);
assert.match(APP, /function bindCreativeSourceJobRecommendations\(fields, options = \{\}\)/); assert.match(APP, /function bindCreativeSourceJobRecommendations\(fields, options = \{\}\)/);
assert.match(APP, /const contextHtml = fields\.querySelector\('\[data-action-field="context"] \.sheet-html'\);/);
assert.match(APP, /const assistantSelect = fields\.querySelector\('\[data-action-field="assistantId"]'\);/);
assert.match(APP, /registerManagedField\(assistantSelect, \(job\) => \{/);
assert.match(APP, /contextHtml\.innerHTML = renderIntakeActionContextHtml\(projectId, assistantId\);/);
assert.match(APP, /function bindLiveRecorderSheetRecommendations\(fields, options = \{\}\)/); assert.match(APP, /function bindLiveRecorderSheetRecommendations\(fields, options = \{\}\)/);
assert.match(importHomepage, /label: "当前上下文", type: "html"/); assert.match(importHomepage, /label: "当前上下文", type: "html"/);
assert.match(importHomepage, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/); assert.match(importHomepage, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
@@ -1292,6 +1296,8 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
assert.match(reviewAction, /value: existingReview\?\.title \|\| defaults\.title \|\| recommendDerivativeJobTitle\(sourceJob, "复盘", ""\)/); assert.match(reviewAction, /value: existingReview\?\.title \|\| defaults\.title \|\| recommendDerivativeJobTitle\(sourceJob, "复盘", ""\)/);
assert.match(reviewAction, /normalizePlatformValue\(existingReview\?\.platform \|\| defaults\.platform \|\| sourceJob\?\.platform \|\| "douyin"\)/); assert.match(reviewAction, /normalizePlatformValue\(existingReview\?\.platform \|\| defaults\.platform \|\| sourceJob\?\.platform \|\| "douyin"\)/);
assert.match(reviewAction, /onOpen:\s*\(\{ fields \}\) => \{\s*bindCreativeSourceJobRecommendations\(fields, \{/); assert.match(reviewAction, /onOpen:\s*\(\{ fields \}\) => \{\s*bindCreativeSourceJobRecommendations\(fields, \{/);
assert.match(reviewAction, /projectId:\s*project\.id/);
assert.match(reviewAction, /defaultAssistantId:\s*existingReview\?\.assistant_id \|\| defaultAssistantId/);
}); });
test("discovery analysis actions focus the most relevant detail tab after success", () => { test("discovery analysis actions focus the most relevant detail tab after success", () => {