feat(y-log): 问答按 LLM 调用各记一条 + 全量补齐各 API 操作埋点

- 问答日志从「每轮 onFinish 一条」改为 useThreadStream onLangChainEvent
  监听 on_chat_model_end,每次大模型调用结束各上报一条(含子代理)
- 广覆盖各 API client 成功路径:查看(view)/查询(search)/新增(create)/
  编辑(edit)/删除(delete)/启停(enabled·disabled)/导入导出,补齐此前
  整模块零埋点与漏掉的单条详情 GET;跳过高频 typeahead/轮询/bootstrap

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
AdministratorandClaude Opus 4.8 committed 2026-06-23 20:42:54 +08:00
1 parent 320f2ef814
commit df9c59e21c
35 files changed
+1569 -224

No files matched your search

@@ -8,6 +8,7 @@
import { apiFetch } from "@/core/api/fetch-client";
import { getBackendBaseURL } from "@/core/config";
import { OptType, writeYLog } from "@/core/log/y-log";
export interface BusinessMapping {
businessCode: string;
@@ -63,7 +64,14 @@ async function readJson<T>(res: Response, ctx: string): Promise<T> {
export async function listBusinessMappings(): Promise<BusinessMapping[]> {
const res = await apiFetch(BASE(), { method: "GET" });
const data = await readJson<{ mappings: ServerMapping[] }>(res, "list business mappings");
return (data.mappings ?? []).map(toMapping);
const mappings = (data.mappings ?? []).map(toMapping);
writeYLog({
optType: OptType.search,
optDetail: "B1-CM助手-业务映射-列表",
url: "/api/business-mapping",
returnParam: mappings,
});
return mappings;
}
/** 更新某业务行的智能体 / 业务链选择。404 返回 null。 */
@@ -83,5 +91,13 @@ export async function updateBusinessMapping(
});
if (res.status === 404) return null;
const data = await readJson<ServerMapping>(res, "update business mapping");
return toMapping(data);
const mapping = toMapping(data);
writeYLog({
optType: OptType.edit,
optDetail: "B1-CM助手-业务映射-修改",
url: `${BASE()}/${encodeURIComponent(businessCode)}`,
reqParam: { businessCode, patch },
returnParam: mapping,
});
return mapping;
}
@@ -170,7 +170,15 @@ export async function listLightApps(params: ListLightAppsParams = {}): Promise<L
export async function getLightApp(appId: string): Promise<LightApp> {
const res = await apiFetch(`${BASE()}/${encodeURIComponent(appId)}`, { method: "GET" });
return toApp(await readJson<ServerApp>(res, "get light app"));
const app = toApp(await readJson<ServerApp>(res, "get light app"));
writeYLog({
optType: OptType.view,
optDetail: "B1-CM助手-轻应用-查看",
url: `${BASE()}/${encodeURIComponent(appId)}`,
reqParam: { appId },
returnParam: app,
});
return app;
}
export async function createLightApp(input: LightAppInput): Promise<LightApp> {
@@ -242,7 +242,15 @@ export async function previewReplacements(replacements: Replacement[]): Promise<
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ replacements }),
});
return toPreviewResult(await readJson<any>(res, "预览"));
const result = toPreviewResult(await readJson<any>(res, "预览"));
writeYLog({
optType: OptType.view,
optDetail: "B1-CM助手-技能地址管理-预览",
url: `${BASE()}/preview`,
reqParam: { replacements },
returnParam: result,
});
return result;
}
/** 流式应用:逐文件上报进度,返回最终结果。`expectedTotal` 做乐观锁。 */
@@ -280,13 +288,20 @@ export async function applyReplacementsStream(
export async function listBackups(): Promise<BackupEntry[]> {
const res = await apiFetch(`${BASE()}/backups`, { method: "GET" });
const data = await readJson<{ backups: any[] }>(res, "读取备份");
return (data.backups ?? []).map((b) => ({
const backups = (data.backups ?? []).map((b) => ({
backupId: b.backup_id,
files: b.files ?? 0,
createdAt: b.created_at ?? "",
replacedTotal: b.replaced_total ?? 0,
replacements: (b.replacements ?? []).map((r: any) => ({ from: r.from, to: r.to, kind: r.kind })),
}));
writeYLog({
optType: OptType.search,
optDetail: "B1-CM助手-技能地址管理-修改记录",
url: `${BASE()}/backups`,
returnParam: backups,
});
return backups;
}
export async function rollbackBackup(backupId: string): Promise<{ restored: number }> {