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

View File

@@ -34,13 +34,13 @@ LangMem uses a hybrid approach combining:
```bash ```bash
git clone <repository> git clone <repository>
cd langmem-project cd langmem
``` ```
### 2. Start Development Environment ### 2. Start Development Environment
```bash ```bash
./start-dev.sh ./scripts/start-dev.sh
``` ```
This will: This will:
@@ -52,7 +52,7 @@ This will:
### 3. Test the API ### 3. Test the API
```bash ```bash
./test.sh ./scripts/test.sh
``` ```
## API Endpoints ## API Endpoints
@@ -139,19 +139,40 @@ NEO4J_PASSWORD=langmem_neo4j_password
### Project Structure ### Project Structure
``` ```
langmem-project/ langmem/
├── src/ ├── src/ # Source code
── api/ ── api/ # FastAPI application
── main.py # Main API application ── main.py # Main API server
├── tests/ │ │ ├── fact_extraction.py # Fact-based memory logic
├── test_api.py # API unit tests │ └── memory_manager.py # Memory management
│ └── mcp/ # Model Context Protocol
│ ├── server.py # MCP server for Claude Code
│ └── requirements.txt
├── scripts/ # Utility scripts
│ ├── start-dev.sh # Development startup
│ ├── start-mcp-server.sh # MCP server startup
│ ├── start-docs-server.sh # Documentation server
│ ├── docs_server.py # Authenticated docs server
│ ├── get-claude-token.py # Matrix setup utility
│ └── test.sh # Test runner
├── tests/ # Test suite
│ ├── test_api.py # API tests
│ ├── test_integration.py # Integration tests │ ├── test_integration.py # Integration tests
│ ├── test_fact_based_memory.py # Fact extraction tests
│ ├── debug_*.py # Debug utilities
│ └── conftest.py # Test configuration │ └── conftest.py # Test configuration
├── docs/ # Documentation website
│ ├── index.html # Main documentation
│ ├── api/ # API documentation
│ ├── architecture/ # Architecture docs
│ └── implementation/ # Setup guides
├── config/ # Configuration files
│ ├── mcp_config.json # MCP server config
│ ├── claude-matrix-config.json # Matrix setup
│ └── caddyfile-docs-update.txt # Caddy config
├── docker-compose.yml # Docker services ├── docker-compose.yml # Docker services
├── Dockerfile # API container ├── Dockerfile # API container
├── requirements.txt # Python dependencies ├── requirements.txt # Python dependencies
├── start-dev.sh # Development startup
├── test.sh # Test runner
└── README.md # This file └── README.md # This file
``` ```
@@ -159,19 +180,19 @@ langmem-project/
```bash ```bash
# All tests # All tests
./test.sh all ./scripts/test.sh all
# Unit tests only # Unit tests only
./test.sh unit ./scripts/test.sh unit
# Integration tests only # Integration tests only
./test.sh integration ./scripts/test.sh integration
# Quick tests (no slow tests) # Quick tests (no slow tests)
./test.sh quick ./scripts/test.sh quick
# With coverage # With coverage
./test.sh coverage ./scripts/test.sh coverage
``` ```
### Local Development ### Local Development
@@ -289,10 +310,10 @@ Start the authenticated documentation server:
```bash ```bash
# Start documentation server on port 8080 (default) # Start documentation server on port 8080 (default)
./start-docs-server.sh ./scripts/start-docs-server.sh
# Or specify a custom port # Or specify a custom port
./start-docs-server.sh 8090 ./scripts/start-docs-server.sh 8090
``` ```
**Access Credentials:** **Access Credentials:**
@@ -310,7 +331,7 @@ Start the authenticated documentation server:
You can also run the documentation server directly: You can also run the documentation server directly:
```bash ```bash
python3 docs_server.py [port] python3 scripts/docs_server.py [port]
``` ```
Then visit: `http://localhost:8080` (or your specified port) Then visit: `http://localhost:8080` (or your specified port)

View File

