2 Commits
v1.3 ... main

Author SHA1 Message Date
root
5a83ee0c6c fix: add PYTHONUNBUFFERED=1 for real-time logging
- Python buffers stdout when running as systemd service (no TTY)
- This prevented journalctl from showing real-time turn captures
- Added Environment='PYTHONUNBUFFERED=1' to disable buffering
- Now shows ' Turn N (role) → Qdrant' in real-time via journalctl -f

Fixes issue where watcher captured turns but only logged on restart.
2026-03-13 13:13:40 -05:00
root
f21c4db0f8 feat: add v1.3 validation script 2026-03-10 12:14:17 -05:00
3 changed files with 278 additions and 0 deletions

View File

@@ -2,6 +2,36 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [v1.4] - 2026-03-13
### Fixed
#### Real-Time Logging Not Visible in journalctl
**Error:** Watcher was capturing turns to Qdrant but `journalctl -u mem-qdrant-watcher -f` showed no output between restarts.
**Root Cause:**
- Python buffers stdout when not connected to a TTY (systemd service)
- `print()` statements in the watcher were buffered, not flushed
- Logs only appeared on service restart when buffer was flushed
**Impact:** Impossible to monitor real-time capture status, difficult to debug
**Fix:**
- Added `Environment="PYTHONUNBUFFERED=1"` to systemd service file
- This disables Python's stdout buffering, forcing immediate flush
**Changed Files:**
- `watcher/mem-qdrant-watcher.service` - Added PYTHONUNBUFFERED environment variable
**Validation:**
```bash
journalctl -u mem-qdrant-watcher -f
# Now shows: ✅ Turn 170 (assistant) → Qdrant (in real-time)
```
---
## [v1.3] - 2026-03-10 ## [v1.3] - 2026-03-10
### Fixed ### Fixed

247
validate_v1.3.sh Executable file
View File

