Skip to content

About

πŸ€– Autonomous, self-hosted AI agent architecture designed to manage local Arr media stacks via natural language, LangGraph, FastMCP & Ollama.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

11 Commits

Folders and files

Repository files navigation

Radarr-Agent

Radarr-Agent is an autonomous, self-hosted AI agent architecture designed to manage local media libraries via natural language. Built with LangGraph, FastMCP, and Ollama, Radarr-Agent connects local large language models (LLMs) to a self-hosted Arr stack (Radarr, qBittorrent, Prowlarr, FlareSolverr).

The system features real-time state persistence, accurate ChatML token tracking, asynchronous non-blocking tool invocation, and dynamic MCP-to-LangChain tool remapping.


1. System Architecture & Workflow

Radarr-Agent uses a stateful ReAct (Reasoning + Acting) loop powered by LangGraph. It processes user intent, inspects library state, queries external metadata indexes, and executes media pipeline mutations through Radarr's REST v3 API.

                      +-----------------------------+
                      |     User CLI Interface      |
                      +--------------+--------------+
                                     |
                                     v
                      +-----------------------------+
                      |    LangGraph ReAct Loop     |
                      |         (agent.py)          |
                      +--------------+--------------+
                                     |
           +-------------------------+-------------------------+
           |                                                   |
           v                                                   v
+-----------------------+                           +--------------------+
|  Ollama Model Engine  |                           | FastMCP Tool Server|
|      (qwen2.5:3b)     |                           |    (MCP Adapter)   |
+-----------------------+                           +---------+----------+
                                                              |
                                                              v
                                                    +--------------------+
                                                    | Async Radarr Client|
                                                    |    (client.py)     |
                                                    +---------+----------+
                                                              |
                                                              v
                                                    +--------------------+
                                                    | Docker Media Stack |
                                                    | Radarr | qBittorr. |
                                                    | Prowl. | FlareSol. |
                                                    +--------------------+

Key Technical Mechanisms

  • Stateful ReAct Loop (agent.py): Manages conversation history (AgentState) using LangGraph message annotations, directing flow conditionally between LLM evaluation and tool execution.
  • Dynamic FastMCP Conversion (convert_fastmcp_to_langchain_tool): Maps FastMCP tool input schemas into Pydantic models at runtime, wrapping execution in asynchronous calls to prevent event loop deadlocks.
  • Exact Token Accounting (count_qwen_tokens): Uses HuggingFace's AutoTokenizer for Qwen/Qwen2.5-3B to calculate exact context window usage, accounting for ChatML format headers (<|im_start|>, <|im_end|>) and tool call JSON payloads.
  • Atomic State Dumping (periodic_state_logger): Periodically serializes full conversation state and metadata into agent_state.json. Uses atomic file replacements (.tmp write followed by os.replace) to ensure zero read-collision for external monitoring scripts.
  • Asynchronous Client Engine (client.py): Built on httpx.AsyncClient with custom header management (X-Api-Key), handling non-blocking API communications with Radarr.

2. Infrastructure & Service Topology

The media management stack is containerized using Docker Compose to ensure network isolation, persistent storage, and reproducible deployment.

Service Matrix

Service Port Description
Radarr 7878 Movie library management, quality profiles, metadata tracking, and disk mapping
qBittorrent 8081 (WebUI)
6881 (Peer)
BitTorrent download client for handling media retrieval tasks
Prowlarr 9696 Indexer management platform connected to torrent and usenet providers
FlareSolverr 8191 Cloudflare challenge solver proxy for Prowlarr indexers

Directory Mapping Structure

  • /Media/downloads: Shared volume between qBittorrent and Radarr for completed downloads.
  • /Media/Movies: Target library storage directory monitored and organized by Radarr.
  • Config directories (/config): Stored locally per service to maintain persistent database state across container restarts.

3. Directory Layout

.
β”œβ”€β”€ agent.py            # LangGraph agent loop, FastMCP converter, and state logger
β”œβ”€β”€ client.py           # Asynchronous Radarr v3 API HTTP client
β”œβ”€β”€ schemas.py          # Pydantic state models, action responses, and lookup schemas
β”œβ”€β”€ prompts.py          # System prompt, operational constraints, and few-shot examples
β”œβ”€β”€ log.py              # Logging setup and log level suppression for HTTP clients
β”œβ”€β”€ docker-compose.yml  # Container orchestration for media automation stack
β”œβ”€β”€ pyproject.toml      # Dependency definitions and project metadata
└── README.md           # Technical documentation

