This document describes the modular architecture for the Tavus Python client, which replaces the monolithic state_machine.py with a plugin-based system that allows for easy extension and maintenance.
- Purpose: Central orchestrator that manages state transitions and module coordination
- Responsibilities:
- Maintains current state
- Routes state execution to appropriate modules
- Provides common state constants via
CommonStates - Manages module registration and discovery
- Uses module-defined choice-to-state mappings (no hardcoding)
- Provides API client access to modules
- Purpose: Defines the contract that all modules must implement
- Key Components:
ModuleInterface: Abstract base class for all modulesCommonStates: Constants for common states to avoid hardcodingModuleRegistry: Manages module registration and state routing
Each module is self-contained and implements the ModuleInterface:
- States:
set_api_key - Functionality: API key management and validation
- Menu Option: "Set API Key" →
set_api_key
- States:
work_with_replicas,create_replica,list_replicas,rename_replica,delete_replica - Functionality: Complete replica lifecycle management
- Create replicas with name, description, and video file
- List replicas with pagination and filtering (user/system/all)
- Rename existing replicas
- Delete user replicas (system replicas cannot be deleted)
- Sectioned display for "all" filter (User Replicas / System Replicas)
- Menu Option: "Work with Replicas" →
work_with_replicas
- States:
work_with_personas,create_persona,list_personas,delete_persona - Functionality: Complete persona lifecycle management
- Create personas with name, system prompt, context, and optional default replica
- List personas with pagination and filtering (user/system)
- Delete user personas (system personas cannot be deleted)
- Default to user personas for listing
- Replica selection during creation with sectioned display
- Menu Option: "Work with Personas" →
work_with_personas
- States:
work_with_videos,generate_video,list_videos,rename_video,delete_video - Functionality: Complete video lifecycle management
- Generate videos with name, replica selection, and script
- List videos with pagination (no filtering - only user videos shown)
- Rename existing videos
- Delete videos
- Replica selection during creation with sectioned display
- Menu Option: "Work with Videos" →
work_with_videos
- Approach: All states are represented as strings
- Benefits:
- Simple and readable
- Easy to debug and log
- No enum import complexity
- Flexible for dynamic state creation
- Trade-offs:
- No compile-time validation
- Potential for typos
- Mitigation: Use
CommonStatesconstants to avoid hardcoding
- Purpose: Eliminate hardcoded string literals in modules
- Implementation:
CommonStatesclass provides constants likeMAIN_MENUandEXIT - Benefits:
- Modules don't need to know about state machine internals
- Centralized state name management
- Easy refactoring if state names change
- Purpose: Eliminate hardcoded mappings in the state machine
- Implementation: Each module defines its own
get_choice_to_state_mapping()method - Benefits:
- State machine doesn't need to know about module internals
- Modules control their own menu-to-state relationships
- Easy to add new modules without modifying state machine
- Complete encapsulation of module behavior
- Principle: Each module defines its own states and handles its own logic
- Benefits:
- Clear separation of concerns
- Independent development and testing
- Easy to add/remove functionality
- Principle: State machine routes states to appropriate modules
- Implementation: Registry pattern for state-to-module mapping
- Benefits:
- Centralized state management
- Easy to add new modules
- Clear state ownership
- Principle: Each module manages its own data lists independently
- Implementation: Modules maintain local storage for their entities (replicas, personas, videos)
- Benefits:
- No centralized data dependencies
- Modules can cache data for performance
- Independent data refresh cycles
- Better encapsulation
- Principle: Modules call state machine update methods instead of directly manipulating data
- Implementation: State machine provides
update_*()andremove_*()methods - Benefits:
- Centralized data management
- Consistent update patterns
- Better error handling
- Easier debugging
class ModuleInterface(ABC):
def get_name(self) -> str:
"""Return the module name"""
def get_states(self) -> List[str]:
"""Return list of state names this module handles"""
def get_menu_options(self) -> List[str]:
"""Return menu options this module provides"""
def get_choice_to_state_mapping(self) -> Dict[str, str]:
"""Return mapping from menu choices to states"""
def execute_state(self, state: str, state_machine) -> str:
"""Execute a specific state and return the next state"""- Main Menu: State machine shows menu options from all registered modules
- Choice Selection: User selects an option, state machine looks up state using module-defined mapping
- State Execution: Module executes the state and returns the next state
- State Transition: State machine updates current state and continues the loop
- Purpose: Provides consistent pagination across all modules
- Features:
- Configurable items per page
- Navigation controls (next/previous page)
- Filter option support
- Custom item selection callbacks
- Empty state handling
- Purpose: Enhanced pagination with sectioned display
- Features:
- Sections with headers (e.g., "User Replicas", "System Replicas")
- Automatic section detection and display
- Consistent with regular pagination interface
- Used for "all" filter views
- Implementation: Consistent filtering across modules
- Replica Filters: "user", "system", "all" (with sections)
- Persona Filters: "user", "system" (API-level filtering)
- Video Filters: No filtering (user videos only)
- Each feature is self-contained in its own module
- Easy to add new features without modifying existing code
- Clear separation of concerns
- Smaller, focused files are easier to understand and modify
- Changes to one module don't affect others
- Clear interfaces between components
- New modules can be added without changing core state machine
- Modules can be developed independently
- Easy to implement feature flags or conditional loading
- Each module can be tested in isolation
- Mock state machine for unit testing
- Clear interfaces make testing straightforward
- Modules use
CommonStatesconstants instead of string literals - State machine uses module-defined choice-to-state mappings
- Complete decoupling between modules and state machine
- Consistent pagination and filtering across all modules
- Sectioned displays for better organization
- Intuitive navigation and selection patterns
- Proper error handling and user feedback
- Create module interface and registry
- Implement core state machine
- Create API key module
- Create replica module
- Implement choice-to-state mapping system
- Remove centralized state definitions
- Implement string-only states
- Add CommonStates constants
- Extract persona management into module
- Extract video management into module
- Remove old state machine (
state_machine.pydeleted) - Implement local data management in modules
- Add update method pattern
- Implement pagination and filtering system
- Add sectioned display for replica selection
- Add comprehensive pagination system
- Implement filtering with API integration
- Add sectioned displays for better UX
- Implement replica selection in persona creation
- Add video rename functionality
- Optimize persona listing (no reload on page change)
- Fix filter labels and API integration
- Remove redundant print statements
- Set and validate API keys
- Persistent storage
- Error handling for invalid keys
- Create: Upload video files with name and description
- List: Paginated view with user/system/all filters
- Rename: Update replica names
- Delete: Remove user replicas (system replicas protected)
- Sections: User/System sections when viewing all replicas
- Create: Define personas with system prompts and optional default replicas
- List: Paginated view with user/system filters (defaults to user)
- Delete: Remove user personas (system personas protected)
- Replica Selection: Choose default replica during creation with sectioned display
- Generate: Create videos with replica selection and scripts
- List: Paginated view of user videos
- Rename: Update video names
- Delete: Remove videos
- Replica Selection: Choose replica during creation with sectioned display
# Create state machine
state_machine = ModularStateMachine()
# Register modules (done automatically in main.py)
state_machine.register_module(ApiKeyModule())
state_machine.register_module(ReplicaModule())
state_machine.register_module(PersonaModule())
state_machine.register_module(VideoModule())
# Run the state machine
while not state_machine.is_exit_state():
state_machine.execute_current_state()- Use descriptive, lowercase names with underscores
- Prefix module-specific states with module name if needed
- Use
CommonStatesconstants for common states
- Keep modules focused on a single responsibility
- Return
CommonStates.MAIN_MENUorCommonStates.EXITfor navigation - Handle errors gracefully and return to appropriate state
- Define clear choice-to-state mappings in
get_choice_to_state_mapping() - Manage local data storage for performance
- Use update methods for data manipulation
- Use the registry for state routing
- Provide clear interfaces for module communication
- Handle unknown states gracefully
- Never hardcode choice-to-state mappings in the state machine
- Modules should handle their own errors
- Return to safe states on errors
- Provide clear error messages to users
- Use try-catch blocks for API calls
- Provide consistent pagination and filtering
- Use sectioned displays for better organization
- Give clear feedback for all operations
- Handle empty states gracefully
- Cache data locally in modules
- Fetch data only when needed
- Use pagination to handle large datasets
- Optimize API calls with proper filtering
This architecture provides a solid foundation for a maintainable and extensible Tavus Python client while eliminating all hardcoded dependencies between modules and the state machine. The modular design allows for easy feature additions and modifications while maintaining a consistent user experience across all functionality.