get-claudia 1.35.0 → 1.35.2

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.
@@ -331,14 +331,104 @@ def main():
331
331
  old_dim = int(old_dims_row[0]["value"]) if old_dims_row else 384
332
332
 
333
333
  if old_model == new_model and old_dim == new_dim:
334
- print(f"Already using {new_model} ({new_dim}D). Nothing to migrate.")
335
- return
334
+ # No mismatch -- offer interactive model selection
335
+ print(f"\nCurrent embedding model: {old_model} ({old_dim}D)")
336
+ print()
337
+ print("Available models:")
338
+ models_info = [
339
+ ("1", "all-minilm:l6-v2", 384, " 23MB", "Fast, good baseline"),
340
+ ("2", "nomic-embed-text", 768, " 274MB", "Better retrieval (+6%)"),
341
+ ("3", "mxbai-embed-large", 1024, " 669MB", "Best accuracy, larger"),
342
+ ]
343
+ for num, name, dim, size, desc in models_info:
344
+ current = " (current)" if name == old_model else ""
345
+ print(f" {num}) {name:<20s} {dim}D {size} {desc}{current}")
346
+ print(" 4) Cancel")
347
+ print()
348
+ choice = input("Switch to [1-4, default=4]: ").strip()
349
+
350
+ model_map = {
351
+ "1": ("all-minilm:l6-v2", 384),
352
+ "2": ("nomic-embed-text", 768),
353
+ "3": ("mxbai-embed-large", 1024),
354
+ }
355
+
356
+ if choice not in model_map:
357
+ print("No changes made.")
358
+ return
359
+
360
+ new_model, new_dim = model_map[choice]
361
+
362
+ if new_model == old_model and new_dim == old_dim:
363
+ print(f"Already using {new_model}. No changes needed.")
364
+ return
365
+
366
+ # Update config.json with the user's choice
367
+ config_path = Path.home() / ".claudia" / "config.json"
368
+ try:
369
+ if config_path.exists():
370
+ with open(config_path) as f:
371
+ cfg_data = _json.load(f)
372
+ else:
373
+ cfg_data = {}
374
+ cfg_data["embedding_model"] = new_model
375
+ cfg_data["embedding_dimensions"] = new_dim
376
+ with open(config_path, "w") as f:
377
+ _json.dump(cfg_data, f, indent=2)
378
+ print(f"\nConfig updated: {new_model} ({new_dim}D)")
379
+ except Exception as e:
380
+ print(f"Warning: Could not update config.json: {e}")
381
+
382
+ # Reinitialize embedding service with new model
383
+ svc.model = new_model
384
+ svc.dimensions = new_dim
385
+ svc._available = None # Force re-check
336
386
 
337
387
  # Pre-flight: verify Ollama is running and model is available
338
388
  if not svc.is_available_sync():
339
- print(f"Error: Ollama is not available or model '{new_model}' not found.")
340
- print(f"Start Ollama and pull the model: ollama pull {new_model}")
341
- sys.exit(1)
389
+ # Distinguish: Ollama not running vs model not pulled
390
+ import subprocess
391
+ import httpx
392
+
393
+ ollama_running = False
394
+ try:
395
+ resp = httpx.get(f"{svc.host}/api/tags", timeout=5)
396
+ ollama_running = resp.status_code == 200
397
+ except Exception:
398
+ pass
399
+
400
+ if not ollama_running:
401
+ print(f"Error: Ollama is not running.")
402
+ print(f"Please start Ollama and try again.")
403
+ sys.exit(1)
404
+
405
+ # Ollama is running but model is missing -- offer to pull it
406
+ print(f"\nThe model '{new_model}' is not installed in Ollama.")
407
+ pull_choice = input(f"Download it now? (Y/n): ").strip().lower()
408
+ if pull_choice in ("", "y", "yes"):
409
+ print(f"Downloading {new_model}... (this may take a minute)")
410
+ try:
411
+ result = subprocess.run(
412
+ ["ollama", "pull", new_model],
413
+ capture_output=False,
414
+ text=True,
415
+ )
416
+ if result.returncode != 0:
417
+ print(f"Error: Failed to pull {new_model}.")
418
+ sys.exit(1)
419
+ except FileNotFoundError:
420
+ print("Error: 'ollama' command not found. Please install Ollama.")
421
+ sys.exit(1)
422
+
423
+ # Re-check availability after pull
424
+ svc._available = None
425
+ if not svc.is_available_sync():
426
+ print(f"Error: Model still not available after pull.")
427
+ sys.exit(1)
428
+ print(f"Model '{new_model}' ready.")
429
+ else:
430
+ print("Migration cancelled.")
431
+ return
342
432
 
343
433
  # Count embeddings across all tables
344
434
  embedding_counts = {}
@@ -359,7 +449,7 @@ def main():
359
449
 
360
450
  # Count source data to re-embed
361
451
  mem_count_rows = db.execute(
362
- "SELECT COUNT(*) as cnt FROM memories WHERE deleted_at IS NULL",
452
+ "SELECT COUNT(*) as cnt FROM memories WHERE invalidated_at IS NULL",
363
453
  fetch=True,
364
454
  )
365
455
  ent_count_rows = db.execute(
@@ -441,7 +531,7 @@ def main():
441
531
  # 3a. Memory embeddings (largest, most important)
442
532
  if mem_count > 0:
443
533
  memories = db.execute(
444
- "SELECT id, content FROM memories WHERE deleted_at IS NULL",
534
+ "SELECT id, content FROM memories WHERE invalidated_at IS NULL",
445
535
  fetch=True,
446
536
  )
447
537
  success = 0
@@ -220,7 +220,7 @@ class TestMigration:
220
220
 
221
221
  # Simulate re-embedding (what --migrate-embeddings does in step 3)
222
222
  memories = database.execute(
223
- "SELECT id, content FROM memories WHERE deleted_at IS NULL",
223
+ "SELECT id, content FROM memories WHERE invalidated_at IS NULL",
224
224
  fetch=True,
225
225
  )
226
226
  for row in memories:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "get-claudia",
3
- "version": "1.35.0",
3
+ "version": "1.35.2",
4
4
  "description": "An AI assistant who learns how you work.",
5
5
  "keywords": [
6
6
  "claudia",