feat: add database mode for LLM message storage and analytics

Implements a database mode that stores LLM prompts and responses in PostgreSQL
instead of making API calls. This enables:
- Testing without consuming API credits
- Collecting analytics on usage patterns
- Development and debugging workflows

Changes:
- Added DatabaseClient class for PostgreSQL operations
- Modified LLMAnalyzer to support database/API mode toggle
- Added USE_DATABASE config flag to switch between modes
- Included Docker Compose setup for PostgreSQL
- Added utility scripts for database init and analytics viewing
- Comprehensive documentation in DATABASE_MODE.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-03-10 21:13:13 -04:00
parent 11a4aba46f
commit 44456cb073
11 changed files with 952 additions and 4 deletions
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""View stored messages from the database.
This script displays stored LLM messages with filtering options.
Usage:
python scripts/view_messages.py [--company COMPANY] [--type TYPE] [--limit LIMIT]
"""
import sys
import os
import argparse
from datetime import datetime
# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from SPARC import config
from SPARC.database import DatabaseClient
def main():
"""Display messages from the database."""
parser = argparse.ArgumentParser(description="View stored SPARC messages")
parser.add_argument(
"--company",
type=str,
help="Filter by company name"
)
parser.add_argument(
"--type",
type=str,
choices=["single_patent", "portfolio"],
help="Filter by analysis type"
)
parser.add_argument(
"--limit",
type=int,
default=10,
help="Maximum number of messages to display (default: 10)"
)
args = parser.parse_args()
print("SPARC Stored Messages")
print("=" * 70)
try:
db_client = DatabaseClient(config.database_url)
messages = db_client.get_messages(
company_name=args.company,
analysis_type=args.type,
limit=args.limit
)
if not messages:
print("\nNo messages found.")
return
print(f"\nShowing {len(messages)} message(s):\n")
for i, msg in enumerate(messages, 1):
print(f"Message #{msg['id']} - {msg['timestamp']}")
print(f"Company: {msg['company_name'] or '(unknown)'}")
print(f"Type: {msg['analysis_type'] or '(unknown)'}")
print(f"Model: {msg['model'] or '(unknown)'}")
print(f"\nPrompt (first 200 chars):")
print(f" {msg['prompt'][:200]}...")
print(f"\nResponse (first 200 chars):")
print(f" {msg['response'][:200] if msg['response'] else '(no response)'}...")
print("\n" + "-" * 70 + "\n")
except Exception as e:
print(f"Error retrieving messages: {e}")
sys.exit(1)
if __name__ == "__main__":
main()