get-claudia 1.2.4 → 1.2.5

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.
@@ -46,46 +46,54 @@ class Database:
46
46
  conn.execute("PRAGMA synchronous = NORMAL")
47
47
  conn.execute("PRAGMA foreign_keys = ON")
48
48
 
49
- # Try to load sqlite-vec extension
49
+ # Try to load sqlite-vec for vector search
50
+ # Priority: sqlite_vec Python package first (works on Python 3.13+),
51
+ # then fall back to native extension loading
52
+ loaded = False
53
+
54
+ # Method 1: Try sqlite_vec Python package (recommended, works everywhere)
50
55
  try:
51
- conn.enable_load_extension(True)
52
- # Try common locations for sqlite-vec
53
- sqlite_vec_paths = [
54
- "vec0", # If installed system-wide
55
- "/usr/local/lib/sqlite-vec/vec0",
56
- "/opt/homebrew/lib/sqlite-vec/vec0",
57
- str(Path.home() / ".local" / "lib" / "sqlite-vec" / "vec0"),
58
- ]
59
-
60
- loaded = False
61
- for path in sqlite_vec_paths:
62
- try:
63
- conn.load_extension(path)
64
- loaded = True
65
- logger.debug(f"Loaded sqlite-vec from {path}")
66
- break
67
- except sqlite3.OperationalError:
68
- continue
69
-
70
- if not loaded:
71
- # Try loading via sqlite_vec Python package
72
- try:
73
- import sqlite_vec
74
- sqlite_vec.load(conn)
75
- loaded = True
76
- logger.debug("Loaded sqlite-vec via Python package")
77
- except ImportError:
78
- pass
79
-
80
- if not loaded:
81
- logger.warning(
82
- "sqlite-vec extension not found. Vector search will be unavailable. "
83
- "Install with: pip install sqlite-vec"
84
- )
85
-
86
- conn.enable_load_extension(False)
56
+ import sqlite_vec
57
+ sqlite_vec.load(conn)
58
+ loaded = True
59
+ logger.debug("Loaded sqlite-vec via Python package")
60
+ except ImportError:
61
+ logger.debug("sqlite_vec package not installed")
87
62
  except Exception as e:
88
- logger.warning(f"Could not load sqlite-vec: {e}")
63
+ logger.debug(f"sqlite_vec package failed: {e}")
64
+
65
+ # Method 2: Try native extension loading (for systems with pre-installed sqlite-vec)
66
+ if not loaded:
67
+ try:
68
+ conn.enable_load_extension(True)
69
+ sqlite_vec_paths = [
70
+ "vec0", # If installed system-wide
71
+ "/usr/local/lib/sqlite-vec/vec0",
72
+ "/opt/homebrew/lib/sqlite-vec/vec0",
73
+ str(Path.home() / ".local" / "lib" / "sqlite-vec" / "vec0"),
74
+ ]
75
+
76
+ for path in sqlite_vec_paths:
77
+ try:
78
+ conn.load_extension(path)
79
+ loaded = True
80
+ logger.debug(f"Loaded sqlite-vec from {path}")
81
+ break
82
+ except sqlite3.OperationalError:
83
+ continue
84
+
85
+ conn.enable_load_extension(False)
86
+ except AttributeError:
87
+ # Python 3.13+ may not have enable_load_extension
88
+ logger.debug("enable_load_extension not available (Python 3.13+)")
89
+ except Exception as e:
90
+ logger.debug(f"Extension loading failed: {e}")
91
+
92
+ if not loaded:
93
+ logger.warning(
94
+ "sqlite-vec not available. Vector search will be disabled. "
95
+ "Install with: pip install sqlite-vec"
96
+ )
89
97
 
90
98
  self._local.connection = conn
91
99
 
@@ -3,10 +3,13 @@ Embedding service for Claudia Memory System
3
3
 
4
4
  Connects to local Ollama for generating text embeddings.
5
5
  Uses all-minilm:l6-v2 model (384 dimensions) for semantic search.
