feat: bound news AI concurrency with immediate task refill
Development CI / check-and-build (push) Successful in 2m35s
GitHub Git mirror / mirror (push) Successful in 5s
Development CI / deploy-development (push) Failing after 7s

This commit is contained in:
eezd committed 2026-11-01 12:29:03 +08:00
1 parent f1d060d475
commit cbd2618a20
15 files changed
+603 -129

No files matched your search

+2
View File
@@ -19,3 +19,5 @@ export const startReprocessApi = (data: Risk.ReprocessRange) => request<{ id: nu
export const listReprocessApi = () => request<Risk.ReprocessJob[]>({ url: "/risk/reprocess", method: "get" })
export const cancelReprocessApi = (id: number) => request<null>({ url: `/risk/reprocess/${id}/cancel`, method: "post" })
export const retryReprocessApi = (id: number) => request<null>({ url: `/risk/reprocess/${id}/retry`, method: "post" })
export const saveRiskConcurrencyApi = (max_concurrency: number) => request<{ max_concurrency: number }>({ url: "/risk/settings/concurrency", method: "patch", data: { max_concurrency } })
+2
View File
@@ -43,6 +43,7 @@ export interface RiskEvent {
updated_at: string
}
export interface Settings {
max_concurrency: number
enabled: boolean
provider_id: number | null
model: string
@@ -79,6 +80,7 @@ export interface Briefing {
inputs: { kind?: "package" | "event", id: number, revision: number, category: string, tags: string[], result: RiskResult & { title: string } }[]
}
export interface Overview {
concurrency: { limit: number, running: number | null, queued: number, stages: Record<string, number>, updated_at: string | null }
briefing: Briefing | null
stage2: Record<string, number>
stage3?: { state: string, pending: boolean, heartbeat: string | null }
+26
View File
@@ -59,6 +59,7 @@ const showFailures = ref(false)
const loading = ref(false)
const failed = ref(false)
const saving = ref(false)
const concurrencySaving = ref(false)
let sequence = 0
let detailSequence = 0
let modelsSequence = 0
@@ -157,6 +158,19 @@ async function createTemplates() {
newsSkills.value = skills.filter(s => s.scope === "news" && s.kind === "skill")
ElMessage.success("默认新闻 Skills 已准备好,请前往 Skills 页面审阅并发布")
}
async function saveConcurrency() {
if (!settings.value || concurrencySaving.value) return
const version = user.sessionVersion
concurrencySaving.value = true
try {
await api.saveRiskConcurrencyApi(settings.value.max_concurrency)
if (version !== user.sessionVersion) return
ElMessage.success("并发上限已保存;降低上限时等待在途任务完成")
await load()
} finally {
if (version === user.sessionVersion) concurrencySaving.value = false
}
}
async function save(enable: boolean) {
if (!settings.value) return
const version = user.sessionVersion
@@ -207,6 +221,7 @@ watch(taskStage, () => {
if (showFailures.value) void openFailures()
})
watch(() => user.sessionVersion, () => {
concurrencySaving.value = false
thirdSequence++
secondModelsSequence++
secondModels.value = []
@@ -284,6 +299,10 @@ onBeforeUnmount(() => {
失败 {{ overview.coverage.failed }}
</el-button>
</p>
<p v-if="overview.concurrency">
AI 并发:{{ overview.concurrency.running ?? "未知" }} / {{ overview.concurrency.limit }} · 排队 {{ overview.concurrency.queued }}
<span v-for="(count, stage) in overview.concurrency.stages" :key="stage" class="ml-2">{{ ({ grouping: "分包", analysis: "研判", events: "事件", outlook: "阅读", history: "历史" } as Record<string, string>)[stage] }} {{ count }}</span>
</p>
<p>包研判:完成 {{ overview.stage2.done ?? 0 }} · 待更新 {{ overview.stage2.pending ?? 0 }} · 处理中 {{ overview.stage2.processing ?? 0 }} · 失败 {{ overview.stage2.failed ?? 0 }}</p>
<p v-if="overview.stage3">
阅读编排:{{ overview.stage3.state === 'processing' ? '处理中' : overview.stage3.state === 'failed' ? '失败' : overview.stage3.pending ? '等待聚合更新' : '已更新' }}
@@ -441,6 +460,13 @@ onBeforeUnmount(() => {
<router-link to="/skills">
编辑与发布 Skills
</router-link>
<el-form-item label="最大并发任务数">
<el-input-number v-model="settings.max_concurrency" :min="1" :precision="0" :step="1" :disabled="concurrencySaving" />
<el-button class="ml-2" :loading="concurrencySaving" @click="saveConcurrency">
保存并发上限
</el-button>
</el-form-item>
<p>所有新闻阶段共享名额,完成即补位;实时优先,历史使用空闲名额。测试并启用前请先暂停并等待在途任务完成。</p>
<el-alert type="info" :closable="false" title="测试并启用会分别调用分包、中级研判和高级阅读编排模型。首次启用后仅处理新发布或源站修订的新闻;暂停后恢复沿用原起点。" />
<p>当前状态:{{ settings.enabled ? '已启用' : '已关闭' }}</p>
<el-button :loading="saving" @click="save(false)">