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 |
|
| Service | Port | Purpose | Admin URL |
|
||||||
|---------|------|---------|-----------|
|
|---------|------|---------|-----------|
|
||||||
| PostgreSQL | 5432 | Main database | - |
|
| PostgreSQL | 15432 | Main database | - |
|
||||||
| Redis | 6379 | Caching & pub/sub | - |
|
| Redis | 6379 | Caching & pub/sub | - |
|
||||||
| ChromaDB | 8000 | Vector embeddings | http://localhost:8000 |
|
| ChromaDB | 8000 | Vector embeddings | http://localhost:8000 |
|
||||||
| PgAdmin | 8080 | Database admin | http://localhost:8080 |
|
| PgAdmin | 8080 | Database admin | http://localhost:8080 |
|
||||||
@@ -73,7 +73,7 @@ docker compose -f docker-compose.services.yml logs -f
|
|||||||
## 🔑 Default Credentials
|
## 🔑 Default Credentials
|
||||||
|
|
||||||
### PostgreSQL
|
### PostgreSQL
|
||||||
- **Host**: localhost:5432
|
- **Host**: localhost:15432
|
||||||
- **Database**: discord_fishbowl
|
- **Database**: discord_fishbowl
|
||||||
- **Username**: postgres
|
- **Username**: postgres
|
||||||
- **Password**: fishbowl_password (configurable)
|
- **Password**: fishbowl_password (configurable)
|
||||||
@@ -122,7 +122,7 @@ When using Docker services, update your Discord Fishbowl configuration:
|
|||||||
"database": {
|
"database": {
|
||||||
"type": "postgresql",
|
"type": "postgresql",
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"port": 5432,
|
"port": 15432,
|
||||||
"name": "discord_fishbowl",
|
"name": "discord_fishbowl",
|
||||||
"username": "postgres",
|
"username": "postgres",
|
||||||
"password": "fishbowl_password"
|
"password": "fishbowl_password"
|
||||||
@@ -145,7 +145,7 @@ When using Docker services, update your Discord Fishbowl configuration:
|
|||||||
|
|
||||||
### Services Won't Start
|
### Services Won't Start
|
||||||
1. Check if Docker is running: `docker info`
|
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`
|
3. Check logs: `./docker-services.sh logs`
|
||||||
|
|
||||||
### Permission Errors
|
### Permission Errors
|
||||||
|
|||||||
@@ -11,9 +11,14 @@ RUN apt-get update && apt-get install -y \
|
|||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r 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 application code
|
||||||
COPY src/ ./src/
|
COPY src/ ./src/
|
||||||
COPY config/ ./config/
|
COPY config/ ./config/
|
||||||
|
COPY migrations/ ./migrations/
|
||||||
|
COPY alembic.ini ./
|
||||||
|
|
||||||
# Create logs directory
|
# Create logs directory
|
||||||
RUN mkdir -p logs
|
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-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.8.0",
|
"react-router-dom": "^6.8.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^4.9.5",
|
||||||
"web-vitals": "^3.0.0",
|
"web-vitals": "^3.0.0",
|
||||||
"@tailwindcss/forms": "^0.5.0",
|
"@tailwindcss/forms": "^0.5.0",
|
||||||
"tailwindcss": "^3.3.0",
|
"tailwindcss": "^3.3.0",
|
||||||
@@ -55,5 +55,17 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.0.0"
|
"@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"
|
"proxy": "http://localhost:8000"
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ prepend_sys_path = .
|
|||||||
# are written from script.py.mako
|
# are written from script.py.mako
|
||||||
# output_encoding = utf-8
|
# 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]
|
||||||
# post_write_hooks defines scripts or Python functions that are run
|
# 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": {
|
"discord": {
|
||||||
"token": "test_token_placeholder",
|
"bot_token": "MTM5MDkxODI2MDc5NDU5MzM0NQ.GVlKpo.TrF51dlBv-3uJcscrK9xzs0CLqvakKePCCU350",
|
||||||
"application_id": "123456789",
|
"guild_id": "110670463348260864",
|
||||||
"guild_id": "987654321",
|
"channel_id": "312806692717068288"
|
||||||
"channel_id": "111222333"
|
|
||||||
},
|
},
|
||||||
"conversation": {
|
"database": {
|
||||||
"min_delay_seconds": 30,
|
"type": "postgresql",
|
||||||
"max_delay_seconds": 180,
|
"host": "localhost",
|
||||||
"max_conversation_length": 20,
|
"port": 15432,
|
||||||
"quiet_hours_start": 23,
|
"name": "discord_fishbowl",
|
||||||
"quiet_hours_end": 7
|
"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": {
|
"admin": {
|
||||||
"host": "localhost",
|
"enabled": true,
|
||||||
"port": 8000,
|
"host": "0.0.0.0",
|
||||||
"secret_key": "test-secret-key",
|
"port": 8294,
|
||||||
"cors_origins": ["http://localhost:3000"]
|
"secret_key": "CAKUZ5ds49B1PUEWDWt07TdgxjTtDvvxOOkvOOfbnDE",
|
||||||
},
|
"admin_username": "admin",
|
||||||
"vector_store": {
|
"admin_password": "FIre!@34"
|
||||||
"storage_path": "./data/vector_stores",
|
|
||||||
"collection_name": "fishbowl_memories"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ discord:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
host: ${DB_HOST:-localhost}
|
host: ${DB_HOST:-localhost}
|
||||||
port: ${DB_PORT:-5432}
|
port: ${DB_PORT:-15432}
|
||||||
name: ${DB_NAME:-discord_fishbowl}
|
name: ${DB_NAME:-discord_fishbowl}
|
||||||
user: ${DB_USER:-postgres}
|
user: ${DB_USER:-postgres}
|
||||||
password: ${DB_PASSWORD}
|
password: ${DB_PASSWORD}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ services:
|
|||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
- ./init-scripts:/docker-entrypoint-initdb.d
|
- ./init-scripts:/docker-entrypoint-initdb.d
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "15432:5432"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15
|
image: postgres:15
|
||||||
@@ -10,13 +8,15 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "15432:5432"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- fishbowl-network
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
@@ -31,39 +31,134 @@ services:
|
|||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- fishbowl-network
|
||||||
|
|
||||||
# ChromaDB for vector storage
|
# ChromaDB for vector storage
|
||||||
chromadb:
|
chromadb:
|
||||||
image: chromadb/chroma:latest
|
image: chromadb/chroma:latest
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8001:8000"
|
||||||
volumes:
|
volumes:
|
||||||
- chroma_data:/chroma/chroma
|
- chroma_data:/chroma/chroma
|
||||||
environment:
|
environment:
|
||||||
- IS_PERSISTENT=TRUE
|
- IS_PERSISTENT=TRUE
|
||||||
restart: unless-stopped
|
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:
|
fishbowl:
|
||||||
build: .
|
build: .
|
||||||
|
network_mode: host
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
postgres:
|
||||||
- redis
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
environment:
|
environment:
|
||||||
DB_HOST: postgres
|
# Database configuration
|
||||||
REDIS_HOST: redis
|
DATABASE_URL: postgresql+asyncpg://postgres:${DB_PASSWORD:-fishbowl_password}@localhost:15432/discord_fishbowl
|
||||||
DB_PASSWORD: ${DB_PASSWORD}
|
DB_HOST: localhost
|
||||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
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_BOT_TOKEN: ${DISCORD_BOT_TOKEN}
|
||||||
DISCORD_GUILD_ID: ${DISCORD_GUILD_ID}
|
DISCORD_GUILD_ID: "${DISCORD_GUILD_ID}"
|
||||||
DISCORD_CHANNEL_ID: ${DISCORD_CHANNEL_ID}
|
DISCORD_CHANNEL_ID: "${DISCORD_CHANNEL_ID}"
|
||||||
LLM_BASE_URL: ${LLM_BASE_URL}
|
|
||||||
LLM_MODEL: ${LLM_MODEL}
|
# 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:
|
volumes:
|
||||||
- ./logs:/app/logs
|
- ./logs:/app/logs
|
||||||
- ./config:/app/config
|
- ./config:/app/config
|
||||||
restart: unless-stopped
|
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:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
chroma_data:
|
chroma_data:
|
||||||
|
qdrant_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
fishbowl-network:
|
||||||
|
driver: bridge
|
||||||
@@ -65,7 +65,7 @@ EOF
|
|||||||
echo -e "${GREEN}✅ Services started successfully!${NC}"
|
echo -e "${GREEN}✅ Services started successfully!${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Services available at:"
|
echo "Services available at:"
|
||||||
echo " 📊 PostgreSQL: localhost:5432"
|
echo " 📊 PostgreSQL: localhost:15432"
|
||||||
echo " 🔴 Redis: localhost:6379"
|
echo " 🔴 Redis: localhost:6379"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Run '$0 status' to check service health"
|
echo "Run '$0 status' to check service health"
|
||||||
@@ -90,7 +90,7 @@ EOF
|
|||||||
echo -e "${GREEN}✅ Services started successfully!${NC}"
|
echo -e "${GREEN}✅ Services started successfully!${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Services available at:"
|
echo "Services available at:"
|
||||||
echo " 📊 PostgreSQL: localhost:5432"
|
echo " 📊 PostgreSQL: localhost:15432"
|
||||||
echo " 🔴 Redis: localhost:6379"
|
echo " 🔴 Redis: localhost:6379"
|
||||||
echo " 🧠 ChromaDB: http://localhost:8000"
|
echo " 🧠 ChromaDB: http://localhost:8000"
|
||||||
echo " 🌐 PgAdmin: http://localhost:8080"
|
echo " 🌐 PgAdmin: http://localhost:8080"
|
||||||
@@ -116,7 +116,7 @@ EOF
|
|||||||
echo -e "${GREEN}✅ Services started successfully!${NC}"
|
echo -e "${GREEN}✅ Services started successfully!${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Services available at:"
|
echo "Services available at:"
|
||||||
echo " 📊 PostgreSQL: localhost:5432"
|
echo " 📊 PostgreSQL: localhost:15432"
|
||||||
echo " 🔴 Redis: localhost:6379"
|
echo " 🔴 Redis: localhost:6379"
|
||||||
echo " 🧠 ChromaDB: http://localhost:8000"
|
echo " 🧠 ChromaDB: http://localhost:8000"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -140,7 +140,7 @@ EOF
|
|||||||
echo -e "${GREEN}✅ Services started successfully!${NC}"
|
echo -e "${GREEN}✅ Services started successfully!${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Services available at:"
|
echo "Services available at:"
|
||||||
echo " 📊 PostgreSQL: localhost:5432"
|
echo " 📊 PostgreSQL: localhost:15432"
|
||||||
echo " 🔴 Redis: localhost:6379"
|
echo " 🔴 Redis: localhost:6379"
|
||||||
echo " 🔍 Qdrant: http://localhost:6333"
|
echo " 🔍 Qdrant: http://localhost:6333"
|
||||||
echo " Dashboard: http://localhost:6333/dashboard"
|
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"
|
self.python_executable, "-m", "pip", "install", "-r", "requirements.txt"
|
||||||
], check=True)
|
], 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")
|
self.print_success("All Python dependencies installed successfully")
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
self.print_error(f"Failed to install dependencies: {e}")
|
self.print_error(f"Failed to install dependencies: {e}")
|
||||||
@@ -294,7 +300,7 @@ class FishbowlSetup:
|
|||||||
self.config["database"] = {
|
self.config["database"] = {
|
||||||
"type": "postgresql",
|
"type": "postgresql",
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"port": 5432,
|
"port": 15432,
|
||||||
"name": "discord_fishbowl",
|
"name": "discord_fishbowl",
|
||||||
"username": "postgres",
|
"username": "postgres",
|
||||||
"password": self.ask_question("Database password", "fishbowl_password"),
|
"password": self.ask_question("Database password", "fishbowl_password"),
|
||||||
@@ -305,7 +311,7 @@ class FishbowlSetup:
|
|||||||
self.config["database"] = {
|
self.config["database"] = {
|
||||||
"type": "postgresql",
|
"type": "postgresql",
|
||||||
"host": self.ask_question("PostgreSQL host", "localhost"),
|
"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"),
|
"name": self.ask_question("Database name", "discord_fishbowl"),
|
||||||
"username": self.ask_question("Database username", "postgres"),
|
"username": self.ask_question("Database username", "postgres"),
|
||||||
"password": self.ask_question("Database password", secret=True),
|
"password": self.ask_question("Database password", secret=True),
|
||||||
@@ -378,7 +384,7 @@ class FishbowlSetup:
|
|||||||
self.config["vector_db"] = {
|
self.config["vector_db"] = {
|
||||||
"type": "chromadb",
|
"type": "chromadb",
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"port": 8000,
|
"port": 8001,
|
||||||
"use_docker": True
|
"use_docker": True
|
||||||
}
|
}
|
||||||
self.use_docker_services = True
|
self.use_docker_services = True
|
||||||
@@ -738,10 +744,17 @@ python -m src.admin.app
|
|||||||
f.write(docker_env_content)
|
f.write(docker_env_content)
|
||||||
self.print_success("Docker environment file created")
|
self.print_success("Docker environment file created")
|
||||||
|
|
||||||
# Start Docker services
|
# Ask which Docker setup to use
|
||||||
if self.ask_yes_no("Start Docker services now?", True):
|
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:
|
try:
|
||||||
self.print_info("Starting PostgreSQL and Redis containers...")
|
self.print_info("Starting PostgreSQL, Redis, and ChromaDB containers...")
|
||||||
subprocess.run([
|
subprocess.run([
|
||||||
"docker", "compose", "-f", "docker-compose.services.yml",
|
"docker", "compose", "-f", "docker-compose.services.yml",
|
||||||
"--env-file", ".env.docker", "up", "-d"
|
"--env-file", ".env.docker", "up", "-d"
|
||||||
@@ -759,8 +772,46 @@ python -m src.admin.app
|
|||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
self.print_error(f"Failed to start Docker services: {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")
|
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:
|
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:
|
def create_docker_env_content(self) -> str:
|
||||||
"""Create Docker environment file content"""
|
"""Create Docker environment file content"""
|
||||||
@@ -770,22 +821,64 @@ python -m src.admin.app
|
|||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Database configuration
|
||||||
if self.config["database"].get("use_docker"):
|
if self.config["database"].get("use_docker"):
|
||||||
lines.extend([
|
lines.extend([
|
||||||
|
"# Database",
|
||||||
f"DB_PASSWORD={self.config['database']['password']}",
|
f"DB_PASSWORD={self.config['database']['password']}",
|
||||||
"",
|
"",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# Redis configuration
|
||||||
if self.config["redis"].get("use_docker"):
|
if self.config["redis"].get("use_docker"):
|
||||||
lines.extend([
|
lines.extend([
|
||||||
|
"# Redis",
|
||||||
f"REDIS_PASSWORD={self.config['redis']['password']}",
|
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([
|
lines.extend([
|
||||||
"# Optional PgAdmin credentials (if using --profile admin)",
|
"# Optional PgAdmin credentials (if using --profile admin)",
|
||||||
"PGADMIN_PASSWORD=admin123",
|
"PGADMIN_PASSWORD=admin123",
|
||||||
"",
|
"",
|
||||||
|
"# Logging",
|
||||||
|
"LOG_LEVEL=INFO",
|
||||||
|
"",
|
||||||
])
|
])
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
@@ -828,6 +921,11 @@ python -m src.admin.app
|
|||||||
|
|
||||||
# How to start
|
# How to start
|
||||||
print("🚀 To start the fishbowl:")
|
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":
|
if platform.system() == "Windows":
|
||||||
print(" > start.bat")
|
print(" > start.bat")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Add creative projects tables
|
"""Initial tables including creative projects
|
||||||
|
|
||||||
Revision ID: 004
|
Revision ID: 004
|
||||||
Revises: 003
|
Revises: None
|
||||||
Create Date: 2024-12-20 12:00:00.000000
|
Create Date: 2024-12-20 12:00:00.000000
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -11,11 +11,130 @@ from sqlalchemy.dialects import postgresql
|
|||||||
|
|
||||||
# revision identifiers
|
# revision identifiers
|
||||||
revision = '004'
|
revision = '004'
|
||||||
down_revision = '003'
|
down_revision = None
|
||||||
branch_labels = None
|
branch_labels = None
|
||||||
depends_on = None
|
depends_on = None
|
||||||
|
|
||||||
def upgrade():
|
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
|
# Create creative_projects table
|
||||||
op.create_table('creative_projects',
|
op.create_table('creative_projects',
|
||||||
sa.Column('id', sa.String(255), primary_key=True, index=True),
|
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'])
|
op.create_index('ix_invitations_project', 'project_invitations', ['project_id', 'created_at'])
|
||||||
|
|
||||||
def downgrade():
|
def downgrade():
|
||||||
# Drop tables in reverse order
|
# Drop tables in reverse order of creation
|
||||||
op.drop_table('project_invitations')
|
op.drop_table('project_invitations')
|
||||||
op.drop_table('project_contributions')
|
op.drop_table('project_contributions')
|
||||||
op.drop_table('project_collaborators')
|
op.drop_table('project_collaborators')
|
||||||
op.drop_table('creative_projects')
|
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
|
numpy>=1.26.0
|
||||||
faiss-cpu>=1.8.0
|
faiss-cpu>=1.8.0
|
||||||
|
|
||||||
# MCP Integration (remove non-existent packages)
|
# MCP Integration
|
||||||
|
mcp>=1.0.0
|
||||||
aiofiles>=23.2.0
|
aiofiles>=23.2.0
|
||||||
watchdog>=3.0.0
|
watchdog>=3.0.0
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ nltk>=3.8.1
|
|||||||
fastapi>=0.104.1
|
fastapi>=0.104.1
|
||||||
uvicorn>=0.24.0
|
uvicorn>=0.24.0
|
||||||
python-multipart>=0.0.6
|
python-multipart>=0.0.6
|
||||||
|
pyjwt>=2.8.0
|
||||||
python-jose[cryptography]>=3.3.0
|
python-jose[cryptography]>=3.3.0
|
||||||
passlib[bcrypt]>=1.7.4
|
passlib[bcrypt]>=1.7.4
|
||||||
websockets>=12.0
|
websockets>=12.0
|
||||||
|
|||||||
@@ -5,71 +5,95 @@ Initialize characters in the database from configuration
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Load environment variables
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
# Add src to Python path
|
# Add src to Python path
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||||
|
|
||||||
from database.connection import init_database, get_db_session
|
from database.models import Character, Base
|
||||||
from database.models import Character
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||||
from utils.config import get_character_settings
|
|
||||||
from utils.logging import setup_logging
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
import yaml
|
||||||
import logging
|
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():
|
async def init_characters():
|
||||||
"""Initialize characters from configuration"""
|
"""Initialize characters from configuration"""
|
||||||
try:
|
try:
|
||||||
logger.info("Initializing database connection...")
|
# Get database URL from environment and convert to async format
|
||||||
await init_database()
|
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...")
|
logger.info("Loading character configuration...")
|
||||||
character_settings = get_character_settings()
|
character_config = load_character_config()
|
||||||
|
|
||||||
async with get_db_session() as session:
|
async with session_factory() as session:
|
||||||
for char_config in character_settings.characters:
|
for char_data in character_config.get('characters', []):
|
||||||
# Check if character already exists
|
# 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)
|
existing = await session.scalar(query)
|
||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
logger.info(f"Character '{char_config.name}' already exists, skipping...")
|
logger.info(f"Character '{char_data['name']}' already exists, skipping...")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Create system prompt
|
# 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."""
|
Be natural, engaging, and authentic in all your interactions."""
|
||||||
|
|
||||||
# Create character
|
# Create character
|
||||||
character = Character(
|
character = Character(
|
||||||
name=char_config.name,
|
name=char_data['name'],
|
||||||
personality=char_config.personality,
|
personality=char_data['personality'],
|
||||||
system_prompt=system_prompt,
|
system_prompt=system_prompt,
|
||||||
interests=char_config.interests,
|
interests=char_data['interests'],
|
||||||
speaking_style=char_config.speaking_style,
|
speaking_style=char_data['speaking_style'],
|
||||||
background=char_config.background,
|
background=char_data['background'],
|
||||||
avatar_url=char_config.avatar_url or "",
|
avatar_url=char_data.get('avatar_url', ""),
|
||||||
is_active=True
|
is_active=True
|
||||||
)
|
)
|
||||||
|
|
||||||
session.add(character)
|
session.add(character)
|
||||||
logger.info(f"Created character: {char_config.name}")
|
logger.info(f"Created character: {char_data['name']}")
|
||||||
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
logger.info("✅ Character initialization completed successfully!")
|
logger.info("✅ Character initialization completed successfully!")
|
||||||
|
|
||||||
|
await engine.dispose()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to initialize characters: {e}")
|
logger.error(f"Failed to initialize characters: {e}")
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
import socketio
|
import socketio
|
||||||
|
|
||||||
from ..database.connection import init_database, get_db_session
|
from database.connection import init_database, get_db_session
|
||||||
from ..database.models import Character, Conversation, Message, Memory, CharacterRelationship
|
from database.models import Character, Conversation, Message, Memory, CharacterRelationship
|
||||||
from ..utils.config import get_settings
|
from utils.config import get_settings
|
||||||
from ..utils.logging import setup_logging
|
from utils.logging import setup_logging_interceptor
|
||||||
from .models import (
|
from .models import (
|
||||||
AdminUser, DashboardMetrics, CharacterProfile, ConversationSummary,
|
AdminUser, DashboardMetrics, CharacterProfile, ConversationSummary,
|
||||||
SystemStatus, AnalyticsData
|
SystemStatus, AnalyticsData
|
||||||
@@ -346,7 +346,7 @@ socket_app = websocket_manager.get_app()
|
|||||||
app.mount("/socket.io", socket_app)
|
app.mount("/socket.io", socket_app)
|
||||||
|
|
||||||
# Serve React frontend
|
# 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("/")
|
@app.get("/")
|
||||||
async def root():
|
async def root():
|
||||||
@@ -354,10 +354,12 @@ async def root():
|
|||||||
return {"message": "Discord Fishbowl Admin Interface", "admin_url": "/admin", "socket_url": "/socket.io"}
|
return {"message": "Discord Fishbowl Admin Interface", "admin_url": "/admin", "socket_url": "/socket.io"}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
import os
|
||||||
|
admin_port = int(os.getenv("ADMIN_PORT", "8000"))
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
"src.admin.app:app",
|
"src.admin.app:app",
|
||||||
host="0.0.0.0",
|
host="0.0.0.0",
|
||||||
port=8000,
|
port=admin_port,
|
||||||
reload=True,
|
reload=True,
|
||||||
log_level="info"
|
log_level="info"
|
||||||
)
|
)
|
||||||
@@ -10,7 +10,7 @@ from typing import Optional, Dict, Any
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from ..utils.config import get_settings
|
from utils.config import get_settings
|
||||||
from .models import AdminUser
|
from .models import AdminUser
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ from typing import Dict, List, Any, Optional
|
|||||||
from collections import defaultdict, Counter
|
from collections import defaultdict, Counter
|
||||||
|
|
||||||
from sqlalchemy import select, func, and_, or_, desc
|
from sqlalchemy import select, func, and_, or_, desc
|
||||||
from ...database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ...database.models import Character, Conversation, Message, CharacterRelationship
|
from database.models import Character, Conversation, Message, CharacterRelationship
|
||||||
from ..models import (
|
from admin.models import (
|
||||||
TopicTrend, RelationshipAnalytics, CommunityHealth,
|
TopicTrend, RelationshipAnalytics, CommunityHealth,
|
||||||
EngagementMetrics, Relationship
|
EngagementMetrics, Relationship
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ from typing import List, Dict, Any, Optional
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from sqlalchemy import select, func, and_, or_, desc, asc
|
from sqlalchemy import select, func, and_, or_, desc, asc
|
||||||
from ...database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ...database.models import Character, Message, Memory, CharacterRelationship, CharacterEvolution
|
from database.models import Character, Message, Memory, CharacterRelationship, CharacterEvolution
|
||||||
from ..models import (
|
from admin.models import (
|
||||||
CharacterProfile, CharacterStatusEnum, PersonalityEvolution,
|
CharacterProfile, CharacterStatusEnum, PersonalityEvolution,
|
||||||
Relationship, MemorySummary, CreativeWork
|
Relationship, MemorySummary, CreativeWork
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ from typing import List, Dict, Any, Optional
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from sqlalchemy import select, func, and_, or_, desc, asc, text
|
from sqlalchemy import select, func, and_, or_, desc, asc, text
|
||||||
from ...database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ...database.models import Conversation, Message, Character
|
from database.models import Conversation, Message, Character
|
||||||
from ..models import ConversationSummary, ConversationDetail, SearchResult
|
from admin.models import ConversationSummary, ConversationDetail, SearchResult
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ from collections import deque
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from sqlalchemy import select, func, and_, desc
|
from sqlalchemy import select, func, and_, desc
|
||||||
from ...database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ...database.models import Character, Conversation, Message, Memory
|
from database.models import Character, Conversation, Message, Memory
|
||||||
from ..models import DashboardMetrics, ActivityEvent, ActivityType
|
from admin.models import DashboardMetrics, ActivityEvent, ActivityType
|
||||||
from .websocket_manager import WebSocketManager
|
from admin.services.websocket_manager import WebSocketManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from typing import Dict, List, Any, Optional
|
|||||||
import psutil
|
import psutil
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from ..models import SystemStatus, SystemStatusEnum, SystemConfiguration, LogEntry
|
from admin.models import SystemStatus, SystemStatusEnum, SystemConfiguration, LogEntry
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import asyncio
|
|||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from ..utils.config import get_settings
|
from utils.config import get_settings
|
||||||
from ..utils.logging import log_error_with_context, log_system_health
|
from utils.logging import log_error_with_context, log_system_health
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Message, Conversation, Character
|
from database.models import Message, Conversation, Character
|
||||||
from sqlalchemy import select, and_
|
from sqlalchemy import select, and_
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from ..utils.logging import log_error_with_context, log_character_action
|
from utils.logging import log_error_with_context, log_character_action
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Character, Message, Conversation
|
from database.models import Character, Message, Conversation
|
||||||
from sqlalchemy import select, and_, or_
|
from sqlalchemy import select, and_, or_
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import json
|
|||||||
from typing import Dict, Any, List, Optional, Tuple
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from dataclasses import dataclass, asdict
|
from dataclasses import dataclass, asdict
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Character as CharacterModel, Memory, CharacterRelationship, Message, CharacterEvolution
|
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 utils.logging import log_character_action, log_error_with_context, log_autonomous_decision, log_memory_operation
|
||||||
from sqlalchemy import select, and_, or_, func, desc
|
from sqlalchemy import select, and_, or_, func, desc
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|||||||
@@ -4,18 +4,18 @@ from typing import Dict, Any, List, Optional, Tuple
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from .character import Character
|
from characters.character import Character
|
||||||
from .personality import PersonalityManager
|
from characters.personality import PersonalityManager
|
||||||
from .memory import MemoryManager
|
from characters.memory import MemoryManager
|
||||||
from ..rag.personal_memory import PersonalMemoryRAG, MemoryInsight
|
from rag.personal_memory import PersonalMemoryRAG, MemoryInsight
|
||||||
from ..rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
||||||
from ..rag.memory_sharing import MemorySharingManager, SharePermissionLevel
|
from rag.memory_sharing import MemorySharingManager, SharePermissionLevel
|
||||||
from ..mcp.self_modification_server import SelfModificationMCPServer
|
from mcp_servers.self_modification_server import SelfModificationMCPServer
|
||||||
from ..mcp.file_system_server import CharacterFileSystemMCP
|
from mcp_servers.file_system_server import CharacterFileSystemMCP
|
||||||
from ..mcp.memory_sharing_server import MemorySharingMCPServer
|
from mcp_servers.memory_sharing_server import MemorySharingMCPServer
|
||||||
from ..mcp.creative_projects_server import CreativeProjectsMCPServer
|
from mcp_servers.creative_projects_server import CreativeProjectsMCPServer
|
||||||
from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision
|
from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision
|
||||||
from ..database.models import Character as CharacterModel
|
from database.models import Character as CharacterModel
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import json
|
|||||||
from typing import Dict, Any, List, Optional, Tuple
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Memory, Character, Message, CharacterRelationship
|
from database.models import Memory, Character, Message, CharacterRelationship
|
||||||
from ..utils.logging import log_memory_operation, log_error_with_context
|
from utils.logging import log_memory_operation, log_error_with_context
|
||||||
from sqlalchemy import select, and_, or_, func, desc
|
from sqlalchemy import select, and_, or_, func, desc
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import json
|
|||||||
import random
|
import random
|
||||||
from typing import Dict, Any, List, Optional, Tuple
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from ..utils.logging import log_character_action, log_error_with_context
|
from utils.logging import log_character_action, log_error_with_context
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import CharacterEvolution, Character as CharacterModel
|
from database.models import CharacterEvolution, Character as CharacterModel
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
class PersonalityManager:
|
class PersonalityManager:
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ from dataclasses import dataclass, asdict
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from ..rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
||||||
from ..rag.memory_sharing import MemorySharingManager
|
from rag.memory_sharing import MemorySharingManager
|
||||||
from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision
|
from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import (
|
from database.models import (
|
||||||
Character, CreativeProject as DBCreativeProject, ProjectCollaborator,
|
Character, CreativeProject as DBCreativeProject, ProjectCollaborator,
|
||||||
ProjectContribution as DBProjectContribution, ProjectInvitation as DBProjectInvitation
|
ProjectContribution as DBProjectContribution, ProjectInvitation as DBProjectInvitation
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ from dataclasses import dataclass, asdict
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Character as CharacterModel, Conversation, Message, Memory
|
from database.models import Character as CharacterModel, Conversation, Message, Memory
|
||||||
from ..characters.character import Character
|
from characters.character import Character
|
||||||
from ..characters.enhanced_character import EnhancedCharacter
|
from characters.enhanced_character import EnhancedCharacter
|
||||||
from ..llm.client import llm_client, prompt_manager
|
from llm.client import llm_client, prompt_manager
|
||||||
from ..llm.prompt_manager import advanced_prompt_manager
|
from llm.prompt_manager import advanced_prompt_manager
|
||||||
from ..utils.config import get_settings, get_character_settings
|
from utils.config import get_settings, get_character_settings
|
||||||
from ..utils.logging import (log_conversation_event, log_character_action,
|
from utils.logging import (log_conversation_event, log_character_action,
|
||||||
log_autonomous_decision, log_error_with_context)
|
log_autonomous_decision, log_error_with_context)
|
||||||
from sqlalchemy import select, and_, or_, func, desc
|
from sqlalchemy import select, and_, or_, func, desc
|
||||||
|
|
||||||
@@ -402,8 +402,8 @@ class ConversationEngine:
|
|||||||
# Use EnhancedCharacter if RAG systems are available
|
# Use EnhancedCharacter if RAG systems are available
|
||||||
if self.vector_store and self.memory_sharing_manager:
|
if self.vector_store and self.memory_sharing_manager:
|
||||||
# Find the appropriate MCP servers for this character
|
# Find the appropriate MCP servers for this character
|
||||||
from ..mcp.self_modification_server import mcp_server
|
from mcp.self_modification_server import mcp_server
|
||||||
from ..mcp.file_system_server import filesystem_server
|
from mcp.file_system_server import filesystem_server
|
||||||
|
|
||||||
# Find creative projects MCP server
|
# Find creative projects MCP server
|
||||||
creative_projects_mcp = None
|
creative_projects_mcp = None
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ from dataclasses import dataclass
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ..utils.logging import log_autonomous_decision, log_error_with_context, log_system_health
|
from utils.logging import log_autonomous_decision, log_error_with_context, log_system_health
|
||||||
from ..utils.config import get_settings
|
from utils.config import get_settings
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ from sqlalchemy.orm import sessionmaker
|
|||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import AsyncGenerator, Optional
|
||||||
import logging
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -87,7 +90,7 @@ class DatabaseManager:
|
|||||||
db_manager = DatabaseManager()
|
db_manager = DatabaseManager()
|
||||||
|
|
||||||
# Convenience functions
|
# Convenience functions
|
||||||
async def get_db_session():
|
def get_db_session():
|
||||||
return db_manager.get_session()
|
return db_manager.get_session()
|
||||||
|
|
||||||
async def init_database():
|
async def init_database():
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class Character(Base):
|
|||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
messages = relationship("Message", back_populates="character", foreign_keys="Message.character_id")
|
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_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")
|
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")
|
evolution_history = relationship("CharacterEvolution", back_populates="character", cascade="all, delete-orphan")
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import json
|
|||||||
import time
|
import time
|
||||||
from typing import Dict, Any, Optional, List
|
from typing import Dict, Any, Optional, List
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from ..utils.config import get_settings
|
from utils.config import get_settings
|
||||||
from ..utils.logging import log_llm_interaction, log_error_with_context, log_system_health
|
from utils.logging import log_llm_interaction, log_error_with_context, log_system_health
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -55,7 +55,34 @@ class LLMClient:
|
|||||||
|
|
||||||
start_time = time.time()
|
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 = {
|
request_data = {
|
||||||
"model": self.model,
|
"model": self.model,
|
||||||
"prompt": prompt,
|
"prompt": prompt,
|
||||||
@@ -69,19 +96,20 @@ class LLMClient:
|
|||||||
"stream": False
|
"stream": False
|
||||||
}
|
}
|
||||||
|
|
||||||
# Make API call
|
|
||||||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
f"{self.base_url}/api/generate",
|
f"{self.base_url}/api/generate",
|
||||||
json=request_data,
|
json=request_data,
|
||||||
headers={"Content-Type": "application/json"}
|
headers={"Content-Type": "application/json"}
|
||||||
)
|
)
|
||||||
|
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
if 'response' in result and result['response']:
|
if 'response' in result and result['response']:
|
||||||
generated_text = result['response'].strip()
|
generated_text = result['response'].strip()
|
||||||
|
else:
|
||||||
|
generated_text = None
|
||||||
|
|
||||||
|
if generated_text:
|
||||||
|
|
||||||
# Cache the response
|
# Cache the response
|
||||||
self._cache_response(cache_key, generated_text)
|
self._cache_response(cache_key, generated_text)
|
||||||
@@ -143,9 +171,16 @@ class LLMClient:
|
|||||||
"""Check if the LLM model is available"""
|
"""Check if the LLM model is available"""
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(timeout=10) as client:
|
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 = await client.get(f"{self.base_url}/api/tags")
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
models = response.json()
|
models = response.json()
|
||||||
available_models = [model.get('name', '') for model in models.get('models', [])]
|
available_models = [model.get('name', '') for model in models.get('models', [])]
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import json
|
|||||||
import re
|
import re
|
||||||
from typing import Dict, Any, List, Optional, Tuple
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from ..utils.logging import log_error_with_context
|
from utils.logging import log_error_with_context
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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.community_knowledge import initialize_community_knowledge_rag
|
||||||
from rag.memory_sharing import MemorySharingManager
|
from rag.memory_sharing import MemorySharingManager
|
||||||
from collaboration.creative_projects import CollaborativeCreativeManager
|
from collaboration.creative_projects import CollaborativeCreativeManager
|
||||||
from mcp.self_modification_server import mcp_server
|
from mcp_servers.self_modification_server import mcp_server
|
||||||
from mcp.file_system_server import filesystem_server
|
from mcp_servers.file_system_server import filesystem_server
|
||||||
from mcp.calendar_server import calendar_server
|
from mcp_servers.calendar_server import calendar_server
|
||||||
from mcp.memory_sharing_server import initialize_memory_sharing_mcp_server
|
from mcp_servers.memory_sharing_server import MemorySharingMCPServer
|
||||||
from mcp.creative_projects_server import initialize_creative_projects_mcp_server
|
from mcp_servers.creative_projects_server import initialize_creative_projects_mcp_server
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Setup logging first
|
# Setup logging first
|
||||||
@@ -118,7 +118,7 @@ class FishbowlApplication:
|
|||||||
logger.info("Calendar/time awareness MCP server initialized")
|
logger.info("Calendar/time awareness MCP server initialized")
|
||||||
|
|
||||||
# Initialize memory sharing MCP server
|
# 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)
|
self.mcp_servers.append(memory_sharing_mcp)
|
||||||
logger.info("Memory sharing MCP server initialized")
|
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.server import Server
|
||||||
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
||||||
|
|
||||||
from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision
|
from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Character, Message, Conversation
|
from database.models import Character, Message, Conversation
|
||||||
from sqlalchemy import select, and_, or_, func, desc
|
from sqlalchemy import select, and_, or_, func, desc
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -14,21 +14,18 @@ from mcp.server.models import InitializationOptions
|
|||||||
from mcp.server.stdio import stdio_server
|
from mcp.server.stdio import stdio_server
|
||||||
from mcp.types import (
|
from mcp.types import (
|
||||||
CallToolRequestParams,
|
CallToolRequestParams,
|
||||||
GetToolRequestParams,
|
ListToolsRequest,
|
||||||
ListToolsRequestParams,
|
|
||||||
TextContent,
|
TextContent,
|
||||||
Tool,
|
Tool
|
||||||
INVALID_PARAMS,
|
|
||||||
INTERNAL_ERROR
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..collaboration.creative_projects import (
|
from collaboration.creative_projects import (
|
||||||
CollaborativeCreativeManager,
|
CollaborativeCreativeManager,
|
||||||
ProjectType,
|
ProjectType,
|
||||||
ContributionType,
|
ContributionType,
|
||||||
ProjectStatus
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -47,7 +44,7 @@ class CreativeProjectsMCPServer:
|
|||||||
"""Register all creative project tools"""
|
"""Register all creative project tools"""
|
||||||
|
|
||||||
@self.server.list_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"""
|
"""List available creative project tools"""
|
||||||
return [
|
return [
|
||||||
Tool(
|
Tool(
|
||||||
@@ -11,8 +11,8 @@ from mcp.server.stdio import stdio_server
|
|||||||
from mcp.server import Server
|
from mcp.server import Server
|
||||||
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
||||||
|
|
||||||
from ..utils.logging import log_character_action, log_error_with_context
|
from utils.logging import log_character_action, log_error_with_context
|
||||||
from ..rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -16,12 +16,12 @@ from mcp.types import (
|
|||||||
LoggingLevel
|
LoggingLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..rag.memory_sharing import (
|
from rag.memory_sharing import (
|
||||||
MemorySharingManager, SharePermissionLevel, ShareRequestStatus,
|
MemorySharingManager, SharePermissionLevel, ShareRequestStatus,
|
||||||
SharedMemory, ShareRequest, TrustLevel
|
SharedMemory, ShareRequest, TrustLevel
|
||||||
)
|
)
|
||||||
from ..rag.vector_store import VectorStoreManager
|
from rag.vector_store import VectorStoreManager
|
||||||
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -10,9 +10,9 @@ from mcp.server.stdio import stdio_server
|
|||||||
from mcp.server import Server
|
from mcp.server import Server
|
||||||
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
||||||
|
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Character, CharacterEvolution
|
from database.models import Character, CharacterEvolution
|
||||||
from ..utils.logging import log_character_action, log_error_with_context, log_autonomous_decision
|
from utils.logging import log_character_action, log_error_with_context, log_autonomous_decision
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -5,10 +5,10 @@ from datetime import datetime, timedelta
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from .vector_store import VectorStoreManager, VectorMemory, MemoryType
|
from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
||||||
from ..utils.logging import log_conversation_event, log_error_with_context
|
from utils.logging import log_conversation_event, log_error_with_context
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Conversation, Message, Character
|
from database.models import Conversation, Message, Character
|
||||||
from sqlalchemy import select, and_, or_, func, desc
|
from sqlalchemy import select, and_, or_, func, desc
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ from dataclasses import dataclass, asdict
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from .vector_store import VectorStoreManager, VectorMemory, MemoryType
|
from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
||||||
from .personal_memory import PersonalMemoryRAG, MemoryInsight
|
from rag.personal_memory import PersonalMemoryRAG, MemoryInsight
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Character, CharacterRelationship
|
from database.models import Character, CharacterRelationship
|
||||||
from ..utils.logging import log_character_action, log_error_with_context
|
from utils.logging import log_character_action, log_error_with_context
|
||||||
from sqlalchemy import select, and_
|
from sqlalchemy import select, and_
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ from datetime import datetime, timedelta
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from .vector_store import VectorStoreManager, VectorMemory, MemoryType
|
from rag.vector_store import VectorStoreManager, VectorMemory, MemoryType
|
||||||
from ..utils.logging import log_character_action, log_error_with_context, log_memory_operation
|
from utils.logging import log_character_action, log_error_with_context, log_memory_operation
|
||||||
from ..database.connection import get_db_session
|
from database.connection import get_db_session
|
||||||
from ..database.models import Memory
|
from database.models import Memory
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ from dataclasses import dataclass, asdict
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from sentence_transformers import SentenceTransformer
|
from sentence_transformers import SentenceTransformer
|
||||||
from ..utils.logging import log_error_with_context, log_character_action
|
from utils.logging import log_error_with_context, log_character_action
|
||||||
from ..utils.config import get_settings
|
from utils.config import get_settings
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class DatabaseConfig(BaseModel):
|
class DatabaseConfig(BaseModel):
|
||||||
host: str = "localhost"
|
host: str = "localhost"
|
||||||
port: int = 5432
|
port: int = 15432
|
||||||
name: str = "discord_fishbowl"
|
name: str = "discord_fishbowl"
|
||||||
user: str = "postgres"
|
user: str = "postgres"
|
||||||
password: str
|
password: str
|
||||||
@@ -76,7 +76,13 @@ def load_yaml_config(file_path: str) -> Dict[str, Any]:
|
|||||||
def replace_env_var(match):
|
def replace_env_var(match):
|
||||||
var_name = match.group(1)
|
var_name = match.group(1)
|
||||||
default_value = match.group(2) if match.group(2) else ""
|
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
|
# Replace ${VAR} and ${VAR:-default} patterns
|
||||||
content = re.sub(r'\$\{([^}:]+)(?::([^}]*))?\}', replace_env_var, content)
|
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