Spaces:
Sleeping
Sleeping
| """ | |
| Feature 4: LLM-Driven Variation Analysis | |
| Claude explains variations and stabilizations in a testing cycle | |
| """ | |
| import streamlit as st | |
| from core.db_connector import get_db_connector | |
| from analysis.llm_analyzer import LLMCycleAnalyzer | |
| from ui.components import page_header, analysis_card, follow_up_section | |
| page_header( | |
| "Variation Analysis", | |
| "AI-powered interpretation of pressure/temperature variations and stabilizations in a testing cycle." | |
| ) | |
| db = get_db_connector() | |
| llm = LLMCycleAnalyzer() | |
| # Check prerequisites | |
| cycle_stats = st.session_state.get('current_cycle_stats') | |
| current_cycle = st.session_state.get('current_cycle') | |
| plateaus = st.session_state.get('current_plateaus', {}) | |
| if not cycle_stats or not current_cycle: | |
| st.warning( | |
| "No cycle selected. Please go to **Testing Cycles** to detect cycles, " | |
| "then **Cycle Detail** to select and analyze a specific cycle." | |
| ) | |
| st.stop() | |
| # Header info | |
| st.subheader(f"Cycle {current_cycle['cycle_id']} - {current_cycle['start_time'].strftime('%b %d %H:%M')}") | |
| col1, col2, col3 = st.columns(3) | |
| col1.metric("Duration", f"{cycle_stats['time_range']['duration_minutes']:.0f} min") | |
| if 'discharge_pressure' in cycle_stats: | |
| col2.metric("Peak PT130", f"{cycle_stats['discharge_pressure']['peak']:.0f} bar") | |
| plateau_count = sum(len(p) for p in plateaus.values()) | |
| col3.metric("Plateaus", plateau_count) | |
| st.divider() | |
| # LLM availability check | |
| if not llm.api_available: | |
| st.error("Claude API key not configured. Add ANTHROPIC_API_KEY to your .env file to enable AI analysis.") | |
| st.stop() | |
| # Check for cached result | |
| cache_key = f"variation_{current_cycle['cycle_id']}_{current_cycle['start_time']}" | |
| prompt_key = f"{cache_key}_prompt" | |
| followup_key = f"{cache_key}_followups" | |
| cached_result = st.session_state.get(cache_key) | |
| if cached_result: | |
| analysis_card("AI Analysis", cached_result) | |
| # Follow-up section | |
| original_prompt = st.session_state.get(prompt_key, "") | |
| follow_up_section( | |
| session_key=followup_key, | |
| llm_analyzer=llm, | |
| original_prompt=original_prompt, | |
| original_analysis=cached_result, | |
| ) | |
| if st.button("Re-analyze", key="reanalyze_variation"): | |
| del st.session_state[cache_key] | |
| st.session_state.pop(prompt_key, None) | |
| st.session_state.pop(followup_key, None) | |
| st.rerun() | |
| else: | |
| st.info("Click below to generate an AI-powered analysis of this cycle's variations and stabilizations.") | |
| if st.button("Analyze Cycle", type="primary", use_container_width=True): | |
| with st.spinner("Claude is analyzing the cycle data..."): | |
| result = llm.analyze_cycle_variations( | |
| cycle_stats=cycle_stats, | |
| plateaus=plateaus, | |
| ) | |
| # Store the prompt that was used (for follow-up context) | |
| st.session_state[prompt_key] = llm._build_variation_prompt( | |
| cycle_stats, plateaus, None, | |
| llm.retriever.format_for_prompt( | |
| llm.retriever.get_context_for_cycle(cycle_stats, plateaus) | |
| ) if llm.retriever else "", | |
| ) | |
| st.session_state[cache_key] = result | |
| st.rerun() | |
| # Show input data summary | |
| with st.expander("Data sent to AI", expanded=False): | |
| st.json({ | |
| 'time_range': { | |
| 'start': str(cycle_stats['time_range']['start']), | |
| 'end': str(cycle_stats['time_range']['end']), | |
| 'duration_minutes': cycle_stats['time_range']['duration_minutes'], | |
| }, | |
| 'discharge_pressure': cycle_stats.get('discharge_pressure'), | |
| 'compression_ratio': cycle_stats.get('compression_ratio'), | |
| 'flow': cycle_stats.get('flow'), | |
| 'ramp_rate': cycle_stats.get('ramp_rate'), | |
| 'motor': cycle_stats.get('motor'), | |
| 'plateaus': { | |
| tag: [ | |
| {'value': p['value'], 'duration_min': p['duration_minutes']} | |
| for p in periods | |
| ] | |
| for tag, periods in plateaus.items() | |
| }, | |
| 'performance_vs_targets': cycle_stats.get('performance_vs_targets'), | |
| }) | |