Commit Graph

137 Commits

Author SHA1 Message Date
5ac1f7fa8c cleanup: remove dead code + deduplicate GPU state reads
Dead code removed:
- globals.py: GUILD_SETTINGS (empty dict, zero consumers)
- config.py: unused 'import os'
- config_manager.py: unused 'import os' and 'Union'
- server_manager.py: duplicate 'from datetime import datetime, timedelta'

GPU deduplication:
- get_current_gpu_url() now delegates to config_manager.get_gpu()
- get_gpu_status() endpoint now delegates to config_manager.get_gpu()
- Both previously re-read memory/gpu_state.json directly
2026-04-09 20:34:17 +03:00
834b2ea188 fix: start with zero servers when config is missing or corrupt
Removed _create_default_config() which hardcoded a specific guild ID
(759889672804630530) as a fallback. Now:
- Missing servers_config.json → starts with empty servers dict
- Corrupt JSON → logs error, starts with empty servers dict
- Servers are added via the API/dashboard, not by magic defaults

All code that iterates server_manager.servers handles empty dicts safely.
2026-04-09 20:15:57 +03:00
7804aa4d76 cleanup: remove dead server_memories code
The server_memories dict and its methods (get_server_memory,
set_server_memory) plus API endpoints (GET/POST /servers/{guild_id}/memory)
were never called by any bot logic, command, or frontend code.

All per-server state is stored as ServerConfig dataclass fields and
persisted via servers_config.json. The generic key-value store was an
unfinished scaffolding feature superseded by the dataclass approach.
2026-04-09 20:10:53 +03:00
5c5c9e2723 cleanup: remove dead server config methods from config_manager
get_server_config() and set_server_config() in ConfigManager had zero
callers — every part of the codebase already uses the server_manager
singleton. Removing them eliminates the risk of a stale write that
bypasses the in-memory cache in ServerManager.

server_manager is now the sole owner of servers_config.json.
2026-04-08 15:47:36 +03:00
b4e48ce375 fix: /config/set now syncs all runtime-relevant globals
Previously only 4 of 5+ settings were synced to globals when set via
the generic /config/set endpoint. Added:
- memory.use_cheshire_cat -> globals.USE_CHESHIRE_CAT
- runtime.mood.dm_mood -> globals.DM_MOOD + DM_MOOD_DESCRIPTION
- Uses same _GLOBALS_SYNC mapping pattern as restore_runtime_settings
2026-04-08 15:05:25 +03:00
7c9cf0d8b4 fix: /config/reset now resets live globals to defaults
reset_to_defaults() previously only cleared the runtime_config dict and
saved config_runtime.yaml, but never touched the actual globals that
control runtime behavior. After a reset, LANGUAGE_MODE, AUTONOMOUS_DEBUG,
VOICE_DEBUG_MODE, USE_CHESHIRE_CAT, PREFER_AMD_GPU, and DM_MOOD all kept
their current in-memory values until the next restart.

Now reset_to_defaults() also resets the corresponding globals to their
default values from CONFIG (the static config loaded from config.yaml).
Both full reset and single-key reset are supported. The default values
come from the Pydantic AppConfig schema, ensuring consistency.

Tested: set non-default values, full reset -> all back to defaults,
single-key reset -> only that key back to default, runtime_state property
reflects the reset immediately.
2026-04-08 14:58:29 +03:00
9be7c0b1d2 fix: make /config/state return live runtime values from globals
config_manager.runtime_state was a plain dict initialized with hardcoded
defaults (dm_mood='neutral', evil_mode=False, etc.) that were never updated
by any code path except current_gpu. The /config/state endpoint and
get_full_config() both returned this stale dict, so the API always reported
neutral mood and english mode regardless of actual state.

Replaced the static dict with a @property that reads live values from
globals (DM_MOOD, EVIL_MODE, BIPOLAR_MODE, LANGUAGE_MODE) on every access.
GPU state is still managed via _current_gpu and persisted to gpu_state.json.

