import { kToTokens, tokensToK } from "~/lib/tokens.ts"; import { AgentToolsBlock } from "./agent-tools.tsx"; import { WEBSEARCH_CARD } from "./agent-websearch.tsx"; import { PiUpstreamsBlock } from "./pi-upstreams.tsx"; import type { FieldSpec, FieldValue } from "./resource-section.tsx"; import { num, str } from "./resource-section.tsx"; import { type CardSpec, n, SettingsCard, SettingsGate } from "./settings-card.tsx"; // Agent 分区:上游(pi_upstream)+ 上下文与子代理(pi)+ 预算(budget.agent* 与单次上限)+ 自动调查 + 网页搜索 + 高级 /** 可清空的数字:空串 → null */ const nullNum = (v: FieldValue | undefined) => (str(v) === "" ? null : Number(str(v))); const RUNTIME_CARD: CardSpec = { anchor: "runtime", title: "上下文与子会话", fields: [ { name: "pi.contextBudget", label: "上下文预算", kind: "tokens", tokens: { min: 4000, max: 2_000_000, required: true }, hint: "4K–2000K,超过模型窗口时按窗口算", }, { name: "pi.subagentsEnabled", label: "允许子会话", kind: "switch" }, { name: "pi.compaction.enabled", label: "上下文压缩", kind: "switch" }, { name: "pi.compaction.thresholdRatio", label: "压缩触发比例", kind: "number", hint: "0.3–0.95,用到预算的这个比例时压缩", visible: (v) => v["pi.compaction.enabled"] === true, }, ], fromSettings: (s) => ({ "pi.contextBudget": tokensToK(s.pi.contextBudget), "pi.subagentsEnabled": s.pi.subagentsEnabled, "pi.compaction.enabled": s.pi.compaction.enabled, "pi.compaction.thresholdRatio": n(s.pi.compaction.thresholdRatio), }), toPatch: (v) => ({ pi: { contextBudget: kToTokens(str(v["pi.contextBudget"])), subagentsEnabled: v["pi.subagentsEnabled"] === true, compaction: { enabled: v["pi.compaction.enabled"] === true, thresholdRatio: num(v["pi.compaction.thresholdRatio"]), }, }, }), }; const BUDGET_CARD: CardSpec = { anchor: "budget", title: "预算", description: "超出每日预算的运行停在「超出预算」,等你放行。", fields: [ { name: "budget.agentMaxRunsPerDay", label: "每日运行次数", kind: "number" }, { name: "budget.agentMaxCostUsdPerDay", label: "每日费用(USD)", kind: "number" }, { name: "agent.maxToolCalls", label: "单次工具调用上限", kind: "number", hint: "1–500" }, { name: "agent.maxDurationSec", label: "单次时长上限(秒)", kind: "number" }, { name: "agent.maxTokensPerRun", label: "单次 token 上限", kind: "tokens", tokens: { min: 1 }, hint: "单位 K,留空不限", }, { name: "agent.maxCostUsdPerRun", label: "单次费用上限(USD)", kind: "number", hint: "留空不限" }, ], fromSettings: (s) => ({ "budget.agentMaxRunsPerDay": n(s.budget.agentMaxRunsPerDay), "budget.agentMaxCostUsdPerDay": n(s.budget.agentMaxCostUsdPerDay), "agent.maxToolCalls": n(s.agent.maxToolCalls), "agent.maxDurationSec": n(s.agent.maxDurationSec), "agent.maxTokensPerRun": tokensToK(s.agent.maxTokensPerRun), "agent.maxCostUsdPerRun": n(s.agent.maxCostUsdPerRun), }), toPatch: (v) => ({ budget: { agentMaxRunsPerDay: num(v["budget.agentMaxRunsPerDay"]), agentMaxCostUsdPerDay: num(v["budget.agentMaxCostUsdPerDay"]), }, agent: { maxToolCalls: num(v["agent.maxToolCalls"]), maxDurationSec: num(v["agent.maxDurationSec"]), maxTokensPerRun: kToTokens(str(v["agent.maxTokensPerRun"])) ?? null, maxCostUsdPerRun: nullNum(v["agent.maxCostUsdPerRun"]), }, }), }; const INVESTIGATE_CARD: CardSpec = { anchor: "investigate", title: "自动调查", description: "待处理事项(没识别出作品、下载失败等)创建时,自动发起一次有界调查。", fields: [ { name: "agent.autoInvestigate", label: "自动调查", kind: "switch", wide: true }, { name: "agent.autoInvestigateMode", label: "模式", kind: "select", options: [ { value: "investigate", label: "只读调查,结论交还给你" }, { value: "recover", label: "授权内恢复(重试任务、重新校验、确认识别)" }, ], hint: "恢复只在已发布策略或持续授权覆盖时执行,换源、删除、清理始终排除;没有授权时按只读调查", wide: true, visible: (v) => v["agent.autoInvestigate"] === true, }, { name: "agent.investigateMaxToolCalls", label: "工具调用上限", kind: "number", hint: "1–100", visible: (v) => v["agent.autoInvestigate"] === true, }, { name: "agent.investigateMaxAttempts", label: "同一根因最多恢复次数", kind: "number", hint: "1–10,用完后只交还给你", visible: (v) => v["agent.autoInvestigate"] === true && v["agent.autoInvestigateMode"] === "recover", }, { name: "agent.recoverVerifySec", label: "恢复后核实时长(秒)", kind: "number", hint: "等下载或任务状态变化的最长时间", visible: (v) => v["agent.autoInvestigate"] === true && v["agent.autoInvestigateMode"] === "recover", }, ], fromSettings: (s) => ({ "agent.autoInvestigate": s.agent.autoInvestigate, "agent.autoInvestigateMode": s.agent.autoInvestigateMode, "agent.investigateMaxToolCalls": n(s.agent.investigateMaxToolCalls), "agent.investigateMaxAttempts": n(s.agent.investigateMaxAttempts), "agent.recoverVerifySec": n(s.agent.recoverVerifySec), }), toPatch: (v) => ({ agent: { autoInvestigate: v["agent.autoInvestigate"] === true, autoInvestigateMode: v["agent.autoInvestigateMode"] === "recover" ? "recover" : "investigate", investigateMaxToolCalls: num(v["agent.investigateMaxToolCalls"]), investigateMaxAttempts: num(v["agent.investigateMaxAttempts"]), recoverVerifySec: num(v["agent.recoverVerifySec"]), }, }), }; const DEV_ONLY_FIELDS = new Set(["agent.sessionDir", "agent.fakeScript"]); const ADVANCED_CARD: CardSpec = { anchor: "advanced", title: "高级", // 会话目录与 fake 脚本是开发调试项,生产构建不显示(DEAI-2) fields: ( [ { name: "agent.goalPollSec", label: "目标轮询间隔(秒)", kind: "number", hint: "等下载、入库结束时的检查间隔" }, { name: "agent.goalWaitHours", label: "目标最长等待(小时)", kind: "number", hint: "超时后按「仍在等待」汇报结束", }, { name: "agent.optionsTimeoutSec", label: "方案选择等待(秒)", kind: "number", hint: "智能体给出方案后等你选择的最长时间", }, { name: "agent.optionsTimeoutAction", label: "超时未选", kind: "select", options: [ { value: "recommended", label: "自动选推荐项" }, { value: "stop", label: "不选,智能体收尾" }, ], }, { name: "agent.approvalTimeoutSec", label: "会话内审批有效期(秒)", kind: "number", hint: "步骤处的审批卡过期未批准,按拒绝处理", }, { name: "agent.sessionDir", label: "会话目录", kind: "text", wide: true, placeholder: "留空用 DATA_DIR/agent-sessions", hint: "绝对路径,api 与 worker 都要可读写;有运行中的会话时不要改", }, { name: "agent.fakeScript", label: "fake 脚本(JSON)", kind: "textarea", hint: "只对 fake 模型服务生效,留空用内置演示脚本;格式见 docs/runbook/agent.md", }, ] as FieldSpec[] ).filter((f) => import.meta.env.DEV || !DEV_ONLY_FIELDS.has(f.name)), fromSettings: (s) => ({ "agent.goalPollSec": n(s.agent.goalPollSec), "agent.goalWaitHours": n(s.agent.goalWaitHours), "agent.optionsTimeoutSec": n(s.agent.optionsTimeoutSec), "agent.optionsTimeoutAction": s.agent.optionsTimeoutAction, "agent.approvalTimeoutSec": n(s.agent.approvalTimeoutSec), "agent.sessionDir": s.agent.sessionDir ?? "", "agent.fakeScript": s.agent.fakeScript ?? "", }), toPatch: (v) => ({ agent: { goalPollSec: num(v["agent.goalPollSec"]), goalWaitHours: num(v["agent.goalWaitHours"]), optionsTimeoutSec: num(v["agent.optionsTimeoutSec"]), optionsTimeoutAction: v["agent.optionsTimeoutAction"] === "stop" ? "stop" : "recommended", approvalTimeoutSec: num(v["agent.approvalTimeoutSec"]), sessionDir: str(v["agent.sessionDir"]) || null, fakeScript: str(v["agent.fakeScript"]) || null, }, }), }; export function AgentSection() { return (
{(s) => ( <> )}
); }