llmxray 0.4.8 → 0.5.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 +6 -5
- package/README.fr.md +6 -5
- package/README.md +9 -8
- package/README.sr.md +6 -5
- package/README.zh-CN.md +6 -5
- package/dist/assets/{AITrainingPage-DKjKWcx-.js → AITrainingPage-BGxiNftI.js} +1 -1
- package/dist/assets/{AnalyticsPage-jTBXre2c.js → AnalyticsPage-Ct-sjfwM.js} +1 -1
- package/dist/assets/{BenchmarkPage-BVmzrGF3.js → BenchmarkPage-q41YsHY8.js} +2 -2
- package/dist/assets/{ComparisonPage-C9o6y6vj.js → ComparisonPage-BnJNpxsl.js} +1 -1
- package/dist/assets/{CostDashboardPage-CHoN2eQ2.js → CostDashboardPage-B4NDxJsX.js} +1 -1
- package/dist/assets/DashboardPage-BGQp7-l9.js +114 -0
- package/dist/assets/EmbeddingsPage-VBfXkb7Z.js +1 -0
- package/dist/assets/{FimPlaygroundPage-BcrmoLxo.js → FimPlaygroundPage-DLoI_Bvm.js} +1 -1
- package/dist/assets/{GoogleCallbackPage-C9_rQxue.js → GoogleCallbackPage-Cs3tBy_I.js} +1 -1
- package/dist/assets/{JsonTreeNode-RIieSDXb.js → JsonTreeNode-BSiw1fzq.js} +1 -1
- package/dist/assets/{ModelCapabilityIcons.vue_vue_type_script_setup_true_lang-BN_k6QDr.js → ModelCapabilityIcons.vue_vue_type_script_setup_true_lang-BEGN0dLS.js} +1 -1
- package/dist/assets/{ProtocolObservatoryPage-CXoj3a1O.js → ProtocolObservatoryPage-CgunJ_Wi.js} +1 -1
- package/dist/assets/{RAGPage-HwnlHxQ0.js → RAGPage-CnnZW7un.js} +1 -1
- package/dist/assets/{SessionPage-12S3fV1c.js → SessionPage-NE_x02_4.js} +1 -1
- package/dist/assets/{SettingsPage-B_hBcAPj.js → SettingsPage-DTpQIrlG.js} +1 -1
- package/dist/assets/{StatusBadge.vue_vue_type_script_setup_true_lang-BQhKjjl9.js → StatusBadge.vue_vue_type_script_setup_true_lang-DO0LFtqz.js} +1 -1
- package/dist/assets/{StorageGauge.vue_vue_type_style_index_0_lang-DOKnxhnC.js → StorageGauge.vue_vue_type_style_index_0_lang-BvYHZigP.js} +1 -1
- package/dist/assets/{SystemPage-BtFTvlZl.js → SystemPage-BEGyNOxu.js} +2 -2
- package/dist/assets/{TabBar.vue_vue_type_script_setup_true_lang-BStny7LH.js → TabBar.vue_vue_type_script_setup_true_lang-BwHA1qGL.js} +1 -1
- package/dist/assets/{TokenStreamDisplay.vue_vue_type_script_setup_true_lang-DMEwK9YF.js → TokenStreamDisplay.vue_vue_type_script_setup_true_lang-DyRMkTKm.js} +1 -1
- package/dist/assets/{ToolWorkshopPage-BRXtDJIQ.js → ToolWorkshopPage-CNhpkdiG.js} +1 -1
- package/dist/assets/{agent-store-CR4CQ_VV.js → agent-store-Ak5tH3gE.js} +1 -1
- package/dist/assets/{canvas-ai-db-Ck_6zOgd.js → canvas-ai-db-Bw1M5Zgc.js} +1 -1
- package/dist/assets/{download-CaV4vzuu.js → download-DgBo5doZ.js} +1 -1
- package/dist/assets/{generate-service-Bhf2jPuC.js → generate-service-D1KjeWIo.js} +1 -1
- package/dist/assets/{google-auth-store-LaJFtciC.js → google-auth-store-Db6JKC8L.js} +1 -1
- package/dist/assets/{index-CQPzvMUz.js → index-BM7fRtY0.js} +1 -1
- package/dist/assets/index-S2vqeDf7.js +36 -0
- package/dist/assets/{index-Dy_48xr0.js → index-YmhR__TY.js} +1 -1
- package/dist/assets/{info-C9qZFgvQ.js → info-47X0r1B2.js} +1 -1
- package/dist/assets/{metrics-store-B4lQqH-w.js → metrics-store-DzujlkfW.js} +1 -1
- package/dist/assets/{papaparse.min-CfA1otDr.js → papaparse.min-D0aQsTQw.js} +1 -1
- package/dist/assets/{pencil-BcLPhQp1.js → pencil-B-eZ2Kq9.js} +1 -1
- package/dist/assets/{play-CYCSil_U.js → play-DT41vD7M.js} +1 -1
- package/dist/assets/{rag-store-BXD_zIis.js → rag-store-gqowRN8t.js} +3 -3
- package/dist/assets/{session-store-Dy1-rRkz.js → session-store-BO1u_qJQ.js} +1 -1
- package/dist/assets/{share-2-D2IlHB5H.js → share-2-IBretU3M.js} +1 -1
- package/dist/assets/{square-Dtyb8Nls.js → square-CbKkOlNA.js} +1 -1
- package/dist/assets/{storage-store-me3LAKpj.js → storage-store-GsT1cPEh.js} +1 -1
- package/dist/assets/{stream-handler-DDNDUY1M.js → stream-handler-B5y5PItW.js} +1 -1
- package/dist/assets/{tool-workshop-store-C_CvI0_B.js → tool-workshop-store-BLoF0Gob.js} +1 -1
- package/dist/assets/{toolcall-store-CUeITA8C.js → toolcall-store-BEFdRqL-.js} +1 -1
- package/dist/assets/{trash-2-Ct6xU7VV.js → trash-2-BdlHNMOp.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +89 -89
- package/dist/assets/DashboardPage-CMZSfb4t.js +0 -114
- package/dist/assets/EmbeddingsPage-DdbGCntf.js +0 -1
- package/dist/assets/index-CZ4a6rAU.js +0 -36
package/README.ar.md
CHANGED
|
@@ -77,7 +77,7 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
77
77
|
## الميزات
|
|
78
78
|
|
|
79
79
|
### دردشة لحظية بذكاء على مستوى الرمز
|
|
80
|
-
تحدّث مع أي نموذج Ollama وراقب وصول الرموز مع **تلوين الثقة** — كل رمز يُلوَّن وفق سرعة توليده. يدعم Markdown، والمحادثات متعددة الأدوار، ومرفقات الملفات، ونماذج الرؤية، والأوامر بالشرطة المائلة.
|
|
80
|
+
تحدّث مع أي نموذج Ollama وراقب وصول الرموز مع **تلوين الثقة** — كل رمز يُلوَّن وفق سرعة توليده. يدعم Markdown، والمحادثات متعددة الأدوار، ومرفقات الملفات، ونماذج الرؤية، والأوامر بالشرطة المائلة. وللنماذج الاستدلالية، اضبط ميزانية التفكير لكل محادثة — إيقاف، أو ترك الاختيار للنموذج، أو جهد صريح منخفض / متوسط / مرتفع / أقصى.
|
|
81
81
|
|
|
82
82
|
### بوّابات جودة الاستجابة
|
|
83
83
|
كل استجابة تُحلَّل تلقائياً. تظهر شارات ملوّنة فقط حين يحدث خطأ:
|
|
@@ -104,7 +104,7 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
104
104
|
اختبر معرفة النموذج بحزم أسئلة متعدّدة الاختيار. يستخدم logprobs حقيقية عبر النقطة المتوافقة مع OpenAI لقياس دقيق للثقة. ابنِ حزماً مخصّصة بصرياً أو دع الذكاء الاصطناعي يولّدها من موضوع.
|
|
105
105
|
|
|
106
106
|
### مختبر التضمينات وخط أنابيب RAG
|
|
107
|
-
ضمّن النصوص، وصوّر المتجهات، وقِس تشابه جيب التمام. ابنِ قاعدة معرفة محلية من ملفات PDF و DOCX و CSV — مقطَّعة، مضمَّنة، وقابلة للبحث. كل ذلك مخزَّن في IndexedDB. بلا تكلفة.
|
|
107
|
+
ضمّن النصوص، وصوّر المتجهات، وقِس تشابه جيب التمام. اطلب متجه إخراج أضيق لترى كم يكلّف اقتطاع ماتريوشكا من التشابه. ابنِ قاعدة معرفة محلية من ملفات PDF و DOCX و CSV — مقطَّعة، مضمَّنة، وقابلة للبحث. كل ذلك مخزَّن في IndexedDB. بلا تكلفة.
|
|
108
108
|
|
|
109
109
|
### ورشة الأدوات (لوحة بصرية)
|
|
110
110
|
لوحة عُقَد بالسحب والإفلات لبناء تعريفات الأدوات. مزامنة كود ثنائية الاتجاه (تحرير العُقَد أو TypeScript — كلاهما يتحدّث). اسبر واجهات API، ولّد المخططات تلقائياً، واختبر بتنفيذ مباشر.
|
|
@@ -128,19 +128,20 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
128
128
|
|
|
129
129
|
## التوافق مع Ollama
|
|
130
130
|
|
|
131
|
-
تم اختبارها والتحقّق منها مع **Ollama 0.
|
|
131
|
+
تم اختبارها والتحقّق منها مع **Ollama 0.32.x** (أحدث إصدار مستقرّ حالياً في أغسطس ٢٠٢٦). يستخدم LLMxRay نقاط Ollama التالية:
|
|
132
132
|
|
|
133
133
|
| النقطة | الاستخدام |
|
|
134
134
|
|---|---|
|
|
135
135
|
| `/api/chat` | دردشة متدفّقة (NDJSON، مع `tools` و `think` ومخطّط `format`) |
|
|
136
136
|
| `/api/generate` | التوليد والإكمال في الوسط عبر `suffix` |
|
|
137
|
-
| `/api/tags
|
|
137
|
+
| `/api/tags` | قائمة النماذج والقدرات وطول السياق وعرض التضمين |
|
|
138
|
+
| `/api/show` | المعاملات والقالب والترخيص وبيانات البنية |
|
|
138
139
|
| `/api/embed` | تضمينات متّجهة لـ RAG |
|
|
139
140
|
| `/api/pull`، `/api/delete`، `/api/ps`، `/api/version` | إدارة النماذج وحالتها |
|
|
140
141
|
| `/v1/chat/completions` | مسار متوافق مع OpenAI يستخدمه المعيار الجراحي للحصول على logprobs حقيقية |
|
|
141
142
|
| `/v1/messages` | مسار متوافق مع Anthropic يستخدمه مرصد البروتوكولات |
|
|
142
143
|
|
|
143
|
-
**متوافق مع:** Ollama 0.20 وما بعده (الإصدارات الأقدم تعمل للدردشة/التوليد لكن تفتقر إلى `think` ومخطّط `format` بصيغة JSON Schema). **مُوصى به:** Ollama 0.
|
|
144
|
+
**متوافق مع:** Ollama 0.20 وما بعده (الإصدارات الأقدم تعمل للدردشة/التوليد لكن تفتقر إلى `think` ومخطّط `format` بصيغة JSON Schema). **مُوصى به:** Ollama 0.32+ — تصل القدرات وطول السياق مع قائمة النماذج، ويقبل `think` مستويات جهد متدرّجة، وتقبل التضمينات عرض `dimensions`.
|
|
144
145
|
|
|
145
146
|
---
|
|
146
147
|
|
package/README.fr.md
CHANGED
|
@@ -75,7 +75,7 @@ Vous lancez un LLM local. Vous discutez avec lui. Mais que s'est-il réellement
|
|
|
75
75
|
## Fonctionnalités
|
|
76
76
|
|
|
77
77
|
### Chat en temps réel avec intelligence par token
|
|
78
|
-
Discutez avec n'importe quel modèle Ollama et observez les tokens arriver avec une **coloration selon la confiance** — chaque token est teinté selon sa vitesse de génération. Supporte Markdown, conversations multi-tours, pièces jointes, modèles vision et commandes slash.
|
|
78
|
+
Discutez avec n'importe quel modèle Ollama et observez les tokens arriver avec une **coloration selon la confiance** — chaque token est teinté selon sa vitesse de génération. Supporte Markdown, conversations multi-tours, pièces jointes, modèles vision et commandes slash. Pour les modèles de raisonnement, réglez le budget de réflexion par conversation — désactivé, au choix du modèle, ou un effort explicite faible / moyen / élevé / max.
|
|
79
79
|
|
|
80
80
|
### Filtres qualité des réponses
|
|
81
81
|
Chaque réponse est analysée automatiquement. Des badges colorés n'apparaissent que lorsqu'il y a un problème :
|
|
@@ -102,7 +102,7 @@ Consommation de tokens par modèle/jour avec estimation du prix équivalent en c
|
|
|
102
102
|
Testez les connaissances d'un modèle avec des suites de questions à choix multiples. Utilise les vrais logprobs via l'endpoint compatible OpenAI pour une mesure précise de la confiance. Créez des suites personnalisées visuellement ou laissez l'IA les générer à partir d'un sujet.
|
|
103
103
|
|
|
104
104
|
### Laboratoire d'embeddings et pipeline RAG
|
|
105
|
-
Embeddez du texte, visualisez les vecteurs, mesurez la similarité cosinus. Construisez une base de connaissances locale à partir de PDF, DOCX et CSV — découpée, embeddée, recherchable. Stockée dans IndexedDB. Coût zéro.
|
|
105
|
+
Embeddez du texte, visualisez les vecteurs, mesurez la similarité cosinus. Demandez un vecteur de sortie plus étroit pour voir ce que coûte la troncature Matryoshka en similarité. Construisez une base de connaissances locale à partir de PDF, DOCX et CSV — découpée, embeddée, recherchable. Stockée dans IndexedDB. Coût zéro.
|
|
106
106
|
|
|
107
107
|
### Atelier d'outils (canvas visuel)
|
|
108
108
|
Canvas à nœuds en drag-and-drop pour construire des définitions d'outils. Synchronisation bidirectionnelle du code (modifiez les nœuds ou le TypeScript — les deux se mettent à jour). Sondez des APIs, générez des schémas automatiquement, testez avec exécution en direct.
|
|
@@ -126,19 +126,20 @@ Traductions complètes en anglais, français, serbe (latin + cyrillique), chinoi
|
|
|
126
126
|
|
|
127
127
|
## Compatibilité Ollama
|
|
128
128
|
|
|
129
|
-
Testé et vérifié avec **Ollama 0.
|
|
129
|
+
Testé et vérifié avec **Ollama 0.32.x** (la version stable la plus récente en août 2026). LLMxRay utilise les endpoints Ollama suivants :
|
|
130
130
|
|
|
131
131
|
| Endpoint | Utilisation |
|
|
132
132
|
|---|---|
|
|
133
133
|
| `/api/chat` | Chat streaming (NDJSON, avec `tools`, `think`, schema `format`) |
|
|
134
134
|
| `/api/generate` | Génération + Fill-in-the-Middle via `suffix` |
|
|
135
|
-
| `/api/tags
|
|
135
|
+
| `/api/tags` | Liste des modèles + capacités, longueur de contexte et largeur d'embedding |
|
|
136
|
+
| `/api/show` | Paramètres, template, licence et métadonnées d'architecture |
|
|
136
137
|
| `/api/embed` | Embeddings vectoriels pour RAG |
|
|
137
138
|
| `/api/pull`, `/api/delete`, `/api/ps`, `/api/version` | Gestion et statut des modèles |
|
|
138
139
|
| `/v1/chat/completions` | Chemin compatible OpenAI utilisé par le Benchmark chirurgical pour les vrais logprobs |
|
|
139
140
|
| `/v1/messages` | Chemin compatible Anthropic utilisé par l'Observatoire de protocoles |
|
|
140
141
|
|
|
141
|
-
**Compatible avec :** Ollama 0.20 et plus récent (les versions antérieures fonctionnent pour chat/generate mais sans `think` ni `format` JSON-schema). **Recommandé :** Ollama 0.
|
|
142
|
+
**Compatible avec :** Ollama 0.20 et plus récent (les versions antérieures fonctionnent pour chat/generate mais sans `think` ni `format` JSON-schema). **Recommandé :** Ollama 0.32+ — les capacités et la longueur de contexte arrivent avec la liste des modèles, `think` accepte des niveaux d'effort gradués, et les embeddings acceptent une largeur `dimensions`.
|
|
142
143
|
|
|
143
144
|
---
|
|
144
145
|
|
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ You run a local LLM. You chat with it. But what actually happened?
|
|
|
75
75
|
## Features
|
|
76
76
|
|
|
77
77
|
### Real-Time Chat with Token Intelligence
|
|
78
|
-
Chat with any Ollama model and watch tokens arrive with **confidence coloring** — each token is tinted based on generation speed. Supports markdown, multi-turn conversations, file attachments, vision models, and slash commands.
|
|
78
|
+
Chat with any Ollama model and watch tokens arrive with **confidence coloring** — each token is tinted based on generation speed. Supports markdown, multi-turn conversations, file attachments, vision models, and slash commands. For reasoning models, set the thinking budget per conversation — off, model's choice, or an explicit low / medium / high / max effort.
|
|
79
79
|
|
|
80
80
|
### Response Quality Gates
|
|
81
81
|
Every response is automatically analyzed. Colored badges appear only when something is wrong:
|
|
@@ -102,7 +102,7 @@ Token usage per model/day with estimated cloud-equivalent pricing. See what you'
|
|
|
102
102
|
Test model knowledge with multi-choice question suites. Uses real logprobs via OpenAI-compatible endpoint for accurate confidence measurement. Build custom suites visually or let AI generate them from a topic.
|
|
103
103
|
|
|
104
104
|
### Embeddings Lab & RAG Pipeline
|
|
105
|
-
Embed text, visualize vectors, measure cosine similarity. Build a local knowledge base from PDFs, DOCX, and CSV — chunked, embedded, and searchable. All stored in IndexedDB. Zero cost.
|
|
105
|
+
Embed text, visualize vectors, measure cosine similarity. Request a narrower output vector to see what Matryoshka truncation costs in similarity. Build a local knowledge base from PDFs, DOCX, and CSV — chunked, embedded, and searchable. All stored in IndexedDB. Zero cost.
|
|
106
106
|
|
|
107
107
|
### Tool Workshop (Visual Canvas)
|
|
108
108
|
Drag-and-drop node canvas for building tool definitions. Bidirectional code sync (edit nodes or TypeScript — both update). Probe APIs, auto-generate schemas, test with live execution.
|
|
@@ -126,19 +126,20 @@ Full translations in English, French, Serbian (Latin + Cyrillic), Chinese, and A
|
|
|
126
126
|
|
|
127
127
|
## Ollama Compatibility
|
|
128
128
|
|
|
129
|
-
Tested and verified against **Ollama 0.
|
|
129
|
+
Tested and verified against **Ollama 0.32.x** (the current latest stable as of August 2026). LLMxRay uses these Ollama endpoints:
|
|
130
130
|
|
|
131
131
|
| Endpoint | Used for |
|
|
132
132
|
|---|---|
|
|
133
|
-
| `/api/chat` | Streaming chat (NDJSON, with `tools`, `think
|
|
133
|
+
| `/api/chat` | Streaming chat (NDJSON, with `tools`, `think` effort levels, `format` schema) |
|
|
134
134
|
| `/api/generate` | Generation + Fill-in-the-Middle via `suffix` |
|
|
135
|
-
| `/api/tags
|
|
136
|
-
| `/api/
|
|
135
|
+
| `/api/tags` | Model list + capabilities, context length, and embedding width |
|
|
136
|
+
| `/api/show` | Parameters, template, license, and architecture metadata |
|
|
137
|
+
| `/api/embed` | Vector embeddings for RAG, with optional `dimensions` truncation |
|
|
137
138
|
| `/api/pull`, `/api/delete`, `/api/ps`, `/api/version` | Model management + status |
|
|
138
|
-
| `/v1/chat/completions` | OpenAI-compat path used by Surgical Benchmark for real logprobs |
|
|
139
|
+
| `/v1/chat/completions` | OpenAI-compat path used by Surgical Benchmark for real logprobs and usage totals |
|
|
139
140
|
| `/v1/messages` | Anthropic-compat path used by Protocol Observatory |
|
|
140
141
|
|
|
141
|
-
**Compatible with:** Ollama 0.20 and newer (older versions work for chat/generate but lack `think` and JSON-schema `format`). **Recommended:** Ollama 0.
|
|
142
|
+
**Compatible with:** Ollama 0.20 and newer (older versions work for chat/generate but lack `think` and JSON-schema `format`). **Recommended:** Ollama 0.32+ — capabilities and context length arrive with the model listing, `think` accepts graded effort levels, and embeddings accept a `dimensions` width.
|
|
142
143
|
|
|
143
144
|
---
|
|
144
145
|
|
package/README.sr.md
CHANGED
|
@@ -75,7 +75,7 @@ Pokrenuo si lokalni LLM. Razgovaraš sa njim. Ali šta se zaista desilo?
|
|
|
75
75
|
## Mogućnosti
|
|
76
76
|
|
|
77
77
|
### Razgovor u realnom vremenu sa inteligencijom tokena
|
|
78
|
-
Pričaj sa bilo kojim Ollama modelom i posmatraj tokene kako pristižu sa **bojenjem prema poverenju** — svaki token je obojen prema brzini generisanja. Podržava Markdown, razgovore u više rundi, priloge datoteka, vision modele i slash komande.
|
|
78
|
+
Pričaj sa bilo kojim Ollama modelom i posmatraj tokene kako pristižu sa **bojenjem prema poverenju** — svaki token je obojen prema brzini generisanja. Podržava Markdown, razgovore u više rundi, priloge datoteka, vision modele i slash komande. Za modele koji rezonuju, podesi budžet razmišljanja po razgovoru — isključeno, po izboru modela, ili eksplicitan napor nisko / srednje / visoko / max.
|
|
79
79
|
|
|
80
80
|
### Kapije kvaliteta odgovora
|
|
81
81
|
Svaki odgovor se automatski analizira. Obojene oznake se pojavljuju samo kada nešto nije u redu:
|
|
@@ -102,7 +102,7 @@ Potrošnja tokena po modelu/danu sa procenom ekvivalentne cene u cloudu. Vidi ko
|
|
|
102
102
|
Testiraj znanje modela skupovima pitanja sa višestrukim izborom. Koristi prave logprobs preko OpenAI-kompatibilnog endpointa za tačno merenje poverenja. Pravi custom skupove vizuelno ili pusti AI da ih generiše iz teme.
|
|
103
103
|
|
|
104
104
|
### Embeddings Lab i RAG pipeline
|
|
105
|
-
Embeduj tekst, vizualizuj vektore, meri kosinusnu sličnost. Izgradi lokalnu bazu znanja iz PDF, DOCX i CSV fajlova — podeljeno na delove, embedovano i pretraživo. Sve smešteno u IndexedDB. Bez troškova.
|
|
105
|
+
Embeduj tekst, vizualizuj vektore, meri kosinusnu sličnost. Zatraži uži izlazni vektor da vidiš koliko Matrjoška skraćivanje košta u sličnosti. Izgradi lokalnu bazu znanja iz PDF, DOCX i CSV fajlova — podeljeno na delove, embedovano i pretraživo. Sve smešteno u IndexedDB. Bez troškova.
|
|
106
106
|
|
|
107
107
|
### Tool Workshop (vizuelno platno)
|
|
108
108
|
Drag-and-drop platno sa čvorovima za pravljenje definicija alata. Dvosmerna sinhronizacija koda (uređuj čvorove ili TypeScript — oba se ažuriraju). Sondiraj API-je, automatski generiši šeme, testiraj uživo.
|
|
@@ -126,19 +126,20 @@ Potpuni prevodi na engleskom, francuskom, srpskom (latinica + ćirilica), kinesk
|
|
|
126
126
|
|
|
127
127
|
## Ollama kompatibilnost
|
|
128
128
|
|
|
129
|
-
Testirano i verifikovano sa **Ollama 0.
|
|
129
|
+
Testirano i verifikovano sa **Ollama 0.32.x** (trenutno najnovija stabilna verzija u avgustu 2026). LLMxRay koristi sledeće Ollama endpointe:
|
|
130
130
|
|
|
131
131
|
| Endpoint | Za šta se koristi |
|
|
132
132
|
|---|---|
|
|
133
133
|
| `/api/chat` | Strimovan chat (NDJSON, sa `tools`, `think`, `format` schema) |
|
|
134
134
|
| `/api/generate` | Generisanje + Fill-in-the-Middle preko `suffix` |
|
|
135
|
-
| `/api/tags
|
|
135
|
+
| `/api/tags` | Lista modela + sposobnosti, dužina konteksta i širina embedinga |
|
|
136
|
+
| `/api/show` | Parametri, template, licenca i metapodaci arhitekture |
|
|
136
137
|
| `/api/embed` | Vektorski embeddings za RAG |
|
|
137
138
|
| `/api/pull`, `/api/delete`, `/api/ps`, `/api/version` | Upravljanje modelima + status |
|
|
138
139
|
| `/v1/chat/completions` | OpenAI-compat putanja koju Surgical Benchmark koristi za prave logprobs |
|
|
139
140
|
| `/v1/messages` | Anthropic-compat putanja koju koristi Protocol Observatory |
|
|
140
141
|
|
|
141
|
-
**Kompatibilno sa:** Ollama 0.20 i novijim (starije verzije rade za chat/generate, ali nemaju `think` i JSON-schema `format`). **Preporučeno:** Ollama 0.
|
|
142
|
+
**Kompatibilno sa:** Ollama 0.20 i novijim (starije verzije rade za chat/generate, ali nemaju `think` i JSON-schema `format`). **Preporučeno:** Ollama 0.32+ — sposobnosti i dužina konteksta stižu sa listom modela, `think` prihvata stepenovane nivoe napora, a embedinzi prihvataju `dimensions` širinu.
|
|
142
143
|
|
|
143
144
|
---
|
|
144
145
|
|
package/README.zh-CN.md
CHANGED
|
@@ -75,7 +75,7 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
75
75
|
## 功能特性
|
|
76
76
|
|
|
77
77
|
### 实时对话与词元智能
|
|
78
|
-
与任何 Ollama 模型对话,观察词元到达时的**置信度着色** —— 每个词元根据生成速度被染色。支持 Markdown
|
|
78
|
+
与任何 Ollama 模型对话,观察词元到达时的**置信度着色** —— 每个词元根据生成速度被染色。支持 Markdown、多轮对话、文件附件、视觉模型与斜杠命令。对于推理模型,可按对话设置思考预算 —— 关闭、由模型自选,或显式指定低 / 中 / 高 / 最大推理强度。
|
|
79
79
|
|
|
80
80
|
### 响应质量门禁
|
|
81
81
|
每条响应都会被自动分析。仅在出现问题时才显示彩色徽章:
|
|
@@ -102,7 +102,7 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
102
102
|
用多选题套件测试模型知识。通过 OpenAI 兼容端点使用真实 logprobs 进行准确的置信度测量。可视化构建自定义题组,或让 AI 根据主题自动生成。
|
|
103
103
|
|
|
104
104
|
### 嵌入实验室与 RAG 流水线
|
|
105
|
-
|
|
105
|
+
嵌入文本、可视化向量、测量余弦相似度。可请求更窄的输出向量,直观看到套娃式截断在相似度上的代价。从 PDF、DOCX 与 CSV 构建本地知识库 —— 分块、嵌入、可搜索。全部存储在 IndexedDB。零成本。
|
|
106
106
|
|
|
107
107
|
### 工具工坊(可视化画布)
|
|
108
108
|
拖拽式节点画布用于构建工具定义。双向代码同步(编辑节点或 TypeScript —— 两边同步更新)。探测 API、自动生成 schema、实时执行测试。
|
|
@@ -126,19 +126,20 @@ docker run -p 5174:5174 djovaneli/llmxray
|
|
|
126
126
|
|
|
127
127
|
## Ollama 兼容性
|
|
128
128
|
|
|
129
|
-
针对 **Ollama 0.
|
|
129
|
+
针对 **Ollama 0.32.x**(2026 年 8 月最新稳定版)进行了测试与验证。LLMxRay 使用以下 Ollama 端点:
|
|
130
130
|
|
|
131
131
|
| 端点 | 用途 |
|
|
132
132
|
|---|---|
|
|
133
133
|
| `/api/chat` | 流式对话(NDJSON,支持 `tools`、`think`、`format` schema) |
|
|
134
134
|
| `/api/generate` | 生成与中段填充(通过 `suffix`) |
|
|
135
|
-
| `/api/tags
|
|
135
|
+
| `/api/tags` | 模型列表与能力、上下文长度、嵌入维度 |
|
|
136
|
+
| `/api/show` | 参数、模板、许可证与架构元数据 |
|
|
136
137
|
| `/api/embed` | RAG 的向量嵌入 |
|
|
137
138
|
| `/api/pull`、`/api/delete`、`/api/ps`、`/api/version` | 模型管理与状态 |
|
|
138
139
|
| `/v1/chat/completions` | OpenAI 兼容路径,外科手术式基准测试用于获取真实 logprobs |
|
|
139
140
|
| `/v1/messages` | Anthropic 兼容路径,协议观察台使用 |
|
|
140
141
|
|
|
141
|
-
**兼容版本:** Ollama 0.20 及更新版本(更早版本支持对话/生成但缺少 `think` 与 JSON-schema `format`)。**推荐版本:** Ollama 0.
|
|
142
|
+
**兼容版本:** Ollama 0.20 及更新版本(更早版本支持对话/生成但缺少 `think` 与 JSON-schema `format`)。**推荐版本:** Ollama 0.32+ —— 能力与上下文长度随模型列表一并返回,`think` 支持分级推理强度,嵌入支持 `dimensions` 维度。
|
|
142
143
|
|
|
143
144
|
---
|
|
144
145
|
|
|
@@ -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-CZ4a6rAU.js";import{canvasAiDB as B}from"./canvas-ai-db-Ck_6zOgd.js";import{b as _,d as z}from"./format-BqVZ9rwM.js";import{_ as ee}from"./StorageGauge.vue_vue_type_style_index_0_lang-DOKnxhnC.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-S2vqeDf7.js";import{canvasAiDB as B}from"./canvas-ai-db-Bw1M5Zgc.js";import{b as _,d as z}from"./format-BqVZ9rwM.js";import{_ as ee}from"./StorageGauge.vue_vue_type_style_index_0_lang-BvYHZigP.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-Dy_48xr0.js";import{u as S}from"./metrics-store-B4lQqH-w.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-CZ4a6rAU.js";import{u as W}from"./session-store-Dy1-rRkz.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-YmhR__TY.js";import{u as S}from"./metrics-store-DzujlkfW.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-S2vqeDf7.js";import{u as W}from"./session-store-BO1u_qJQ.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};
|