Clean and organize project structure

Major reorganization:
- Created scripts/ directory for all utility scripts
- Created config/ directory for configuration files
- Moved all test files to tests/ directory
- Updated all script paths to work with new structure
- Updated README.md with new project structure diagram

New structure:
├── src/          # Source code (API + MCP)
├── scripts/      # Utility scripts (start-*.sh, docs_server.py, etc.)
├── tests/        # All test files and debug utilities
├── config/       # Configuration files (JSON, Caddy config)
├── docs/         # Documentation website
└── logs/         # Log files

All scripts updated to use relative paths from project root.
Documentation updated with new folder structure.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Docker Config Backup
2025-07-17 14:11:08 +02:00
parent b74c7d79ca
commit f0db3e5546
25 changed files with 209 additions and 383 deletions

105
scripts/docs_server.py Executable file
View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""
Simple authenticated web server for LangMem documentation
"""
import http.server
import socketserver
import base64
import os
from urllib.parse import parse_qs
# Authentication credentials
USERNAME = "langmem"
PASSWORD = "langmem2025"
class AuthHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
"""HTTP handler with basic authentication"""
def __init__(self, *args, **kwargs):
super().__init__(*args, directory="docs", **kwargs)
def do_GET(self):
"""Handle GET requests with authentication"""
if self.authenticate():
super().do_GET()
else:
self.send_auth_request()
def authenticate(self):
"""Check if request has valid authentication"""
auth_header = self.headers.get('Authorization')
if not auth_header:
return False
try:
auth_type, credentials = auth_header.split(' ', 1)
if auth_type.lower() != 'basic':
return False
decoded = base64.b64decode(credentials).decode('utf-8')
username, password = decoded.split(':', 1)
return username == USERNAME and password == PASSWORD
except Exception:
return False
def send_auth_request(self):
"""Send authentication request to client"""
self.send_response(401)
self.send_header('WWW-Authenticate', 'Basic realm="LangMem Documentation"')
self.send_header('Content-type', 'text/html')
self.end_headers()
html = """
<!DOCTYPE html>
<html>
<head>
<title>Authentication Required</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin-top: 100px; }
.container { max-width: 400px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; border-radius: 10px; }
</style>
</head>
<body>
<div class="container">
<h2>🔐 LangMem Documentation</h2>
<p>Authentication required to access documentation.</p>
<p><strong>Username:</strong> langmem<br>
<strong>Password:</strong> langmem2025</p>
<p>Please refresh the page and enter your credentials when prompted.</p>
</div>
</body>
</html>
"""
self.wfile.write(html.encode())
def start_server(port=8080):
"""Start the authenticated documentation server"""
try:
with socketserver.TCPServer(("", port), AuthHTTPRequestHandler) as httpd:
print(f"🌐 LangMem Documentation Server started!")
print(f"📍 URL: http://localhost:{port}")
print(f"🔑 Username: {USERNAME}")
print(f"🔑 Password: {PASSWORD}")
print(f"🛑 Press Ctrl+C to stop the server")
print("=" * 50)
httpd.serve_forever()
except KeyboardInterrupt:
print("\n🛑 Server stopped by user")
except Exception as e:
print(f"❌ Error starting server: {e}")
if __name__ == "__main__":
import sys
port = 8080
if len(sys.argv) > 1:
try:
port = int(sys.argv[1])
except ValueError:
print("Invalid port number. Using default port 8080.")
start_server(port)

156
scripts/get-claude-token.py Executable file
View File

@@ -0,0 +1,156 @@
#!/usr/bin/env python3
"""
Get access token for Claude Matrix user and join Home Assistant room
"""
import asyncio
import httpx
import json
MATRIX_HOMESERVER = "https://matrix.klas.chat"
CLAUDE_USERNAME = "claude"
CLAUDE_PASSWORD = "claude_assistant_2025"
HOME_ASSISTANT_ROOM_ID = "!xZkScMybPseErYMJDz:matrix.klas.chat"
async def login_and_get_token():
"""Login as Claude user and get access token"""
try:
async with httpx.AsyncClient() as client:
# Login request
response = await client.post(
f"{MATRIX_HOMESERVER}/_matrix/client/v3/login",
json={
"type": "m.login.password",
"user": CLAUDE_USERNAME,
"password": CLAUDE_PASSWORD
}
)
if response.status_code == 200:
data = response.json()
access_token = data["access_token"]
user_id = data["user_id"]
print(f"✅ Successfully logged in as {user_id}")
print(f"🔑 Access Token: {access_token}")
return access_token, user_id
else:
print(f"❌ Login failed: {response.status_code}")
print(f"Response: {response.text}")
return None, None
except Exception as e:
print(f"❌ Error during login: {e}")
return None, None
async def join_home_assistant_room(access_token):
"""Join the Home Assistant room"""
try:
async with httpx.AsyncClient() as client:
headers = {"Authorization": f"Bearer {access_token}"}
# Join room
response = await client.post(
f"{MATRIX_HOMESERVER}/_matrix/client/v3/join/{HOME_ASSISTANT_ROOM_ID}",
headers=headers
)
if response.status_code == 200:
print(f"✅ Successfully joined Home Assistant room")
return True
else:
print(f"❌ Failed to join room: {response.status_code}")
print(f"Response: {response.text}")
return False
except Exception as e:
print(f"❌ Error joining room: {e}")
return False
async def send_test_message(access_token):
"""Send a test message to verify everything works"""
try:
async with httpx.AsyncClient() as client:
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
message = "🤖 Hello! Claude Assistant is now connected to Matrix and ready to send you updates about your projects."
response = await client.post(
f"{MATRIX_HOMESERVER}/_matrix/client/v3/rooms/{HOME_ASSISTANT_ROOM_ID}/send/m.room.message",
headers=headers,
json={
"msgtype": "m.text",
"body": message
}
)
if response.status_code == 200:
print(f"✅ Test message sent successfully!")
return True
else:
print(f"❌ Failed to send test message: {response.status_code}")
print(f"Response: {response.text}")
return False
except Exception as e:
print(f"❌ Error sending test message: {e}")
return False
async def main():
"""Main function"""
print("🤖 Setting up Claude Matrix Integration")
print("=" * 50)
# Step 1: Login and get token
print("\n1. Logging in as Claude user...")
access_token, user_id = await login_and_get_token()
if not access_token:
print("\n❌ Cannot proceed without access token.")
print("Make sure the Claude user has been created first!")
return
# Step 2: Join Home Assistant room
print("\n2. Joining Home Assistant room...")
joined = await join_home_assistant_room(access_token)
if not joined:
print("\n❌ Cannot proceed without joining the room.")
return
# Step 3: Send test message
print("\n3. Sending test message...")
message_sent = await send_test_message(access_token)
# Step 4: Output configuration for Claude
print("\n" + "=" * 50)
print("🎉 Claude Matrix Setup Complete!")
print("\n📋 Configuration for Claude's global config:")
print(f" Matrix Homeserver: {MATRIX_HOMESERVER}")
print(f" Claude User ID: {user_id}")
print(f" Claude Access Token: {access_token}")
print(f" Home Assistant Room: {HOME_ASSISTANT_ROOM_ID}")
# Save to file for easy copying
config = {
"matrix_homeserver": MATRIX_HOMESERVER,
"claude_user_id": user_id,
"claude_access_token": access_token,
"home_assistant_room_id": HOME_ASSISTANT_ROOM_ID,
"credentials": {
"username": CLAUDE_USERNAME,
"password": CLAUDE_PASSWORD
}
}
with open("/home/klas/langmem/claude-matrix-config.json", "w") as f:
json.dump(config, f, indent=2)
print(f"\n💾 Configuration saved to: /home/klas/langmem/claude-matrix-config.json")
if __name__ == "__main__":
asyncio.run(main())

91
scripts/start-dev.sh Executable file
View File

@@ -0,0 +1,91 @@
#!/bin/bash
# LangMem Development Startup Script
set -e
echo "🚀 Starting LangMem Development Environment"
# Check if .env exists, if not copy from example
if [ ! -f .env ]; then
echo "📋 Creating .env file from example..."
cp .env.example .env
echo "⚠️ Please update .env with your actual database password"
fi
# Check if required services are running
echo "🔍 Checking required services..."
# Check Ollama
if ! curl -s http://localhost:11434/api/tags > /dev/null; then
echo "❌ Ollama is not running. Please start Ollama first."
exit 1
fi
echo "✅ Ollama is running"
# Check Supabase
if ! docker ps | grep -q supabase-db; then
echo "❌ Supabase is not running. Please start Supabase first."
exit 1
fi
echo "✅ Supabase is running"
# Check if localai network exists
if ! docker network ls | grep -q localai; then
echo "📡 Creating localai network..."
docker network create localai
fi
echo "✅ Docker network 'localai' is ready"
# Build and start services
echo "🏗️ Building and starting LangMem services..."
docker compose up -d --build
# Wait for services to be ready
echo "⏳ Waiting for services to be ready..."
sleep 10
# Check service health
echo "🔍 Checking service health..."
max_retries=30
retry_count=0
while [ $retry_count -lt $max_retries ]; do
if curl -s http://localhost:8765/health > /dev/null; then
echo "✅ LangMem API is healthy"
break
fi
retry_count=$((retry_count + 1))
echo "⏳ Waiting for API to be ready (attempt $retry_count/$max_retries)"
sleep 2
done
if [ $retry_count -eq $max_retries ]; then
echo "❌ API failed to become ready"
docker compose logs langmem-api
exit 1
fi
# Display service status
echo ""
echo "🎉 LangMem Development Environment is ready!"
echo ""
echo "📊 Service Status:"
echo " - LangMem API: http://localhost:8765"
echo " - API Documentation: http://localhost:8765/docs"
echo " - Neo4j Browser: http://localhost:7474"
echo " - Health Check: http://localhost:8765/health"
echo ""
echo "🔧 Useful Commands:"
echo " - View logs: docker compose logs -f langmem-api"
echo " - Run tests: ./scripts/test.sh"
echo " - Stop services: docker compose down"
echo " - Restart API: docker compose restart langmem-api"
echo ""
echo "🔑 API Key: langmem_api_key_2025"
echo ""
# Show current health status
echo "🏥 Current Health Status:"
curl -s http://localhost:8765/health | python3 -m json.tool || echo "Failed to get health status"

15
scripts/start-docs-server.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Start LangMem Documentation Server with Authentication
echo "🚀 Starting LangMem Documentation Server..."
echo "🔐 Authentication enabled with basic auth"
echo ""
# Change to project root
cd "$(dirname "$0")/.."
# Default port
PORT=${1:-8080}
# Start the authenticated server
python3 scripts/docs_server.py $PORT

62
scripts/start-mcp-server.sh Executable file
View File

@@ -0,0 +1,62 @@
#!/bin/bash
# Start LangMem MCP Server
# This script starts the MCP server for Claude Code integration
echo "🚀 Starting LangMem MCP Server..."
# Change to project root
cd "$(dirname "$0")/.."
# Check if virtual environment exists
if [ ! -d "venv" ]; then
echo "📦 Creating virtual environment..."
python3 -m venv venv
fi
# Activate virtual environment
echo "🔄 Activating virtual environment..."
source venv/bin/activate
# Install MCP requirements
echo "📥 Installing MCP requirements..."
pip install -r src/mcp/requirements.txt
# Check if LangMem API is running
echo "🔍 Checking LangMem API health..."
if curl -s -f http://localhost:8765/health > /dev/null; then
echo "✅ LangMem API is healthy"
else
echo "❌ LangMem API is not running!"
echo "💡 Start the LangMem API first: ./start-dev.sh"
exit 1
fi
# Set environment variables
export LANGMEM_API_URL="http://localhost:8765"
export LANGMEM_API_KEY="langmem_api_key_2025"
# Start MCP server
echo "🏃 Starting MCP server..."
echo "🔗 Connect from Claude Code using:"
echo " Server command: python /home/klas/langmem/src/mcp/server.py"
echo " Working directory: /home/klas/langmem"
echo ""
echo "📖 Available tools:"
echo " - store_memory: Store memories with AI relationship extraction"
echo " - search_memories: Search memories with hybrid vector + graph search"
echo " - retrieve_memories: Retrieve relevant memories for conversation context"
echo " - get_user_memories: Get all memories for a specific user"
echo " - delete_memory: Delete a specific memory"
echo " - health_check: Check LangMem system health"
echo ""
echo "🌐 Available resources:"
echo " - langmem://memories: Memory storage resource"
echo " - langmem://search: Search capabilities resource"
echo " - langmem://relationships: AI relationships resource"
echo " - langmem://health: System health resource"
echo ""
echo "Press Ctrl+C to stop the server..."
echo ""
python src/mcp/server.py

59
scripts/test.sh Executable file
View File

@@ -0,0 +1,59 @@
#!/bin/bash
# LangMem Test Runner Script
set -e
echo "🧪 Running LangMem API Tests"
# Change to project root
cd "$(dirname "$0")/.."
# Check if services are running
if ! curl -s http://localhost:8765/health > /dev/null; then
echo "❌ LangMem API is not running. Please start with ./scripts/start-dev.sh first."
exit 1
fi
# Install test dependencies
echo "📦 Installing test dependencies..."
pip install -r tests/requirements.txt
# Run different test suites based on argument
case "${1:-all}" in
"unit")
echo "🔬 Running unit tests..."
python -m pytest tests/test_api.py -v -m "not integration"
;;
"integration")
echo "🔗 Running integration tests..."
python -m pytest tests/test_integration.py -v -m "integration"
;;
"all")
echo "🎯 Running all tests..."
python -m pytest tests/ -v
;;
"quick")
echo "⚡ Running quick tests..."
python -m pytest tests/ -v -m "not slow"
;;
"coverage")
echo "📊 Running tests with coverage..."
python -m pytest tests/ -v --cov=src --cov-report=html --cov-report=term
;;
*)
echo "❌ Unknown test type: $1"
echo "Usage: ./scripts/test.sh [unit|integration|all|quick|coverage]"
exit 1
;;
esac
echo ""
echo "✅ Tests completed!"
# Show service logs if tests failed
if [ $? -ne 0 ]; then
echo ""
echo "❌ Tests failed. Showing recent API logs:"
docker-compose logs --tail=50 langmem-api
fi