Track bundled vendor runtime sources
This commit is contained in:
70
vendor/Agent-Reach/agent_reach/channels/base.py
vendored
Normal file
70
vendor/Agent-Reach/agent_reach/channels/base.py
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Channel base class — platform availability checking.
|
||||
|
||||
Each channel represents a platform (YouTube, Twitter, GitHub, etc.)
|
||||
and provides:
|
||||
- can_handle(url) → does this URL belong to this platform?
|
||||
- check(config) → is the upstream tool installed and configured?
|
||||
|
||||
After installation, agents call upstream tools directly.
|
||||
|
||||
Backend routing semantics:
|
||||
- `backends` is an ORDERED candidate list: backends[0] is the preferred
|
||||
backend, the rest are fallbacks. "Switching backends" for a platform
|
||||
means reordering this list (or a user override) — not rewriting code.
|
||||
- check() must set `self.active_backend` to the backend that is actually
|
||||
serving the channel right now (None when nothing usable is found).
|
||||
shutil.which() alone is NOT proof of health — a stale venv shim passes
|
||||
which() but cannot execute (see agent_reach.probe). Channels should
|
||||
really execute a lightweight command before claiming a backend active.
|
||||
- Users can force a backend with config key `<channel>_backend`
|
||||
(or env var `<CHANNEL>_BACKEND`); ordered_backends() applies it.
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
|
||||
class Channel(ABC):
|
||||
"""Base class for all channels."""
|
||||
|
||||
name: str = "" # e.g. "youtube"
|
||||
description: str = "" # e.g. "YouTube 视频和字幕"
|
||||
backends: List[str] = [] # ordered candidates — backends[0] = preferred
|
||||
tier: int = 0 # 0=zero-config, 1=needs free key, 2=needs setup
|
||||
|
||||
#: Backend currently serving this channel; set by check(), None = unavailable.
|
||||
active_backend: Optional[str] = None
|
||||
|
||||
@abstractmethod
|
||||
def can_handle(self, url: str) -> bool:
|
||||
"""Check if this channel can handle this URL."""
|
||||
...
|
||||
|
||||
def ordered_backends(self, config=None) -> List[str]:
|
||||
"""Candidate backends in probe order, honoring the user override.
|
||||
|
||||
The config key `<channel>_backend` (env `<CHANNEL>_BACKEND`) moves the
|
||||
named backend to the front of the list; unknown values are ignored so
|
||||
a stale override can never hide working backends.
|
||||
"""
|
||||
candidates = list(self.backends)
|
||||
override = config.get(f"{self.name}_backend") if config else None
|
||||
if override:
|
||||
for i, b in enumerate(candidates):
|
||||
if b == override or b.startswith(override):
|
||||
candidates.insert(0, candidates.pop(i))
|
||||
break
|
||||
return candidates
|
||||
|
||||
def check(self, config=None) -> Tuple[str, str]:
|
||||
"""
|
||||
Check if this channel's upstream tool is available.
|
||||
Returns (status, message) where status is 'ok'/'warn'/'off'/'error'.
|
||||
|
||||
Subclasses with external backends must really probe them (see
|
||||
agent_reach.probe.probe_command) and set self.active_backend.
|
||||
"""
|
||||
self.active_backend = self.backends[0] if self.backends else "内置"
|
||||
return "ok", f"{'、'.join(self.backends) if self.backends else '内置'}"
|
||||
Reference in New Issue
Block a user