fix(vibe): keep playback advancing with full plans
This commit is contained in:
@@ -55,6 +55,7 @@ describe('DbService v2 methods', () => {
|
||||
expect(clientQuery.mock.calls[4][0]).toContain('INSERT INTO evidence');
|
||||
expect(clientQuery.mock.calls[8][0]).toContain('exploration_coefficient');
|
||||
expect(clientQuery.mock.calls[8][0]).toContain('ELSE goals END');
|
||||
expect(clientQuery.mock.calls[8][0]).toContain('ELSE $3::real END');
|
||||
expect(clientQuery.mock.calls[9][1][4]).toBe(JSON.stringify({ type: 'familiar', target: 1, progress: 1 }));
|
||||
expect(clientQuery.mock.calls.filter(([sql]) => String(sql).includes('INSERT INTO evidence'))).toHaveLength(1);
|
||||
});
|
||||
|
||||
@@ -1664,12 +1664,12 @@ export class DbService {
|
||||
|
||||
const profile = await client.query(
|
||||
`UPDATE vibe_session_profiles
|
||||
SET exploration_coefficient = GREATEST(0, LEAST(1, exploration_coefficient + CASE WHEN $4 THEN 0 ELSE $3 END)),
|
||||
discovery_radius = GREATEST(0.15, LEAST(0.9, 0.2 + (GREATEST(0, LEAST(1, exploration_coefficient + CASE WHEN $4 THEN 0 ELSE $3 END)) * 0.65))),
|
||||
SET exploration_coefficient = GREATEST(0, LEAST(1, exploration_coefficient + CASE WHEN $4 THEN 0::real ELSE $3::real END)),
|
||||
discovery_radius = GREATEST(0.15, LEAST(0.9, 0.2 + (GREATEST(0, LEAST(1, exploration_coefficient + CASE WHEN $4 THEN 0::real ELSE $3::real END)) * 0.65))),
|
||||
goals = CASE
|
||||
WHEN goals->>'type' = 'familiar' AND $4 AND $5
|
||||
THEN jsonb_set(goals, '{progress}', to_jsonb(LEAST(COALESCE((goals->>'progress')::int, 0) + 1, COALESCE((goals->>'target')::int, 1))))
|
||||
WHEN goals->>'type' IN ('discovery', 'surprise', 'artist_introduction') AND NOT $4 AND $3 > 0
|
||||
WHEN goals->>'type' IN ('discovery', 'surprise', 'artist_introduction') AND NOT $4 AND $3::real > 0
|
||||
THEN jsonb_set(goals, '{progress}', to_jsonb(LEAST(COALESCE((goals->>'progress')::int, 0) + 1, COALESCE((goals->>'target')::int, 1))))
|
||||
ELSE goals END,
|
||||
updated_at = NOW()
|
||||
|
||||
@@ -60,6 +60,7 @@ export interface GeneratorContext {
|
||||
export type Generator = (db: DbService, ctx: GeneratorContext) => Promise<Candidate[]>;
|
||||
|
||||
const OBJECTIVE_USER = '00000000-0000-0000-0000-000000000000';
|
||||
const FALLBACK_CANDIDATE_LIMIT = 60;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. COMFORT — Top artists by longterm affinity > 0.5
|
||||
@@ -536,6 +537,33 @@ async function contextualGenerator(db: DbService, ctx: GeneratorContext): Promis
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 9. LIBRARY FALLBACK — keeps a Vibe usable when the metadata graph is sparse
|
||||
// ---------------------------------------------------------------------------
|
||||
async function libraryFallbackGenerator(db: DbService, ctx: GeneratorContext): Promise<Candidate[]> {
|
||||
const res = await db.pgClient.query(
|
||||
`SELECT t.id
|
||||
FROM tracks t
|
||||
WHERE t.state = 'LIBRARY'
|
||||
AND NOT (t.id = ANY($1::uuid[]))
|
||||
ORDER BY RANDOM()
|
||||
LIMIT $2`,
|
||||
[ctx.recentExclusions, FALLBACK_CANDIDATE_LIMIT],
|
||||
);
|
||||
|
||||
return (res.rows as { id: string }[]).map(row => ({
|
||||
trackId: row.id,
|
||||
generatorId: 'library-fallback',
|
||||
relevance: 0.05,
|
||||
explanation: [{
|
||||
subjectType: 'track', subjectId: row.id,
|
||||
predicate: 'library_fallback',
|
||||
objectType: 'track', objectId: row.id,
|
||||
fusedValue: 0.05,
|
||||
}],
|
||||
}));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// All generators, ordered by priority (comfort first, experimental last)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -548,4 +576,5 @@ export const ALL_GENERATORS: Generator[] = [
|
||||
noveltyGenerator,
|
||||
contextualGenerator,
|
||||
experimentalGenerator,
|
||||
libraryFallbackGenerator,
|
||||
];
|
||||
|
||||
@@ -199,8 +199,8 @@ describe('generators', () => {
|
||||
});
|
||||
|
||||
describe('ALL_GENERATORS', () => {
|
||||
it('contains 8 generators', () => {
|
||||
expect(ALL_GENERATORS).toHaveLength(8);
|
||||
it('contains 9 generators', () => {
|
||||
expect(ALL_GENERATORS).toHaveLength(9);
|
||||
ALL_GENERATORS.forEach(g => expect(typeof g).toBe('function'));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user