@@ -79,7 +79,7 @@ echo " - Health Check: http://localhost:8765/health"
echo "" echo ""
echo "🔧 Useful Commands:" echo "🔧 Useful Commands:"
echo " - View logs: docker compose logs -f langmem-api" echo " - View logs: docker compose logs -f langmem-api"
echo " - Run tests: ./test.sh" echo " - Run tests: ./scripts/test.sh"
echo " - Stop services: docker compose down" echo " - Stop services: docker compose down"
echo " - Restart API: docker compose restart langmem-api" echo " - Restart API: docker compose restart langmem-api"
echo "" echo ""

View File

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

View File

@@ -5,6 +5,9 @@
echo "🚀 Starting LangMem MCP Server..." echo "🚀 Starting LangMem MCP Server..."
# Change to project root
cd "$(dirname "$0")/.."
# Check if virtual environment exists # Check if virtual environment exists
if [ ! -d "venv" ]; then if [ ! -d "venv" ]; then
echo "📦 Creating virtual environment..." echo "📦 Creating virtual environment..."
@@ -36,8 +39,8 @@ export LANGMEM_API_KEY="langmem_api_key_2025"
# Start MCP server # Start MCP server
echo "🏃 Starting MCP server..." echo "🏃 Starting MCP server..."
echo "🔗 Connect from Claude Code using:" echo "🔗 Connect from Claude Code using:"
echo " Server command: python /home/klas/langmem-project/src/mcp/server.py" echo " Server command: python /home/klas/langmem/src/mcp/server.py"
echo " Working directory: /home/klas/langmem-project" echo " Working directory: /home/klas/langmem"
echo "" echo ""
echo "📖 Available tools:" echo "📖 Available tools:"
echo " - store_memory: Store memories with AI relationship extraction" echo " - store_memory: Store memories with AI relationship extraction"

View File

@@ -6,9 +6,12 @@ set -e
echo "🧪 Running LangMem API Tests" echo "🧪 Running LangMem API Tests"
# Change to project root
cd "$(dirname "$0")/.."
# Check if services are running # Check if services are running
if ! curl -s http://localhost:8765/health > /dev/null; then if ! curl -s http://localhost:8765/health > /dev/null; then
echo "❌ LangMem API is not running. Please start with ./start-dev.sh first." echo "❌ LangMem API is not running. Please start with ./scripts/start-dev.sh first."
exit 1 exit 1
fi fi
@@ -40,7 +43,7 @@ case "${1:-all}" in
;; ;;
*) *)
echo "❌ Unknown test type: $1" echo "❌ Unknown test type: $1"
echo "Usage: ./test.sh [unit|integration|all|quick|coverage]" echo "Usage: ./scripts/test.sh [unit|integration|all|quick|coverage]"
exit 1 exit 1
;; ;;
esac esac

View File

