Add multi-session auth and changelog tracking

这个提交包含在:
cryptocommuniums-afk
2026-03-15 17:30:19 +08:00
父节点 c4ec397ed3
当前提交 a9ea94fb78
修改 27 个文件,包含 1280 行新增89 行删除

查看文件

@@ -34,8 +34,13 @@ type StructuredParams<T> = {
};
};
parse: (content: unknown) => T;
timeoutMs?: number;
retryCount?: number;
};
const TRAINING_PLAN_LLM_TIMEOUT_MS = Math.max(ENV.llmTimeoutMs, 120_000);
const TRAINING_PLAN_LLM_RETRY_COUNT = Math.max(ENV.llmRetryCount, 2);
async function invokeStructured<T>(params: StructuredParams<T>) {
let lastError: unknown;
@@ -56,6 +61,8 @@ async function invokeStructured<T>(params: StructuredParams<T>) {
model: params.model,
messages: [...params.baseMessages, ...retryHint],
response_format: params.responseFormat,
timeoutMs: params.timeoutMs,
retryCount: params.retryCount,
});
try {
@@ -136,6 +143,17 @@ async function runTrainingPlanGenerateTask(task: NonNullable<TaskRow>) {
durationDays: number;
focusAreas?: string[];
};
const user = await db.getUserById(task.userId);
if (!user) {
throw new Error("User not found");
}
const latestSnapshot = await db.getLatestNtrpSnapshot(task.userId);
const trainingProfileStatus = db.getTrainingProfileStatus(user, latestSnapshot);
if (!trainingProfileStatus.isComplete) {
const missingLabels = trainingProfileStatus.missingFields.map((field) => db.TRAINING_PROFILE_FIELD_LABELS[field]).join("、");
throw new Error(`训练计划生成前请先完善训练档案:${missingLabels}`);
}
const analyses = await db.getUserAnalyses(task.userId);
const recentScores = analyses.slice(0, 5).map((analysis) => ({
score: analysis.overallScore ?? null,
@@ -154,6 +172,9 @@ async function runTrainingPlanGenerateTask(task: NonNullable<TaskRow>) {
content: buildTrainingPlanPrompt({
...payload,
recentScores,
effectiveNtrpRating: trainingProfileStatus.effectiveNtrp,
ntrpSource: trainingProfileStatus.ntrpSource,
assessmentSnapshot: trainingProfileStatus.assessmentSnapshot,
}),
},
],
@@ -194,6 +215,8 @@ async function runTrainingPlanGenerateTask(task: NonNullable<TaskRow>) {
content,
fallbackTitle: `${payload.durationDays}天训练计划`,
}),
timeoutMs: TRAINING_PLAN_LLM_TIMEOUT_MS,
retryCount: TRAINING_PLAN_LLM_RETRY_COUNT,
});
const planId = await db.createTrainingPlan({
@@ -280,6 +303,8 @@ async function runTrainingPlanAdjustTask(task: NonNullable<TaskRow>) {
content,
fallbackTitle: currentPlan.title,
}),
timeoutMs: TRAINING_PLAN_LLM_TIMEOUT_MS,
retryCount: TRAINING_PLAN_LLM_RETRY_COUNT,
});
await db.updateTrainingPlan(payload.planId, {
@@ -418,6 +443,11 @@ async function runMediaFinalizeTask(task: NonNullable<TaskRow>) {
exerciseType?: string;
sessionMode?: "practice" | "pk";
durationMinutes?: number;
actionCount?: number;
actionSummary?: Record<string, number>;
dominantAction?: string;
validityStatus?: string;
invalidReason?: string;
};
const session = await getRemoteMediaSession(payload.sessionId);
@@ -495,6 +525,11 @@ async function runMediaFinalizeTask(task: NonNullable<TaskRow>) {
title: payload.title || session.title,
sessionMode: payload.sessionMode || "practice",
durationMinutes: payload.durationMinutes ?? 5,
actionCount: payload.actionCount ?? 0,
actionSummary: payload.actionSummary ?? {},
dominantAction: payload.dominantAction ?? null,
validityStatus: payload.validityStatus ?? "pending",
invalidReason: payload.invalidReason ?? null,
});
return {