refactor(agent): runAgent uses Agent upstreams only; deprecate legacy settings.pi fields

pi.provider / baseUrl / model / thinkingLevel / apiKeySecretRef are now
DEPRECATED_SETTINGS_FIELDS: kept in the schema for stored JSON, dropped
from PATCH /settings and from the settings sections, never read by the
server. runAgent fails with a clear message when no upstream is set.
The agent demo gate checks the default upstream provider; e2e seeds a
fake upstream as default. OpenAPI summaries say Agent upstream.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 13:45:40 +08:00
1 parent fb7727cf66
commit 3e37ac8ae7
15 files changed
+122 -115

No files matched your search

@@ -1,7 +1,7 @@
import type { FakeScript, FakeStep, FakeToolResult } from "@umt/agent";
import { z } from "zod";
// fake 模式脚本(pi.provider = "fake"):测试直接注入 FakeScript;设置里的 agent.fakeScript 是可序列化子集(JSON):
// fake 模式脚本(默认上游 provider = "fake"):测试直接注入 FakeScript;设置里的 agent.fakeScript 是可序列化子集(JSON):
// { "match": "收进来" | { "regex": "..." }, "steps": [{ "text"?, "toolCalls": [{ "capability", "input" }] }], "finalText": "..." }
// 或它们的数组。input 里的字符串可以写 "{{download.plan.plan.id}}":取该能力最近一次成功结果里的路径值。
+15 -27
View File
@@ -117,31 +117,17 @@ export interface DriveResult {
/**
* 设置 → @umt/agent 的运行参数。按上游运行时(W7-A)凭据由上游的 CredentialStore 在请求时解密注入,这里不取明文;
* 没有任何上游时回退 v0.1 旧字段(pi.provider / pi.apiKeySecretRef,apiKey 现场经 secrets 解密,不落日志)。
* 只走上游:v0.1 的旧字段(pi.provider / model / apiKeySecretRef)已弃用,不再读取(W8-A)。
*/
async function agentSettings(
env: AgentEnv,
s: GlobalSettings,
budget: AgentBudget,
fake: boolean,
upstream: RunUpstream | null,
): Promise<AgentSettings> {
function agentSettings(s: GlobalSettings, budget: AgentBudget, fake: boolean, upstream: RunUpstream): AgentSettings {
const pi = s.pi;
let apiKey: string | undefined;
if (!fake && !upstream) {
if (!pi.apiKeySecretRef) throw new Error("未配置 Agent 上游(设置 → Agent → 上游)");
apiKey = await env.ctx.secrets.resolve(pi.apiKeySecretRef);
if (!pi.model) throw new Error("未配置模型(设置 → Agent → 上游)");
}
const maxTokens = budget.maxTokens ?? s.agent.maxTokensPerRun ?? undefined;
const maxCost = budget.maxCostUsd ?? s.agent.maxCostUsdPerRun ?? undefined;
const up = upstream?.row;
const up = upstream.row;
return {
provider: up ? up.provider : pi.provider === "openai-compatible" ? "openai-compatible" : "anthropic",
...(!up && pi.baseUrl && !fake ? { baseUrl: pi.baseUrl } : {}),
...(apiKey ? { apiKey } : fake && !up ? { apiKey: "fake-key-not-used" } : {}),
model: (up ? up.default_model : pi.model) ?? (fake ? "fake-model" : "default"),
thinkingLevel: up ? up.thinking_level : pi.thinkingLevel,
provider: up.provider,
model: up.default_model ?? (fake ? "fake-model" : "default"),
thinkingLevel: up.thinking_level,
contextBudgetTokens: pi.contextBudget,
compaction: {
mode: pi.compaction.enabled ? "threshold" : "manual",
@@ -329,14 +315,16 @@ export async function drive(
if (blocked) return { status: "failed", reason: "budget", budget: "daily", message: blocked, finalText: "" };
const scope = (run.scope ?? {}) as unknown as RunScope;
// 上游:run.upstream_id(runAgent 的 upstreamId 覆盖)> settings.pi.defaultUpstreamId > 旧字段兼容
let upstream: RunUpstream | null;
// 上游:run.upstream_id(runAgent 的 upstreamId 覆盖)> settings.pi.defaultUpstreamId;都没有直接失败
let upstream: RunUpstream;
let fake: boolean;
let settings: AgentSettings;
try {
upstream = await resolveRunUpstream(ctx, run.upstream_id);
fake = upstream ? upstream.row.provider === "fake" : s.pi.provider === "fake";
settings = await agentSettings(env, s, (run.budget ?? {}) as AgentBudget, fake, upstream);
const resolved = await resolveRunUpstream(ctx, run.upstream_id);
if (!resolved) throw new Error("未配置 Agent 上游(设置 → Agent → 上游)");
upstream = resolved;
fake = upstream.row.provider === "fake";
settings = agentSettings(s, (run.budget ?? {}) as AgentBudget, fake, upstream);
} catch (err) {
return { status: "failed", reason: "error", budget: null, message: (err as Error).message, finalText: "" };
}
@@ -346,9 +334,9 @@ export async function drive(
status: "running",
started_at: run.started_at ?? new Date(),
provider: fake ? "fake" : settings.provider,
model: upstream ? (upstream.row.default_model ?? (fake ? "fake-model" : null)) : fake ? "fake-model" : s.pi.model,
model: upstream.row.default_model ?? (fake ? "fake-model" : null),
thinking_level: settings.thinkingLevel ?? null,
...(upstream ? { upstream_id: upstream.row.id } : {}),
upstream_id: upstream.row.id,
})
.where("id", "=", runId)
.execute();