From de0620ecef5e4de1de741f32d0bd739d804f9d7d Mon Sep 17 00:00:00 2001 From: capJavert Date: Wed, 8 Jul 2026 10:49:37 +0000 Subject: [PATCH] fix: resolve daily personalise state via snotra user API FeedDailyConfigGenerator was wired with the snotra memory API client, whose origin does not serve /api/v1/user/profile and returns 404 on every call. The personalise state therefore always resolved to undefined, the generator always fell back to daily_v1, and cold-start users (no sauron embeddings) got an empty daily feed while daily_cs_v1 was never selected. Use a client pointed at SNOTRA_USER_API_ORIGIN, matching what the personalized digest flow already does for the same endpoint. --- __tests__/feeds.ts | 8 ++++---- src/integrations/feed/generators.ts | 4 ++-- src/integrations/snotra/clients.ts | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/__tests__/feeds.ts b/__tests__/feeds.ts index 7d47d67678..fefd5c7f99 100644 --- a/__tests__/feeds.ts +++ b/__tests__/feeds.ts @@ -36,7 +36,7 @@ import { } from '../src/entity'; import { PollOption } from '../src/entity/polls/PollOption'; import { SourceMemberRoles } from '../src/roles'; -import { snotraClient } from '../src/integrations/snotra/clients'; +import { snotraUserApiClient } from '../src/integrations/snotra/clients'; import { PersonaliseState } from '../src/integrations/snotra/types'; import { Category } from '../src/entity/Category'; import { Persona } from '../src/entity/Persona'; @@ -1578,7 +1578,7 @@ describe('query dailyFeed', () => { loggedUser = '1'; await saveFeedFixtures(); - jest.spyOn(snotraClient, 'getUserProfile').mockResolvedValue({ + jest.spyOn(snotraUserApiClient, 'getUserProfile').mockResolvedValue({ personalise: { state: PersonaliseState.Personalised }, }); nock('http://localhost:6000') @@ -1605,7 +1605,7 @@ describe('query dailyFeed', () => { loggedUser = '1'; await saveFeedFixtures(); - jest.spyOn(snotraClient, 'getUserProfile').mockResolvedValue({ + jest.spyOn(snotraUserApiClient, 'getUserProfile').mockResolvedValue({ personalise: { state: PersonaliseState.NonPersonalised }, }); nock('http://localhost:6000') @@ -1630,7 +1630,7 @@ describe('query dailyFeed', () => { loggedUser = '1'; await saveFeedFixtures(); - jest.spyOn(snotraClient, 'getUserProfile').mockResolvedValue({ + jest.spyOn(snotraUserApiClient, 'getUserProfile').mockResolvedValue({ personalise: { state: PersonaliseState.Personalised }, }); // Only one feed-service mock: a second call would have no nock match and fail. diff --git a/src/integrations/feed/generators.ts b/src/integrations/feed/generators.ts index 219f95c50f..cee58794d9 100644 --- a/src/integrations/feed/generators.ts +++ b/src/integrations/feed/generators.ts @@ -18,7 +18,7 @@ import { SimpleFeedConfigGenerator, } from './configs'; import { LofnClient } from '../lofn'; -import { snotraClient } from '../snotra/clients'; +import { snotraUserApiClient } from '../snotra/clients'; import { GarmrService } from '../garmr'; import { FeedOrderBy } from '../../entity/Feed'; import { postTypes } from '../../entity/posts/Post'; @@ -131,7 +131,7 @@ export const getForYouByTagFeedGenerator = (tags: string[]): FeedGenerator => export const dailyFeedConfigGenerator = new FeedDailyConfigGenerator( baseFeedConfig, - snotraClient, + snotraUserApiClient, opts, ); diff --git a/src/integrations/snotra/clients.ts b/src/integrations/snotra/clients.ts index b3795d0c42..310fc46ddc 100644 --- a/src/integrations/snotra/clients.ts +++ b/src/integrations/snotra/clients.ts @@ -90,3 +90,19 @@ const garmrSnotraService = new GarmrService({ export const snotraClient = new SnotraClient(process.env.SNOTRA_ORIGIN!, { garmr: garmrSnotraService, }); + +const garmrSnotraUserApiService = new GarmrService({ + service: 'snotra-user-api', + breakerOpts: { + halfOpenAfter: 5 * 1000, + threshold: 0.1, + duration: 10 * 1000, + }, +}); + +export const snotraUserApiClient = new SnotraClient( + process.env.SNOTRA_USER_API_ORIGIN!, + { + garmr: garmrSnotraUserApiService, + }, +);