4. Setup & Deployment Guide

Prerequisites

  • Linux / macOS / Windows with WSL2
  • Python >=3.13
  • Docker and Docker Compose
  • Ollama installed and running locally

Step 1: Clone Repository & Install Dependencies

git clone https://github.com/your-username/Radarr-Agent.git
cd Radarr-Agent

# Using uv (Recommended)
uv sync

# Using standard pip
pip install -r pyproject.toml

Step 2: Start Container Stack

docker compose up -d

Verify that all services are running correctly:

docker compose ps

Step 3: Configure Environment Variables

Create a .env file in the project root:

RADARR_URL=http://localhost:7878
RADARR_API=your_radarr_api_key_here

Note: Obtain your API Key from Radarr via Settings -> General -> API Key.

Step 4: Pull Model & Run MCP Server

Pull the required language model into Ollama:

ollama pull qwen2.5:3b

Ensure your FastMCP server hosting the Radarr tools is running at http://127.0.0.1:8000/mcp.

Step 5: Start Radarr-Agent CLI

python -m agent

5. Operational Logic & Rules

Radarr-Agent operates under strict logic rules to ensure accurate state reporting and prevent unintended mutations:

  1. "Ready to Watch" Distinction:

    • has_file == True: Movie is downloaded, stored on disk, and ready for immediate viewing.
    • in_library == True & has_file == False: Movie is monitored/queued in Radarr, but currently downloading or missing files. Cannot be watched yet.
    • in_library == False: Movie is not present in the system.
  2. Targeted vs. Global Tool Invocation:

    • search_movie(term): Used for title lookup, status checking, or pre-addition verification.
    • list_library_movies(): Used strictly when the user requests an overall summary of the collection.
    • add_movie_to_library(tmdb_id): Requires explicit user confirmation before execution.
    • delete_movie_from_library(id): Invoked strictly on explicit removal requests.

6. Example CLI Session

==================================================
πŸ€– AI Chatbot Initialized (Type 'exit' or 'quit' to stop)
πŸ“ Dumping state to 'agent_state.json'
==================================================

You: Do we have Inception ready to watch?
AI: Yes, Inception (2010) is in your library and ready to watch.

You: Is Gladiator downloaded?
AI: Gladiator (2000) is in your library, but it has not finished downloading yet and is not ready to watch.

You: Can we watch Interstellar tonight?
AI: Interstellar is currently not in your movie library. Would you like me to add it for you?

You: Yes, please add it.
AI: Interstellar (2014) has been added to Radarr and is now queued for downloading.

7. Troubleshooting & Common Issues

  • MCP Client Connection Error: Ensure the FastMCP server process is active on port 8000 before starting agent.py.
  • Out of Memory / Context Error: Ensure Ollama has sufficient RAM/VRAM allocated for qwen2.5:3b.
  • API Authentication Failures: Double check RADARR_API in .env and verify Radarr is reachable at http://localhost:7878/api/v3/system/status.

8. Conclusion & Engineering Lessons

Building Radarr-Agent was a journey through the realities of stateful, local-first AI development. Moving away from standard cloud APIs toward running everything on local hardware introduced distinct engineering challenges:

  • Handling Tool Schemas Dynamically: Adapting MCP schemas into LangChain tools without blocking the asyncio event loop required deep debugging into function signatures and dynamic type instantiation.
  • Token Management Precision: Small local models like qwen2.5:3b have strict context windows. Relying on rough word counts led to unexpected truncation. Implementing exact token counting via HuggingFace's tokenizer was a key turning point for system reliability.
  • Async Synchronization: Coordinating HTTP clients, file logging, terminal user inputs, and LangGraph streams asynchronously required careful handling of locks and background tasks to keep execution smooth.

Ultimately, building Radarr-Agent demonstrated that with the right architecture, self-hosted local agents can deliver private, precise, and practical automation without relying on external cloud infrastructure.

About

πŸ€– Autonomous, self-hosted AI agent architecture designed to manage local Arr media stacks via natural language, LangGraph, FastMCP & Ollama.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages