chore: import upstream snapshot with attribution
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:08:55 +08:00
commit 7a0da7932b
2985 changed files with 1049377 additions and 0 deletions
+202
View File
@@ -0,0 +1,202 @@
# HTTP API Examples
This directory contains working examples for using the LDR HTTP API with authentication.
## 🚀 Quick Start
### 1. Start the LDR Server
```bash
# Option 1: Direct startup
python -m local_deep_research.web.app
# Option 2: Use the restart script (recommended)
bash scripts/dev/restart_server.sh
# Option 3: Docker compose
docker-compose up -d
```
### 2. Run the Simple Working Example
```bash
# This example works completely out of the box!
python simple_working_example.py
```
## 📁 Available Examples
### 🎯 `simple_working_example.py` - **RECOMMENDED START**
-**Works completely out of the box**
-**Automatic user creation** (no manual setup needed)
-**Correct API endpoints** and authentication
-**Tested and verified** to work
- ⏱️ **Runtime:** 2-10 minutes (research processing time)
**Perfect for:** First-time users, testing if API works, quick demos
## 📚 Advanced Examples (`advanced/` folder)
More comprehensive examples for learning and advanced use cases:
### 📚 `advanced/simple_http_example.py` - **COMPREHENSIVE GUIDE**
-**Automatic user creation**
- 📊 **Multiple API examples** (research, settings, history)
- 🔍 **Progress monitoring** with status updates
- ⏱️ **Runtime:** 3-15 minutes (more comprehensive testing)
**Perfect for:** Learning different API endpoints, understanding the full API surface
### 🚀 `advanced/http_api_examples.py` - **ADVANCED CLIENT**
- 🔧 **Reusable client class** for integration
- 📈 **Advanced features** (batch processing, polling)
- 🎛️ **Comprehensive patterns** for production use
- ⏱️ **Runtime:** 5-30 minutes (extensive testing)
**Perfect for:** Building applications, production integration, advanced use cases
## ⚙️ Configuration
### Environment Variables
You can configure the LDR service endpoints using environment variables:
```bash
# For local Ollama (default)
export LDR_LLM_OLLAMA_URL=http://localhost:11434
# For remote Ollama server
export LDR_LLM_OLLAMA_URL=http://192.168.178.66:11434
# For Docker compose service names
export LDR_LLM_OLLAMA_URL=http://ollama:11434
# For Docker with host networking
export LDR_LLM_OLLAMA_URL=http://host.docker.internal:11434
```
### Docker Compose
In your `docker-compose.yml`, you can set the Ollama URL:
```yaml
services:
ldr:
environment:
# For service name (recommended for docker-compose)
- LDR_LLM_OLLAMA_URL=http://ollama:11434
# For remote Ollama instance
# - LDR_LLM_OLLAMA_URL=http://192.168.178.66:11434
# For host machine Ollama
# - LDR_LLM_OLLAMA_URL=http://host.docker.internal:11434
```
### Common Network Scenarios
| Scenario | Environment Variable | When to Use |
|----------|---------------------|-------------|
| **Local Ollama** | `http://localhost:11434` | Running Ollama on same machine |
| **Remote Ollama** | `http://IP:11434` | Ollama on different server |
| **Docker Compose** | `http://ollama:11434` | Using docker-compose service names |
| **Docker Host** | `http://host.docker.internal:11434` | Docker container accessing host Ollama |
## 🔍 Monitoring Progress
### Server Logs
```bash
# Monitor real-time progress
tail -f /tmp/ldr_server_5000.log
# Check recent logs
tail -20 /tmp/ldr_server_5000.log
```
### Web Interface
- **Research Results:** http://localhost:5000/results/{research_id}
- **Settings:** http://localhost:5000/settings
- **History:** http://localhost:5000/history
## 🚨 Troubleshooting
### Common Issues
**❌ "Cannot connect to server"**
```bash
# Start the server first
python -m local_deep_research.web.app
# or
bash scripts/dev/restart_server.sh
```
**❌ "Authentication failed"**
- The examples create users automatically, so this shouldn't happen
- If it does, check that the server is running correctly
**❌ "Research failed"**
```bash
# Check server logs for details
tail -f /tmp/ldr_server_5000.log
# Common issues:
# - Ollama not running or wrong URL
# - Model not available in Ollama
# - Network connectivity issues
```
**❌ "No output from script"**
- Scripts may take 2-10 minutes to complete research
- Monitor progress in server logs
- Check if research started successfully
### Model Configuration
Make sure your Ollama has the required models:
```bash
# List available models
ollama list
# Pull a model if needed
ollama pull gemma3:12b
ollama pull llama3
ollama pull mistral
```
## 📚 What Each Example Demonstrates
### simple_working_example.py
- ✅ User creation and authentication
- ✅ Basic research request
- ✅ Proper CSRF token handling
- ✅ Result URL generation
### advanced/simple_http_example.py
- ✅ All of the above PLUS:
- ✅ Settings management
- ✅ Research history
- ✅ Progress polling
- ✅ Multiple research examples
### advanced/http_api_examples.py
- ✅ All of the above PLUS:
- ✅ Batch processing
- ✅ Advanced polling strategies
- ✅ Error handling patterns
- ✅ Production-ready client class
## 🎯 Recommended Usage Path
1. **Start with `simple_working_example.py`** - Verify everything works
2. **Try `advanced/simple_http_example.py`** - Learn the API surface
3. **Use `advanced/http_api_examples.py`** - Build your application
## 🔗 Related Documentation
- [Main API Documentation](../README.md)
- [API Quick Start](../../../docs/api-quickstart.md)
- [Docker Configuration](../../../docker-compose.yml)
- [Troubleshooting Guide](../../../docs/troubleshooting.md)
@@ -0,0 +1,67 @@
# Advanced HTTP API Examples
This folder contains more comprehensive HTTP API examples for learning advanced features and production use cases.
## 📁 Available Examples
### 📚 `simple_http_example.py` - **COMPREHENSIVE GUIDE**
-**Automatic user creation**
- 📊 **Multiple API examples** (research, settings, history)
- 🔍 **Progress monitoring** with status updates
- ⏱️ **Runtime:** 3-15 minutes (more comprehensive testing)
**Perfect for:** Learning different API endpoints, understanding the full API surface
### 🚀 `http_api_examples.py` - **ADVANCED CLIENT**
- 🔧 **Reusable client class** for integration
- 📈 **Advanced features** (batch processing, polling)
- 🎛️ **Comprehensive patterns** for production use
- ⏱️ **Runtime:** 5-30 minutes (extensive testing)
**Perfect for:** Building applications, production integration, advanced use cases
## 🚀 Quick Start
### 1. Run the Comprehensive Example
```bash
cd advanced
python simple_http_example.py
```
### 2. Try the Advanced Client
```bash
cd advanced
python http_api_examples.py
```
## ⚠️ Important Notes
- **Longer Runtime**: These examples take longer than the basic example
- **More Features**: They demonstrate additional API endpoints and patterns
- **Learning Focused**: Designed to help you understand the full API surface
- **Production Ready**: Advanced examples include patterns for production use
## 📚 What These Examples Demonstrate
### simple_http_example.py
- ✅ All basic functionality from the main example
- ✅ Settings management (get/update configuration)
- ✅ Research history access
- ✅ Progress polling and monitoring
- ✅ Multiple research scenarios
- ✅ Error handling patterns
### http_api_examples.py
- ✅ All functionality from simple_http_example.py
- ✅ Reusable client class for application integration
- ✅ Batch processing capabilities
- ✅ Advanced polling strategies
- ✅ Production-ready error handling
- ✅ Comprehensive API coverage
- ✅ Settings management patterns
## 🔗 Related Documentation
- [Main HTTP Examples](../README.md)
- [Basic Working Example](../simple_working_example.py)
- [API Documentation](../../../../README.md)
+499
View File
@@ -0,0 +1,499 @@
#!/usr/bin/env python3
"""
HTTP API Examples for Local Deep Research v1.0+
This script demonstrates comprehensive usage of the LDR HTTP API with authentication.
Includes examples for research, settings management, and batch operations.
Requirements:
- LDR v1.0+ (with authentication features)
- LDR server running: python -m local_deep_research.web.app
- Beautiful Soup: pip install beautifulsoup4
================================================================================
IMPORTANT - LOCALHOST ONLY
================================================================================
This example ONLY works when connecting via localhost:
✅ http://localhost:5000
✅ http://127.0.0.1:5000
It will NOT work when connecting via:
❌ http://192.168.x.x:5000 (local network IP)
❌ http://your-server.com:5000 (remote server)
WHY: For security, session cookies require HTTPS for non-localhost connections.
SOLUTIONS for non-localhost access:
1. Use HTTPS with a reverse proxy (recommended for production)
2. SSH tunnel: ssh -L 5000:localhost:5000 user@server, then use localhost:5000
3. Set TESTING=1 when starting server (INSECURE - development only!)
WARNING: TESTING=1 disables secure cookie protection. Never use in production.
================================================================================
"""
import time
from typing import Any, Dict, List
import requests
from pathlib import Path
import sys
from bs4 import BeautifulSoup
# Add the src directory to Python path for programmatic user creation
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
from local_deep_research.database.encrypted_db import DatabaseManager
from local_deep_research.database.models import User
from local_deep_research.database.auth_db import auth_db_session
# Configuration
BASE_URL = "http://localhost:5000"
def create_test_user():
"""Create a test user programmatically - works out of the box!"""
username = f"testuser_{int(time.time())}"
password = "testpassword123"
print(f"Creating test user: {username}")
try:
# Create user in auth database
with auth_db_session() as session:
new_user = User(username=username)
session.add(new_user)
session.commit()
# Create encrypted database for user
db_manager = DatabaseManager()
db_manager.create_user_database(username, password)
print(f"✅ User created successfully: {username}")
return username, password
except Exception as e:
print(f"❌ Failed to create user: {e}")
return None, None
class LDRClient:
"""Client for interacting with LDR API v1.0+ with authentication"""
def __init__(self, base_url: str = BASE_URL):
self.base_url = base_url
self.session = requests.Session()
self.csrf_token = None
self.username = None
def login(self, username: str, password: str) -> bool:
"""Authenticate with the LDR server."""
try:
# Get login page and CSRF token
login_page = self.session.get(f"{self.base_url}/auth/login")
if login_page.status_code != 200:
return False
soup = BeautifulSoup(login_page.text, "html.parser")
csrf_input = soup.find("input", {"name": "csrf_token"})
login_csrf = csrf_input.get("value")
if not login_csrf:
return False
# Submit login form
login_response = self.session.post(
f"{self.base_url}/auth/login",
data={
"username": username,
"password": password,
"csrf_token": login_csrf,
},
allow_redirects=False,
)
if login_response.status_code not in [200, 302]:
return False
self.username = username
# Get API CSRF token for API calls
csrf_response = self.session.get(f"{self.base_url}/auth/csrf-token")
if csrf_response.status_code == 200:
self.csrf_token = csrf_response.json().get("csrf_token")
return True
except Exception:
return False
def logout(self) -> None:
"""Logout from the server."""
if self.csrf_token:
self.session.post(
f"{self.base_url}/auth/logout",
headers={"X-CSRF-Token": self.csrf_token},
)
def _get_headers(self) -> Dict[str, str]:
"""Get headers with CSRF token."""
return {"X-CSRF-Token": self.csrf_token} if self.csrf_token else {}
def check_health(self) -> Dict[str, Any]:
"""Check API health status."""
response = self.session.get(f"{self.base_url}/auth/check")
return response.json()
def start_research(self, query: str, **kwargs) -> Dict[str, Any]:
"""Start a new research task."""
payload = {
"query": query,
"model": kwargs.get("model"),
"search_engines": kwargs.get("search_engines", ["wikipedia"]),
"iterations": kwargs.get("iterations", 2),
"questions_per_iteration": kwargs.get("questions_per_iteration", 3),
"temperature": kwargs.get("temperature", 0.7),
"local_context": kwargs.get("local_context", 2000),
"web_context": kwargs.get("web_context", 2000),
}
response = self.session.post(
f"{self.base_url}/api/start_research",
json=payload,
headers=self._get_headers(),
)
if response.status_code == 200:
return response.json()
raise Exception(f"Failed to start research: {response.text}")
def get_research_status(self, research_id: str) -> Dict[str, Any]:
"""Get the status of a research task."""
response = self.session.get(
f"{self.base_url}/api/research/{research_id}/status"
)
return response.json()
def get_research_result(self, research_id: str) -> Dict[str, Any]:
"""Get the results of a completed research task."""
response = self.session.get(f"{self.base_url}/api/report/{research_id}")
return response.json()
def wait_for_research(
self, research_id: str, timeout: int = 300
) -> Dict[str, Any]:
"""Wait for research to complete and return results."""
start_time = time.time()
while time.time() - start_time < timeout:
status = self.get_research_status(research_id)
if status.get("status") == "completed":
return self.get_research_result(research_id)
if status.get("status") == "failed":
raise Exception(
f"Research failed: {status.get('error', 'Unknown error')}"
)
print(
f" Status: {status.get('status', 'unknown')} - {status.get('progress', 'N/A')}"
)
time.sleep(3)
raise TimeoutError(
f"Research {research_id} timed out after {timeout} seconds"
)
def get_settings(self) -> Dict[str, Any]:
"""Get all user settings."""
response = self.session.get(f"{self.base_url}/settings/api")
return response.json()
def get_setting(self, key: str) -> Any:
"""Get a specific setting value."""
response = self.session.get(f"{self.base_url}/settings/api/{key}")
if response.status_code == 200:
return response.json()
return None
def update_setting(self, key: str, value: Any) -> bool:
"""Update a setting value."""
response = self.session.put(
f"{self.base_url}/settings/api/{key}",
json={"value": value},
headers=self._get_headers(),
)
return response.status_code in [200, 201]
def get_history(self, limit: int = 10) -> List[Dict[str, Any]]:
"""Get research history."""
response = self.session.get(
f"{self.base_url}/history/api", params={"limit": limit}
)
data = response.json()
return data.get("items", data.get("history", []))
def get_available_models(self) -> Dict[str, str]:
"""Get available LLM providers and models."""
response = self.session.get(
f"{self.base_url}/settings/api/available-models"
)
data = response.json()
return data.get("providers", data.get("models", {}))
def get_available_search_engines(self) -> List[str]:
"""Get available search engines."""
response = self.session.get(
f"{self.base_url}/settings/api/available-search-engines"
)
data = response.json()
return data.get("engines", data.get("engine_options", []))
def example_quick_research(client: LDRClient) -> None:
"""Example: Quick research with minimal parameters."""
print("\n=== Example 1: Quick Research ===")
research = client.start_research(
query="What are the key principles of machine learning?",
iterations=1,
questions_per_iteration=2,
)
print(f"Started research ID: {research['research_id']}")
# Wait for completion
result = client.wait_for_research(research["research_id"])
print(f"\nSummary: {result['summary'][:500]}...")
print(f"Sources: {len(result.get('sources', []))}")
print(f"Findings: {len(result.get('findings', []))}")
def example_detailed_research(client: LDRClient) -> None:
"""Example: Detailed research with multiple search engines."""
print("\n=== Example 2: Detailed Research ===")
# Check available search engines
engines = client.get_available_search_engines()
print(f"Available search engines: {engines}")
# Use multiple engines
selected_engines = (
["wikipedia", "arxiv"] if "arxiv" in engines else ["wikipedia"]
)
research = client.start_research(
query="Impact of climate change on global food security",
search_engines=selected_engines,
iterations=3,
questions_per_iteration=4,
temperature=0.7,
)
print(f"Started detailed research ID: {research['research_id']}")
# Monitor progress
result = client.wait_for_research(research["research_id"], timeout=600)
print(f"\nTitle: {result.get('query', 'N/A')}")
print(f"Summary length: {len(result['summary'])} characters")
print(f"Sources: {len(result.get('sources', []))}")
# Show some findings
findings = result.get("findings", [])
if findings:
print("\nTop findings:")
for i, finding in enumerate(findings[:3], 1):
print(f"{i}. {finding.get('text', 'N/A')[:100]}...")
def example_settings_management(client: LDRClient) -> None:
"""Example: Managing user settings."""
print("\n=== Example 3: Settings Management ===")
# Get current settings
settings = client.get_settings()
settings_data = settings.get("settings", {})
# Display current LLM configuration
llm_provider = settings_data.get("llm.provider", {}).get("value", "Not set")
llm_model = settings_data.get("llm.model", {}).get("value", "Not set")
print(f"Current LLM Provider: {llm_provider}")
print(f"Current LLM Model: {llm_model}")
# Get available models
models = client.get_available_models()
print(f"\nAvailable providers: {list(models.keys())}")
# Example: Update temperature setting
current_temp = settings_data.get("llm.temperature", {}).get("value", 0.7)
print(f"\nCurrent temperature: {current_temp}")
# Update temperature (example - uncomment to actually update)
# success = client.update_setting("llm.temperature", 0.5)
# print(f"Temperature update: {'Success' if success else 'Failed'}")
def example_batch_research(client: LDRClient) -> None:
"""Example: Running multiple research tasks in batch."""
print("\n=== Example 4: Batch Research ===")
queries = [
"What is quantum entanglement?",
"How does CRISPR gene editing work?",
"What are the applications of blockchain technology?",
]
research_ids = []
# Start all research tasks
for query in queries:
try:
research = client.start_research(
query=query, iterations=1, questions_per_iteration=2
)
research_ids.append(
{
"id": research["research_id"],
"query": query,
"status": "started",
}
)
print(f"Started: {query} (ID: {research['research_id']})")
except Exception as e:
print(f"Failed to start '{query}': {e}")
# Wait for all to complete
print("\nWaiting for batch completion...")
completed = 0
while completed < len(research_ids):
for research in research_ids:
if research["status"] != "completed":
try:
status = client.get_research_status(research["id"])
if status.get("status") == "completed":
research["status"] = "completed"
completed += 1
print(f"✓ Completed: {research['query']}")
except Exception:
pass
if completed < len(research_ids):
time.sleep(3)
# Get all results
print("\nBatch Results Summary:")
for research in research_ids:
try:
result = client.get_research_result(research["id"])
print(f"\n{research['query']}:")
print(f" - Summary: {result['summary'][:150]}...")
print(f" - Sources: {len(result.get('sources', []))}")
except Exception as e:
print(f" - Error getting results: {e}")
def example_research_history(client: LDRClient) -> None:
"""Example: Viewing research history."""
print("\n=== Example 5: Research History ===")
history = client.get_history(limit=5)
if not history:
print("No research history found.")
return
print(f"Found {len(history)} recent research items:\n")
for item in history:
created = item.get("created_at", "Unknown date")
query = item.get("query", "Unknown query")
status = item.get("status", "Unknown")
research_id = item.get("id", item.get("research_id", "N/A"))
print(f"ID: {research_id}")
print(f"Query: {query}")
print(f"Date: {created}")
print(f"Status: {status}")
print("-" * 40)
def main():
"""Run all examples."""
print("=== LDR HTTP API v1.0 Examples ===")
print(
"🎯 This example works completely out of the box - no manual setup required!"
)
# First, check if server is running
try:
response = requests.get(f"{BASE_URL}/auth/check", timeout=5)
if response.status_code in [
200,
401,
]: # 401 is expected when not authenticated
print("✅ Server is running")
else:
print(f"❌ Server returned status code: {response.status_code}")
print("Make sure the server is running:")
print(" python -m local_deep_research.web.app")
return
except requests.exceptions.ConnectionError:
print("❌ Cannot connect to LDR server!")
print("Make sure the server is running:")
print(" python -m local_deep_research.web.app")
return
# Create test user automatically
username, password = create_test_user()
if not username:
print("❌ Failed to create test user")
return
# Create client
client = LDRClient(BASE_URL)
try:
# Login with the created user
print(f"\nLogging in as: {username}")
if not client.login(username, password):
print("❌ Login failed!")
return
print("✅ Login successful")
# Check health
health = client.check_health()
print(f"Authenticated: {health.get('authenticated', False)}")
print(f"Username: {health.get('username', 'N/A')}")
# Run examples
example_quick_research(client)
example_detailed_research(client)
example_settings_management(client)
example_batch_research(client)
example_research_history(client)
print("\n✅ All examples completed successfully!")
print(f"🔑 Created user: {username}")
print("📝 You can now use this user for manual testing:")
print(f" Username: {username}")
print(f" Password: {password}")
print(f" Login URL: {BASE_URL}/auth/login")
except requests.exceptions.ConnectionError:
print("\n❌ Cannot connect to LDR server!")
print("Make sure the server is running:")
print(" python -m local_deep_research.web.app")
except Exception as e:
print(f"\n❌ Error: {e}")
finally:
# Always logout
client.logout()
print("\n✅ Logged out")
if __name__ == "__main__":
main()
+440
View File
@@ -0,0 +1,440 @@
#!/usr/bin/env python3
"""
Simple HTTP API Example for Local Deep Research v1.0+
This example shows how to use the LDR API with authentication.
Works completely out of the box with automatic user creation.
================================================================================
IMPORTANT - LOCALHOST ONLY
================================================================================
This example ONLY works when connecting via localhost:
✅ http://localhost:5000
✅ http://127.0.0.1:5000
It will NOT work via http://192.168.x.x:5000 or other non-localhost addresses.
WHY: Session cookies require HTTPS for non-localhost (security).
SOLUTIONS for non-localhost:
1. HTTPS with reverse proxy (production)
2. SSH tunnel: ssh -L 5000:localhost:5000 user@server
3. TESTING=1 env var (INSECURE - dev only!)
WARNING: TESTING=1 disables cookie security. Never use in production.
================================================================================
"""
import requests
import time
import sys
from bs4 import BeautifulSoup
from pathlib import Path
# Add the src directory to Python path for programmatic user creation
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
from local_deep_research.database.encrypted_db import DatabaseManager
from local_deep_research.database.models import User
from local_deep_research.database.auth_db import auth_db_session
# Configuration
API_URL = "http://localhost:5000"
def create_test_user():
"""Create a test user programmatically."""
username = f"testuser_{int(time.time())}"
password = "testpassword123"
print(f"Creating test user: {username}")
try:
# Create user in auth database
with auth_db_session() as session:
new_user = User(username=username)
session.add(new_user)
session.commit()
# Create encrypted database for user
db_manager = DatabaseManager()
db_manager.create_user_database(username, password)
print(f"✅ User created successfully: {username}")
return username, password
except Exception as e:
print(f"❌ Failed to create user: {e}")
return None, None
def main():
print("=== LDR HTTP API Example ===")
print("🎯 This example works completely out of the box!\n")
print("⚠️ IMPORTANT NOTES:")
print(" • This script may take several minutes to complete")
print(" • Research progress can be monitored in the server logs")
print(" • Server logs are available at: /tmp/ldr_server_5000.log")
print(
" • Use 'tail -f /tmp/ldr_server_5000.log' to monitor progress in real-time"
)
print(" • Results will be available at the URL shown when complete\n")
# Check if server is running
try:
response = requests.get(f"{API_URL}/", timeout=5)
if response.status_code != 200:
print("❌ Server is not responding correctly")
print("\n📋 HOW TO START THE SERVER:")
print(" • Option 1: python -m local_deep_research.web.app")
print(
" • Option 2: bash scripts/dev/restart_server.sh (recommended)"
)
print(
" • Note: restart_server.sh stops only the instance on its target port"
)
sys.exit(1)
print("✅ Server is running")
except Exception:
print(
"❌ Cannot connect to server. Please make sure it's running on http://localhost:5000"
)
print("\n📋 HOW TO START THE SERVER:")
print(" • Option 1: python -m local_deep_research.web.app")
print(" • Option 2: bash scripts/dev/restart_server.sh (recommended)")
print(
" • Note: restart_server.sh stops only the instance on its target port"
)
sys.exit(1)
# Create test user automatically
username, password = create_test_user()
if not username:
print("❌ Failed to create test user")
sys.exit(1)
# Create a session to persist cookies
session = requests.Session()
print(f"\nTesting with user: {username}")
# Step 1: Login
print("\n1. Authenticating...")
# Get login page and CSRF token
login_page = session.get(f"{API_URL}/auth/login")
soup = BeautifulSoup(login_page.text, "html.parser")
csrf_input = soup.find("input", {"name": "csrf_token"})
login_csrf = csrf_input.get("value")
if not login_csrf:
print("❌ Could not get CSRF token from login page")
sys.exit(1)
# Login with form data (not JSON)
login_response = session.post(
f"{API_URL}/auth/login",
data={
"username": username,
"password": password,
"csrf_token": login_csrf,
},
allow_redirects=False,
)
if login_response.status_code not in [200, 302]:
print(f"❌ Login failed: {login_response.text}")
print("\nPlease ensure:")
print("- The server is running: python -m local_deep_research.web.app")
sys.exit(1)
print("✅ Login successful")
# Step 2: Get CSRF token
print("\n2. Getting CSRF token...")
csrf_response = session.get(f"{API_URL}/auth/csrf-token")
csrf_token = csrf_response.json()["csrf_token"]
headers = {"X-CSRF-Token": csrf_token}
print("✅ CSRF token obtained")
# Initialize research_id to None
research_id = None
# Example 1: Quick Summary (using the start endpoint)
print("\n=== Example 1: Quick Summary ===")
print(
"📝 This example demonstrates starting a research query and polling for results"
)
print("⏱️ This typically takes 1-3 minutes to complete\n")
research_request = {
"query": "What is machine learning?",
"model": None, # Will use default from settings
"search_engines": ["wikipedia"], # Fast for demo
"iterations": 1,
"questions_per_iteration": 2,
}
# Start research - CORRECT ENDPOINT
print("🚀 Starting research...")
start_response = session.post(
f"{API_URL}/api/start_research", json=research_request, headers=headers
)
if start_response.status_code != 200:
print(f"❌ Failed to start research: {start_response.text}")
sys.exit(1)
research_data = start_response.json()
research_id = research_data["research_id"]
print("✅ Research started successfully!")
print(f"🆔 Research ID: {research_id}")
print(
"📊 Monitor progress in server logs: tail -f /tmp/ldr_server_5000.log"
)
print(f"🌐 Results will be available at: {API_URL}/results/{research_id}\n")
# Poll for results
print("⏳ Waiting for research to complete...")
print(
"⚠️ NOTE: This will poll for up to 3 minutes to ensure research completes"
)
print(
" If it fails, the research may still be running - check the results URL\n"
)
poll_count = 0
max_polls = 18 # Maximum 3 minutes (18 * 10 seconds)
while poll_count < max_polls:
status_response = session.get(
f"{API_URL}/api/research/{research_id}/status"
)
if status_response.status_code == 200:
status = status_response.json()
current_status = status.get("status", "unknown")
progress = status.get("progress", 0)
poll_count += 1
elapsed_time = poll_count * 10 # 10 seconds per poll
print(
f" Check {poll_count} ({elapsed_time}s): Status = {current_status} (Progress: {progress}%)"
)
if current_status == "completed":
print("🎉 Research completed successfully!")
break
if current_status == "failed":
print(
f"❌ Research failed: {status.get('error', 'Unknown error')}"
)
print(
"📋 Check server logs for details: tail -f /tmp/ldr_server_5000.log"
)
sys.exit(1)
elif current_status in ["queued", "in_progress"]:
# Continue polling
pass
else:
print(f"⚠️ Unexpected status: {current_status}")
else:
print(
f"⚠️ Status check failed with code: {status_response.status_code}"
)
time.sleep(10) # Wait 10 seconds between polls
if poll_count >= max_polls:
print("⏰ 3-minute timeout reached - research is still running")
print("💡 This is normal for complex research queries!")
print(f"📊 Check results later at: {API_URL}/results/{research_id}")
print("📋 Monitor progress with: tail -f /tmp/ldr_server_5000.log")
print(
"🔍 The script will still try to fetch results (may be incomplete)"
)
# Get results
results_response = session.get(f"{API_URL}/api/report/{research_id}")
if results_response.status_code == 200:
results = results_response.json()
print(f"\n📝 Summary: {results['summary'][:300]}...")
print(f"📚 Sources: {len(results.get('sources', []))} found")
print(f"🔍 Findings: {len(results.get('findings', []))} findings")
# Example 2: Check Settings
print("\n=== Example 2: Current Settings ===")
settings_response = session.get(f"{API_URL}/settings/api")
if settings_response.status_code == 200:
settings = settings_response.json()["settings"]
# Show some key settings
llm_provider = settings.get("llm.provider", {}).get("value", "Not set")
llm_model = settings.get("llm.model", {}).get("value", "Not set")
print(f"LLM Provider: {llm_provider}")
print(f"LLM Model: {llm_model}")
# Example 3: Get Research History
print("\n=== Example 3: Research History ===")
history_response = session.get(f"{API_URL}/history/api")
if history_response.status_code == 200:
history = history_response.json()
items = history.get("items", history.get("history", []))
print(f"Found {len(items)} research items")
for item in items[:3]: # Show first 3
print(
f"- {item.get('query', 'Unknown query')} ({item.get('created_at', 'Unknown date')})"
)
# Example 4: Get and Display Research Results (with retry logic)
print("\n=== Example 4: Research Results ===")
if research_id:
print(f"📄 Fetching research results for ID: {research_id}")
print(
"🔄 Will retry until results are available (up to 2 additional minutes)\n"
)
# Retry fetching results until available
results_retries = 0
max_results_retries = 12 # 2 minutes (12 * 10 seconds)
while results_retries < max_results_retries:
results_response = session.get(
f"{API_URL}/api/report/{research_id}"
)
if results_response.status_code == 200:
# Results are available, parse and display them
results = results_response.json()
content = results.get("content", "")
sources = results.get("sources", [])
findings = results.get("findings", [])
print(
f"✅ Results retrieved successfully after {(results_retries + 1) * 10} seconds!"
)
print("\n📝 RESEARCH SUMMARY:")
print("=" * 50)
if content:
# Show first 500 characters of the summary
summary_preview = (
content[:500] + "..." if len(content) > 500 else content
)
print(summary_preview)
else:
print("No summary content available")
print(f"\n📚 SOURCES FOUND: {len(sources)}")
for i, source in enumerate(
sources[:3], 1
): # Show first 3 sources
title = source.get("title", "Unknown Title")
url = source.get("url", "No URL")
print(f" {i}. {title}")
print(f" {url}")
if len(sources) > 3:
print(f" ... and {len(sources) - 3} more sources")
print(f"\n🔍 KEY FINDINGS: {len(findings)}")
for i, finding in enumerate(
findings[:3], 1
): # Show first 3 findings
finding_text = finding.get("text", "No finding text")
finding_preview = (
finding_text[:150] + "..."
if len(finding_text) > 150
else finding_text
)
print(f" {i}. {finding_preview}")
if len(findings) > 3:
print(f" ... and {len(findings) - 3} more findings")
print(
f"\n🌐 View full results at: {API_URL}/results/{research_id}"
)
print("=" * 50)
print("🎉 Results displayed successfully!")
break # Exit retry loop - success!
if results_response.status_code == 404:
results_retries += 1
elapsed_time = results_retries * 10
print(
f" Retry {results_retries}/{max_results_retries} ({elapsed_time}s): Results not ready yet, waiting..."
)
time.sleep(10) # Wait 10 seconds before retrying
else:
print(
f"❌ Failed to fetch results: {results_response.status_code}"
)
print(f"Response: {results_response.text[:200]}")
break # Exit retry loop - error
# Handle case where max retries reached
if results_retries >= max_results_retries:
print(
f"\n⏰ Maximum retry time reached ({max_results_retries * 10} seconds)"
)
print("💡 This is normal for complex research queries!")
print(f"📊 Check results later at: {API_URL}/results/{research_id}")
print("📋 Monitor progress with: tail -f /tmp/ldr_server_5000.log")
print(
"🔍 The research is still running - results will be available when complete"
)
else:
print(
"⚠️ No research ID available - research may not have started properly"
)
# Logout
print("\n5. Logging out...")
session.post(f"{API_URL}/auth/logout", headers=headers)
print("✅ Logged out successfully")
if __name__ == "__main__":
print("🎯 Simple LDR HTTP API Example - Works out of the box!")
print("⚡ This script creates a user automatically and tests the API")
print(
"⏱️ Total runtime: Up to 3 minutes polling + 2 minutes results retry + research time"
)
print(
"🔄 Automatically retries fetching results until available (up to 2 minutes)\n"
)
print("📋 REQUIREMENTS:")
print(" • LDR server running")
print(" • Beautiful Soup: pip install beautifulsoup4\n")
print("🚀 START THE SERVER:")
print(" • Option 1: python -m local_deep_research.web.app")
print(" • Option 2: bash scripts/dev/restart_server.sh (recommended)")
print(
" • Note: restart_server.sh stops only the instance on its target port\n"
)
print("📊 MONITORING:")
print(" • Server logs: tail -f /tmp/ldr_server_5000.log")
print(" • This script polls for up to 3 minutes")
print(" • If research takes longer, script shows where to check results\n")
print("⏰ TIMING INFO:")
print(" • Script polls for 3 minutes to let research complete")
print(" • Then retries fetching results for up to 2 additional minutes")
print(" • Research typically completes in 2-10 minutes")
print(" • Script displays results automatically when available")
print(
" • If timeout reached, results URL provided for checking completion\n"
)
main()
@@ -0,0 +1,264 @@
#!/usr/bin/env python3
"""
Simple Working HTTP API Example for Local Deep Research v1.0+
This is a clean, working example that demonstrates the correct way to use the LDR API.
It creates a user automatically and handles authentication properly.
Requirements:
- LDR v1.0+ server running: python -m local_deep_research.web.app
- Beautiful Soup: pip install beautifulsoup4
================================================================================
IMPORTANT - LOCALHOST ONLY
================================================================================
This example ONLY works when connecting via localhost:
✅ http://localhost:5000
✅ http://127.0.0.1:5000
It will NOT work when connecting via:
❌ http://192.168.x.x:5000 (local network IP)
❌ http://your-server.com:5000 (remote server)
❌ http://0.0.0.0:5000 (even from the same machine)
WHY: For security, session cookies require HTTPS for non-localhost connections.
This prevents session hijacking on untrusted networks.
SOLUTIONS for non-localhost access:
1. Use HTTPS with a reverse proxy (recommended for production)
2. SSH tunnel: ssh -L 5000:localhost:5000 user@server, then use localhost:5000
3. Set TESTING=1 when starting server (INSECURE - development only!)
Example: TESTING=1 python -m local_deep_research.web.app
WARNING: TESTING=1 disables secure cookie protection. Session cookies can be
intercepted by network attackers. Never use in production or on public networks.
================================================================================
"""
import requests
from bs4 import BeautifulSoup
import sys
import time
from pathlib import Path
# Add the src directory to Python path for programmatic user creation
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
from local_deep_research.database.encrypted_db import DatabaseManager
from local_deep_research.database.models import User
from local_deep_research.database.auth_db import auth_db_session
def create_test_user():
"""Create a test user programmatically - works out of the box!"""
username = f"testuser_{int(time.time())}"
password = "testpassword123"
print(f"Creating test user: {username}")
try:
# Create user in auth database
with auth_db_session() as session:
new_user = User(username=username)
session.add(new_user)
session.commit()
# Create encrypted database for user
db_manager = DatabaseManager()
db_manager.create_user_database(username, password)
print(f"✅ User created successfully: {username}")
return username, password
except Exception as e:
print(f"❌ Failed to create user: {e}")
return None, None
def test_api_with_user(username, password):
"""Test the API with the created user."""
print(f"\n=== Testing API with user: {username} ===")
base_url = "http://localhost:5000"
session = requests.Session()
# 1. Test login
print("1. Testing login...")
try:
login_page = session.get(f"{base_url}/auth/login")
if login_page.status_code != 200:
print(f" ❌ Failed to get login page: {login_page.status_code}")
return False
soup = BeautifulSoup(login_page.text, "html.parser")
csrf_input = soup.find("input", {"name": "csrf_token"})
login_csrf = csrf_input.get("value")
if not login_csrf:
print(" ❌ No CSRF token found")
return False
login_response = session.post(
f"{base_url}/auth/login",
data={
"username": username,
"password": password,
"csrf_token": login_csrf,
},
allow_redirects=False,
)
print(f" Login status: {login_response.status_code}")
if login_response.status_code not in [200, 302]:
print(" ❌ Login failed")
return False
print(" ✅ Login successful")
except Exception as e:
print(f" ❌ Login error: {e}")
return False
# 2. Get CSRF token for API
print("\n2. Getting API CSRF token...")
try:
csrf_response = session.get(f"{base_url}/auth/csrf-token")
if csrf_response.status_code != 200:
print(
f" ❌ Failed to get CSRF token: {csrf_response.status_code}"
)
return False
csrf_data = csrf_response.json()
csrf_token = csrf_data.get("csrf_token")
if not csrf_token:
print(" ❌ No CSRF token in response")
return False
print(f" ✅ API CSRF token: {csrf_token[:20]}...")
except Exception as e:
print(f" ❌ CSRF token error: {e}")
return False
# 3. Test research API
print("\n3. Testing research API...")
research_request = {
"query": "What is machine learning?",
"model": "gpt-4o-mini",
"search_engines": ["searxng"],
}
headers = {"X-CSRF-Token": csrf_token, "Content-Type": "application/json"}
# Test the correct endpoint
print("\n 3.1 Testing /api/start_research...")
try:
url = f"{base_url}/api/start_research"
response = session.post(url, json=research_request, headers=headers)
print(f" Status: {response.status_code}")
print(f" Response: {response.text[:300]}")
if response.status_code == 200:
try:
data = response.json()
if data.get("status") == "success":
print(" ✅ Research started successfully!")
research_id = data.get("research_id")
if research_id:
print(f" Research ID: {research_id}")
print("\n🎉 SUCCESS! API is working correctly.")
print(
f"📊 View results at: {base_url}/results/{research_id}"
)
return True
elif data.get("status") == "queued":
print(" ✅ Research queued successfully!")
return True
else:
print(
f" ⚠️ Research returned: {data.get('status', 'unknown')}"
)
except Exception:
print(" ⚠️ Response is not valid JSON")
elif response.status_code == 401:
print(" ❌ Authentication failed")
elif response.status_code == 403:
print(" ❌ Forbidden - CSRF token issue")
elif response.status_code == 404:
print(" ❌ Endpoint not found")
elif response.status_code == 500:
print(" ❌ Server error")
print(" Check server logs: tail -f /tmp/ldr_server.log")
else:
print(" ⚠️ Unexpected status code")
except Exception as e:
print(f" ❌ Error testing endpoint: {e}")
return False
def main():
"""Main function that works completely out of the box!"""
print("=== Simple LDR API Working Example ===")
print(
"🎯 This example works completely out of the box - no manual setup required!\n"
)
# Check if server is running
try:
response = requests.get("http://localhost:5000/", timeout=5)
if response.status_code != 200:
print("❌ Server is not responding correctly")
print("\nPlease start the server:")
print(" python -m local_deep_research.web.app")
sys.exit(1)
print("✅ Server is running")
except Exception:
print(
"❌ Cannot connect to server. Please make sure it's running on http://localhost:5000"
)
print("\nStart the server with:")
print(" python -m local_deep_research.web.app")
sys.exit(1)
# Create test user automatically
username, password = create_test_user()
if not username:
print("❌ Failed to create test user")
sys.exit(1)
# Test API
success = test_api_with_user(username, password)
if success:
print("\n✅ API test completed successfully")
print(f"\n🔑 Created user: {username}")
print("📝 You can now use this user for manual testing:")
print(f" Username: {username}")
print(f" Password: {password}")
print(" Login URL: http://localhost:5000/auth/login")
print("\nNext steps:")
print("- Try different research queries")
print("- Explore other API endpoints")
print("- Check out the web interface at http://localhost:5000")
print("- Use the credentials above to log in manually")
sys.exit(0)
else:
print("\n❌ API test failed")
print("\nTroubleshooting:")
print(
"- Make sure the server is running: python -m local_deep_research.web.app"
)
print("- Check server logs for errors: tail -f /tmp/ldr_server.log")
print("- Ensure all dependencies are installed")
sys.exit(1)
if __name__ == "__main__":
main()