fix vibe engine audit findings: pg.Pool, plan replan, dead exclusions, legacy engine removal
Backend: - app.ts: switch shared pg.Client to pg.Pool with per-transaction clients (#205) - v2.routes.ts: replace plan instead of appending on replan, fixing self-duplication (#206) - session-director: populate recentExclusions, per-candidate ranking, batch repetition checks (#209/#211/#213/#215 + minor) - db.service.ts: claim-fusion watermark, legacy recommendation_batch engine removed (#216/#219/#232) - app.ts: drop test enqueue-job endpoint (#234) Frontend: - AudioEngine/Vibe/usePlaybackStore: dedupe completed feedback, gate feedback to vibe sessions, End Vibe stops playback, Keep toast, shuffle played-set (#207/#236/#237/#238/#239/#240) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -178,7 +178,7 @@ async function discoveryGenerator(db: DbService, ctx: GeneratorContext): Promise
|
||||
const maxCandidates = Math.max(1, Math.floor(10 * noveltyTolerance));
|
||||
|
||||
const unfamiliarRes = await db.pgClient.query(
|
||||
`SELECT DISTINCT cf.object_id AS artist_id
|
||||
`SELECT cf.object_id AS artist_id, MAX(cf.fused_value) AS edge_strength
|
||||
FROM claim_fusion cf
|
||||
WHERE cf.subject_type = 'artist'
|
||||
AND cf.subject_id = ANY($1::uuid[])
|
||||
@@ -191,16 +191,19 @@ async function discoveryGenerator(db: DbService, ctx: GeneratorContext): Promise
|
||||
AND lb.entity_id = cf.object_id
|
||||
AND lb.profile IN ('longterm', 'obsession')
|
||||
)
|
||||
GROUP BY cf.object_id
|
||||
LIMIT 30`,
|
||||
[trustedIds, ctx.userId]
|
||||
);
|
||||
|
||||
const unfamiliarArtistIds = (unfamiliarRes.rows as { artist_id: string }[]).map(r => r.artist_id);
|
||||
const unfamiliarRows = unfamiliarRes.rows as { artist_id: string; edge_strength: number }[];
|
||||
const unfamiliarArtistIds = unfamiliarRows.map(r => r.artist_id);
|
||||
const edgeStrengthMap = new Map(unfamiliarRows.map(r => [r.artist_id, r.edge_strength]));
|
||||
if (unfamiliarArtistIds.length === 0) return [];
|
||||
|
||||
const trackRes = await db.pgClient.query(
|
||||
`SELECT id FROM (
|
||||
SELECT DISTINCT t.id
|
||||
`SELECT id, artist_id FROM (
|
||||
SELECT DISTINCT ON (t.id) t.id, cf.object_id AS artist_id
|
||||
FROM tracks t
|
||||
JOIN claim_fusion cf ON cf.subject_type = 'track' AND cf.subject_id = t.id
|
||||
AND cf.predicate IN ('credited_main_on', 'featured_on')
|
||||
@@ -208,25 +211,29 @@ async function discoveryGenerator(db: DbService, ctx: GeneratorContext): Promise
|
||||
AND cf.object_id = ANY($1::uuid[])
|
||||
WHERE t.state = 'LIBRARY'
|
||||
AND NOT (t.id = ANY($2::uuid[]))
|
||||
ORDER BY t.id, cf.fused_value DESC NULLS LAST
|
||||
) sub
|
||||
ORDER BY RANDOM()
|
||||
LIMIT $3`,
|
||||
[unfamiliarArtistIds, ctx.recentExclusions, maxCandidates]
|
||||
);
|
||||
|
||||
return (trackRes.rows as { id: string }[]).map(row => ({
|
||||
trackId: row.id,
|
||||
generatorId: 'discovery',
|
||||
explanation: [{
|
||||
subjectType: 'artist',
|
||||
subjectId: unfamiliarArtistIds[0],
|
||||
predicate: 'credited_main_on',
|
||||
objectType: 'track',
|
||||
objectId: row.id,
|
||||
fusedValue: 0.4,
|
||||
}],
|
||||
relevance: 0.4,
|
||||
}));
|
||||
return (trackRes.rows as { id: string; artist_id: string }[]).map(row => {
|
||||
const relevance = edgeStrengthMap.get(row.artist_id) ?? 0.4;
|
||||
return {
|
||||
trackId: row.id,
|
||||
generatorId: 'discovery',
|
||||
explanation: [{
|
||||
subjectType: 'artist',
|
||||
subjectId: row.artist_id,
|
||||
predicate: 'credited_main_on',
|
||||
objectType: 'track',
|
||||
objectId: row.id,
|
||||
fusedValue: relevance,
|
||||
}],
|
||||
relevance,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -253,6 +260,11 @@ async function deepDiveGenerator(db: DbService, ctx: GeneratorContext): Promise<
|
||||
|
||||
const albumIds = albumRows.map(a => a.album_id);
|
||||
const albumArtistMap = new Map(albumRows.map(a => [a.album_id, a.artist_id]));
|
||||
const obsessionValueMap = new Map(
|
||||
ctx.beliefs
|
||||
.filter(b => b.profile === 'obsession' && b.entity_type === 'artist' && b.dimension === 'affinity' && b.value > 0.3)
|
||||
.map(b => [b.entity_id, b.value])
|
||||
);
|
||||
|
||||
const trackRes = await db.pgClient.query(
|
||||
`SELECT sub.id, sub.album_id
|
||||
@@ -269,19 +281,23 @@ async function deepDiveGenerator(db: DbService, ctx: GeneratorContext): Promise<
|
||||
[albumIds, ctx.recentExclusions]
|
||||
);
|
||||
|
||||
return (trackRes.rows as { id: string; album_id: string }[]).map(row => ({
|
||||
trackId: row.id,
|
||||
generatorId: 'deep-dive',
|
||||
explanation: [{
|
||||
subjectType: 'artist',
|
||||
subjectId: albumArtistMap.get(row.album_id) ?? 'unknown',
|
||||
predicate: 'credited_main_on',
|
||||
objectType: 'track',
|
||||
objectId: row.id,
|
||||
fusedValue: 0.7,
|
||||
}],
|
||||
relevance: 0.7,
|
||||
}));
|
||||
return (trackRes.rows as { id: string; album_id: string }[]).map(row => {
|
||||
const artistId = albumArtistMap.get(row.album_id) ?? 'unknown';
|
||||
const relevance = obsessionValueMap.get(artistId) ?? 0.7;
|
||||
return {
|
||||
trackId: row.id,
|
||||
generatorId: 'deep-dive',
|
||||
explanation: [{
|
||||
subjectType: 'artist',
|
||||
subjectId: artistId,
|
||||
predicate: 'credited_main_on',
|
||||
objectType: 'track',
|
||||
objectId: row.id,
|
||||
fusedValue: relevance,
|
||||
}],
|
||||
relevance,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -306,10 +322,11 @@ async function revivalGenerator(db: DbService, ctx: GeneratorContext): Promise<C
|
||||
if (staleArtists.length === 0) return [];
|
||||
|
||||
const staleArtistIds = staleArtists.map(a => a.artist_id);
|
||||
const affinityMap = new Map(staleArtists.map(a => [a.artist_id, a.affinity]));
|
||||
|
||||
const trackRes = await db.pgClient.query(
|
||||
`SELECT id FROM (
|
||||
SELECT DISTINCT t.id
|
||||
`SELECT id, artist_id FROM (
|
||||
SELECT DISTINCT ON (t.id) t.id, cf.object_id AS artist_id
|
||||
FROM tracks t
|
||||
JOIN claim_fusion cf ON cf.subject_type = 'track' AND cf.subject_id = t.id
|
||||
AND cf.predicate IN ('credited_main_on', 'featured_on')
|
||||
@@ -318,25 +335,29 @@ async function revivalGenerator(db: DbService, ctx: GeneratorContext): Promise<C
|
||||
AND (cf.user_id = $2 OR cf.user_id = $3)
|
||||
WHERE t.state = 'LIBRARY'
|
||||
AND NOT (t.id = ANY($4::uuid[]))
|
||||
ORDER BY t.id, cf.fused_value DESC NULLS LAST
|
||||
) sub
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 20`,
|
||||
[staleArtistIds, OBJECTIVE_USER, ctx.userId, ctx.recentExclusions]
|
||||
);
|
||||
|
||||
return (trackRes.rows as { id: string }[]).map(row => ({
|
||||
trackId: row.id,
|
||||
generatorId: 'revival',
|
||||
explanation: [{
|
||||
subjectType: 'artist',
|
||||
subjectId: staleArtistIds[0],
|
||||
predicate: 'credited_main_on',
|
||||
objectType: 'track',
|
||||
objectId: row.id,
|
||||
fusedValue: 0.6,
|
||||
}],
|
||||
relevance: 0.6,
|
||||
}));
|
||||
return (trackRes.rows as { id: string; artist_id: string }[]).map(row => {
|
||||
const relevance = affinityMap.get(row.artist_id) ?? 0.6;
|
||||
return {
|
||||
trackId: row.id,
|
||||
generatorId: 'revival',
|
||||
explanation: [{
|
||||
subjectType: 'artist',
|
||||
subjectId: row.artist_id,
|
||||
predicate: 'credited_main_on',
|
||||
objectType: 'track',
|
||||
objectId: row.id,
|
||||
fusedValue: relevance,
|
||||
}],
|
||||
relevance,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -455,15 +476,15 @@ async function contextualGenerator(db: DbService, ctx: GeneratorContext): Promis
|
||||
order: 'DESC',
|
||||
});
|
||||
|
||||
const targetArtistIds = contextualBeliefs
|
||||
.filter(b => b.entity_type === 'artist' && b.value > 0.2)
|
||||
.map(b => b.entity_id);
|
||||
const targetBeliefs = contextualBeliefs.filter(b => b.entity_type === 'artist' && b.value > 0.2);
|
||||
const targetArtistIds = targetBeliefs.map(b => b.entity_id);
|
||||
const targetValueMap = new Map(targetBeliefs.map(b => [b.entity_id, b.value]));
|
||||
|
||||
if (targetArtistIds.length === 0) return [];
|
||||
|
||||
const trackRes = await db.pgClient.query(
|
||||
`SELECT id FROM (
|
||||
SELECT DISTINCT t.id
|
||||
`SELECT id, artist_id FROM (
|
||||
SELECT DISTINCT ON (t.id) t.id, cf.object_id AS artist_id
|
||||
FROM tracks t
|
||||
JOIN claim_fusion cf ON cf.subject_type = 'track' AND cf.subject_id = t.id
|
||||
AND cf.predicate IN ('credited_main_on', 'featured_on')
|
||||
@@ -472,25 +493,29 @@ async function contextualGenerator(db: DbService, ctx: GeneratorContext): Promis
|
||||
AND (cf.user_id = $2 OR cf.user_id = $3)
|
||||
WHERE t.state = 'LIBRARY'
|
||||
AND NOT (t.id = ANY($4::uuid[]))
|
||||
ORDER BY t.id, cf.fused_value DESC NULLS LAST
|
||||
) sub
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 15`,
|
||||
[targetArtistIds, OBJECTIVE_USER, ctx.userId, ctx.recentExclusions]
|
||||
);
|
||||
|
||||
return (trackRes.rows as { id: string }[]).map(row => ({
|
||||
trackId: row.id,
|
||||
generatorId: 'contextual',
|
||||
explanation: [{
|
||||
subjectType: 'artist',
|
||||
subjectId: targetArtistIds[0],
|
||||
predicate: 'credited_main_on',
|
||||
objectType: 'track',
|
||||
objectId: row.id,
|
||||
fusedValue: 0.5,
|
||||
}],
|
||||
relevance: 0.5,
|
||||
}));
|
||||
return (trackRes.rows as { id: string; artist_id: string }[]).map(row => {
|
||||
const relevance = targetValueMap.get(row.artist_id) ?? 0.5;
|
||||
return {
|
||||
trackId: row.id,
|
||||
generatorId: 'contextual',
|
||||
explanation: [{
|
||||
subjectType: 'artist',
|
||||
subjectId: row.artist_id,
|
||||
predicate: 'credited_main_on',
|
||||
objectType: 'track',
|
||||
objectId: row.id,
|
||||
fusedValue: relevance,
|
||||
}],
|
||||
relevance,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user