diff --git a/.env.docker b/.env.docker index bb1ac14..0176d8a 100644 --- a/.env.docker +++ b/.env.docker @@ -18,13 +18,13 @@ LLM_MODEL=koboldcpp/Broken-Tutu-24B-Transgression-v2.0.i1-Q4_K_M LLM_TIMEOUT=300 LLM_MAX_TOKENS=2000 LLM_TEMPERATURE=0.8 -LLM_MAX_PROMPT_LENGTH=6000 +LLM_MAX_PROMPT_LENGTH=16000 LLM_MAX_HISTORY_MESSAGES=5 LLM_MAX_MEMORIES=5 # Admin Interface ADMIN_PORT=8294 -SECRET_KEY=your-secret-key-here +SECRET_KEY=stable-secret-key-for-jwt-tokens-fishbowl-2025 ADMIN_USERNAME=admin ADMIN_PASSWORD=FIre!@34 diff --git a/.env.example b/.env.example index 14e5c66..9055416 100644 --- a/.env.example +++ b/.env.example @@ -35,7 +35,7 @@ LLM_API_KEY=x LLM_TIMEOUT=300 LLM_MAX_TOKENS=2000 LLM_TEMPERATURE=0.8 -LLM_MAX_PROMPT_LENGTH=6000 +LLM_MAX_PROMPT_LENGTH=16000 LLM_MAX_HISTORY_MESSAGES=5 LLM_MAX_MEMORIES=5 @@ -46,6 +46,29 @@ ADMIN_USERNAME=admin ADMIN_PASSWORD=FIre!@34 SECRET_KEY=CAKUZ5ds49B1PUEWDWt07TdgxjTtDvvxOOkvOOfbnDE +# LLM Provider Configuration +# OpenRouter (supports Claude, GPT, Llama, etc.) +OPENROUTER_ENABLED=false +OPENROUTER_API_KEY= +OPENROUTER_MODEL=anthropic/claude-3-sonnet + +# OpenAI +OPENAI_ENABLED=false +OPENAI_API_KEY= +OPENAI_MODEL=gpt-4o-mini + +# Google Gemini +GEMINI_ENABLED=false +GEMINI_API_KEY= +GEMINI_MODEL=gemini-1.5-flash + +# Custom/Local LLM (current setup) +CUSTOM_LLM_ENABLED=true + +# Ollama +OLLAMA_ENABLED=false +OLLAMA_MODEL=llama3 + # System Configuration CONVERSATION_FREQUENCY=0.5 RESPONSE_DELAY_MIN=1.0 diff --git a/Dockerfile.admin b/Dockerfile.admin index d9b4005..45b43c3 100644 --- a/Dockerfile.admin +++ b/Dockerfile.admin @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* # Install Node.js for frontend build -RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ +RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \ && apt-get install -y nodejs # Copy requirements first for better caching @@ -26,20 +26,30 @@ COPY migrations/ ./migrations/ COPY alembic.ini ./ # Build frontend -COPY admin-frontend/ ./admin-frontend/ +COPY admin-frontend/package*.json ./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 first (better caching) +RUN npm install --silent -# Install dependencies with npm (using .npmrc config) -RUN npm install +# Copy frontend source code +COPY admin-frontend/ ./ -# Build with increased memory for Node.js +# Build with increased memory for Node.js and disable optimization ENV NODE_OPTIONS="--max-old-space-size=4096" -# Build React app or create fallback -RUN npm run build || mkdir -p build -RUN test -f build/index.html || echo "

Discord Fishbowl Admin

Interface loading...

