llm_council/scripts/test_setup.py
Irina Levit 3546c04348
Some checks failed
CI / backend-test (push) Successful in 4m9s
CI / frontend-test (push) Failing after 3m48s
CI / lint-python (push) Successful in 1m41s
CI / secret-scanning (push) Successful in 1m20s
CI / dependency-scan (push) Successful in 10m50s
CI / workflow-summary (push) Successful in 1m11s
feat: Major UI/UX improvements and production readiness
## Features Added

### Document Reference System
- Implemented numbered document references (@1, @2, etc.) with autocomplete dropdown
- Added fuzzy filename matching for @filename references
- Document filtering now prioritizes numeric refs > filename refs > all documents
- Autocomplete dropdown appears when typing @ with keyboard navigation (Up/Down, Enter/Tab, Escape)
- Document numbers displayed in UI for easy reference

### Conversation Management
- Added conversation rename functionality with inline editing
- Implemented conversation search (by title and content)
- Search box always visible, even when no conversations exist
- Export reports now replace @N references with actual filenames

### UI/UX Improvements
- Removed debug toggle button
- Improved text contrast in dark mode (better visibility)
- Made input textarea expand to full available width
- Fixed file text color for better readability
- Enhanced document display with numbered badges

### Configuration & Timeouts
- Made HTTP client timeouts configurable (connect, write, pool)
- Added .env.example with all configuration options
- Updated timeout documentation

### Developer Experience
- Added `make test-setup` target for automated test conversation creation
- Test setup script supports TEST_MESSAGE and TEST_DOCS env vars
- Improved Makefile with dev and test-setup targets

### Documentation
- Updated ARCHITECTURE.md with all new features
- Created comprehensive deployment documentation
- Added GPU VM setup guides
- Removed unnecessary markdown files (CLAUDE.md, CONTRIBUTING.md, header.jpg)
- Organized documentation in docs/ directory

### GPU VM / Ollama (Stability + GPU Offload)
- Updated GPU VM docs to reflect the working systemd environment for remote Ollama
- Standardized remote Ollama port to 11434 (and added /v1/models verification)
- Documented required env for GPU offload on this VM:
  - `OLLAMA_MODELS=/mnt/data/ollama`, `HOME=/mnt/data/ollama/home`
  - `OLLAMA_LLM_LIBRARY=cuda_v12` (not `cuda`)
  - `LD_LIBRARY_PATH=/usr/local/lib/ollama:/usr/local/lib/ollama/cuda_v12`

## Technical Changes

### Backend
- Enhanced `docs_context.py` with reference parsing (numeric and filename)
- Added `update_conversation_title` to storage.py
- New endpoints: PATCH /api/conversations/{id}/title, GET /api/conversations/search
- Improved report generation with filename substitution

### Frontend
- Removed debugMode state and related code
- Added autocomplete dropdown component
- Implemented search functionality in Sidebar
- Enhanced ChatInterface with autocomplete and improved textarea sizing
- Updated CSS for better contrast and responsive design

## Files Changed
- Backend: config.py, council.py, docs_context.py, main.py, storage.py
- Frontend: App.jsx, ChatInterface.jsx, Sidebar.jsx, and related CSS files
- Documentation: README.md, ARCHITECTURE.md, new docs/ directory
- Configuration: .env.example, Makefile
- Scripts: scripts/test_setup.py

## Breaking Changes
None - all changes are backward compatible

## Testing
- All existing tests pass
- New test-setup script validates conversation creation workflow
- Manual testing of autocomplete, search, and rename features
2025-12-28 18:15:02 -05:00

