File size: 9,251 Bytes
8334f8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kronos API Prediction Visualizer</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.3.3/dist/echarts.min.js"></script>
    <style>
        body { font-family: sans-serif; margin: 2em; }
        .container { max-width: 1200px; margin: auto; }
        .form-group { margin-bottom: 1em; }
        label { display: block; margin-bottom: 0.5em; }
        input, textarea, select { width: 100%; padding: 0.5em; box-sizing: border-box; }
        textarea { min-height: 150px; }
        button { padding: 0.7em 1.5em; cursor: pointer; }
        #chart { width: 100%; height: 600px; margin-top: 2em; border: 1px solid #ccc; }
        .error { color: red; }
        .success { color: green; }
        .status { margin-top: 1em; font-weight: bold; }
        .info { background-color: #f0f0f0; border-left: 4px solid #007bff; padding: 1em; margin-bottom: 1em; }
        hr { margin: 2em 0; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Kronos API Control Panel</h1>

        <div class="info">
            <p>
                For local testing, first run <code>app.py</code> in VS Code (press F5). The API endpoints will be available at <code>http://localhost:7860</code>.
            </p>
        </div>

        <!-- Section for Model Loading -->
        <section id="model-loader">
            <h2>1. Load Model</h2>
            <div class="form-group">
                <label for="model-select">Available Models:</label>
                <select id="model-select"></select>
            </div>
            <button id="load-model-btn">Load Selected Model</button>
            <div id="model-status" class="status"></div>
        </section>

        <hr>

        <!-- Section for Prediction -->
        <section id="predictor">
            <h2>2. Get Prediction</h2>
            <div class="form-group">
                <label for="api-key">API Key (Bearer Token, if required):</label>
                <input type="password" id="api-key" placeholder="Enter your API Key">
            </div>
            <div class="form-group">
                <label for="k-lines">K-line Data (JSON Array of Arrays):</label>
                <textarea id="k-lines" placeholder="Paste your k-line data here..."></textarea>
            </div>
            <div class="form-group">
                <label for="pred-len">Prediction Length:</label>
                <input type="number" id="pred-len" value="120">
            </div>
            <button id="predict-btn">Get Prediction & Visualize</button>
        </section>

        <div id="chart"></div>
        <div id="error-message" class="status error"></div>
    </div>

    <script>
        // --- Global DOM Elements ---
        const modelSelect = document.getElementById('model-select');
        const loadModelBtn = document.getElementById('load-model-btn');
        const modelStatusDiv = document.getElementById('model-status');
        const predictBtn = document.getElementById('predict-btn');
        const apiKeyInput = document.getElementById('api-key');
        const kLinesTextarea = document.getElementById('k-lines');
        const predLenInput = document.getElementById('pred-len');
        const chartDom = document.getElementById('chart');
        const errorDiv = document.getElementById('error-message');

        // --- API Base URLs ---
        // Use relative paths for deployed environment, detect local for testing.
        const isLocal = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
        const apiBaseUrl = isLocal ? 'http://localhost:7860' : '';

        // --- Helper Functions ---
        async function apiFetch(endpoint, options) {
            const apiKey = apiKeyInput.value;
            const headers = {
                'Content-Type': 'application/json',
                ...options.headers,
            };
            if (apiKey) {
                headers['Authorization'] = `Bearer ${apiKey}`;
            }
            
            const response = await fetch(apiBaseUrl + endpoint, { ...options, headers });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`API Error (${response.status}): ${errorData.error || 'Unknown error'}`);
            }
            return response.json();
        }

        // --- Model Loading Logic ---
        async function populateModels() {
            try {
                const models = await apiFetch('/api/available-models', { method: 'GET' });
                modelSelect.innerHTML = ''; // Clear existing options
                for (const key in models) {
                    const option = document.createElement('option');
                    option.value = key;
                    option.textContent = `${models[key].name} (${models[key].params}) - ${models[key].description}`;
                    modelSelect.appendChild(option);
                }
            } catch (error) {
                modelStatusDiv.className = 'status error';
                modelStatusDiv.textContent = `Failed to fetch models: ${error.message}`;
            }
        }

        loadModelBtn.addEventListener('click', async () => {
            const modelKey = modelSelect.value;
            modelStatusDiv.className = 'status';
            modelStatusDiv.textContent = `Loading model '${modelKey}'...`;
            try {
                const result = await apiFetch('/api/load-model', {
                    method: 'POST',
                    body: JSON.stringify({ model_key: modelKey })
                });
                modelStatusDiv.className = 'status success';
                modelStatusDiv.textContent = result.status;
            } catch (error) {
                modelStatusDiv.className = 'status error';
                modelStatusDiv.textContent = error.message;
            }
        });

        // --- Prediction Logic ---
        predictBtn.addEventListener('click', async () => {
            const kLinesText = kLinesTextarea.value;
            const predLen = parseInt(predLenInput.value, 10);
            
            errorDiv.textContent = '';
            const myChart = echarts.init(chartDom);
            myChart.showLoading();

            if (!kLinesText) {
                errorDiv.textContent = 'K-line data cannot be empty.';
                myChart.hideLoading();
                return;
            }

            let kLines;
            try {
                kLines = JSON.parse(kLinesText);
            } catch (e) {
                errorDiv.textContent = 'Invalid JSON in K-line data. Please check the format.';
                myChart.hideLoading();
                return;
            }

            const payload = {
                k_lines: kLines,
                prediction_params: { pred_len: predLen }
            };

            try {
                const result = await apiFetch('/api/predict', {
                    method: 'POST',
                    body: JSON.stringify(payload)
                });
                
                const historicalData = kLines.map(item => [
                    item[0], parseFloat(item[1]), parseFloat(item[4]), parseFloat(item[3]), parseFloat(item[2])
                ]);
                
                const predictionData = result.prediction_results.map(item => [
                    item[0], parseFloat(item[1]), parseFloat(item[4]), parseFloat(item[3]), parseFloat(item[2])
                ]);

                const allTimestamps = [...historicalData.map(d => d[0]), ...predictionData.map(d => d[0])];
                
                const option = {
                    tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } },
                    legend: { data: ['Historical', 'Prediction'] },
                    grid: { left: '10%', right: '10%', bottom: '15%' },
                    xAxis: { type: 'time', min: allTimestamps[0], max: allTimestamps[allTimestamps.length - 1] },
                    yAxis: { scale: true, splitArea: { show: true } },
                    dataZoom: [
                        { type: 'inside', start: 50, end: 100 },
                        { show: true, type: 'slider', top: '90%', start: 50, end: 100 }
                    ],
                    series: [
                        { name: 'Historical', type: 'candlestick', data: historicalData, itemStyle: { color: '#00da3c', color0: '#ec0000', borderColor: '#008F28', borderColor0: '#8A0000' } },
                        { name: 'Prediction', type: 'candlestick', data: predictionData, itemStyle: { color: '#4287f5', color0: '#f54242', borderColor: '#285199', borderColor0: '#992828' } }
                    ]
                };
                
                myChart.hideLoading();
                myChart.setOption(option);

            } catch (error) {
                myChart.hideLoading();
                errorDiv.textContent = error.message;
                console.error('Fetch error:', error);
            }
        });

        // --- Initial Load ---
        document.addEventListener('DOMContentLoaded', populateModels);
    </script>
</body>
</html>