Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ docker start iris
| `PUT /settings/models/{chat\|embedding}` | 保存模型;api_key 省略保留,空字符串清除,enabled=false 停用 |
| `POST /settings/models/{chat\|embedding}/test` | 空 JSON 测已保存配置,模型 JSON 测草稿 |
| `POST /settings/models/{chat\|embedding}/retry` | 对暂时不可用/账户问题请求重试 |
| `PATCH /settings/limits` | daily_token_limit(null 不限)、learning_concurrency、learning_image_limit、native_image_learning、media_retention_days(默认 7,整数 1—36500) |
| `PATCH /settings/limits` | daily_token_limit(null 不限)、learning_concurrency、learning_image_limit、native_image_learning、media_retention_days(默认 7,整数 1—36500);所有字段省略即保留现值,daily_token_limit 显式 null 才表示不限;操作记录只包含实际提交的字段 |

## 中文试用与管理界面

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/Access.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ test.each(["high", "medium"])(
await user.click(screen.getByRole("button", { name: "测试对话模型连接" }));
expect(await screen.findByText(/密钥无效.*12/)).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "保存对话模型" }));
await user.clear(screen.getByLabelText("学习并发"));
await user.type(screen.getByLabelText("学习并发"), "3");
await user.click(screen.getByRole("button", { name: "保存用量与学习" }));
const saved = calls.find(
(c) =>
Expand Down
88 changes: 83 additions & 5 deletions frontend/src/ConsolidationReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { Badge, Pagination, RoleTime, seconds } from "./ui";
import { PersonaChecks } from "./PersonaDetails";
import { personaDegrees } from "./persona-labels";
import {
consolidationBudgets,
consolidationSteps,
consolidationReason,
consolidationSwitches,
suggestionKinds,
Expand Down Expand Up @@ -334,6 +336,80 @@ function PersonSummaryReport({ data }: { data: MaintenanceReport }) {
);
}

function StepBudgetReport({ data }: { data: MaintenanceReport }) {
const budgets = data.items.filter(
(item) => item.outcome === "model_step_budget",
);
const trimmed = data.items.filter(
(item) => item.outcome === "backlog_trimmed",
);
const expiry = data.question_expiry;
return (
<>
{(data.consolidation || budgets.length > 0) && (
<section
className="detail-section"
aria-label="各步骤模型调用上限与用量"
>
<h3>各步骤模型调用上限与用量</h3>
<p className="fine-print">
每一步独立计数,包含重试和修正;上限沿用本次运行接受时的设置。未到期、关闭或没有任务的步骤可能没有记录。
</p>
{!budgets.length && <p className="quiet">本次未记录步骤用量。</p>}
{budgets.map((item) => (
<article
className="maintenance-item"
key={item.item_key}
aria-label={
consolidationSteps[item.details.step || ""] ||
item.details.step ||
"未知步骤"
}
>
<h4>
{consolidationSteps[item.details.step || ""] ||
item.details.step ||
"未知步骤"}
</h4>
<p>
本轮上限:{item.details.max_calls ?? "未记录"} 次 · 已用:
{item.details.calls_used ?? "未记录"} 次
</p>
<p>
停止原因:
{item.reason ? consolidationReason(item.reason) : "未记录"}
</p>
</article>
))}
</section>
)}
{!!trimmed.length && (
<section className="detail-section" aria-label="配对积压修剪">
<h3>配对积压修剪</h3>
{trimmed.map((item) => (
<p key={item.item_key}>
配对积压修剪:{item.details.count ?? "未记录"} 项 ·
待处理配对上限:{item.details.limit ?? "未记录"} 项
</p>
))}
<p className="fine-print">
仅修剪待处理配对,不改变记忆;相关记忆发生变化后可重新规划。
</p>
</section>
)}
{expiry && (
<section className="detail-section" aria-label="询问到期结束">
<h3>询问到期结束</h3>
<p>到期自动结束的询问:{expiry.count} 条</p>
<p className="fine-print">
仅学习产生且超过保留天数仍未结束的询问;宿主或管理员创建的询问和普通目标不受影响。
</p>
</section>
)}
</>
);
}

export function ConsolidationReportSections({
data,
openMemory,
Expand Down Expand Up @@ -362,17 +438,18 @@ export function ConsolidationReportSections({
</p>
<p className="fine-print">
{calls.unknown_usage_calls ?? 0}{" "}
次调用用量未知;用量为已知合计,包含实际重试、persona
次调用用量未知;用量为四步骤已知合计,包含实际重试、入口画像、persona
及人物摘要调用。
</p>
</>
)}
<details>
<summary>本次接受任务时的整理设置</summary>
<p>
模型调用次数预算:
{data.consolidation.settings.max_calls ?? "未记录"}
</p>
{consolidationBudgets.map(([key, label]) => (
<p key={key}>
{label}:{data.consolidation?.settings[key] ?? "未记录"}
</p>
))}
{consolidationSwitches.map(([key, label]) => (
<p key={key}>
{label}:
Expand All @@ -387,6 +464,7 @@ export function ConsolidationReportSections({
</details>
</section>
)}
<StepBudgetReport data={data} />
{(data.consolidation ||
data.items.some((i) => i.phase === "consolidation")) && (
<>
Expand Down
15 changes: 6 additions & 9 deletions frontend/src/ConsolidationSettings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ beforeEach(() => {
);
if (url.endsWith("/consolidation")) {
settings.consolidation = { ...settings.consolidation, ...body };
settings.lifecycle.maintenance_time = body.maintenance_time;
if ("maintenance_time" in body)
settings.lifecycle.maintenance_time = body.maintenance_time;
}
if (url.endsWith("/goal-dedup-judge")) settings.goal_dedup_judge = body;
settings.operations = [
Expand Down Expand Up @@ -107,7 +108,7 @@ test("整理设置保存时间、调用预算与所有开关,并显示操作
target: { value: "04:15" },
});
fireEvent.change(
within(section).getByLabelText("每次整理的模型调用次数预算"),
within(section).getByLabelText("记忆整理(合并/矛盾/依赖)每轮调用上限"),
{ target: { value: "0" } },
);
await userEvent.click(within(section).getByLabelText("矛盾检查与建议"));
Expand All @@ -122,12 +123,8 @@ test("整理设置保存时间、调用预算与所有开关,并显示操作
expect(request.body).toEqual({
maintenance_time: "04:15",
max_calls: 0,
merge_enabled: true,
conflict_enabled: false,
dependency_enabled: true,
persona_enabled: true,
person_summary_enabled: false,
goal_review_enabled: true,
});
expect((request.init?.headers as any)["X-Iris-CSRF"]).toBe(
"consolidation-settings-test",
Expand All @@ -136,19 +133,19 @@ test("整理设置保存时间、调用预算与所有开关,并显示操作
expect(within(section).getByText(/只影响之后接受的新运行/)).toBeVisible();
});

test.each(["", "51", "1.5"])(
test.each(["", "-1", "1001", "1.5"])(
"整理预算 %s 被阻止,未发送请求",
async (input) => {
render(<SettingsPage />);
fireEvent.change(
await screen.findByLabelText("每次整理的模型调用次数预算"),
await screen.findByLabelText("记忆整理(合并/矛盾/依赖)每轮调用上限"),
{ target: { value: input } },
);
await userEvent.click(
screen.getByRole("button", { name: "保存梦境整理设置" }),
);
expect(
await screen.findByText(/模型调用次数预算须为 0—50 的整数/),
await screen.findByText(/每轮调用上限须为 0—1000 的整数/),
).toBeVisible();
expect(requests.some((r) => r.init?.method === "PATCH")).toBe(false);
},
Expand Down
Loading