Browse Source

fix(ai): add actionable fallback for diversify prompts

pull/6395/head
Max P 1 month ago
parent
commit
5dc6d31921
  1. 34
      apps/api/src/app/endpoints/ai/ai-agent.chat.helpers.spec.ts
  2. 4
      apps/api/src/app/endpoints/ai/ai-agent.chat.helpers.ts
  3. 46
      apps/api/src/app/endpoints/ai/ai.service.spec.ts

34
apps/api/src/app/endpoints/ai/ai-agent.chat.helpers.spec.ts

@ -73,6 +73,40 @@ describe('AiAgentChatHelpers', () => {
expect(answer).toBe(generatedText); expect(answer).toBe(generatedText);
}); });
it('adds deterministic diversification action guidance when generated answer is unreliable', async () => {
const answer = await buildAnswer({
generateText: jest.fn().mockResolvedValue({
text: 'Diversify.'
}),
languageCode: 'en',
memory: { turns: [] },
portfolioAnalysis: {
allocationSum: 1,
holdings: [
{
allocationInPercentage: 0.7,
dataSource: DataSource.YAHOO,
symbol: 'AAPL',
valueInBaseCurrency: 7000
},
{
allocationInPercentage: 0.3,
dataSource: DataSource.YAHOO,
symbol: 'MSFT',
valueInBaseCurrency: 3000
}
],
holdingsCount: 2,
totalValueInBaseCurrency: 10000
},
query: 'help me diversify',
userCurrency: 'USD'
});
expect(answer).toContain('Next-step allocation:');
expect(answer).toContain('AAPL');
});
it('parses and persists concise response-style preference updates', () => { it('parses and persists concise response-style preference updates', () => {
const result = resolvePreferenceUpdate({ const result = resolvePreferenceUpdate({
query: 'Remember to keep responses concise.', query: 'Remember to keep responses concise.',

4
apps/api/src/app/endpoints/ai/ai-agent.chat.helpers.ts

@ -194,6 +194,8 @@ export async function buildAnswer({
'add', 'add',
'allocat', 'allocat',
'buy', 'buy',
'deconcentrat',
'diversif',
'invest', 'invest',
'next', 'next',
'rebalanc', 'rebalanc',
@ -265,7 +267,7 @@ export async function buildAnswer({
if (topLongShare >= 0.35) { if (topLongShare >= 0.35) {
fallbackSections.push( fallbackSections.push(
'Next-step allocation: direct new capital to positions outside your top holding until concentration falls below 35%.' `Next-step allocation: cap ${longHoldings[0].symbol} contribution and direct new capital to positions outside your top holding until concentration falls below 35% to improve diversification.`
); );
} else { } else {
fallbackSections.push( fallbackSections.push(

46
apps/api/src/app/endpoints/ai/ai.service.spec.ts

@ -430,6 +430,52 @@ describe('AiService', () => {
); );
}); });
it('returns deterministic diversification action guidance when generated output is unreliable', async () => {
portfolioService.getDetails.mockResolvedValue({
holdings: {
AAPL: {
allocationInPercentage: 0.665,
dataSource: DataSource.YAHOO,
symbol: 'AAPL',
valueInBaseCurrency: 6650
},
VTI: {
allocationInPercentage: 0.159,
dataSource: DataSource.YAHOO,
symbol: 'VTI',
valueInBaseCurrency: 1590
},
MSFT: {
allocationInPercentage: 0.085,
dataSource: DataSource.YAHOO,
symbol: 'MSFT',
valueInBaseCurrency: 850
}
}
});
redisCacheService.get.mockResolvedValue(undefined);
jest.spyOn(subject, 'generateText').mockResolvedValue({
text: 'Diversify.'
} as never);
const result = await subject.chat({
languageCode: 'en',
query: 'help me diversify',
sessionId: 'session-diversify-1',
userCurrency: 'USD',
userId: 'user-diversify-1'
});
expect(result.answer).toContain('Next-step allocation:');
expect(result.answer).toContain('AAPL');
expect(result.toolCalls).toEqual(
expect.arrayContaining([
expect.objectContaining({ tool: 'portfolio_analysis' }),
expect.objectContaining({ tool: 'risk_assessment' })
])
);
});
it('returns graceful failure metadata when a tool execution fails', async () => { it('returns graceful failure metadata when a tool execution fails', async () => {
dataProviderService.getQuotes.mockRejectedValue( dataProviderService.getQuotes.mockRejectedValue(
new Error('market provider unavailable') new Error('market provider unavailable')

Loading…
Cancel
Save