feat: refine assistant sheets with project-aware knowledge bases
This commit is contained in:
@@ -646,3 +646,7 @@
|
||||
- 新增 Windows `ASR HTTP` 服务资产,兼容 StoryForge 当前 `/transcribe` 协议,便于把 ASR 迁到 Windows 主机 `192.168.31.18`
|
||||
- Windows 端新增 `ASR` 启动脚本、云端桥接脚本与计划任务注册脚本,并放通 `8088` 入站,保证局域网和公网都可直连该 `ASR` 服务
|
||||
- 创作类表单的来源任务联动继续收口:`写复盘` 现在切换来源任务时,会同步推荐更合适的负责 Agent,并即时刷新顶部当前上下文摘要,避免标题、平台已经切过去了但负责人和上下文还停在旧任务上。
|
||||
- 套餐/额度页面补上“剩余额度预测”:额度页、额度面板和套餐预览现在都会明确显示剩余预算、剩余文案、剩余 AI 视频、剩余实拍和剩余存储,不再只展示总预算和总配额。
|
||||
- `创建 Agent / 编辑 Agent` 这两张表单也补成了带上下文和知识库联动的产品化表单:创建时切项目会同步刷新默认知识库,编辑时可以直接更新默认知识库,不必再回别处改。
|
||||
- 额度页残留的半成品口径已收口,不再出现“后端尚未完全接入真实预算”这类提示;未配置独立额度策略时,会直接引导按预算基线和动作池去建立试用、增长或规模套餐。
|
||||
- `smoke_public_storyforge.sh` 和 `smoke_fnos_storyforge_lan.sh` 现在会显式校验 `integrations/health` 的关键依赖状态、部署位置和 `local_model=not_configured` 口径,不再只看页面能打开和基础 healthz。
|
||||
|
||||
@@ -535,6 +535,41 @@ function bindActionContextRecommendation(fields, options = {}) {
|
||||
sync();
|
||||
}
|
||||
|
||||
function bindAssistantSheetRecommendations(fields, options = {}) {
|
||||
const contextHtml = fields.querySelector('[data-action-field="context"] .sheet-html');
|
||||
const projectSelect = fields.querySelector('[data-action-field="projectId"]');
|
||||
const knowledgeBaseSelect = fields.querySelector('[data-action-field="knowledgeBaseId"]');
|
||||
const defaultProjectId = String(options.defaultProjectId || getSelectedProject()?.id || "").trim();
|
||||
const defaultKnowledgeBaseId = String(options.defaultKnowledgeBaseId || "").trim();
|
||||
const defaultAssistantId = String(options.defaultAssistantId || "").trim();
|
||||
const syncKnowledgeBaseOptions = () => {
|
||||
if (!(knowledgeBaseSelect instanceof HTMLSelectElement)) return;
|
||||
const projectId = projectSelect instanceof HTMLSelectElement ? projectSelect.value : defaultProjectId;
|
||||
const knowledgeBases = getKnowledgeBaseOptions(projectId);
|
||||
const currentValue = knowledgeBaseSelect.value || "";
|
||||
const fallbackValue = knowledgeBases.some((item) => item.value === currentValue)
|
||||
? currentValue
|
||||
: knowledgeBases.some((item) => item.value === defaultKnowledgeBaseId)
|
||||
? defaultKnowledgeBaseId
|
||||
: knowledgeBases[0]?.value || "";
|
||||
knowledgeBaseSelect.innerHTML = [{ value: "", label: "暂不绑定" }, ...knowledgeBases].map((item) => `
|
||||
<option value="${escapeHtml(item.value)}">${escapeHtml(item.label)}</option>
|
||||
`).join("");
|
||||
knowledgeBaseSelect.value = fallbackValue;
|
||||
};
|
||||
const syncContext = () => {
|
||||
if (!(contextHtml instanceof HTMLElement)) return;
|
||||
const projectId = projectSelect instanceof HTMLSelectElement ? projectSelect.value : defaultProjectId;
|
||||
contextHtml.innerHTML = renderIntakeActionContextHtml(projectId, defaultAssistantId);
|
||||
};
|
||||
const sync = () => {
|
||||
syncKnowledgeBaseOptions();
|
||||
syncContext();
|
||||
};
|
||||
projectSelect?.addEventListener("change", sync);
|
||||
sync();
|
||||
}
|
||||
|
||||
function getCompletedJobById(jobId = "") {
|
||||
const normalizedId = String(jobId || "").trim();
|
||||
if (!normalizedId) return null;
|
||||
@@ -4812,6 +4847,7 @@ function renderTenantQuotaPanel() {
|
||||
const packageLabel = String(quotaConfig.package_title || quotaConfig.package_label || "").trim() || (hasHardLimit ? "自定义套餐" : "未设套餐");
|
||||
const packageFocus = String(quotaConfig.package_focus || "").trim();
|
||||
const warnThreshold = Number(quotaConfig.warn_threshold ?? 0.8);
|
||||
const forecast = getTenantQuotaForecastValues(quota || {}, usage);
|
||||
const quotaTaskTitle = quota?.storage_over_limit
|
||||
? "先处理存储超限"
|
||||
: quota?.enabled === false
|
||||
@@ -4840,12 +4876,12 @@ function renderTenantQuotaPanel() {
|
||||
];
|
||||
const cards = [
|
||||
{ label: "套餐档位", value: packageLabel, sub: packageFocus || `预警阈值 ${formatNumber((warnThreshold || 0.8) * 100)}%` },
|
||||
{ label: "预算", value: `${formatNumber((quota?.monthly_budget_cents || 0) / 100)} 元`, sub: `已用 ${formatNumber((usage?.total_cost_cents || 0) / 100)} 元` },
|
||||
{ label: "分析配额", value: formatNumber(quota?.analysis_quota || 0), sub: `已用 ${formatNumber(categories.analysis?.quantity || 0)}` },
|
||||
{ label: "文案配额", value: formatNumber(quota?.copy_quota || 0), sub: `已用 ${formatNumber(categories.copy?.quantity || 0)}` },
|
||||
{ label: "AI 视频配额", value: formatNumber(quota?.ai_video_quota || 0), sub: `已用 ${formatNumber(categories.ai_video?.quantity || 0)}` },
|
||||
{ label: "实拍剪辑配额", value: formatNumber(quota?.real_cut_quota || 0), sub: `已用 ${formatNumber(categories.real_cut?.quantity || 0)}` },
|
||||
{ label: "存储上限", value: formatBytes(quota?.storage_limit_bytes || 0), sub: `当前 ${formatBytes(usage?.storage_bytes || 0)}` }
|
||||
{ label: "预算", value: `${formatNumber((quota?.monthly_budget_cents || 0) / 100)} 元`, sub: `已用 ${formatNumber((usage?.total_cost_cents || 0) / 100)} 元 · 剩余 ${formatNumber(forecast.remainingBudgetCents / 100)} 元` },
|
||||
{ label: "分析配额", value: formatNumber(quota?.analysis_quota || 0), sub: `已用 ${formatNumber(categories.analysis?.quantity || 0)} · 剩余 ${formatNumber(forecast.remainingAnalysisQuota)}` },
|
||||
{ label: "文案配额", value: formatNumber(quota?.copy_quota || 0), sub: `已用 ${formatNumber(categories.copy?.quantity || 0)} · 剩余 ${formatNumber(forecast.remainingCopyQuota)}` },
|
||||
{ label: "AI 视频配额", value: formatNumber(quota?.ai_video_quota || 0), sub: `已用 ${formatNumber(categories.ai_video?.quantity || 0)} · 剩余 ${formatNumber(forecast.remainingAiVideoQuota)}` },
|
||||
{ label: "实拍剪辑配额", value: formatNumber(quota?.real_cut_quota || 0), sub: `已用 ${formatNumber(categories.real_cut?.quantity || 0)} · 剩余 ${formatNumber(forecast.remainingRealCutQuota)}` },
|
||||
{ label: "存储上限", value: formatBytes(quota?.storage_limit_bytes || 0), sub: `当前 ${formatBytes(usage?.storage_bytes || 0)} · 剩余 ${formatBytes(forecast.remainingStorageBytes)}` }
|
||||
];
|
||||
return `
|
||||
<div class="panel pad">
|
||||
@@ -4876,6 +4912,9 @@ function renderTenantQuotaPanel() {
|
||||
<span class="tag">${escapeHtml(`成本 ${(usage?.total_cost_cents || 0) / 100} 元`)}</span>
|
||||
<span class="tag">${escapeHtml(`预警 ${(warnThreshold || 0.8) * 100}%`)}</span>
|
||||
</div>
|
||||
<div class="compact-summary-row" style="margin-top:10px;">
|
||||
${renderTenantQuotaForecastTags(quota || {}, usage)}
|
||||
</div>
|
||||
${quotaNotice}
|
||||
<div class="mini-grid" style="margin-top:14px;">
|
||||
${cards.map((item) => `
|
||||
@@ -8139,6 +8178,7 @@ function renderCreditsScreen() {
|
||||
const quota = appState.tenantQuota;
|
||||
const usage = appState.tenantUsage || quota?.usage || {};
|
||||
const categories = usage?.categories || {};
|
||||
const forecast = getTenantQuotaForecastValues(quota || {}, usage);
|
||||
const estimatedVideoUsage = (categories.ai_video?.quantity || 0) + (categories.real_cut?.quantity || 0);
|
||||
const budgetAmount = (quota?.monthly_budget_cents || usage?.total_cost_cents || 0) / 100;
|
||||
const usedAmount = (usage?.total_cost_cents || 0) / 100;
|
||||
@@ -8179,9 +8219,12 @@ function renderCreditsScreen() {
|
||||
<span class="tag">${escapeHtml(formatNumber(categories.copy?.quantity || jobs.filter((item) => item.line_type === "analysis").length))} 条文案</span>
|
||||
<span class="tag">${escapeHtml(formatNumber(estimatedVideoUsage || jobs.filter((item) => item.line_type === "ai_video" || item.line_type === "real_cut").length))} 次视频</span>
|
||||
</div>
|
||||
<div class="mobile-only compact-summary-row" style="margin-bottom:14px;">
|
||||
${renderTenantQuotaForecastTags(quota || {}, usage)}
|
||||
</div>
|
||||
<div class="layout-grid grid-3">
|
||||
<div class="stat-card"><small>文案消耗预估</small><strong>${escapeHtml(formatNumber(categories.copy?.quantity || jobs.filter((item) => item.line_type === "analysis").length))}</strong><div class="stat-foot"><span>分析 / 生成链路</span><span class="positive">按任务量估算</span></div></div>
|
||||
<div class="stat-card"><small>本周期预算</small><strong>${escapeHtml(formatNumber((quota?.monthly_budget_cents || usage?.total_cost_cents || 0) / 100))}</strong><div class="stat-foot"><span>元</span><span class="warn">已用 ${escapeHtml(formatNumber((usage?.total_cost_cents || 0) / 100))} 元</span></div></div>
|
||||
<div class="stat-card"><small>本周期预算</small><strong>${escapeHtml(formatNumber((quota?.monthly_budget_cents || usage?.total_cost_cents || 0) / 100))}</strong><div class="stat-foot"><span>元</span><span class="warn">已用 ${escapeHtml(formatNumber((usage?.total_cost_cents || 0) / 100))} 元 · 剩余 ${escapeHtml(formatNumber(forecast.remainingBudgetCents / 100))} 元</span></div></div>
|
||||
<div class="stat-card"><small>视频消耗预估</small><strong>${escapeHtml(formatNumber(estimatedVideoUsage || jobs.filter((item) => item.line_type === "ai_video" || item.line_type === "real_cut").length))}</strong><div class="stat-foot"><span>AI 视频 / 实拍剪辑</span><span class="positive">可做套餐</span></div></div>
|
||||
</div>
|
||||
<div class="layout-grid grid-main" style="margin-top:18px;">
|
||||
@@ -8191,11 +8234,11 @@ function renderCreditsScreen() {
|
||||
<div class="list">
|
||||
<div class="task-item">
|
||||
<h4>预算与已用</h4>
|
||||
<p>${escapeHtml(quota ? `当前预算 ${formatNumber((quota.monthly_budget_cents || 0) / 100)} 元,已用 ${formatNumber((usage?.total_cost_cents || 0) / 100)} 元。` : "后端尚未完全接入真实预算,当前先按任务量做用户可理解的额度看板。")}</p>
|
||||
<p>${escapeHtml(quota ? `当前预算 ${formatNumber((quota.monthly_budget_cents || 0) / 100)} 元,已用 ${formatNumber((usage?.total_cost_cents || 0) / 100)} 元,剩余 ${formatNumber(forecast.remainingBudgetCents / 100)} 元。` : "当前项目还没有独立额度策略,先按最近动作量和成本信号建立可执行的预算基线。")}</p>
|
||||
</div>
|
||||
<div class="task-item">
|
||||
<h4>动作额度</h4>
|
||||
<p>${escapeHtml(quota ? `文案 ${formatNumber(quota.copy_quota || 0)} / AI 视频 ${formatNumber(quota.ai_video_quota || 0)} / 实拍剪辑 ${formatNumber(quota.real_cut_quota || 0)}。` : "当前先按文案、AI 视频、实拍剪辑三类动作池展示,便于先按项目阶段配置套餐。")}</p>
|
||||
<p>${escapeHtml(quota ? `文案剩余 ${formatNumber(forecast.remainingCopyQuota)} / AI 视频剩余 ${formatNumber(forecast.remainingAiVideoQuota)} / 实拍剪辑剩余 ${formatNumber(forecast.remainingRealCutQuota)}。` : "文案、AI 视频和实拍剪辑会按动作池分开管理,适合先按项目阶段配置试用、增长或规模套餐。")}</p>
|
||||
</div>
|
||||
<div class="task-item">
|
||||
<h4>使用建议</h4>
|
||||
@@ -8274,6 +8317,49 @@ function getTenantQuotaPackagePreset(label) {
|
||||
return TENANT_QUOTA_PACKAGE_PRESETS[String(label || "").trim().toLowerCase()] || null;
|
||||
}
|
||||
|
||||
function getTenantQuotaForecastValues(values = {}, usage = null) {
|
||||
const resolvedUsage = usage || appState.tenantUsage || {};
|
||||
const categories = resolvedUsage?.categories || {};
|
||||
const readValue = (camelKey, snakeKey) => Number(values?.[camelKey] ?? values?.[snakeKey] ?? 0);
|
||||
const readUsageCount = (key) => Number(categories?.[key]?.quantity || 0);
|
||||
const monthlyBudgetCents = readValue("monthlyBudgetCents", "monthly_budget_cents");
|
||||
const storageLimitBytes = readValue("storageLimitBytes", "storage_limit_bytes");
|
||||
const analysisQuota = readValue("analysisQuota", "analysis_quota");
|
||||
const copyQuota = readValue("copyQuota", "copy_quota");
|
||||
const aiVideoQuota = readValue("aiVideoQuota", "ai_video_quota");
|
||||
const realCutQuota = readValue("realCutQuota", "real_cut_quota");
|
||||
const recorderQuota = readValue("recorderQuota", "recorder_quota");
|
||||
const totalCostCents = Number(resolvedUsage?.total_cost_cents || 0);
|
||||
const storageBytes = Number(resolvedUsage?.storage_bytes || 0);
|
||||
return {
|
||||
monthlyBudgetCents,
|
||||
remainingBudgetCents: Math.max(monthlyBudgetCents - totalCostCents, 0),
|
||||
storageLimitBytes,
|
||||
remainingStorageBytes: Math.max(storageLimitBytes - storageBytes, 0),
|
||||
analysisQuota,
|
||||
remainingAnalysisQuota: Math.max(analysisQuota - readUsageCount("analysis"), 0),
|
||||
copyQuota,
|
||||
remainingCopyQuota: Math.max(copyQuota - readUsageCount("copy"), 0),
|
||||
aiVideoQuota,
|
||||
remainingAiVideoQuota: Math.max(aiVideoQuota - readUsageCount("ai_video"), 0),
|
||||
realCutQuota,
|
||||
remainingRealCutQuota: Math.max(realCutQuota - readUsageCount("real_cut"), 0),
|
||||
recorderQuota,
|
||||
remainingRecorderQuota: Math.max(recorderQuota - readUsageCount("live_recorder"), 0)
|
||||
};
|
||||
}
|
||||
|
||||
function renderTenantQuotaForecastTags(values, usage = null) {
|
||||
const forecast = getTenantQuotaForecastValues(values, usage);
|
||||
return `
|
||||
<span class="tag green">${escapeHtml(`剩余预算 ${formatNumber(forecast.remainingBudgetCents / 100)} 元`)}</span>
|
||||
<span class="tag">${escapeHtml(`剩余文案 ${formatNumber(forecast.remainingCopyQuota)}`)}</span>
|
||||
<span class="tag">${escapeHtml(`剩余 AI 视频 ${formatNumber(forecast.remainingAiVideoQuota)}`)}</span>
|
||||
<span class="tag">${escapeHtml(`剩余实拍 ${formatNumber(forecast.remainingRealCutQuota)}`)}</span>
|
||||
<span class="tag">${escapeHtml(`剩余存储 ${formatBytes(forecast.remainingStorageBytes)}`)}</span>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderTenantQuotaPackagePreview(label, values = null) {
|
||||
const preset = getTenantQuotaPackagePreset(label);
|
||||
const packageLabel = String(label || "custom").trim() || "custom";
|
||||
@@ -8298,6 +8384,9 @@ function renderTenantQuotaPackagePreview(label, values = null) {
|
||||
<span class="tag">${escapeHtml(`AI 视频 ${formatNumber(currentValues.aiVideoQuota || 0)}`)}</span>
|
||||
<span class="tag">${escapeHtml(`实拍剪辑 ${formatNumber(currentValues.realCutQuota || 0)}`)}</span>
|
||||
</div>
|
||||
<div class="task-meta">
|
||||
${renderTenantQuotaForecastTags(currentValues)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -11109,6 +11198,7 @@ function openCreateAssistantAction() {
|
||||
description: "先定义用途、平台与目标,再让 Agent 学习内容。",
|
||||
submitLabel: "创建 Agent",
|
||||
fields: [
|
||||
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, "") },
|
||||
{ name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() },
|
||||
{ name: "name", label: "名称", placeholder: "例如:创业成交助手" },
|
||||
{ name: "description", label: "说明", placeholder: "例如:服务创业 IP 与成交型短视频" },
|
||||
@@ -11117,6 +11207,10 @@ function openCreateAssistantAction() {
|
||||
{ name: "knowledgeBaseId", label: "默认知识库", type: "select", value: kbOptions[0]?.value || "", options: [{ value: "", label: "暂不绑定" }, ...kbOptions] },
|
||||
{ name: "modelProfileId", label: "主模型", type: "select", value: modelOptions.find((item) => item.value === safeArray(appState.dashboard?.model_profiles).find((m) => m.is_default)?.id)?.value || modelOptions[0]?.value || "", options: modelOptions }
|
||||
],
|
||||
onOpen: ({ fields }) => {
|
||||
bindAssistantSheetRecommendations(fields, { defaultProjectId: project.id, defaultKnowledgeBaseId: kbOptions[0]?.value || "" });
|
||||
fields.querySelector('[data-action-field="name"]')?.focus();
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
if (!values.name?.trim()) throw new Error("请填写 Agent 名称");
|
||||
const projectId = values.projectId || project.id;
|
||||
@@ -11148,17 +11242,27 @@ function openEditAssistantAction(assistantId = "") {
|
||||
return;
|
||||
}
|
||||
const modelOptions = getModelOptions();
|
||||
const kbOptions = getKnowledgeBaseOptions(assistant.project_id || "");
|
||||
openActionModal({
|
||||
title: "编辑 Agent",
|
||||
description: "更新当前 Agent 的名称、目标和主模型,不会影响已完成任务。",
|
||||
submitLabel: "保存 Agent",
|
||||
fields: [
|
||||
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(assistant.project_id || "", assistant.id) },
|
||||
{ name: "name", label: "名称", value: assistant.name || "", placeholder: "例如:创业成交助手" },
|
||||
{ name: "description", label: "说明", value: assistant.description || "", placeholder: "例如:服务创业 IP 与成交型短视频" },
|
||||
{ name: "goal", label: "生成目标", value: assistant.generation_goal || "", placeholder: "例如:输出创业口播、对标拆解和成交文案" },
|
||||
{ name: "systemPrompt", label: "系统提示词", type: "textarea", rows: 5, value: assistant.system_prompt || "", placeholder: "可选,可先留空,后面随时补充" },
|
||||
{ name: "knowledgeBaseId", label: "默认知识库", type: "select", value: safeArray(assistant.knowledge_base_ids)[0] || "", options: [{ value: "", label: "暂不绑定" }, ...kbOptions] },
|
||||
{ name: "modelProfileId", label: "主模型", type: "select", value: assistant.model_profile_id || modelOptions[0]?.value || "", options: modelOptions }
|
||||
],
|
||||
onOpen: ({ fields }) => {
|
||||
bindAssistantSheetRecommendations(fields, {
|
||||
defaultProjectId: assistant.project_id || "",
|
||||
defaultKnowledgeBaseId: safeArray(assistant.knowledge_base_ids)[0] || "",
|
||||
defaultAssistantId: assistant.id
|
||||
});
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
if (!values.name?.trim()) throw new Error("请填写 Agent 名称");
|
||||
const updated = await storyforgeFetch(`/v2/assistants/${encodeURIComponent(assistant.id)}`, {
|
||||
@@ -11168,6 +11272,7 @@ function openEditAssistantAction(assistantId = "") {
|
||||
description: values.description || "",
|
||||
generation_goal: values.goal || "",
|
||||
system_prompt: values.systemPrompt || "",
|
||||
knowledge_base_ids: values.knowledgeBaseId ? [values.knowledgeBaseId] : [],
|
||||
model_profile_id: values.modelProfileId || ""
|
||||
}
|
||||
});
|
||||
|
||||
@@ -314,12 +314,23 @@ test("quota and review screens foreground live next-step guidance", () => {
|
||||
assert.doesNotMatch(storage, /后端暂未提供 \/v2\/storage\/status/);
|
||||
assert.match(storage, /当前实例没有返回存储策略时/);
|
||||
assert.doesNotMatch(credits, /后续再接真实套餐/);
|
||||
assert.match(credits, /按项目阶段配置套餐/);
|
||||
assert.doesNotMatch(credits, /后端尚未完全接入真实预算/);
|
||||
assert.doesNotMatch(credits, /当前先按文案、AI 视频、实拍剪辑三类动作池展示/);
|
||||
assert.match(credits, /当前项目还没有独立额度策略/);
|
||||
assert.match(credits, /文案、AI 视频和实拍剪辑会按动作池分开管理/);
|
||||
assert.match(credits, /按最近动作量和成本信号建立可执行的预算基线|按项目阶段配置试用、增长或规模套餐/);
|
||||
assert.match(credits, /预算、动作池和项目阶段绑定成正式套餐/);
|
||||
assert.match(tenantQuota, /套餐档位/);
|
||||
assert.match(tenantQuota, /预警阈值|预警 80%/);
|
||||
assert.match(APP, /const TENANT_QUOTA_PACKAGE_PRESETS =/);
|
||||
assert.match(APP, /function getTenantQuotaForecastValues\(values = \{\}, usage = null\)/);
|
||||
assert.match(APP, /function renderTenantQuotaForecastTags\(values, usage = null\)/);
|
||||
assert.match(APP, /renderTenantQuotaForecastTags\(currentValues\)/);
|
||||
assert.match(APP, /function renderTenantQuotaPackagePreview/);
|
||||
assert.match(tenantQuota, /renderTenantQuotaForecastTags\(quota \|\| \{\}, usage\)/);
|
||||
assert.match(credits, /renderTenantQuotaForecastTags\(quota \|\| \{\}, usage\)/);
|
||||
assert.match(credits, /剩余 \${escapeHtml\(formatNumber\(forecast\.remainingBudgetCents \/ 100\)\)} 元/);
|
||||
assert.match(credits, /文案剩余 \${formatNumber\(forecast\.remainingCopyQuota\)}/);
|
||||
assert.match(quotaAction, /renderTenantQuotaPackagePreview/);
|
||||
assert.match(APP, /预设已锁定|支持自定义/);
|
||||
assert.match(quotaAction, /input\.disabled = Boolean\(preset\)/);
|
||||
@@ -1197,6 +1208,7 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
|
||||
assert.match(APP, /function recommendManualIntakeTitle\(project, platform, kind\)/);
|
||||
assert.match(APP, /function bindManualIntakeTitleRecommendation\(fields, kind, options = \{\}\)/);
|
||||
assert.match(APP, /function bindActionContextRecommendation\(fields, options = \{\}\)/);
|
||||
assert.match(APP, /function bindAssistantSheetRecommendations\(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"]'\);/);
|
||||
@@ -1386,7 +1398,15 @@ test("assistant actions return to the playbook workspace with the saved assistan
|
||||
const editAssistant = extractBetween(APP, "function openEditAssistantAction(assistantId = \"\")", "function openAnalyzeSelectedAccountAction()");
|
||||
const clickHandler = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
|
||||
assert.match(APP, /function focusPlaybookWorkspace\(assistantId = ""\)/);
|
||||
assert.match(createAssistant, /label: "当前上下文", type: "html"/);
|
||||
assert.match(createAssistant, /label: "默认知识库", type: "select"/);
|
||||
assert.match(createAssistant, /bindAssistantSheetRecommendations\(fields, \{ defaultProjectId: project\.id, defaultKnowledgeBaseId: kbOptions\[0\]\?\.value \|\| "" \}\);/);
|
||||
assert.match(createAssistant, /fields\.querySelector\('\[data-action-field="name"\]'\)\?\.focus\(\);/);
|
||||
assert.match(createAssistant, /focusPlaybookWorkspace\(assistant\.id\)/);
|
||||
assert.match(editAssistant, /label: "当前上下文", type: "html"/);
|
||||
assert.match(editAssistant, /label: "默认知识库", type: "select"/);
|
||||
assert.match(editAssistant, /bindAssistantSheetRecommendations\(fields, \{/);
|
||||
assert.match(editAssistant, /knowledge_base_ids:\s*values\.knowledgeBaseId \? \[values\.knowledgeBaseId\] : \[\]/);
|
||||
assert.match(editAssistant, /focusPlaybookWorkspace\(updated\.id\)/);
|
||||
assert.match(clickHandler, /name === "select-assistant"[\s\S]*focusPlaybookWorkspace\(appState\.selectedAssistantId\)/);
|
||||
assert.match(APP, /id="current-agent-anchor"/);
|
||||
|
||||
Reference in New Issue
Block a user