141 lines
5.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""Setup a test conversation with optional message and documents."""
import os
import sys
import httpx
import time
from datetime import datetime
from pathlib import Path
from dotenv import load_dotenv
from urllib.parse import quote
# Load .env file if it exists
load_dotenv()
API_BASE = "http://localhost:8001"
def create_test_conversation():
"""Create a new conversation with today's date/time as title."""
now = datetime.now()
title = now.strftime("%Y-%m-%d %H:%M:%S")
with httpx.Client(timeout=10.0) as client:
# Create conversation
response = client.post(f"{API_BASE}/api/conversations", json={})
if response.status_code != 200:
print(f"Error creating conversation: {response.text}", file=sys.stderr)
sys.exit(1)
conv = response.json()
conv_id = conv["id"]
# Update title
response = client.patch(
f"{API_BASE}/api/conversations/{conv_id}/title",
json={"title": title}
)
if response.status_code != 200:
print(f"Warning: Could not set title: {response.text}", file=sys.stderr)
print(f"Created conversation: {conv_id}")
print(f"Title: {title}")
return conv_id
def upload_document(conv_id, filepath):
"""Upload a document to a conversation."""
path = Path(filepath)
if not path.exists():
print(f"Warning: File not found: {filepath}", file=sys.stderr)
return False
with httpx.Client(timeout=30.0) as client:
with open(path, 'rb') as f:
files = {'file': (path.name, f.read(), 'text/markdown')}
response = client.post(
f"{API_BASE}/api/conversations/{conv_id}/documents",
files=files
)
if response.status_code != 200:
print(f"Warning: Could not upload {filepath}: {response.text}", file=sys.stderr)
return False
print(f"Uploaded: {path.name}")
return True
def send_message(conv_id, message):
"""Send a message to a conversation."""
# Use a very long timeout since the council process can take several minutes
# Default to 10 minutes, but allow override via env var
timeout_seconds = float(os.getenv("TEST_MESSAGE_TIMEOUT_SECONDS", "600.0"))
print(f"Sending message (this may take several minutes, timeout: {timeout_seconds}s)...")
with httpx.Client(timeout=timeout_seconds) as client:
try:
response = client.post(
f"{API_BASE}/api/conversations/{conv_id}/message",
json={"content": message}
)
if response.status_code != 200:
print(f"Warning: Could not send message: {response.text}", file=sys.stderr)
return False
print(f"✓ Message sent and processed: {message[:50]}...")
return True
except httpx.ReadTimeout:
print(f"Error: Request timed out after {timeout_seconds}s", file=sys.stderr)
print("The council process is still running. You can check the conversation in the UI.", file=sys.stderr)
print(f"Conversation ID: {conv_id}", file=sys.stderr)
return False
except httpx.RequestError as e:
print(f"Error sending message: {e}", file=sys.stderr)
return False
def main():
# Wait for backend to be ready
max_retries = 10
for i in range(max_retries):
try:
with httpx.Client(timeout=1.0) as client:
response = client.get(f"{API_BASE}/")
if response.status_code == 200:
break
except httpx.RequestError:
if i < max_retries - 1:
time.sleep(1)
else:
print(f"Error: Backend not available at {API_BASE}", file=sys.stderr)
print("Make sure the backend is running: uv run python -m backend.main", file=sys.stderr)
sys.exit(1)
# Create conversation
conv_id = create_test_conversation()
# Upload documents if specified
test_docs = os.getenv("TEST_DOCS", "")
if test_docs:
doc_paths = [p.strip() for p in test_docs.split(",") if p.strip()]
for doc_path in doc_paths:
upload_document(conv_id, doc_path)
# Note: TEST_MESSAGE is NOT sent automatically
# It's provided here for reference - user should type it in the UI
test_message = os.getenv("TEST_MESSAGE", "")
open_url = f"http://localhost:5173/?conversation={conv_id}"
if test_message:
open_url += f"&message={quote(test_message)}"
print(f"\n✓ Conversation created: {conv_id}")
print(f"CONVERSATION_ID={conv_id}") # For Makefile to parse
print(f"OPEN_URL={open_url}") # For Makefile to parse
print(f"Open in browser: {open_url}")
if test_message:
print(f"\n💡 Pre-filled message (copy/paste into input):")
print(f" {test_message}")
return conv_id
if __name__ == "__main__":
main()