6
+
7
+ Includes retry logic to wait for Ollama to start (e.g., after system boot).
6
8
  """
7
9
 
8
10
  import asyncio
9
11
  import logging
12
+ import time
10
13
  from typing import List, Optional
11
14
 
12
15
  import httpx
@@ -15,6 +18,10 @@ from .config import get_config
15
18
 
16
19
  logger = logging.getLogger(__name__)
17
20
 
21
+ # Retry configuration for waiting on Ollama
22
+ OLLAMA_RETRY_ATTEMPTS = 5
23
+ OLLAMA_RETRY_DELAY = 2 # seconds
24
+
18
25
 
19
26
  class EmbeddingService:
20
27
  """Generate embeddings using local Ollama"""
@@ -40,11 +47,52 @@ class EmbeddingService:
40
47
  self._sync_client = httpx.Client(timeout=30.0)
41
48
  return self._sync_client
42
49
 
50
+ async def _wait_for_ollama(self, max_retries: int = OLLAMA_RETRY_ATTEMPTS, delay: float = OLLAMA_RETRY_DELAY) -> bool:
51
+ """Wait for Ollama to be available with retries (async)"""
52
+ client = await self._get_client()
53
+ for i in range(max_retries):
54
+ try:
55
+ response = await client.get(f"{self.host}/api/tags", timeout=5.0)
56
+ if response.status_code == 200:
57
+ logger.debug(f"Ollama available after {i + 1} attempt(s)")
58
+ return True
59
+ except Exception:
60
+ pass
61
+ if i < max_retries - 1:
62
+ logger.debug(f"Waiting for Ollama (attempt {i + 1}/{max_retries})...")
63
+ await asyncio.sleep(delay)
64
+ return False
65
+
66
+ def _wait_for_ollama_sync(self, max_retries: int = OLLAMA_RETRY_ATTEMPTS, delay: float = OLLAMA_RETRY_DELAY) -> bool:
67
+ """Wait for Ollama to be available with retries (sync)"""
68
+ client = self._get_sync_client()
69
+ for i in range(max_retries):
70
+ try:
71
+ response = client.get(f"{self.host}/api/tags", timeout=5.0)
72
+ if response.status_code == 200:
73
+ logger.debug(f"Ollama available after {i + 1} attempt(s)")
74
+ return True
75
+ except Exception:
76
+ pass
77
+ if i < max_retries - 1:
78
+ logger.debug(f"Waiting for Ollama (attempt {i + 1}/{max_retries})...")
79
+ time.sleep(delay)
80
+ return False
81
+
43
82
  async def is_available(self) -> bool:
44
- """Check if Ollama is running and model is available"""
83
+ """Check if Ollama is running and model is available.
84
+
85
+ Uses retry logic to wait for Ollama if it's starting up (e.g., after boot).
86
+ """
45
87
  if self._available is not None:
46
88
  return self._available
47
89
 
90
+ # Wait for Ollama to be available (with retries)
91
+ if not await self._wait_for_ollama():
92
+ logger.warning("Ollama not available after retries. Vector search disabled.")
93
+ self._available = False
94
+ return self._available
95
+
48
96
  try:
49
97
  client = await self._get_client()
50
98
  response = await client.get(f"{self.host}/api/tags")
@@ -65,16 +113,25 @@ class EmbeddingService:
65
113
  else:
66
114
  self._available = False
67
115
  except Exception as e:
68
- logger.warning(f"Ollama not available: {e}")
116
+ logger.warning(f"Ollama error: {e}")
69
117
  self._available = False
70
118
 
71
119
  return self._available
72
120
 
73
121
  def is_available_sync(self) -> bool:
74
- """Synchronous check if Ollama is available"""
122
+ """Synchronous check if Ollama is available.
123
+
124
+ Uses retry logic to wait for Ollama if it's starting up (e.g., after boot).
125
+ """
75
126
  if self._available is not None:
76
127
  return self._available
77
128
 
129
+ # Wait for Ollama to be available (with retries)
130
+ if not self._wait_for_ollama_sync():
131
+ logger.warning("Ollama not available after retries. Vector search disabled.")
132
+ self._available = False
133
+ return self._available
134
+
78
135
  try:
79
136
  client = self._get_sync_client()
80
137
  response = client.get(f"{self.host}/api/tags")
@@ -88,7 +145,7 @@ class EmbeddingService:
88
145
  else:
89
146
  self._available = False
90
147
  except Exception as e:
91
- logger.warning(f"Ollama not available: {e}")
148
+ logger.warning(f"Ollama error: {e}")
92
149
  self._available = False
93
150
 
94
151
  return self._available
@@ -94,18 +94,73 @@ else
94
94
  echo -e " ${DIM}Database will be created on first use.${NC}"
95
95
  fi
96
96
 
97
- # Check 7: Ollama (optional)
98
- echo -n "7. Ollama for vectors... "
97
+ # Check 7: Ollama installed
98
+ echo -n "7. Ollama installed... "
99
+ if command -v ollama &> /dev/null; then
100
+ echo -e "${GREEN}✓ OK${NC}"
101
+ else
102
+ echo -e "${YELLOW}○ Not installed (keyword search will be used)${NC}"
103
+ echo -e " ${DIM}Optional: brew install ollama${NC}"
104
+ fi
105
+
106
+ # Check 8: Ollama running
107
+ echo -n "8. Ollama running... "
108
+ if curl -s http://localhost:11434/api/tags &>/dev/null; then
109
+ echo -e "${GREEN}✓ Running${NC}"
110
+ else
111
+ echo -e "${YELLOW}○ Not running${NC}"
112
+ if [[ "$OSTYPE" == "darwin"* ]]; then
113
+ if [ -f "$HOME/Library/LaunchAgents/com.ollama.serve.plist" ]; then
114
+ echo -e " ${DIM}LaunchAgent exists - try: launchctl load ~/Library/LaunchAgents/com.ollama.serve.plist${NC}"
115
+ else
116
+ echo -e " ${DIM}Start with: ollama serve${NC}"
117
+ fi
118
+ else
119
+ echo -e " ${DIM}Start with: ollama serve${NC}"
120
+ fi
121
+ ISSUES_FOUND=$((ISSUES_FOUND + 1))
122
+ fi
123
+
124
+ # Check 9: Ollama auto-start (macOS only)
125
+ if [[ "$OSTYPE" == "darwin"* ]]; then
126
+ echo -n "9. Ollama auto-start... "
127
+ if [ -f "$HOME/Library/LaunchAgents/com.ollama.serve.plist" ]; then
128
+ echo -e "${GREEN}✓ LaunchAgent configured${NC}"
129
+ else
130
+ echo -e "${YELLOW}○ No LaunchAgent${NC}"
131
+ echo -e " ${DIM}Ollama won't start on boot. Re-run memory installer to configure.${NC}"
132
+ fi
133
+ else
134
+ echo -e "9. Ollama auto-start... ${DIM}(Linux - check systemd if needed)${NC}"
135
+ fi
136
+
137
+ # Check 10: Embedding model
138
+ echo -n "10. Embedding model... "
99
139
  if command -v ollama &> /dev/null; then
100
140
  if ollama list 2>/dev/null | grep -q "minilm"; then
101
- echo -e "${GREEN}✓ Available with embedding model${NC}"
141
+ echo -e "${GREEN}✓ all-minilm model available${NC}"
102
142
  else
103
- echo -e "${YELLOW}○ Installed but no embedding model${NC}"
143
+ echo -e "${YELLOW}○ No embedding model${NC}"
104
144
  echo -e " ${DIM}Run: ollama pull all-minilm:l6-v2${NC}"
145
+ ISSUES_FOUND=$((ISSUES_FOUND + 1))
105
146
  fi
106
147
  else
107
- echo -e "${YELLOW}○ Not installed (keyword search will be used)${NC}"
108
- echo -e " ${DIM}Optional: brew install ollama${NC}"
148
+ echo -e "${DIM}○ Skipped (Ollama not installed)${NC}"
149
+ fi
150
+
151
+ # Check 11: sqlite-vec (vector search)
152
+ echo -n "11. Vector search (sqlite-vec)... "
153
+ VENV_PYTHON="$HOME/.claudia/daemon/venv/bin/python"
154
+ if [ -f "$VENV_PYTHON" ]; then
155
+ if $VENV_PYTHON -c "import sqlite_vec; print('ok')" 2>/dev/null | grep -q "ok"; then
156
+ echo -e "${GREEN}✓ sqlite-vec available${NC}"
157
+ else
158
+ echo -e "${YELLOW}○ sqlite-vec not working${NC}"
159
+ echo -e " ${DIM}Fix: $HOME/.claudia/daemon/venv/bin/pip install sqlite-vec${NC}"
160
+ ISSUES_FOUND=$((ISSUES_FOUND + 1))
161
+ fi
162
+ else
163
+ echo -e "${RED}✗ Virtual environment missing${NC}"
109
164
  fi
110
165
 
111
166
  # Summary
@@ -137,6 +137,45 @@ else
137
137
  fi
138
138
  fi
139
139
 
140
+ # Configure Ollama to auto-start on boot (macOS)
141
+ if [[ "$OSTYPE" == "darwin"* ]] && [ "$OLLAMA_AVAILABLE" = true ]; then
142
+ OLLAMA_PLIST="$HOME/Library/LaunchAgents/com.ollama.serve.plist"
143
+
144
+ if [ ! -f "$OLLAMA_PLIST" ]; then
145
+ # Find Ollama binary location
146
+ OLLAMA_BIN=$(command -v ollama)
147
+
148
+ mkdir -p "$HOME/Library/LaunchAgents"
149
+ cat > "$OLLAMA_PLIST" << PLIST
150
+ <?xml version="1.0" encoding="UTF-8"?>
151
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
152
+ <plist version="1.0">
153
+ <dict>
154
+ <key>Label</key>
155
+ <string>com.ollama.serve</string>
156
+ <key>ProgramArguments</key>
157
+ <array>
158
+ <string>$OLLAMA_BIN</string>
159
+ <string>serve</string>
160
+ </array>
161
+ <key>RunAtLoad</key>
162
+ <true/>
163
+ <key>KeepAlive</key>
164
+ <true/>
165
+ <key>StandardOutPath</key>
166
+ <string>/tmp/ollama.log</string>
167
+ <key>StandardErrorPath</key>
168
+ <string>/tmp/ollama.err</string>
169
+ </dict>
170
+ </plist>
171
+ PLIST
172
+ launchctl load "$OLLAMA_PLIST" 2>/dev/null || true
173
+ echo -e " ${GREEN}✓${NC} Ollama configured to auto-start on boot"
174
+ else
175
+ echo -e " ${GREEN}✓${NC} Ollama LaunchAgent already configured"
176
+ fi
177
+ fi
178
+
140
179
  echo
141
180
  echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
142
181
  echo
@@ -145,14 +184,41 @@ echo
145
184
  echo -e "${BOLD}Step 2/7: AI Models${NC}"
146
185
  echo
147
186
  if [ "$OLLAMA_AVAILABLE" = true ]; then
187
+ # Ensure Ollama is running before pulling model
188
+ if ! curl -s http://localhost:11434/api/tags &>/dev/null; then
189
+ echo -e " ${CYAN}◐${NC} Starting Ollama server..."
190
+ ollama serve &>/dev/null &
191
+ OLLAMA_PID=$!
192
+
193
+ # Wait for Ollama to be ready (up to 10 seconds)
194
+ for i in {1..10}; do
195
+ if curl -s http://localhost:11434/api/tags &>/dev/null; then
196
+ break
197
+ fi
198
+ sleep 1
199
+ done
200
+
201
+ if curl -s http://localhost:11434/api/tags &>/dev/null; then
202
+ echo -e " ${GREEN}✓${NC} Ollama server running"
203
+ else
204
+ echo -e " ${YELLOW}!${NC} Could not start Ollama (will retry on boot)"
205
+ fi
206
+ else
207
+ echo -e " ${GREEN}✓${NC} Ollama server already running"
208
+ fi
209
+
210
+ # Pull embedding model
148
211
  if ollama list 2>/dev/null | grep -q "all-minilm"; then
149
212
  echo -e " ${GREEN}✓${NC} Embedding model ready"
150
213
  else
151
214
  echo -e " ${CYAN}◐${NC} Downloading embedding model (45MB)..."
152
215
  echo -e " ${DIM}This gives Claudia semantic understanding${NC}"
153
216
  echo
154
- ollama pull all-minilm:l6-v2 2>/dev/null || echo -e " ${YELLOW}!${NC} Model pull failed, continuing"
155
- echo -e " ${GREEN}✓${NC} Model downloaded"
217
+ if ollama pull all-minilm:l6-v2 2>/dev/null; then
218
+ echo -e " ${GREEN}✓${NC} Model downloaded"
219
+ else
220
+ echo -e " ${YELLOW}!${NC} Model pull failed (will retry when Ollama runs)"
221
+ fi
156
222
  fi
157
223
  else
158
224
  echo -e " ${YELLOW}○${NC} Skipping (Ollama not available)"
@@ -309,20 +375,58 @@ echo
309
375
  echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
310
376
  echo
311
377
 
312
- # Verify
378
+ # Verify all services
313
379
  echo -e "${BOLD}Step 7/7: Verification${NC}"
314
380
  echo
315
- echo -e " ${CYAN}◐${NC} Waiting for daemon to start..."
381
+ echo -e " ${CYAN}◐${NC} Checking all services..."
316
382
  sleep 3
317
383
 
384
+ # Check 1: Ollama running
385
+ if curl -s http://localhost:11434/api/tags &>/dev/null; then
386
+ echo -e " ${GREEN}✓${NC} Ollama running"
387
+ else
388
+ echo -e " ${YELLOW}○${NC} Ollama not running (will start on next boot)"
389
+ fi
390
+
391
+ # Check 2: Ollama LaunchAgent (macOS)
392
+ if [[ "$OSTYPE" == "darwin"* ]]; then
393
+ if [ -f "$HOME/Library/LaunchAgents/com.ollama.serve.plist" ]; then
394
+ echo -e " ${GREEN}✓${NC} Ollama auto-start configured"
395
+ else
396
+ echo -e " ${YELLOW}○${NC} Ollama auto-start not configured"
397
+ fi
398
+ fi
399
+
400
+ # Check 3: Embedding model
401
+ if [ "$OLLAMA_AVAILABLE" = true ] && ollama list 2>/dev/null | grep -q "minilm"; then
402
+ echo -e " ${GREEN}✓${NC} Embedding model ready"
403
+ else
404
+ echo -e " ${YELLOW}○${NC} Embedding model pending"
405
+ fi
406
+
407
+ # Check 4: sqlite-vec (vector search)
408
+ if "$VENV_DIR/bin/python" -c "import sqlite_vec" 2>/dev/null; then
409
+ echo -e " ${GREEN}✓${NC} Vector search available (sqlite-vec)"
410
+ else
411
+ echo -e " ${YELLOW}○${NC} Vector search unavailable (keyword search only)"
412
+ fi
413
+
414
+ # Check 5: Memory daemon health
318
415
  if curl -s "http://localhost:3848/health" 2>/dev/null | grep -q "healthy"; then
319
- echo -e " ${GREEN}✓${NC} Health check passed"
416
+ echo -e " ${GREEN}✓${NC} Memory daemon running"
320
417
  HEALTH_OK=true
321
418
  else
322
- echo -e " ${YELLOW}○${NC} Health check pending (daemon still starting)"
419
+ echo -e " ${YELLOW}○${NC} Memory daemon starting..."
323
420
  HEALTH_OK=false
324
421
  fi
325
422
 
423
+ # Check 6: Claudia LaunchAgent (macOS)
424
+ if [[ "$OSTYPE" == "darwin"* ]]; then
425
+ if [ -f "$HOME/Library/LaunchAgents/com.claudia.memory.plist" ]; then
426
+ echo -e " ${GREEN}✓${NC} Claudia auto-start configured"
427
+ fi
428
+ fi
429
+
326
430
  echo
327
431
  echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
328
432
  echo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "get-claudia",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "An AI assistant who learns how you work.",
5
5
  "keywords": [
6
6
  "claudia",