@@ -1,169 +0,0 @@
#!/usr/bin/env python3
"""
Test the LangMem API endpoints
"""
import asyncio
import httpx
import json
from uuid import uuid4
# Configuration
API_BASE_URL = "http://localhost:8765"
API_KEY = "langmem_api_key_2025"
TEST_USER_ID = f"test_user_{uuid4()}"
async def test_api_endpoints():
"""Test all API endpoints"""
print("🧪 Testing LangMem API Endpoints")
print("=" * 50)
headers = {"Authorization": f"Bearer {API_KEY}"}
async with httpx.AsyncClient() as client:
# Test 1: Root endpoint
print("\n1. Testing root endpoint...")
try:
response = await client.get(f"{API_BASE_URL}/")
print(f"✅ Root endpoint: {response.status_code}")
print(f" Response: {response.json()}")
except Exception as e:
print(f"❌ Root endpoint failed: {e}")
# Test 2: Health check
print("\n2. Testing health check...")
try:
response = await client.get(f"{API_BASE_URL}/health")
print(f"✅ Health check: {response.status_code}")
data = response.json()
print(f" Overall status: {data.get('status')}")
for service, status in data.get('services', {}).items():
print(f" {service}: {status}")
except Exception as e:
print(f"❌ Health check failed: {e}")
# Test 3: Store memory
print("\n3. Testing memory storage...")
try:
memory_data = {
"content": "FastAPI is a modern web framework for building APIs with Python",
"user_id": TEST_USER_ID,
"session_id": "test_session_1",
"metadata": {
"category": "programming",
"language": "python",
"framework": "fastapi"
}
}
response = await client.post(
f"{API_BASE_URL}/v1/memories/store",
json=memory_data,
headers=headers,
timeout=30.0
)
if response.status_code == 200:
data = response.json()
memory_id = data["id"]
print(f"✅ Memory stored successfully: {memory_id}")
print(f" Status: {data['status']}")
else:
print(f"❌ Memory storage failed: {response.status_code}")
print(f" Response: {response.text}")
return
except Exception as e:
print(f"❌ Memory storage failed: {e}")
return
# Test 4: Search memories
print("\n4. Testing memory search...")
try:
search_data = {
"query": "Python web framework",
"user_id": TEST_USER_ID,
"limit": 5,
"threshold": 0.5
}
response = await client.post(
f"{API_BASE_URL}/v1/memories/search",
json=search_data,
headers=headers,
timeout=30.0
)
if response.status_code == 200:
data = response.json()
print(f"✅ Memory search successful")
print(f" Found {data['total_count']} memories")
for memory in data['memories']:
print(f" - {memory['content'][:50]}... (similarity: {memory['similarity']:.3f})")
else:
print(f"❌ Memory search failed: {response.status_code}")
print(f" Response: {response.text}")
except Exception as e:
print(f"❌ Memory search failed: {e}")
# Test 5: Retrieve memories for conversation
print("\n5. Testing memory retrieval...")
try:
retrieve_data = {
"messages": [
{"role": "user", "content": "I want to learn about web development"},
{"role": "assistant", "content": "Great! What technology are you interested in?"},
{"role": "user", "content": "I heard Python is good for web APIs"}
],
"user_id": TEST_USER_ID,
"session_id": "test_session_1"
}
response = await client.post(
f"{API_BASE_URL}/v1/memories/retrieve",
json=retrieve_data,
headers=headers,
timeout=30.0
)
if response.status_code == 200:
data = response.json()
print(f"✅ Memory retrieval successful")
print(f" Retrieved {data['total_count']} relevant memories")
for memory in data['memories']:
print(f" - {memory['content'][:50]}... (similarity: {memory['similarity']:.3f})")
else:
print(f"❌ Memory retrieval failed: {response.status_code}")
print(f" Response: {response.text}")
except Exception as e:
print(f"❌ Memory retrieval failed: {e}")
# Test 6: Get user memories
print("\n6. Testing user memory listing...")
try:
response = await client.get(
f"{API_BASE_URL}/v1/memories/users/{TEST_USER_ID}",
headers=headers,
timeout=30.0
)
if response.status_code == 200:
data = response.json()
print(f"✅ User memory listing successful")
print(f" User has {data['total_count']} memories")
for memory in data['memories']:
print(f" - {memory['content'][:50]}... (created: {memory['created_at'][:19]})")
else:
print(f"❌ User memory listing failed: {response.status_code}")
print(f" Response: {response.text}")
except Exception as e:
print(f"❌ User memory listing failed: {e}")
print("\n" + "=" * 50)
print("🎉 API Testing Complete!")
if __name__ == "__main__":
asyncio.run(test_api_endpoints())

View File

