feat: add QR login and browser-backed web tools

This commit is contained in:
Azimkin
2026-09-14 14:19:20 +02:00
parent 3a8c198caa
commit b68e715267
19 changed files with 2740 additions and 8 deletions

View File

@@ -31,6 +31,7 @@ class AccountConfig:
no_updates: bool = False
takeout: bool = False
hide_password: bool = False
use_qr: bool = False
@dataclass(slots=True)
@@ -69,6 +70,8 @@ class AiConfig:
max_tool_rounds: int = 16
brave_api_key: str | None = None
brave_timeout_seconds: float = 10.0
web_page_timeout_seconds: float = 45.0
web_page_max_chars: int = 60_000
@dataclass(slots=True)
@@ -93,6 +96,20 @@ def _parse_int(value: Any, *, field_name: str) -> int:
raise ValueError(f"{field_name} must be an integer") from exc
def _parse_bool(value: Any, *, field_name: str) -> bool:
if isinstance(value, bool):
return value
if isinstance(value, str):
normalized = value.strip().lower()
if normalized in {"1", "true", "yes", "on"}:
return True
if normalized in {"0", "false", "no", "off"}:
return False
if isinstance(value, int):
return bool(value)
raise ValueError(f"{field_name} must be a boolean")
def _parse_float(value: Any, *, field_name: str) -> float:
try:
return float(value)
@@ -206,6 +223,14 @@ def _parse_ai_config(raw: Any) -> AiConfig:
raw_config.get("brave_timeout_seconds", 10),
field_name="ai.brave_timeout_seconds",
)
web_page_timeout_seconds = _parse_float(
raw_config.get("web_page_timeout_seconds", 45),
field_name="ai.web_page_timeout_seconds",
)
web_page_max_chars = _parse_int(
raw_config.get("web_page_max_chars", 60_000),
field_name="ai.web_page_max_chars",
)
raw_brave_key = raw_config.get("brave_api_key")
brave_api_key = str(raw_brave_key).strip() if raw_brave_key is not None else None
@@ -234,6 +259,16 @@ def _parse_ai_config(raw: Any) -> AiConfig:
os.environ["BRAVE_SEARCH_TIMEOUT_SECONDS"],
field_name="BRAVE_SEARCH_TIMEOUT_SECONDS",
)
if os.environ.get("AI_WEB_PAGE_TIMEOUT_SECONDS") is not None:
web_page_timeout_seconds = _parse_float(
os.environ["AI_WEB_PAGE_TIMEOUT_SECONDS"],
field_name="AI_WEB_PAGE_TIMEOUT_SECONDS",
)
if os.environ.get("AI_WEB_PAGE_MAX_CHARS") is not None:
web_page_max_chars = _parse_int(
os.environ["AI_WEB_PAGE_MAX_CHARS"],
field_name="AI_WEB_PAGE_MAX_CHARS",
)
if max_context_messages < 0:
raise ValueError("ai.max_context_messages must not be negative")
@@ -243,6 +278,10 @@ def _parse_ai_config(raw: Any) -> AiConfig:
raise ValueError("ai.max_tool_rounds must be greater than zero")
if brave_timeout_seconds <= 0:
raise ValueError("ai.brave_timeout_seconds must be greater than zero")
if web_page_timeout_seconds <= 0:
raise ValueError("ai.web_page_timeout_seconds must be greater than zero")
if web_page_max_chars < 1:
raise ValueError("ai.web_page_max_chars must be greater than zero")
return AiConfig(
glm=glm,
@@ -252,6 +291,8 @@ def _parse_ai_config(raw: Any) -> AiConfig:
max_tool_rounds=max_tool_rounds,
brave_api_key=brave_api_key,
brave_timeout_seconds=brave_timeout_seconds,
web_page_timeout_seconds=web_page_timeout_seconds,
web_page_max_chars=web_page_max_chars,
)
@@ -314,6 +355,10 @@ def _parse_accounts(raw_accounts: Any, *, default_api_id: int | None, default_ap
no_updates=bool(raw_account.get("no_updates", False)),
takeout=bool(raw_account.get("takeout", False)),
hide_password=bool(raw_account.get("hide_password", False)),
use_qr=_parse_bool(
raw_account.get("use_qr", False),
field_name=f"accounts[{index}].use_qr",
),
)
)