@@ -0,0 +1,247 @@
#!/bin/bash
# Validation Script for openclaw-true-recall-base v1.3
# Tests all fixes and changes from v1.2 → v1.3
set -e
echo "╔══════════════════════════════════════════════════════════════════════════╗"
echo "║ TrueRecall Base v1.3 Validation Script ║"
echo "╚══════════════════════════════════════════════════════════════════════════╝"
echo ""
PASS=0
FAIL=0
WARN=0
check_pass() { echo "$1"; ((PASS++)); }
check_fail() { echo "$1"; ((FAIL++)); }
check_warn() { echo "⚠️ $1"; ((WARN++)); }
# ════════════════════════════════════════════════════════════════════════════
# SECTION 1: File Structure
# ════════════════════════════════════════════════════════════════════════════
echo ""
echo "═════════════════════════════════════════════════════════════════════════"
echo "SECTION 1: File Structure"
echo "═════════════════════════════════════════════════════════════════════════"
PROJECT_DIR="$(dirname "$0")"
if [ -f "$PROJECT_DIR/CHANGELOG.md" ]; then
check_pass "CHANGELOG.md exists"
else
check_fail "CHANGELOG.md missing"
fi
if [ -f "$PROJECT_DIR/watcher/realtime_qdrant_watcher.py" ]; then
check_pass "realtime_qdrant_watcher.py exists"
else
check_fail "realtime_qdrant_watcher.py missing"
fi
# Check version in file
VERSION=$(grep -m1 "TrueRecall v" "$PROJECT_DIR/watcher/realtime_qdrant_watcher.py" | grep -oE "v[0-9]+\.[0-9]+")
if [ "$VERSION" = "v1.3" ]; then
check_pass "Version is v1.3"
else
check_fail "Version mismatch: expected v1.3, got $VERSION"
fi
# ════════════════════════════════════════════════════════════════════════════
# SECTION 2: Code Changes (v1.3 Fixes)
# ════════════════════════════════════════════════════════════════════════════
echo ""
echo "═════════════════════════════════════════════════════════════════════════"
echo "SECTION 2: Code Changes (v1.3 Fixes)"
echo "═════════════════════════════════════════════════════════════════════════"
# Fix 1: FileNotFoundError check
if grep -q "if not session_file.exists():" "$PROJECT_DIR/watcher/realtime_qdrant_watcher.py"; then
check_pass "FileNotFoundError fix: Pre-check exists before open()"
else
check_fail "FileNotFoundError fix MISSING: No session_file.exists() check"
fi
if grep -q "except FileNotFoundError:" "$PROJECT_DIR/watcher/realtime_qdrant_watcher.py"; then
check_pass "FileNotFoundError fix: Exception handler present"
else
check_fail "FileNotFoundError fix MISSING: No FileNotFoundError exception handler"
fi
# Fix 2: Chunking for long content
if grep -q "def chunk_text" "$PROJECT_DIR/watcher/realtime_qdrant_watcher.py"; then
check_pass "Chunking fix: chunk_text() function defined"
else
check_fail "Chunking fix MISSING: No chunk_text() function"
fi
if grep -q "chunk_text_content" "$PROJECT_DIR/watcher/realtime_qdrant_watcher.py"; then
check_pass "Chunking fix: chunk_text_content used in store_to_qdrant()"
else
check_fail "Chunking fix MISSING: Not using chunked content"
fi
if grep -q "chunk_index" "$PROJECT_DIR/watcher/realtime_qdrant_watcher.py"; then
check_pass "Chunking fix: chunk_index metadata added"
else
check_fail "Chunking fix MISSING: No chunk_index metadata"
fi
# ════════════════════════════════════════════════════════════════════════════
# SECTION 3: Service Status
# ════════════════════════════════════════════════════════════════════════════
echo ""
echo "═════════════════════════════════════════════════════════════════════════"
echo "SECTION 3: Service Status"
echo "═════════════════════════════════════════════════════════════════════════"
if systemctl is-active --quiet mem-qdrant-watcher 2>/dev/null; then
check_pass "mem-qdrant-watcher service is running"
else
check_warn "mem-qdrant-watcher service not running (may be running in daemon mode)"
fi
# Check for running watcher process
if pgrep -f "realtime_qdrant_watcher" > /dev/null; then
check_pass "realtime_qdrant_watcher process is running"
else
check_fail "realtime_qdrant_watcher process NOT running"
fi
# ════════════════════════════════════════════════════════════════════════════
# SECTION 4: Connectivity
# ════════════════════════════════════════════════════════════════════════════
echo ""
echo "═════════════════════════════════════════════════════════════════════════"
echo "SECTION 4: Connectivity"
echo "═════════════════════════════════════════════════════════════════════════"
# Qdrant
QDRANT_URL="${QDRANT_URL:-http://10.0.0.40:6333}"
if curl -s -o /dev/null -w "%{http_code}" "$QDRANT_URL/collections/memories_tr" | grep -q "200"; then
check_pass "Qdrant memories_tr collection reachable"
else
check_fail "Qdrant memories_tr collection NOT reachable"
fi
# Ollama (local)
if curl -s -o /dev/null -w "%{http_code}" "http://localhost:11434/api/tags" | grep -q "200"; then
check_pass "Ollama (localhost) reachable"
else
check_fail "Ollama (localhost) NOT reachable"
fi
# Check embedding model
if curl -s "http://localhost:11434/api/tags" | grep -q "snowflake-arctic-embed2"; then
check_pass "Embedding model snowflake-arctic-embed2 available"
else
check_fail "Embedding model snowflake-arctic-embed2 NOT available"
fi
# ════════════════════════════════════════════════════════════════════════════
# SECTION 5: Crash Loop Test
# ════════════════════════════════════════════════════════════════════════════
echo ""
echo "═════════════════════════════════════════════════════════════════════════"
echo "SECTION 5: Crash Loop Test (Last 1 Hour)"
echo "═════════════════════════════════════════════════════════════════════════"
RESTARTS=$(journalctl -u mem-qdrant-watcher --since "1 hour ago" --no-pager 2>/dev/null | grep -c "Started mem-qdrant-watcher" || echo "0")
if [ "$RESTARTS" -le 2 ]; then
check_pass "Restarts in last hour: $RESTARTS (expected ≤2)"
else
check_fail "Restarts in last hour: $RESTARTS (too many, expected ≤2)"
fi
# Check for FileNotFoundError in logs
ERRORS=$(journalctl -u mem-qdrant-watcher --since "1 hour ago" --no-pager 2>/dev/null | grep -c "FileNotFoundError" || echo "0")
if [ "$ERRORS" -eq 0 ]; then
check_pass "No FileNotFoundError in last hour"
else
check_fail "FileNotFoundError found $ERRORS times in last hour"
fi
# ════════════════════════════════════════════════════════════════════════════
# SECTION 6: Chunking Test
# ════════════════════════════════════════════════════════════════════════════
echo ""
echo "═════════════════════════════════════════════════════════════════════════"
echo "SECTION 6: Chunking Test"
echo "═════════════════════════════════════════════════════════════════════════"
# Test chunking with Python
python3 -c "
import sys
sys.path.insert(0, '$PROJECT_DIR/watcher')
# Import chunk_text function
exec(open('$PROJECT_DIR/watcher/realtime_qdrant_watcher.py').read().split('def chunk_text')[1].split('def store_to_qdrant')[0])
# Test with long content
test_content = 'A' * 10000
chunks = chunk_text(test_content, max_chars=6000, overlap=200)
if len(chunks) > 1:
print(f'PASS: chunk_text splits 10000 chars into {len(chunks)} chunks')
sys.exit(0)
else:
print(f'FAIL: chunk_text returned {len(chunks)} chunks for 10000 chars')
sys.exit(1)
" 2>/dev/null && check_pass "chunk_text() splits long content correctly" || check_fail "chunk_text() test failed"
# ════════════════════════════════════════════════════════════════════════════
# SECTION 7: Git Status
# ════════════════════════════════════════════════════════════════════════════
echo ""
echo "═════════════════════════════════════════════════════════════════════════"
echo "SECTION 7: Git Status"
echo "═════════════════════════════════════════════════════════════════════════"
cd "$PROJECT_DIR"
# Check for v1.3 tag
if git tag -l | grep -q "v1.3"; then
check_pass "Git tag v1.3 exists"
else
check_fail "Git tag v1.3 missing"
fi
# Check CHANGELOG.md committed
if git log --oneline -1 | grep -q "v1.3"; then
check_pass "v1.3 commit in git log"
else
check_fail "v1.3 commit not found in git log"
fi
# Check for uncommitted changes
UNCOMMITTED=$(git status --short 2>/dev/null | wc -l)
if [ "$UNCOMMITTED" -eq 0 ]; then
check_pass "No uncommitted changes"
else
check_warn "$UNCOMMITTED uncommitted files"
fi
# ════════════════════════════════════════════════════════════════════════════
# SUMMARY
# ════════════════════════════════════════════════════════════════════════════
echo ""
echo "═════════════════════════════════════════════════════════════════════════"
echo "VALIDATION SUMMARY"
echo "═════════════════════════════════════════════════════════════════════════"
echo ""
echo "✅ Passed: $PASS"
echo "❌ Failed: $FAIL"
echo "⚠️ Warnings: $WARN"
echo ""
if [ $FAIL -eq 0 ]; then
echo "╔══════════════════════════════════════════════════════════════════════════╗"
echo "║ ✅ ALL VALIDATIONS PASSED - v1.3 READY FOR PRODUCTION ║"
echo "╚══════════════════════════════════════════════════════════════════════════╝"
exit 0
else
echo "╔══════════════════════════════════════════════════════════════════════════╗"
echo "║ ❌ VALIDATION FAILED - $FAIL ISSUE(S) NEED ATTENTION ║"
echo "╚══════════════════════════════════════════════════════════════════════════╝"
exit 1
fi

View File

@@ -11,6 +11,7 @@ Environment="QDRANT_COLLECTION=memories_tr"
Environment="OLLAMA_URL=http://localhost:11434" Environment="OLLAMA_URL=http://localhost:11434"
Environment="EMBEDDING_MODEL=snowflake-arctic-embed2" Environment="EMBEDDING_MODEL=snowflake-arctic-embed2"
Environment="USER_ID=rob" Environment="USER_ID=rob"
Environment="PYTHONUNBUFFERED=1"
ExecStart=/usr/bin/python3 /root/.openclaw/workspace/.local_projects/openclaw-true-recall-base/watcher/realtime_qdrant_watcher.py --daemon ExecStart=/usr/bin/python3 /root/.openclaw/workspace/.local_projects/openclaw-true-recall-base/watcher/realtime_qdrant_watcher.py --daemon
Restart=always Restart=always
RestartSec=5 RestartSec=5