feat: sync creative agent context with source jobs
This commit is contained in:
@@ -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,并即时刷新顶部当前上下文摘要,避免标题、平台已经切过去了但负责人和上下文还停在旧任务上。
|
||||||
|
|||||||
@@ -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("请填写复盘标题");
|
||||||
|
|||||||
@@ -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", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user