get_state() and set_state() continue to work for the GPU path.
2026-04-08 14:53:13 +03:00
0831f721e1 cleanup: remove dead backward-compat globals from config.py
Removed the Config Manager Integration block and all 19 backward-compat
variable re-exports (LLAMA_URL, CHESHIRE_CAT_URL, LANGUAGE_MODE, etc.)
from config.py. These were dead code because:

1. Circular import: config.py tried to import config_manager at module
   level, but config_manager.py imports from config.py first, so
   HAS_CONFIG_MANAGER was always False and _get_config_value() was a
   no-op that always returned the static value.

2. Frozen snapshots: Even if the circular import worked, the values were
   assigned to module-level names at import time and never updated. Other
   modules importing 'from config import LLAMA_URL' would get a stale
   snapshot, not a live value.

3. Nothing imports them: The entire codebase uses globals.py for mutable
   runtime state, not these config.py copies. Only ERROR_WEBHOOK_URL was
   imported (by error_handler.py), so it is kept as a simple re-export
   from SECRETS.

Also cleaned up unused imports: Any, field_validator.

Japanese mode is NOT affected — LANGUAGE_MODE and JAPANESE_TEXT_MODEL live
in globals.py and are untouched.
2026-04-08 14:40:16 +03:00
4b5be3bf97 fix: align config.yaml structure with Pydantic AppConfig schema
config.yaml nested cheshire_cat and face_detector under the 'services' key,
and llama URLs under 'services.llama'. But AppConfig expects:
- services -> {url, amd_url} (llama endpoints directly)
- cheshire_cat -> top-level key
- face_detector -> top-level key

Because Pydantic silently ignores extra fields, ServicesConfig received
{llama: {...}, cheshire_cat: {...}, face_detector: {...}} and none matched
its 'url'/'amd_url' fields, so ALL service config from YAML was silently
ignored and Pydantic defaults were always used instead.

Flattened services to contain url/amd_url directly, and moved cheshire_cat
and face_detector to top-level keys matching the AppConfig model. Verified
both AppConfig(**yaml_data) and config_manager dot-path traversal work.
2026-04-08 14:14:56 +03:00
742b7b6b64 fix: persist config_runtime.yaml inside volume-mounted memory dir
config_runtime.yaml was written to the container root (/) because the path
resolved via Path(__file__).parent.parent from /app/config_manager.py = /.
This location is not volume-mounted, so all runtime config changes (language,
debug flags, Cheshire Cat toggle, mood, GPU preference) were lost on every
container restart.

Moved runtime_config_path to memory/config_runtime.yaml, which lives inside
the volume-mounted ./bot/memory:/app/memory directory and persists across
restarts. Also reordered __init__ so memory_dir is initialized before
runtime_config_path depends on it.
2026-04-08 14:14:31 +03:00
f50c677baf ui: move Re-crop Current button under cropped avatar preview in tab11
Relocated the button from the top action row to below the '512x512
displayed as circle' label for more intuitive placement next to the
avatar it acts on.
2026-03-31 15:16:40 +03:00
56a70705b2 feat: add PFP album/gallery system with batch upload, cropping, and disk management
- Backend: album storage in memory/profile_pictures/album/{uuid}/ with
  original.png, cropped.png, and metadata.json per entry
- add_to_album/add_batch_to_album with efficient resource management
  (vision model + face detector kept alive across batch)
