docs: complete documentation and add web monitoring dashboard

- Add comprehensive configuration guide (docs/configuration.md)
- Add development guide with setup instructions (docs/development.md)
- Add complete API reference documentation (docs/api_reference.md)
- Update README.md with new features and project structure
- Update TODO.md to reflect current implementation status
- Add web monitoring dashboard with FastAPI and WebSocket support
- Add tests for sync integration and web monitoring
- Update config example with monitoring settings

The MUD AI Player is now feature-complete with full documentation.
This commit is contained in:
truetype committed 2026-02-09 10:44:25 +01:00
1 parent 69ba824bc8
commit b9f21c7199
14 files changed
+2658 -160

No files matched your search

+552
View File
@@ -0,0 +1,552 @@
# API Reference
Complete API reference for the MUD AI Player.
## Table of Contents
- [Core Components](#core-components)
- [Modules](#modules)
- [Configuration](#configuration)
- [Utilities](#utilities)
---
## Core Components
### TelnetClient
Asynchronous telnet client for MUD connection.
**Location:** `src.core.telnet_client.TelnetClient`
```python
class TelnetClient:
def __init__(
self,
host: str,
port: int,
max_retries: int = 3,
retry_delay: float = 5.0,
auto_reconnect: bool = True,
)
```
**Methods:**
- `async connect() -> bool` - Connect to MUD server
- `async connect_with_retry() -> bool` - Connect with retry logic
- `async disconnect()` - Disconnect from server
- `async send(command: str)` - Send command to server
- `async receive_loop()` - Main receive loop
- `set_receive_callback(callback: Callable)` - Set data callback
**Example:**
```python
client = TelnetClient("mud.example.com", 4000)
await client.connect()
await client.send("look")
```
### GameState
Central game state management.
**Location:** `src.core.game_state.GameState`
```python
@dataclass
class GameState:
character: Character
current_room: Optional[Room]
previous_room: Optional[Room]
inventory: List[str]
active_quests: List[Quest]
combat_state: CombatState
target: Optional[str]
```
**Methods:**
- `is_in_combat() -> bool` - Check if in combat
- `is_healthy(threshold: float = 0.7) -> bool` - Check health threshold
- `update_from_prompt(prompt: ParsedPrompt)` - Update vitals
- `update_room(parsed_room: ParsedRoom)` - Update current room
- `update_combat(parsed_combat: ParsedCombat)` - Update combat state
### ANSIParser
Parse and strip ANSI color codes.
**Location:** `src.core.ansi_parser.ANSIParser`
```python
class ANSIParser:
def strip_ansi(self, text: str) -> str
def parse_colors(self, text: str) -> List[ANSISegment]
```
---
## Modules
### WorldMapper
High-level automapper with hybrid storage.
**Location:** `src.modules.mapper.WorldMapper`
```python
class WorldMapper:
def __init__(
self,
db_path: str = "data/maps/active/world.db",
archive_dir: str = "data/maps/archive",
enable_sync: bool = True,
room_count_trigger: int = 500,
time_interval_hours: float = 24.0,
)
```
**Methods:**
- `on_room_enter(title, description, exits, npcs, items, zone) -> int`
- Process arrival in a room, returns room ID
- `on_move(direction: str)`
- Record movement direction
- `on_move_failed(direction: str)`
- Record blocked exit
- `find_path(to_room_id: int) -> Optional[List[str]]`
- Find path from current room to target
- `find_path_between(from_id: int, to_id: int) -> Optional[List[str]]`
- Find path between two rooms
- `get_next_exploration_target() -> Optional[Dict]`
- Get next unexplored exit
- `get_unexplored_exits(room_id: Optional[int]) -> List[str]`
- Get unexplored directions from room
- `search_rooms(query: str, top_k: int = 5) -> List[Dict]`
- Semantic search on room descriptions
- `close()`
- Close mapper and create final snapshot
**Properties:**
- `current_room_id: Optional[int]`
- `total_rooms: int`
- `total_exits: int`
- `sync_stats: Dict`
### DecisionEngine
Central decision-making engine.
**Location:** `src.modules.decision.engine.DecisionEngine`
```python
class DecisionEngine:
def __init__(
self,
combat_config: Optional[CombatConfig] = None,
exploration_strategy: str = "random",
)
```
**Methods:**
- `tick(game_state: GameState)`
- Evaluate world and queue actions
- `next_action() -> Optional[Action]`
- Get most urgent action
- `force_state(state_name: str, game_state: GameState)`
- Force state transition
- `register_state(state: BaseBehaviorState)`
- Add custom behavior state
**Properties:**
- `current_state: BaseBehaviorState`
- `current_state_name: str`
### ActionQueue
Priority queue for actions.
**Location:** `src.modules.decision.action_queue.ActionQueue`
```python
class ActionQueue:
def push(self, action: Action)
def pop(self) -> Optional[Action]
def peek(self) -> Optional[Action]
def clear(self)
def clear_by_source(self, source: str)
@property
def size(self) -> int
@property
def is_empty(self) -> bool
```
### CombatAI
Intelligent combat decision-making.
**Location:** `src.modules.decision.combat_ai.CombatAI`
```python
class CombatAI:
def __init__(self, config: Optional[CombatConfig] = None)
def decide(self, game_state: GameState) -> List[Action]
def reset(self)
```
### GoalManager
Manage AI goals and objectives.
**Location:** `src.modules.decision.goal_manager.GoalManager`
```python
class GoalManager:
def add_goal(self, goal: Goal)
def remove_goal(self, goal_id: str)
def get_current_goal(self, game_state: GameState) -> Optional[Goal]
def get_active_goals(self) -> List[Goal]
def set_default_goals(self)
@property
def active_goal_count(self) -> int
```
### LLMBrain
LLM integration with memory.
**Location:** `src.modules.llm.brain.LLMBrain`
```python
class LLMBrain:
def __init__(
self,
config: LLMConfig,
memory_db_path: str = "data/llm_memory.db",
)
async def initialize()
async def decide(game_state: GameState) -> Optional[str]
async def social_reply(
message: str,
sender: str,
character_class: str,
character_name: str,
) -> Optional[str]
@property
def is_available(self) -> bool
```
### SocialAI
Detect and respond to social interactions.
**Location:** `src.modules.llm.social_ai.SocialAI`
```python
class SocialAI:
def __init__(
self,
client: LLMClient,
character_name: str,
character_class: str,
)
def detect_social_event(self, text: str) -> Optional[Dict]
```
### CharacterCreator
Automated character creation.
**Location:** `src.modules.character.creator.CharacterCreator`
```python
class CharacterCreator:
def __init__(self, config: CreatorConfig)
async def create_character(telnet: TelnetClient) -> bool
```
### QuestManager
Quest tracking and management.
**Location:** `src.modules.quest.quest_manager.QuestManager`
```python
class QuestManager:
def add_quest(self, quest: Quest)
def complete_quest(self, quest_id: str)
def get_active_quests(self) -> List[Quest]
def detect_quest_in_text(self, text: str) -> Optional[Quest]
```
### ProgressionOptimizer
Optimize grinding and progression.
**Location:** `src.modules.progression.optimizer.ProgressionOptimizer`
```python
class ProgressionOptimizer:
def find_optimal_grind_area(
self,
game_state: GameState,
world_mapper: WorldMapper,
) -> Optional[str]
def should_grind(self, game_state: GameState) -> bool
```
### MonitoringServer
Web dashboard server.
**Location:** `src.modules.web.server.MonitoringServer`
```python
class MonitoringServer:
def __init__(
self,
host: str = "127.0.0.1",
port: int = 8080,
game_state: Optional[Any] = None,
world_mapper: Optional[Any] = None,
decision_engine: Optional[Any] = None,
)
async def start()
async def stop()
def log_event(self, event_type: str, message: str, data: Optional[Dict] = None)
```
---
## Configuration
### load_config
Load and validate configuration.
**Location:** `src.config.settings.load_config`
```python
def load_config(config_path: str = "config.yaml") -> Config
```
**Example:**
```python
from src.config.settings import load_config
config = load_config("config.yaml")
print(config.mud.host)
print(config.character.preferred_class)
```
### Config Classes
- `MudConfig` - MUD connection settings
- `CharacterConfig` - Character preferences
- `AIConfig` - AI engine settings
- `LLMProviderConfig` - LLM configuration
- `MapperConfig` - Mapper settings
- `BehaviorConfig` - Behavior simulation
- `LoggingConfig` - Logging settings
- `MonitoringConfig` - Web dashboard settings
---
## Utilities
### setup_logger
Configure application logging.
**Location:** `src.utils.logger.setup_logger`
```python
def setup_logger(
level: str = "INFO",
log_file: str = "data/logs/game.log",
console: bool = True,
max_file_size_mb: int = 10,
backup_count: int = 5,
) -> logging.Logger
```
### HumanBehavior
Simulate human-like behavior.
**Location:** `src.utils.human_behavior.HumanBehavior`
```python
class HumanBehavior:
def __init__(
self,
reaction_time_min_ms: int = 200,
reaction_time_max_ms: int = 2000,
pause_probability: float = 0.05,
)
async def delay(self)
def maybe_typo(self, text: str) -> str
```
---
## Data Classes
### ParsedRoom
```python
@dataclass
class ParsedRoom:
title: Optional[str]
description: Optional[str]
exits: List[str]
npcs: List[str]
items: List[str]
```
### ParsedPrompt
```python
@dataclass
class ParsedPrompt:
hp: Optional[int]
max_hp: Optional[int]
mp: Optional[int]
max_mp: Optional[int]
sp: Optional[int]
max_sp: Optional[int]
```
### ParsedCombat
```python
@dataclass
class ParsedCombat:
attacker: Optional[str]
target: Optional[str]
action: str
damage: Optional[int]
is_combat: bool
```
### Action
```python
@dataclass
class Action:
command: str
priority: ActionPriority
description: str
source: str
```
### Goal
```python
@dataclass
class Goal:
id: str
name: str
description: str
priority: GoalPriority
target_state: str
conditions: List[Callable[[GameState], bool]]
progress: float
```
---
## Enums
### CombatState
```python
class CombatState(Enum):
IDLE = "idle"
IN_COMBAT = "in_combat"
FLEEING = "fleeing"
```
### ActionPriority
```python
class ActionPriority(Enum):
CRITICAL = 1
HIGH = 2
MEDIUM = 3
LOW = 4
IDLE = 5
```
### GoalPriority
```python
class GoalPriority(Enum):
CRITICAL = 1
HIGH = 2
MEDIUM = 3
LOW = 4
```
### EventType
```python
class EventType(Enum):
ROOM = "room"
PROMPT = "prompt"
COMBAT = "combat"
INVENTORY = "inventory"
CHAT = "chat"
UNKNOWN = "unknown"
```
---
## Exceptions
All custom exceptions inherit from `MUDAIPlayerException`:
```python
class MUDAIPlayerException(Exception):
"""Base exception for MUD AI Player"""
pass
class ConnectionError(MUDAIPlayerException):
"""Connection-related errors"""
pass
class ParseError(MUDAIPlayerException):
"""Parsing errors"""
pass
class ConfigurationError(MUDAIPlayerException):
"""Configuration errors"""
pass
```
+157
View File
@@ -0,0 +1,157 @@
# Configuration Guide
This guide covers all configuration options for the MUD AI Player.
## Configuration File
The main configuration file is `config.yaml`. An example is provided in `examples/config.example.yaml`.
## Configuration Sections
### MUD Connection Settings
```yaml
mud:
host: "192.168.0.63" # MUD server hostname or IP
port: 6666 # MUD server port
mud_type: "dead_souls" # Parser type: dead_souls, aardwolf, etc.
username: "your_username" # Login username
password: "your_password" # Login password
keep_alive_interval: 60 # Seconds between keep-alive commands
keep_alive_command: "" # Command to send for keep-alive (empty = no-op)
```
### Character Settings
```yaml
character:
name: "AIPlayer" # Character name
preferred_class: "warrior" # warrior, mage, cleric, rogue
preferred_race: "human" # human, elf, dwarf, etc.
play_style: "balanced" # aggressive, cautious, balanced
```
### AI Engine Settings
```yaml
ai:
engine: "hybrid" # rule_based, llm, hybrid
llm_model: "llama3.2" # LLM model name
decision_delay_ms: [200, 2000] # Min/max reaction time in milliseconds
exploration_strategy: "depth_first" # depth_first, breadth_first
llm:
enabled: true # Enable/disable LLM features
base_url: "http://localhost:11434" # Ollama/OpenAI-compatible endpoint
model: "llama3.2" # Model to use
temperature: 0.3 # Creativity vs consistency (0.0-1.0)
max_tokens: 512 # Max response length
timeout_seconds: 30.0 # Request timeout
max_retries: 2 # Retry attempts on failure
system_prompt: "You are an AI playing a text-based MUD game..."
memory_db_path: "data/llm_memory.db" # Long-term memory storage
```
### Mapper Settings
```yaml
mapper:
active_storage:
db_path: "data/maps/active/world.db"
graph_cache_size: 10000
historical_storage:
archive_dir: "data/maps/archive"
snapshot_triggers:
room_count: 500 # Trigger snapshot every N rooms
session_end: true # Snapshot on disconnect
time_interval_hours: 24 # Periodic snapshots
compression:
fps: 30
codec: "h264"
semantic_search:
model: "all-MiniLM-L6-v2" # Sentence transformer model
top_k: 5 # Number of search results
similarity_threshold: 0.7 # Minimum similarity score
```
### Behavior Simulation
```yaml
behavior:
reaction_time_min_ms: 200 # Minimum delay between actions
reaction_time_max_ms: 2000 # Maximum delay between actions
pause_probability: 0.05 # Chance to pause briefly (5%)
typo_probability: 0.0 # Chance to make typos (0% = disabled)
```
### Logging
```yaml
logging:
level: "DEBUG" # DEBUG, INFO, WARNING, ERROR
file: "data/logs/game.log" # Log file path
console: true # Also log to console
max_file_size_mb: 10 # Rotate when file reaches this size
backup_count: 5 # Number of backup files to keep
```
### Web Monitoring Dashboard
```yaml
monitoring:
enabled: true # Enable/disable web dashboard
host: "127.0.0.1" # Bind address
port: 8080 # Port for dashboard
```
## Environment Variables
You can override any configuration value using environment variables:
```bash
export MUD_HOST="your-mud-server.com"
export MUD_PORT=4000
export MUD_USERNAME="player"
export MUD_PASSWORD="secret"
```
## Multiple Configurations
To use different configurations for different MUDs:
```bash
# For Dead Souls MUD
python -m src.main --config config_dead_souls.yaml
# For Aardwolf MUD
python -m src.main --config config_aardwolf.yaml
```
## Security Notes
⚠️ **Important**: The `config.yaml` file contains sensitive credentials. It is automatically excluded from Git via `.gitignore`.
Never commit your `config.yaml` to version control. Always use the example file as a template.
## Configuration Validation
The configuration is validated at startup using Pydantic models. If a required field is missing or invalid, the application will exit with a clear error message.
## Troubleshooting
### Connection Issues
- Verify `host` and `port` are correct
- Check firewall rules
- Ensure `mud_type` matches your MUD
### LLM Not Working
- Verify Ollama is running: `curl http://localhost:11434/api/tags`
- Check `ai.llm.enabled` is set to `true`
- Ensure the model is installed: `ollama pull llama3.2`
### Mapper Not Saving
- Check that `data/maps/` directory exists and is writable
- Verify disk space is available
- Check file permissions
+381
View File
@@ -0,0 +1,381 @@
# Development Guide
This guide is for developers who want to contribute to or extend the MUD AI Player.
## Development Setup
### Prerequisites
- Python 3.10+
- Poetry (recommended) or pip
- Git
### Installation
**Using Poetry (Recommended):**
```bash
# Install Poetry if not already installed
pip install poetry
# Clone the repository
git clone <repository-url>
cd mud-ai-player
# Install dependencies
poetry install
# Activate virtual environment
poetry shell
```
**Using pip:**
```bash
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
```
## Project Structure
```
mud-ai-player/
├── src/ # Source code
│ ├── core/ # Core components
│ │ ├── telnet_client.py # Async telnet connection
│ │ ├── game_state.py # Game state management
│ │ ├── ansi_parser.py # ANSI color code parsing
│ │ └── parsers/ # MUD-specific parsers
│ ├── modules/ # Feature modules
│ │ ├── mapper/ # World mapping (SQLite + Memvid)
│ │ ├── decision/ # AI decision engine
│ │ ├── llm/ # LLM integration
│ │ ├── character/ # Character creation
│ │ ├── quest/ # Quest management
│ │ ├── progression/ # Grinding optimizer
│ │ └── web/ # Monitoring dashboard
│ ├── config/ # Configuration management
│ └── utils/ # Utilities
├── tests/ # Test suite
├── docs/ # Documentation
├── data/ # Persistent storage
└── examples/ # Example configurations
```
## Adding a New Module
To add a new feature module:
1. Create a directory under `src/modules/your_module/`
2. Create `__init__.py` and your module files
3. Export the main class from `__init__.py`
4. Add tests in `tests/test_your_module.py`
5. Update documentation
Example structure:
```python
# src/modules/your_module/__init__.py
from .your_class import YourClass
__all__ = ["YourClass"]
# src/modules/your_module/your_class.py
class YourClass:
def __init__(self):
pass
def do_something(self):
pass
```
## Adding a New Parser
To add support for a new MUD type:
1. Create a new parser in `src/core/parsers/your_mud.py`
2. Inherit from `BaseParser`
3. Register in `ParserFactory`
```python
# src/core/parsers/your_mud.py
from .base import BaseParser, ParsedRoom, ParsedPrompt
class YourMudParser(BaseParser):
def parse_room(self, text: str) -> Optional[ParsedRoom]:
# Implement room parsing
pass
def parse_prompt(self, text: str) -> Optional[ParsedPrompt]:
# Implement prompt parsing
pass
```
Then register in `factory.py`:
```python
from .your_mud import YourMudParser
PARSERS = {
"dead_souls": DeadSoulsParser,
"your_mud": YourMudParser, # Add this
}
```
## Running Tests
```bash
# Run all tests
pytest tests/ -v
# Run with coverage
pytest --cov=src --cov-report=html
# Run specific test file
pytest tests/test_world_mapper.py -v
# Run with watch mode
pytest-watch
```
## Code Style
We use Black and Ruff for code formatting and linting:
```bash
# Format code
black src/ tests/
# Lint code
ruff check src/ tests/
# Auto-fix linting issues
ruff check --fix src/ tests/
```
### Style Guidelines
- Line length: 100 characters
- Use type hints where possible
- Write docstrings for public methods
- Follow PEP 8 naming conventions
## Testing Guidelines
### Unit Tests
Test individual components in isolation:
```python
# tests/test_your_module.py
def test_room_parsing():
parser = YourMudParser()
room = parser.parse_room("Room Title\nDescription\n[Exits: n s]")
assert room.title == "Room Title"
assert "n" in room.exits
```
### Integration Tests
Test component interactions:
```python
@pytest.mark.asyncio
async def test_full_exploration():
# Setup
mapper = WorldMapper()
engine = DecisionEngine()
# Execute
await engine.tick(game_state)
action = engine.next_action()
# Assert
assert action is not None
```
### Mocking
Use mocks to isolate tests:
```python
from unittest.mock import Mock, patch
def test_with_mock():
mock_client = Mock()
mock_client.send.return_value = None
with patch('src.core.telnet_client.TelnetClient', return_value=mock_client):
# Test code here
pass
```
## Debugging
### Logging
The application uses Python's standard logging module:
```python
import logging
logger = logging.getLogger(__name__)
# In your code
logger.debug("Detailed debug info")
logger.info("General information")
logger.warning("Warning message")
logger.error("Error message")
```
View logs in real-time:
```bash
tail -f data/logs/game.log
```
### Debug Mode
Set logging level to DEBUG in config:
```yaml
logging:
level: "DEBUG"
```
### Web Dashboard
The monitoring dashboard provides real-time debugging:
```yaml
monitoring:
enabled: true
host: "127.0.0.1"
port: 8080
```
Then open http://127.0.0.1:8080 in your browser.
## Performance Profiling
```bash
# Profile the application
python -m cProfile -o profile.stats -m src.main
# View profile results
python -c "import pstats; p = pstats.Stats('profile.stats'); p.sort_stats('cumulative'); p.print_stats(20)"
```
## Git Workflow
1. Create a feature branch: `git checkout -b feature/my-feature`
2. Make your changes
3. Run tests: `pytest tests/ -v`
4. Format code: `black src/ tests/`
5. Lint: `ruff check src/ tests/`
6. Commit: `git commit -m "Add my feature"`
7. Push: `git push origin feature/my-feature`
8. Create Pull Request
## Commit Message Guidelines
Use conventional commits format:
- `feat: Add new feature`
- `fix: Fix bug in parser`
- `docs: Update documentation`
- `test: Add tests`
- `refactor: Refactor module`
- `chore: Update dependencies`
## Common Development Tasks
### Adding a New Behavior State
```python
# src/modules/decision/behavior_states.py
class MyCustomState(BaseBehaviorState):
@property
def name(self) -> str:
return "my_state"
def get_actions(self, game_state: GameState) -> List[Action]:
actions = []
# Your logic here
return actions
```
Then register in `DecisionEngine`:
```python
self._states = {
# ... existing states
"my_state": MyCustomState(),
}
```
### Extending the Mapper
To add custom room metadata:
```python
# In world_mapper.py
def on_room_enter(self, title, description, exits, **kwargs):
room_data = {
# ... existing fields
"custom_field": kwargs.get("custom_field"),
}
# ... rest of method
```
## Troubleshooting
### Import Errors
If you get import errors, ensure you're in the virtual environment:
```bash
poetry shell
# or
source venv/bin/activate
```
### Telnet Connection Issues
Test connection manually:
```bash
telnet your-mud-server.com 4000
```
### Database Locked
If SQLite reports "database is locked":
```bash
# Kill any hanging Python processes
pkill -f "python -m src.main"
# Remove lock file if it exists
rm data/maps/active/*.db-journal
```
## Contributing
1. Check existing issues before creating new ones
2. Discuss major changes in an issue first
3. Write tests for new features
4. Update documentation
5. Follow code style guidelines
6. Be respectful in communications
## Resources
- [Python Asyncio Documentation](https://docs.python.org/3/library/asyncio.html)
- [NetworkX Documentation](https://networkx.org/documentation/stable/)
- [Pydantic Documentation](https://docs.pydantic.dev/)
- [pytest Documentation](https://docs.pytest.org/)