llmxray 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ar.md +10 -7
- package/README.fr.md +10 -7
- package/README.md +10 -7
- package/README.sr.md +10 -7
- package/README.zh-CN.md +10 -7
- package/dist/assets/{AITrainingPage-Cb771S8Z.js → AITrainingPage-E06gN8gf.js} +1 -1
- package/dist/assets/{AnalyticsPage-D4bgNoXM.js → AnalyticsPage-DE1P_0ls.js} +1 -1
- package/dist/assets/{BenchmarkPage-BTfBJXIj.js → BenchmarkPage-aPSp8X68.js} +1 -1
- package/dist/assets/CacheLabPage-DK6ysPiC.js +3 -0
- package/dist/assets/{ComparisonPage-DMcD9JL0.js → ComparisonPage-B-TTYJzF.js} +1 -1
- package/dist/assets/{CostDashboardPage-BjUiVjlg.js → CostDashboardPage-uaU9LjEe.js} +1 -1
- package/dist/assets/{DashboardPage-xgeJtl-Q.js → DashboardPage-i7XFboZS.js} +1 -1
- package/dist/assets/{EmbeddingsPage-DY1JKxCN.js → EmbeddingsPage-BIj9zAA3.js} +1 -1
- package/dist/assets/{FimPlaygroundPage-BWSlc3k7.js → FimPlaygroundPage-BQLvqvU_.js} +1 -1
- package/dist/assets/{GoogleCallbackPage-C9xQpfMW.js → GoogleCallbackPage-eAe26FFg.js} +1 -1
- package/dist/assets/{JsonTreeNode-D9YzXU8h.js → JsonTreeNode-CWxvNXOX.js} +1 -1
- package/dist/assets/{ModelCapabilityIcons.vue_vue_type_script_setup_true_lang-CPvepOP2.js → ModelCapabilityIcons.vue_vue_type_script_setup_true_lang-B5ZTvJWj.js} +1 -1
- package/dist/assets/{ProtocolObservatoryPage-D7szMZuY.js → ProtocolObservatoryPage--sesoYfn.js} +1 -1
- package/dist/assets/{RAGPage-C7awGOKp.js → RAGPage-CEwQVaIo.js} +1 -1
- package/dist/assets/{SessionPage-8CBF4JLG.js → SessionPage-BZ6kBsHI.js} +1 -1
- package/dist/assets/SettingsPage-CnF1KHWK.js +1 -0
- package/dist/assets/{StatusBadge.vue_vue_type_script_setup_true_lang-BPTPrdQf.js → StatusBadge.vue_vue_type_script_setup_true_lang-BGo19Xph.js} +1 -1
- package/dist/assets/{StorageGauge.vue_vue_type_style_index_0_lang-a4pXCCmZ.js → StorageGauge.vue_vue_type_style_index_0_lang-CUFDMHxV.js} +1 -1
- package/dist/assets/{SystemPage-mElnkuG4.js → SystemPage-B6rcHN5S.js} +2 -2
- package/dist/assets/{TabBar.vue_vue_type_script_setup_true_lang-g3X_zm3P.js → TabBar.vue_vue_type_script_setup_true_lang-C0hGnp0C.js} +1 -1
- package/dist/assets/{TokenStreamDisplay.vue_vue_type_script_setup_true_lang-DQBSq5z2.js → TokenStreamDisplay.vue_vue_type_script_setup_true_lang-CW2jbXwZ.js} +1 -1
- package/dist/assets/{ToolWorkshopPage-3Za0iXSV.js → ToolWorkshopPage-DmrV0xap.js} +1 -1
- package/dist/assets/{agent-store-Cboc3nde.js → agent-store--SqycFe6.js} +1 -1
- package/dist/assets/{canvas-ai-db-Dy65jrST.js → canvas-ai-db-Da1vIVOq.js} +1 -1
- package/dist/assets/{download-Cipln2ej.js → download-CUsL7GrM.js} +1 -1
- package/dist/assets/{generate-service-C8to1Kz5.js → generate-service-BZdEHgFU.js} +1 -1
- package/dist/assets/{google-auth-store-8ShUecGK.js → google-auth-store-DXrzgrLv.js} +1 -1
- package/dist/assets/{index-NGfHHJ7k.js → index-BPkqt5Qe.js} +1 -1
- package/dist/assets/{index-UYM0582r.css → index-C78FkuPP.css} +1 -1
- package/dist/assets/{index-DlTtfQCm.js → index-C8P_C63i.js} +1 -1
- package/dist/assets/{index-DAqgYcEq.js → index-YRiA3BUj.js} +11 -11
- package/dist/assets/{info-BzNBY8Xz.js → info-DH8FG2G3.js} +1 -1
- package/dist/assets/{metrics-store-DLxxEHpn.js → metrics-store-BCparPrV.js} +1 -1
- package/dist/assets/{papaparse.min-BMNThItx.js → papaparse.min-Dopi_5A-.js} +1 -1
- package/dist/assets/{pencil-c-VIQAV1.js → pencil-gkZEyblB.js} +1 -1
- package/dist/assets/{play-CyV4NPEG.js → play-HRQny-4Y.js} +1 -1
- package/dist/assets/{rag-store-7BNJiZff.js → rag-store-D0yMVMjG.js} +3 -3
- package/dist/assets/{session-store-DJJV_fRl.js → session-store-2bGNgO7a.js} +1 -1
- package/dist/assets/{share-2-DTMUvOzC.js → share-2-c3XzX2pt.js} +1 -1
- package/dist/assets/{square-CVjmHbTf.js → square-CqZgk1mI.js} +1 -1
- package/dist/assets/{storage-store-CAp_Ass2.js → storage-store-BLDOaasz.js} +1 -1
- package/dist/assets/{stream-handler-DVtwM8Et.js → stream-handler-BGYCTeOR.js} +1 -1
- package/dist/assets/{tool-workshop-store-C_snrdcI.js → tool-workshop-store-DBnJxusu.js} +1 -1
- package/dist/assets/{toolcall-store-DVrrMX0Y.js → toolcall-store-CJSLkRLe.js} +1 -1
- package/dist/assets/{trash-2-DfEdg6Up.js → trash-2-DU091nIw.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +9 -1
- package/dist/assets/SettingsPage-MEHyheCY.js +0 -1
package/README.ar.md
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
</p>
|
|
36
36
|
|
|
37
37
|
<p align="center">
|
|
38
|
-
<img src="docs/public/screenshots/demo.gif" alt="عرض LLMxRay — بث الرموز في الوقت الفعلي مع تلوين الثقة" width="800" />
|
|
38
|
+
<img src="https://raw.githubusercontent.com/LogneBudo/llmxray/master/docs/public/screenshots/demo.gif" alt="عرض LLMxRay — بث الرموز في الوقت الفعلي مع تلوين الثقة" width="800" />
|
|
39
39
|
</p>
|
|
40
40
|
|
|
41
41
|
---
|
|
@@ -112,6 +112,9 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
112
112
|
### ساحة الإكمال في الوسط *(جديد في v0.4.7)*
|
|
113
113
|
إكمال الكود لـ Qwen-Coder و CodeLlama و Codestral و DeepSeek-Coder و StarCoder. حقلا نص (بادئة / لاحقة)، والنموذج يملأ الفراغ. يستخدم حقل `suffix` في `/api/generate` من Ollama. معاينة مدمجة تُظهر النتيجة كما ستبدو في محرّرك.
|
|
114
114
|
|
|
115
|
+
### مختبر الذاكرة المؤقتة *(جديد في v0.6.0)*
|
|
116
|
+
اعرف لماذا يفوت مُوجّهك ذاكرة KV للنموذج، وقس كم يكلّف ذلك في كل دور. لا يعيد النموذج المحلّي استخدام الذاكرة إلا ما دام المُوجّه مطابقاً من أول رمز، فطابع زمني واحد في الأعلى يُفقد كل ما دونه. يجد المختبر القيم التي تتغيّر بين الأدوار، ويُظهر النقطة التي تموت عندها إعادة الاستخدام، ثم **يقيس** — بإرسال كل ترتيب مرّتين بقيمة مختلفة على خادمك أنت — ما يوفّره نقلها إلى النهاية. مقيس على مُوجّه حقيقي من 324 رمزاً: **4 رموز مُعادة وـ 64.6 ملّي ثانية تعبئة مسبقة مع الطابع في المقدّمة، و290 رمزاً وـ 18.6 ملّي ثانية معه في النهاية. أسرع بـ 3.5 مرّة، بالكلمات نفسها.** يتطلّب Ollama 0.33.3+.
|
|
117
|
+
|
|
115
118
|
### مرصد البروتوكولات *(جديد في v0.4.7)*
|
|
116
119
|
أطلق نفس المطالبة عبر بروتوكولات Ollama الثلاثة للتقديم — **الأصلي** `/api/chat`، **المتوافق مع OpenAI** `/v1/chat/completions`، و **المتوافق مع Anthropic** `/v1/messages` — بالتوازي ضد نموذجك المحلي. بثّ جنباً إلى جنب، ومقاييس لكل بروتوكول، وعلامة تبويب لفروق الأظرف تُظهر كيف يصوغ كل بروتوكول أسباب الإنهاء وعدّ الرموز وأظرف الأخطاء. بلا سحابة، بلا مفاتيح API — النقاط الثلاث جميعها محلية على `localhost:11434`.
|
|
117
120
|
|
|
@@ -152,13 +155,13 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
152
155
|
<td width="50%">
|
|
153
156
|
|
|
154
157
|
**دردشة ببث الرموز ومؤشّر الثقة**
|
|
155
|
-

|
|
158
|
+

|
|
156
159
|
|
|
157
160
|
</td>
|
|
158
161
|
<td width="50%">
|
|
159
162
|
|
|
160
163
|
**مقارنة النماذج — جنباً إلى جنب**
|
|
161
|
-

|
|
164
|
+

|
|
162
165
|
|
|
163
166
|
</td>
|
|
164
167
|
</tr>
|
|
@@ -166,13 +169,13 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
166
169
|
<td width="50%">
|
|
167
170
|
|
|
168
171
|
**تشريح الجلسة — مقاييس وتوقيت**
|
|
169
|
-

|
|
172
|
+

|
|
170
173
|
|
|
171
174
|
</td>
|
|
172
175
|
<td width="50%">
|
|
173
176
|
|
|
174
177
|
**معيار مع رادار الثقة**
|
|
175
|
-

|
|
178
|
+

|
|
176
179
|
|
|
177
180
|
</td>
|
|
178
181
|
</tr>
|
|
@@ -180,13 +183,13 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
180
183
|
<td width="50%">
|
|
181
184
|
|
|
182
185
|
**التضمينات — تشابه جيب التمام**
|
|
183
|
-

|
|
186
|
+

|
|
184
187
|
|
|
185
188
|
</td>
|
|
186
189
|
<td width="50%">
|
|
187
190
|
|
|
188
191
|
**مراقب النظام — العتاد وحالة Ollama**
|
|
189
|
-

|
|
192
|
+

|
|
190
193
|
|
|
191
194
|
</td>
|
|
192
195
|
</tr>
|
package/README.fr.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</p>
|
|
34
34
|
|
|
35
35
|
<p align="center">
|
|
36
|
-
<img src="docs/public/screenshots/demo.gif" alt="Démo LLMxRay — streaming de tokens en temps réel avec coloration selon la confiance" width="800" />
|
|
36
|
+
<img src="https://raw.githubusercontent.com/LogneBudo/llmxray/master/docs/public/screenshots/demo.gif" alt="Démo LLMxRay — streaming de tokens en temps réel avec coloration selon la confiance" width="800" />
|
|
37
37
|
</p>
|
|
38
38
|
|
|
39
39
|
---
|
|
@@ -110,6 +110,9 @@ Canvas à nœuds en drag-and-drop pour construire des définitions d'outils. Syn
|
|
|
110
110
|
### Aire de jeu Fill-in-the-Middle *(nouveau en v0.4.7)*
|
|
111
111
|
Complétion de code pour Qwen-Coder, CodeLlama, Codestral, DeepSeek-Coder et StarCoder. Deux zones de texte (préfixe / suffixe), le modèle remplit le trou. Utilise le champ `suffix` d'Ollama sur `/api/generate`. L'aperçu assemblé montre le résultat tel qu'il apparaîtrait dans votre éditeur.
|
|
112
112
|
|
|
113
|
+
### Labo de cache *(nouveau en v0.6.0)*
|
|
114
|
+
Comprenez pourquoi votre invite manque le cache KV du modèle, et mesurez ce que cela coûte à chaque tour. Un modèle local ne réutilise son cache que tant que l’invite correspond depuis le tout premier token : un seul horodatage en haut fait perdre tout ce qui suit. Le labo repère les valeurs qui changent d’un tour à l’autre, montre le point exact où la réutilisation meurt, puis **mesure** — en envoyant chaque disposition deux fois avec une valeur modifiée, sur votre propre démon — ce que rapporte leur déplacement à la fin. Mesuré sur une invite réelle de 324 tokens : **4 tokens réutilisés et 64,6 ms de préremplissage avec l’horodatage au début, 290 réutilisés et 18,6 ms à la fin. 3,5x plus rapide, à mots identiques.** Nécessite Ollama 0.33.3+.
|
|
115
|
+
|
|
113
116
|
### Observatoire de protocoles *(nouveau en v0.4.7)*
|
|
114
117
|
Envoyez le même prompt à travers les trois protocoles de service d'Ollama — **natif** `/api/chat`, **compatible OpenAI** `/v1/chat/completions`, et **compatible Anthropic** `/v1/messages` — en parallèle contre votre modèle local. Streaming côte à côte, métriques par protocole, et onglet « envelope diff » qui montre comment chaque protocole encode les raisons de fin, le comptage de tokens et les enveloppes d'erreur. Pas de cloud, pas de clés API — les trois endpoints sont locaux sur `localhost:11434`.
|
|
115
118
|
|
|
@@ -150,13 +153,13 @@ Testé et vérifié avec **Ollama 0.33.x** (vérifié sur 0.33.3, septembre 2026
|
|
|
150
153
|
<td width="50%">
|
|
151
154
|
|
|
152
155
|
**Chat avec streaming de tokens et confiance**
|
|
153
|
-

|
|
156
|
+

|
|
154
157
|
|
|
155
158
|
</td>
|
|
156
159
|
<td width="50%">
|
|
157
160
|
|
|
158
161
|
**Comparaison de modèles — côte à côte**
|
|
159
|
-

|
|
162
|
+

|
|
160
163
|
|
|
161
164
|
</td>
|
|
162
165
|
</tr>
|
|
@@ -164,13 +167,13 @@ Testé et vérifié avec **Ollama 0.33.x** (vérifié sur 0.33.3, septembre 2026
|
|
|
164
167
|
<td width="50%">
|
|
165
168
|
|
|
166
169
|
**Analyse approfondie d'une session — métriques et timing**
|
|
167
|
-

|
|
170
|
+

|
|
168
171
|
|
|
169
172
|
</td>
|
|
170
173
|
<td width="50%">
|
|
171
174
|
|
|
172
175
|
**Benchmark avec radar de confiance**
|
|
173
|
-

|
|
176
|
+

|
|
174
177
|
|
|
175
178
|
</td>
|
|
176
179
|
</tr>
|
|
@@ -178,13 +181,13 @@ Testé et vérifié avec **Ollama 0.33.x** (vérifié sur 0.33.3, septembre 2026
|
|
|
178
181
|
<td width="50%">
|
|
179
182
|
|
|
180
183
|
**Embeddings — similarité cosinus**
|
|
181
|
-

|
|
184
|
+

|
|
182
185
|
|
|
183
186
|
</td>
|
|
184
187
|
<td width="50%">
|
|
185
188
|
|
|
186
189
|
**Moniteur système — matériel et statut Ollama**
|
|
187
|
-

|
|
190
|
+

|
|
188
191
|
|
|
189
192
|
</td>
|
|
190
193
|
</tr>
|
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</p>
|
|
34
34
|
|
|
35
35
|
<p align="center">
|
|
36
|
-
<img src="docs/public/screenshots/demo.gif" alt="LLMxRay demo — real-time token streaming with confidence coloring" width="800" />
|
|
36
|
+
<img src="https://raw.githubusercontent.com/LogneBudo/llmxray/master/docs/public/screenshots/demo.gif" alt="LLMxRay demo — real-time token streaming with confidence coloring" width="800" />
|
|
37
37
|
</p>
|
|
38
38
|
|
|
39
39
|
---
|
|
@@ -110,6 +110,9 @@ Drag-and-drop node canvas for building tool definitions. Bidirectional code sync
|
|
|
110
110
|
### Fill-in-the-Middle Playground *(new in v0.4.7)*
|
|
111
111
|
Code completion for Qwen-Coder, CodeLlama, Codestral, DeepSeek-Coder, and StarCoder. Two textareas (prefix / suffix), the model fills the gap. Uses Ollama's `suffix` field on `/api/generate`. Stitched preview shows the result as it would appear in your editor.
|
|
112
112
|
|
|
113
|
+
### Cache Lab *(new in v0.6.0)*
|
|
114
|
+
Find out why your prompt misses the model’s KV cache, and measure what it costs every turn. A local model reuses its cache only while the prompt still matches from the very first token, so a single timestamp near the top forfeits everything below it. The lab finds the values that change between turns, shows the exact point where reuse dies, and then **measures** — sending each layout twice with a changed value, against your own daemon — what moving them to the end actually saves. Measured on a real 324-token prompt: **4 tokens reused and 64.6 ms of prefill with the timestamp at the front, 290 reused and 18.6 ms with it at the back. 3.5x faster, same words.** Requires Ollama 0.33.3+.
|
|
115
|
+
|
|
113
116
|
### Protocol Observatory *(new in v0.4.7)*
|
|
114
117
|
Fire the same prompt through Ollama's three serving protocols — **native** `/api/chat`, **OpenAI-compat** `/v1/chat/completions`, and **Anthropic-compat** `/v1/messages` — in parallel against your local model. Side-by-side streaming, per-protocol metrics, and an envelope-diff tab that shows how each protocol frames finish reasons, token counts, and error envelopes. No cloud, no API keys — all three endpoints are local on `localhost:11434`.
|
|
115
118
|
|
|
@@ -150,13 +153,13 @@ Tested and verified against **Ollama 0.33.x** (verified on 0.33.3, September 202
|
|
|
150
153
|
<td width="50%">
|
|
151
154
|
|
|
152
155
|
**Chat with token streaming and confidence**
|
|
153
|
-

|
|
156
|
+

|
|
154
157
|
|
|
155
158
|
</td>
|
|
156
159
|
<td width="50%">
|
|
157
160
|
|
|
158
161
|
**Model comparison — side by side**
|
|
159
|
-

|
|
162
|
+

|
|
160
163
|
|
|
161
164
|
</td>
|
|
162
165
|
</tr>
|
|
@@ -164,13 +167,13 @@ Tested and verified against **Ollama 0.33.x** (verified on 0.33.3, September 202
|
|
|
164
167
|
<td width="50%">
|
|
165
168
|
|
|
166
169
|
**Session deep dive — metrics and timing**
|
|
167
|
-

|
|
170
|
+

|
|
168
171
|
|
|
169
172
|
</td>
|
|
170
173
|
<td width="50%">
|
|
171
174
|
|
|
172
175
|
**Benchmark with confidence radar**
|
|
173
|
-

|
|
176
|
+

|
|
174
177
|
|
|
175
178
|
</td>
|
|
176
179
|
</tr>
|
|
@@ -178,13 +181,13 @@ Tested and verified against **Ollama 0.33.x** (verified on 0.33.3, September 202
|
|
|
178
181
|
<td width="50%">
|
|
179
182
|
|
|
180
183
|
**Embeddings — cosine similarity**
|
|
181
|
-

|
|
184
|
+

|
|
182
185
|
|
|
183
186
|
</td>
|
|
184
187
|
<td width="50%">
|
|
185
188
|
|
|
186
189
|
**System monitor — hardware and Ollama status**
|
|
187
|
-

|
|
190
|
+

|
|
188
191
|
|
|
189
192
|
</td>
|
|
190
193
|
</tr>
|
package/README.sr.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</p>
|
|
34
34
|
|
|
35
35
|
<p align="center">
|
|
36
|
-
<img src="docs/public/screenshots/demo.gif" alt="LLMxRay demo — strimovanje tokena u realnom vremenu sa bojenjem prema poverenju" width="800" />
|
|
36
|
+
<img src="https://raw.githubusercontent.com/LogneBudo/llmxray/master/docs/public/screenshots/demo.gif" alt="LLMxRay demo — strimovanje tokena u realnom vremenu sa bojenjem prema poverenju" width="800" />
|
|
37
37
|
</p>
|
|
38
38
|
|
|
39
39
|
---
|
|
@@ -110,6 +110,9 @@ Drag-and-drop platno sa čvorovima za pravljenje definicija alata. Dvosmerna sin
|
|
|
110
110
|
### Fill-in-the-Middle Playground *(novo u v0.4.7)*
|
|
111
111
|
Dopuna koda za Qwen-Coder, CodeLlama, Codestral, DeepSeek-Coder i StarCoder. Dva polja (prefiks / sufiks), model popunjava prazninu. Koristi Ollama `suffix` polje na `/api/generate`. Spojeni pregled pokazuje rezultat kao što bi izgledao u tvom editoru.
|
|
112
112
|
|
|
113
|
+
### Laboratorija keša *(novo u v0.6.0)*
|
|
114
|
+
Saznajte zašto vaš prompt promašuje KV keš modela i izmerite koliko to košta u svakom krugu. Lokalni model ponovo koristi keš samo dok se prompt poklapa od prvog tokena, pa jedna vremenska oznaka pri vrhu poništava sve ispod nje. Laboratorija pronalazi vrednosti koje se menjaju između krugova, pokazuje tačku na kojoj ponovna upotreba umire, a zatim **meri** — šaljajući svaki raspored dvaput sa izmenjenom vrednošću, na vašem demonu — koliko donosi njihovo pomeranje na kraj. Izmereno na stvarnom promptu od 324 tokena: **4 tokena ponovo upotrebljena i 64,6 ms prefila sa oznakom na početku, 290 i 18,6 ms na kraju. 3,5x brže, uz iste reči.** Zahteva Ollama 0.33.3+.
|
|
115
|
+
|
|
113
116
|
### Protocol Observatory *(novo u v0.4.7)*
|
|
114
117
|
Pošalji isti prompt kroz tri Ollama protokola posluživanja — **nativni** `/api/chat`, **OpenAI-compat** `/v1/chat/completions` i **Anthropic-compat** `/v1/messages` — paralelno protiv tvog lokalnog modela. Strimovanje uporedo, metrike po protokolu i tab za poređenje envelope-a koji pokazuje kako svaki protokol uobličava razloge završetka, brojeve tokena i envelope grešaka. Bez clouda, bez API ključeva — sva tri endpointa su lokalna na `localhost:11434`.
|
|
115
118
|
|
|
@@ -150,13 +153,13 @@ Testirano i verifikovano sa **Ollama 0.33.x** (verifikovano na 0.33.3, septembar
|
|
|
150
153
|
<td width="50%">
|
|
151
154
|
|
|
152
155
|
**Chat sa strimovanjem tokena i poverenjem**
|
|
153
|
-

|
|
156
|
+

|
|
154
157
|
|
|
155
158
|
</td>
|
|
156
159
|
<td width="50%">
|
|
157
160
|
|
|
158
161
|
**Poređenje modela — uporedo**
|
|
159
|
-

|
|
162
|
+

|
|
160
163
|
|
|
161
164
|
</td>
|
|
162
165
|
</tr>
|
|
@@ -164,13 +167,13 @@ Testirano i verifikovano sa **Ollama 0.33.x** (verifikovano na 0.33.3, septembar
|
|
|
164
167
|
<td width="50%">
|
|
165
168
|
|
|
166
169
|
**Detaljni pregled sesije — metrike i tajming**
|
|
167
|
-

|
|
170
|
+

|
|
168
171
|
|
|
169
172
|
</td>
|
|
170
173
|
<td width="50%">
|
|
171
174
|
|
|
172
175
|
**Benchmark sa radarom poverenja**
|
|
173
|
-

|
|
176
|
+

|
|
174
177
|
|
|
175
178
|
</td>
|
|
176
179
|
</tr>
|
|
@@ -178,13 +181,13 @@ Testirano i verifikovano sa **Ollama 0.33.x** (verifikovano na 0.33.3, septembar
|
|
|
178
181
|
<td width="50%">
|
|
179
182
|
|
|
180
183
|
**Embeddings — kosinusna sličnost**
|
|
181
|
-

|
|
184
|
+

|
|
182
185
|
|
|
183
186
|
</td>
|
|
184
187
|
<td width="50%">
|
|
185
188
|
|
|
186
189
|
**Monitor sistema — hardver i Ollama status**
|
|
187
|
-

|
|
190
|
+

|
|
188
191
|
|
|
189
192
|
</td>
|
|
190
193
|
</tr>
|
package/README.zh-CN.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</p>
|
|
34
34
|
|
|
35
35
|
<p align="center">
|
|
36
|
-
<img src="docs/public/screenshots/demo.gif" alt="LLMxRay 演示 — 带置信度着色的实时词元流" width="800" />
|
|
36
|
+
<img src="https://raw.githubusercontent.com/LogneBudo/llmxray/master/docs/public/screenshots/demo.gif" alt="LLMxRay 演示 — 带置信度着色的实时词元流" width="800" />
|
|
37
37
|
</p>
|
|
38
38
|
|
|
39
39
|
---
|
|
@@ -110,6 +110,9 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
110
110
|
### 中段填充游乐场 *(v0.4.7 新增)*
|
|
111
111
|
为 Qwen-Coder、CodeLlama、Codestral、DeepSeek-Coder 与 StarCoder 提供代码补全。两个文本框(前缀 / 后缀),模型填补中间空缺。使用 Ollama 的 `/api/generate` 的 `suffix` 字段。拼接预览展示编辑器中的最终效果。
|
|
112
112
|
|
|
113
|
+
### 缓存实验室 *(v0.6.0 新增)*
|
|
114
|
+
查明提示为何未命中模型的 KV 缓存,并测量每轮为此付出的代价。本地模型只有在提示从第一个词元起仍然一致时才会复用缓存,因此顶部的一个时间戳会让其下的全部失效。本实验室会找出逐轮变化的值,标出复用中断的确切位置,然后在你自己的守护进程上对每种排布发送两次(其中一次改变该值)来**实测**将其移至末尾究竟能节省多少。在一个真实的 324 词元提示上实测:**时间戳在前时复用 4 个词元、预填充 64.6 毫秒;在后时复用 290 个、仅 18.6 毫秒。字句相同,快 3.5 倍。**需要 Ollama 0.33.3+。
|
|
115
|
+
|
|
113
116
|
### 协议观察台 *(v0.4.7 新增)*
|
|
114
117
|
将同一提示并行发送至 Ollama 的三种服务协议 —— **原生** `/api/chat`、**OpenAI 兼容** `/v1/chat/completions`、**Anthropic 兼容** `/v1/messages` —— 全部针对您的本地模型运行。并排流式输出、各协议指标对比,以及信封差异标签页,展示各协议如何封装结束原因、词元计数与错误信封。无云端、无 API 密钥 —— 三个端点全部位于 `localhost:11434`。
|
|
115
118
|
|
|
@@ -150,13 +153,13 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
150
153
|
<td width="50%">
|
|
151
154
|
|
|
152
155
|
**带词元流与置信度的对话**
|
|
153
|
-

|
|
156
|
+

|
|
154
157
|
|
|
155
158
|
</td>
|
|
156
159
|
<td width="50%">
|
|
157
160
|
|
|
158
161
|
**模型对比 — 并排**
|
|
159
|
-

|
|
162
|
+

|
|
160
163
|
|
|
161
164
|
</td>
|
|
162
165
|
</tr>
|
|
@@ -164,13 +167,13 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
164
167
|
<td width="50%">
|
|
165
168
|
|
|
166
169
|
**会话深度分析 — 指标与时序**
|
|
167
|
-

|
|
170
|
+

|
|
168
171
|
|
|
169
172
|
</td>
|
|
170
173
|
<td width="50%">
|
|
171
174
|
|
|
172
175
|
**基准测试与置信度雷达**
|
|
173
|
-

|
|
176
|
+

|
|
174
177
|
|
|
175
178
|
</td>
|
|
176
179
|
</tr>
|
|
@@ -178,13 +181,13 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
178
181
|
<td width="50%">
|
|
179
182
|
|
|
180
183
|
**嵌入 — 余弦相似度**
|
|
181
|
-

|
|
184
|
+

|
|
182
185
|
|
|
183
186
|
</td>
|
|
184
187
|
<td width="50%">
|
|
185
188
|
|
|
186
189
|
**系统监视器 — 硬件与 Ollama 状态**
|
|
187
|
-

|
|
190
|
+

|
|
188
191
|
|
|
189
192
|
</td>
|
|
190
193
|
</tr>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d as Z,r as $,a as D,b as N,e as m,f as e,t as o,q as R,u as c,i as w,l as G,F as P,k as S,g as k,j as p,h as H,p as j,X as Q,C as E,D as U,v as W,Z as V,B as Y}from"./index-DAqgYcEq.js";import{canvasAiDB as B}from"./canvas-ai-db-Dy65jrST.js";import{b as _,d as z}from"./format-CKxSMkvr.js";import{_ as ee}from"./StorageGauge.vue_vue_type_style_index_0_lang-a4pXCCmZ.js";const te=Z("training",()=>{const t=$([]),n=$(!1),g=$(new Set),l=$(null),r=$({phase:null,model:null,accepted:null,toolName:null,tag:null,search:"",sortBy:"timestamp",sortDir:"desc"}),f=D(()=>{let i=[...t.value];const u=r.value;if(u.phase&&(i=i.filter(x=>x.phase===u.phase)),u.model&&(i=i.filter(x=>x.model===u.model)),u.accepted!==null&&(i=i.filter(x=>x.accepted===u.accepted)),u.toolName&&(i=i.filter(x=>x.toolName===u.toolName)),u.tag&&(i=i.filter(x=>x.tags?.includes(u.tag)??!1)),u.search){const x=u.search.toLowerCase();i=i.filter(b=>b.userPrompt.toLowerCase().includes(x)||b.response.toLowerCase().includes(x)||b.toolName.toLowerCase().includes(x))}return i.sort((x,b)=>{let y=0;return u.sortBy==="timestamp"?y=x.timestamp-b.timestamp:u.sortBy==="phase"?y=x.phase.localeCompare(b.phase):u.sortBy==="model"&&(y=x.model.localeCompare(b.model)),u.sortDir==="desc"?-y:y}),i}),d=D(()=>{const i=t.value,u=new Map,x=new Map;let b=0,y=0;for(const A of i)A.accepted&&b++,u.set(A.phase,(u.get(A.phase)??0)+1),x.set(A.model,(x.get(A.model)??0)+1),y+=A.systemPrompt.length+A.userPrompt.length+A.response.length;return{total:i.length,accepted:b,rejected:i.length-b,phaseBreakdown:u,modelBreakdown:x,estimatedBytes:y*2}}),a=D(()=>{const i=new Set;for(const u of t.value)for(const x of u.tags??[])i.add(x);return[...i].sort()}),s=D(()=>[...new Set(t.value.map(i=>i.model))].sort()),v=D(()=>[...new Set(t.value.map(i=>i.toolName))].sort());async function C(){n.value=!0;try{t.value=await B.getAllPairs()}catch(i){console.error("Failed to load training pairs:",i)}finally{n.value=!1}}async function M(i,u){await B.updatePairResponse(i,u);const x=t.value.find(b=>b.id===i);x&&(x.response=u)}async function h(i,u){await B.updatePairTags(i,u);const x=t.value.find(b=>b.id===i);x&&(x.tags=u)}async function T(i){const u=t.value.find(b=>b.id===i);if(!u)return;const x=!u.accepted;await B.updateAccepted(i,x),u.accepted=x}async function I(i){await B.deletePairs(i);const u=new Set(i);t.value=t.value.filter(x=>!u.has(x.id));for(const x of i)g.value.delete(x);l.value&&u.has(l.value)&&(l.value=null)}async function L(i,u){for(const x of i){await B.updateAccepted(x,u);const b=t.value.find(y=>y.id===x);b&&(b.accepted=u)}}async function F(i,u){for(const x of i){const b=t.value.find(A=>A.id===x);if(!b)continue;const y=[...b.tags??[]];y.includes(u)||(y.push(u),await B.updatePairTags(x,y),b.tags=y)}}function K(i){B.exportSelectedAsJsonl(i)}function q(i){const u=new Set(g.value);u.has(i)?u.delete(i):u.add(i),g.value=u}function J(){g.value=new Set(f.value.map(i=>i.id))}function O(){g.value=new Set}function X(i){l.value=l.value===i?null:i}return{pairs:t,loading:n,selectedIds:g,expandedId:l,filters:r,filteredPairs:f,stats:d,allTags:a,allModels:s,allToolNames:v,loadPairs:C,updateResponse:M,updateTags:h,toggleAccepted:T,deletePairs:I,bulkSetAccepted:L,bulkAddTag:F,exportSelected:K,toggleSelection:q,selectAll:J,deselectAll:O,setExpanded:X}}),se={class:"grid grid-cols-2 gap-3 lg:grid-cols-4"},ae={class:"rounded-lg border border-border-default bg-surface-raised p-3 space-y-2"},oe={class:"text-[10px] text-text-muted uppercase tracking-wider"},ne={class:"text-xl font-bold text-text-primary"},le={class:"rounded-lg border border-border-default bg-surface-raised p-3 space-y-2"},re={class:"text-[10px] text-text-muted uppercase tracking-wider"},de={class:"text-xl font-bold text-text-primary"},ie={class:"flex items-center gap-2 text-[10px]"},ue={class:"text-success"},ce={class:"text-error"},pe={key:0,class:"h-1.5 rounded-full bg-surface-overlay overflow-hidden"},xe={class:"rounded-lg border border-border-default bg-surface-raised p-3 space-y-2"},ge={class:"text-[10px] text-text-muted uppercase tracking-wider"},me={class:"flex flex-wrap gap-1.5"},fe={class:"rounded-lg border border-border-default bg-surface-raised p-3 space-y-2"},be={class:"text-[10px] text-text-muted uppercase tracking-wider"},ve={key:0,class:"space-y-1"},ye={class:"text-text-secondary truncate"},he={class:"text-text-muted shrink-0"},$e={key:1,class:"text-[10px] text-text-muted italic"},ke=N({__name:"TrainingDashboard",props:{stats:{}},setup(t){const n=t,g=D(()=>["draft","insights","automap","fix"].map(a=>({key:a,count:n.stats.phaseBreakdown.get(a)??0}))),l=D(()=>[...n.stats.modelBreakdown.entries()].sort((d,a)=>a[1]-d[1]).slice(0,3)),r=D(()=>n.stats.total>0?Math.round(n.stats.accepted/n.stats.total*100):0),f={draft:"bg-blue-500/20 text-blue-400",insights:"bg-amber-500/20 text-amber-400",automap:"bg-emerald-500/20 text-emerald-400",fix:"bg-rose-500/20 text-rose-400"};return(d,a)=>(p(),m("div",se,[e("div",ae,[e("p",oe,o(d.$t("training.dashboard.trainingPairs")),1),e("p",ne,o(t.stats.total),1),t.stats.estimatedBytes>0?(p(),R(ee,{key:0,used:t.stats.estimatedBytes,total:t.stats.estimatedBytes*5,compact:"",label:`~${c(_)(t.stats.estimatedBytes)} dataset`},null,8,["used","total","label"])):w("",!0)]),e("div",le,[e("p",re,o(d.$t("training.dashboard.acceptanceRate")),1),e("p",de,o(r.value)+"%",1),e("div",ie,[e("span",ue,o(t.stats.accepted)+" "+o(d.$t("training.dashboard.accepted")),1),a[0]||(a[0]=e("span",{class:"text-text-muted"},"/",-1)),e("span",ce,o(t.stats.rejected)+" "+o(d.$t("training.dashboard.rejected")),1)]),t.stats.total>0?(p(),m("div",pe,[e("div",{class:"h-full rounded-full bg-success transition-all duration-500",style:G({width:`${r.value}%`})},null,4)])):w("",!0)]),e("div",xe,[e("p",ge,o(d.$t("training.dashboard.byPhase")),1),e("div",me,[(p(!0),m(P,null,S(g.value,s=>(p(),m("span",{key:s.key,class:k(["rounded-full px-2 py-0.5 text-[10px] font-medium",f[s.key]])},o(s.key)+" "+o(s.count),3))),128))])]),e("div",fe,[e("p",be,o(d.$t("training.dashboard.topModels")),1),l.value.length>0?(p(),m("div",ve,[(p(!0),m(P,null,S(l.value,([s,v])=>(p(),m("div",{key:s,class:"flex items-center justify-between text-[10px]"},[e("span",ye,o(s),1),e("span",he,o(v),1)]))),128))])):(p(),m("p",$e,o(d.$t("common.empty.noData")),1))])]))}}),we={class:"flex flex-wrap items-center gap-2"},Ce=["value"],Te=["value"],Ae=["value"],Pe=["value"],Se=["value"],Be=["value"],De={class:"flex rounded-lg border border-border-default overflow-hidden"},Ie=["value"],Ne=["value"],je=["value","placeholder"],Me=["value"],Re=["title"],Ee=N({__name:"TrainingFilters",props:{filters:{},models:{},toolNames:{},tags:{}},emits:["update:filters"],setup(t,{emit:n}){const g=t,l=n;function r(d){l("update:filters",{...g.filters,...d})}const f=[{value:"",label:"All phases"},{value:"draft",label:"Draft"},{value:"insights",label:"Insights"},{value:"automap",label:"Auto-map"},{value:"fix",label:"Fix"}];return(d,a)=>(p(),m("div",we,[e("select",{value:t.filters.phase??"",class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[0]||(a[0]=s=>r({phase:s.target.value||null}))},[(p(),m(P,null,S(f,s=>e("option",{key:s.label,value:s.value},o(s.label),9,Te)),64))],40,Ce),e("select",{value:t.filters.model??"",class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[1]||(a[1]=s=>r({model:s.target.value||null}))},[a[10]||(a[10]=e("option",{value:""},"All models",-1)),(p(!0),m(P,null,S(t.models,s=>(p(),m("option",{key:s,value:s},o(s),9,Pe))),128))],40,Ae),e("select",{value:t.filters.toolName??"",class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[2]||(a[2]=s=>r({toolName:s.target.value||null}))},[a[11]||(a[11]=e("option",{value:""},"All tools",-1)),(p(!0),m(P,null,S(t.toolNames,s=>(p(),m("option",{key:s,value:s},o(s),9,Be))),128))],40,Se),e("div",De,[e("button",{class:k(["px-2.5 py-1.5 text-[10px] transition-colors",t.filters.accepted===null?"bg-accent/10 text-accent":"bg-surface text-text-muted hover:text-text-primary"]),onClick:a[3]||(a[3]=s=>r({accepted:null}))},"All",2),e("button",{class:k(["px-2.5 py-1.5 text-[10px] transition-colors border-l border-border-default",t.filters.accepted===!0?"bg-success/10 text-success":"bg-surface text-text-muted hover:text-text-primary"]),onClick:a[4]||(a[4]=s=>r({accepted:!0}))},"Accepted",2),e("button",{class:k(["px-2.5 py-1.5 text-[10px] transition-colors border-l border-border-default",t.filters.accepted===!1?"bg-error/10 text-error":"bg-surface text-text-muted hover:text-text-primary"]),onClick:a[5]||(a[5]=s=>r({accepted:!1}))},"Rejected",2)]),t.tags.length>0?(p(),m("select",{key:0,value:t.filters.tag??"",class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[6]||(a[6]=s=>r({tag:s.target.value||null}))},[a[12]||(a[12]=e("option",{value:""},"All tags",-1)),(p(!0),m(P,null,S(t.tags,s=>(p(),m("option",{key:s,value:s},o(s),9,Ne))),128))],40,Ie)):w("",!0),e("input",{value:t.filters.search,type:"text",placeholder:d.$t("training.filters.searchPrompts"),class:"flex-1 min-w-[150px] rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary placeholder-text-muted outline-none focus:border-accent",onInput:a[7]||(a[7]=s=>r({search:s.target.value}))},null,40,je),e("select",{value:t.filters.sortBy,class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[8]||(a[8]=s=>r({sortBy:s.target.value}))},[...a[13]||(a[13]=[e("option",{value:"timestamp"},"Sort by time",-1),e("option",{value:"phase"},"Sort by phase",-1),e("option",{value:"model"},"Sort by model",-1)])],40,Me),e("button",{class:"rounded-lg border border-border-default bg-surface px-2 py-1.5 text-xs text-text-muted hover:text-text-primary transition-colors",title:t.filters.sortDir==="desc"?"Newest first":"Oldest first",onClick:a[9]||(a[9]=s=>r({sortDir:t.filters.sortDir==="desc"?"asc":"desc"}))},o(t.filters.sortDir==="desc"?"↓":"↑"),9,Re)]))}}),Ue={class:"flex flex-wrap items-center gap-1.5"},Ve=["onClick"],ze=["placeholder"],Le=N({__name:"TrainingTagEditor",props:{tags:{},allTags:{}},emits:["update:tags"],setup(t,{emit:n}){const g=n,l=$("");function r(d,a){if(d.key==="Enter"||d.key===","){d.preventDefault();const s=l.value.trim().toLowerCase();s&&!a.includes(s)&&g("update:tags",[...a,s]),l.value=""}}function f(d,a){g("update:tags",d.filter(s=>s!==a))}return(d,a)=>(p(),m("div",Ue,[(p(!0),m(P,null,S(t.tags,s=>(p(),m("span",{key:s,class:"flex items-center gap-1 rounded-full bg-accent/10 px-2 py-0.5 text-[10px] text-accent"},[H(o(s)+" ",1),e("button",{class:"text-accent/60 hover:text-accent transition-colors",onClick:v=>f(t.tags,s)},[j(c(Q),{class:"h-2.5 w-2.5","stroke-width":3})],8,Ve)]))),128)),E(e("input",{"onUpdate:modelValue":a[0]||(a[0]=s=>l.value=s),type:"text",placeholder:d.$t("training.tags.addPlaceholder"),class:"w-20 bg-transparent text-[10px] text-text-primary placeholder-text-muted outline-none",onKeydown:a[1]||(a[1]=s=>r(s,t.tags))},null,40,ze),[[U,l.value]])]))}}),Fe={class:"rounded-lg border border-accent/30 bg-surface-raised p-4 space-y-4"},Ke={class:"flex items-center justify-between"},qe={class:"flex items-center gap-2"},Je={class:"text-xs text-text-secondary"},Oe={class:"text-[10px] text-text-muted"},Xe={class:"text-[10px] text-text-muted"},Ze={class:"flex items-center gap-2"},Ge={class:"mb-1 text-[10px] font-medium uppercase tracking-wider text-text-muted"},He={class:"max-h-32 overflow-auto rounded-lg bg-surface p-3 text-xs text-text-secondary whitespace-pre-wrap font-mono"},Qe={class:"mb-1 text-[10px] font-medium uppercase tracking-wider text-text-muted"},We={class:"max-h-32 overflow-auto rounded-lg bg-surface p-3 text-xs text-text-secondary whitespace-pre-wrap font-mono"},Ye={key:0},_e={class:"mb-1 text-[10px] font-medium uppercase tracking-wider text-accent"},et={class:"max-h-40 overflow-auto rounded-lg bg-accent/5 border border-accent/20 p-3 text-xs text-text-secondary whitespace-pre-wrap font-mono italic"},tt={class:"mb-1 flex items-center justify-between"},st={class:"text-[10px] font-medium uppercase tracking-wider text-text-muted"},at={key:1,class:"max-h-64 overflow-auto rounded-lg bg-surface p-3 text-xs text-text-secondary whitespace-pre-wrap font-mono"},ot={key:2,class:"mt-2 flex gap-2"},nt={class:"mb-1.5 text-[10px] font-medium uppercase tracking-wider text-text-muted"},lt={class:"flex items-center justify-end border-t border-border-default pt-3"},rt=N({__name:"TrainingDetailPanel",props:{pair:{},allTags:{}},emits:["close","update:response","update:tags","toggle-accepted","delete"],setup(t,{emit:n}){const g=t,l=n,r=$(!1),f=$(""),d=$(!1);function a(){f.value=g.pair.response,r.value=!0}function s(){l("update:response",g.pair.id,f.value),r.value=!1}function v(){r.value=!1}function C(){d.value?(l("delete",g.pair.id),d.value=!1):(d.value=!0,setTimeout(()=>{d.value=!1},3e3))}const M={draft:"bg-blue-500/20 text-blue-400",insights:"bg-amber-500/20 text-amber-400",automap:"bg-emerald-500/20 text-emerald-400",fix:"bg-rose-500/20 text-rose-400"};return(h,T)=>(p(),m("div",Fe,[e("div",Ke,[e("div",qe,[e("span",{class:k(["rounded-full px-2 py-0.5 text-[10px] font-medium",M[t.pair.phase]])},o(t.pair.phase),3),e("span",Je,o(t.pair.toolName),1),e("span",Oe,o(t.pair.model),1),e("span",Xe,o(c(z)(t.pair.timestamp)),1)]),e("div",Ze,[e("button",{class:k(["rounded px-2 py-1 text-[10px] transition-colors",t.pair.accepted?"bg-success/10 text-success":"bg-error/10 text-error"]),onClick:T[0]||(T[0]=I=>l("toggle-accepted",t.pair.id))},o(t.pair.accepted?h.$t("training.detail.accepted"):h.$t("training.detail.rejected")),3),e("button",{class:"text-text-muted hover:text-text-primary transition-colors text-xs","aria-label":"Close panel",onClick:T[1]||(T[1]=I=>l("close"))}," ✕ ")])]),e("div",null,[e("p",Ge,o(h.$t("training.detail.systemPrompt")),1),e("pre",He,o(t.pair.systemPrompt),1)]),e("div",null,[e("p",Qe,o(h.$t("training.detail.userPrompt")),1),e("pre",We,o(t.pair.userPrompt),1)]),t.pair.reasoning?(p(),m("div",Ye,[e("p",_e,o(h.$t("training.detail.reasoning")),1),e("pre",et,o(t.pair.reasoning),1)])):w("",!0),e("div",null,[e("div",tt,[e("p",st,o(h.$t("training.detail.aiResponse")),1),r.value?w("",!0):(p(),m("button",{key:0,class:"text-[10px] text-accent hover:underline",onClick:a},o(h.$t("training.detail.editResponse")),1))]),r.value?E((p(),m("textarea",{key:0,"onUpdate:modelValue":T[2]||(T[2]=I=>f.value=I),rows:"8",class:"w-full rounded-lg border border-accent bg-surface px-3 py-2 text-xs text-text-primary font-mono outline-none resize-y"},null,512)),[[U,f.value]]):(p(),m("pre",at,o(t.pair.response),1)),r.value?(p(),m("div",ot,[e("button",{class:"rounded-lg bg-accent px-3 py-1 text-xs text-white hover:bg-accent-hover transition-colors",onClick:s},o(h.$t("common.actions.save")),1),e("button",{class:"rounded-lg px-3 py-1 text-xs text-text-muted hover:text-text-primary transition-colors",onClick:v},o(h.$t("common.actions.cancel")),1)])):w("",!0)]),e("div",null,[e("p",nt,o(h.$t("training.detail.tags")),1),j(Le,{tags:t.pair.tags??[],"all-tags":t.allTags,"onUpdate:tags":T[3]||(T[3]=I=>l("update:tags",t.pair.id,I))},null,8,["tags","all-tags"])]),e("div",lt,[e("button",{class:k(["rounded-lg px-3 py-1 text-xs transition-colors",d.value?"bg-error/10 text-error":"text-text-muted hover:text-error"]),onClick:C},o(d.value?h.$t("training.detail.confirmDelete"):h.$t("training.detail.deletePair")),3)])]))}}),dt={key:0,class:"rounded-lg bg-surface-overlay p-8 text-center"},it={class:"text-sm text-text-muted"},ut={key:1,class:"space-y-1"},ct=["onClick"],pt=["onClick"],xt={key:0,class:"text-[8px]"},gt={class:"shrink-0 text-text-secondary w-24 truncate"},mt={class:"shrink-0 text-text-muted w-28 truncate"},ft={class:"flex items-center gap-1 shrink-0"},bt={key:0,class:"text-[8px] text-text-muted"},vt={class:"flex-1 min-w-0 truncate text-text-muted"},yt={class:"shrink-0 text-[10px] text-text-muted"},ht=N({__name:"TrainingTable",props:{pairs:{},selectedIds:{},expandedId:{},allTags:{}},emits:["toggle-selection","set-expanded","update:response","update:tags","toggle-accepted","delete"],setup(t,{emit:n}){const g=n,l={draft:"bg-blue-500/20 text-blue-400",insights:"bg-amber-500/20 text-amber-400",automap:"bg-emerald-500/20 text-emerald-400",fix:"bg-rose-500/20 text-rose-400"};return(r,f)=>t.pairs.length===0?(p(),m("div",dt,[e("p",it,o(r.$t("training.table.noResults")),1)])):(p(),m("div",ut,[(p(!0),m(P,null,S(t.pairs,d=>(p(),m("div",{key:d.id},[e("div",{class:k(["flex items-center gap-3 rounded-lg border border-border-default bg-surface-raised px-3 py-2.5 text-xs transition-colors cursor-pointer hover:border-accent/30",t.expandedId===d.id?"border-accent/50":""]),onClick:a=>g("set-expanded",d.id)},[e("button",{class:k(["flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",t.selectedIds.has(d.id)?"border-accent bg-accent text-white":"border-border-default hover:border-accent"]),onClick:W(a=>g("toggle-selection",d.id),["stop"])},[t.selectedIds.has(d.id)?(p(),m("span",xt,"✓")):w("",!0)],10,pt),e("span",{class:k(["shrink-0 rounded-full px-2 py-0.5 text-[9px] font-medium",l[d.phase]])},o(d.phase),3),e("span",gt,o(d.toolName),1),e("span",mt,o(d.model),1),e("span",{class:k(["shrink-0 rounded-full px-1.5 py-0.5 text-[9px]",d.accepted?"bg-success/10 text-success":"bg-error/10 text-error"])},o(d.accepted?r.$t("training.dashboard.accepted"):r.$t("training.dashboard.rejected")),3),e("div",ft,[(p(!0),m(P,null,S((d.tags??[]).slice(0,2),a=>(p(),m("span",{key:a,class:"rounded-full bg-accent/10 px-1.5 py-0.5 text-[8px] text-accent"},o(a),1))),128)),(d.tags??[]).length>2?(p(),m("span",bt,"+"+o((d.tags??[]).length-2),1)):w("",!0)]),e("span",vt,o(d.userPrompt.slice(0,80))+o(d.userPrompt.length>80?"...":""),1),e("span",yt,o(c(z)(d.timestamp)),1)],10,ct),t.expandedId===d.id?(p(),R(rt,{key:0,pair:d,"all-tags":t.allTags,class:"mt-1 ms-7",onClose:f[0]||(f[0]=a=>g("set-expanded",null)),"onUpdate:response":f[1]||(f[1]=(a,s)=>g("update:response",a,s)),"onUpdate:tags":f[2]||(f[2]=(a,s)=>g("update:tags",a,s)),onToggleAccepted:f[3]||(f[3]=a=>g("toggle-accepted",a)),onDelete:f[4]||(f[4]=a=>g("delete",a))},null,8,["pair","all-tags"])):w("",!0)]))),128))]))}}),$t={class:"flex items-center gap-2 rounded-lg border border-accent/30 bg-surface-raised px-4 py-2.5"},kt={class:"text-xs font-medium text-accent"},wt={class:"ms-auto flex items-center gap-2"},Ct={key:0,class:"flex items-center gap-1"},Tt=["placeholder"],At=N({__name:"TrainingBulkActions",props:{selectedCount:{}},emits:["export","mark-accepted","mark-rejected","add-tag","delete","deselect-all"],setup(t,{emit:n}){const g=n,l=$(!1),r=$(""),f=$(!1);function d(){const s=r.value.trim().toLowerCase();s&&(g("add-tag",s),r.value="",l.value=!1)}function a(){f.value?(g("delete"),f.value=!1):(f.value=!0,setTimeout(()=>{f.value=!1},3e3))}return(s,v)=>(p(),m("div",$t,[e("span",kt,o(s.$t("training.bulk.selected",{count:t.selectedCount})),1),e("div",wt,[e("button",{class:"rounded-lg bg-surface-overlay px-3 py-1 text-[10px] text-text-secondary hover:text-text-primary transition-colors",onClick:v[0]||(v[0]=C=>g("export"))},o(s.$t("training.bulk.exportJsonl")),1),e("button",{class:"rounded-lg bg-success/10 px-3 py-1 text-[10px] text-success hover:bg-success/20 transition-colors",onClick:v[1]||(v[1]=C=>g("mark-accepted"))},o(s.$t("training.bulk.markAccepted")),1),e("button",{class:"rounded-lg bg-error/10 px-3 py-1 text-[10px] text-error hover:bg-error/20 transition-colors",onClick:v[2]||(v[2]=C=>g("mark-rejected"))},o(s.$t("training.bulk.markRejected")),1),l.value?(p(),m("div",Ct,[E(e("input",{"onUpdate:modelValue":v[3]||(v[3]=C=>r.value=C),type:"text",placeholder:s.$t("training.bulk.tagPlaceholder"),class:"w-24 rounded border border-accent bg-surface px-2 py-0.5 text-[10px] text-text-primary outline-none",onKeydown:[V(d,["enter"]),v[4]||(v[4]=V(C=>l.value=!1,["escape"]))]},null,40,Tt),[[U,r.value]]),e("button",{class:"text-[10px] text-accent hover:underline",onClick:d},o(s.$t("common.actions.add")),1)])):(p(),m("button",{key:1,class:"rounded-lg bg-accent/10 px-3 py-1 text-[10px] text-accent hover:bg-accent/20 transition-colors",onClick:v[5]||(v[5]=C=>l.value=!0)},o(s.$t("training.bulk.addTag")),1)),e("button",{class:k(["rounded-lg px-3 py-1 text-[10px] transition-colors",f.value?"bg-error/10 text-error":"text-text-muted hover:text-error"]),onClick:a},o(f.value?s.$t("training.bulk.confirm"):s.$t("training.bulk.delete")),3),e("button",{class:"text-[10px] text-text-muted hover:text-text-primary transition-colors",onClick:v[6]||(v[6]=C=>g("deselect-all"))},o(s.$t("training.bulk.deselectAll")),1)])]))}}),Pt={class:"mx-auto max-w-6xl space-y-6"},St={class:"text-lg font-semibold text-text-primary"},Bt={class:"text-sm text-text-muted"},Dt={key:1,class:"flex items-center gap-3"},It={class:"text-[10px] text-text-muted"},Nt={key:2,class:"rounded-lg border border-border-default bg-surface-raised p-8 text-center"},jt={class:"text-sm text-text-muted mb-2"},Mt={class:"text-xs text-text-muted"},zt=N({__name:"AITrainingPage",setup(t){const n=te();return Y(()=>{n.loadPairs()}),(g,l)=>(p(),m("div",Pt,[e("div",null,[e("h2",St,o(g.$t("training.title")),1),e("p",Bt,o(g.$t("training.subtitle")),1)]),j(ke,{stats:c(n).stats},null,8,["stats"]),j(Ee,{filters:c(n).filters,models:c(n).allModels,"tool-names":c(n).allToolNames,tags:c(n).allTags,"onUpdate:filters":l[0]||(l[0]=r=>c(n).filters=r)},null,8,["filters","models","tool-names","tags"]),c(n).selectedIds.size>0?(p(),R(At,{key:0,"selected-count":c(n).selectedIds.size,onExport:l[1]||(l[1]=r=>c(n).exportSelected([...c(n).selectedIds])),onMarkAccepted:l[2]||(l[2]=r=>c(n).bulkSetAccepted([...c(n).selectedIds],!0)),onMarkRejected:l[3]||(l[3]=r=>c(n).bulkSetAccepted([...c(n).selectedIds],!1)),onAddTag:l[4]||(l[4]=r=>c(n).bulkAddTag([...c(n).selectedIds],r)),onDelete:l[5]||(l[5]=r=>c(n).deletePairs([...c(n).selectedIds])),onDeselectAll:l[6]||(l[6]=r=>c(n).deselectAll())},null,8,["selected-count"])):w("",!0),c(n).filteredPairs.length>0?(p(),m("div",Dt,[e("button",{class:"text-[10px] text-text-muted hover:text-accent transition-colors",onClick:l[7]||(l[7]=r=>c(n).selectedIds.size===c(n).filteredPairs.length?c(n).deselectAll():c(n).selectAll())},o(c(n).selectedIds.size===c(n).filteredPairs.length?g.$t("training.table.deselectAll"):g.$t("training.table.selectAll")+" "+c(n).filteredPairs.length),1),e("span",It,o(g.$t("training.table.pairsShown",{shown:c(n).filteredPairs.length,total:c(n).pairs.length})),1)])):w("",!0),j(ht,{pairs:c(n).filteredPairs,"selected-ids":c(n).selectedIds,"expanded-id":c(n).expandedId,"all-tags":c(n).allTags,onToggleSelection:l[8]||(l[8]=r=>c(n).toggleSelection(r)),onSetExpanded:l[9]||(l[9]=r=>c(n).setExpanded(r)),"onUpdate:response":l[10]||(l[10]=(r,f)=>c(n).updateResponse(r,f)),"onUpdate:tags":l[11]||(l[11]=(r,f)=>c(n).updateTags(r,f)),onToggleAccepted:l[12]||(l[12]=r=>c(n).toggleAccepted(r)),onDelete:l[13]||(l[13]=r=>c(n).deletePairs([r]))},null,8,["pairs","selected-ids","expanded-id","all-tags"]),c(n).pairs.length===0&&!c(n).loading?(p(),m("div",Nt,[e("p",jt,o(g.$t("training.empty.title")),1),e("p",Mt,o(g.$t("training.empty.description")),1)])):w("",!0)]))}});export{zt as default};
|
|
1
|
+
import{d as Z,r as $,a as D,b as N,e as m,f as e,t as o,q as R,u as c,i as w,l as G,F as P,k as S,g as k,j as p,h as H,p as j,X as Q,C as E,D as U,v as W,Z as V,B as Y}from"./index-YRiA3BUj.js";import{canvasAiDB as B}from"./canvas-ai-db-Da1vIVOq.js";import{b as _,d as z}from"./format-CKxSMkvr.js";import{_ as ee}from"./StorageGauge.vue_vue_type_style_index_0_lang-CUFDMHxV.js";const te=Z("training",()=>{const t=$([]),n=$(!1),g=$(new Set),l=$(null),r=$({phase:null,model:null,accepted:null,toolName:null,tag:null,search:"",sortBy:"timestamp",sortDir:"desc"}),f=D(()=>{let i=[...t.value];const u=r.value;if(u.phase&&(i=i.filter(x=>x.phase===u.phase)),u.model&&(i=i.filter(x=>x.model===u.model)),u.accepted!==null&&(i=i.filter(x=>x.accepted===u.accepted)),u.toolName&&(i=i.filter(x=>x.toolName===u.toolName)),u.tag&&(i=i.filter(x=>x.tags?.includes(u.tag)??!1)),u.search){const x=u.search.toLowerCase();i=i.filter(b=>b.userPrompt.toLowerCase().includes(x)||b.response.toLowerCase().includes(x)||b.toolName.toLowerCase().includes(x))}return i.sort((x,b)=>{let y=0;return u.sortBy==="timestamp"?y=x.timestamp-b.timestamp:u.sortBy==="phase"?y=x.phase.localeCompare(b.phase):u.sortBy==="model"&&(y=x.model.localeCompare(b.model)),u.sortDir==="desc"?-y:y}),i}),d=D(()=>{const i=t.value,u=new Map,x=new Map;let b=0,y=0;for(const A of i)A.accepted&&b++,u.set(A.phase,(u.get(A.phase)??0)+1),x.set(A.model,(x.get(A.model)??0)+1),y+=A.systemPrompt.length+A.userPrompt.length+A.response.length;return{total:i.length,accepted:b,rejected:i.length-b,phaseBreakdown:u,modelBreakdown:x,estimatedBytes:y*2}}),a=D(()=>{const i=new Set;for(const u of t.value)for(const x of u.tags??[])i.add(x);return[...i].sort()}),s=D(()=>[...new Set(t.value.map(i=>i.model))].sort()),v=D(()=>[...new Set(t.value.map(i=>i.toolName))].sort());async function C(){n.value=!0;try{t.value=await B.getAllPairs()}catch(i){console.error("Failed to load training pairs:",i)}finally{n.value=!1}}async function M(i,u){await B.updatePairResponse(i,u);const x=t.value.find(b=>b.id===i);x&&(x.response=u)}async function h(i,u){await B.updatePairTags(i,u);const x=t.value.find(b=>b.id===i);x&&(x.tags=u)}async function T(i){const u=t.value.find(b=>b.id===i);if(!u)return;const x=!u.accepted;await B.updateAccepted(i,x),u.accepted=x}async function I(i){await B.deletePairs(i);const u=new Set(i);t.value=t.value.filter(x=>!u.has(x.id));for(const x of i)g.value.delete(x);l.value&&u.has(l.value)&&(l.value=null)}async function L(i,u){for(const x of i){await B.updateAccepted(x,u);const b=t.value.find(y=>y.id===x);b&&(b.accepted=u)}}async function F(i,u){for(const x of i){const b=t.value.find(A=>A.id===x);if(!b)continue;const y=[...b.tags??[]];y.includes(u)||(y.push(u),await B.updatePairTags(x,y),b.tags=y)}}function K(i){B.exportSelectedAsJsonl(i)}function q(i){const u=new Set(g.value);u.has(i)?u.delete(i):u.add(i),g.value=u}function J(){g.value=new Set(f.value.map(i=>i.id))}function O(){g.value=new Set}function X(i){l.value=l.value===i?null:i}return{pairs:t,loading:n,selectedIds:g,expandedId:l,filters:r,filteredPairs:f,stats:d,allTags:a,allModels:s,allToolNames:v,loadPairs:C,updateResponse:M,updateTags:h,toggleAccepted:T,deletePairs:I,bulkSetAccepted:L,bulkAddTag:F,exportSelected:K,toggleSelection:q,selectAll:J,deselectAll:O,setExpanded:X}}),se={class:"grid grid-cols-2 gap-3 lg:grid-cols-4"},ae={class:"rounded-lg border border-border-default bg-surface-raised p-3 space-y-2"},oe={class:"text-[10px] text-text-muted uppercase tracking-wider"},ne={class:"text-xl font-bold text-text-primary"},le={class:"rounded-lg border border-border-default bg-surface-raised p-3 space-y-2"},re={class:"text-[10px] text-text-muted uppercase tracking-wider"},de={class:"text-xl font-bold text-text-primary"},ie={class:"flex items-center gap-2 text-[10px]"},ue={class:"text-success"},ce={class:"text-error"},pe={key:0,class:"h-1.5 rounded-full bg-surface-overlay overflow-hidden"},xe={class:"rounded-lg border border-border-default bg-surface-raised p-3 space-y-2"},ge={class:"text-[10px] text-text-muted uppercase tracking-wider"},me={class:"flex flex-wrap gap-1.5"},fe={class:"rounded-lg border border-border-default bg-surface-raised p-3 space-y-2"},be={class:"text-[10px] text-text-muted uppercase tracking-wider"},ve={key:0,class:"space-y-1"},ye={class:"text-text-secondary truncate"},he={class:"text-text-muted shrink-0"},$e={key:1,class:"text-[10px] text-text-muted italic"},ke=N({__name:"TrainingDashboard",props:{stats:{}},setup(t){const n=t,g=D(()=>["draft","insights","automap","fix"].map(a=>({key:a,count:n.stats.phaseBreakdown.get(a)??0}))),l=D(()=>[...n.stats.modelBreakdown.entries()].sort((d,a)=>a[1]-d[1]).slice(0,3)),r=D(()=>n.stats.total>0?Math.round(n.stats.accepted/n.stats.total*100):0),f={draft:"bg-blue-500/20 text-blue-400",insights:"bg-amber-500/20 text-amber-400",automap:"bg-emerald-500/20 text-emerald-400",fix:"bg-rose-500/20 text-rose-400"};return(d,a)=>(p(),m("div",se,[e("div",ae,[e("p",oe,o(d.$t("training.dashboard.trainingPairs")),1),e("p",ne,o(t.stats.total),1),t.stats.estimatedBytes>0?(p(),R(ee,{key:0,used:t.stats.estimatedBytes,total:t.stats.estimatedBytes*5,compact:"",label:`~${c(_)(t.stats.estimatedBytes)} dataset`},null,8,["used","total","label"])):w("",!0)]),e("div",le,[e("p",re,o(d.$t("training.dashboard.acceptanceRate")),1),e("p",de,o(r.value)+"%",1),e("div",ie,[e("span",ue,o(t.stats.accepted)+" "+o(d.$t("training.dashboard.accepted")),1),a[0]||(a[0]=e("span",{class:"text-text-muted"},"/",-1)),e("span",ce,o(t.stats.rejected)+" "+o(d.$t("training.dashboard.rejected")),1)]),t.stats.total>0?(p(),m("div",pe,[e("div",{class:"h-full rounded-full bg-success transition-all duration-500",style:G({width:`${r.value}%`})},null,4)])):w("",!0)]),e("div",xe,[e("p",ge,o(d.$t("training.dashboard.byPhase")),1),e("div",me,[(p(!0),m(P,null,S(g.value,s=>(p(),m("span",{key:s.key,class:k(["rounded-full px-2 py-0.5 text-[10px] font-medium",f[s.key]])},o(s.key)+" "+o(s.count),3))),128))])]),e("div",fe,[e("p",be,o(d.$t("training.dashboard.topModels")),1),l.value.length>0?(p(),m("div",ve,[(p(!0),m(P,null,S(l.value,([s,v])=>(p(),m("div",{key:s,class:"flex items-center justify-between text-[10px]"},[e("span",ye,o(s),1),e("span",he,o(v),1)]))),128))])):(p(),m("p",$e,o(d.$t("common.empty.noData")),1))])]))}}),we={class:"flex flex-wrap items-center gap-2"},Ce=["value"],Te=["value"],Ae=["value"],Pe=["value"],Se=["value"],Be=["value"],De={class:"flex rounded-lg border border-border-default overflow-hidden"},Ie=["value"],Ne=["value"],je=["value","placeholder"],Me=["value"],Re=["title"],Ee=N({__name:"TrainingFilters",props:{filters:{},models:{},toolNames:{},tags:{}},emits:["update:filters"],setup(t,{emit:n}){const g=t,l=n;function r(d){l("update:filters",{...g.filters,...d})}const f=[{value:"",label:"All phases"},{value:"draft",label:"Draft"},{value:"insights",label:"Insights"},{value:"automap",label:"Auto-map"},{value:"fix",label:"Fix"}];return(d,a)=>(p(),m("div",we,[e("select",{value:t.filters.phase??"",class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[0]||(a[0]=s=>r({phase:s.target.value||null}))},[(p(),m(P,null,S(f,s=>e("option",{key:s.label,value:s.value},o(s.label),9,Te)),64))],40,Ce),e("select",{value:t.filters.model??"",class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[1]||(a[1]=s=>r({model:s.target.value||null}))},[a[10]||(a[10]=e("option",{value:""},"All models",-1)),(p(!0),m(P,null,S(t.models,s=>(p(),m("option",{key:s,value:s},o(s),9,Pe))),128))],40,Ae),e("select",{value:t.filters.toolName??"",class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[2]||(a[2]=s=>r({toolName:s.target.value||null}))},[a[11]||(a[11]=e("option",{value:""},"All tools",-1)),(p(!0),m(P,null,S(t.toolNames,s=>(p(),m("option",{key:s,value:s},o(s),9,Be))),128))],40,Se),e("div",De,[e("button",{class:k(["px-2.5 py-1.5 text-[10px] transition-colors",t.filters.accepted===null?"bg-accent/10 text-accent":"bg-surface text-text-muted hover:text-text-primary"]),onClick:a[3]||(a[3]=s=>r({accepted:null}))},"All",2),e("button",{class:k(["px-2.5 py-1.5 text-[10px] transition-colors border-l border-border-default",t.filters.accepted===!0?"bg-success/10 text-success":"bg-surface text-text-muted hover:text-text-primary"]),onClick:a[4]||(a[4]=s=>r({accepted:!0}))},"Accepted",2),e("button",{class:k(["px-2.5 py-1.5 text-[10px] transition-colors border-l border-border-default",t.filters.accepted===!1?"bg-error/10 text-error":"bg-surface text-text-muted hover:text-text-primary"]),onClick:a[5]||(a[5]=s=>r({accepted:!1}))},"Rejected",2)]),t.tags.length>0?(p(),m("select",{key:0,value:t.filters.tag??"",class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[6]||(a[6]=s=>r({tag:s.target.value||null}))},[a[12]||(a[12]=e("option",{value:""},"All tags",-1)),(p(!0),m(P,null,S(t.tags,s=>(p(),m("option",{key:s,value:s},o(s),9,Ne))),128))],40,Ie)):w("",!0),e("input",{value:t.filters.search,type:"text",placeholder:d.$t("training.filters.searchPrompts"),class:"flex-1 min-w-[150px] rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary placeholder-text-muted outline-none focus:border-accent",onInput:a[7]||(a[7]=s=>r({search:s.target.value}))},null,40,je),e("select",{value:t.filters.sortBy,class:"rounded-lg border border-border-default bg-surface px-2.5 py-1.5 text-xs text-text-primary outline-none focus:border-accent",onChange:a[8]||(a[8]=s=>r({sortBy:s.target.value}))},[...a[13]||(a[13]=[e("option",{value:"timestamp"},"Sort by time",-1),e("option",{value:"phase"},"Sort by phase",-1),e("option",{value:"model"},"Sort by model",-1)])],40,Me),e("button",{class:"rounded-lg border border-border-default bg-surface px-2 py-1.5 text-xs text-text-muted hover:text-text-primary transition-colors",title:t.filters.sortDir==="desc"?"Newest first":"Oldest first",onClick:a[9]||(a[9]=s=>r({sortDir:t.filters.sortDir==="desc"?"asc":"desc"}))},o(t.filters.sortDir==="desc"?"↓":"↑"),9,Re)]))}}),Ue={class:"flex flex-wrap items-center gap-1.5"},Ve=["onClick"],ze=["placeholder"],Le=N({__name:"TrainingTagEditor",props:{tags:{},allTags:{}},emits:["update:tags"],setup(t,{emit:n}){const g=n,l=$("");function r(d,a){if(d.key==="Enter"||d.key===","){d.preventDefault();const s=l.value.trim().toLowerCase();s&&!a.includes(s)&&g("update:tags",[...a,s]),l.value=""}}function f(d,a){g("update:tags",d.filter(s=>s!==a))}return(d,a)=>(p(),m("div",Ue,[(p(!0),m(P,null,S(t.tags,s=>(p(),m("span",{key:s,class:"flex items-center gap-1 rounded-full bg-accent/10 px-2 py-0.5 text-[10px] text-accent"},[H(o(s)+" ",1),e("button",{class:"text-accent/60 hover:text-accent transition-colors",onClick:v=>f(t.tags,s)},[j(c(Q),{class:"h-2.5 w-2.5","stroke-width":3})],8,Ve)]))),128)),E(e("input",{"onUpdate:modelValue":a[0]||(a[0]=s=>l.value=s),type:"text",placeholder:d.$t("training.tags.addPlaceholder"),class:"w-20 bg-transparent text-[10px] text-text-primary placeholder-text-muted outline-none",onKeydown:a[1]||(a[1]=s=>r(s,t.tags))},null,40,ze),[[U,l.value]])]))}}),Fe={class:"rounded-lg border border-accent/30 bg-surface-raised p-4 space-y-4"},Ke={class:"flex items-center justify-between"},qe={class:"flex items-center gap-2"},Je={class:"text-xs text-text-secondary"},Oe={class:"text-[10px] text-text-muted"},Xe={class:"text-[10px] text-text-muted"},Ze={class:"flex items-center gap-2"},Ge={class:"mb-1 text-[10px] font-medium uppercase tracking-wider text-text-muted"},He={class:"max-h-32 overflow-auto rounded-lg bg-surface p-3 text-xs text-text-secondary whitespace-pre-wrap font-mono"},Qe={class:"mb-1 text-[10px] font-medium uppercase tracking-wider text-text-muted"},We={class:"max-h-32 overflow-auto rounded-lg bg-surface p-3 text-xs text-text-secondary whitespace-pre-wrap font-mono"},Ye={key:0},_e={class:"mb-1 text-[10px] font-medium uppercase tracking-wider text-accent"},et={class:"max-h-40 overflow-auto rounded-lg bg-accent/5 border border-accent/20 p-3 text-xs text-text-secondary whitespace-pre-wrap font-mono italic"},tt={class:"mb-1 flex items-center justify-between"},st={class:"text-[10px] font-medium uppercase tracking-wider text-text-muted"},at={key:1,class:"max-h-64 overflow-auto rounded-lg bg-surface p-3 text-xs text-text-secondary whitespace-pre-wrap font-mono"},ot={key:2,class:"mt-2 flex gap-2"},nt={class:"mb-1.5 text-[10px] font-medium uppercase tracking-wider text-text-muted"},lt={class:"flex items-center justify-end border-t border-border-default pt-3"},rt=N({__name:"TrainingDetailPanel",props:{pair:{},allTags:{}},emits:["close","update:response","update:tags","toggle-accepted","delete"],setup(t,{emit:n}){const g=t,l=n,r=$(!1),f=$(""),d=$(!1);function a(){f.value=g.pair.response,r.value=!0}function s(){l("update:response",g.pair.id,f.value),r.value=!1}function v(){r.value=!1}function C(){d.value?(l("delete",g.pair.id),d.value=!1):(d.value=!0,setTimeout(()=>{d.value=!1},3e3))}const M={draft:"bg-blue-500/20 text-blue-400",insights:"bg-amber-500/20 text-amber-400",automap:"bg-emerald-500/20 text-emerald-400",fix:"bg-rose-500/20 text-rose-400"};return(h,T)=>(p(),m("div",Fe,[e("div",Ke,[e("div",qe,[e("span",{class:k(["rounded-full px-2 py-0.5 text-[10px] font-medium",M[t.pair.phase]])},o(t.pair.phase),3),e("span",Je,o(t.pair.toolName),1),e("span",Oe,o(t.pair.model),1),e("span",Xe,o(c(z)(t.pair.timestamp)),1)]),e("div",Ze,[e("button",{class:k(["rounded px-2 py-1 text-[10px] transition-colors",t.pair.accepted?"bg-success/10 text-success":"bg-error/10 text-error"]),onClick:T[0]||(T[0]=I=>l("toggle-accepted",t.pair.id))},o(t.pair.accepted?h.$t("training.detail.accepted"):h.$t("training.detail.rejected")),3),e("button",{class:"text-text-muted hover:text-text-primary transition-colors text-xs","aria-label":"Close panel",onClick:T[1]||(T[1]=I=>l("close"))}," ✕ ")])]),e("div",null,[e("p",Ge,o(h.$t("training.detail.systemPrompt")),1),e("pre",He,o(t.pair.systemPrompt),1)]),e("div",null,[e("p",Qe,o(h.$t("training.detail.userPrompt")),1),e("pre",We,o(t.pair.userPrompt),1)]),t.pair.reasoning?(p(),m("div",Ye,[e("p",_e,o(h.$t("training.detail.reasoning")),1),e("pre",et,o(t.pair.reasoning),1)])):w("",!0),e("div",null,[e("div",tt,[e("p",st,o(h.$t("training.detail.aiResponse")),1),r.value?w("",!0):(p(),m("button",{key:0,class:"text-[10px] text-accent hover:underline",onClick:a},o(h.$t("training.detail.editResponse")),1))]),r.value?E((p(),m("textarea",{key:0,"onUpdate:modelValue":T[2]||(T[2]=I=>f.value=I),rows:"8",class:"w-full rounded-lg border border-accent bg-surface px-3 py-2 text-xs text-text-primary font-mono outline-none resize-y"},null,512)),[[U,f.value]]):(p(),m("pre",at,o(t.pair.response),1)),r.value?(p(),m("div",ot,[e("button",{class:"rounded-lg bg-accent px-3 py-1 text-xs text-white hover:bg-accent-hover transition-colors",onClick:s},o(h.$t("common.actions.save")),1),e("button",{class:"rounded-lg px-3 py-1 text-xs text-text-muted hover:text-text-primary transition-colors",onClick:v},o(h.$t("common.actions.cancel")),1)])):w("",!0)]),e("div",null,[e("p",nt,o(h.$t("training.detail.tags")),1),j(Le,{tags:t.pair.tags??[],"all-tags":t.allTags,"onUpdate:tags":T[3]||(T[3]=I=>l("update:tags",t.pair.id,I))},null,8,["tags","all-tags"])]),e("div",lt,[e("button",{class:k(["rounded-lg px-3 py-1 text-xs transition-colors",d.value?"bg-error/10 text-error":"text-text-muted hover:text-error"]),onClick:C},o(d.value?h.$t("training.detail.confirmDelete"):h.$t("training.detail.deletePair")),3)])]))}}),dt={key:0,class:"rounded-lg bg-surface-overlay p-8 text-center"},it={class:"text-sm text-text-muted"},ut={key:1,class:"space-y-1"},ct=["onClick"],pt=["onClick"],xt={key:0,class:"text-[8px]"},gt={class:"shrink-0 text-text-secondary w-24 truncate"},mt={class:"shrink-0 text-text-muted w-28 truncate"},ft={class:"flex items-center gap-1 shrink-0"},bt={key:0,class:"text-[8px] text-text-muted"},vt={class:"flex-1 min-w-0 truncate text-text-muted"},yt={class:"shrink-0 text-[10px] text-text-muted"},ht=N({__name:"TrainingTable",props:{pairs:{},selectedIds:{},expandedId:{},allTags:{}},emits:["toggle-selection","set-expanded","update:response","update:tags","toggle-accepted","delete"],setup(t,{emit:n}){const g=n,l={draft:"bg-blue-500/20 text-blue-400",insights:"bg-amber-500/20 text-amber-400",automap:"bg-emerald-500/20 text-emerald-400",fix:"bg-rose-500/20 text-rose-400"};return(r,f)=>t.pairs.length===0?(p(),m("div",dt,[e("p",it,o(r.$t("training.table.noResults")),1)])):(p(),m("div",ut,[(p(!0),m(P,null,S(t.pairs,d=>(p(),m("div",{key:d.id},[e("div",{class:k(["flex items-center gap-3 rounded-lg border border-border-default bg-surface-raised px-3 py-2.5 text-xs transition-colors cursor-pointer hover:border-accent/30",t.expandedId===d.id?"border-accent/50":""]),onClick:a=>g("set-expanded",d.id)},[e("button",{class:k(["flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",t.selectedIds.has(d.id)?"border-accent bg-accent text-white":"border-border-default hover:border-accent"]),onClick:W(a=>g("toggle-selection",d.id),["stop"])},[t.selectedIds.has(d.id)?(p(),m("span",xt,"✓")):w("",!0)],10,pt),e("span",{class:k(["shrink-0 rounded-full px-2 py-0.5 text-[9px] font-medium",l[d.phase]])},o(d.phase),3),e("span",gt,o(d.toolName),1),e("span",mt,o(d.model),1),e("span",{class:k(["shrink-0 rounded-full px-1.5 py-0.5 text-[9px]",d.accepted?"bg-success/10 text-success":"bg-error/10 text-error"])},o(d.accepted?r.$t("training.dashboard.accepted"):r.$t("training.dashboard.rejected")),3),e("div",ft,[(p(!0),m(P,null,S((d.tags??[]).slice(0,2),a=>(p(),m("span",{key:a,class:"rounded-full bg-accent/10 px-1.5 py-0.5 text-[8px] text-accent"},o(a),1))),128)),(d.tags??[]).length>2?(p(),m("span",bt,"+"+o((d.tags??[]).length-2),1)):w("",!0)]),e("span",vt,o(d.userPrompt.slice(0,80))+o(d.userPrompt.length>80?"...":""),1),e("span",yt,o(c(z)(d.timestamp)),1)],10,ct),t.expandedId===d.id?(p(),R(rt,{key:0,pair:d,"all-tags":t.allTags,class:"mt-1 ms-7",onClose:f[0]||(f[0]=a=>g("set-expanded",null)),"onUpdate:response":f[1]||(f[1]=(a,s)=>g("update:response",a,s)),"onUpdate:tags":f[2]||(f[2]=(a,s)=>g("update:tags",a,s)),onToggleAccepted:f[3]||(f[3]=a=>g("toggle-accepted",a)),onDelete:f[4]||(f[4]=a=>g("delete",a))},null,8,["pair","all-tags"])):w("",!0)]))),128))]))}}),$t={class:"flex items-center gap-2 rounded-lg border border-accent/30 bg-surface-raised px-4 py-2.5"},kt={class:"text-xs font-medium text-accent"},wt={class:"ms-auto flex items-center gap-2"},Ct={key:0,class:"flex items-center gap-1"},Tt=["placeholder"],At=N({__name:"TrainingBulkActions",props:{selectedCount:{}},emits:["export","mark-accepted","mark-rejected","add-tag","delete","deselect-all"],setup(t,{emit:n}){const g=n,l=$(!1),r=$(""),f=$(!1);function d(){const s=r.value.trim().toLowerCase();s&&(g("add-tag",s),r.value="",l.value=!1)}function a(){f.value?(g("delete"),f.value=!1):(f.value=!0,setTimeout(()=>{f.value=!1},3e3))}return(s,v)=>(p(),m("div",$t,[e("span",kt,o(s.$t("training.bulk.selected",{count:t.selectedCount})),1),e("div",wt,[e("button",{class:"rounded-lg bg-surface-overlay px-3 py-1 text-[10px] text-text-secondary hover:text-text-primary transition-colors",onClick:v[0]||(v[0]=C=>g("export"))},o(s.$t("training.bulk.exportJsonl")),1),e("button",{class:"rounded-lg bg-success/10 px-3 py-1 text-[10px] text-success hover:bg-success/20 transition-colors",onClick:v[1]||(v[1]=C=>g("mark-accepted"))},o(s.$t("training.bulk.markAccepted")),1),e("button",{class:"rounded-lg bg-error/10 px-3 py-1 text-[10px] text-error hover:bg-error/20 transition-colors",onClick:v[2]||(v[2]=C=>g("mark-rejected"))},o(s.$t("training.bulk.markRejected")),1),l.value?(p(),m("div",Ct,[E(e("input",{"onUpdate:modelValue":v[3]||(v[3]=C=>r.value=C),type:"text",placeholder:s.$t("training.bulk.tagPlaceholder"),class:"w-24 rounded border border-accent bg-surface px-2 py-0.5 text-[10px] text-text-primary outline-none",onKeydown:[V(d,["enter"]),v[4]||(v[4]=V(C=>l.value=!1,["escape"]))]},null,40,Tt),[[U,r.value]]),e("button",{class:"text-[10px] text-accent hover:underline",onClick:d},o(s.$t("common.actions.add")),1)])):(p(),m("button",{key:1,class:"rounded-lg bg-accent/10 px-3 py-1 text-[10px] text-accent hover:bg-accent/20 transition-colors",onClick:v[5]||(v[5]=C=>l.value=!0)},o(s.$t("training.bulk.addTag")),1)),e("button",{class:k(["rounded-lg px-3 py-1 text-[10px] transition-colors",f.value?"bg-error/10 text-error":"text-text-muted hover:text-error"]),onClick:a},o(f.value?s.$t("training.bulk.confirm"):s.$t("training.bulk.delete")),3),e("button",{class:"text-[10px] text-text-muted hover:text-text-primary transition-colors",onClick:v[6]||(v[6]=C=>g("deselect-all"))},o(s.$t("training.bulk.deselectAll")),1)])]))}}),Pt={class:"mx-auto max-w-6xl space-y-6"},St={class:"text-lg font-semibold text-text-primary"},Bt={class:"text-sm text-text-muted"},Dt={key:1,class:"flex items-center gap-3"},It={class:"text-[10px] text-text-muted"},Nt={key:2,class:"rounded-lg border border-border-default bg-surface-raised p-8 text-center"},jt={class:"text-sm text-text-muted mb-2"},Mt={class:"text-xs text-text-muted"},zt=N({__name:"AITrainingPage",setup(t){const n=te();return Y(()=>{n.loadPairs()}),(g,l)=>(p(),m("div",Pt,[e("div",null,[e("h2",St,o(g.$t("training.title")),1),e("p",Bt,o(g.$t("training.subtitle")),1)]),j(ke,{stats:c(n).stats},null,8,["stats"]),j(Ee,{filters:c(n).filters,models:c(n).allModels,"tool-names":c(n).allToolNames,tags:c(n).allTags,"onUpdate:filters":l[0]||(l[0]=r=>c(n).filters=r)},null,8,["filters","models","tool-names","tags"]),c(n).selectedIds.size>0?(p(),R(At,{key:0,"selected-count":c(n).selectedIds.size,onExport:l[1]||(l[1]=r=>c(n).exportSelected([...c(n).selectedIds])),onMarkAccepted:l[2]||(l[2]=r=>c(n).bulkSetAccepted([...c(n).selectedIds],!0)),onMarkRejected:l[3]||(l[3]=r=>c(n).bulkSetAccepted([...c(n).selectedIds],!1)),onAddTag:l[4]||(l[4]=r=>c(n).bulkAddTag([...c(n).selectedIds],r)),onDelete:l[5]||(l[5]=r=>c(n).deletePairs([...c(n).selectedIds])),onDeselectAll:l[6]||(l[6]=r=>c(n).deselectAll())},null,8,["selected-count"])):w("",!0),c(n).filteredPairs.length>0?(p(),m("div",Dt,[e("button",{class:"text-[10px] text-text-muted hover:text-accent transition-colors",onClick:l[7]||(l[7]=r=>c(n).selectedIds.size===c(n).filteredPairs.length?c(n).deselectAll():c(n).selectAll())},o(c(n).selectedIds.size===c(n).filteredPairs.length?g.$t("training.table.deselectAll"):g.$t("training.table.selectAll")+" "+c(n).filteredPairs.length),1),e("span",It,o(g.$t("training.table.pairsShown",{shown:c(n).filteredPairs.length,total:c(n).pairs.length})),1)])):w("",!0),j(ht,{pairs:c(n).filteredPairs,"selected-ids":c(n).selectedIds,"expanded-id":c(n).expandedId,"all-tags":c(n).allTags,onToggleSelection:l[8]||(l[8]=r=>c(n).toggleSelection(r)),onSetExpanded:l[9]||(l[9]=r=>c(n).setExpanded(r)),"onUpdate:response":l[10]||(l[10]=(r,f)=>c(n).updateResponse(r,f)),"onUpdate:tags":l[11]||(l[11]=(r,f)=>c(n).updateTags(r,f)),onToggleAccepted:l[12]||(l[12]=r=>c(n).toggleAccepted(r)),onDelete:l[13]||(l[13]=r=>c(n).deletePairs([r]))},null,8,["pairs","selected-ids","expanded-id","all-tags"]),c(n).pairs.length===0&&!c(n).loading?(p(),m("div",Nt,[e("p",jt,o(g.$t("training.empty.title")),1),e("p",Mt,o(g.$t("training.empty.description")),1)])):w("",!0)]))}});export{zt as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{C as v,a as R,L as D,P as T,b as L,p as $,c as k,d as C,i as H,e as O,B as F,f as B,A as K,D as Y,S as G}from"./index-DlTtfQCm.js";import{u as S}from"./metrics-store-DLxxEHpn.js";import{b as x,e as c,f as l,t as m,p as _,u as y,a as p,j as d,F as w,i as U,l as J,k as E,g as q,I as X}from"./index-DAqgYcEq.js";import{u as W}from"./session-store-DJJV_fRl.js";function P(u,n){if(u.length===0)return 0;const o=n/100*(u.length-1),a=Math.floor(o),r=Math.ceil(o);return a===r?u[a]:u[a]+(u[r]-u[a])*(o-a)}function N(u){const n=[...u].sort((o,a)=>o-a);return{p50:P(n,50),p95:P(n,95),p99:P(n,99)}}function V(u,n=10){if(u.length<n){if(u.length===0)return[];const r=N(u.map(f=>f.value));return[{label:new Date(u[u.length-1].timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),...r}]}const o=[],a=Math.max(1,Math.floor(u.length/20));for(let r=n-1;r<u.length;r+=a){const f=u.slice(Math.max(0,r-n+1),r+1),s=N(f.map(t=>t.value));o.push({label:new Date(u[r].timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),...s})}return o}const Z={class:"rounded-lg border border-border-default bg-surface-raised p-4"},tt={class:"mb-3 text-sm font-medium text-text-secondary"},et={key:0,class:"h-48"},st={key:1,class:"py-6 text-center text-xs text-text-muted"},ot=x({__name:"LatencyPercentilesChart",setup(u){v.register(R,D,T,L,$,k,C,H);const n=S(),o=p(()=>{const s=n.metricsHistory.map(t=>({timestamp:t.startedAt,value:t.totalDurationMs}));return V(s)}),a=p(()=>({labels:o.value.map(s=>s.label),datasets:[{label:"P50",data:o.value.map(s=>s.p50),borderColor:"#34d399",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3},{label:"P95",data:o.value.map(s=>s.p95),borderColor:"#fbbf24",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3},{label:"P99",data:o.value.map(s=>s.p99),borderColor:"#ef4444",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3}]})),r={responsive:!0,maintainAspectRatio:!1,interaction:{mode:"index",intersect:!1},plugins:{legend:{position:"bottom",labels:{color:"#94a3b8",font:{size:10},usePointStyle:!0,pointStyle:"line"}},tooltip:{callbacks:{label:s=>`${s.dataset.label??""}: ${(s.parsed.y??0).toFixed(0)}ms`}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10},maxRotation:45},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{ticks:{color:"#94a3b8",font:{size:10},callback:s=>`${s}ms`},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},f=p(()=>o.value.length>0);return(s,t)=>(d(),c("div",Z,[l("h3",tt,m(s.$t("cost.percentiles.latencyTitle")),1),f.value?(d(),c("div",et,[_(y(O),{data:a.value,options:r},null,8,["data"])])):(d(),c("p",st,m(s.$t("cost.percentiles.noData")),1))]))}}),at={class:"rounded-lg border border-border-default bg-surface-raised p-4"},nt={class:"mb-3 text-sm font-medium text-text-secondary"},rt={key:0,class:"h-48"},lt={key:1,class:"py-6 text-center text-xs text-text-muted"},it=x({__name:"TtftPercentilesChart",setup(u){v.register(R,D,T,L,$,k,C,H);const n=S(),o=p(()=>{const s=n.metricsHistory.map(t=>({timestamp:t.startedAt,value:t.ttftMs}));return V(s)}),a=p(()=>({labels:o.value.map(s=>s.label),datasets:[{label:"P50",data:o.value.map(s=>s.p50),borderColor:"#34d399",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3},{label:"P95",data:o.value.map(s=>s.p95),borderColor:"#fbbf24",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3},{label:"P99",data:o.value.map(s=>s.p99),borderColor:"#ef4444",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3}]})),r={responsive:!0,maintainAspectRatio:!1,interaction:{mode:"index",intersect:!1},plugins:{legend:{position:"bottom",labels:{color:"#94a3b8",font:{size:10},usePointStyle:!0,pointStyle:"line"}},tooltip:{callbacks:{label:s=>`${s.dataset.label??""}: ${(s.parsed.y??0).toFixed(0)}ms`}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10},maxRotation:45},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{ticks:{color:"#94a3b8",font:{size:10},callback:s=>`${s}ms`},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},f=p(()=>o.value.length>0);return(s,t)=>(d(),c("div",at,[l("h3",nt,m(s.$t("cost.percentiles.ttftTitle")),1),f.value?(d(),c("div",rt,[_(y(O),{data:a.value,options:r},null,8,["data"])])):(d(),c("p",lt,m(s.$t("cost.percentiles.noData")),1))]))}}),ct={class:"rounded-lg border border-border-default bg-surface-raised p-4"},dt={class:"mb-3 text-sm font-medium text-text-secondary"},ut={class:"h-48"},mt={class:"mt-2 text-[10px] text-text-muted"},pt={key:1,class:"py-6 text-center text-xs text-text-muted"},bt=x({__name:"InterTokenLatencyHistogram",setup(u){v.register(R,D,F,$,k,C);const n=S(),o=[10,25,50,75,100,150,200,300,400,500,750,1e3],a=p(()=>{const t=[];for(const b of n.metricsHistory)for(const g of b.tokenLatencies)t.push(g);if(t.length===0)return null;const e=new Array(o.length+1).fill(0);for(const b of t){let g=!1;for(let h=0;h<o.length;h++)if(b<=o[h]){e[h]++,g=!0;break}g||e[o.length]++}const i=o.map((b,g)=>g===0?`0-${b}`:`${o[g-1]}-${b}`);return i.push(`>${o[o.length-1]}`),{labels:i,counts:e,total:t.length}}),r=p(()=>a.value?{labels:a.value.labels,datasets:[{label:"Tokens",data:a.value.counts,backgroundColor:"#818cf8"}]}:{labels:[],datasets:[]}),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},tooltip:{callbacks:{label:t=>{const e=t.parsed.y??0,i=a.value?.total??1;return`${e} tokens (${(e/i*100).toFixed(1)}%)`}}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:9},maxRotation:45},grid:{display:!1},title:{display:!0,text:"ms",color:"#94a3b8",font:{size:10}}},y:{ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"},title:{display:!0,text:"Count",color:"#94a3b8",font:{size:10}}}}},s=p(()=>a.value!==null);return(t,e)=>(d(),c("div",ct,[l("h3",dt,m(t.$t("cost.percentiles.histogramTitle")),1),s.value?(d(),c(w,{key:0},[l("div",ut,[_(y(B),{data:r.value,options:f},null,8,["data"])]),l("p",mt,m(t.$t("cost.percentiles.histogramHint")),1)],64)):(d(),c("p",pt,m(t.$t("cost.percentiles.noData")),1))]))}}),ft={class:"rounded-lg border border-border-default bg-surface-raised p-4"},gt={class:"mb-3 flex items-center justify-between"},_t={class:"text-sm font-medium text-text-secondary"},ht={key:0,class:"rounded-full bg-warning/15 px-2 py-0.5 text-[9px] font-medium text-warning"},xt={class:"h-48"},yt={class:"mt-2 text-[10px] text-text-muted"},vt={key:1,class:"py-6 text-center text-xs text-text-muted"},$t=500,kt=x({__name:"LoadDurationTrendChart",setup(u){v.register(R,D,T,L,$,k,C,H);const n=S(),o=p(()=>n.metricsHistory.slice(-50).map(e=>({label:new Date(e.startedAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),loadMs:e.loadDurationMs,model:e.model,isCold:e.loadDurationMs>$t}))),a=p(()=>({labels:o.value.map(t=>t.label),datasets:[{label:"Load Duration (ms)",data:o.value.map(t=>t.loadMs),borderColor:"#a855f7",backgroundColor:"rgba(168, 85, 247, 0.1)",fill:!0,tension:.3,pointBackgroundColor:o.value.map(t=>t.isCold?"#f97316":"#a855f7"),pointRadius:o.value.map(t=>t.isCold?4:2)}]})),r={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},tooltip:{callbacks:{label:t=>{const e=o.value[t.dataIndex],i=t.parsed.y??0;return`${e?.model??""}: ${i.toFixed(0)}ms${e?.isCold?" (cold start)":""}`}}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10},maxRotation:45},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{ticks:{color:"#94a3b8",font:{size:10},callback:t=>`${t}ms`},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},f=p(()=>o.value.length>0),s=p(()=>o.value.filter(t=>t.isCold).length);return(t,e)=>(d(),c("div",ft,[l("div",gt,[l("h3",_t,m(t.$t("cost.loadDuration.title")),1),s.value>0?(d(),c("span",ht,m(s.value)+" "+m(t.$t("cost.loadDuration.coldStarts")),1)):U("",!0)]),f.value?(d(),c(w,{key:0},[l("div",xt,[_(y(O),{data:a.value,options:r},null,8,["data"])]),l("p",yt,m(t.$t("cost.loadDuration.hint")),1)],64)):(d(),c("p",vt,m(t.$t("cost.loadDuration.noData")),1))]))}}),Ct=[[/connection refused|ECONNREFUSED|fetch failed|network error|Failed to fetch/i,"connection","Connection refused"],[/timed?\s*out|ETIMEDOUT|deadline exceeded/i,"timeout","Timeout"],[/model.*not found|no such model|unknown model|pull.*first/i,"model_not_found","Model not found"],[/context length|context window|too long|maximum context|num_ctx/i,"context_exceeded","Context exceeded"],[/out of memory|OOM|CUDA.*memory|not enough memory|VRAM/i,"oom","Out of memory"],[/cancel|abort/i,"cancelled","Cancelled"],[/tool.*no implementation|tool.*failed|tool.*error/i,"tool_error","Tool error"]];function j(u){for(const[n,o,a]of Ct)if(n.test(u))return{category:o,label:a,original:u};return{category:"unknown",label:"Unknown error",original:u}}const Dt={connection:"#ef4444",timeout:"#f97316",model_not_found:"#eab308",context_exceeded:"#a855f7",oom:"#dc2626",cancelled:"#6b7280",tool_error:"#f472b6",unknown:"#94a3b8"},St={class:"rounded-lg border border-border-default bg-surface-raised p-4"},wt={class:"mb-3 text-sm font-medium text-text-secondary"},Rt={key:1,class:"py-6 text-center text-xs text-text-muted"},Mt=x({__name:"ErrorRateByModelChart",setup(u){v.register(R,D,F,$,k,C);const n=W(),o=p(()=>{const e=new Map;for(const i of n.sessions.values()){const b=i.model,g=e.get(b)??{total:0,errors:0,byCategory:{}};if(g.total++,i.status==="error"&&i.error){g.errors++;const{category:h}=j(i.error);g.byCategory[h]=(g.byCategory[h]??0)+1}e.set(b,g)}return[...e.entries()].map(([i,b])=>({model:i,...b,rate:b.total>0?b.errors/b.total:0})).filter(i=>i.total>0).sort((i,b)=>b.rate-i.rate)}),a=["connection","timeout","model_not_found","context_exceeded","oom","tool_error","cancelled","unknown"],r={connection:"Connection",timeout:"Timeout",model_not_found:"Not found",context_exceeded:"Context exceeded",oom:"Out of memory",tool_error:"Tool error",cancelled:"Cancelled",unknown:"Unknown"},f=p(()=>({labels:o.value.map(e=>e.model),datasets:a.filter(e=>o.value.some(i=>(i.byCategory[e]??0)>0)).map(e=>({label:r[e],data:o.value.map(i=>i.byCategory[e]??0),backgroundColor:Dt[e]}))})),s={indexAxis:"y",responsive:!0,maintainAspectRatio:!1,plugins:{legend:{position:"bottom",labels:{color:"#94a3b8",font:{size:10}}}},scales:{x:{stacked:!0,ticks:{color:"#94a3b8",font:{size:10},stepSize:1},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{stacked:!0,ticks:{color:"#94a3b8",font:{size:11}},grid:{display:!1}}}},t=p(()=>o.value.some(e=>e.errors>0));return(e,i)=>(d(),c("div",St,[l("h3",wt,m(e.$t("cost.errors.title")),1),t.value?(d(),c("div",{key:0,style:J({height:Math.max(100,o.value.length*36)+"px"})},[_(y(B),{data:f.value,options:s},null,8,["data"])],4)):(d(),c("p",Rt,m(e.$t("cost.errors.noErrors")),1))]))}}),Tt={class:"rounded-lg border border-border-default bg-surface-raised p-4"},zt={class:"mb-3 text-sm font-medium text-text-secondary"},At={key:0,class:"h-48"},Lt={key:1,class:"py-6 text-center text-xs text-text-muted"},Ot=x({__name:"ErrorTimelineChart",setup(u){v.register(R,D,T,L,$,k,C);const n=W(),o=p(()=>{const t=[];for(const e of n.sessions.values())if(e.status==="error"&&e.error){const{category:i,label:b}=j(e.error);t.push({date:new Date(e.createdAt).toISOString().slice(0,10),category:i,model:e.model,error:b})}return t.sort((e,i)=>e.date.localeCompare(i.date))}),a=p(()=>{const t=new Map;for(const e of o.value)t.set(e.date,(t.get(e.date)??0)+1);return[...t.entries()].sort((e,i)=>e[0].localeCompare(i[0]))}),r=p(()=>({labels:a.value.map(([t])=>t),datasets:[{label:"Errors",data:a.value.map(([,t])=>t),borderColor:"#ef4444",backgroundColor:"rgba(239, 68, 68, 0.1)",fill:!0,tension:.3,pointRadius:3}]})),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{ticks:{color:"#94a3b8",font:{size:10},stepSize:1},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},s=p(()=>a.value.length>0);return(t,e)=>(d(),c("div",Tt,[l("h3",zt,m(t.$t("analytics.errors.timelineTitle")),1),s.value?(d(),c("div",At,[_(y(O),{data:r.value,options:f},null,8,["data"])])):(d(),c("p",Lt,m(t.$t("analytics.errors.noErrors")),1))]))}}),Et={class:"rounded-lg border border-border-default bg-surface-raised p-4"},Ht={class:"mb-3 text-sm font-medium text-text-secondary"},Pt={key:0,class:"h-48"},qt={key:1,class:"py-6 text-center text-xs text-text-muted"},Ft=x({__name:"RequestVolumeChart",setup(u){v.register(R,D,F,$,k,C);const n=S(),o=p(()=>{const s=new Map;for(const t of n.metricsHistory){const e=new Date(t.startedAt).toISOString().slice(0,10);s.set(e,(s.get(e)??0)+1)}return[...s.entries()].sort((t,e)=>t[0].localeCompare(e[0]))}),a=p(()=>({labels:o.value.map(([s])=>s),datasets:[{label:"Requests",data:o.value.map(([,s])=>s),backgroundColor:"#818cf8"}]})),r={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10}},grid:{display:!1}},y:{ticks:{color:"#94a3b8",font:{size:10},stepSize:1},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},f=p(()=>o.value.length>0);return(s,t)=>(d(),c("div",Et,[l("h3",Ht,m(s.$t("analytics.usage.requestVolume")),1),f.value?(d(),c("div",Pt,[_(y(B),{data:a.value,options:r},null,8,["data"])])):(d(),c("p",qt,m(s.$t("analytics.usage.noData")),1))]))}}),Bt={class:"rounded-lg border border-border-default bg-surface-raised p-4"},Wt={class:"mb-3 text-sm font-medium text-text-secondary"},It={key:0,class:"h-48"},Nt={key:1,class:"py-6 text-center text-xs text-text-muted"},Ut=x({__name:"ModelDistributionChart",setup(u){v.register(K,$,k,C);const n=S(),o=["#818cf8","#a855f7","#34d399","#fbbf24","#f472b6","#fb923c","#60a5fa","#c084fc"],a=p(()=>{const t=new Map;for(const e of n.metricsHistory)t.set(e.model,(t.get(e.model)??0)+1);return[...t.entries()].sort((e,i)=>i[1]-e[1])}),r=p(()=>({labels:a.value.map(([t])=>t),datasets:[{data:a.value.map(([,t])=>t),backgroundColor:a.value.map((t,e)=>o[e%o.length]),borderWidth:0}]})),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{position:"right",labels:{color:"#94a3b8",font:{size:10},padding:12}}}},s=p(()=>a.value.length>0);return(t,e)=>(d(),c("div",Bt,[l("h3",Wt,m(t.$t("analytics.usage.modelDistribution")),1),s.value?(d(),c("div",It,[_(y(Y),{data:r.value,options:f},null,8,["data"])])):(d(),c("p",Nt,m(t.$t("analytics.usage.noData")),1))]))}}),Vt={class:"rounded-lg border border-border-default bg-surface-raised p-4"},jt={class:"mb-3 text-sm font-medium text-text-secondary"},Qt={key:0,class:"overflow-x-auto"},Kt={class:"min-w-[600px]"},Yt={class:"flex items-center"},Gt={class:"w-10 shrink-0 text-[10px] text-text-muted"},Jt=["title"],Xt={key:1,class:"py-6 text-center text-xs text-text-muted"},Zt=x({__name:"ActiveHoursHeatmap",setup(u){const n=S(),o=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],a=Array.from({length:24},(t,e)=>e),r=p(()=>{const t=Array.from({length:7},()=>new Array(24).fill(0));let e=0;for(const i of n.metricsHistory){const b=new Date(i.startedAt),g=(b.getDay()+6)%7,h=b.getHours();t[g][h]++,t[g][h]>e&&(e=t[g][h])}return{counts:t,max:e}});function f(t){if(t===0)return"bg-surface-overlay/30";const e=r.value.max>0?t/r.value.max:0;return e>.75?"bg-accent":e>.5?"bg-accent/70":e>.25?"bg-accent/40":"bg-accent/20"}const s=p(()=>n.metricsHistory.length>0);return(t,e)=>(d(),c("div",Vt,[l("h3",jt,m(t.$t("analytics.usage.activeHours")),1),s.value?(d(),c("div",Qt,[l("div",Kt,[l("div",Yt,[e[0]||(e[0]=l("div",{class:"w-10 shrink-0"},null,-1)),(d(!0),c(w,null,E(y(a),i=>(d(),c("div",{key:i,class:"flex-1 text-center text-[8px] text-text-muted"},m(i),1))),128))]),(d(),c(w,null,E(o,(i,b)=>l("div",{key:i,class:"flex items-center gap-0.5 mt-0.5"},[l("div",Gt,m(i),1),(d(!0),c(w,null,E(y(a),g=>(d(),c("div",{key:g,class:q(["flex-1 aspect-square rounded-sm transition-colors",f(r.value.counts[b][g])]),title:`${i} ${g}:00 — ${r.value.counts[b][g]} requests`},null,10,Jt))),128))])),64))])])):(d(),c("p",Xt,m(t.$t("analytics.usage.noData")),1))]))}}),te={class:"rounded-lg border border-border-default bg-surface-raised p-4"},ee={class:"mb-3 text-sm font-medium text-text-secondary"},se={class:"h-48"},oe={class:"mt-2 text-[10px] text-text-muted"},ae={key:1,class:"py-6 text-center text-xs text-text-muted"},ne=x({__name:"SettingsImpactScatter",setup(u){v.register(D,T,$,k,C);const n=W(),o=S(),a=p(()=>{const t=[];for(const e of n.sessions.values()){const i=e.options?.temperature;if(i===void 0)continue;const b=o.getMetrics(e.id);b&&t.push({x:i,y:b.tokensPerSecond,model:e.model})}return t}),r=p(()=>({datasets:[{label:"Temperature vs Speed",data:a.value.map(t=>({x:t.x,y:t.y})),backgroundColor:"#818cf880",borderColor:"#818cf8",pointRadius:5}]})),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},tooltip:{callbacks:{label:t=>`${a.value[t.dataIndex]?.model??""}: temp=${(t.parsed.x??0).toFixed(1)} → ${(t.parsed.y??0).toFixed(1)} tok/s`}}},scales:{x:{title:{display:!0,text:"Temperature",color:"#94a3b8",font:{size:10}},ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{title:{display:!0,text:"Tokens/sec",color:"#94a3b8",font:{size:10}},ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},s=p(()=>a.value.length>=2);return(t,e)=>(d(),c("div",te,[l("h3",ee,m(t.$t("analytics.settings.title")),1),s.value?(d(),c(w,{key:0},[l("div",se,[_(y(G),{data:r.value,options:f},null,8,["data"])]),l("p",oe,m(t.$t("analytics.settings.hint")),1)],64)):(d(),c("p",ae,m(t.$t("analytics.settings.noData")),1))]))}}),re={class:"rounded-lg border border-border-default bg-surface-raised p-4"},le={class:"mb-3 text-sm font-medium text-text-secondary"},ie={key:0,class:"max-h-64 overflow-y-auto"},ce={class:"text-[10px] text-text-muted w-14 shrink-0"},de={class:"text-xs text-text-primary truncate flex-1"},ue={key:0,class:"rounded-full bg-warning/15 px-1.5 py-0.5 text-[8px] font-medium text-warning shrink-0"},me={key:1,class:"py-6 text-center text-xs text-text-muted"},pe=500,be=x({__name:"ModelLoadHistory",setup(u){const n=S(),o=p(()=>n.metricsHistory.slice(-30).map(r=>({time:new Date(r.startedAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),date:new Date(r.startedAt).toLocaleDateString(),model:r.model,loadMs:r.loadDurationMs,isCold:r.loadDurationMs>pe}))),a=p(()=>o.value.length>0);return(r,f)=>(d(),c("div",re,[l("h3",le,m(r.$t("analytics.modelLoad.title")),1),a.value?(d(),c("div",ie,[(d(!0),c(w,null,E(o.value,(s,t)=>(d(),c("div",{key:t,class:"flex items-center gap-3 border-b border-border-default/30 py-1.5 last:border-0"},[l("div",{class:q(["h-2 w-2 shrink-0 rounded-full",s.isCold?"bg-warning":"bg-success"])},null,2),l("span",ce,m(s.time),1),l("span",de,m(s.model),1),l("span",{class:q(["text-[10px] font-mono shrink-0",s.isCold?"text-warning":"text-text-muted"])},m(s.loadMs.toFixed(0))+"ms ",3),s.isCold?(d(),c("span",ue,m(r.$t("analytics.modelLoad.cold")),1)):U("",!0)]))),128))])):(d(),c("p",me,m(r.$t("analytics.modelLoad.noData")),1))]))}});function fe(u,n){const o=[];for(let a=0;a<=u.length-n;a++)o.push(u.slice(a,a+n).join(" "));return o}function ge(u,n,o){const a=u.trim().split(/\s+/).filter(Boolean),r=a.length,s=u.split(/[.!?]+/).filter(M=>M.trim().length>0).length,t=new Set(a.map(M=>M.toLowerCase())),e=r>0?t.size/r:0;let i=0;if(a.length>=8){const M=fe(a.map(z=>z.toLowerCase()),4);if(M.length>0){const z=new Map;for(const A of M)z.set(A,(z.get(A)??0)+1);let I=0;for(const A of z.values())A>1&&(I+=A);i=I/M.length}}const b=Math.min(r/50,1)*.4,g=e*.3,h=(1-i)*.3,Q=Math.min(5,Math.max(0,(b+g+h)*5));return{turnIndex:n,role:o,wordCount:r,sentenceCount:s,uniqueWordRatio:e,repetitionRatio:i,score:Q}}function _e(u){return u.filter(n=>n.role==="user"||n.role==="assistant").map((n,o)=>ge(n.content.replace(/<think>[\s\S]*?<\/think>/g,"").trim(),o,n.role))}const he={class:"rounded-lg border border-border-default bg-surface-raised p-4"},xe={class:"mb-3 text-sm font-medium text-text-secondary"},ye={class:"h-48"},ve={class:"mt-2 text-[10px] text-text-muted"},$e={key:1,class:"py-6 text-center text-xs text-text-muted"},ke=x({__name:"QualityOverTurnsChart",setup(u){v.register(R,D,T,L,$,k,C,H);const n=X(),o=p(()=>{const t=n.activeConversation;return!t||t.messages.length<2?[]:_e(t.messages)}),a=p(()=>o.value.filter(t=>t.role==="assistant")),r=p(()=>({labels:a.value.map((t,e)=>`Turn ${e+1}`),datasets:[{label:"Quality Score",data:a.value.map(t=>t.score),borderColor:"#818cf8",backgroundColor:"rgba(129, 140, 248, 0.1)",fill:!0,tension:.3,pointBackgroundColor:a.value.map(t=>t.score>=3.5?"#34d399":t.score>=2?"#fbbf24":"#ef4444"),pointRadius:4}]})),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},tooltip:{callbacks:{label:t=>{const e=a.value[t.dataIndex];return`Score: ${(t.parsed.y??0).toFixed(1)}/5 (${e?.wordCount??0} words)`}}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{min:0,max:5,ticks:{color:"#94a3b8",font:{size:10},stepSize:1},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},s=p(()=>a.value.length>=2);return(t,e)=>(d(),c("div",he,[l("h3",xe,m(t.$t("analytics.quality.title")),1),s.value?(d(),c(w,{key:0},[l("div",ye,[_(y(O),{data:r.value,options:f},null,8,["data"])]),l("p",ve,m(t.$t("analytics.quality.hint")),1)],64)):(d(),c("p",$e,m(t.$t("analytics.quality.noData")),1))]))}}),Ce={class:"h-full overflow-y-auto p-6"},De={class:"mb-6 text-lg font-semibold text-text-primary"},Se={class:"mb-8"},we={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Re={class:"mb-8"},Me={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Te={class:"grid gap-6 lg:grid-cols-2"},ze={class:"mt-6"},Ae={class:"mb-8"},Le={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Oe={class:"grid gap-6 lg:grid-cols-2"},Ee={class:"mt-6"},He={class:"mb-8"},Pe={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},qe={class:"grid gap-6 lg:grid-cols-2"},Fe={class:"mt-6"},Be={class:"mb-8"},We={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Ie={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Qe=x({__name:"AnalyticsPage",setup(u){return(n,o)=>(d(),c("div",Ce,[l("h1",De,m(n.$t("analytics.title")),1),l("section",Se,[l("h2",we,m(n.$t("analytics.sections.quality")),1),_(ke)]),l("section",Re,[l("h2",Me,m(n.$t("analytics.sections.latency")),1),l("div",Te,[_(ot),_(it)]),l("div",ze,[_(bt)])]),l("section",Ae,[l("h2",Le,m(n.$t("analytics.sections.reliability")),1),l("div",Oe,[_(Mt),_(Ot)]),l("div",Ee,[_(kt)])]),l("section",He,[l("h2",Pe,m(n.$t("analytics.sections.usage")),1),l("div",qe,[_(Ft),_(Ut)]),l("div",Fe,[_(Zt)])]),l("section",Be,[l("h2",We,m(n.$t("analytics.sections.settings")),1),_(ne)]),l("section",null,[l("h2",Ie,m(n.$t("analytics.sections.modelMemory")),1),_(be)])]))}});export{Qe as default};
|
|
1
|
+
import{C as v,a as R,L as D,P as T,b as L,p as $,c as k,d as C,i as H,e as O,B as F,f as B,A as K,D as Y,S as G}from"./index-C8P_C63i.js";import{u as S}from"./metrics-store-BCparPrV.js";import{b as x,e as c,f as l,t as m,p as _,u as y,a as p,j as d,F as w,i as U,l as J,k as E,g as q,I as X}from"./index-YRiA3BUj.js";import{u as W}from"./session-store-2bGNgO7a.js";function P(u,n){if(u.length===0)return 0;const o=n/100*(u.length-1),a=Math.floor(o),r=Math.ceil(o);return a===r?u[a]:u[a]+(u[r]-u[a])*(o-a)}function N(u){const n=[...u].sort((o,a)=>o-a);return{p50:P(n,50),p95:P(n,95),p99:P(n,99)}}function V(u,n=10){if(u.length<n){if(u.length===0)return[];const r=N(u.map(f=>f.value));return[{label:new Date(u[u.length-1].timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),...r}]}const o=[],a=Math.max(1,Math.floor(u.length/20));for(let r=n-1;r<u.length;r+=a){const f=u.slice(Math.max(0,r-n+1),r+1),s=N(f.map(t=>t.value));o.push({label:new Date(u[r].timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),...s})}return o}const Z={class:"rounded-lg border border-border-default bg-surface-raised p-4"},tt={class:"mb-3 text-sm font-medium text-text-secondary"},et={key:0,class:"h-48"},st={key:1,class:"py-6 text-center text-xs text-text-muted"},ot=x({__name:"LatencyPercentilesChart",setup(u){v.register(R,D,T,L,$,k,C,H);const n=S(),o=p(()=>{const s=n.metricsHistory.map(t=>({timestamp:t.startedAt,value:t.totalDurationMs}));return V(s)}),a=p(()=>({labels:o.value.map(s=>s.label),datasets:[{label:"P50",data:o.value.map(s=>s.p50),borderColor:"#34d399",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3},{label:"P95",data:o.value.map(s=>s.p95),borderColor:"#fbbf24",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3},{label:"P99",data:o.value.map(s=>s.p99),borderColor:"#ef4444",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3}]})),r={responsive:!0,maintainAspectRatio:!1,interaction:{mode:"index",intersect:!1},plugins:{legend:{position:"bottom",labels:{color:"#94a3b8",font:{size:10},usePointStyle:!0,pointStyle:"line"}},tooltip:{callbacks:{label:s=>`${s.dataset.label??""}: ${(s.parsed.y??0).toFixed(0)}ms`}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10},maxRotation:45},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{ticks:{color:"#94a3b8",font:{size:10},callback:s=>`${s}ms`},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},f=p(()=>o.value.length>0);return(s,t)=>(d(),c("div",Z,[l("h3",tt,m(s.$t("cost.percentiles.latencyTitle")),1),f.value?(d(),c("div",et,[_(y(O),{data:a.value,options:r},null,8,["data"])])):(d(),c("p",st,m(s.$t("cost.percentiles.noData")),1))]))}}),at={class:"rounded-lg border border-border-default bg-surface-raised p-4"},nt={class:"mb-3 text-sm font-medium text-text-secondary"},rt={key:0,class:"h-48"},lt={key:1,class:"py-6 text-center text-xs text-text-muted"},it=x({__name:"TtftPercentilesChart",setup(u){v.register(R,D,T,L,$,k,C,H);const n=S(),o=p(()=>{const s=n.metricsHistory.map(t=>({timestamp:t.startedAt,value:t.ttftMs}));return V(s)}),a=p(()=>({labels:o.value.map(s=>s.label),datasets:[{label:"P50",data:o.value.map(s=>s.p50),borderColor:"#34d399",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3},{label:"P95",data:o.value.map(s=>s.p95),borderColor:"#fbbf24",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3},{label:"P99",data:o.value.map(s=>s.p99),borderColor:"#ef4444",backgroundColor:"transparent",borderWidth:1.5,pointRadius:2,tension:.3}]})),r={responsive:!0,maintainAspectRatio:!1,interaction:{mode:"index",intersect:!1},plugins:{legend:{position:"bottom",labels:{color:"#94a3b8",font:{size:10},usePointStyle:!0,pointStyle:"line"}},tooltip:{callbacks:{label:s=>`${s.dataset.label??""}: ${(s.parsed.y??0).toFixed(0)}ms`}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10},maxRotation:45},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{ticks:{color:"#94a3b8",font:{size:10},callback:s=>`${s}ms`},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},f=p(()=>o.value.length>0);return(s,t)=>(d(),c("div",at,[l("h3",nt,m(s.$t("cost.percentiles.ttftTitle")),1),f.value?(d(),c("div",rt,[_(y(O),{data:a.value,options:r},null,8,["data"])])):(d(),c("p",lt,m(s.$t("cost.percentiles.noData")),1))]))}}),ct={class:"rounded-lg border border-border-default bg-surface-raised p-4"},dt={class:"mb-3 text-sm font-medium text-text-secondary"},ut={class:"h-48"},mt={class:"mt-2 text-[10px] text-text-muted"},pt={key:1,class:"py-6 text-center text-xs text-text-muted"},bt=x({__name:"InterTokenLatencyHistogram",setup(u){v.register(R,D,F,$,k,C);const n=S(),o=[10,25,50,75,100,150,200,300,400,500,750,1e3],a=p(()=>{const t=[];for(const b of n.metricsHistory)for(const g of b.tokenLatencies)t.push(g);if(t.length===0)return null;const e=new Array(o.length+1).fill(0);for(const b of t){let g=!1;for(let h=0;h<o.length;h++)if(b<=o[h]){e[h]++,g=!0;break}g||e[o.length]++}const i=o.map((b,g)=>g===0?`0-${b}`:`${o[g-1]}-${b}`);return i.push(`>${o[o.length-1]}`),{labels:i,counts:e,total:t.length}}),r=p(()=>a.value?{labels:a.value.labels,datasets:[{label:"Tokens",data:a.value.counts,backgroundColor:"#818cf8"}]}:{labels:[],datasets:[]}),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},tooltip:{callbacks:{label:t=>{const e=t.parsed.y??0,i=a.value?.total??1;return`${e} tokens (${(e/i*100).toFixed(1)}%)`}}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:9},maxRotation:45},grid:{display:!1},title:{display:!0,text:"ms",color:"#94a3b8",font:{size:10}}},y:{ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"},title:{display:!0,text:"Count",color:"#94a3b8",font:{size:10}}}}},s=p(()=>a.value!==null);return(t,e)=>(d(),c("div",ct,[l("h3",dt,m(t.$t("cost.percentiles.histogramTitle")),1),s.value?(d(),c(w,{key:0},[l("div",ut,[_(y(B),{data:r.value,options:f},null,8,["data"])]),l("p",mt,m(t.$t("cost.percentiles.histogramHint")),1)],64)):(d(),c("p",pt,m(t.$t("cost.percentiles.noData")),1))]))}}),ft={class:"rounded-lg border border-border-default bg-surface-raised p-4"},gt={class:"mb-3 flex items-center justify-between"},_t={class:"text-sm font-medium text-text-secondary"},ht={key:0,class:"rounded-full bg-warning/15 px-2 py-0.5 text-[9px] font-medium text-warning"},xt={class:"h-48"},yt={class:"mt-2 text-[10px] text-text-muted"},vt={key:1,class:"py-6 text-center text-xs text-text-muted"},$t=500,kt=x({__name:"LoadDurationTrendChart",setup(u){v.register(R,D,T,L,$,k,C,H);const n=S(),o=p(()=>n.metricsHistory.slice(-50).map(e=>({label:new Date(e.startedAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),loadMs:e.loadDurationMs,model:e.model,isCold:e.loadDurationMs>$t}))),a=p(()=>({labels:o.value.map(t=>t.label),datasets:[{label:"Load Duration (ms)",data:o.value.map(t=>t.loadMs),borderColor:"#a855f7",backgroundColor:"rgba(168, 85, 247, 0.1)",fill:!0,tension:.3,pointBackgroundColor:o.value.map(t=>t.isCold?"#f97316":"#a855f7"),pointRadius:o.value.map(t=>t.isCold?4:2)}]})),r={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},tooltip:{callbacks:{label:t=>{const e=o.value[t.dataIndex],i=t.parsed.y??0;return`${e?.model??""}: ${i.toFixed(0)}ms${e?.isCold?" (cold start)":""}`}}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10},maxRotation:45},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{ticks:{color:"#94a3b8",font:{size:10},callback:t=>`${t}ms`},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},f=p(()=>o.value.length>0),s=p(()=>o.value.filter(t=>t.isCold).length);return(t,e)=>(d(),c("div",ft,[l("div",gt,[l("h3",_t,m(t.$t("cost.loadDuration.title")),1),s.value>0?(d(),c("span",ht,m(s.value)+" "+m(t.$t("cost.loadDuration.coldStarts")),1)):U("",!0)]),f.value?(d(),c(w,{key:0},[l("div",xt,[_(y(O),{data:a.value,options:r},null,8,["data"])]),l("p",yt,m(t.$t("cost.loadDuration.hint")),1)],64)):(d(),c("p",vt,m(t.$t("cost.loadDuration.noData")),1))]))}}),Ct=[[/connection refused|ECONNREFUSED|fetch failed|network error|Failed to fetch/i,"connection","Connection refused"],[/timed?\s*out|ETIMEDOUT|deadline exceeded/i,"timeout","Timeout"],[/model.*not found|no such model|unknown model|pull.*first/i,"model_not_found","Model not found"],[/context length|context window|too long|maximum context|num_ctx/i,"context_exceeded","Context exceeded"],[/out of memory|OOM|CUDA.*memory|not enough memory|VRAM/i,"oom","Out of memory"],[/cancel|abort/i,"cancelled","Cancelled"],[/tool.*no implementation|tool.*failed|tool.*error/i,"tool_error","Tool error"]];function j(u){for(const[n,o,a]of Ct)if(n.test(u))return{category:o,label:a,original:u};return{category:"unknown",label:"Unknown error",original:u}}const Dt={connection:"#ef4444",timeout:"#f97316",model_not_found:"#eab308",context_exceeded:"#a855f7",oom:"#dc2626",cancelled:"#6b7280",tool_error:"#f472b6",unknown:"#94a3b8"},St={class:"rounded-lg border border-border-default bg-surface-raised p-4"},wt={class:"mb-3 text-sm font-medium text-text-secondary"},Rt={key:1,class:"py-6 text-center text-xs text-text-muted"},Mt=x({__name:"ErrorRateByModelChart",setup(u){v.register(R,D,F,$,k,C);const n=W(),o=p(()=>{const e=new Map;for(const i of n.sessions.values()){const b=i.model,g=e.get(b)??{total:0,errors:0,byCategory:{}};if(g.total++,i.status==="error"&&i.error){g.errors++;const{category:h}=j(i.error);g.byCategory[h]=(g.byCategory[h]??0)+1}e.set(b,g)}return[...e.entries()].map(([i,b])=>({model:i,...b,rate:b.total>0?b.errors/b.total:0})).filter(i=>i.total>0).sort((i,b)=>b.rate-i.rate)}),a=["connection","timeout","model_not_found","context_exceeded","oom","tool_error","cancelled","unknown"],r={connection:"Connection",timeout:"Timeout",model_not_found:"Not found",context_exceeded:"Context exceeded",oom:"Out of memory",tool_error:"Tool error",cancelled:"Cancelled",unknown:"Unknown"},f=p(()=>({labels:o.value.map(e=>e.model),datasets:a.filter(e=>o.value.some(i=>(i.byCategory[e]??0)>0)).map(e=>({label:r[e],data:o.value.map(i=>i.byCategory[e]??0),backgroundColor:Dt[e]}))})),s={indexAxis:"y",responsive:!0,maintainAspectRatio:!1,plugins:{legend:{position:"bottom",labels:{color:"#94a3b8",font:{size:10}}}},scales:{x:{stacked:!0,ticks:{color:"#94a3b8",font:{size:10},stepSize:1},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{stacked:!0,ticks:{color:"#94a3b8",font:{size:11}},grid:{display:!1}}}},t=p(()=>o.value.some(e=>e.errors>0));return(e,i)=>(d(),c("div",St,[l("h3",wt,m(e.$t("cost.errors.title")),1),t.value?(d(),c("div",{key:0,style:J({height:Math.max(100,o.value.length*36)+"px"})},[_(y(B),{data:f.value,options:s},null,8,["data"])],4)):(d(),c("p",Rt,m(e.$t("cost.errors.noErrors")),1))]))}}),Tt={class:"rounded-lg border border-border-default bg-surface-raised p-4"},zt={class:"mb-3 text-sm font-medium text-text-secondary"},At={key:0,class:"h-48"},Lt={key:1,class:"py-6 text-center text-xs text-text-muted"},Ot=x({__name:"ErrorTimelineChart",setup(u){v.register(R,D,T,L,$,k,C);const n=W(),o=p(()=>{const t=[];for(const e of n.sessions.values())if(e.status==="error"&&e.error){const{category:i,label:b}=j(e.error);t.push({date:new Date(e.createdAt).toISOString().slice(0,10),category:i,model:e.model,error:b})}return t.sort((e,i)=>e.date.localeCompare(i.date))}),a=p(()=>{const t=new Map;for(const e of o.value)t.set(e.date,(t.get(e.date)??0)+1);return[...t.entries()].sort((e,i)=>e[0].localeCompare(i[0]))}),r=p(()=>({labels:a.value.map(([t])=>t),datasets:[{label:"Errors",data:a.value.map(([,t])=>t),borderColor:"#ef4444",backgroundColor:"rgba(239, 68, 68, 0.1)",fill:!0,tension:.3,pointRadius:3}]})),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{ticks:{color:"#94a3b8",font:{size:10},stepSize:1},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},s=p(()=>a.value.length>0);return(t,e)=>(d(),c("div",Tt,[l("h3",zt,m(t.$t("analytics.errors.timelineTitle")),1),s.value?(d(),c("div",At,[_(y(O),{data:r.value,options:f},null,8,["data"])])):(d(),c("p",Lt,m(t.$t("analytics.errors.noErrors")),1))]))}}),Et={class:"rounded-lg border border-border-default bg-surface-raised p-4"},Ht={class:"mb-3 text-sm font-medium text-text-secondary"},Pt={key:0,class:"h-48"},qt={key:1,class:"py-6 text-center text-xs text-text-muted"},Ft=x({__name:"RequestVolumeChart",setup(u){v.register(R,D,F,$,k,C);const n=S(),o=p(()=>{const s=new Map;for(const t of n.metricsHistory){const e=new Date(t.startedAt).toISOString().slice(0,10);s.set(e,(s.get(e)??0)+1)}return[...s.entries()].sort((t,e)=>t[0].localeCompare(e[0]))}),a=p(()=>({labels:o.value.map(([s])=>s),datasets:[{label:"Requests",data:o.value.map(([,s])=>s),backgroundColor:"#818cf8"}]})),r={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10}},grid:{display:!1}},y:{ticks:{color:"#94a3b8",font:{size:10},stepSize:1},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},f=p(()=>o.value.length>0);return(s,t)=>(d(),c("div",Et,[l("h3",Ht,m(s.$t("analytics.usage.requestVolume")),1),f.value?(d(),c("div",Pt,[_(y(B),{data:a.value,options:r},null,8,["data"])])):(d(),c("p",qt,m(s.$t("analytics.usage.noData")),1))]))}}),Bt={class:"rounded-lg border border-border-default bg-surface-raised p-4"},Wt={class:"mb-3 text-sm font-medium text-text-secondary"},It={key:0,class:"h-48"},Nt={key:1,class:"py-6 text-center text-xs text-text-muted"},Ut=x({__name:"ModelDistributionChart",setup(u){v.register(K,$,k,C);const n=S(),o=["#818cf8","#a855f7","#34d399","#fbbf24","#f472b6","#fb923c","#60a5fa","#c084fc"],a=p(()=>{const t=new Map;for(const e of n.metricsHistory)t.set(e.model,(t.get(e.model)??0)+1);return[...t.entries()].sort((e,i)=>i[1]-e[1])}),r=p(()=>({labels:a.value.map(([t])=>t),datasets:[{data:a.value.map(([,t])=>t),backgroundColor:a.value.map((t,e)=>o[e%o.length]),borderWidth:0}]})),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{position:"right",labels:{color:"#94a3b8",font:{size:10},padding:12}}}},s=p(()=>a.value.length>0);return(t,e)=>(d(),c("div",Bt,[l("h3",Wt,m(t.$t("analytics.usage.modelDistribution")),1),s.value?(d(),c("div",It,[_(y(Y),{data:r.value,options:f},null,8,["data"])])):(d(),c("p",Nt,m(t.$t("analytics.usage.noData")),1))]))}}),Vt={class:"rounded-lg border border-border-default bg-surface-raised p-4"},jt={class:"mb-3 text-sm font-medium text-text-secondary"},Qt={key:0,class:"overflow-x-auto"},Kt={class:"min-w-[600px]"},Yt={class:"flex items-center"},Gt={class:"w-10 shrink-0 text-[10px] text-text-muted"},Jt=["title"],Xt={key:1,class:"py-6 text-center text-xs text-text-muted"},Zt=x({__name:"ActiveHoursHeatmap",setup(u){const n=S(),o=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],a=Array.from({length:24},(t,e)=>e),r=p(()=>{const t=Array.from({length:7},()=>new Array(24).fill(0));let e=0;for(const i of n.metricsHistory){const b=new Date(i.startedAt),g=(b.getDay()+6)%7,h=b.getHours();t[g][h]++,t[g][h]>e&&(e=t[g][h])}return{counts:t,max:e}});function f(t){if(t===0)return"bg-surface-overlay/30";const e=r.value.max>0?t/r.value.max:0;return e>.75?"bg-accent":e>.5?"bg-accent/70":e>.25?"bg-accent/40":"bg-accent/20"}const s=p(()=>n.metricsHistory.length>0);return(t,e)=>(d(),c("div",Vt,[l("h3",jt,m(t.$t("analytics.usage.activeHours")),1),s.value?(d(),c("div",Qt,[l("div",Kt,[l("div",Yt,[e[0]||(e[0]=l("div",{class:"w-10 shrink-0"},null,-1)),(d(!0),c(w,null,E(y(a),i=>(d(),c("div",{key:i,class:"flex-1 text-center text-[8px] text-text-muted"},m(i),1))),128))]),(d(),c(w,null,E(o,(i,b)=>l("div",{key:i,class:"flex items-center gap-0.5 mt-0.5"},[l("div",Gt,m(i),1),(d(!0),c(w,null,E(y(a),g=>(d(),c("div",{key:g,class:q(["flex-1 aspect-square rounded-sm transition-colors",f(r.value.counts[b][g])]),title:`${i} ${g}:00 — ${r.value.counts[b][g]} requests`},null,10,Jt))),128))])),64))])])):(d(),c("p",Xt,m(t.$t("analytics.usage.noData")),1))]))}}),te={class:"rounded-lg border border-border-default bg-surface-raised p-4"},ee={class:"mb-3 text-sm font-medium text-text-secondary"},se={class:"h-48"},oe={class:"mt-2 text-[10px] text-text-muted"},ae={key:1,class:"py-6 text-center text-xs text-text-muted"},ne=x({__name:"SettingsImpactScatter",setup(u){v.register(D,T,$,k,C);const n=W(),o=S(),a=p(()=>{const t=[];for(const e of n.sessions.values()){const i=e.options?.temperature;if(i===void 0)continue;const b=o.getMetrics(e.id);b&&t.push({x:i,y:b.tokensPerSecond,model:e.model})}return t}),r=p(()=>({datasets:[{label:"Temperature vs Speed",data:a.value.map(t=>({x:t.x,y:t.y})),backgroundColor:"#818cf880",borderColor:"#818cf8",pointRadius:5}]})),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},tooltip:{callbacks:{label:t=>`${a.value[t.dataIndex]?.model??""}: temp=${(t.parsed.x??0).toFixed(1)} → ${(t.parsed.y??0).toFixed(1)} tok/s`}}},scales:{x:{title:{display:!0,text:"Temperature",color:"#94a3b8",font:{size:10}},ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{title:{display:!0,text:"Tokens/sec",color:"#94a3b8",font:{size:10}},ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},s=p(()=>a.value.length>=2);return(t,e)=>(d(),c("div",te,[l("h3",ee,m(t.$t("analytics.settings.title")),1),s.value?(d(),c(w,{key:0},[l("div",se,[_(y(G),{data:r.value,options:f},null,8,["data"])]),l("p",oe,m(t.$t("analytics.settings.hint")),1)],64)):(d(),c("p",ae,m(t.$t("analytics.settings.noData")),1))]))}}),re={class:"rounded-lg border border-border-default bg-surface-raised p-4"},le={class:"mb-3 text-sm font-medium text-text-secondary"},ie={key:0,class:"max-h-64 overflow-y-auto"},ce={class:"text-[10px] text-text-muted w-14 shrink-0"},de={class:"text-xs text-text-primary truncate flex-1"},ue={key:0,class:"rounded-full bg-warning/15 px-1.5 py-0.5 text-[8px] font-medium text-warning shrink-0"},me={key:1,class:"py-6 text-center text-xs text-text-muted"},pe=500,be=x({__name:"ModelLoadHistory",setup(u){const n=S(),o=p(()=>n.metricsHistory.slice(-30).map(r=>({time:new Date(r.startedAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),date:new Date(r.startedAt).toLocaleDateString(),model:r.model,loadMs:r.loadDurationMs,isCold:r.loadDurationMs>pe}))),a=p(()=>o.value.length>0);return(r,f)=>(d(),c("div",re,[l("h3",le,m(r.$t("analytics.modelLoad.title")),1),a.value?(d(),c("div",ie,[(d(!0),c(w,null,E(o.value,(s,t)=>(d(),c("div",{key:t,class:"flex items-center gap-3 border-b border-border-default/30 py-1.5 last:border-0"},[l("div",{class:q(["h-2 w-2 shrink-0 rounded-full",s.isCold?"bg-warning":"bg-success"])},null,2),l("span",ce,m(s.time),1),l("span",de,m(s.model),1),l("span",{class:q(["text-[10px] font-mono shrink-0",s.isCold?"text-warning":"text-text-muted"])},m(s.loadMs.toFixed(0))+"ms ",3),s.isCold?(d(),c("span",ue,m(r.$t("analytics.modelLoad.cold")),1)):U("",!0)]))),128))])):(d(),c("p",me,m(r.$t("analytics.modelLoad.noData")),1))]))}});function fe(u,n){const o=[];for(let a=0;a<=u.length-n;a++)o.push(u.slice(a,a+n).join(" "));return o}function ge(u,n,o){const a=u.trim().split(/\s+/).filter(Boolean),r=a.length,s=u.split(/[.!?]+/).filter(M=>M.trim().length>0).length,t=new Set(a.map(M=>M.toLowerCase())),e=r>0?t.size/r:0;let i=0;if(a.length>=8){const M=fe(a.map(z=>z.toLowerCase()),4);if(M.length>0){const z=new Map;for(const A of M)z.set(A,(z.get(A)??0)+1);let I=0;for(const A of z.values())A>1&&(I+=A);i=I/M.length}}const b=Math.min(r/50,1)*.4,g=e*.3,h=(1-i)*.3,Q=Math.min(5,Math.max(0,(b+g+h)*5));return{turnIndex:n,role:o,wordCount:r,sentenceCount:s,uniqueWordRatio:e,repetitionRatio:i,score:Q}}function _e(u){return u.filter(n=>n.role==="user"||n.role==="assistant").map((n,o)=>ge(n.content.replace(/<think>[\s\S]*?<\/think>/g,"").trim(),o,n.role))}const he={class:"rounded-lg border border-border-default bg-surface-raised p-4"},xe={class:"mb-3 text-sm font-medium text-text-secondary"},ye={class:"h-48"},ve={class:"mt-2 text-[10px] text-text-muted"},$e={key:1,class:"py-6 text-center text-xs text-text-muted"},ke=x({__name:"QualityOverTurnsChart",setup(u){v.register(R,D,T,L,$,k,C,H);const n=X(),o=p(()=>{const t=n.activeConversation;return!t||t.messages.length<2?[]:_e(t.messages)}),a=p(()=>o.value.filter(t=>t.role==="assistant")),r=p(()=>({labels:a.value.map((t,e)=>`Turn ${e+1}`),datasets:[{label:"Quality Score",data:a.value.map(t=>t.score),borderColor:"#818cf8",backgroundColor:"rgba(129, 140, 248, 0.1)",fill:!0,tension:.3,pointBackgroundColor:a.value.map(t=>t.score>=3.5?"#34d399":t.score>=2?"#fbbf24":"#ef4444"),pointRadius:4}]})),f={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},tooltip:{callbacks:{label:t=>{const e=a.value[t.dataIndex];return`Score: ${(t.parsed.y??0).toFixed(1)}/5 (${e?.wordCount??0} words)`}}}},scales:{x:{ticks:{color:"#94a3b8",font:{size:10}},grid:{color:"rgba(71, 85, 105, 0.3)"}},y:{min:0,max:5,ticks:{color:"#94a3b8",font:{size:10},stepSize:1},grid:{color:"rgba(71, 85, 105, 0.3)"}}}},s=p(()=>a.value.length>=2);return(t,e)=>(d(),c("div",he,[l("h3",xe,m(t.$t("analytics.quality.title")),1),s.value?(d(),c(w,{key:0},[l("div",ye,[_(y(O),{data:r.value,options:f},null,8,["data"])]),l("p",ve,m(t.$t("analytics.quality.hint")),1)],64)):(d(),c("p",$e,m(t.$t("analytics.quality.noData")),1))]))}}),Ce={class:"h-full overflow-y-auto p-6"},De={class:"mb-6 text-lg font-semibold text-text-primary"},Se={class:"mb-8"},we={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Re={class:"mb-8"},Me={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Te={class:"grid gap-6 lg:grid-cols-2"},ze={class:"mt-6"},Ae={class:"mb-8"},Le={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Oe={class:"grid gap-6 lg:grid-cols-2"},Ee={class:"mt-6"},He={class:"mb-8"},Pe={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},qe={class:"grid gap-6 lg:grid-cols-2"},Fe={class:"mt-6"},Be={class:"mb-8"},We={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Ie={class:"mb-4 text-sm font-semibold uppercase tracking-wide text-text-muted"},Qe=x({__name:"AnalyticsPage",setup(u){return(n,o)=>(d(),c("div",Ce,[l("h1",De,m(n.$t("analytics.title")),1),l("section",Se,[l("h2",we,m(n.$t("analytics.sections.quality")),1),_(ke)]),l("section",Re,[l("h2",Me,m(n.$t("analytics.sections.latency")),1),l("div",Te,[_(ot),_(it)]),l("div",ze,[_(bt)])]),l("section",Ae,[l("h2",Le,m(n.$t("analytics.sections.reliability")),1),l("div",Oe,[_(Mt),_(Ot)]),l("div",Ee,[_(kt)])]),l("section",He,[l("h2",Pe,m(n.$t("analytics.sections.usage")),1),l("div",qe,[_(Ft),_(Ut)]),l("div",Fe,[_(Zt)])]),l("section",Be,[l("h2",We,m(n.$t("analytics.sections.settings")),1),_(ne)]),l("section",null,[l("h2",Ie,m(n.$t("analytics.sections.modelMemory")),1),_(be)])]))}});export{Qe as default};
|