| | import streamlit as st |
| | import pandas as pd |
| | from typing import Optional, Dict |
| | import os |
| |
|
| | from config import get_settings |
| |
|
| | @st.cache_resource |
| | def init_ai_model(): |
| | settings = get_settings() |
| | api_key = settings.groq_api_key |
| | |
| | if not api_key: |
| | return None |
| | |
| | try: |
| | from groq import Groq |
| | os.environ["GROQ_API_KEY"] = api_key |
| | return Groq() |
| | except Exception as e: |
| | st.error(f"AI configuration failed: {str(e)}") |
| | return None |
| |
|
| | def generate_ai_summary(model, df: pd.DataFrame, stats: Dict, outliers: Dict, lang: str = 'English') -> str: |
| | if not model: |
| | return "AI analysis unavailable - Groq API key not configured." |
| | |
| | language_instruction = "Respond in Norwegian." if lang == 'Norsk' else "Respond in English." |
| | |
| | try: |
| | materials = [k for k in stats.keys() if k != '_total_'] |
| | |
| | context_parts = [ |
| | "# Production Data Analysis Context", |
| | "## Overview", |
| | f"- Total Production: {stats['_total_']['total']:,.0f} kg", |
| | f"- Production Period: {stats['_total_']['work_days']} working days", |
| | f"- Daily Average: {stats['_total_']['daily_avg']:,.0f} kg", |
| | f"- Materials Tracked: {len(materials)}", |
| | "", |
| | "## Material Breakdown:" |
| | ] |
| | |
| | for material in materials: |
| | info = stats[material] |
| | context_parts.append( |
| | f"- {material.title()}: {info['total']:,.0f} kg ({info['percentage']:.1f}%), " |
| | f"avg {info['daily_avg']:,.0f} kg/day" |
| | ) |
| | |
| | daily_data = df.groupby('date')['weight_kg'].sum() |
| | trend_direction = "increasing" if daily_data.iloc[-1] > daily_data.iloc[0] else "decreasing" |
| | volatility = daily_data.std() / daily_data.mean() * 100 |
| | |
| | context_parts.extend([ |
| | "", |
| | "## Trend Analysis:", |
| | f"- Overall trend: {trend_direction}", |
| | f"- Production volatility: {volatility:.1f}% coefficient of variation", |
| | f"- Peak production: {daily_data.max():,.0f} kg", |
| | f"- Lowest production: {daily_data.min():,.0f} kg" |
| | ]) |
| | |
| | total_outliers = sum(info['count'] for info in outliers.values()) |
| | context_parts.extend([ |
| | "", |
| | "## Quality Control:", |
| | f"- Total outliers detected: {total_outliers}", |
| | f"- Materials with quality issues: {sum(1 for info in outliers.values() if info['count'] > 0)}" |
| | ]) |
| | |
| | if 'shift' in df.columns: |
| | shift_stats = df.groupby('shift')['weight_kg'].sum() |
| | context_parts.extend([ |
| | "", |
| | "## Shift Performance:", |
| | f"- Day shift: {shift_stats.get('day', 0):,.0f} kg", |
| | f"- Night shift: {shift_stats.get('night', 0):,.0f} kg" |
| | ]) |
| | |
| | context_text = "\n".join(context_parts) |
| | |
| | prompt = f""" |
| | {language_instruction} |
| | |
| | {context_text} |
| | |
| | As an expert AI analyst for the Production Monitor platform, provide concise analysis. |
| | |
| | Structure your response: |
| | |
| | **PRODUCTION ASSESSMENT** |
| | Evaluate status (Excellent/Good/Needs Attention) with brief justification. |
| | |
| | **KEY FINDINGS** |
| | Identify 3-4 critical insights. Reference platform features like Quality Check module or Production Trend chart. |
| | |
| | **RECOMMENDATIONS** |
| | Provide 2-3 actionable steps for management. |
| | |
| | Keep under 300 words. |
| | """ |
| | |
| | response = model.chat.completions.create( |
| | messages=[{"role": "user", "content": prompt}], |
| | model="llama-3.3-70b-versatile", |
| | temperature=0.7, |
| | max_tokens=1000 |
| | ) |
| | return response.choices[0].message.content |
| | |
| | except Exception as e: |
| | error_msg = str(e) |
| | if '429' in error_msg or 'quota' in error_msg.lower(): |
| | return "AI analysis temporarily unavailable due to API quota limits. Please try again later." |
| | return f"AI analysis error: {error_msg}" |
| |
|
| | def query_ai(model, stats: Dict, question: str, df: Optional[pd.DataFrame] = None, lang: str = 'English') -> str: |
| | if not model: |
| | return "AI assistant not available - Please configure Groq API key" |
| | |
| | language_instruction = "Respond in Norwegian." if lang == 'Norsk' else "Respond in English." |
| | |
| | context_parts = [ |
| | "Production Data Summary:", |
| | *[f"- {mat.title()}: {info['total']:,.0f}kg ({info['percentage']:.1f}%)" |
| | for mat, info in stats.items() if mat != '_total_'], |
| | f"\nTotal Production: {stats['_total_']['total']:,.0f}kg across {stats['_total_']['work_days']} work days" |
| | ] |
| | |
| | if df is not None: |
| | available_cols = list(df.columns) |
| | context_parts.append(f"\nAvailable data fields: {', '.join(available_cols)}") |
| | |
| | if 'shift' in df.columns: |
| | shift_stats = df.groupby('shift')['weight_kg'].sum() |
| | context_parts.append(f"Shift breakdown: {dict(shift_stats)}") |
| | |
| | if 'day_name' in df.columns: |
| | day_stats = df.groupby('day_name')['weight_kg'].mean() |
| | context_parts.append(f"Average daily production: {dict(day_stats.round(0))}") |
| | |
| | context = "\n".join(context_parts) + f"\n\n{language_instruction}\n\nQuestion: {question}\nAnswer based on available data:" |
| | |
| | try: |
| | response = model.chat.completions.create( |
| | messages=[{"role": "user", "content": context}], |
| | model="llama-3.3-70b-versatile", |
| | temperature=0.7, |
| | max_tokens=500 |
| | ) |
| | return response.choices[0].message.content |
| | except Exception as e: |
| | error_msg = str(e) |
| | if '429' in error_msg or 'quota' in error_msg.lower(): |
| | return "AI assistant temporarily unavailable due to API quota limits. Please try again later." |
| | return f"Error getting AI response: {error_msg}" |