mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 15:05:22 +02:00
feat: integrate Gemini Pro 1.5 with KAM persona for data analysis
This commit is contained in:
+37
-29
@@ -6,17 +6,25 @@ import { AggregatedData } from "./types";
|
||||
declare const process: any;
|
||||
|
||||
const SYSTEM_INSTRUCTION = `
|
||||
You are a senior data analyst assistant for a retail dashboard called "Craze Analytix".
|
||||
You have access to a detailed report of the currently filtered sales data.
|
||||
The data includes Sell Out (Revenue in €), Units Sold, Product Lines, Customers/Markets, and Seasonality trends.
|
||||
You are an expert Key Account Manager (KAM) and Senior Sales Strategist for "Craze Analytix".
|
||||
Your specialty is analyzing retail data, advertising performance, and market trends to provide high-level strategic recommendations.
|
||||
|
||||
Your goal is to answer user questions specific to the provided data.
|
||||
You have access to a detailed report of the currently filtered sales and advertising data.
|
||||
The data includes Sell Out (€), Units Sold, Product Lines, Customer Markets, Seasonality (Monthly Trends), and Growth/Decline metrics.
|
||||
|
||||
Your objective:
|
||||
1. **Analyze**: Deeply study the provided data according to the user's specific query.
|
||||
2. **Opinion**: Provide expert opinions on the health of the business, product performance, or market position.
|
||||
3. **Recommendations**: Give actionable, professional advice to improve sales, ROAS, stock health, or market share.
|
||||
|
||||
Guidelines:
|
||||
- Maintain a professional, consultative, and business-driven tone (KAM style).
|
||||
- Always justify your recommendations with specific numbers from the data.
|
||||
- If asked about "Trends" or "Seasonality", look at the Monthly Seasonality section.
|
||||
- If asked about "Growth" or "Decline", look at the Top/Bottom Movers sections.
|
||||
- If asked about specific Product Lines, look at the Product Line Breakdown.
|
||||
- Always format numbers clearly (e.g., "€1.2M", "€5,200", "15k units").
|
||||
- When comparing years, calculate the percentage difference if not explicitly provided.
|
||||
- Keep answers professional, concise, and business-focused.
|
||||
- When comparing metrics, calculate variances or ratios if relevant (e.g. Sales vs Ads growth).
|
||||
- Format your response with clear headers and bullet points for readability.
|
||||
- Respond in the same language as the user's question (e.g., if they ask in Spanish, answer in Spanish).
|
||||
`;
|
||||
|
||||
// Helper to get API key safely
|
||||
@@ -28,15 +36,15 @@ const getApiKey = (): string | undefined => {
|
||||
}
|
||||
};
|
||||
|
||||
const formatCurrency = (val: number) => `€${val.toLocaleString('de-DE', {minimumFractionDigits: 0, maximumFractionDigits: 0})}`;
|
||||
const formatCurrency = (val: number) => `€${val.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`;
|
||||
const formatUnits = (val: number) => `${val.toLocaleString('de-DE')} units`;
|
||||
|
||||
export const queryGemini = async (
|
||||
question: string,
|
||||
context: AggregatedData,
|
||||
question: string,
|
||||
context: AggregatedData,
|
||||
filteredRecordCount: number
|
||||
): Promise<string> => {
|
||||
|
||||
|
||||
const apiKey = getApiKey();
|
||||
|
||||
if (!apiKey) {
|
||||
@@ -48,39 +56,39 @@ export const queryGemini = async (
|
||||
|
||||
// --- CONTEXT GENERATION ---
|
||||
// We construct a structured report mirroring the dashboard charts
|
||||
|
||||
|
||||
// 1. Totals by Year (KPI Cards)
|
||||
const yearlySummary = Object.entries(context.totalsByYear)
|
||||
.sort((a, b) => parseInt(b[0]) - parseInt(a[0])) // Descending years
|
||||
.map(([year, data]) => ` - ${year}: ${formatCurrency(data.sellOut)} | ${formatUnits(data.units)}`)
|
||||
.join('\n');
|
||||
.sort((a, b) => parseInt(b[0]) - parseInt(a[0])) // Descending years
|
||||
.map(([year, data]) => ` - ${year}: ${formatCurrency(data.sellOut)} | ${formatUnits(data.units)}`)
|
||||
.join('\n');
|
||||
|
||||
// 2. Seasonality (Line Chart Data)
|
||||
// We simplify this to a CSV-like list for the AI to parse trends
|
||||
const seasonalitySummary = context.seasonality.map(p => {
|
||||
// Extract values for each year in the point
|
||||
const yearValues = context.availableYears.map(y => `${y}: ${formatCurrency(p[y] as number || 0)}`).join(', ');
|
||||
return ` - ${p.name}: [${yearValues}]`;
|
||||
// Extract values for each year in the point
|
||||
const yearValues = context.availableYears.map(y => `${y}: ${formatCurrency(p[y] as number || 0)}`).join(', ');
|
||||
return ` - ${p.name}: [${yearValues}]`;
|
||||
}).join('\n');
|
||||
|
||||
// 3. Top Movers (Growth Table) - Limit to Top 10
|
||||
const growthSummary = context.topMovers.slice(0, 10).map(m =>
|
||||
` - ${m.line}: +€${m.sellOutGrowthValue.toLocaleString('de-DE')} (${m.sellOutGrowthPercentage.toFixed(1)}%)`
|
||||
const growthSummary = context.topMovers.slice(0, 10).map(m =>
|
||||
` - ${m.line}: +€${m.sellOutGrowthValue.toLocaleString('de-DE')} (${m.sellOutGrowthPercentage.toFixed(1)}%)`
|
||||
).join('\n');
|
||||
|
||||
// 4. Declining Movers (Decline Table) - Limit to Top 10
|
||||
const declineSummary = context.bottomMovers.slice(0, 10).map(m =>
|
||||
` - ${m.line}: -€${Math.abs(m.sellOutGrowthValue).toLocaleString('de-DE')} (${m.sellOutGrowthPercentage.toFixed(1)}%)`
|
||||
const declineSummary = context.bottomMovers.slice(0, 10).map(m =>
|
||||
` - ${m.line}: -€${Math.abs(m.sellOutGrowthValue).toLocaleString('de-DE')} (${m.sellOutGrowthPercentage.toFixed(1)}%)`
|
||||
).join('\n');
|
||||
|
||||
// 5. Product Lines Overview (Bar Charts) - Limit to Top 50 to save tokens but give depth
|
||||
const topLinesSummary = context.byLine.slice(0, 50).map((l, i) =>
|
||||
` ${i+1}. ${l.name}: ${formatCurrency(l.value)} | ${formatUnits(l.units)}`
|
||||
const topLinesSummary = context.byLine.slice(0, 50).map((l, i) =>
|
||||
` ${i + 1}. ${l.name}: ${formatCurrency(l.value)} | ${formatUnits(l.units)}`
|
||||
).join('\n');
|
||||
|
||||
// 6. Customer Distribution (Customer Chart)
|
||||
const customerSummary = context.byCustomer.map(c =>
|
||||
` - ${c.name}: ${formatCurrency(c.value)}`
|
||||
const customerSummary = context.byCustomer.map(c =>
|
||||
` - ${c.name}: ${formatCurrency(c.value)}`
|
||||
).join('\n');
|
||||
|
||||
const fullReport = `
|
||||
@@ -122,9 +130,9 @@ ${customerSummary}
|
||||
return response.text || "I couldn't generate a response based on the data provided.";
|
||||
} catch (error: any) {
|
||||
console.error("Gemini API Error:", error);
|
||||
|
||||
|
||||
if (error.message && error.message.includes("Not implemented on this platform")) {
|
||||
return "System Error: The AI SDK detected a platform mismatch.";
|
||||
return "System Error: The AI SDK detected a platform mismatch.";
|
||||
}
|
||||
|
||||
return `Error: ${error.message || "An unexpected error occurred while analyzing the data."}`;
|
||||
|
||||
Reference in New Issue
Block a user