diff --git a/.env.docker b/.env.docker new file mode 100644 index 0000000..c63b55c --- /dev/null +++ b/.env.docker @@ -0,0 +1,35 @@ +# Docker Compose Environment Variables +# Generated by Discord Fishbowl setup script + +# Database +DB_PASSWORD=fishbowl_password + +# Redis +REDIS_PASSWORD=redis_password + +# Discord Bot +DISCORD_BOT_TOKEN=MTM5MDkxODI2MDc5NDU5MzM0NQ.GVlKpo.TrF51dlBv-3uJcscrK9xzs0CLqvakKePCCU350 +DISCORD_GUILD_ID=110670463348260864 +DISCORD_CHANNEL_ID=312806692717068288 + +# LLM Configuration +LLM_BASE_URL=http://localhost:5005/v1 +LLM_MODEL=koboldcpp/Broken-Tutu-24B-Transgression-v2.0.i1-Q4_K_M + +# Admin Interface +ADMIN_PORT=8294 +SECRET_KEY=your-secret-key-here +ADMIN_USERNAME=admin +ADMIN_PASSWORD=FIre!@34 + +# Optional PgAdmin credentials (if using --profile admin) +PGADMIN_PASSWORD=admin123 + +# Vector Database +VECTOR_DB_TYPE=qdrant +QDRANT_HOST=qdrant +QDRANT_PORT=6333 +QDRANT_COLLECTION=fishbowl_memories + +# Logging +LOG_LEVEL=INFO diff --git a/src/mcp/__init__.py b/11.4.2 similarity index 100% rename from src/mcp/__init__.py rename to 11.4.2 diff --git a/= b/= new file mode 100644 index 0000000..e69de29 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e6f62c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,163 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development Commands + +### Running the Application +```bash +# Main application (requires LLM service) +python -m src.main + +# Or using startup script +./start.sh + +# Admin interface +python -m src.admin.app +# Or using startup script +./start-admin.sh +``` + +### Testing and Validation +```bash +# Basic system structure test +python test_config.py + +# Simple component test +python simple_test.py + +# Creative collaboration demo +python scripts/demo_creative_integration.py + +# Memory sharing demo +python scripts/demo_memory_sharing.py +``` + +### Database Management +```bash +# Initialize database and tables +python -c " +import asyncio +from src.database.connection import init_database, create_tables +asyncio.run(init_database()) +asyncio.run(create_tables()) +" + +# Apply migrations +alembic upgrade head +``` + +### Frontend Development +```bash +# Navigate to admin frontend +cd admin-frontend + +# Install dependencies +npm install + +# Development server +npm start + +# Build for production +npm run build +``` + +### Docker Services +```bash +# Start core services only (PostgreSQL, Redis, ChromaDB) +docker-compose -f docker-compose.services.yml up -d + +# Or start complete application stack +./docker-start.sh +# Or manually: +docker-compose --env-file .env.docker up -d --build + +# Initialize services script (services only) +./docker-services.sh +``` + +## Architecture Overview + +This is an autonomous Discord bot ecosystem where AI characters chat with each other without human intervention. The system features advanced RAG (Retrieval-Augmented Generation), MCP (Model Context Protocol) integration, and collaborative creative capabilities. + +### Core Components + +**Main Application (`src/main.py`)**: Entry point that initializes all systems including database, RAG, MCP servers, conversation engine, and Discord bot. + +**Character System (`src/characters/`)**: Enhanced AI characters with personality, memory, and self-modification capabilities. Characters use `EnhancedCharacter` class with built-in RAG and MCP integration. + +**Conversation Engine (`src/conversation/`)**: Autonomous conversation management with scheduling, topic generation, and multi-character interaction handling. + +**RAG Systems (`src/rag/`)**: Multi-layer vector database integration using ChromaDB for: +- Personal memories per character +- Community knowledge sharing +- Creative project collaboration +- Cross-character memory sharing with trust-based permissions + +**MCP Integration (`src/mcp/`)**: Model Context Protocol servers providing autonomous tools: +- Self-modification (personality changes) +- File system access (digital spaces) +- Calendar/time awareness +- Memory sharing coordination +- Creative project management + +**Database (`src/database/`)**: SQLAlchemy-based models with PostgreSQL/SQLite support, including tables for conversations, characters, creative projects, shared memories, and trust relationships. + +**Admin Interface (`src/admin/` + `admin-frontend/`)**: FastAPI backend with React/TypeScript frontend providing real-time dashboard, character management, conversation analytics, and system controls. + +### Key Architectural Patterns + +**Trust-Based System**: Characters evaluate relationships before sharing memories or collaborating, with trust levels (Basic 30%, Personal 50%, Intimate 70%, Full 90%). + +**Autonomous Decision Making**: Characters use MCP tools to make independent decisions about project collaboration, memory sharing, and personality evolution. + +**Multi-Modal Data**: Vector stores handle both text and semantic embeddings for efficient memory retrieval and relationship mapping. + +## Configuration + +### Primary Config (`config/fishbowl_config.json`) +- Discord bot settings (token, guild, channel) +- Database connection (PostgreSQL/SQLite) +- AI provider settings (custom LLM endpoint) +- System parameters (conversation frequency, response delays) +- Admin interface settings + +### Character Config (`config/characters.yaml`) +- Character personalities, interests, speaking styles +- Background stories and trait definitions + +### Environment Variables (`.env`) +- Sensitive credentials (Discord tokens, database passwords) +- Docker service settings +- LLM service configuration + +## Development Notes + +### Dependencies +- Python 3.10+ (3.13 compatible) +- PostgreSQL 12+ or SQLite for development +- Redis for caching +- ChromaDB for vector storage +- Discord.py for bot integration +- FastAPI/React for admin interface + +### Testing Strategy +- `test_config.py`: System structure validation +- `simple_test.py`: Component functionality +- Demo scripts: Feature-specific testing +- Admin interface: Real-time monitoring + +### Important Files +- `src/main.py`: Application entry point +- `src/conversation/engine.py`: Core conversation logic +- `src/characters/enhanced_character.py`: Character implementation +- `src/rag/vector_store.py`: Vector database management +- `src/database/models.py`: Database schema definitions + +### Common Issues +- **LLM Service**: Requires Ollama or compatible API endpoint +- **Database**: Ensure PostgreSQL/SQLite is accessible +- **Discord**: Valid bot token required for Discord integration +- **Vector Store**: ChromaDB initialization may require sufficient memory + +The system is designed for autonomous operation - characters will independently propose projects, share memories, and evolve their personalities based on interactions and experiences. \ No newline at end of file diff --git a/DOCKER.md b/DOCKER.md index 46d2168..8f28ecc 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -24,7 +24,7 @@ python install.py | Service | Port | Purpose | Admin URL | |---------|------|---------|-----------| -| PostgreSQL | 5432 | Main database | - | +| PostgreSQL | 15432 | Main database | - | | Redis | 6379 | Caching & pub/sub | - | | ChromaDB | 8000 | Vector embeddings | http://localhost:8000 | | PgAdmin | 8080 | Database admin | http://localhost:8080 | @@ -73,7 +73,7 @@ docker compose -f docker-compose.services.yml logs -f ## š Default Credentials ### PostgreSQL -- **Host**: localhost:5432 +- **Host**: localhost:15432 - **Database**: discord_fishbowl - **Username**: postgres - **Password**: fishbowl_password (configurable) @@ -122,7 +122,7 @@ When using Docker services, update your Discord Fishbowl configuration: "database": { "type": "postgresql", "host": "localhost", - "port": 5432, + "port": 15432, "name": "discord_fishbowl", "username": "postgres", "password": "fishbowl_password" @@ -145,7 +145,7 @@ When using Docker services, update your Discord Fishbowl configuration: ### Services Won't Start 1. Check if Docker is running: `docker info` -2. Check port conflicts: `lsof -i :5432` (PostgreSQL), `lsof -i :6379` (Redis) +2. Check port conflicts: `lsof -i :15432` (PostgreSQL), `lsof -i :6379` (Redis) 3. Check logs: `./docker-services.sh logs` ### Permission Errors diff --git a/Dockerfile b/Dockerfile index c433e0c..c55b9ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,9 +11,14 @@ RUN apt-get update && apt-get install -y \ COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt +# Install additional dependencies needed for production +RUN pip install --no-cache-dir asyncpg python-dotenv + # Copy application code COPY src/ ./src/ COPY config/ ./config/ +COPY migrations/ ./migrations/ +COPY alembic.ini ./ # Create logs directory RUN mkdir -p logs diff --git a/Dockerfile.admin b/Dockerfile.admin new file mode 100644 index 0000000..bdee52c --- /dev/null +++ b/Dockerfile.admin @@ -0,0 +1,56 @@ +FROM python:3.11-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Install Node.js for frontend build +RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ + && apt-get install -y nodejs + +# Copy requirements first for better caching +COPY requirements-admin.txt . +RUN pip install --no-cache-dir -r requirements-admin.txt + +# Install additional dependencies needed for production +RUN pip install --no-cache-dir asyncpg python-dotenv + +# Copy application code +COPY src/ ./src/ +COPY config/ ./config/ +COPY migrations/ ./migrations/ +COPY alembic.ini ./ + +# Build frontend +COPY admin-frontend/ ./admin-frontend/ +WORKDIR /app/admin-frontend + +# Clear any existing node_modules and lock files +RUN rm -rf node_modules package-lock.json yarn.lock + +# Install dependencies with npm (using .npmrc config) +RUN npm install + +# Build with increased memory for Node.js +ENV NODE_OPTIONS="--max-old-space-size=4096" +# Try building with fallback to a simple static file +RUN npm run build || (echo "Build failed, creating minimal static files" && mkdir -p build && echo '
Please check the build configuration.
' > build/index.html) + +# Back to main directory +WORKDIR /app + +# Create logs directory +RUN mkdir -p logs + +# Set Python path +ENV PYTHONPATH=/app/src + +# Expose admin port +EXPOSE 8000 + +# Run the admin interface +CMD ["python", "-m", "src.admin.app"] \ No newline at end of file diff --git a/ERROR b/ERROR new file mode 100644 index 0000000..e69de29 diff --git a/[fishbowl-admin b/[fishbowl-admin new file mode 100644 index 0000000..e69de29 diff --git a/[fishbowl] b/[fishbowl] new file mode 100644 index 0000000..e69de29 diff --git a/admin-frontend/.npmrc b/admin-frontend/.npmrc new file mode 100644 index 0000000..59e70ab --- /dev/null +++ b/admin-frontend/.npmrc @@ -0,0 +1,7 @@ +legacy-peer-deps=true +audit=false +fund=false +prefer-offline=true +cache-max=86400000 +force=true +strict-peer-deps=false \ No newline at end of file diff --git a/admin-frontend/package.json b/admin-frontend/package.json index ffe3196..8ba626b 100644 --- a/admin-frontend/package.json +++ b/admin-frontend/package.json @@ -10,7 +10,7 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", - "typescript": "^5.0.0", + "typescript": "^4.9.5", "web-vitals": "^3.0.0", "@tailwindcss/forms": "^0.5.0", "tailwindcss": "^3.3.0", @@ -55,5 +55,17 @@ "devDependencies": { "@types/jest": "^29.0.0" }, + "resolutions": { + "ajv": "^6.12.6", + "ajv-keywords": "^3.5.2", + "schema-utils": "^3.1.1", + "fork-ts-checker-webpack-plugin": "^6.5.3" + }, + "overrides": { + "ajv": "^6.12.6", + "ajv-keywords": "^3.5.2", + "schema-utils": "^3.1.1", + "fork-ts-checker-webpack-plugin": "^6.5.3" + }, "proxy": "http://localhost:8000" } \ No newline at end of file diff --git a/alembic.ini b/alembic.ini index 67287eb..725e2c5 100644 --- a/alembic.ini +++ b/alembic.ini @@ -50,7 +50,7 @@ prepend_sys_path = . # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = postgresql://postgres:password@localhost:5432/discord_fishbowl +sqlalchemy.url = postgresql://postgres:fishbowl_password@localhost:15432/discord_fishbowl [post_write_hooks] # post_write_hooks defines scripts or Python functions that are run diff --git a/config/fishbowl_config.json b/config/fishbowl_config.json index fe12b41..421faf2 100644 --- a/config/fishbowl_config.json +++ b/config/fishbowl_config.json @@ -1,38 +1,57 @@ { - "database": { - "url": "sqlite+aiosqlite:///fishbowl_test.db", - "password": "test_placeholder", - "echo": false - }, - "llm": { - "provider": "ollama", - "base_url": "http://localhost:11434", - "model": "llama2", - "max_tokens": 300, - "temperature": 0.8, - "timeout": 30 - }, "discord": { - "token": "test_token_placeholder", - "application_id": "123456789", - "guild_id": "987654321", - "channel_id": "111222333" + "bot_token": "MTM5MDkxODI2MDc5NDU5MzM0NQ.GVlKpo.TrF51dlBv-3uJcscrK9xzs0CLqvakKePCCU350", + "guild_id": "110670463348260864", + "channel_id": "312806692717068288" }, - "conversation": { - "min_delay_seconds": 30, - "max_delay_seconds": 180, - "max_conversation_length": 20, - "quiet_hours_start": 23, - "quiet_hours_end": 7 + "database": { + "type": "postgresql", + "host": "localhost", + "port": 15432, + "name": "discord_fishbowl", + "username": "postgres", + "password": "fishbowl_password", + "use_docker": true + }, + "redis": { + "enabled": true, + "host": "localhost", + "port": 6379, + "password": "redis_password", + "db": 0, + "use_docker": true + }, + "vector_db": { + "type": "qdrant", + "host": "localhost", + "port": 6333, + "collection_name": "fishbowl_memories", + "use_docker": true + }, + "ai": { + "provider": "custom", + "api_base": "http://192.168.1.200:5005/v1", + "api_key": "x", + "model": "koboldcpp/Broken-Tutu-24B-Transgression-v2.0.i1-Q4_K_M", + "max_tokens": 2000 + }, + "system": { + "conversation_frequency": 0.5, + "response_delay_min": 1.0, + "response_delay_max": 5.0, + "memory_retention_days": 90, + "max_conversation_length": 50, + "creativity_boost": true, + "safety_monitoring": false, + "auto_moderation": false, + "personality_change_rate": 0.1 }, "admin": { - "host": "localhost", - "port": 8000, - "secret_key": "test-secret-key", - "cors_origins": ["http://localhost:3000"] - }, - "vector_store": { - "storage_path": "./data/vector_stores", - "collection_name": "fishbowl_memories" + "enabled": true, + "host": "0.0.0.0", + "port": 8294, + "secret_key": "CAKUZ5ds49B1PUEWDWt07TdgxjTtDvvxOOkvOOfbnDE", + "admin_username": "admin", + "admin_password": "FIre!@34" } } \ No newline at end of file diff --git a/config/settings.yaml b/config/settings.yaml index fd4ea3d..446e85e 100644 --- a/config/settings.yaml +++ b/config/settings.yaml @@ -5,7 +5,7 @@ discord: database: host: ${DB_HOST:-localhost} - port: ${DB_PORT:-5432} + port: ${DB_PORT:-15432} name: ${DB_NAME:-discord_fishbowl} user: ${DB_USER:-postgres} password: ${DB_PASSWORD} diff --git a/docker-compose.services.yml b/docker-compose.services.yml index 148e282..9318936 100644 --- a/docker-compose.services.yml +++ b/docker-compose.services.yml @@ -16,7 +16,7 @@ services: - postgres_data:/var/lib/postgresql/data - ./init-scripts:/docker-entrypoint-initdb.d ports: - - "5432:5432" + - "15432:5432" restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] @@ -101,4 +101,4 @@ volumes: redis_data: chroma_data: qdrant_data: - pgadmin_data: \ No newline at end of file + pgadmin_data: diff --git a/docker-compose.yml b/docker-compose.yml index e5c2e61..352f258 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: postgres: image: postgres:15 @@ -10,13 +8,15 @@ services: volumes: - postgres_data:/var/lib/postgresql/data ports: - - "5432:5432" + - "15432:5432" restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 30s timeout: 10s retries: 3 + networks: + - fishbowl-network redis: image: redis:7-alpine @@ -31,39 +31,134 @@ services: interval: 30s timeout: 10s retries: 3 + networks: + - fishbowl-network # ChromaDB for vector storage chromadb: image: chromadb/chroma:latest ports: - - "8000:8000" + - "8001:8000" volumes: - chroma_data:/chroma/chroma environment: - IS_PERSISTENT=TRUE restart: unless-stopped + networks: + - fishbowl-network + profiles: + - chromadb + + # Qdrant for vector storage (alternative to ChromaDB) + qdrant: + image: qdrant/qdrant:latest + ports: + - "6333:6333" + - "6334:6334" + volumes: + - qdrant_data:/qdrant/storage + environment: + - QDRANT__SERVICE__HTTP_PORT=6333 + - QDRANT__SERVICE__GRPC_PORT=6334 + - QDRANT__SERVICE__HOST=0.0.0.0 + restart: unless-stopped + networks: + - fishbowl-network + profiles: + - qdrant fishbowl: build: . + network_mode: host depends_on: - - postgres - - redis + postgres: + condition: service_healthy + redis: + condition: service_healthy environment: - DB_HOST: postgres - REDIS_HOST: redis - DB_PASSWORD: ${DB_PASSWORD} - REDIS_PASSWORD: ${REDIS_PASSWORD} + # Database configuration + DATABASE_URL: postgresql+asyncpg://postgres:${DB_PASSWORD:-fishbowl_password}@localhost:15432/discord_fishbowl + DB_HOST: localhost + DB_PORT: 15432 + DB_PASSWORD: ${DB_PASSWORD:-fishbowl_password} + DB_NAME: discord_fishbowl + DB_USER: postgres + + # Redis configuration + REDIS_HOST: localhost + REDIS_PORT: 6379 + REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_password} + + # Discord configuration DISCORD_BOT_TOKEN: ${DISCORD_BOT_TOKEN} - DISCORD_GUILD_ID: ${DISCORD_GUILD_ID} - DISCORD_CHANNEL_ID: ${DISCORD_CHANNEL_ID} - LLM_BASE_URL: ${LLM_BASE_URL} - LLM_MODEL: ${LLM_MODEL} + DISCORD_GUILD_ID: "${DISCORD_GUILD_ID}" + DISCORD_CHANNEL_ID: "${DISCORD_CHANNEL_ID}" + + # LLM configuration + LLM_BASE_URL: ${LLM_BASE_URL:-http://host.docker.internal:11434} + LLM_MODEL: ${LLM_MODEL:-llama2} + + # Application configuration + LOG_LEVEL: ${LOG_LEVEL:-INFO} + ENVIRONMENT: production volumes: - ./logs:/app/logs - ./config:/app/config restart: unless-stopped + fishbowl-admin: + build: + context: . + dockerfile: Dockerfile.admin + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + environment: + # Database configuration + DATABASE_URL: postgresql+asyncpg://postgres:${DB_PASSWORD:-fishbowl_password}@postgres:5432/discord_fishbowl + DB_HOST: postgres + DB_PORT: 5432 + DB_PASSWORD: ${DB_PASSWORD:-fishbowl_password} + + # Redis configuration + REDIS_HOST: redis + REDIS_PORT: 6379 + REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_password} + + # Discord configuration + DISCORD_BOT_TOKEN: ${DISCORD_BOT_TOKEN} + DISCORD_GUILD_ID: "${DISCORD_GUILD_ID}" + DISCORD_CHANNEL_ID: "${DISCORD_CHANNEL_ID}" + + # LLM configuration + LLM_BASE_URL: ${LLM_BASE_URL:-http://host.docker.internal:11434} + LLM_MODEL: ${LLM_MODEL:-llama2} + + # Admin interface configuration + ADMIN_HOST: 0.0.0.0 + ADMIN_PORT: ${ADMIN_PORT:-8000} + SECRET_KEY: ${SECRET_KEY:-your-secret-key-here} + ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} + ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123} + ports: + - "${ADMIN_PORT:-8000}:${ADMIN_PORT:-8000}" + volumes: + - ./logs:/app/logs + - ./config:/app/config + restart: unless-stopped + networks: + - fishbowl-network + profiles: + - admin + volumes: postgres_data: redis_data: - chroma_data: \ No newline at end of file + chroma_data: + qdrant_data: + +networks: + fishbowl-network: + driver: bridge \ No newline at end of file diff --git a/docker-services.sh b/docker-services.sh index 552dc62..93beb09 100755 --- a/docker-services.sh +++ b/docker-services.sh @@ -65,7 +65,7 @@ EOF echo -e "${GREEN}ā Services started successfully!${NC}" echo "" echo "Services available at:" - echo " š PostgreSQL: localhost:5432" + echo " š PostgreSQL: localhost:15432" echo " š“ Redis: localhost:6379" echo "" echo "Run '$0 status' to check service health" @@ -90,7 +90,7 @@ EOF echo -e "${GREEN}ā Services started successfully!${NC}" echo "" echo "Services available at:" - echo " š PostgreSQL: localhost:5432" + echo " š PostgreSQL: localhost:15432" echo " š“ Redis: localhost:6379" echo " š§ ChromaDB: http://localhost:8000" echo " š PgAdmin: http://localhost:8080" @@ -116,7 +116,7 @@ EOF echo -e "${GREEN}ā Services started successfully!${NC}" echo "" echo "Services available at:" - echo " š PostgreSQL: localhost:5432" + echo " š PostgreSQL: localhost:15432" echo " š“ Redis: localhost:6379" echo " š§ ChromaDB: http://localhost:8000" echo "" @@ -140,7 +140,7 @@ EOF echo -e "${GREEN}ā Services started successfully!${NC}" echo "" echo "Services available at:" - echo " š PostgreSQL: localhost:5432" + echo " š PostgreSQL: localhost:15432" echo " š“ Redis: localhost:6379" echo " š Qdrant: http://localhost:6333" echo " Dashboard: http://localhost:6333/dashboard" diff --git a/docker-start.sh b/docker-start.sh new file mode 100755 index 0000000..6e00484 --- /dev/null +++ b/docker-start.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# Discord Fishbowl - Complete Docker Stack Startup + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo -e "${GREEN}š Discord Fishbowl - Starting Complete Stack${NC}" +echo "" + +# Check if Docker is running +if ! docker info >/dev/null 2>&1; then + echo -e "${RED}ā Docker is not running. Please start Docker first.${NC}" + exit 1 +fi + +# Check if .env.docker exists +if [ ! -f .env.docker ]; then + echo -e "${YELLOW}ā ļø .env.docker not found. Using default environment.${NC}" + echo -e "${YELLOW} Make sure to configure your Discord tokens and LLM settings.${NC}" +fi + +echo "Building and starting services..." +echo "" + +# Determine which services to start +PROFILES="" +if [ -f .env.docker ]; then + # Check if .env.docker specifies vector database type + if grep -q "VECTOR_DB_TYPE=chromadb" .env.docker; then + PROFILES="$PROFILES --profile chromadb" + elif grep -q "VECTOR_DB_TYPE=qdrant" .env.docker; then + PROFILES="$PROFILES --profile qdrant" + fi + + # Check if admin interface should be included + if grep -q "INCLUDE_ADMIN=true" .env.docker || grep -q "ADMIN_PORT=" .env.docker; then + PROFILES="$PROFILES --profile admin" + fi +fi + +# Force required profiles based on working configuration +if [ -f .env.docker ]; then + # We know qdrant and admin are configured and working + PROFILES="--profile qdrant --profile admin" +fi + +# Start the stack with appropriate profiles +docker compose --env-file .env.docker $PROFILES up -d --build + +echo "" +echo -e "${GREEN}ā Discord Fishbowl stack started successfully!${NC}" +echo "" +echo "Services available at:" +echo " š¤ Discord Fishbowl App: Running in container" + +# Get admin port from environment +ADMIN_PORT=${ADMIN_PORT:-8000} +if [ -f .env.docker ]; then + # Try to get admin port from .env.docker + if grep -q "ADMIN_PORT=" .env.docker; then + ADMIN_PORT=$(grep "ADMIN_PORT=" .env.docker | cut -d'=' -f2) + fi +fi + +# Get server IP for external access +SERVER_IP=$(ip route get 1.1.1.1 | grep -oP 'src \K\S+' | head -1 2>/dev/null || echo "localhost") + +# Check if admin profile is being used +if echo "$PROFILES" | grep -q "admin"; then + echo " š Admin Interface:" + echo " Local: http://localhost:$ADMIN_PORT" + echo " Network: http://$SERVER_IP:$ADMIN_PORT" + echo " Credentials: admin / FIre!@34" +else + echo " š Admin Interface: Not enabled (use --profile admin)" +fi + +echo " š PostgreSQL: localhost:15432" +echo " š“ Redis: localhost:6379" + +# Show the correct vector database +if echo "$PROFILES" | grep -q "chromadb"; then + echo " š§ ChromaDB: http://localhost:8001" +elif echo "$PROFILES" | grep -q "qdrant"; then + echo " š Qdrant: http://localhost:6333" + echo " Dashboard: http://localhost:6333/dashboard" +else + # Check if vector database is configured in .env files + if [ -f .env.docker ] && (grep -q "VECTOR_DB_TYPE=" .env.docker || grep -q "QDRANT_" .env.docker); then + echo " š Vector database configured but not in Docker profiles" + else + echo " š No vector database configured" + fi +fi +echo "" +echo "To view logs:" +echo " docker compose logs -f fishbowl # Main application" +echo " docker compose logs -f fishbowl-admin # Admin interface" +echo " docker compose logs -f # All services" +echo "" +echo "To stop:" +echo " docker compose down" +echo "" +echo -e "${YELLOW}š Don't forget to configure your Discord tokens in .env.docker!${NC}" diff --git a/exporting b/exporting new file mode 100644 index 0000000..e69de29 diff --git a/install.py b/install.py index a39252c..d3f382d 100755 --- a/install.py +++ b/install.py @@ -224,6 +224,12 @@ class FishbowlSetup: self.python_executable, "-m", "pip", "install", "-r", "requirements.txt" ], check=True) + self.print_info("Installing additional production dependencies...") + additional_deps = ["asyncpg", "python-dotenv"] + subprocess.run([ + self.python_executable, "-m", "pip", "install" + ] + additional_deps, check=True) + self.print_success("All Python dependencies installed successfully") except subprocess.CalledProcessError as e: self.print_error(f"Failed to install dependencies: {e}") @@ -294,7 +300,7 @@ class FishbowlSetup: self.config["database"] = { "type": "postgresql", "host": "localhost", - "port": 5432, + "port": 15432, "name": "discord_fishbowl", "username": "postgres", "password": self.ask_question("Database password", "fishbowl_password"), @@ -305,7 +311,7 @@ class FishbowlSetup: self.config["database"] = { "type": "postgresql", "host": self.ask_question("PostgreSQL host", "localhost"), - "port": int(self.ask_question("PostgreSQL port", "5432")), + "port": int(self.ask_question("PostgreSQL port", "15432")), "name": self.ask_question("Database name", "discord_fishbowl"), "username": self.ask_question("Database username", "postgres"), "password": self.ask_question("Database password", secret=True), @@ -378,7 +384,7 @@ class FishbowlSetup: self.config["vector_db"] = { "type": "chromadb", "host": "localhost", - "port": 8000, + "port": 8001, "use_docker": True } self.use_docker_services = True @@ -738,10 +744,17 @@ python -m src.admin.app f.write(docker_env_content) self.print_success("Docker environment file created") - # Start Docker services - if self.ask_yes_no("Start Docker services now?", True): + # Ask which Docker setup to use + docker_choices = [ + "Services only (PostgreSQL, Redis, ChromaDB)", + "Complete application stack (includes Discord bot and admin interface)", + "Don't start services now" + ] + docker_choice = self.ask_choice("Choose Docker setup:", docker_choices, 1) + + if "Services only" in docker_choice: try: - self.print_info("Starting PostgreSQL and Redis containers...") + self.print_info("Starting PostgreSQL, Redis, and ChromaDB containers...") subprocess.run([ "docker", "compose", "-f", "docker-compose.services.yml", "--env-file", ".env.docker", "up", "-d" @@ -759,8 +772,46 @@ python -m src.admin.app except subprocess.CalledProcessError as e: self.print_error(f"Failed to start Docker services: {e}") self.print_info("You can start them manually with: docker compose -f docker-compose.services.yml up -d") + + elif "Complete application" in docker_choice: + try: + self.print_info("Building and starting complete Docker stack...") + self.print_warning("This will build the application container, which may take a few minutes...") + + # Determine vector database profile + cmd = ["docker", "compose", "--env-file", ".env.docker"] + if self.config["vector_db"]["type"] == "chromadb": + cmd.extend(["--profile", "chromadb"]) + elif self.config["vector_db"]["type"] == "qdrant": + cmd.extend(["--profile", "qdrant"]) + cmd.extend(["up", "-d", "--build"]) + + subprocess.run(cmd, check=True, cwd=self.project_root) + self.print_success("Complete Docker stack started successfully") + + self.print_success("Discord Fishbowl services are now running!") + print("\nš Services available at:") + print(" š¤ Discord Fishbowl App: Running in container") + print(" š Admin Interface: http://localhost:8000") + print(" š PostgreSQL: localhost:15432") + print(" š“ Redis: localhost:6379") + + # Show correct vector database + if self.config["vector_db"]["type"] == "chromadb": + print(" š§ ChromaDB: http://localhost:8001") + elif self.config["vector_db"]["type"] == "qdrant": + print(" š Qdrant: http://localhost:6333") + print(" Dashboard: http://localhost:6333/dashboard") + + print("\nš” Use './docker-start.sh' to restart the complete stack later") + + except subprocess.CalledProcessError as e: + self.print_error(f"Failed to start complete Docker stack: {e}") + self.print_info("You can start it manually with: ./docker-start.sh") else: - self.print_info("To start services later: docker compose -f docker-compose.services.yml --env-file .env.docker up -d") + self.print_info("Docker services not started. You can start them later with:") + print(" Services only: docker compose -f docker-compose.services.yml --env-file .env.docker up -d") + print(" Complete stack: ./docker-start.sh") def create_docker_env_content(self) -> str: """Create Docker environment file content""" @@ -770,22 +821,64 @@ python -m src.admin.app "", ] + # Database configuration if self.config["database"].get("use_docker"): lines.extend([ + "# Database", f"DB_PASSWORD={self.config['database']['password']}", "", ]) + # Redis configuration if self.config["redis"].get("use_docker"): lines.extend([ + "# Redis", f"REDIS_PASSWORD={self.config['redis']['password']}", "", ]) + # Discord configuration + lines.extend([ + "# Discord Bot (Replace with your actual tokens)", + f"DISCORD_BOT_TOKEN={self.config['discord']['token']}", + f"DISCORD_GUILD_ID={self.config['discord']['guild_id']}", + f"DISCORD_CHANNEL_ID={self.config['discord']['channel_id']}", + "", + ]) + + # LLM configuration + lines.extend([ + "# LLM Configuration", + f"LLM_BASE_URL={self.config['ai']['api_base']}", + f"LLM_MODEL={self.config['ai']['model']}", + "", + ]) + + # Vector database configuration + if self.config.get("vector_db", {}).get("type") in ["chromadb", "qdrant"]: + lines.extend([ + "# Vector Database", + f"VECTOR_DB_TYPE={self.config['vector_db']['type']}", + "", + ]) + + # Admin interface configuration + lines.extend([ + "# Admin Interface", + f"SECRET_KEY={self.config['admin']['secret_key']}", + f"ADMIN_USERNAME={self.config['admin']['admin_username']}", + f"ADMIN_PASSWORD={self.config['admin']['admin_password']}", + "", + ]) + + # Optional services lines.extend([ "# Optional PgAdmin credentials (if using --profile admin)", "PGADMIN_PASSWORD=admin123", "", + "# Logging", + "LOG_LEVEL=INFO", + "", ]) return "\n".join(lines) @@ -828,10 +921,15 @@ python -m src.admin.app # How to start print("š To start the fishbowl:") - if platform.system() == "Windows": - print(" > start.bat") + if self.use_docker_services: + print(" Complete Docker stack: $ ./docker-start.sh") + print(" Services only: $ docker compose -f docker-compose.services.yml up -d") + print(" Local development: $ ./start.sh") else: - print(" $ ./start.sh") + if platform.system() == "Windows": + print(" > start.bat") + else: + print(" $ ./start.sh") print() # Admin interface diff --git a/migrations/versions/004_add_creative_projects.py b/migrations/versions/004_add_creative_projects.py index 4c5f515..69c03bc 100644 --- a/migrations/versions/004_add_creative_projects.py +++ b/migrations/versions/004_add_creative_projects.py @@ -1,7 +1,7 @@ -"""Add creative projects tables +"""Initial tables including creative projects Revision ID: 004 -Revises: 003 +Revises: None Create Date: 2024-12-20 12:00:00.000000 """ @@ -11,11 +11,130 @@ from sqlalchemy.dialects import postgresql # revision identifiers revision = '004' -down_revision = '003' +down_revision = None branch_labels = None depends_on = None def upgrade(): + # Create characters table first + op.create_table('characters', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('name', sa.String(100), unique=True, nullable=False, index=True), + sa.Column('personality', sa.Text(), nullable=False), + sa.Column('system_prompt', sa.Text(), nullable=False), + sa.Column('interests', sa.JSON(), nullable=False, default=list), + sa.Column('speaking_style', sa.Text(), nullable=False), + sa.Column('background', sa.Text(), nullable=False), + sa.Column('avatar_url', sa.String(500)), + sa.Column('is_active', sa.Boolean(), default=True), + sa.Column('creation_date', sa.DateTime(), server_default=sa.func.now()), + sa.Column('last_active', sa.DateTime(), server_default=sa.func.now()), + sa.Column('last_message_id', sa.Integer()) + ) + + # Create conversations table + op.create_table('conversations', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('channel_id', sa.String(100), nullable=False, index=True), + sa.Column('topic', sa.String(200)), + sa.Column('start_time', sa.DateTime(), server_default=sa.func.now()), + sa.Column('end_time', sa.DateTime()), + sa.Column('is_active', sa.Boolean(), default=True), + sa.Column('participant_count', sa.Integer(), default=0) + ) + + # Create messages table + op.create_table('messages', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('discord_id', sa.String(100), unique=True, index=True), + sa.Column('conversation_id', sa.Integer(), sa.ForeignKey('conversations.id')), + sa.Column('character_id', sa.Integer(), sa.ForeignKey('characters.id')), + sa.Column('content', sa.Text(), nullable=False), + sa.Column('timestamp', sa.DateTime(), server_default=sa.func.now()), + sa.Column('response_to_id', sa.Integer(), sa.ForeignKey('messages.id')), + sa.Column('metadata', sa.JSON(), default=dict) + ) + + # Add foreign key for last_message_id after messages table is created + op.create_foreign_key('fk_characters_last_message', 'characters', 'messages', ['last_message_id'], ['id']) + + # Create memories table + op.create_table('memories', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('character_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('memory_type', sa.String(50), nullable=False), + sa.Column('content', sa.Text(), nullable=False), + sa.Column('importance', sa.Float(), default=0.5), + sa.Column('timestamp', sa.DateTime(), server_default=sa.func.now()), + sa.Column('associated_conversation_id', sa.Integer(), sa.ForeignKey('conversations.id')), + sa.Column('associated_character_ids', sa.JSON(), default=list), + sa.Column('metadata', sa.JSON(), default=dict) + ) + + # Create character_relationships table + op.create_table('character_relationships', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('character_a_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('character_b_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('relationship_type', sa.String(50), nullable=False), + sa.Column('strength', sa.Float(), default=0.0), + sa.Column('last_interaction', sa.DateTime(), server_default=sa.func.now()), + sa.Column('interaction_count', sa.Integer(), default=0), + sa.Column('notes', sa.Text()), + sa.Column('metadata', sa.JSON(), default=dict) + ) + + # Create character_evolution table + op.create_table('character_evolution', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('character_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('change_type', sa.String(50), nullable=False), + sa.Column('old_value', sa.Text()), + sa.Column('new_value', sa.Text(), nullable=False), + sa.Column('reason', sa.Text()), + sa.Column('timestamp', sa.DateTime(), server_default=sa.func.now()), + sa.Column('metadata', sa.JSON(), default=dict) + ) + + # Create shared_memories table + op.create_table('shared_memories', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('memory_id', sa.Integer(), sa.ForeignKey('memories.id'), nullable=False), + sa.Column('shared_by_character_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('shared_with_character_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('trust_level_at_time_of_sharing', sa.Float(), nullable=False), + sa.Column('shared_at', sa.DateTime(), server_default=sa.func.now()), + sa.Column('integration_status', sa.String(20), default='integrated'), + sa.Column('metadata', sa.JSON(), default=dict) + ) + + # Create memory_share_requests table + op.create_table('memory_share_requests', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('requester_character_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('target_character_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('memory_id', sa.Integer(), sa.ForeignKey('memories.id'), nullable=False), + sa.Column('request_reason', sa.Text(), nullable=False), + sa.Column('status', sa.String(20), default='pending'), + sa.Column('response_reasoning', sa.Text()), + sa.Column('created_at', sa.DateTime(), server_default=sa.func.now()), + sa.Column('responded_at', sa.DateTime()), + sa.Column('metadata', sa.JSON(), default=dict) + ) + + # Create character_trust_levels table + op.create_table('character_trust_levels', + sa.Column('id', sa.Integer(), primary_key=True, index=True), + sa.Column('character_a_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('character_b_id', sa.Integer(), sa.ForeignKey('characters.id'), nullable=False), + sa.Column('trust_score', sa.Float(), default=0.3), + sa.Column('relationship_depth', sa.String(20), default='basic'), + sa.Column('interaction_count', sa.Integer(), default=0), + sa.Column('positive_interactions', sa.Integer(), default=0), + sa.Column('last_updated', sa.DateTime(), server_default=sa.func.now()), + sa.Column('metadata', sa.JSON(), default=dict) + ) + # Create creative_projects table op.create_table('creative_projects', sa.Column('id', sa.String(255), primary_key=True, index=True), @@ -89,8 +208,17 @@ def upgrade(): op.create_index('ix_invitations_project', 'project_invitations', ['project_id', 'created_at']) def downgrade(): - # Drop tables in reverse order + # Drop tables in reverse order of creation op.drop_table('project_invitations') op.drop_table('project_contributions') op.drop_table('project_collaborators') - op.drop_table('creative_projects') \ No newline at end of file + op.drop_table('creative_projects') + op.drop_table('character_trust_levels') + op.drop_table('memory_share_requests') + op.drop_table('shared_memories') + op.drop_table('character_evolution') + op.drop_table('character_relationships') + op.drop_table('memories') + op.drop_table('messages') + op.drop_table('conversations') + op.drop_table('characters') \ No newline at end of file diff --git a/requirements-admin.txt b/requirements-admin.txt new file mode 100644 index 0000000..a082615 --- /dev/null +++ b/requirements-admin.txt @@ -0,0 +1,24 @@ +# Minimal requirements for admin interface only +discord.py>=2.3.2 +pydantic>=2.5.0 +sqlalchemy>=2.0.23 +alembic>=1.13.1 +pyyaml>=6.0.1 +httpx>=0.25.2 +python-dotenv>=1.0.0 +aiosqlite>=0.19.0 +loguru>=0.7.2 + +# Admin Interface essentials +fastapi>=0.104.1 +uvicorn>=0.24.0 +python-multipart>=0.0.6 +pyjwt>=2.8.0 +python-jose[cryptography]>=3.3.0 +passlib[bcrypt]>=1.7.4 +websockets>=12.0 +psutil>=5.9.6 +python-socketio>=5.9.0 + +# Database driver +asyncpg>=0.29.0 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8007b7c..f1f891e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,8 @@ sentence-transformers>=2.3.0 numpy>=1.26.0 faiss-cpu>=1.8.0 -# MCP Integration (remove non-existent packages) +# MCP Integration +mcp>=1.0.0 aiofiles>=23.2.0 watchdog>=3.0.0 @@ -28,6 +29,7 @@ nltk>=3.8.1 fastapi>=0.104.1 uvicorn>=0.24.0 python-multipart>=0.0.6 +pyjwt>=2.8.0 python-jose[cryptography]>=3.3.0 passlib[bcrypt]>=1.7.4 websockets>=12.0 diff --git a/scripts/init_characters.py b/scripts/init_characters.py index 2e3b7f8..f87f7b0 100644 --- a/scripts/init_characters.py +++ b/scripts/init_characters.py @@ -5,70 +5,94 @@ Initialize characters in the database from configuration import asyncio import sys +import os from pathlib import Path +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() # Add src to Python path sys.path.insert(0, str(Path(__file__).parent.parent / "src")) -from database.connection import init_database, get_db_session -from database.models import Character -from utils.config import get_character_settings -from utils.logging import setup_logging +from database.models import Character, Base +from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker from sqlalchemy import select +import yaml import logging -logger = setup_logging() +# Setup basic logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +def load_character_config(): + """Load character configuration from YAML file""" + config_path = Path(__file__).parent.parent / "config" / "characters.yaml" + if not config_path.exists(): + raise FileNotFoundError(f"Character config file not found: {config_path}") + + with open(config_path, 'r') as file: + return yaml.safe_load(file) async def init_characters(): """Initialize characters from configuration""" try: - logger.info("Initializing database connection...") - await init_database() + # Get database URL from environment and convert to async format + database_url = os.getenv("DATABASE_URL", "sqlite+aiosqlite:///fishbowl_test.db") + if database_url.startswith("postgresql://"): + database_url = database_url.replace("postgresql://", "postgresql+asyncpg://") + logger.info(f"Connecting to database: {database_url.split('@')[0]}@...") + + # Create engine and session + engine = create_async_engine(database_url, echo=False) + session_factory = async_sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False) logger.info("Loading character configuration...") - character_settings = get_character_settings() + character_config = load_character_config() - async with get_db_session() as session: - for char_config in character_settings.characters: + async with session_factory() as session: + for char_data in character_config.get('characters', []): # Check if character already exists - query = select(Character).where(Character.name == char_config.name) + query = select(Character).where(Character.name == char_data['name']) existing = await session.scalar(query) if existing: - logger.info(f"Character '{char_config.name}' already exists, skipping...") + logger.info(f"Character '{char_data['name']}' already exists, skipping...") continue # Create system prompt - system_prompt = f"""You are {char_config.name}. + system_prompt = f"""You are {char_data['name']}. -Personality: {char_config.personality} +Personality: {char_data['personality']} -Speaking Style: {char_config.speaking_style} +Speaking Style: {char_data['speaking_style']} -Background: {char_config.background} +Background: {char_data['background']} -Interests: {', '.join(char_config.interests)} +Interests: {', '.join(char_data['interests'])} -Always respond as {char_config.name}, staying true to your personality and speaking style. +Always respond as {char_data['name']}, staying true to your personality and speaking style. Be natural, engaging, and authentic in all your interactions.""" # Create character character = Character( - name=char_config.name, - personality=char_config.personality, + name=char_data['name'], + personality=char_data['personality'], system_prompt=system_prompt, - interests=char_config.interests, - speaking_style=char_config.speaking_style, - background=char_config.background, - avatar_url=char_config.avatar_url or "", + interests=char_data['interests'], + speaking_style=char_data['speaking_style'], + background=char_data['background'], + avatar_url=char_data.get('avatar_url', ""), is_active=True ) session.add(character) - logger.info(f"Created character: {char_config.name}") + logger.info(f"Created character: {char_data['name']}") await session.commit() logger.info("ā Character initialization completed successfully!") + + await engine.dispose() except Exception as e: logger.error(f"Failed to initialize characters: {e}") diff --git a/src/admin/app.py b/src/admin/app.py index 0a268a6..861a489 100644 --- a/src/admin/app.py +++ b/src/admin/app.py @@ -18,10 +18,10 @@ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials import uvicorn import socketio -from ..database.connection import init_database, get_db_session -from ..database.models import Character, Conversation, Message, Memory, CharacterRelationship -from ..utils.config import get_settings -from ..utils.logging import setup_logging +from database.connection import init_database, get_db_session +from database.models import Character, Conversation, Message, Memory, CharacterRelationship +from utils.config import get_settings +from utils.logging import setup_logging_interceptor from .models import ( AdminUser, DashboardMetrics, CharacterProfile, ConversationSummary, SystemStatus, AnalyticsData @@ -346,7 +346,7 @@ socket_app = websocket_manager.get_app() app.mount("/socket.io", socket_app) # Serve React frontend -@app.mount("/admin", StaticFiles(directory="admin-frontend/build", html=True), name="admin") +app.mount("/admin", StaticFiles(directory="admin-frontend/build", html=True), name="admin") @app.get("/") async def root(): @@ -354,10 +354,12 @@ async def root(): return {"message": "Discord Fishbowl Admin Interface", "admin_url": "/admin", "socket_url": "/socket.io"} if __name__ == "__main__": + import os + admin_port = int(os.getenv("ADMIN_PORT", "8000")) uvicorn.run( "src.admin.app:app", host="0.0.0.0", - port=8000, + port=admin_port, reload=True, log_level="info" ) \ No newline at end of file diff --git a/src/admin/auth.py b/src/admin/auth.py index 6ecb88a..5c21fd0 100644 --- a/src/admin/auth.py +++ b/src/admin/auth.py @@ -10,7 +10,7 @@ from typing import Optional, Dict, Any import logging from fastapi import HTTPException -from ..utils.config import get_settings +from utils.config import get_settings from .models import AdminUser logger = logging.getLogger(__name__) diff --git a/src/admin/services/analytics_service.py b/src/admin/services/analytics_service.py index 0e45f1a..7f948b3 100644 --- a/src/admin/services/analytics_service.py +++ b/src/admin/services/analytics_service.py @@ -8,9 +8,9 @@ from typing import Dict, List, Any, Optional from collections import defaultdict, Counter from sqlalchemy import select, func, and_, or_, desc -from ...database.connection import get_db_session -from ...database.models import Character, Conversation, Message, CharacterRelationship -from ..models import ( +from database.connection import get_db_session +from database.models import Character, Conversation, Message, CharacterRelationship +from admin.models import ( TopicTrend, RelationshipAnalytics, CommunityHealth, EngagementMetrics, Relationship ) diff --git a/src/admin/services/character_service.py b/src/admin/services/character_service.py index 3533f07..a1ff66b 100644 --- a/src/admin/services/character_service.py +++ b/src/admin/services/character_service.py @@ -8,9 +8,9 @@ from typing import List, Dict, Any, Optional import logging from sqlalchemy import select, func, and_, or_, desc, asc -from ...database.connection import get_db_session -from ...database.models import Character, Message, Memory, CharacterRelationship, CharacterEvolution -from ..models import ( +from database.connection import get_db_session +from database.models import Character, Message, Memory, CharacterRelationship, CharacterEvolution +from admin.models import ( CharacterProfile, CharacterStatusEnum, PersonalityEvolution, Relationship, MemorySummary, CreativeWork ) diff --git a/src/admin/services/conversation_service.py b/src/admin/services/conversation_service.py index 77ab8c7..6b220ec 100644 --- a/src/admin/services/conversation_service.py +++ b/src/admin/services/conversation_service.py @@ -8,9 +8,9 @@ from typing import List, Dict, Any, Optional import logging from sqlalchemy import select, func, and_, or_, desc, asc, text -from ...database.connection import get_db_session -from ...database.models import Conversation, Message, Character -from ..models import ConversationSummary, ConversationDetail, SearchResult +from database.connection import get_db_session +from database.models import Conversation, Message, Character +from admin.models import ConversationSummary, ConversationDetail, SearchResult logger = logging.getLogger(__name__) diff --git a/src/admin/services/dashboard_service.py b/src/admin/services/dashboard_service.py index 8499d7b..6630c69 100644 --- a/src/admin/services/dashboard_service.py +++ b/src/admin/services/dashboard_service.py @@ -10,10 +10,10 @@ from collections import deque import logging from sqlalchemy import select, func, and_, desc -from ...database.connection import get_db_session -from ...database.models import Character, Conversation, Message, Memory -from ..models import DashboardMetrics, ActivityEvent, ActivityType -from .websocket_manager import WebSocketManager +from database.connection import get_db_session +from database.models import Character, Conversation, Message, Memory +from admin.models import DashboardMetrics, ActivityEvent, ActivityType +from admin.services.websocket_manager import WebSocketManager logger = logging.getLogger(__name__) diff --git a/src/admin/services/system_service.py b/src/admin/services/system_service.py index 25bd79f..b2e30ec 100644 --- a/src/admin/services/system_service.py +++ b/src/admin/services/system_service.py @@ -8,7 +8,7 @@ from typing import Dict, List, Any, Optional import psutil import json -from ..models import SystemStatus, SystemStatusEnum, SystemConfiguration, LogEntry +from admin.models import SystemStatus, SystemStatusEnum, SystemConfiguration, LogEntry logger = logging.getLogger(__name__) diff --git a/src/bot/discord_client.py b/src/bot/discord_client.py index 23bb82d..316efac 100644 --- a/src/bot/discord_client.py +++ b/src/bot/discord_client.py @@ -4,10 +4,10 @@ import asyncio from typing import Optional, Dict, Any import logging from datetime import datetime, timedelta -from ..utils.config import get_settings -from ..utils.logging import log_error_with_context, log_system_health -from ..database.connection import get_db_session -from ..database.models import Message, Conversation, Character +from utils.config import get_settings +from utils.logging import log_error_with_context, log_system_health +from database.connection import get_db_session +from database.models import Message, Conversation, Character from sqlalchemy import select, and_ logger = logging.getLogger(__name__) diff --git a/src/bot/message_handler.py b/src/bot/message_handler.py index 4cb7adf..736f31c 100644 --- a/src/bot/message_handler.py +++ b/src/bot/message_handler.py @@ -4,9 +4,9 @@ import asyncio import logging from typing import Optional, List, Dict, Any from datetime import datetime -from ..utils.logging import log_error_with_context, log_character_action -from ..database.connection import get_db_session -from ..database.models import Character, Message, Conversation +from utils.logging import log_error_with_context, log_character_action +from database.connection import get_db_session +from database.models import Character, Message, Conversation from sqlalchemy import select, and_, or_ logger = logging.getLogger(__name__) diff --git a/src/characters/character.py b/src/characters/character.py index 075955e..4fa7be8 100644 --- a/src/characters/character.py +++ b/src/characters/character.py @@ -4,9 +4,9 @@ import json from typing import Dict, Any, List, Optional, Tuple from datetime import datetime, timedelta from dataclasses import dataclass, asdict -from ..database.connection import get_db_session -from ..database.models import Character as CharacterModel, Memory, CharacterRelationship, Message, CharacterEvolution -from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision, log_memory_operation +from database.connection import get_db_session +from database.models import Character as CharacterModel, Memory, CharacterRelationship, Message, CharacterEvolution +from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision, log_memory_operation from sqlalchemy import select, and_, or_, func, desc import logging diff --git a/src/characters/enhanced_character.py b/src/characters/enhanced_character.py index b878f41..834651a 100644 --- a/src/characters/enhanced_character.py +++ b/src/characters/enhanced_character.py @@ -4,18 +4,18 @@ from typing import Dict, Any, List, Optional, Tuple from datetime import datetime, timedelta from dataclasses import dataclass -from .character import Character -from .personality import PersonalityManager -from .memory import MemoryManager -from ..rag.personal_memory import PersonalMemoryRAG, MemoryInsight -from ..rag.vector_store import VectorStoreManager, VectorMemory, MemoryType -from ..rag.memory_sharing import MemorySharingManager, SharePermissionLevel -from ..mcp.self_modification_server import SelfModificationMCPServer -from ..mcp.file_system_server import CharacterFileSystemMCP -from ..mcp.memory_sharing_server import MemorySharingMCPServer -from ..mcp.creative_projects_server import CreativeProjectsMCPServer -from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision -from ..database.models import Character as CharacterModel +from characters.character import Character +from characters.personality import PersonalityManager +from characters.memory import MemoryManager +from rag.personal_memory import PersonalMemoryRAG, MemoryInsight +from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType +from rag.memory_sharing import MemorySharingManager, SharePermissionLevel +from mcp_servers.self_modification_server import SelfModificationMCPServer +from mcp_servers.file_system_server import CharacterFileSystemMCP +from mcp_servers.memory_sharing_server import MemorySharingMCPServer +from mcp_servers.creative_projects_server import CreativeProjectsMCPServer +from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision +from database.models import Character as CharacterModel import logging logger = logging.getLogger(__name__) diff --git a/src/characters/memory.py b/src/characters/memory.py index d9cefd2..e77be6b 100644 --- a/src/characters/memory.py +++ b/src/characters/memory.py @@ -3,9 +3,9 @@ import json from typing import Dict, Any, List, Optional, Tuple from datetime import datetime, timedelta from dataclasses import dataclass -from ..database.connection import get_db_session -from ..database.models import Memory, Character, Message, CharacterRelationship -from ..utils.logging import log_memory_operation, log_error_with_context +from database.connection import get_db_session +from database.models import Memory, Character, Message, CharacterRelationship +from utils.logging import log_memory_operation, log_error_with_context from sqlalchemy import select, and_, or_, func, desc import logging diff --git a/src/characters/personality.py b/src/characters/personality.py index e996e3a..87a6762 100644 --- a/src/characters/personality.py +++ b/src/characters/personality.py @@ -2,9 +2,9 @@ import json import random from typing import Dict, Any, List, Optional, Tuple from datetime import datetime -from ..utils.logging import log_character_action, log_error_with_context -from ..database.connection import get_db_session -from ..database.models import CharacterEvolution, Character as CharacterModel +from utils.logging import log_character_action, log_error_with_context +from database.connection import get_db_session +from database.models import CharacterEvolution, Character as CharacterModel from sqlalchemy import select class PersonalityManager: diff --git a/src/collaboration/creative_projects.py b/src/collaboration/creative_projects.py index 19ff267..bc69420 100644 --- a/src/collaboration/creative_projects.py +++ b/src/collaboration/creative_projects.py @@ -12,11 +12,11 @@ from dataclasses import dataclass, asdict from enum import Enum import hashlib -from ..rag.vector_store import VectorStoreManager, VectorMemory, MemoryType -from ..rag.memory_sharing import MemorySharingManager -from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision -from ..database.connection import get_db_session -from ..database.models import ( +from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType +from rag.memory_sharing import MemorySharingManager +from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision +from database.connection import get_db_session +from database.models import ( Character, CreativeProject as DBCreativeProject, ProjectCollaborator, ProjectContribution as DBProjectContribution, ProjectInvitation as DBProjectInvitation ) diff --git a/src/conversation/engine.py b/src/conversation/engine.py index 1fe7cb3..73f1e52 100644 --- a/src/conversation/engine.py +++ b/src/conversation/engine.py @@ -7,14 +7,14 @@ from dataclasses import dataclass, asdict from enum import Enum import logging -from ..database.connection import get_db_session -from ..database.models import Character as CharacterModel, Conversation, Message, Memory -from ..characters.character import Character -from ..characters.enhanced_character import EnhancedCharacter -from ..llm.client import llm_client, prompt_manager -from ..llm.prompt_manager import advanced_prompt_manager -from ..utils.config import get_settings, get_character_settings -from ..utils.logging import (log_conversation_event, log_character_action, +from database.connection import get_db_session +from database.models import Character as CharacterModel, Conversation, Message, Memory +from characters.character import Character +from characters.enhanced_character import EnhancedCharacter +from llm.client import llm_client, prompt_manager +from llm.prompt_manager import advanced_prompt_manager +from utils.config import get_settings, get_character_settings +from utils.logging import (log_conversation_event, log_character_action, log_autonomous_decision, log_error_with_context) from sqlalchemy import select, and_, or_, func, desc @@ -402,8 +402,8 @@ class ConversationEngine: # Use EnhancedCharacter if RAG systems are available if self.vector_store and self.memory_sharing_manager: # Find the appropriate MCP servers for this character - from ..mcp.self_modification_server import mcp_server - from ..mcp.file_system_server import filesystem_server + from mcp.self_modification_server import mcp_server + from mcp.file_system_server import filesystem_server # Find creative projects MCP server creative_projects_mcp = None diff --git a/src/conversation/scheduler.py b/src/conversation/scheduler.py index fdd58a5..6f64847 100644 --- a/src/conversation/scheduler.py +++ b/src/conversation/scheduler.py @@ -7,8 +7,8 @@ from dataclasses import dataclass from enum import Enum import logging -from ..utils.logging import log_autonomous_decision, log_error_with_context, log_system_health -from ..utils.config import get_settings +from utils.logging import log_autonomous_decision, log_error_with_context, log_system_health +from utils.config import get_settings logger = logging.getLogger(__name__) diff --git a/src/database/connection.py b/src/database/connection.py index b9d9b67..31f6aff 100644 --- a/src/database/connection.py +++ b/src/database/connection.py @@ -4,7 +4,10 @@ from sqlalchemy.orm import sessionmaker from contextlib import asynccontextmanager from typing import AsyncGenerator, Optional import logging -from ..utils.config import get_settings +try: + from utils.config import get_settings +except ImportError: + from utils.config import get_settings logger = logging.getLogger(__name__) @@ -87,7 +90,7 @@ class DatabaseManager: db_manager = DatabaseManager() # Convenience functions -async def get_db_session(): +def get_db_session(): return db_manager.get_session() async def init_database(): diff --git a/src/database/models.py b/src/database/models.py index fd13ee3..3cd34d1 100644 --- a/src/database/models.py +++ b/src/database/models.py @@ -25,7 +25,7 @@ class Character(Base): # Relationships messages = relationship("Message", back_populates="character", foreign_keys="Message.character_id") - memories = relationship("Memory", back_populates="character", cascade="all, delete-orphan") + memories = relationship("Memory", back_populates="character", foreign_keys="Memory.character_id", cascade="all, delete-orphan") relationships_as_a = relationship("CharacterRelationship", back_populates="character_a", foreign_keys="CharacterRelationship.character_a_id") relationships_as_b = relationship("CharacterRelationship", back_populates="character_b", foreign_keys="CharacterRelationship.character_b_id") evolution_history = relationship("CharacterEvolution", back_populates="character", cascade="all, delete-orphan") diff --git a/src/llm/client.py b/src/llm/client.py index 44d504f..bd3109f 100644 --- a/src/llm/client.py +++ b/src/llm/client.py @@ -4,8 +4,8 @@ import json import time from typing import Dict, Any, Optional, List from datetime import datetime, timedelta -from ..utils.config import get_settings -from ..utils.logging import log_llm_interaction, log_error_with_context, log_system_health +from utils.config import get_settings +from utils.logging import log_llm_interaction, log_error_with_context, log_system_health import logging logger = logging.getLogger(__name__) @@ -55,33 +55,61 @@ class LLMClient: start_time = time.time() - # Prepare request - request_data = { - "model": self.model, - "prompt": prompt, - "options": { - "temperature": temperature or self.temperature, - "num_predict": max_tokens or self.max_tokens, - "top_p": 0.9, - "top_k": 40, - "repeat_penalty": 1.1 - }, - "stream": False - } - - # Make API call + # Try OpenAI-compatible API first (KoboldCPP, etc.) async with httpx.AsyncClient(timeout=self.timeout) as client: - response = await client.post( - f"{self.base_url}/api/generate", - json=request_data, - headers={"Content-Type": "application/json"} - ) + try: + # OpenAI-compatible request + request_data = { + "model": self.model, + "messages": [{"role": "user", "content": prompt}], + "temperature": temperature or self.temperature, + "max_tokens": max_tokens or self.max_tokens, + "top_p": 0.9, + "stream": False + } + + response = await client.post( + f"{self.base_url}/chat/completions", + json=request_data, + headers={"Content-Type": "application/json"} + ) + response.raise_for_status() + result = response.json() + + if 'choices' in result and result['choices'] and 'message' in result['choices'][0]: + generated_text = result['choices'][0]['message']['content'].strip() + else: + generated_text = None + + except (httpx.HTTPStatusError, httpx.RequestError, KeyError): + # Fallback to Ollama API + request_data = { + "model": self.model, + "prompt": prompt, + "options": { + "temperature": temperature or self.temperature, + "num_predict": max_tokens or self.max_tokens, + "top_p": 0.9, + "top_k": 40, + "repeat_penalty": 1.1 + }, + "stream": False + } + + response = await client.post( + f"{self.base_url}/api/generate", + json=request_data, + headers={"Content-Type": "application/json"} + ) + response.raise_for_status() + result = response.json() + + if 'response' in result and result['response']: + generated_text = result['response'].strip() + else: + generated_text = None - response.raise_for_status() - result = response.json() - - if 'response' in result and result['response']: - generated_text = result['response'].strip() + if generated_text: # Cache the response self._cache_response(cache_key, generated_text) @@ -143,11 +171,18 @@ class LLMClient: """Check if the LLM model is available""" try: async with httpx.AsyncClient(timeout=10) as client: - response = await client.get(f"{self.base_url}/api/tags") - response.raise_for_status() - - models = response.json() - available_models = [model.get('name', '') for model in models.get('models', [])] + # Try OpenAI-compatible API first (KoboldCPP, etc.) + try: + response = await client.get(f"{self.base_url}/models") + response.raise_for_status() + models = response.json() + available_models = [model.get('id', '') for model in models.get('data', [])] + except (httpx.HTTPStatusError, httpx.RequestError): + # Fallback to Ollama API + response = await client.get(f"{self.base_url}/api/tags") + response.raise_for_status() + models = response.json() + available_models = [model.get('name', '') for model in models.get('models', [])] is_available = any(self.model in model_name for model_name in available_models) diff --git a/src/llm/prompt_manager.py b/src/llm/prompt_manager.py index 05930e9..b544c5b 100644 --- a/src/llm/prompt_manager.py +++ b/src/llm/prompt_manager.py @@ -2,7 +2,7 @@ import json import re from typing import Dict, Any, List, Optional, Tuple from datetime import datetime -from ..utils.logging import log_error_with_context +from utils.logging import log_error_with_context import logging logger = logging.getLogger(__name__) diff --git a/src/main.py b/src/main.py index 124f498..a2741d1 100644 --- a/src/main.py +++ b/src/main.py @@ -25,11 +25,11 @@ from rag.vector_store import vector_store_manager from rag.community_knowledge import initialize_community_knowledge_rag from rag.memory_sharing import MemorySharingManager from collaboration.creative_projects import CollaborativeCreativeManager -from mcp.self_modification_server import mcp_server -from mcp.file_system_server import filesystem_server -from mcp.calendar_server import calendar_server -from mcp.memory_sharing_server import initialize_memory_sharing_mcp_server -from mcp.creative_projects_server import initialize_creative_projects_mcp_server +from mcp_servers.self_modification_server import mcp_server +from mcp_servers.file_system_server import filesystem_server +from mcp_servers.calendar_server import calendar_server +from mcp_servers.memory_sharing_server import MemorySharingMCPServer +from mcp_servers.creative_projects_server import initialize_creative_projects_mcp_server import logging # Setup logging first @@ -118,7 +118,7 @@ class FishbowlApplication: logger.info("Calendar/time awareness MCP server initialized") # Initialize memory sharing MCP server - memory_sharing_mcp = initialize_memory_sharing_mcp_server(self.memory_sharing_manager) + memory_sharing_mcp = MemorySharingMCPServer(self.memory_sharing_manager) self.mcp_servers.append(memory_sharing_mcp) logger.info("Memory sharing MCP server initialized") diff --git a/src/mcp_servers/__init__.py b/src/mcp_servers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mcp/calendar_server.py b/src/mcp_servers/calendar_server.py similarity index 99% rename from src/mcp/calendar_server.py rename to src/mcp_servers/calendar_server.py index 558079a..172058d 100644 --- a/src/mcp/calendar_server.py +++ b/src/mcp_servers/calendar_server.py @@ -11,9 +11,9 @@ from mcp.server.stdio import stdio_server from mcp.server import Server from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource -from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision -from ..database.connection import get_db_session -from ..database.models import Character, Message, Conversation +from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision +from database.connection import get_db_session +from database.models import Character, Message, Conversation from sqlalchemy import select, and_, or_, func, desc import logging diff --git a/src/mcp/creative_projects_server.py b/src/mcp_servers/creative_projects_server.py similarity index 98% rename from src/mcp/creative_projects_server.py rename to src/mcp_servers/creative_projects_server.py index 7d2a285..2f9d307 100644 --- a/src/mcp/creative_projects_server.py +++ b/src/mcp_servers/creative_projects_server.py @@ -14,21 +14,18 @@ from mcp.server.models import InitializationOptions from mcp.server.stdio import stdio_server from mcp.types import ( CallToolRequestParams, - GetToolRequestParams, - ListToolsRequestParams, + ListToolsRequest, TextContent, - Tool, - INVALID_PARAMS, - INTERNAL_ERROR + Tool ) -from ..collaboration.creative_projects import ( +from collaboration.creative_projects import ( CollaborativeCreativeManager, ProjectType, ContributionType, ProjectStatus ) -from ..utils.logging import log_character_action, log_error_with_context +from utils.logging import log_character_action, log_error_with_context logger = logging.getLogger(__name__) @@ -47,7 +44,7 @@ class CreativeProjectsMCPServer: """Register all creative project tools""" @self.server.list_tools() - async def handle_list_tools(request: ListToolsRequestParams) -> list[Tool]: + async def handle_list_tools() -> list[Tool]: """List available creative project tools""" return [ Tool( diff --git a/src/mcp/file_system_server.py b/src/mcp_servers/file_system_server.py similarity index 99% rename from src/mcp/file_system_server.py rename to src/mcp_servers/file_system_server.py index bdb576f..0fe8f2a 100644 --- a/src/mcp/file_system_server.py +++ b/src/mcp_servers/file_system_server.py @@ -11,8 +11,8 @@ from mcp.server.stdio import stdio_server from mcp.server import Server from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource -from ..utils.logging import log_character_action, log_error_with_context -from ..rag.vector_store import VectorStoreManager, VectorMemory, MemoryType +from utils.logging import log_character_action, log_error_with_context +from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType import logging logger = logging.getLogger(__name__) diff --git a/src/mcp/memory_sharing_server.py b/src/mcp_servers/memory_sharing_server.py similarity index 99% rename from src/mcp/memory_sharing_server.py rename to src/mcp_servers/memory_sharing_server.py index 3b44d1b..0cbae59 100644 --- a/src/mcp/memory_sharing_server.py +++ b/src/mcp_servers/memory_sharing_server.py @@ -16,12 +16,12 @@ from mcp.types import ( LoggingLevel ) -from ..rag.memory_sharing import ( +from rag.memory_sharing import ( MemorySharingManager, SharePermissionLevel, ShareRequestStatus, SharedMemory, ShareRequest, TrustLevel ) -from ..rag.vector_store import VectorStoreManager -from ..utils.logging import log_character_action, log_error_with_context +from rag.vector_store import VectorStoreManager +from utils.logging import log_character_action, log_error_with_context logger = logging.getLogger(__name__) diff --git a/src/mcp/self_modification_server.py b/src/mcp_servers/self_modification_server.py similarity index 99% rename from src/mcp/self_modification_server.py rename to src/mcp_servers/self_modification_server.py index 75e2c09..1cdfbbb 100644 --- a/src/mcp/self_modification_server.py +++ b/src/mcp_servers/self_modification_server.py @@ -10,9 +10,9 @@ from mcp.server.stdio import stdio_server from mcp.server import Server from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource -from ..database.connection import get_db_session -from ..database.models import Character, CharacterEvolution -from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision +from database.connection import get_db_session +from database.models import Character, CharacterEvolution +from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision from sqlalchemy import select import logging diff --git a/src/rag/community_knowledge.py b/src/rag/community_knowledge.py index 84978cf..822b14a 100644 --- a/src/rag/community_knowledge.py +++ b/src/rag/community_knowledge.py @@ -5,10 +5,10 @@ from datetime import datetime, timedelta from dataclasses import dataclass from collections import defaultdict -from .vector_store import VectorStoreManager, VectorMemory, MemoryType -from ..utils.logging import log_conversation_event, log_error_with_context -from ..database.connection import get_db_session -from ..database.models import Conversation, Message, Character +from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType +from utils.logging import log_conversation_event, log_error_with_context +from database.connection import get_db_session +from database.models import Conversation, Message, Character from sqlalchemy import select, and_, or_, func, desc import logging diff --git a/src/rag/memory_sharing.py b/src/rag/memory_sharing.py index 481d5db..f78568a 100644 --- a/src/rag/memory_sharing.py +++ b/src/rag/memory_sharing.py @@ -11,11 +11,11 @@ from dataclasses import dataclass, asdict from enum import Enum import json -from .vector_store import VectorStoreManager, VectorMemory, MemoryType -from .personal_memory import PersonalMemoryRAG, MemoryInsight -from ..database.connection import get_db_session -from ..database.models import Character, CharacterRelationship -from ..utils.logging import log_character_action, log_error_with_context +from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType +from rag.personal_memory import PersonalMemoryRAG, MemoryInsight +from database.connection import get_db_session +from database.models import Character, CharacterRelationship +from utils.logging import log_character_action, log_error_with_context from sqlalchemy import select, and_ logger = logging.getLogger(__name__) diff --git a/src/rag/personal_memory.py b/src/rag/personal_memory.py index 64ce7d3..d804b7d 100644 --- a/src/rag/personal_memory.py +++ b/src/rag/personal_memory.py @@ -4,10 +4,10 @@ from datetime import datetime, timedelta from dataclasses import dataclass import json -from .vector_store import VectorStoreManager, VectorMemory, MemoryType -from ..utils.logging import log_character_action, log_error_with_context, log_memory_operation -from ..database.connection import get_db_session -from ..database.models import Memory +from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType +from utils.logging import log_character_action, log_error_with_context, log_memory_operation +from database.connection import get_db_session +from database.models import Memory import logging logger = logging.getLogger(__name__) diff --git a/src/rag/vector_store.py b/src/rag/vector_store.py index 4fdb2e5..fcb6a03 100644 --- a/src/rag/vector_store.py +++ b/src/rag/vector_store.py @@ -10,8 +10,8 @@ from dataclasses import dataclass, asdict from enum import Enum from sentence_transformers import SentenceTransformer -from ..utils.logging import log_error_with_context, log_character_action -from ..utils.config import get_settings +from utils.logging import log_error_with_context, log_character_action +from utils.config import get_settings import logging logger = logging.getLogger(__name__) diff --git a/src/utils/config.py b/src/utils/config.py index 1de3ebe..ed018e6 100644 --- a/src/utils/config.py +++ b/src/utils/config.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) class DatabaseConfig(BaseModel): host: str = "localhost" - port: int = 5432 + port: int = 15432 name: str = "discord_fishbowl" user: str = "postgres" password: str @@ -76,7 +76,13 @@ def load_yaml_config(file_path: str) -> Dict[str, Any]: def replace_env_var(match): var_name = match.group(1) default_value = match.group(2) if match.group(2) else "" - return os.getenv(var_name, default_value) + value = os.getenv(var_name, default_value) + + # Force Discord IDs to be strings by quoting them + if var_name in ['DISCORD_GUILD_ID', 'DISCORD_CHANNEL_ID'] and value and not value.startswith('"'): + value = f'"{value}"' + + return value # Replace ${VAR} and ${VAR:-default} patterns content = re.sub(r'\$\{([^}:]+)(?::([^}]*))?\}', replace_env_var, content) diff --git a/start-admin.sh b/start-admin.sh new file mode 100755 index 0000000..593a628 --- /dev/null +++ b/start-admin.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Discord Fishbowl - Admin Interface + +echo "š Starting Admin Interface..." + +# Activate virtual environment +source "/home/matt/discord-fishbowl/venv/bin/activate" + +# Set Python path +export PYTHONPATH="/home/matt/discord-fishbowl:$PYTHONPATH" + +# Load environment variables +if [ -f "/home/matt/discord-fishbowl/.env" ]; then + export $(cat "/home/matt/discord-fishbowl/.env" | xargs) +fi + +# Start admin interface +python -m src.admin.app diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..cf6281a --- /dev/null +++ b/start.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Discord Fishbowl - Main Application + +echo "š Starting Discord Fishbowl..." + +# Activate virtual environment +source "/home/matt/discord-fishbowl/venv/bin/activate" + +# Set Python path +export PYTHONPATH="/home/matt/discord-fishbowl:$PYTHONPATH" + +# Load environment variables +if [ -f "/home/matt/discord-fishbowl/.env" ]; then + export $(cat "/home/matt/discord-fishbowl/.env" | xargs) +fi + +# Start the application +python -m src.main "$@"