#!/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()