@@ -1,204 +1,169 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Test suite for LangMem API Test the LangMem API endpoints
""" """
import asyncio import asyncio
import json
import pytest
import httpx import httpx
import json
from uuid import uuid4 from uuid import uuid4
# Configuration # Configuration
API_BASE_URL = "http://localhost:8765" API_BASE_URL = "http://localhost:8765"
API_KEY = "langmem_api_key_2025" API_KEY = "langmem_api_key_2025"
TEST_USER_ID = f"test_user_{uuid4()}"
class TestLangMemAPI: async def test_api_endpoints():
"""Test suite for LangMem API endpoints""" """Test all API endpoints"""
print("🧪 Testing LangMem API Endpoints")
print("=" * 50)
def setup_method(self): headers = {"Authorization": f"Bearer {API_KEY}"}
"""Setup test client"""
self.client = httpx.AsyncClient(base_url=API_BASE_URL)
self.headers = {"Authorization": f"Bearer {API_KEY}"}
self.test_user_id = f"test_user_{uuid4()}"
async def teardown_method(self): async with httpx.AsyncClient() as client:
"""Cleanup test client""" # Test 1: Root endpoint
await self.client.aclose() print("\n1. Testing root endpoint...")
try:
response = await client.get(f"{API_BASE_URL}/")
print(f"✅ Root endpoint: {response.status_code}")
print(f" Response: {response.json()}")
except Exception as e:
print(f"❌ Root endpoint failed: {e}")
@pytest.mark.asyncio # Test 2: Health check
async def test_root_endpoint(self): print("\n2. Testing health check...")
"""Test root endpoint""" try:
response = await self.client.get("/") response = await client.get(f"{API_BASE_URL}/health")
assert response.status_code == 200 print(f"✅ Health check: {response.status_code}")
data = response.json() data = response.json()
assert data["message"] == "LangMem API - Long-term Memory System" print(f" Overall status: {data.get('status')}")
assert data["version"] == "1.0.0" for service, status in data.get('services', {}).items():
print(f" {service}: {status}")
except Exception as e:
print(f"❌ Health check failed: {e}")
@pytest.mark.asyncio # Test 3: Store memory
async def test_health_check(self): print("\n3. Testing memory storage...")
"""Test health check endpoint""" try:
response = await self.client.get("/health")
assert response.status_code == 200
data = response.json()
assert "status" in data
assert "services" in data
assert "timestamp" in data
@pytest.mark.asyncio
async def test_store_memory(self):
"""Test storing a memory"""
memory_data = { memory_data = {
"content": "This is a test memory about Python programming", "content": "FastAPI is a modern web framework for building APIs with Python",
"user_id": self.test_user_id, "user_id": TEST_USER_ID,
"session_id": "test_session_1", "session_id": "test_session_1",
"metadata": { "metadata": {
"category": "programming", "category": "programming",
"language": "python", "language": "python",
"importance": "high" "framework": "fastapi"
} }
} }
response = await self.client.post( response = await client.post(
"/v1/memories/store", f"{API_BASE_URL}/v1/memories/store",
json=memory_data, json=memory_data,
headers=self.headers headers=headers,
timeout=30.0
) )
assert response.status_code == 200 if response.status_code == 200:
data = response.json() data = response.json()
assert data["status"] == "stored" memory_id = data["id"]
assert data["user_id"] == self.test_user_id print(f"✅ Memory stored successfully: {memory_id}")
assert "id" in data print(f" Status: {data['status']}")
assert "created_at" in data else:
print(f"❌ Memory storage failed: {response.status_code}")
print(f" Response: {response.text}")
return
return data["id"] except Exception as e:
print(f"❌ Memory storage failed: {e}")
return
@pytest.mark.asyncio # Test 4: Search memories
async def test_search_memories(self): print("\n4. Testing memory search...")
"""Test searching memories""" try:
# First store a memory
memory_id = await self.test_store_memory()
# Wait a moment for indexing
await asyncio.sleep(1)
# Search for the memory
search_data = { search_data = {
"query": "Python programming", "query": "Python web framework",
"user_id": self.test_user_id, "user_id": TEST_USER_ID,
"limit": 10, "limit": 5,
"threshold": 0.5, "threshold": 0.5
"include_graph": True
} }
response = await self.client.post( response = await client.post(
"/v1/memories/search", f"{API_BASE_URL}/v1/memories/search",
json=search_data, json=search_data,
headers=self.headers headers=headers,
timeout=30.0
) )
assert response.status_code == 200 if response.status_code == 200:
data = response.json() data = response.json()
assert "memories" in data print(f"✅ Memory search successful")
assert "context" in data print(f" Found {data['total_count']} memories")
assert "total_count" in data for memory in data['memories']:
assert data["total_count"] > 0 print(f" - {memory['content'][:50]}... (similarity: {memory['similarity']:.3f})")
else:
print(f"❌ Memory search failed: {response.status_code}")
print(f" Response: {response.text}")
# Check first memory result except Exception as e:
if data["memories"]: print(f"❌ Memory search failed: {e}")
memory = data["memories"][0]
assert "id" in memory
assert "content" in memory
assert "similarity" in memory
assert memory["user_id"] == self.test_user_id
@pytest.mark.asyncio # Test 5: Retrieve memories for conversation
async def test_retrieve_memories(self): print("\n5. Testing memory retrieval...")
"""Test retrieving memories for conversation context""" try:
# Store a memory first
await self.test_store_memory()
# Wait a moment for indexing
await asyncio.sleep(1)
# Retrieve memories based on conversation
retrieve_data = { retrieve_data = {
"messages": [ "messages": [
{"role": "user", "content": "I want to learn about Python"}, {"role": "user", "content": "I want to learn about web development"},
{"role": "assistant", "content": "Python is a great programming language"}, {"role": "assistant", "content": "Great! What technology are you interested in?"},
{"role": "user", "content": "Tell me more about Python programming"} {"role": "user", "content": "I heard Python is good for web APIs"}
], ],
"user_id": self.test_user_id, "user_id": TEST_USER_ID,
"session_id": "test_session_1" "session_id": "test_session_1"
} }
response = await self.client.post( response = await client.post(
"/v1/memories/retrieve", f"{API_BASE_URL}/v1/memories/retrieve",
json=retrieve_data, json=retrieve_data,
headers=self.headers headers=headers,
timeout=30.0
) )
assert response.status_code == 200 if response.status_code == 200:
data = response.json() data = response.json()
assert "memories" in data print(f"✅ Memory retrieval successful")
assert "context" in data print(f" Retrieved {data['total_count']} relevant memories")
assert "total_count" in data for memory in data['memories']:
print(f" - {memory['content'][:50]}... (similarity: {memory['similarity']:.3f})")
else:
print(f"❌ Memory retrieval failed: {response.status_code}")
print(f" Response: {response.text}")
@pytest.mark.asyncio except Exception as e:
async def test_get_user_memories(self): print(f"❌ Memory retrieval failed: {e}")
"""Test getting all memories for a user"""
# Store a memory first
await self.test_store_memory()
response = await self.client.get( # Test 6: Get user memories
f"/v1/memories/users/{self.test_user_id}", print("\n6. Testing user memory listing...")
headers=self.headers try:
response = await client.get(
f"{API_BASE_URL}/v1/memories/users/{TEST_USER_ID}",
headers=headers,
timeout=30.0
) )
assert response.status_code == 200 if response.status_code == 200:
data = response.json() data = response.json()
assert "memories" in data print(f"✅ User memory listing successful")
assert "total_count" in data print(f" User has {data['total_count']} memories")
assert data["total_count"] > 0 for memory in data['memories']:
print(f" - {memory['content'][:50]}... (created: {memory['created_at'][:19]})")
else:
print(f"❌ User memory listing failed: {response.status_code}")
print(f" Response: {response.text}")
# Check memory structure except Exception as e:
if data["memories"]: print(f"❌ User memory listing failed: {e}")
memory = data["memories"][0]
assert "id" in memory
assert "content" in memory
assert "user_id" in memory
assert "created_at" in memory
@pytest.mark.asyncio print("\n" + "=" * 50)
async def test_delete_memory(self): print("🎉 API Testing Complete!")
"""Test deleting a memory"""
# Store a memory first
memory_id = await self.test_store_memory()
# Delete the memory
response = await self.client.delete(
f"/v1/memories/{memory_id}",
headers=self.headers
)
assert response.status_code == 200
data = response.json()
assert data["status"] == "deleted"
assert data["id"] == memory_id
@pytest.mark.asyncio
async def test_authentication_required(self):
"""Test that authentication is required"""
response = await self.client.get("/v1/memories/users/test_user")
assert response.status_code == 401
@pytest.mark.asyncio
async def test_invalid_api_key(self):
"""Test invalid API key"""
headers = {"Authorization": "Bearer invalid_key"}
response = await self.client.get("/v1/memories/users/test_user", headers=headers)
assert response.status_code == 401
if __name__ == "__main__": if __name__ == "__main__":
pytest.main([__file__, "-v"]) asyncio.run(test_api_endpoints())