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.
Files changed (53) hide show
  1. package/README.ar.md +6 -5
  2. package/README.fr.md +6 -5
  3. package/README.md +9 -8
  4. package/README.sr.md +6 -5
  5. package/README.zh-CN.md +6 -5
  6. package/dist/assets/{AITrainingPage-DKjKWcx-.js → AITrainingPage-BGxiNftI.js} +1 -1
  7. package/dist/assets/{AnalyticsPage-jTBXre2c.js → AnalyticsPage-Ct-sjfwM.js} +1 -1
  8. package/dist/assets/{BenchmarkPage-BVmzrGF3.js → BenchmarkPage-q41YsHY8.js} +2 -2
  9. package/dist/assets/{ComparisonPage-C9o6y6vj.js → ComparisonPage-BnJNpxsl.js} +1 -1
  10. package/dist/assets/{CostDashboardPage-CHoN2eQ2.js → CostDashboardPage-B4NDxJsX.js} +1 -1
  11. package/dist/assets/DashboardPage-BGQp7-l9.js +114 -0
  12. package/dist/assets/EmbeddingsPage-VBfXkb7Z.js +1 -0
  13. package/dist/assets/{FimPlaygroundPage-BcrmoLxo.js → FimPlaygroundPage-DLoI_Bvm.js} +1 -1
  14. package/dist/assets/{GoogleCallbackPage-C9_rQxue.js → GoogleCallbackPage-Cs3tBy_I.js} +1 -1
  15. package/dist/assets/{JsonTreeNode-RIieSDXb.js → JsonTreeNode-BSiw1fzq.js} +1 -1
  16. 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
  17. package/dist/assets/{ProtocolObservatoryPage-CXoj3a1O.js → ProtocolObservatoryPage-CgunJ_Wi.js} +1 -1
  18. package/dist/assets/{RAGPage-HwnlHxQ0.js → RAGPage-CnnZW7un.js} +1 -1
  19. package/dist/assets/{SessionPage-12S3fV1c.js → SessionPage-NE_x02_4.js} +1 -1
  20. package/dist/assets/{SettingsPage-B_hBcAPj.js → SettingsPage-DTpQIrlG.js} +1 -1
  21. 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
  22. 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
  23. package/dist/assets/{SystemPage-BtFTvlZl.js → SystemPage-BEGyNOxu.js} +2 -2
  24. 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
  25. 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
  26. package/dist/assets/{ToolWorkshopPage-BRXtDJIQ.js → ToolWorkshopPage-CNhpkdiG.js} +1 -1
  27. package/dist/assets/{agent-store-CR4CQ_VV.js → agent-store-Ak5tH3gE.js} +1 -1
  28. package/dist/assets/{canvas-ai-db-Ck_6zOgd.js → canvas-ai-db-Bw1M5Zgc.js} +1 -1
  29. package/dist/assets/{download-CaV4vzuu.js → download-DgBo5doZ.js} +1 -1
  30. package/dist/assets/{generate-service-Bhf2jPuC.js → generate-service-D1KjeWIo.js} +1 -1
  31. package/dist/assets/{google-auth-store-LaJFtciC.js → google-auth-store-Db6JKC8L.js} +1 -1
  32. package/dist/assets/{index-CQPzvMUz.js → index-BM7fRtY0.js} +1 -1
  33. package/dist/assets/index-S2vqeDf7.js +36 -0
  34. package/dist/assets/{index-Dy_48xr0.js → index-YmhR__TY.js} +1 -1
  35. package/dist/assets/{info-C9qZFgvQ.js → info-47X0r1B2.js} +1 -1
  36. package/dist/assets/{metrics-store-B4lQqH-w.js → metrics-store-DzujlkfW.js} +1 -1
  37. package/dist/assets/{papaparse.min-CfA1otDr.js → papaparse.min-D0aQsTQw.js} +1 -1
  38. package/dist/assets/{pencil-BcLPhQp1.js → pencil-B-eZ2Kq9.js} +1 -1
  39. package/dist/assets/{play-CYCSil_U.js → play-DT41vD7M.js} +1 -1
  40. package/dist/assets/{rag-store-BXD_zIis.js → rag-store-gqowRN8t.js} +3 -3
  41. package/dist/assets/{session-store-Dy1-rRkz.js → session-store-BO1u_qJQ.js} +1 -1
  42. package/dist/assets/{share-2-D2IlHB5H.js → share-2-IBretU3M.js} +1 -1
  43. package/dist/assets/{square-Dtyb8Nls.js → square-CbKkOlNA.js} +1 -1
  44. package/dist/assets/{storage-store-me3LAKpj.js → storage-store-GsT1cPEh.js} +1 -1
  45. package/dist/assets/{stream-handler-DDNDUY1M.js → stream-handler-B5y5PItW.js} +1 -1
  46. package/dist/assets/{tool-workshop-store-C_CvI0_B.js → tool-workshop-store-BLoF0Gob.js} +1 -1
  47. package/dist/assets/{toolcall-store-CUeITA8C.js → toolcall-store-BEFdRqL-.js} +1 -1
  48. package/dist/assets/{trash-2-Ct6xU7VV.js → trash-2-BdlHNMOp.js} +1 -1
  49. package/dist/index.html +1 -1
  50. package/package.json +89 -89
  51. package/dist/assets/DashboardPage-CMZSfb4t.js +0 -114
  52. package/dist/assets/EmbeddingsPage-DdbGCntf.js +0 -1
  53. 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.24.0** (أحدث إصدار مستقرّ حالياً في مايو ٢٠٢٦). يستخدم LLMxRay نقاط Ollama التالية:
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`، `/api/show` | قائمة النماذج واكتشاف القدرات (`thinking`، `tools`، `vision`) |
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.24+ للحصول على تكافؤ كامل للميزات، بما في ذلك النقطة المتوافقة مع Anthropic (أُضيفت في 0.23) ووضع `think: "max"` (أُضيف في 0.21.3).
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.24.0** (la version stable la plus récente en mai 2026). LLMxRay utilise les endpoints Ollama suivants :
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`, `/api/show` | Liste des modèles + détection des capacités (`thinking`, `tools`, `vision`) |
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.24+ pour une parité fonctionnelle complète, y compris l'endpoint compatible Anthropic (ajouté en 0.23) et le mode `think: "max"` (ajouté en 0.21.3).
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.24.0** (the current latest stable as of May 2026). LLMxRay uses these Ollama endpoints:
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`, `format` schema) |
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`, `/api/show` | Model list + capability detection (`thinking`, `tools`, `vision`) |
136
- | `/api/embed` | Vector embeddings for RAG |
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.24+ for full feature parity including the Anthropic-compat endpoint (added in 0.23) and the `think: "max"` mode (added in 0.21.3).
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.24.0** (trenutno najnovija stabilna verzija u maju 2026). LLMxRay koristi sledeće Ollama endpointe:
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`, `/api/show` | Lista modela + detekcija sposobnosti (`thinking`, `tools`, `vision`) |
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.24+ za potpunu paritetnost funkcija, uključujući Anthropic-compat endpoint (dodat u 0.23) i `think: "max"` režim (dodat u 0.21.3).
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
- 嵌入文本、可视化向量、测量余弦相似度。从 PDF、DOCX 与 CSV 构建本地知识库 —— 分块、嵌入、可搜索。全部存储在 IndexedDB。零成本。
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.24.0**(2026 年 5 月最新稳定版)进行了测试与验证。LLMxRay 使用以下 Ollama 端点:
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`、`/api/show` | 模型列表与能力检测(`thinking`、`tools`、`vision`) |
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.24+,可获得完整功能,包括 Anthropic 兼容端点(0.23 新增)与 `think: "max"` 模式(0.21.3 新增)。
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};