- set_album_entry_as_current auto-archives current PFP before replacing
- manual/auto crop album entries without applying to Discord
- Disk usage tracking, single & bulk delete
- API: full CRUD endpoints under /profile-picture/album/*
- Frontend: collapsible album grid in tab11 with thumbnail cards,
  multi-select checkboxes for bulk delete, detail panel with crop
  interface (Cropper.js), description editor, set-as-current action
2026-03-31 15:09:57 +03:00
f092cadb9d feat: add Profile Picture Management tab with manual crop, description editor
- profile_picture_manager.py:
  - Add ORIGINAL_PATH constant; save full-res original before every crop
  - Add skip_crop param to change_profile_picture() for manual crop workflow
  - Add manual_crop(x,y,w,h) method with Discord avatar update + role color sync
  - Add auto_crop_only() to re-run face-detection crop on stored original
  - Add update_description() with Cheshire Cat declarative memory re-injection
  - Add regenerate_description() via vision model
  - Skip crop step if image is already at/below 512x512

- api.py:
  - GET /profile-picture/image/original — serve full-res original (no-cache)
  - GET /profile-picture/image/current  — serve current cropped avatar (no-cache)
  - POST /profile-picture/change-no-crop — acquire image, skip auto-crop
  - POST /profile-picture/manual-crop   — apply crop coords {x,y,width,height}
  - POST /profile-picture/auto-crop     — re-run intelligent crop on original
  - POST /profile-picture/description   — save freeform description + Cat inject
  - POST /profile-picture/regenerate-description — re-generate via vision model
  - GET  /profile-picture/description   — fetch current description text

- index.html:
  - Add new tab11 '🖼️ Profile Picture Management'
  - Remove PFP + role color sections from Actions tab (tab2)
  - Add Cropper.js 1.6.2 via CDN for manual square crop
  - Tab layout: action buttons, file upload, auto/manual crop toggle,
    Cropper.js interface, side-by-side original/cropped previews,
    role color management, freeform description editor, metadata box (bottom)
  - Wire switchTab hook for tab11 → loadPfpTab()
  - All new JS functions: pfpChangeDanbooru, pfpUploadCustom, pfpRestoreFallback,
    pfpShowCropInterface, pfpApplyManualCrop, pfpApplyAutoCrop, pfpSaveDescription,
    pfpRegenerateDescription, pfpRefreshPreviews, setCustomRoleColor, resetRoleColor
2026-03-30 15:10:19 +03:00
08fb465c67 Fix: Cache regular Miku avatar URL to prevent pfp bleed in bipolar arguments
When Evil Mode activates, the bot's Discord account avatar is changed to evil_pfp.png.
Previously, get_persona_avatar_urls() would read this swapped avatar and pass it to
the Miku webhook, causing both webhooks to display Evil Miku's pfp.

Now caching the regular Miku CDN URL before Evil Mode changes the bot's avatar.
When Evil Mode is active, the cached URL is used instead of reading from the bot
account. Discord CDN URLs remain valid after avatar changes, so this reliably
preserves the correct pfp for both regular and Evil Miku webhooks during arguments.

- Added MIKU_NORMAL_AVATAR_URL global in bot/globals.py
- Updated get_persona_avatar_urls() to cache and return the cached URL
- Save the normal avatar URL before Evil Mode switches the bot's avatar
2026-03-30 14:30:34 +03:00
e6529f1bc3 added check to only crop pfp if > minimum Discord pfp resolution 2026-03-30 12:50:34 +03:00
54d9a80089 fixed webhook pfp for regular miku being wrong when evil mode active 2026-03-05 22:16:14 +02:00
832fc0d039 added test log with multiple various test scenarios between models and evil/regular miku 2026-03-05 22:04:26 +02:00
d5b9964ce7 Fix vision pipeline: route images through Cat, pass user question to vision model
- Fix silent None return in analyze_image_with_vision exception handler
- Add None/empty guards after vision analysis in bot.py (image, video, GIF, Tenor)
- Route all image/video/GIF responses through Cheshire Cat pipeline (was
  calling query_llama directly), enabling episodic memory storage for media
  interactions and correct Last Prompt display in Web UI
- Add media_type parameter to cat_adapter.query() and forward as
  discord_media_type in WebSocket payload
- Update discord_bridge plugin to read media_type from payload and inject
  MEDIA NOTE into system prefix in before_agent_starts hook
- Add _extract_vision_question() helper to strip Discord mentions and bot-name
  triggers from user message; pass cleaned question to vision model so specific
  questions (e.g. 'what is the person wearing?') go directly to the vision model
  instead of the generic 'Describe this image in detail.' fallback
- Pass user_prompt to all analyze_image_with_qwen / analyze_video_with_vision
  call sites in bot.py (image, video, GIF, Tenor, embed paths)
- Fix autonomous reaction loops skipping messages that @mention the bot or have
  media attachments in DMs, preventing duplicate vision model calls for images
  already being processed by the main message handler
- Increase vision max_tokens: images 300->800, video/GIF 400->1000 (no VRAM
  impact; KV cache is pre-allocated at model load time)
2026-03-05 21:59:27 +02:00
ae1e0aa144 add: cheshire-cat configuration, tooling, tests, and documentation
Configuration:
- .env.example, .gitignore, compose.yml (main docker compose)
- docker-compose-amd.yml (ROCm), docker-compose-macos.yml
- start.sh, stop.sh convenience scripts
- LICENSE (Apache 2.0, from upstream Cheshire Cat)

Memory management utilities:
- analyze_consolidation.py, manual_consolidation.py, verify_consolidation.py
- check_memories.py, extract_declarative_facts.py, store_declarative_facts.py
- compare_systems.py (system comparison tool)
- benchmark_cat.py, streaming_benchmark.py, streaming_benchmark_v2.py

Test suite:
- quick_test.py, test_setup.py, test_setup_simple.py
- test_consolidation_direct.py, test_declarative_recall.py, test_recall.py
- test_end_to_end.py, test_full_pipeline.py
- test_phase2.py, test_phase2_comprehensive.py

Documentation:
- README.md, QUICK_START.txt, TEST_README.md, SETUP_COMPLETE.md
- PHASE2_IMPLEMENTATION_NOTES.md, PHASE2_TEST_RESULTS.md
- POST_OPTIMIZATION_ANALYSIS.md
2026-03-04 00:51:14 +02:00
eafab336b4 feat: add Traefik proxy, custom chat template, improve Cheshire Cat memory
docker-compose.yml:
- Add Traefik proxy network + labels for miku.panel domain
- Connect miku-bot service to proxy network

llama-swap-config.yaml / llama-swap-rocm-config.yaml:
- Add --chat-template-file flag to disable Llama 3.1 built-in tool
  calling (was causing malformed responses)
- ROCm config: add Rocinante-X 12B model entry for comparison testing

cheshire-cat discord_bridge plugin:
- Increase declarative memory recall (k=3→10, threshold 0.7→0.5)
  for better factual retrieval
- Add agent_prompt_prefix hook to enforce factual accuracy from
  declarative memories
- Add before_agent_starts debug logging for memory inspection
- Add passthrough hooks for message/suffix pipeline
2026-03-04 00:48:58 +02:00
335b58a867 feat: fix evil mode race conditions, expand moods and PFP detection
bipolar_mode.py:
- Replace unsafe globals.EVIL_MODE temporary overrides with
  force_evil_context parameter to fix async race conditions (3 sites)

moods.py:
- Add 6 new evil mood emojis: bored, manic, jealous, melancholic,
  playful_cruel, contemptuous
- Refactor rotate_dm_mood() to skip when evil mode active (evil mode
  has its own independent 2-hour rotation timer)

persona_dialogue.py:
- Same force_evil_context race condition fix (2 sites)
- Fix over-aggressive response cleanup that stripped common words
  (YES/NO/HIGH) — now uses targeted regex for structural markers only
- Update evil mood multipliers to match new mood set

profile_picture_context:
- Expand PFP detection regex for broader coverage (appearance questions,
  opinion queries, selection/change questions)
- Add plugin.json metadata file
2026-03-04 00:45:23 +02:00
5898b0eb3b fix: update .gitignore to cover all bot/memory subdirs, untrack runtime data
- Change bot/memory/*.json to bot/memory/** to properly ignore all
  subdirectories (dms/, dm_reports/, profile_pictures/)
- Untrack bot/memory/ files from index (DMs, profile pics, dm reports)
- Untrack cheshire-cat discord_bridge __pycache__/*.pyc from index
- These files are runtime/user data that should never be in version control
2026-03-04 00:43:10 +02:00
8ca716029e add: absorb soprano_to_rvc as regular subdirectory
Voice conversion pipeline (Soprano TTS → RVC) with Docker support.
Previously tracked as bare gitlink; removed .git/ directories and
absorbed into main repo for unified tracking.

Includes: Soprano TTS, RVC WebUI integration, Docker configs,
WebSocket API, and benchmark scripts.
Updated .gitignore to exclude large model weights (*.pth, *.pt, *.onnx, *.index).
287 files (3.1GB of ML weights properly excluded via gitignore).
2026-03-04 00:24:53 +02:00
34b184a05a add: absorb uno-online as regular subdirectory
UNO card game web app (Node.js/React) with Miku bot integration.
Previously an independent git repo (fork of mizanxali/uno-online).
Removed .git/ and absorbed into main repo for unified tracking.

Includes bot integration code: botActionExecutor, cardParser,
gameStateBuilder, and server-side bot action support.
37 files, node_modules excluded via local .gitignore.
2026-03-04 00:21:38 +02:00
c708770266 reorganize: consolidate all documentation into readmes/
- Moved 20 root-level markdown files to readmes/
- Includes COMMANDS.md, CONFIG_README.md, all UNO docs, all completion reports
- Added new: MEMORY_EDITOR_FEATURE.md, MEMORY_EDITOR_ESCAPING_FIX.md,
  CONFIG_SOURCES_ANALYSIS.md, MCP_TOOL_CALLING_ANALYSIS.md, and others
- Root directory is now clean of documentation clutter
2026-03-04 00:19:49 +02:00
fdde12c03d reorganize: move all test scripts to tests/ directory
- Moved 8 root-level test scripts + 2 from bot/ to tests/
- Moved run_rocinante_test.sh runner script to tests/
- Added tests/README.md documenting each test's purpose, type, and requirements
- Added test_pfp_context.py and test_rocinante_comparison.py (previously untracked)
2026-03-04 00:18:21 +02:00
431f675fc7 cleanup: update .gitignore, sanitize .env.example, remove stale files
- Expanded .gitignore: miku-app/, dashboard/, .continue/, *.code-workspace,
  cheshire-cat artifacts (venv, benchmarks, test output), jinja templates
- Sanitized .env.example: replaced real webhook URL and user ID with placeholders
- Removed SECRETS_CONFIGURED.md (contained sensitive token info)
- Removed bot/static/system.html.bak (stale backup)
- Removed bot/utils/voice_receiver.py.old (superseded)
2026-03-04 00:17:05 +02:00
a226bc41df Rewrite is_miku_addressed() to only trigger when addressed, not mentioned
- Pre-compile 393 name variants into 4 regex patterns at module load
  (was 7,300+ raw re.search() calls per message)
- Strict addressing detection using punctuation context:
  START:  name at beginning + punctuation (Miku, ... / みく!...)
  END:    comma + name at end (..., Miku / ...、ミク)
  MIDDLE: commas on both sides - vocative (..., Miku, ...)
  ALONE:  name is the entire message (Miku! / ミクちゃん)
- Rejects mere mentions: 'I like Miku' / 'Miku is cool' no longer trigger
- Script-family-aware pattern generation (Latin, Cyrillic, Japanese)
  eliminates nonsensical cross-script combos (e.g. o-みく)
- Word boundary enforcement prevents substring matches (mikumiku)
- Fixes regex 'unbalanced parenthesis' errors from old implementation
- Add comprehensive test suite (94 cases, all passing)
2026-03-03 12:42:33 +02:00
892edf5564 feat: Last Prompt shows full prompt with evil mode awareness
- discord_bridge before_agent_starts now checks evil_mode from
  working_memory to load the correct personality files:
  Normal: miku_lore/prompt/lyrics + /app/moods/{mood}.txt
  Evil: evil_miku_lore/prompt/lyrics + /app/moods/evil/{mood}.txt
- Reads files directly instead of relying on cross-plugin working_memory
- cat_client.query() returns (response, full_prompt) tuple
- Full prompt includes system prefix + recalled memories + conversation
- API /prompt/cat returns full_prompt field
2026-03-01 01:17:06 +02:00
a0a16e6784 fix: resolve Cat personality startup race condition
Bot was calling restore_evil_cat_state() in on_ready() before Cheshire
Cat finished booting (~25s), causing all plugin toggle API calls to fail
silently. Evil Miku plugin was left disabled and the bot used Cat's
default personality instead.

Changes:
- cat_client.py: add wait_for_ready() that polls Cat health endpoint
  every 5s for up to 120s before attempting any admin API calls
- evil_mode.py: rewrite restore_evil_cat_state() with:
  - wait_for_ready() gate before any plugin/model switching
  - 3-second extra delay after Cat is up (plugin registry fully loaded)
  - up to 3 retries on failure
  - post-switch verification that the correct plugins are actually active

Also fixes helcyon model references that leaked into the container image
(cat_client.py was switching Cat's LLM to 'helcyon' which has no
llama-swap handler; reverted to correct 'darkidol' / 'llama3.1').
2026-03-01 00:57:13 +02:00
f0b5d71097 feat: add loading spinners on tab switch for data-driven tabs
Show a CSS spinner overlay when switching to Autonomous Stats (tab6),
Memories (tab9), and DM Management (tab10). Spinner only shows on
first visit when content is empty, removed after data loads.
2026-03-01 00:29:03 +02:00
0cdf26dc34 feat: populate all mood dropdowns dynamically from API
Replace hardcoded <option> lists in #mood (tab1 DM mood) and
#chat-mood-select (tab7 chat mood) with empty selects populated
by populateMoodDropdowns(). Respects evil mode emoji mapping.
Called on DOMContentLoaded and after server cards render.
2026-03-01 00:28:07 +02:00
1037d13b0a feat: reorganize tabs + add Last Prompt CC/Fallback toggle
- Split Status tab: moved DM management to new dedicated 📱 DM Management tab
- Added Last Prompt source toggle (Cheshire Cat / Bot Fallback) with
  localStorage persistence, CC as default
- Backend: added LAST_CAT_INTERACTION global, /prompt/cat API endpoint
- Bot tracks Cat interactions (prompt, response, user, mood, timestamp)
- Auto-load data on tab switch (Status loads prompt, DM tab loads users)
2026-03-01 00:26:22 +02:00
5bdd907730 refactor: standardize raw fetch() calls to use apiCall() wrapper
Convert 47 raw fetch+response.json+error-handling patterns to use the
centralized apiCall() utility. The 11 remaining raw fetch() calls are
FormData uploads or SSE streaming that require direct fetch access.
2026-03-01 00:14:08 +02:00
820a226dd9 refactor: consolidate 3 DOMContentLoaded listeners into single init block
- Extract initTabState, initTabWheelScroll, initVisibilityPolling,
  initChatImagePreview, initModalAccessibility as named functions
- Move polling interval vars to outer scope for accessibility
- Single DOMContentLoaded calls all init functions in logical order
- Replace scattered listeners with comment markers at original locations
2026-02-28 23:50:40 +02:00
e0dc190710 feat: add responsive CSS breakpoints for mobile and tablet support
- 1200px: Adjust panel widths to 55/45
- 1024px: Stack panels vertically, logs below main content
- 768px: Tab buttons flow into auto-fill grid rows
- 480px: Two-column tab grid, reduced padding for small screens
2026-02-28 23:48:23 +02:00
191a368258 fix: prevent XSS in addChatMessage by using textContent for user input
- Escape sender name via escapeHtml in innerHTML template
- Set message content via textContent instead of innerHTML injection
- Prevents HTML/script injection from user input or LLM responses
2026-02-28 23:32:28 +02:00
7a10206617 feat: modal UX - close on Escape key and backdrop click, add ARIA attributes
- Escape key closes any open memory modal
- Clicking the dark backdrop behind a modal closes it
- Add role=dialog, aria-modal, aria-label for accessibility
2026-02-28 23:31:28 +02:00
8b96f4dc8a cleanup: remove duplicate escapeHtml function, add null check to remaining one 2026-02-28 23:30:05 +02:00
4666986f78 cleanup: remove ~70 lines of duplicate CSS for conversation view styles
First block of conversation-view, conversations-list, conversation-message,
message-header, sender, timestamp, message-content, message-attachments was
silently overridden by identical selectors defined later. Kept the unique
reaction/delete-button styles.
2026-02-28 23:29:15 +02:00
5e002004cc fix: notification system - timer race condition, success color, z-index above modals
- Cancel previous timer before starting new one (prevents early dismissal)
- Add green background for type='success' notifications
- Bump z-index from 1000 to 3000 so notifications show above modals
- Add fade-out transition for smoother dismissal
2026-02-28 23:28:30 +02:00
d3fb0eacb6 fix: updateBedtimeRange variable scoping - originalText accessible in finally block 2026-02-28 23:26:02 +02:00
7bcb670b96 perf: pause polling intervals when browser tab is hidden
- Replace raw setInterval with startPolling/stopPolling functions
- Add visibilitychange listener to pause when tab is hidden
- Immediately refresh data when tab becomes visible again
- Saves bandwidth and CPU when the dashboard is in background
2026-02-28 23:25:07 +02:00
a434f11561 fix: add viewport meta tag for responsive support 2026-02-28 23:14:32 +02:00
a217f18649 fix: add missing create-memory-collection hidden input to Create Memory modal 2026-02-28 23:13:50 +02:00
ed9df5ff81 fix: resetServerMood variable scoping - originalText accessible in finally block 2026-02-28 23:12:44 +02:00
6a35718a7c feat: enable mouse wheel horizontal scrolling on tab bar 2026-02-28 23:09:09 +02:00
e2077705de fix: improve log panel with auto-scroll and color-coded lines 2026-02-28 23:05:26 +02:00
8ca94fbafc fix: persist active tab via localStorage + fix implicit event bug in switchTab
- Add data-tab attributes to tab buttons for reliable identification
- Replace implicit window.event usage with querySelector by data-tab
- Save active tab to localStorage on switch, restore on page load
2026-02-28 22:59:12 +02:00
66881f4c88 refactor: deduplicate prompts, reorganize persona files, update paths
Prompt deduplication (~20% reduction, 4,743 chars saved):
- evil_miku_lore.txt: remove intra-file duplication (height rule 2x,
  cruelty-has-substance 2x, music secret 2x, adoration secret 2x),
  trim verbose restatements, cut speech examples from 10 to 6
- evil_miku_prompt.txt: remove entire PERSONALITY section (in lore),
  remove entire RESPONSE STYLE section (now only in preamble),
  soften height from prohibition to knowledge
- miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS)
- miku_prompt.txt: remove duplicate intro, 4 personality traits
  already in lore, FAMOUS SONGS section (in lore), fix response
  length inconsistency (1-2 vs 2-3 -> consistent 2-3)

Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py,
miku_personality.py):
- Response rules now exist in ONE place only (preamble)
- Height rule softened: model knows 15.8m, can say it if asked,
  but won't default to quoting it when taunting
- Response length: 2-4 sentences (was 1-3), removed action template
  list that model was copying literally (*scoffs*, *rolls eyes*)
- Added: always include actual words, never action-only responses
- Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences

Directory reorganization:
- Move 6 persona files to bot/persona/{evil,miku}/ subdirectories
- Update all open() paths in evil_mode.py, context_manager.py,
  voice_manager.py, both Cat plugins
- Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona)
- docker-compose: 6 file mounts -> 2 directory mounts
  (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku)

Evil Miku system (previously unstaged):
- Full evil mood management: 2h rotation timer, mood persistence,
  10 mood-specific autonomous template pools, mood-aware DMs
- Evil mode toggle with role color/nickname/pfp management
- get_evil_system_prompt() with mood integration

Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00