Files
alfred/brain/docs/class_diagram.md

9.5 KiB

Class Diagram - Agent Media

classDiagram
    direction TB

    %% ===========================================
    %% MEMORY SYSTEM
    %% ===========================================

    class Memory {
        +Path storage_dir
        +Path ltm_file
        +LongTermMemory ltm
        +ShortTermMemory stm
        +EpisodicMemory episodic
        +__init__(storage_dir: str)
        +save() void
        +get_context_for_prompt() Dict
        +get_full_state() Dict
        +clear_session() void
    }

    class LongTermMemory {
        +Dict config
        +Dict preferences
        +Dict~str, List~ library
        +List~Dict~ following
        +get_config(key: str) Any
        +set_config(key: str, value: Any) void
        +has_config(key: str) bool
        +add_to_library(media_type: str, media: Dict) void
        +get_library(media_type: str) List
        +follow_show(show: Dict) void
        +to_dict() Dict
        +from_dict(data: Dict)$ LongTermMemory
    }

    class ShortTermMemory {
        +List~Dict~ conversation_history
        +Dict current_workflow
        +Dict extracted_entities
        +str current_topic
        +int max_history
        +add_message(role: str, content: str) void
        +get_recent_history(n: int) List
        +start_workflow(type: str, target: Dict) void
        +update_workflow_stage(stage: str) void
        +end_workflow() void
        +set_entity(key: str, value: Any) void
        +get_entity(key: str) Any
        +clear() void
        +to_dict() Dict
    }

    class EpisodicMemory {
        +Dict last_search_results
        +List~Dict~ active_downloads
        +List~Dict~ recent_errors
        +Dict pending_question
        +List~Dict~ background_events
        +store_search_results(query: str, results: List) void
        +get_result_by_index(index: int) Dict
        +get_search_results() Dict
        +add_active_download(download: Dict) void
        +complete_download(task_id: str, path: str) Dict
        +add_error(action: str, error: str) void
        +set_pending_question(question: str, options: List) void
        +resolve_pending_question(index: int) Dict
        +add_background_event(type: str, data: Dict) void
        +get_unread_events() List
        +clear() void
        +to_dict() Dict
    }

    Memory *-- LongTermMemory : ltm
    Memory *-- ShortTermMemory : stm
    Memory *-- EpisodicMemory : episodic

    %% ===========================================
    %% AGENT SYSTEM
    %% ===========================================

    class Agent {
        +LLMClient llm
        +Memory memory
        +Dict~str, Tool~ tools
        +PromptBuilder prompt_builder
        +int max_tool_iterations
        +__init__(llm: LLMClient, memory: Memory)
        +step(user_input: str) str
        -_parse_intent(text: str) Dict
        -_execute_action(intent: Dict) Dict
        -_check_unread_events() str
    }

    class LLMClient {
        <<Protocol>>
        +complete(messages: List) str
    }

    class DeepSeekClient {
        +str api_key
        +str model
        +str base_url
        +complete(messages: List) str
    }

    class OllamaClient {
        +str base_url
        +str model
        +complete(messages: List) str
    }

    class PromptBuilder {
        +Dict~str, Tool~ tools
        +__init__(tools: Dict)
        +build_system_prompt(memory: Memory) str
        -_format_tools_description() str
        -_format_episodic_context(memory: Memory) str
        -_format_stm_context(memory: Memory) str
    }

    class Tool {
        <<dataclass>>
        +str name
        +str description
        +Callable func
        +Dict parameters
    }

    Agent --> LLMClient : uses
    Agent --> Memory : uses
    Agent --> PromptBuilder : uses
    Agent --> Tool : executes
    DeepSeekClient ..|> LLMClient
    OllamaClient ..|> LLMClient
    PromptBuilder --> Tool : formats

    %% ===========================================
    %% DOMAIN - MOVIES
    %% ===========================================

    class Movie {
        <<Entity>>
        +ImdbId imdb_id
        +MovieTitle title
        +ReleaseYear release_year
        +Quality quality
        +FilePath file_path
        +FileSize file_size
        +int tmdb_id
        +datetime added_at
    }

    class MovieTitle {
        <<ValueObject>>
        +str value
        +__init__(value: str)
    }

    class ReleaseYear {
        <<ValueObject>>
        +int value
        +__init__(value: int)
    }

    class Quality {
        <<ValueObject>>
        +str value
        +__init__(value: str)
    }

    class MovieRepository {
        <<Interface>>
        +save(movie: Movie) void
        +find_by_imdb_id(imdb_id: ImdbId) Movie
        +find_all() List~Movie~
        +delete(imdb_id: ImdbId) bool
        +exists(imdb_id: ImdbId) bool
    }

    Movie --> MovieTitle
    Movie --> ReleaseYear
    Movie --> Quality
    Movie --> ImdbId

    %% ===========================================
    %% DOMAIN - TV SHOWS
    %% ===========================================

    class TVShow {
        <<Entity>>
        +ImdbId imdb_id
        +str title
        +int seasons_count
        +ShowStatus status
        +int tmdb_id
        +str first_air_date
        +datetime added_at
    }

    class ShowStatus {
        <<Enum>>
        CONTINUING
        ENDED
        UNKNOWN
        +from_string(value: str)$ ShowStatus
    }

    class TVShowRepository {
        <<Interface>>
        +save(show: TVShow) void
        +find_by_imdb_id(imdb_id: ImdbId) TVShow
        +find_all() List~TVShow~
        +delete(imdb_id: ImdbId) bool
    }

    TVShow --> ShowStatus
    TVShow --> ImdbId

    %% ===========================================
    %% DOMAIN - SHARED
    %% ===========================================

    class ImdbId {
        <<ValueObject>>
        +str value
        +__init__(value: str)
        +__str__() str
    }

    class FilePath {
        <<ValueObject>>
        +str value
        +__init__(value: str)
    }

    class FileSize {
        <<ValueObject>>
        +int bytes
        +__init__(bytes: int)
        +to_human_readable() str
    }

    %% ===========================================
    %% INFRASTRUCTURE - PERSISTENCE
    %% ===========================================

    class JsonMovieRepository {
        +Memory memory
        +__init__(memory: Memory)
        +save(movie: Movie) void
        +find_by_imdb_id(imdb_id: ImdbId) Movie
        +find_all() List~Movie~
        +delete(imdb_id: ImdbId) bool
    }

    class JsonTVShowRepository {
        +Memory memory
        +__init__(memory: Memory)
        +save(show: TVShow) void
        +find_by_imdb_id(imdb_id: ImdbId) TVShow
        +find_all() List~TVShow~
        +delete(imdb_id: ImdbId) bool
    }

    JsonMovieRepository ..|> MovieRepository
    JsonTVShowRepository ..|> TVShowRepository
    JsonMovieRepository --> Memory
    JsonTVShowRepository --> Memory

    %% ===========================================
    %% INFRASTRUCTURE - API CLIENTS
    %% ===========================================

    class TMDBClient {
        +str api_key
        +str base_url
        +search_movie(title: str) TMDBSearchResult
        +search_tv(title: str) TMDBSearchResult
        +get_external_ids(tmdb_id: int) Dict
    }

    class KnabenClient {
        +str base_url
        +search(query: str, limit: int) List~TorrentResult~
    }

    class QBittorrentClient {
        +str host
        +str username
        +str password
        +add_torrent(magnet: str) bool
        +get_torrents() List
    }

    %% ===========================================
    %% INFRASTRUCTURE - FILESYSTEM
    %% ===========================================

    class FileManager {
        +Memory memory
        +__init__(memory: Memory)
        +set_folder_path(name: str, path: str) Dict
        +list_folder(type: str, path: str) Dict
        +move_file(source: str, dest: str) Dict
    }

    FileManager --> Memory

    %% ===========================================
    %% APPLICATION - USE CASES
    %% ===========================================

    class SearchMovieUseCase {
        +TMDBClient tmdb_client
        +execute(title: str) SearchMovieResponse
    }

    class SearchTorrentsUseCase {
        +KnabenClient knaben_client
        +execute(title: str, limit: int) SearchTorrentsResponse
    }

    class AddTorrentUseCase {
        +QBittorrentClient qbittorrent_client
        +execute(magnet: str) AddTorrentResponse
    }

    class SetFolderPathUseCase {
        +FileManager file_manager
        +execute(folder_name: str, path: str) SetFolderPathResponse
    }

    class ListFolderUseCase {
        +FileManager file_manager
        +execute(folder_type: str, path: str) ListFolderResponse
    }

    SearchMovieUseCase --> TMDBClient
    SearchTorrentsUseCase --> KnabenClient
    AddTorrentUseCase --> QBittorrentClient
    SetFolderPathUseCase --> FileManager
    ListFolderUseCase --> FileManager

Legend

Symbol Meaning
<<Entity>> Domain entity with identity
<<ValueObject>> Immutable value object
<<Interface>> Abstract interface/protocol
<<Enum>> Enumeration
<<dataclass>> Python dataclass
<<Protocol>> Python Protocol (structural typing)
*-- Composition (owns)
--> Association (uses)
..|> Implementation

Architecture Layers

  1. Domain Layer - Business entities and rules (Movie, TVShow, ValueObjects)
  2. Application Layer - Use cases orchestrating business logic
  3. Infrastructure Layer - External services (APIs, filesystem, persistence)
  4. Agent Layer - AI agent, LLM clients, tools, prompts