v2.0.0 — Pure glue architecture: zero copied code, pluggable channels

BREAKING: Complete architectural rewrite.

Before: Copied x-reader's fetcher code into readers/ (1205 lines of borrowed code)
After: Pluggable channel system where each channel is a thin wrapper (~50 lines)
       around the best external tool for that platform. Zero copied code.

Architecture:
- channels/base.py — Universal Channel interface (read, search, check)
- channels/web.py — Jina Reader API (swappable)
- channels/github.py — GitHub API (swappable)
- channels/twitter.py — birdx + Jina fallback (swappable)
- channels/youtube.py — yt-dlp (swappable)
- channels/reddit.py — Reddit JSON API + proxy (swappable)
- channels/rss.py — feedparser (swappable)
- channels/bilibili.py — Bilibili API (swappable)
- channels/exa_search.py — Exa semantic search (swappable)

Key design: every backend can be swapped by changing ONE file.
YouTube dies? Change youtube.py. Exa sucks? Swap exa_search.py for Tavily.
Nothing else changes.

Removed: reader.py, schema.py, readers/, search/, utils/ (all x-reader code)
Tests: 36/36 passing
This commit is contained in:
Panniantong
2026-02-24 05:38:21 +01:00
parent 7e4cd961ee
commit 74c3df5c3d
38 changed files with 1155 additions and 2725 deletions
+13 -44
View File
@@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
"""Tests for Agent Eyes doctor module."""
"""Tests for doctor module."""
import pytest
from unittest.mock import patch, MagicMock
from agent_eyes.config import Config
from agent_eyes.doctor import check_all, format_report, STATUS_OK, STATUS_OFF
from agent_eyes.doctor import check_all, format_report
@pytest.fixture
@@ -14,54 +12,25 @@ def tmp_config(tmp_path):
class TestDoctor:
def test_zero_config_platforms_always_ok(self, tmp_config):
def test_zero_config_channels_ok(self, tmp_config):
results = check_all(tmp_config)
assert results["web"]["status"] == STATUS_OK
assert results["github_read"]["status"] == STATUS_OK
assert results["bilibili"]["status"] == STATUS_OK
assert results["rss"]["status"] == STATUS_OK
assert results["tweet_read"]["status"] == STATUS_OK
assert results["web"]["status"] == "ok"
assert results["github"]["status"] == "ok"
assert results["bilibili"]["status"] == "ok"
assert results["rss"]["status"] == "ok"
def test_search_off_without_exa_key(self, tmp_config):
def test_exa_off_without_key(self, tmp_config):
results = check_all(tmp_config)
assert results["search_web"]["status"] == STATUS_OFF
assert results["search_reddit"]["status"] == STATUS_OFF
assert results["search_twitter"]["status"] == STATUS_OFF
assert results["exa_search"]["status"] == "off"
def test_search_on_with_exa_key(self, tmp_config):
def test_exa_on_with_key(self, tmp_config):
tmp_config.set("exa_api_key", "test-key")
results = check_all(tmp_config)
assert results["search_web"]["status"] == STATUS_OK
assert results["search_reddit"]["status"] == STATUS_OK
assert results["search_twitter"]["status"] == STATUS_OK
assert results["exa_search"]["status"] == "ok"
def test_github_search_always_on(self, tmp_config):
results = check_all(tmp_config)
assert results["search_github"]["status"] == STATUS_OK
def test_reddit_full_off_without_proxy(self, tmp_config):
results = check_all(tmp_config)
assert results["reddit_full"]["status"] == STATUS_OFF
def test_reddit_full_on_with_proxy(self, tmp_config):
tmp_config.set("reddit_proxy", "http://user:pass@ip:port")
results = check_all(tmp_config)
assert results["reddit_full"]["status"] == STATUS_OK
@patch("agent_eyes.doctor._check_command")
def test_birdx_detection(self, mock_cmd, tmp_config):
mock_cmd.return_value = True
results = check_all(tmp_config)
assert results["twitter_advanced"]["status"] == STATUS_OK
def test_format_report_is_string(self, tmp_config):
def test_format_report(self, tmp_config):
results = check_all(tmp_config)
report = format_report(results)
assert isinstance(report, str)
assert "Agent Eyes" in report
assert "" in report
def test_format_report_shows_count(self, tmp_config):
results = check_all(tmp_config)
report = format_report(results)
assert "platforms active" in report
assert "channels active" in report