feat(clustering): 1h TTL + skip recompute when tasks unchanged
focus-area now recomputes at most once per hour, and only if the task list actually changed since the last compute. - focus-area TTL: 43200s → 3600s; version bumped to 2.1.0 - computeAndStore hashes sorted task contents (MD5) and checks the stored _task_hash in the existing snapshot; skips the ml-serving call when the hash matches and the output isn't expired - ml-serving injects _task_hash into the snapshot so the next cycle can compare Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,31 @@ async function persistInferredPrefs(
|
||||
}
|
||||
}
|
||||
|
||||
function taskListHash(tasks: { content?: string }[]): string {
|
||||
const sorted = tasks
|
||||
.map((t) => t.content?.trim() ?? '')
|
||||
.filter(Boolean)
|
||||
.sort()
|
||||
.join('\n');
|
||||
return crypto.createHash('md5').update(sorted).digest('hex');
|
||||
}
|
||||
|
||||
async function isUpToDate(userId: string, agentId: string, currentHash: string): Promise<boolean> {
|
||||
const now = new Date().toISOString();
|
||||
const rows = await db
|
||||
.select({ signalsSnapshot: agentOutputs.signalsSnapshot, expiresAt: agentOutputs.expiresAt })
|
||||
.from(agentOutputs)
|
||||
.where(and(eq(agentOutputs.userId, userId), eq(agentOutputs.agentId, agentId)))
|
||||
.limit(1);
|
||||
if (!rows.length) return false;
|
||||
const row = rows[0];
|
||||
if (row.expiresAt <= now) return false;
|
||||
try {
|
||||
const snapshot = JSON.parse(row.signalsSnapshot ?? '{}') as { _task_hash?: string };
|
||||
return snapshot._task_hash === currentHash;
|
||||
} catch { return false; }
|
||||
}
|
||||
|
||||
export async function computeAndStore(userId: string, agentId: string): Promise<void> {
|
||||
let tasks: object[] = [];
|
||||
try {
|
||||
@@ -176,6 +201,9 @@ export async function computeAndStore(userId: string, agentId: string): Promise<
|
||||
// No integration or fetch error — agents that need tasks will report "no tasks"
|
||||
}
|
||||
|
||||
const currentTaskHash = taskListHash(tasks as { content?: string }[]);
|
||||
if (await isUpToDate(userId, agentId, currentTaskHash)) return;
|
||||
|
||||
let profile: Profile = {};
|
||||
try {
|
||||
profile = await getProfile(userId);
|
||||
@@ -202,7 +230,7 @@ export async function computeAndStore(userId: string, agentId: string): Promise<
|
||||
const mlResp = await fetch(`${config.ML_SERVING_URL}/agents/${agentId}/compute`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ user_id: userId, tasks, profile, feedback_history: feedbackHistory, agent_prefs: agentPrefs, enrichment_cache: enrichmentCache }),
|
||||
body: JSON.stringify({ user_id: userId, tasks, profile, feedback_history: feedbackHistory, agent_prefs: agentPrefs, enrichment_cache: enrichmentCache, task_hash: currentTaskHash }),
|
||||
signal: AbortSignal.timeout(60_000),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user