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