" > build/index.html +ENV GENERATE_SOURCEMAP=false +ENV DISABLE_ESLINT_PLUGIN=true +ENV CI=false +ENV REACT_APP_API_URL="" +ENV PUBLIC_URL="/admin" +ENV TSC_COMPILE_ON_ERROR=true +ENV ESLINT_NO_DEV_ERRORS=true + +# Build React app +RUN npm run build + +# Verify build output +RUN ls -la build/ && test -f build/index.html # Back to main directory WORKDIR /app @@ -51,7 +61,7 @@ RUN mkdir -p logs ENV PYTHONPATH=/app/src # Expose admin port -EXPOSE 8000 +EXPOSE 8294 # Run the admin interface CMD ["python", "-m", "src.admin.app"] \ No newline at end of file diff --git a/admin-frontend/package-simple.json b/admin-frontend/package-simple.json new file mode 100644 index 0000000..2650e05 --- /dev/null +++ b/admin-frontend/package-simple.json @@ -0,0 +1,32 @@ +{ + "name": "discord-fishbowl-admin", + "version": "1.0.0", + "private": true, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.8.0", + "axios": "^1.6.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "devDependencies": { + "react-scripts": "5.0.1" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} \ No newline at end of file diff --git a/admin-frontend/package.json b/admin-frontend/package.json index e1e81e1..f1c69ee 100644 --- a/admin-frontend/package.json +++ b/admin-frontend/package.json @@ -2,6 +2,7 @@ "name": "discord-fishbowl-admin", "version": "1.0.0", "private": true, + "homepage": "/admin", "dependencies": { "@types/node": "^20.0.0", "@types/react": "^18.2.0", @@ -9,7 +10,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.8.0", - "react-scripts": "5.0.1", + "react-scripts": "^5.0.1", "typescript": "^4.9.5", "web-vitals": "^3.0.0", "@tailwindcss/forms": "^0.5.0", @@ -53,15 +54,8 @@ ] }, "devDependencies": { - "@types/jest": "^29.0.0" + "@types/jest": "^29.0.0", + "react-scripts": "5.0.1" }, - "resolutions": { - "schema-utils": "^3.3.0", - "fork-ts-checker-webpack-plugin": "^6.5.3" - }, - "overrides": { - "schema-utils": "^3.3.0", - "fork-ts-checker-webpack-plugin": "^6.5.3" - }, - "proxy": "http://localhost:8294" + "proxy": "http://localhost:8000" } \ No newline at end of file diff --git a/admin-frontend/public/favicon.ico b/admin-frontend/public/favicon.ico new file mode 100644 index 0000000..b6fdd27 Binary files /dev/null and b/admin-frontend/public/favicon.ico differ diff --git a/admin-frontend/src/App.tsx b/admin-frontend/src/App.tsx index 2461636..a064737 100644 --- a/admin-frontend/src/App.tsx +++ b/admin-frontend/src/App.tsx @@ -3,14 +3,12 @@ import { Routes, Route, Navigate } from 'react-router-dom'; import { useAuth } from './contexts/AuthContext'; import Layout from './components/Layout/Layout'; import LoginPage from './pages/LoginPage'; -import Dashboard from './pages/Dashboard'; import Characters from './pages/Characters'; import CharacterDetail from './pages/CharacterDetail'; -import Conversations from './pages/Conversations'; -import ConversationDetail from './pages/ConversationDetail'; -import Analytics from './pages/Analytics'; import SystemStatus from './pages/SystemStatus'; import Settings from './pages/Settings'; +import LiveChat from './pages/LiveChat'; +import Guide from './pages/Guide'; import LoadingSpinner from './components/Common/LoadingSpinner'; function App() { @@ -31,16 +29,14 @@ function App() { return ( - } /> - } /> + } /> } /> } /> - } /> - } /> - } /> - } /> } /> - } /> + } /> + } /> + } /> + } /> ); diff --git a/admin-frontend/src/components/Character/CharacterCreationModal.tsx b/admin-frontend/src/components/Character/CharacterCreationModal.tsx new file mode 100644 index 0000000..adfb5da --- /dev/null +++ b/admin-frontend/src/components/Character/CharacterCreationModal.tsx @@ -0,0 +1,295 @@ +import React, { useState } from 'react'; +import { X, Save, User, Brain, FileText } from 'lucide-react'; +import { apiClient } from '../../services/api'; +import LoadingSpinner from '../Common/LoadingSpinner'; +import toast from 'react-hot-toast'; + +interface Character { + name: string; + status: 'active' | 'idle' | 'reflecting' | 'offline'; + is_active: boolean; + last_active?: string; + personality?: string; + system_prompt?: string; + interests?: string[]; + speaking_style?: string; + background?: string; +} + +interface CharacterCreationModalProps { + isOpen: boolean; + onClose: () => void; + onCharacterCreated: (character: Character) => void; +} + +const CharacterCreationModal: React.FC = ({ + isOpen, + onClose, + onCharacterCreated +}) => { + const [formData, setFormData] = useState({ + name: '', + personality: '', + system_prompt: `You are a character named {{name}}. You have the following personality: {{personality}} + +Your speaking style is {{speaking_style}}. You are interested in {{interests}}. + +Background: {{background}} + +When responding to messages: +1. Stay in character at all times +2. Reference your personality and interests naturally +3. Engage authentically with other characters +4. Show growth and development over time + +Remember to be consistent with your established personality while allowing for natural character development through interactions.`, + interests: '', + speaking_style: '', + background: '', + is_active: true + }); + const [saving, setSaving] = useState(false); + + const handleInputChange = (field: keyof typeof formData, value: any) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + + const handleInterestsChange = (interestsText: string) => { + handleInputChange('interests', interestsText); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!formData.name.trim()) { + toast.error('Character name is required'); + return; + } + + try { + setSaving(true); + + const characterData = { + name: formData.name.trim(), + personality: formData.personality, + system_prompt: formData.system_prompt.replace('{{name}}', formData.name.trim()), + interests: formData.interests.split(',').map(s => s.trim()).filter(s => s.length > 0), + speaking_style: formData.speaking_style, + background: formData.background, + is_active: formData.is_active + }; + + const response = await apiClient.createCharacter(characterData); + + // Create character object for local state + const newCharacter: Character = { + name: characterData.name, + status: characterData.is_active ? 'active' : 'offline', + is_active: characterData.is_active, + personality: characterData.personality, + system_prompt: characterData.system_prompt, + interests: characterData.interests, + speaking_style: characterData.speaking_style, + background: characterData.background, + last_active: new Date().toISOString() + }; + + onCharacterCreated(newCharacter); + toast.success(`Character ${characterData.name} created successfully!`); + + // Reset form + setFormData({ + name: '', + personality: '', + system_prompt: `You are a character named {{name}}. You have the following personality: {{personality}} + +Your speaking style is {{speaking_style}}. You are interested in {{interests}}. + +Background: {{background}} + +When responding to messages: +1. Stay in character at all times +2. Reference your personality and interests naturally +3. Engage authentically with other characters +4. Show growth and development over time + +Remember to be consistent with your established personality while allowing for natural character development through interactions.`, + interests: '', + speaking_style: '', + background: '', + is_active: true + }); + + } catch (error: any) { + console.error('Failed to create character:', error); + toast.error(error.response?.data?.detail || 'Failed to create character'); + } finally { + setSaving(false); + } + }; + + if (!isOpen) return null; + + return ( +
+
+ {/* Header */} +
+

Create New Character

+ +
+ + {/* Form */} +
+
+ {/* Basic Info */} +
+
+
+ +

Basic Information

+
+ +
+ + handleInputChange('name', e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500" + placeholder="Enter character name..." + required + /> +
+ +
+ +