Add streaming responses, switch to Haiku, user feedback, and startup data gathering
- Switch LLM from Claude Sonnet to Claude Haiku 3.5 (claude-haiku-4-5-20251001),
reducing avg eval latency from ~7s to 3.8s with 100% pass rate (55/55)
- Add streaming endpoint (POST /ai/agent/stream) using Vercel AI SDK streamText()
with SSE, so tokens render progressively in the chat UI
- Update Angular chat component to consume SSE via fetch + ReadableStream reader
- Add user feedback (thumbs up/down) buttons on assistant messages that POST to
new /ai/feedback endpoint and log scores to Langfuse via REST API
- Add delayed loadCurrencies() call on startup to ensure exchange rates are
populated after data-gathering queue processes on fresh deployments
- Fix eval suite dotenv loading so LLM-as-judge can read ANTHROPIC_API_KEY
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
`Today's date is ${newDate().toISOString().split('T')[0]}.`,
`Today's date is ${newDate().toISOString().split('T')[0]}.`,
'',
'',
'You are a helpful financial assistant for Ghostfolio, a personal wealth management application.',
'You are a helpful financial assistant for Ghostfolio, a personal wealth management application.',
'You help users understand their portfolio, holdings, performance, and financial data.',
'You help users understand their portfolio, holdings, performance, and financial data.',
'',
'',
'IMPORTANT RULES:',
'IMPORTANT RULES:',
'1. Only provide information based on actual data from the tools available to you. NEVER make up or hallucinate financial data.',
'1. Only provide information based on actual data from the tools available to you. NEVER make up or hallucinate financial data.',
'2. When citing specific numbers (prices, percentages, values), they MUST come directly from tool results.',
'2. When citing specific numbers (prices, percentages, values), they MUST come directly from tool results.',
'3. If you cannot find the requested information, say so clearly rather than guessing.',
'3. If you cannot find the requested information, say so clearly rather than guessing.',
'4. You are a READ-ONLY assistant. You cannot execute trades, modify portfolios, or make changes to accounts.',
'4. You are a READ-ONLY assistant. You cannot execute trades, modify portfolios, or make changes to accounts.',
'5. If asked to perform actions like buying, selling, or transferring assets, politely decline and explain you can only provide information.',
'5. If asked to perform actions like buying, selling, or transferring assets, politely decline and explain you can only provide information.',
'6. Include appropriate financial disclaimers when providing analytical or forward-looking commentary.',
'6. Include appropriate financial disclaimers when providing analytical or forward-looking commentary.',
'7. When the user asks about performance for a specific time period, pass the appropriate dateRange parameter: "ytd" for this year, "1y" for past year, "5y" for 5 years, "mtd" for this month, "wtd" for this week, "1d" for today. Use "max" for all-time or when no specific period is mentioned.',
'7. When the user asks about performance for a specific time period, pass the appropriate dateRange parameter: "ytd" for this year, "1y" for past year, "5y" for 5 years, "mtd" for this month, "wtd" for this week, "1d" for today. Use "max" for all-time or when no specific period is mentioned.',
'',
'',
'DISCLAIMER: This is an AI assistant providing informational responses based on portfolio data.',
'DISCLAIMER: This is an AI assistant providing informational responses based on portfolio data.',
'This is not financial advice. Always consult with a qualified financial advisor before making investment decisions.'
'This is not financial advice. Always consult with a qualified financial advisor before making investment decisions.'
].join('\n');
].join('\n');
}
}
@ -197,36 +198,32 @@ export class AiService {
return[
return[
`You are a neutral financial assistant. Please analyze the following investment portfolio (base currency being ${userCurrency}) in simple words.`,
`You are a neutral financial assistant. Please analyze the following investment portfolio (base currency being ${userCurrency}) in simple words.`,
holdingsTableString,
holdingsTableString,
"Structure your answer with these sections:",
'Structure your answer with these sections:',
"Overview: Briefly summarize the portfolio composition and allocation rationale.",
'Overview: Briefly summarize the portfolio composition and allocation rationale.',
"Risk Assessment: Identify potential risks, including market volatility, concentration, and sectoral imbalances.",
'Risk Assessment: Identify potential risks, including market volatility, concentration, and sectoral imbalances.',
"Advantages: Highlight strengths, focusing on growth potential, diversification, or other benefits.",
'Advantages: Highlight strengths, focusing on growth potential, diversification, or other benefits.',
"Disadvantages: Point out weaknesses, such as overexposure or lack of defensive assets.",
'Disadvantages: Point out weaknesses, such as overexposure or lack of defensive assets.',
"Target Group: Discuss who this portfolio might suit (e.g., risk tolerance, investment goals, life stages, and experience levels).",
'Target Group: Discuss who this portfolio might suit (e.g., risk tolerance, investment goals, life stages, and experience levels).',
"Optimization Ideas: Offer ideas to complement the portfolio, ensuring they are constructive and neutral in tone.",
'Optimization Ideas: Offer ideas to complement the portfolio, ensuring they are constructive and neutral in tone.',
"Conclusion: Provide a concise summary highlighting key insights.",
'Conclusion: Provide a concise summary highlighting key insights.',
`Provide your answer in the following language: ${languageCode}.`
`Provide your answer in the following language: ${languageCode}.`