refactor(copy): 术语统一——收录版本、模型服务、智能体等(DEAI-2)
- 版本目标 → 收录版本(前后端用户可见字符串、能力描述、给模型的提示) - Agent 上游 / 上游 → 模型服务;界面上的 Agent / Agent 会话 → 智能体 / 智能体会话 - 身份歧义 / 识别有歧义 / 无法确定作品 → 没识别出作品;待识别(池) → 未识别 - 建档 → 加入媒体库;资产 → 文件;库根 → 媒体库目录;纳管 → 本工具 / 外部任务; 哨兵 → 标记文件 / 检查目录;站点快照 → 抓取;Feed / RSS 订阅 / 来源健康 → RSS - 代码标识符、API 字段名、迁移文件不改;glossary.md 同步;smoke 选择器跟随 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d282212a6b
commit
4480fc346d
212 files changed
+741
-715
No files matched your search
@@ -73,8 +73,8 @@ export function createPiModule(
|
||||
registerScheduledJob(ctx, {
|
||||
key: "pi.token_refresh",
|
||||
defaults: {
|
||||
name: "上游登录刷新",
|
||||
description: "订阅登录即将过期的 Agent 上游,提前刷新 token",
|
||||
name: "模型服务登录刷新",
|
||||
description: "订阅登录即将过期的模型服务,提前刷新 token",
|
||||
group: "maintain",
|
||||
schedule: { type: "interval", everyMs: interval },
|
||||
},
|
||||
|
||||
@@ -92,12 +92,12 @@ function keyOf(cred: UpstreamCredential | undefined): string | null {
|
||||
/** 按 provider / 认证方式挑模型列表接口 */
|
||||
function planFor(spec: UpstreamSpec, cred: UpstreamCredential | undefined): Plan {
|
||||
const extra = spec.extra ?? {};
|
||||
if (spec.provider === "fake") return { kind: "unsupported", reason: "Fake 上游没有远程模型列表" };
|
||||
if (spec.provider === "fake") return { kind: "unsupported", reason: "Fake 模型服务没有远程模型列表" };
|
||||
if (spec.provider === "radius") return { kind: "pi" };
|
||||
const custom = spec.provider === "openai-compatible";
|
||||
// 订阅登录只有 OpenRouter 换到的是普通 API Key,其它订阅 token 不能调公开的模型列表接口
|
||||
if (spec.authKind === "oauth" && spec.provider !== "openrouter")
|
||||
return { kind: "unsupported", reason: "订阅登录的上游没有公开的模型列表接口,用内置模型表" };
|
||||
return { kind: "unsupported", reason: "订阅登录的模型服务没有公开的模型列表接口,用内置模型表" };
|
||||
const key = keyOf(cred);
|
||||
if (spec.authKind !== "none" && !key && !custom) throw new FetchError("auth", "还没有配置 API Key");
|
||||
|
||||
@@ -232,7 +232,7 @@ function normalizeGoogle(item: Record<string, unknown>): Remote | null {
|
||||
function parseList(shape: Shape, body: unknown): Remote[] {
|
||||
const b = obj(body);
|
||||
const raw = Array.isArray(body) ? body : Array.isArray(b.data) ? b.data : Array.isArray(b.models) ? b.models : null;
|
||||
if (!raw) throw new FetchError("other", "上游返回的不是模型列表");
|
||||
if (!raw) throw new FetchError("other", "模型服务返回的不是模型列表");
|
||||
const norm = shape === "anthropic" ? normalizeAnthropic : shape === "google" ? normalizeGoogle : normalizeOpenAI;
|
||||
const out: Remote[] = [];
|
||||
const seen = new Set<string>();
|
||||
@@ -376,7 +376,7 @@ async function fetchRemote(
|
||||
if (plan.kind === "unsupported") throw new FetchError("unsupported", plan.reason);
|
||||
if (plan.kind === "http") return await fetchList(plan.endpoint);
|
||||
// Radius:pi-ai 自带的动态刷新
|
||||
if (!client) throw new FetchError("config", "上游配置不完整");
|
||||
if (!client) throw new FetchError("config", "模型服务配置不完整");
|
||||
const r = await client.listModels({ refresh: true });
|
||||
if (!r.refreshed) throw toFetchError(new Error(r.refreshError ?? "刷新失败"));
|
||||
return r.items.map(fromInfo);
|
||||
|
||||
@@ -105,8 +105,8 @@ export class OAuthSessions {
|
||||
async start(row: PiUpstreamRow, req: { mode?: PiOAuthMode; enterpriseDomain?: string }): Promise<PiOAuthStartResult> {
|
||||
const info = providerInfo(row.provider);
|
||||
if (row.auth_kind !== "oauth" || !info?.oauth)
|
||||
throw new ApiException("precondition_failed", "这个上游不是订阅登录类型");
|
||||
if (!row.enabled) throw new ApiException("precondition_failed", "上游已停用");
|
||||
throw new ApiException("precondition_failed", "这个模型服务不是订阅登录类型");
|
||||
if (!row.enabled) throw new ApiException("precondition_failed", "模型服务已停用");
|
||||
const mode = req.mode ?? info.oauth.modes[0]!;
|
||||
if (!info.oauth.modes.includes(mode))
|
||||
throw new ApiException(
|
||||
@@ -270,6 +270,6 @@ export class OAuthSessions {
|
||||
|
||||
export async function requireRow(ctx: ServerContext, id: string): Promise<PiUpstreamRow> {
|
||||
const row = await getRow(ctx, id);
|
||||
if (!row) throw new ApiException("not_found", "上游不存在");
|
||||
if (!row) throw new ApiException("not_found", "模型服务不存在");
|
||||
return row;
|
||||
}
|
||||
@@ -24,13 +24,13 @@ const DECISION_KEY = (id: string) => `pi_upstream_expired:${id}`;
|
||||
|
||||
/** 与其它模块的 upsertDecision 同一写法(跨模块不 import):同键未关闭时只累加 attempts */
|
||||
async function upsertExpiredDecision(ctx: ServerContext, row: PiUpstreamRow, message: string) {
|
||||
const title = `Agent 上游「${row.name}」登录已失效,需要重新登录`;
|
||||
const title = `模型服务「${row.name}」登录已失效,需要重新登录`;
|
||||
await ctx.db.transaction().execute(async (trx) => {
|
||||
const r = await sql<{ id: string; inserted: boolean }>`
|
||||
insert into decision (kind, severity, title, summary, subject_type, subject_id, dedupe_key, evidence, options)
|
||||
values ('other', 'warning', ${title}, ${message}, 'pi_upstream', ${row.id}, ${DECISION_KEY(row.id)},
|
||||
${JSON.stringify({ provider: row.provider, upstreamId: row.id, error: message })}::jsonb,
|
||||
${JSON.stringify([{ key: "relogin", label: "重新登录", description: "设置 → Agent → 上游" }])}::jsonb)
|
||||
${JSON.stringify([{ key: "relogin", label: "重新登录", description: "设置 → 智能体 → 模型服务" }])}::jsonb)
|
||||
on conflict (dedupe_key) where status in ('open', 'investigating', 'awaiting_user')
|
||||
do update set attempts = decision.attempts + 1, evidence = excluded.evidence, summary = excluded.summary,
|
||||
updated_at = now()
|
||||
@@ -102,7 +102,7 @@ export async function refreshPiTokens(ctx: ServerContext, opts: RefreshOptions =
|
||||
if (r.ok) report.refreshed += 1;
|
||||
else {
|
||||
report.failed.push({ upstreamId: row.id, message: r.message });
|
||||
ctx.logger.warn({ upstreamId: row.id, provider: row.provider, message: r.message }, "Agent 上游 token 刷新失败");
|
||||
ctx.logger.warn({ upstreamId: row.id, provider: row.provider, message: r.message }, "模型服务 token 刷新失败");
|
||||
}
|
||||
}
|
||||
return report;
|
||||
|
||||
@@ -116,7 +116,7 @@ export async function createUpstream(
|
||||
.execute();
|
||||
} catch (err) {
|
||||
if (secretRef) await ctx.secrets.remove(secretRef).catch(() => {});
|
||||
if (isUnique(err)) throw new ApiException("conflict", `上游名称「${body.name}」已存在`);
|
||||
if (isUnique(err)) throw new ApiException("conflict", `模型服务名称「${body.name}」已存在`);
|
||||
throw err;
|
||||
}
|
||||
if (body.makeDefault || !(await defaultUpstreamId(ctx))) await setDefaultUpstream(ctx, id);
|
||||
@@ -140,7 +140,7 @@ export async function updateUpstream(
|
||||
const defaultModel = body.defaultModel !== undefined ? body.defaultModel : row.default_model;
|
||||
validateShape({ provider: row.provider, authKind, baseUrl, defaultModel });
|
||||
if (body.apiKey && authKind !== "api_key")
|
||||
throw new ApiException("validation_failed", "只有 api_key 型上游可以填 apiKey");
|
||||
throw new ApiException("validation_failed", "只有 api_key 型模型服务可以填 apiKey");
|
||||
|
||||
const kindChanged = authKind !== row.auth_kind;
|
||||
const store = dbCredentialStore(ctx, row.id);
|
||||
@@ -167,7 +167,7 @@ export async function updateUpstream(
|
||||
.where("id", "=", row.id)
|
||||
.execute()
|
||||
.catch((err) => {
|
||||
if (isUnique(err)) throw new ApiException("conflict", `上游名称「${body.name}」已存在`);
|
||||
if (isUnique(err)) throw new ApiException("conflict", `模型服务名称「${body.name}」已存在`);
|
||||
throw err;
|
||||
});
|
||||
if (typeof body.apiKey === "string") {
|
||||
@@ -217,10 +217,11 @@ export async function resolveRunUpstream(ctx: ServerContext, upstreamId?: string
|
||||
const id = upstreamId ?? (await defaultUpstreamId(ctx));
|
||||
if (!id) return null;
|
||||
const row = await getRow(ctx, id);
|
||||
if (!row) throw new Error(upstreamId ? `指定的上游 ${upstreamId} 不存在` : "默认上游已不存在,在设置里重新选择");
|
||||
if (!row.enabled) throw new Error(`上游「${row.name}」已停用`);
|
||||
if (row.status === "expired") throw new Error(`上游「${row.name}」登录已失效,在设置里重新登录`);
|
||||
if (!row)
|
||||
throw new Error(upstreamId ? `指定的模型服务 ${upstreamId} 不存在` : "默认模型服务已不存在,在设置里重新选择");
|
||||
if (!row.enabled) throw new Error(`模型服务「${row.name}」已停用`);
|
||||
if (row.status === "expired") throw new Error(`模型服务「${row.name}」登录已失效,在设置里重新登录`);
|
||||
if (row.status === "auth_required")
|
||||
throw new Error(`上游「${row.name}」${row.auth_kind === "oauth" ? "未登录" : "未配置 API Key"}`);
|
||||
throw new Error(`模型服务「${row.name}」${row.auth_kind === "oauth" ? "未登录" : "未配置 API Key"}`);
|
||||
return { row, spec: { ...specOf(row), extra: parseExtra(row.extra) }, credentials: dbCredentialStore(ctx, row.id) };
|
||||
}
|
||||
@@ -59,7 +59,7 @@ async function readSecret(ctx: Ctx, row: Pick<PiUpstreamRow, "auth_kind" | "secr
|
||||
try {
|
||||
return decode(row.auth_kind, await ctx.secrets.resolve(row.secret_ref));
|
||||
} catch (err) {
|
||||
ctx.logger.warn({ secretRef: row.secret_ref, err: { message: (err as Error).message } }, "上游凭证读取失败");
|
||||
ctx.logger.warn({ secretRef: row.secret_ref, err: { message: (err as Error).message } }, "模型服务凭证读取失败");
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ export function dbCredentialStore(ctx: Ctx, upstreamId: string): UpstreamCredent
|
||||
.where("id", "=", upstreamId)
|
||||
.forUpdate()
|
||||
.executeTakeFirst();
|
||||
if (!row) throw new Error("上游不存在或已删除");
|
||||
if (!row) throw new Error("模型服务不存在或已删除");
|
||||
const cur = await readSecret(ctx, row);
|
||||
const next = await fn(cur);
|
||||
if (next === undefined) return cur;
|
||||
|
||||
Reference in new issue
Block a user