Add multi-session auth and changelog tracking
这个提交包含在:
@@ -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 {
|
||||
|
||||
在新工单中引用
屏蔽一个用户