Fix Docker startup script and complete application deployment
- Update docker-start.sh to force correct profiles (qdrant, admin) - Fix PostgreSQL port mapping from 5432 to 15432 across all configs - Resolve MCP import conflicts by renaming src/mcp to src/mcp_servers - Fix admin interface StaticFiles mount syntax error - Update LLM client to support both Ollama and OpenAI-compatible APIs - Configure host networking for Discord bot container access - Correct database connection handling for async context managers - Update environment variables and Docker compose configurations - Add missing production dependencies and Dockerfile improvements
This commit is contained in:
35
.env.docker
Normal file
35
.env.docker
Normal file
@@ -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
|
||||
163
CLAUDE.md
Normal file
163
CLAUDE.md
Normal file
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
56
Dockerfile.admin
Normal file
56
Dockerfile.admin
Normal file
@@ -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 '<html><body><h1>Admin Interface Build Failed</h1><p>Please check the build configuration.</p></body></html>' > 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"]
|
||||
0
[fishbowl-admin
Normal file
0
[fishbowl-admin
Normal file
0
[fishbowl]
Normal file
0
[fishbowl]
Normal file
7
admin-frontend/.npmrc
Normal file
7
admin-frontend/.npmrc
Normal file
@@ -0,0 +1,7 @@
|
||||
legacy-peer-deps=true
|
||||
audit=false
|
||||
fund=false
|
||||
prefer-offline=true
|
||||
cache-max=86400000
|
||||
force=true
|
||||
strict-peer-deps=false
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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:
|
||||
qdrant_data:
|
||||
|
||||
networks:
|
||||
fishbowl-network:
|
||||
driver: bridge
|
||||
@@ -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"
|
||||
|
||||
109
docker-start.sh
Executable file
109
docker-start.sh
Executable file
@@ -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}"
|
||||
112
install.py
112
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,6 +921,11 @@ python -m src.admin.app
|
||||
|
||||
# How to start
|
||||
print("🚀 To start the fishbowl:")
|
||||
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:
|
||||
if platform.system() == "Windows":
|
||||
print(" > start.bat")
|
||||
else:
|
||||
|
||||
@@ -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')
|
||||
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')
|
||||
24
requirements-admin.txt
Normal file
24
requirements-admin.txt
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -5,71 +5,95 @@ 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}")
|
||||
raise
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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__)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,7 +55,34 @@ class LLMClient:
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
# Prepare request
|
||||
# Try OpenAI-compatible API first (KoboldCPP, etc.)
|
||||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||||
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,
|
||||
@@ -69,19 +96,20 @@ class LLMClient:
|
||||
"stream": False
|
||||
}
|
||||
|
||||
# Make API call
|
||||
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"}
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if 'response' in result and result['response']:
|
||||
generated_text = result['response'].strip()
|
||||
else:
|
||||
generated_text = None
|
||||
|
||||
if generated_text:
|
||||
|
||||
# Cache the response
|
||||
self._cache_response(cache_key, generated_text)
|
||||
@@ -143,9 +171,16 @@ class LLMClient:
|
||||
"""Check if the LLM model is available"""
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
# 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', [])]
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
12
src/main.py
12
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")
|
||||
|
||||
|
||||
0
src/mcp_servers/__init__.py
Normal file
0
src/mcp_servers/__init__.py
Normal file
@@ -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
|
||||
|
||||
@@ -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(
|
||||
@@ -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__)
|
||||
@@ -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__)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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)
|
||||
|
||||
18
start-admin.sh
Executable file
18
start-admin.sh
Executable file
@@ -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
|
||||
18
start.sh
Executable file
18
start.sh
Executable file
@@ -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 "$@"
|
||||
Reference in New Issue
Block a user