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'),
    })