vespaembed 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl

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 (49) hide show
  1. vespaembed/__init__.py +1 -1
  2. vespaembed/cli/__init__.py +17 -0
  3. vespaembed/cli/commands/__init__.py +7 -0
  4. vespaembed/cli/commands/evaluate.py +85 -0
  5. vespaembed/cli/commands/export.py +86 -0
  6. vespaembed/cli/commands/info.py +52 -0
  7. vespaembed/cli/commands/serve.py +49 -0
  8. vespaembed/cli/commands/train.py +267 -0
  9. vespaembed/cli/vespaembed.py +55 -0
  10. vespaembed/core/__init__.py +2 -0
  11. vespaembed/core/config.py +164 -0
  12. vespaembed/core/registry.py +158 -0
  13. vespaembed/core/trainer.py +573 -0
  14. vespaembed/datasets/__init__.py +3 -0
  15. vespaembed/datasets/formats/__init__.py +5 -0
  16. vespaembed/datasets/formats/csv.py +15 -0
  17. vespaembed/datasets/formats/huggingface.py +34 -0
  18. vespaembed/datasets/formats/jsonl.py +26 -0
  19. vespaembed/datasets/loader.py +80 -0
  20. vespaembed/db.py +176 -0
  21. vespaembed/enums.py +58 -0
  22. vespaembed/evaluation/__init__.py +3 -0
  23. vespaembed/evaluation/factory.py +86 -0
  24. vespaembed/models/__init__.py +4 -0
  25. vespaembed/models/export.py +89 -0
  26. vespaembed/models/loader.py +25 -0
  27. vespaembed/static/css/styles.css +1800 -0
  28. vespaembed/static/js/app.js +1485 -0
  29. vespaembed/tasks/__init__.py +23 -0
  30. vespaembed/tasks/base.py +144 -0
  31. vespaembed/tasks/pairs.py +91 -0
  32. vespaembed/tasks/similarity.py +84 -0
  33. vespaembed/tasks/triplets.py +90 -0
  34. vespaembed/tasks/tsdae.py +102 -0
  35. vespaembed/templates/index.html +544 -0
  36. vespaembed/utils/__init__.py +3 -0
  37. vespaembed/utils/logging.py +69 -0
  38. vespaembed/web/__init__.py +1 -0
  39. vespaembed/web/api/__init__.py +1 -0
  40. vespaembed/web/app.py +605 -0
  41. vespaembed/worker.py +313 -0
  42. vespaembed-0.0.2.dist-info/METADATA +325 -0
  43. vespaembed-0.0.2.dist-info/RECORD +47 -0
  44. {vespaembed-0.0.1.dist-info → vespaembed-0.0.2.dist-info}/WHEEL +1 -1
  45. vespaembed-0.0.1.dist-info/METADATA +0 -20
  46. vespaembed-0.0.1.dist-info/RECORD +0 -7
  47. {vespaembed-0.0.1.dist-info → vespaembed-0.0.2.dist-info}/entry_points.txt +0 -0
  48. {vespaembed-0.0.1.dist-info → vespaembed-0.0.2.dist-info}/licenses/LICENSE +0 -0
  49. {vespaembed-0.0.1.dist-info → vespaembed-0.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,544 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>VespaEmbed</title>
8
+ <link rel="stylesheet" href="/static/css/styles.css">
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
12
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
13
+ </head>
14
+
15
+ <body>
16
+ <div class="app">
17
+ <!-- Sidebar -->
18
+ <aside class="sidebar">
19
+ <div class="sidebar-brand">
20
+ <span class="brand-icon">◈</span>
21
+ <span class="brand-text">VespaEmbed</span>
22
+ </div>
23
+ <button id="new-training-btn" class="btn-new">+ New Project</button>
24
+ <div class="run-list" id="run-list">
25
+ <div class="run-item placeholder">No training runs yet</div>
26
+ </div>
27
+
28
+ <!-- Project Summary (shown when run selected) -->
29
+ <div id="project-summary" class="project-summary" style="display: none;">
30
+ <div class="summary-header">
31
+ <span class="summary-title" id="summary-project-name">Training</span>
32
+ <span class="status-chip small" id="summary-status">Ready</span>
33
+ </div>
34
+ <div class="summary-grid">
35
+ <div class="summary-row">
36
+ <span class="summary-key">Task</span>
37
+ <span class="summary-val" id="sum-task">--</span>
38
+ </div>
39
+ <div class="summary-row">
40
+ <span class="summary-key">Loss</span>
41
+ <span class="summary-val" id="sum-loss">--</span>
42
+ </div>
43
+ <div class="summary-row">
44
+ <span class="summary-key">Model</span>
45
+ <span class="summary-val" id="sum-model">--</span>
46
+ </div>
47
+ <div class="summary-row">
48
+ <span class="summary-key">Data</span>
49
+ <span class="summary-val" id="sum-data">--</span>
50
+ </div>
51
+ <div class="summary-row">
52
+ <span class="summary-key">Epochs</span>
53
+ <span class="summary-val" id="sum-epochs">--</span>
54
+ </div>
55
+ <div class="summary-row">
56
+ <span class="summary-key">Batch</span>
57
+ <span class="summary-val" id="sum-batch">--</span>
58
+ </div>
59
+ <div class="summary-row">
60
+ <span class="summary-key">LR</span>
61
+ <span class="summary-val" id="sum-lr">--</span>
62
+ </div>
63
+ <div class="summary-row" id="sum-lora-row" style="display: none;">
64
+ <span class="summary-key">LoRA</span>
65
+ <span class="summary-val" id="sum-lora">--</span>
66
+ </div>
67
+ <div class="summary-row" id="sum-matryoshka-row" style="display: none;">
68
+ <span class="summary-key">Matryoshka</span>
69
+ <span class="summary-val" id="sum-matryoshka">--</span>
70
+ </div>
71
+ </div>
72
+ <div class="summary-actions">
73
+ <button type="button" id="artifacts-btn" class="btn-ghost" disabled>
74
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
75
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
76
+ <polyline points="7 10 12 15 17 10" />
77
+ <line x1="12" y1="15" x2="12" y2="3" />
78
+ </svg>
79
+ Artifacts
80
+ </button>
81
+ <button type="button" id="stop-btn" class="btn-danger" style="display: none;">
82
+ <span class="btn-text">Stop</span>
83
+ <span class="btn-spinner"></span>
84
+ </button>
85
+ <button type="button" id="delete-btn" class="btn-ghost">Delete</button>
86
+ </div>
87
+ </div>
88
+ </aside>
89
+
90
+ <!-- Main Content -->
91
+ <main class="main">
92
+ <!-- Top Bar: Status + Metrics -->
93
+ <div class="monitor-header">
94
+ <div class="header-actions">
95
+ <button id="refresh-btn" class="icon-btn" title="Refresh">
96
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
97
+ <path d="M23 4v6h-6" />
98
+ <path d="M1 20v-6h6" />
99
+ <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
100
+ </svg>
101
+ </button>
102
+ </div>
103
+ <div class="metrics-bar">
104
+ <div class="metric">
105
+ <span class="metric-val" id="current-epoch">0</span>
106
+ <span class="metric-label">epoch</span>
107
+ </div>
108
+ <div class="metric">
109
+ <span class="metric-val" id="current-step">0</span>
110
+ <span class="metric-label">steps</span>
111
+ </div>
112
+ <div class="metric highlight">
113
+ <span class="metric-val" id="current-loss">--</span>
114
+ <span class="metric-label">loss</span>
115
+ </div>
116
+ <div class="metric">
117
+ <span class="metric-val" id="current-eta">--</span>
118
+ <span class="metric-label">ETA</span>
119
+ </div>
120
+ </div>
121
+ </div>
122
+
123
+ <!-- Status Banner -->
124
+ <div class="status-banner" id="status-banner" style="display: none;">
125
+ <span class="status-spinner"></span>
126
+ <span class="status-message" id="status-message">Loading...</span>
127
+ </div>
128
+
129
+ <!-- Chart -->
130
+ <div class="chart-card">
131
+ <div class="chart-header">
132
+ <span>Training Progress</span>
133
+ <div class="chart-controls">
134
+ <select id="metric-select" class="metric-select">
135
+ <option value="loss">Loss</option>
136
+ </select>
137
+ </div>
138
+ </div>
139
+ <div class="chart-wrapper">
140
+ <canvas id="loss-chart"></canvas>
141
+ <div class="chart-placeholder" id="chart-placeholder">
142
+ Start training to see progress
143
+ </div>
144
+ </div>
145
+ </div>
146
+
147
+ <!-- Logs -->
148
+ <div class="log-card">
149
+ <div class="card-header">
150
+ Training Logs
151
+ <span class="badge" id="log-count">0</span>
152
+ </div>
153
+ <div class="log-content" id="log-content">
154
+ Logs will appear here...
155
+ </div>
156
+ </div>
157
+
158
+ <!-- Progress Bar (Bottom) -->
159
+ <div class="progress-container" id="progress-container" style="display: none;">
160
+ <div class="progress-bar">
161
+ <div class="progress-fill" id="progress-fill" style="width: 0%"></div>
162
+ </div>
163
+ <div class="progress-text">
164
+ <span id="progress-pct">0%</span>
165
+ <span id="progress-speed">-- it/s</span>
166
+ </div>
167
+ </div>
168
+ </main>
169
+ </div>
170
+
171
+ <!-- New Training Modal (Multi-Step Wizard) -->
172
+ <div id="new-training-modal" class="modal">
173
+ <div class="modal-box modal-wizard">
174
+ <div class="modal-header">
175
+ <span>New Project</span>
176
+ <button class="close-modal" id="close-new-training">&times;</button>
177
+ </div>
178
+
179
+ <!-- Step Indicators -->
180
+ <div class="wizard-steps">
181
+ <div class="wizard-step active" data-step="1">
182
+ <span class="step-number">1</span>
183
+ <span class="step-label">Setup</span>
184
+ </div>
185
+ <div class="wizard-step" data-step="2">
186
+ <span class="step-number">2</span>
187
+ <span class="step-label">Training</span>
188
+ </div>
189
+ <div class="wizard-step" data-step="3">
190
+ <span class="step-number">3</span>
191
+ <span class="step-label">Advanced</span>
192
+ </div>
193
+ </div>
194
+
195
+ <form id="train-form">
196
+ <!-- Step 1: Setup (Task, Model, Data) -->
197
+ <div class="wizard-content" id="wizard-step-1">
198
+ <!-- Project Name -->
199
+ <div class="form-section">
200
+ <label class="section-label">Project Name</label>
201
+ <input type="text" id="project_name" name="project_name" required
202
+ pattern="[a-zA-Z0-9][a-zA-Z0-9-]*">
203
+ <span class="field-hint">Saved to ~/.vespaembed/projects/</span>
204
+ </div>
205
+
206
+ <!-- Task & Model -->
207
+ <div class="form-section">
208
+ <label class="section-label">Task & Model</label>
209
+ <div class="form-field">
210
+ <label>Task</label>
211
+ <select id="task" name="task" required>
212
+ <!-- Options populated via API -->
213
+ </select>
214
+ <span class="field-hint" id="task-description"></span>
215
+ </div>
216
+ <!-- Required columns -->
217
+ <div class="required-columns" id="required-columns">
218
+ <span class="required-columns-label">Required columns:</span>
219
+ <span class="required-columns-list" id="required-columns-list"></span>
220
+ </div>
221
+ <!-- Sample data format -->
222
+ <div class="sample-data-box" id="sample-data-box">
223
+ <div class="sample-data-header">Example data:</div>
224
+ <div class="sample-data-content" id="sample-data-content"></div>
225
+ </div>
226
+ <div class="form-field" style="margin-top: var(--space-sm);">
227
+ <label>Base Model</label>
228
+ <input type="text" id="base_model" name="base_model"
229
+ value="sentence-transformers/all-MiniLM-L6-v2" required>
230
+ </div>
231
+ </div>
232
+
233
+ <!-- Data Source Tabs -->
234
+ <div class="form-section">
235
+ <label class="section-label">Data Source</label>
236
+ <div class="tabs">
237
+ <button type="button" class="tab active" data-tab="file">File Upload</button>
238
+ <button type="button" class="tab" data-tab="huggingface">HuggingFace Dataset</button>
239
+ </div>
240
+
241
+ <!-- Tab content container -->
242
+ <div class="tab-container">
243
+ <!-- File Upload Tab -->
244
+ <div class="tab-content active" id="tab-file">
245
+ <div class="upload-grid">
246
+ <!-- Training Data -->
247
+ <div class="upload-section">
248
+ <label class="upload-label">Training Data <span class="required">*</span></label>
249
+ <div class="upload-box" id="train-upload">
250
+ <input type="file" id="train_file" accept=".csv,.jsonl" hidden>
251
+ <span class="upload-icon">↑</span>
252
+ <span class="upload-text" id="train_file_info">CSV or JSONL</span>
253
+ </div>
254
+ <input type="hidden" id="train_filename" name="train_filename">
255
+ </div>
256
+
257
+ <!-- Evaluation Data (Optional) -->
258
+ <div class="upload-section">
259
+ <label class="upload-label">Eval Data <span class="optional">(optional)</span></label>
260
+ <div class="upload-box" id="eval-upload">
261
+ <input type="file" id="eval_file" accept=".csv,.jsonl" hidden>
262
+ <span class="upload-icon">↑</span>
263
+ <span class="upload-text" id="eval_file_info">CSV or JSONL</span>
264
+ </div>
265
+ <input type="hidden" id="eval_filename" name="eval_filename">
266
+ </div>
267
+ </div>
268
+ </div>
269
+
270
+ <!-- HuggingFace Dataset Tab -->
271
+ <div class="tab-content" id="tab-huggingface">
272
+ <div class="form-row">
273
+ <div class="form-field">
274
+ <label>Dataset <span class="required">*</span></label>
275
+ <input type="text" id="hf_dataset" name="hf_dataset"
276
+ placeholder="sentence-transformers/all-nli">
277
+ </div>
278
+ <div class="form-field">
279
+ <label>Subset</label>
280
+ <input type="text" id="hf_subset" name="hf_subset"
281
+ placeholder="triplet">
282
+ </div>
283
+ </div>
284
+ <div class="form-row">
285
+ <div class="form-field">
286
+ <label>Train Split</label>
287
+ <input type="text" id="hf_train_split" name="hf_train_split"
288
+ value="train">
289
+ </div>
290
+ <div class="form-field">
291
+ <label>Eval Split</label>
292
+ <input type="text" id="hf_eval_split" name="hf_eval_split"
293
+ placeholder="validation">
294
+ </div>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ <!-- Step 2: Training Parameters -->
302
+ <div class="wizard-content" id="wizard-step-2" style="display: none;">
303
+ <!-- Loss Function (shown when task has loss options) -->
304
+ <div class="form-section" id="loss-variant-field" style="display: none;">
305
+ <label class="section-label">Loss Function</label>
306
+ <div class="form-field">
307
+ <select id="loss_variant" name="loss_variant">
308
+ <!-- Options populated based on task -->
309
+ </select>
310
+ <span class="field-hint" id="loss-variant-hint"></span>
311
+ </div>
312
+ </div>
313
+
314
+ <!-- Basic Training Parameters -->
315
+ <div class="form-section">
316
+ <label class="section-label">Training</label>
317
+ <div class="form-row">
318
+ <div class="form-field compact">
319
+ <label>Epochs</label>
320
+ <input type="number" id="epochs" name="epochs" value="3" min="1">
321
+ </div>
322
+ <div class="form-field compact">
323
+ <label>Batch Size</label>
324
+ <input type="number" id="batch_size" name="batch_size" value="32" min="1">
325
+ </div>
326
+ <div class="form-field compact">
327
+ <label>Learning Rate</label>
328
+ <input type="text" id="learning_rate" name="learning_rate" value="2e-5">
329
+ </div>
330
+ <div class="form-field compact">
331
+ <label>Warmup Ratio</label>
332
+ <input type="text" id="warmup_ratio" name="warmup_ratio" value="0.1">
333
+ </div>
334
+ </div>
335
+ <div class="form-row">
336
+ <div class="form-field compact">
337
+ <label>Precision</label>
338
+ <select id="precision" name="precision">
339
+ <option value="fp32" selected>FP32</option>
340
+ <option value="fp16">FP16</option>
341
+ <option value="bf16">BF16</option>
342
+ </select>
343
+ </div>
344
+ <div class="form-field compact">
345
+ <label>Max Seq Length</label>
346
+ <input type="number" id="max_seq_length" name="max_seq_length" placeholder="auto" min="1">
347
+ </div>
348
+ </div>
349
+ <div class="checkbox-row" style="margin-top: var(--space-xs);">
350
+ <input type="checkbox" id="gradient_checkpointing" name="gradient_checkpointing">
351
+ <label for="gradient_checkpointing">Gradient Checkpointing (saves VRAM)</label>
352
+ </div>
353
+ </div>
354
+
355
+ <!-- Matryoshka (shown for compatible tasks) -->
356
+ <div class="form-section" id="matryoshka-section">
357
+ <label class="section-label">Matryoshka Embeddings</label>
358
+ <div class="checkbox-row">
359
+ <input type="checkbox" id="matryoshka_enabled" name="matryoshka_enabled">
360
+ <label for="matryoshka_enabled">Enable Matryoshka (multi-dimensional embeddings)</label>
361
+ </div>
362
+ <div id="matryoshka-fields" style="display: none; margin-top: var(--space-xs);">
363
+ <div class="form-field">
364
+ <label>Dimensions</label>
365
+ <input type="text" id="matryoshka_dims" name="matryoshka_dims"
366
+ value="768,512,256,128,64" placeholder="768,512,256,128,64">
367
+ <span class="field-hint">Comma-separated dimensions (largest to smallest)</span>
368
+ </div>
369
+ </div>
370
+ </div>
371
+
372
+ <!-- Task-specific parameters (populated dynamically) -->
373
+ <div id="task-specific-params"></div>
374
+
375
+ <!-- Optimizer & Scheduler -->
376
+ <div class="form-section">
377
+ <label class="section-label">Optimizer & Scheduler</label>
378
+ <div class="form-row">
379
+ <div class="form-field compact">
380
+ <label>Optimizer</label>
381
+ <select id="optimizer" name="optimizer">
382
+ <option value="adamw_torch" selected>AdamW</option>
383
+ <option value="adamw_torch_fused">AdamW Fused (CUDA)</option>
384
+ <option value="adamw_8bit">AdamW 8-bit</option>
385
+ <option value="adafactor">Adafactor</option>
386
+ <option value="sgd">SGD</option>
387
+ </select>
388
+ </div>
389
+ <div class="form-field compact">
390
+ <label>Scheduler</label>
391
+ <select id="scheduler" name="scheduler">
392
+ <option value="linear" selected>Linear</option>
393
+ <option value="cosine">Cosine</option>
394
+ <option value="cosine_with_restarts">Cosine w/ Restarts</option>
395
+ <option value="constant">Constant</option>
396
+ <option value="constant_with_warmup">Constant w/ Warmup</option>
397
+ <option value="polynomial">Polynomial</option>
398
+ </select>
399
+ </div>
400
+ <div class="form-field compact">
401
+ <label>Weight Decay</label>
402
+ <input type="text" id="weight_decay" name="weight_decay" value="0.01">
403
+ </div>
404
+ </div>
405
+ <div class="form-row">
406
+ <div class="form-field compact">
407
+ <label>Grad Accum</label>
408
+ <input type="number" id="gradient_accumulation_steps" name="gradient_accumulation_steps" value="1" min="1">
409
+ </div>
410
+ <div class="form-field compact">
411
+ <label>Logging Steps</label>
412
+ <input type="number" id="logging_steps" name="logging_steps" value="100" min="1">
413
+ </div>
414
+ <div class="form-field compact">
415
+ <label>Eval Steps</label>
416
+ <input type="number" id="eval_steps" name="eval_steps" value="500" min="1">
417
+ </div>
418
+ <div class="form-field compact">
419
+ <label>Save Steps</label>
420
+ <input type="number" id="save_steps" name="save_steps" value="500" min="1">
421
+ </div>
422
+ </div>
423
+ </div>
424
+ </div>
425
+
426
+ <!-- Step 3: Advanced (LoRA, Unsloth, Hub) -->
427
+ <div class="wizard-content" id="wizard-step-3" style="display: none;">
428
+ <!-- LoRA/PEFT -->
429
+ <div class="form-section">
430
+ <label class="section-label">LoRA (PEFT)</label>
431
+ <div class="checkbox-row">
432
+ <input type="checkbox" id="lora_enabled" name="lora_enabled">
433
+ <label for="lora_enabled">Enable LoRA training</label>
434
+ </div>
435
+ <div id="lora-fields" style="display: none; margin-top: var(--space-xs);">
436
+ <div class="form-row">
437
+ <div class="form-field compact">
438
+ <label>Rank (r)</label>
439
+ <select id="lora_r" name="lora_r">
440
+ <option value="8">8</option>
441
+ <option value="16">16</option>
442
+ <option value="32">32</option>
443
+ <option value="64" selected>64</option>
444
+ <option value="128">128</option>
445
+ </select>
446
+ </div>
447
+ <div class="form-field compact">
448
+ <label>Alpha</label>
449
+ <input type="number" id="lora_alpha" name="lora_alpha" value="128" min="1">
450
+ </div>
451
+ <div class="form-field compact">
452
+ <label>Dropout</label>
453
+ <input type="text" id="lora_dropout" name="lora_dropout" value="0.1">
454
+ </div>
455
+ </div>
456
+ <div class="form-field">
457
+ <label>Target Modules</label>
458
+ <select id="lora_target_preset" class="target-modules-select">
459
+ <option value="query, key, value, dense">BERT / MiniLM / MPNet / BGE</option>
460
+ <option value="Wqkv, Wi, Wo">ModernBERT / GTE-ModernBERT</option>
461
+ <option value="q_proj, k_proj, v_proj, o_proj">Llama / Gemma / Qwen</option>
462
+ <option value="q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj">Llama / Gemma / Qwen (extended)</option>
463
+ <option value="">Custom</option>
464
+ </select>
465
+ <input type="text" id="lora_target_modules" name="lora_target_modules"
466
+ value="query, key, value, dense" style="margin-top: var(--space-xs);"
467
+ placeholder="Enter comma-separated module names">
468
+ <span class="field-hint">Select a preset or choose Custom to enter your own.</span>
469
+ </div>
470
+ </div>
471
+ </div>
472
+
473
+ <!-- Unsloth -->
474
+ <div class="form-section">
475
+ <label class="section-label">Unsloth</label>
476
+ <div class="checkbox-row">
477
+ <input type="checkbox" id="unsloth_enabled" name="unsloth_enabled">
478
+ <label for="unsloth_enabled">Enable Unsloth (faster training)</label>
479
+ </div>
480
+ <div id="unsloth-fields" style="display: none; margin-top: var(--space-xs);">
481
+ <div class="form-row">
482
+ <div class="form-field compact">
483
+ <label>Save Method</label>
484
+ <select id="unsloth_save_method" name="unsloth_save_method">
485
+ <option value="merged_16bit" selected>Merged FP16</option>
486
+ <option value="merged_4bit">Merged 4-bit</option>
487
+ <option value="lora">LoRA Only</option>
488
+ </select>
489
+ </div>
490
+ </div>
491
+ <span class="field-hint">Unsloth requires CUDA GPU. Full finetuning when LoRA is disabled.</span>
492
+ </div>
493
+ </div>
494
+
495
+ <!-- Hub Push -->
496
+ <div class="form-section">
497
+ <label class="section-label">HuggingFace Hub</label>
498
+ <div class="checkbox-row">
499
+ <input type="checkbox" id="push_to_hub" name="push_to_hub">
500
+ <label for="push_to_hub">Push to Hub (private)</label>
501
+ </div>
502
+ <div id="hub-fields" style="display: none; margin-top: var(--space-xs);">
503
+ <div class="form-field">
504
+ <label>Username</label>
505
+ <input type="text" id="hf_username" name="hf_username"
506
+ placeholder="your-username">
507
+ </div>
508
+ <span class="field-hint">Requires HF_TOKEN env var. Model pushed to: username/project-name</span>
509
+ </div>
510
+ </div>
511
+ </div>
512
+
513
+ <div class="modal-footer wizard-footer">
514
+ <button type="button" id="wizard-back" class="btn-secondary" style="display: none;">Back</button>
515
+ <div class="footer-spacer"></div>
516
+ <button type="button" id="wizard-next" class="btn-primary">Next</button>
517
+ <button type="submit" id="start-btn" class="btn-primary" style="display: none;">Start Training</button>
518
+ </div>
519
+ </form>
520
+ </div>
521
+ </div>
522
+
523
+ <!-- Artifacts Modal -->
524
+ <div id="artifacts-modal" class="modal">
525
+ <div class="modal-box">
526
+ <div class="modal-header">
527
+ <span>Artifacts</span>
528
+ <button class="close-modal" id="close-artifacts-modal">&times;</button>
529
+ </div>
530
+ <div class="modal-body">
531
+ <div id="artifacts-list" class="artifacts-list">
532
+ <div class="artifacts-empty">No artifacts available</div>
533
+ </div>
534
+ <div class="artifacts-footer">
535
+ <span class="artifacts-path" id="artifacts-path"></span>
536
+ </div>
537
+ </div>
538
+ </div>
539
+ </div>
540
+
541
+ <script src="/static/js/app.js"></script>
542
+ </body>
543
+
544
+ </html>
@@ -0,0 +1,3 @@
1
+ from vespaembed.utils.logging import get_logger
2
+
3
+ __all__ = ["get_logger"]
@@ -0,0 +1,69 @@
1
+ import logging
2
+
3
+ from rich.console import Console
4
+ from rich.logging import RichHandler
5
+ from rich.theme import Theme
6
+
7
+ # Custom theme matching vespatune
8
+ VESPAEMBED_THEME = Theme(
9
+ {
10
+ "info": "cyan",
11
+ "warning": "yellow",
12
+ "error": "red bold",
13
+ "success": "green",
14
+ "highlight": "magenta",
15
+ }
16
+ )
17
+
18
+ console = Console(theme=VESPAEMBED_THEME)
19
+
20
+
21
+ def get_logger(name: str = "vespaembed", level: int = logging.INFO) -> logging.Logger:
22
+ """Get a configured logger with rich output."""
23
+ logger = logging.getLogger(name)
24
+
25
+ if not logger.handlers:
26
+ handler = RichHandler(
27
+ console=console,
28
+ show_time=True,
29
+ show_path=False,
30
+ rich_tracebacks=True,
31
+ tracebacks_show_locals=True,
32
+ )
33
+ handler.setFormatter(logging.Formatter("%(message)s"))
34
+ logger.addHandler(handler)
35
+ logger.setLevel(level)
36
+
37
+ return logger
38
+
39
+
40
+ class VespaEmbedLogger:
41
+ """Logger with convenience methods for vespaembed."""
42
+
43
+ def __init__(self, name: str = "vespaembed"):
44
+ self._logger = get_logger(name)
45
+ self._console = console
46
+
47
+ def info(self, message: str):
48
+ self._logger.info(message)
49
+
50
+ def debug(self, message: str):
51
+ self._logger.debug(message)
52
+
53
+ def warning(self, message: str):
54
+ self._logger.warning(message)
55
+
56
+ def error(self, message: str):
57
+ self._logger.error(message)
58
+
59
+ def success(self, message: str):
60
+ self._console.print(f"[success]{message}[/success]")
61
+
62
+ def highlight(self, message: str):
63
+ self._console.print(f"[highlight]{message}[/highlight]")
64
+
65
+ def print(self, message: str):
66
+ self._console.print(message)
67
+
68
+
69
+ logger = VespaEmbedLogger()
@@ -0,0 +1 @@
1
+ # Web module for FastAPI application
@@ -0,0 +1 @@
1
+ # API endpoints