Spaces:
Sleeping
Sleeping
File size: 4,065 Bytes
199bfa3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | """
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'),
})
|