mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
refactor: codebase-wide lint cleanup — unused imports, dead code, and inefficient patterns (#5821)
Comprehensive cleanup across 80 files based on automated (ruff, pyflakes, vulture)
and manual analysis of the entire codebase.
Changes by category:
Unused imports removed (~95 across 55 files):
- Removed genuinely unused imports from all major subsystems
- agent/, hermes_cli/, tools/, gateway/, plugins/, cron/
- Includes imports in try/except blocks that were truly unused
(vs availability checks which were left alone)
Unused variables removed (~25):
- Removed dead variables: connected, inner, channels, last_exc,
source, new_server_names, verify, pconfig, default_terminal,
result, pending_handled, temperature, loop
- Dropped unused argparse subparser assignments in hermes_cli/main.py
(12 instances of add_parser() where result was never used)
Dead code removed:
- run_agent.py: Removed dead ternary (None if False else None) and
surrounding unreachable branch in identity fallback
- run_agent.py: Removed write-only attribute _last_reported_tool
- hermes_cli/providers.py: Removed dead @property decorator on
module-level function (decorator has no effect outside a class)
- gateway/run.py: Removed unused MCP config load before reconnect
- gateway/platforms/slack.py: Removed dead SessionSource construction
Undefined name bugs fixed (would cause NameError at runtime):
- batch_runner.py: Added missing logger = logging.getLogger(__name__)
- tools/environments/daytona.py: Added missing Dict and Path imports
Unnecessary global statements removed (14):
- tools/terminal_tool.py: 5 functions declared global for dicts
they only mutated via .pop()/[key]=value (no rebinding)
- tools/browser_tool.py: cleanup thread loop only reads flag
- tools/rl_training_tool.py: 4 functions only do dict mutations
- tools/mcp_oauth.py: only reads the global
- hermes_time.py: only reads cached values
Inefficient patterns fixed:
- startswith/endswith tuple form: 15 instances of
x.startswith('a') or x.startswith('b') consolidated to
x.startswith(('a', 'b'))
- len(x)==0 / len(x)>0: 13 instances replaced with pythonic
truthiness checks (not x / bool(x))
- in dict.keys(): 5 instances simplified to in dict
- Redefined unused name: removed duplicate _strip_mdv2 import in
send_message_tool.py
Other fixes:
- hermes_cli/doctor.py: Replaced undefined logger.debug() with pass
- hermes_cli/config.py: Consolidated chained .endswith() calls
Test results: 3934 passed, 17 failed (all pre-existing on main),
19 skipped. Zero regressions.
This commit is contained in:
parent
afe6c63c52
commit
d0ffb111c2
80 changed files with 81 additions and 210 deletions
|
|
@ -15,7 +15,6 @@ Usage::
|
|||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from hermes_constants import get_hermes_home
|
||||
|
|
|
|||
|
|
@ -262,8 +262,6 @@ class SessionManager:
|
|||
if self._db_instance is not None:
|
||||
return self._db_instance
|
||||
try:
|
||||
import os
|
||||
from pathlib import Path
|
||||
from hermes_state import SessionDB
|
||||
hermes_home = get_hermes_home()
|
||||
self._db_instance = SessionDB(db_path=hermes_home / "state.db")
|
||||
|
|
|
|||
|
|
@ -188,9 +188,7 @@ def _requires_bearer_auth(base_url: str | None) -> bool:
|
|||
if not base_url:
|
||||
return False
|
||||
normalized = base_url.rstrip("/").lower()
|
||||
return normalized.startswith("https://api.minimax.io/anthropic") or normalized.startswith(
|
||||
"https://api.minimaxi.com/anthropic"
|
||||
)
|
||||
return normalized.startswith(("https://api.minimax.io/anthropic", "https://api.minimaxi.com/anthropic"))
|
||||
|
||||
|
||||
def build_anthropic_client(api_key: str, base_url: str = None):
|
||||
|
|
@ -847,7 +845,7 @@ def _convert_openai_image_part_to_anthropic(part: Dict[str, Any]) -> Optional[Di
|
|||
},
|
||||
}
|
||||
|
||||
if url.startswith("http://") or url.startswith("https://"):
|
||||
if url.startswith(("http://", "https://")):
|
||||
return {
|
||||
"type": "image",
|
||||
"source": {
|
||||
|
|
|
|||
|
|
@ -209,7 +209,6 @@ class _CodexCompletionsAdapter:
|
|||
def create(self, **kwargs) -> Any:
|
||||
messages = kwargs.get("messages", [])
|
||||
model = kwargs.get("model", self._model)
|
||||
temperature = kwargs.get("temperature")
|
||||
|
||||
# Separate system/instructions from conversation messages.
|
||||
# Convert chat.completions multimodal content blocks to Responses
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from __future__ import annotations
|
|||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from agent.memory_provider import MemoryProvider
|
||||
|
||||
|
|
|
|||
|
|
@ -10,21 +10,18 @@ import uuid
|
|||
import os
|
||||
import re
|
||||
from dataclasses import dataclass, fields, replace
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
|
||||
from hermes_constants import OPENROUTER_BASE_URL
|
||||
import hermes_cli.auth as auth_mod
|
||||
from hermes_cli.auth import (
|
||||
ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
|
||||
CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
|
||||
DEFAULT_AGENT_KEY_MIN_TTL_SECONDS,
|
||||
PROVIDER_REGISTRY,
|
||||
_agent_key_is_usable,
|
||||
_codex_access_token_is_expiring,
|
||||
_decode_jwt_claims,
|
||||
_import_codex_cli_tokens,
|
||||
_is_expiring,
|
||||
_load_auth_store,
|
||||
_load_provider_state,
|
||||
_resolve_zai_base_url,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from utils import atomic_json_write
|
||||
|
||||
|
|
@ -231,7 +231,7 @@ def fetch_models_dev(force_refresh: bool = False) -> Dict[str, Any]:
|
|||
response = requests.get(MODELS_DEV_URL, timeout=15)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
if isinstance(data, dict) and len(data) > 0:
|
||||
if isinstance(data, dict) and data:
|
||||
_models_dev_cache = data
|
||||
_models_dev_cache_time = time.time()
|
||||
_save_disk_cache(data)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import os
|
|||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
from typing import Any, Dict, List, Set, Tuple
|
||||
|
||||
from hermes_constants import get_hermes_home
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ Inspired by Block/goose's SubdirectoryHintTracker.
|
|||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional, Set
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ from multiprocessing import Pool, Lock
|
|||
import traceback
|
||||
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn, MofNCompleteColumn
|
||||
from rich.console import Console
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
import fire
|
||||
|
||||
from run_agent import AIAgent
|
||||
|
|
@ -1016,7 +1018,7 @@ class BatchRunner:
|
|||
tool_stats = data.get('tool_stats', {})
|
||||
|
||||
# Check for invalid tool names (model hallucinations)
|
||||
invalid_tools = [k for k in tool_stats.keys() if k not in VALID_TOOLS]
|
||||
invalid_tools = [k for k in tool_stats if k not in VALID_TOOLS]
|
||||
|
||||
if invalid_tools:
|
||||
filtered_entries += 1
|
||||
|
|
|
|||
6
cli.py
6
cli.py
|
|
@ -70,7 +70,7 @@ _COMMAND_SPINNER_FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧
|
|||
|
||||
# Load .env from ~/.hermes/.env first, then project root as dev fallback.
|
||||
# User-managed env files should override stale shell exports on restart.
|
||||
from hermes_constants import get_hermes_home, display_hermes_home, OPENROUTER_BASE_URL
|
||||
from hermes_constants import get_hermes_home, display_hermes_home
|
||||
from hermes_cli.env_loader import load_hermes_dotenv
|
||||
|
||||
_hermes_home = get_hermes_home()
|
||||
|
|
@ -4246,7 +4246,6 @@ class HermesCLI:
|
|||
|
||||
try:
|
||||
config = load_gateway_config()
|
||||
connected = config.get_connected_platforms()
|
||||
|
||||
print(" Messaging Platform Configuration:")
|
||||
print(" " + "-" * 55)
|
||||
|
|
@ -6008,7 +6007,7 @@ class HermesCLI:
|
|||
|
||||
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
|
||||
response_queue = queue.Queue()
|
||||
is_open_ended = not choices or len(choices) == 0
|
||||
is_open_ended = not choices
|
||||
|
||||
self._clarify_state = {
|
||||
"question": question,
|
||||
|
|
@ -7839,7 +7838,6 @@ class HermesCLI:
|
|||
title = '🔐 Sudo Password Required'
|
||||
body = 'Enter password below (hidden), or press Enter to skip'
|
||||
box_width = _panel_box_width(title, [body])
|
||||
inner = max(0, box_width - 2)
|
||||
lines = []
|
||||
lines.append(('class:sudo-border', '╭─ '))
|
||||
lines.append(('class:sudo-title', title))
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ except ImportError:
|
|||
import msvcrt
|
||||
except ImportError:
|
||||
msvcrt = None
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ def _build_discord(adapter) -> List[Dict[str, str]]:
|
|||
|
||||
def _build_slack(adapter) -> List[Dict[str, str]]:
|
||||
"""List Slack channels the bot has joined."""
|
||||
channels = []
|
||||
# Slack adapter may expose a web client
|
||||
client = getattr(adapter, "_app", None) or getattr(adapter, "_client", None)
|
||||
if not client:
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ sys.path.insert(0, str(_Path(__file__).resolve().parents[2]))
|
|||
|
||||
from gateway.config import Platform, PlatformConfig
|
||||
from gateway.session import SessionSource, build_session_key
|
||||
from hermes_cli.config import get_hermes_home
|
||||
from hermes_constants import get_hermes_dir
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ try:
|
|||
CreateMessageRequestBody,
|
||||
GetChatRequest,
|
||||
GetMessageRequest,
|
||||
GetImageRequest,
|
||||
GetMessageResourceRequest,
|
||||
P2ImMessageMessageReadV1,
|
||||
ReplyMessageRequest,
|
||||
|
|
|
|||
|
|
@ -1057,7 +1057,7 @@ class MatrixAdapter(BasePlatformAdapter):
|
|||
|
||||
# Message type.
|
||||
msg_type = MessageType.TEXT
|
||||
if body.startswith("!") or body.startswith("/"):
|
||||
if body.startswith(("!", "/")):
|
||||
msg_type = MessageType.COMMAND
|
||||
|
||||
source = self.build_source(
|
||||
|
|
|
|||
|
|
@ -430,7 +430,6 @@ class MattermostAdapter(BasePlatformAdapter):
|
|||
ct = resp.content_type or "application/octet-stream"
|
||||
break
|
||||
except (aiohttp.ClientError, asyncio.TimeoutError) as exc:
|
||||
last_exc = exc
|
||||
if attempt < 2:
|
||||
await asyncio.sleep(1.5 * (attempt + 1))
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -979,16 +979,7 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
try:
|
||||
# Build a SessionSource for this thread
|
||||
from gateway.session import SessionSource
|
||||
from gateway.config import Platform
|
||||
|
||||
source = SessionSource(
|
||||
platform=Platform.SLACK,
|
||||
chat_id=channel_id,
|
||||
chat_type="group",
|
||||
user_id=user_id,
|
||||
thread_id=thread_ts,
|
||||
)
|
||||
|
||||
|
||||
# Generate the session key using the same logic as SessionStore
|
||||
# This mirrors the logic in build_session_key for group sessions
|
||||
key_parts = ["agent:main", "slack", "group", channel_id, thread_ts]
|
||||
|
|
|
|||
|
|
@ -1369,7 +1369,7 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||
|
||||
with open(audio_path, "rb") as audio_file:
|
||||
# .ogg files -> send as voice (round playable bubble)
|
||||
if audio_path.endswith(".ogg") or audio_path.endswith(".opus"):
|
||||
if audio_path.endswith((".ogg", ".opus")):
|
||||
_voice_thread = metadata.get("thread_id") if metadata else None
|
||||
msg = await self._bot.send_voice(
|
||||
chat_id=int(chat_id),
|
||||
|
|
|
|||
|
|
@ -653,7 +653,7 @@ class WeComAdapter(BasePlatformAdapter):
|
|||
return ".png"
|
||||
if data.startswith(b"\xff\xd8\xff"):
|
||||
return ".jpg"
|
||||
if data.startswith(b"GIF87a") or data.startswith(b"GIF89a"):
|
||||
if data.startswith((b"GIF87a", b"GIF89a")):
|
||||
return ".gif"
|
||||
if data.startswith(b"RIFF") and data[8:12] == b"WEBP":
|
||||
return ".webp"
|
||||
|
|
@ -689,7 +689,7 @@ class WeComAdapter(BasePlatformAdapter):
|
|||
@staticmethod
|
||||
def _derive_message_type(body: Dict[str, Any], text: str, media_types: List[str]) -> MessageType:
|
||||
"""Choose the normalized inbound message type."""
|
||||
if any(mtype.startswith("application/") or mtype.startswith("text/") for mtype in media_types):
|
||||
if any(mtype.startswith(("application/", "text/")) for mtype in media_types):
|
||||
return MessageType.DOCUMENT
|
||||
if any(mtype.startswith("image/") for mtype in media_types):
|
||||
return MessageType.TEXT if text else MessageType.PHOTO
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ _IS_WINDOWS = platform.system() == "Windows"
|
|||
from pathlib import Path
|
||||
from typing import Dict, Optional, Any
|
||||
|
||||
from hermes_cli.config import get_hermes_home
|
||||
from hermes_constants import get_hermes_dir
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import signal
|
|||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from typing import Dict, Optional, Any, List
|
||||
|
|
@ -378,7 +377,7 @@ def _check_unavailable_skill(command_name: str) -> str | None:
|
|||
)
|
||||
|
||||
# Check optional skills (shipped with repo but not installed)
|
||||
from hermes_constants import get_hermes_home, get_optional_skills_dir
|
||||
from hermes_constants import get_optional_skills_dir
|
||||
repo_root = Path(__file__).resolve().parent.parent
|
||||
optional_dir = get_optional_skills_dir(repo_root / "optional-skills")
|
||||
if optional_dir.exists():
|
||||
|
|
@ -2822,7 +2821,7 @@ class GatewayRunner:
|
|||
guessed, _ = _mimetypes.guess_type(path)
|
||||
if guessed:
|
||||
mtype = guessed
|
||||
if not (mtype.startswith("application/") or mtype.startswith("text/")):
|
||||
if not mtype.startswith(("application/", "text/")):
|
||||
continue
|
||||
# Extract display filename by stripping the doc_{uuid12}_ prefix
|
||||
import os as _os
|
||||
|
|
@ -3909,7 +3908,7 @@ class GatewayRunner:
|
|||
|
||||
return f"🎭 Personality set to **{args}**\n_(takes effect on next message)_"
|
||||
|
||||
available = "`none`, " + ", ".join(f"`{n}`" for n in personalities.keys())
|
||||
available = "`none`, " + ", ".join(f"`{n}`" for n in personalities)
|
||||
return f"Unknown personality: `{args}`\n\nAvailable: {available}"
|
||||
|
||||
async def _handle_retry_command(self, event: MessageEvent) -> str:
|
||||
|
|
@ -5321,9 +5320,6 @@ class GatewayRunner:
|
|||
old_servers = set(_servers.keys())
|
||||
|
||||
# Read new config before shutting down, so we know what will be added/removed
|
||||
new_config = _load_mcp_config()
|
||||
new_server_names = set(new_config.keys())
|
||||
|
||||
# Shutdown existing connections
|
||||
await loop.run_in_executor(None, shutdown_mcp_servers)
|
||||
|
||||
|
|
@ -5411,7 +5407,6 @@ class GatewayRunner:
|
|||
|
||||
from tools.approval import (
|
||||
resolve_gateway_approval, has_blocking_approval,
|
||||
pending_approval_count,
|
||||
)
|
||||
|
||||
if not has_blocking_approval(session_key):
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ class GatewayStreamConsumer:
|
|||
got_done
|
||||
or got_segment_break
|
||||
or (elapsed >= self.cfg.edit_interval
|
||||
and len(self._accumulated) > 0)
|
||||
and self._accumulated)
|
||||
or len(self._accumulated) >= self.cfg.buffer_threshold
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -2839,7 +2839,6 @@ def _login_nous(args, pconfig: ProviderConfig) -> None:
|
|||
)
|
||||
|
||||
inference_base_url = auth_state["inference_base_url"]
|
||||
verify: bool | str = False if insecure else (ca_bundle if ca_bundle else True)
|
||||
|
||||
with _auth_store_lock():
|
||||
auth_store = _load_auth_store()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ from agent.credential_pool import (
|
|||
STRATEGY_ROUND_ROBIN,
|
||||
STRATEGY_RANDOM,
|
||||
STRATEGY_LEAST_USED,
|
||||
SUPPORTED_POOL_STRATEGIES,
|
||||
PooledCredential,
|
||||
_exhausted_until,
|
||||
_normalize_custom_pool_name,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ Pure display functions with no HermesCLI state dependency.
|
|||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ def clarify_callback(cli, question, choices):
|
|||
|
||||
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
|
||||
response_queue = queue.Queue()
|
||||
is_open_ended = not choices or len(choices) == 0
|
||||
is_open_ended = not choices
|
||||
|
||||
cli._clarify_state = {
|
||||
"question": question,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ Usage:
|
|||
|
||||
import importlib.util
|
||||
import logging
|
||||
import shutil
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
|
@ -24,7 +23,6 @@ from hermes_cli.setup import (
|
|||
print_info,
|
||||
print_success,
|
||||
print_error,
|
||||
print_warning,
|
||||
prompt_yes_no,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -2520,7 +2520,7 @@ def set_config_value(key: str, value: str):
|
|||
'TINKER_API_KEY',
|
||||
]
|
||||
|
||||
if key.upper() in api_keys or key.upper().endswith('_API_KEY') or key.upper().endswith('_TOKEN') or key.upper().startswith('TERMINAL_SSH'):
|
||||
if key.upper() in api_keys or key.upper().endswith(('_API_KEY', '_TOKEN')) or key.upper().startswith('TERMINAL_SSH'):
|
||||
save_env_value(key.upper(), value)
|
||||
print(f"✓ Set {key} in {get_env_path()}")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -920,8 +920,8 @@ def run_doctor(args):
|
|||
pass
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception as _e:
|
||||
logger.debug("Profile health check failed: %s", _e)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# =========================================================================
|
||||
# Summary
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ Usage examples::
|
|||
hermes logs --since 30m -f # follow, starting 30 min ago
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
|
|
|||
|
|
@ -1154,7 +1154,7 @@ def _model_flow_nous(config, current_model="", args=None):
|
|||
from hermes_cli.auth import (
|
||||
get_provider_auth_state, _prompt_model_selection, _save_model_choice,
|
||||
_update_config_for_provider, resolve_nous_runtime_credentials,
|
||||
fetch_nous_models, AuthError, format_auth_error,
|
||||
AuthError, format_auth_error,
|
||||
_login_nous, PROVIDER_REGISTRY,
|
||||
)
|
||||
from hermes_cli.config import get_env_value, save_config, save_env_value
|
||||
|
|
@ -1314,7 +1314,6 @@ def _model_flow_openai_codex(config, current_model=""):
|
|||
PROVIDER_REGISTRY, DEFAULT_CODEX_BASE_URL,
|
||||
)
|
||||
from hermes_cli.codex_models import get_codex_model_ids
|
||||
from hermes_cli.config import get_env_value, save_env_value
|
||||
import argparse
|
||||
|
||||
status = get_codex_auth_status()
|
||||
|
|
@ -1367,7 +1366,7 @@ def _model_flow_custom(config):
|
|||
so it appears in the provider menu on subsequent runs.
|
||||
"""
|
||||
from hermes_cli.auth import _save_model_choice, deactivate_provider
|
||||
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
|
||||
from hermes_cli.config import get_env_value, load_config, save_config
|
||||
|
||||
current_url = get_env_value("OPENAI_BASE_URL") or ""
|
||||
current_key = get_env_value("OPENAI_API_KEY") or ""
|
||||
|
|
@ -1629,7 +1628,7 @@ def _model_flow_named_custom(config, provider_info):
|
|||
Otherwise probes the endpoint's /models API to let the user pick one.
|
||||
"""
|
||||
from hermes_cli.auth import _save_model_choice, deactivate_provider
|
||||
from hermes_cli.config import save_env_value, load_config, save_config
|
||||
from hermes_cli.config import load_config, save_config
|
||||
from hermes_cli.models import fetch_api_models
|
||||
|
||||
name = provider_info["name"]
|
||||
|
|
@ -1839,7 +1838,7 @@ def _model_flow_copilot(config, current_model=""):
|
|||
deactivate_provider,
|
||||
resolve_api_key_provider_credentials,
|
||||
)
|
||||
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
|
||||
from hermes_cli.config import save_env_value, load_config, save_config
|
||||
from hermes_cli.models import (
|
||||
fetch_api_models,
|
||||
fetch_github_model_catalog,
|
||||
|
|
@ -2430,8 +2429,6 @@ def _model_flow_anthropic(config, current_model=""):
|
|||
)
|
||||
from hermes_cli.models import _PROVIDER_MODELS
|
||||
|
||||
pconfig = PROVIDER_REGISTRY["anthropic"]
|
||||
|
||||
# Check ALL credential sources
|
||||
existing_key = (
|
||||
get_env_value("ANTHROPIC_TOKEN")
|
||||
|
|
@ -3700,7 +3697,7 @@ def cmd_update(args):
|
|||
try:
|
||||
from hermes_cli.gateway import (
|
||||
is_macos, is_linux, _ensure_user_systemd_env,
|
||||
get_systemd_linger_status, find_gateway_pids,
|
||||
find_gateway_pids,
|
||||
_get_service_pids,
|
||||
)
|
||||
import signal as _signal
|
||||
|
|
@ -3856,7 +3853,7 @@ def cmd_profile(args):
|
|||
"""Profile management — create, delete, list, switch, alias."""
|
||||
from hermes_cli.profiles import (
|
||||
list_profiles, create_profile, delete_profile, seed_profile_skills,
|
||||
get_active_profile, set_active_profile, get_active_profile_name,
|
||||
set_active_profile, get_active_profile_name,
|
||||
check_alias_collision, create_wrapper_script, remove_wrapper_script,
|
||||
_is_wrapper_dir_in_path, _get_wrapper_dir,
|
||||
)
|
||||
|
|
@ -3984,7 +3981,6 @@ def cmd_profile(args):
|
|||
print(f" {name} chat Start chatting")
|
||||
print(f" {name} gateway start Start the messaging gateway")
|
||||
if clone or clone_all:
|
||||
from hermes_constants import get_hermes_home
|
||||
profile_dir_display = f"~/.hermes/profiles/{name}"
|
||||
print(f"\n Edit {profile_dir_display}/.env for different API keys")
|
||||
print(f" Edit {profile_dir_display}/SOUL.md for different personality")
|
||||
|
|
@ -4407,7 +4403,7 @@ For more help on a command:
|
|||
gateway_uninstall.add_argument("--system", action="store_true", help="Target the Linux system-level gateway service")
|
||||
|
||||
# gateway setup
|
||||
gateway_setup = gateway_subparsers.add_parser("setup", help="Configure messaging platforms")
|
||||
gateway_subparsers.add_parser("setup", help="Configure messaging platforms")
|
||||
|
||||
gateway_parser.set_defaults(func=cmd_gateway)
|
||||
|
||||
|
|
@ -4682,10 +4678,10 @@ For more help on a command:
|
|||
config_subparsers = config_parser.add_subparsers(dest="config_command")
|
||||
|
||||
# config show (default)
|
||||
config_show = config_subparsers.add_parser("show", help="Show current configuration")
|
||||
config_subparsers.add_parser("show", help="Show current configuration")
|
||||
|
||||
# config edit
|
||||
config_edit = config_subparsers.add_parser("edit", help="Open config file in editor")
|
||||
config_subparsers.add_parser("edit", help="Open config file in editor")
|
||||
|
||||
# config set
|
||||
config_set = config_subparsers.add_parser("set", help="Set a configuration value")
|
||||
|
|
@ -4693,16 +4689,16 @@ For more help on a command:
|
|||
config_set.add_argument("value", nargs="?", help="Value to set")
|
||||
|
||||
# config path
|
||||
config_path = config_subparsers.add_parser("path", help="Print config file path")
|
||||
config_subparsers.add_parser("path", help="Print config file path")
|
||||
|
||||
# config env-path
|
||||
config_env = config_subparsers.add_parser("env-path", help="Print .env file path")
|
||||
config_subparsers.add_parser("env-path", help="Print .env file path")
|
||||
|
||||
# config check
|
||||
config_check = config_subparsers.add_parser("check", help="Check for missing/outdated config")
|
||||
config_subparsers.add_parser("check", help="Check for missing/outdated config")
|
||||
|
||||
# config migrate
|
||||
config_migrate = config_subparsers.add_parser("migrate", help="Update config with new options")
|
||||
config_subparsers.add_parser("migrate", help="Update config with new options")
|
||||
|
||||
config_parser.set_defaults(func=cmd_config)
|
||||
|
||||
|
|
@ -4716,7 +4712,7 @@ For more help on a command:
|
|||
)
|
||||
pairing_sub = pairing_parser.add_subparsers(dest="pairing_action")
|
||||
|
||||
pairing_list_parser = pairing_sub.add_parser("list", help="Show pending + approved users")
|
||||
pairing_sub.add_parser("list", help="Show pending + approved users")
|
||||
|
||||
pairing_approve_parser = pairing_sub.add_parser("approve", help="Approve a pairing code")
|
||||
pairing_approve_parser.add_argument("platform", help="Platform name (telegram, discord, slack, whatsapp)")
|
||||
|
|
@ -4726,7 +4722,7 @@ For more help on a command:
|
|||
pairing_revoke_parser.add_argument("platform", help="Platform name")
|
||||
pairing_revoke_parser.add_argument("user_id", help="User ID to revoke")
|
||||
|
||||
pairing_clear_parser = pairing_sub.add_parser("clear-pending", help="Clear all pending codes")
|
||||
pairing_sub.add_parser("clear-pending", help="Clear all pending codes")
|
||||
|
||||
def cmd_pairing(args):
|
||||
from hermes_cli.pairing import pairing_command
|
||||
|
|
@ -4902,7 +4898,7 @@ For more help on a command:
|
|||
memory_sub = memory_parser.add_subparsers(dest="memory_command")
|
||||
memory_sub.add_parser("setup", help="Interactive provider selection and configuration")
|
||||
memory_sub.add_parser("status", help="Show current memory provider config")
|
||||
memory_off_p = memory_sub.add_parser("off", help="Disable external provider (built-in only)")
|
||||
memory_sub.add_parser("off", help="Disable external provider (built-in only)")
|
||||
|
||||
def cmd_memory(args):
|
||||
sub = getattr(args, "memory_command", None)
|
||||
|
|
@ -5066,7 +5062,7 @@ For more help on a command:
|
|||
sessions_prune.add_argument("--source", help="Only prune sessions from this source")
|
||||
sessions_prune.add_argument("--yes", "-y", action="store_true", help="Skip confirmation")
|
||||
|
||||
sessions_stats = sessions_subparsers.add_parser("stats", help="Show session store statistics")
|
||||
sessions_subparsers.add_parser("stats", help="Show session store statistics")
|
||||
|
||||
sessions_rename = sessions_subparsers.add_parser("rename", help="Set or change a session's title")
|
||||
sessions_rename.add_argument("session_id", help="Session ID to rename")
|
||||
|
|
@ -5426,7 +5422,7 @@ For more help on a command:
|
|||
)
|
||||
profile_subparsers = profile_parser.add_subparsers(dest="profile_action")
|
||||
|
||||
profile_list = profile_subparsers.add_parser("list", help="List all profiles")
|
||||
profile_subparsers.add_parser("list", help="List all profiles")
|
||||
profile_use = profile_subparsers.add_parser("use", help="Set sticky default profile")
|
||||
profile_use.add_argument("profile_name", help="Profile name (or 'default')")
|
||||
|
||||
|
|
|
|||
|
|
@ -21,22 +21,16 @@ OpenRouter variant suffixes (``:free``, ``:extended``, ``:fast``).
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
from hermes_cli.providers import (
|
||||
ALIASES,
|
||||
LABELS,
|
||||
TRANSPORT_TO_API_MODE,
|
||||
determine_api_mode,
|
||||
get_label,
|
||||
get_provider,
|
||||
is_aggregator,
|
||||
normalize_provider,
|
||||
resolve_provider_full,
|
||||
)
|
||||
from hermes_cli.model_normalize import (
|
||||
detect_vendor,
|
||||
normalize_model_for_provider,
|
||||
)
|
||||
from agent.models_dev import (
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ def cmd_install(identifier: str, force: bool = False) -> None:
|
|||
sys.exit(1)
|
||||
|
||||
# Warn about insecure / local URL schemes
|
||||
if git_url.startswith("http://") or git_url.startswith("file://"):
|
||||
if git_url.startswith(("http://", "file://")):
|
||||
console.print(
|
||||
"[yellow]Warning:[/yellow] Using insecure/local URL scheme. "
|
||||
"Consider using https:// or git@ for production installs."
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import shutil
|
|||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path, PurePosixPath, PureWindowsPath
|
||||
from typing import List, Optional
|
||||
|
||||
|
|
@ -517,7 +517,6 @@ def delete_profile(name: str, yes: bool = False) -> Path:
|
|||
]
|
||||
|
||||
# Check for service
|
||||
from hermes_cli.gateway import _profile_suffix, get_service_name
|
||||
wrapper_path = _get_wrapper_dir() / name
|
||||
has_wrapper = wrapper_path.exists()
|
||||
if has_wrapper:
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ Other modules import from this file. No parallel registries.
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -357,14 +356,6 @@ def _build_labels() -> Dict[str, str]:
|
|||
# Lazy-built on first access
|
||||
_labels_cache: Optional[Dict[str, str]] = None
|
||||
|
||||
@property
|
||||
def LABELS() -> Dict[str, str]:
|
||||
"""Backward-compatible labels dict."""
|
||||
global _labels_cache
|
||||
if _labels_cache is None:
|
||||
_labels_cache = _build_labels()
|
||||
return _labels_cache
|
||||
|
||||
# For direct import compat, expose as module-level dict
|
||||
# Built on demand by get_label() calls
|
||||
LABELS: Dict[str, str] = {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ from typing import Optional, Dict, Any
|
|||
|
||||
from hermes_cli.nous_subscription import (
|
||||
apply_nous_provider_defaults,
|
||||
get_nous_subscription_explainer_lines,
|
||||
get_nous_subscription_features,
|
||||
)
|
||||
from tools.tool_backend_helpers import managed_nous_tools_enabled
|
||||
|
|
@ -1348,8 +1347,6 @@ def setup_terminal_backend(config: dict):
|
|||
terminal_choices.append(f"Keep current ({current_backend})")
|
||||
idx_to_backend[keep_current_idx] = current_backend
|
||||
|
||||
default_terminal = backend_to_idx.get(current_backend, 0)
|
||||
|
||||
terminal_idx = prompt_choice(
|
||||
"Select terminal backend:", terminal_choices, keep_current_idx
|
||||
)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ Activate with ``/skin <name>`` in the CLI or ``display.skin: <name>`` in config.
|
|||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ Provides options for:
|
|||
- Keep data: Remove code but keep ~/.hermes/ (configs, sessions, logs)
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import re
|
|||
import secrets
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
from typing import Dict
|
||||
|
||||
from hermes_constants import display_hermes_home
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ secrets are never written to disk.
|
|||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ Key design decisions:
|
|||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sqlite3
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ crashes due to a bad timezone string.
|
|||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from hermes_constants import get_hermes_home
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -92,7 +91,6 @@ def get_timezone() -> Optional[ZoneInfo]:
|
|||
|
||||
def get_timezone_name() -> str:
|
||||
"""Return the IANA name of the configured timezone, or empty string."""
|
||||
global _cached_tz_name, _cache_resolved
|
||||
if not _cache_resolved:
|
||||
get_timezone() # populates cache
|
||||
return _cached_tz_name or ""
|
||||
|
|
|
|||
|
|
@ -37,9 +37,8 @@ import sys
|
|||
import threading
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
logger = logging.getLogger("hermes.mcp_serve")
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import os
|
|||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ from __future__ import annotations
|
|||
import json
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from agent.memory_provider import MemoryProvider
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ Single-user Hermes memory store plugin.
|
|||
import re
|
||||
import sqlite3
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ from __future__ import annotations
|
|||
import json
|
||||
import logging
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from agent.memory_provider import MemoryProvider
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import sys
|
|||
from pathlib import Path
|
||||
|
||||
from hermes_constants import get_hermes_home
|
||||
from plugins.memory.honcho.client import resolve_active_host, resolve_config_path, GLOBAL_CONFIG_PATH, HOST
|
||||
from plugins.memory.honcho.client import resolve_active_host, resolve_config_path, HOST
|
||||
|
||||
|
||||
def clone_honcho_for_profile(profile_name: str) -> bool:
|
||||
|
|
@ -1220,7 +1220,6 @@ def register_cli(subparser) -> None:
|
|||
Called by the plugin CLI registration system during argparse setup.
|
||||
The *subparser* is the parser for ``hermes honcho``.
|
||||
"""
|
||||
import argparse
|
||||
|
||||
subparser.add_argument(
|
||||
"--target-profile", metavar="NAME", dest="target_profile",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import logging
|
|||
import os
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from agent.memory_provider import MemoryProvider
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ Config (env vars or hermes config.yaml under retaindb:):
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
|
@ -189,7 +188,7 @@ class _Client:
|
|||
"Content-Type": "application/json",
|
||||
"x-sdk-runtime": "hermes-plugin",
|
||||
}
|
||||
if path.startswith("/v1/memory") or path.startswith("/v1/context"):
|
||||
if path.startswith(("/v1/memory", "/v1/context")):
|
||||
h["X-API-Key"] = token
|
||||
return h
|
||||
|
||||
|
|
|
|||
30
run_agent.py
30
run_agent.py
|
|
@ -20,7 +20,6 @@ Usage:
|
|||
response = agent.run_conversation("Tell me about the latest Python updates")
|
||||
"""
|
||||
|
||||
import atexit
|
||||
import asyncio
|
||||
import base64
|
||||
import concurrent.futures
|
||||
|
|
@ -36,7 +35,6 @@ import sys
|
|||
import tempfile
|
||||
import time
|
||||
import threading
|
||||
import weakref
|
||||
from types import SimpleNamespace
|
||||
import uuid
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
|
@ -654,7 +652,7 @@ class AIAgent:
|
|||
self.stream_delta_callback = stream_delta_callback
|
||||
self.status_callback = status_callback
|
||||
self.tool_gen_callback = tool_gen_callback
|
||||
self._last_reported_tool = None # Track for "new tool" mode
|
||||
|
||||
|
||||
# Tool execution state — allows _vprint during tool execution
|
||||
# even when stream consumers are registered (no tokens streaming then)
|
||||
|
|
@ -2702,20 +2700,7 @@ class AIAgent:
|
|||
|
||||
if not _soul_loaded:
|
||||
# Fallback to hardcoded identity
|
||||
_ai_peer_name = (
|
||||
None
|
||||
if False
|
||||
else None
|
||||
)
|
||||
if _ai_peer_name:
|
||||
_identity = DEFAULT_AGENT_IDENTITY.replace(
|
||||
"You are Hermes Agent",
|
||||
f"You are {_ai_peer_name}",
|
||||
1,
|
||||
)
|
||||
else:
|
||||
_identity = DEFAULT_AGENT_IDENTITY
|
||||
prompt_parts = [_identity]
|
||||
prompt_parts = [DEFAULT_AGENT_IDENTITY]
|
||||
|
||||
# Tool-aware behavioral guidance: only inject when the tools are loaded
|
||||
tool_guidance = []
|
||||
|
|
@ -3400,7 +3385,7 @@ class AIAgent:
|
|||
elif "stream" in api_kwargs:
|
||||
raise ValueError("Codex Responses stream flag is only allowed in fallback streaming requests.")
|
||||
|
||||
unexpected = sorted(key for key in api_kwargs.keys() if key not in allowed_keys)
|
||||
unexpected = sorted(key for key in api_kwargs if key not in allowed_keys)
|
||||
if unexpected:
|
||||
raise ValueError(
|
||||
f"Codex Responses request has unsupported field(s): {', '.join(unexpected)}."
|
||||
|
|
@ -5908,7 +5893,7 @@ class AIAgent:
|
|||
args = json.loads(tc.function.arguments)
|
||||
flush_target = args.get("target", "memory")
|
||||
from tools.memory_tool import memory_tool as _memory_tool
|
||||
result = _memory_tool(
|
||||
_memory_tool(
|
||||
action=args.get("action"),
|
||||
target=flush_target,
|
||||
content=args.get("content"),
|
||||
|
|
@ -7468,7 +7453,7 @@ class AIAgent:
|
|||
elif not isinstance(output_items, list):
|
||||
response_invalid = True
|
||||
error_details.append("response.output is not a list")
|
||||
elif len(output_items) == 0:
|
||||
elif not output_items:
|
||||
# If we reach here, _run_codex_stream's backfill
|
||||
# from output_item.done events and text-delta
|
||||
# synthesis both failed to populate output.
|
||||
|
|
@ -7491,11 +7476,11 @@ class AIAgent:
|
|||
elif not isinstance(content_blocks, list):
|
||||
response_invalid = True
|
||||
error_details.append("response.content is not a list")
|
||||
elif len(content_blocks) == 0:
|
||||
elif not content_blocks:
|
||||
response_invalid = True
|
||||
error_details.append("response.content is empty")
|
||||
else:
|
||||
if response is None or not hasattr(response, 'choices') or response.choices is None or len(response.choices) == 0:
|
||||
if response is None or not hasattr(response, 'choices') or response.choices is None or not response.choices:
|
||||
response_invalid = True
|
||||
if response is None:
|
||||
error_details.append("response is None")
|
||||
|
|
@ -9033,7 +9018,6 @@ class AIAgent:
|
|||
"content": f"Error executing tool: {error_msg}",
|
||||
}
|
||||
messages.append(err_msg)
|
||||
pending_handled = True
|
||||
break
|
||||
|
||||
# Non-tool errors don't need a synthetic message injected.
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ Usage:
|
|||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ Usage:
|
|||
|
||||
import json
|
||||
import random
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Any, Tuple
|
||||
import fire
|
||||
|
|
@ -138,7 +137,6 @@ def sample_from_datasets(
|
|||
List of sampled trajectory entries
|
||||
"""
|
||||
from multiprocessing import Pool
|
||||
from functools import partial
|
||||
|
||||
random.seed(seed)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Adapt this for your specific task by modifying:
|
|||
|
||||
import torch
|
||||
import re
|
||||
from datasets import load_dataset, Dataset
|
||||
from datasets import load_dataset
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
from peft import LoraConfig
|
||||
from trl import GRPOTrainer, GRPOConfig
|
||||
|
|
|
|||
|
|
@ -16,13 +16,10 @@ Usage in execute_code:
|
|||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import re
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
try:
|
||||
from openai import OpenAI
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ Usage in execute_code:
|
|||
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
|
|
@ -404,7 +403,6 @@ def race_godmode_classic(query, api_key=None, timeout=60):
|
|||
Each combo uses a different model paired with its best-performing jailbreak prompt.
|
||||
Returns the best result across all combos.
|
||||
"""
|
||||
from collections import namedtuple
|
||||
|
||||
HALL_OF_FAME = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ Usage:
|
|||
|
||||
import re
|
||||
import base64
|
||||
import sys
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Trigger words that commonly trip safety classifiers
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import requests
|
||||
|
|
@ -445,7 +443,7 @@ def camofox_get_images(task_id: Optional[str] = None) -> str:
|
|||
lines = snapshot.split("\n")
|
||||
for i, line in enumerate(lines):
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("- img ") or stripped.startswith("img "):
|
||||
if stripped.startswith(("- img ", "img ")):
|
||||
alt_match = re.search(r'img\s+"([^"]*)"', stripped)
|
||||
alt = alt_match.group(1) if alt_match else ""
|
||||
# Look for URL on the next line
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ def _resolve_cdp_override(cdp_url: str) -> str:
|
|||
return raw
|
||||
|
||||
discovery_url = raw
|
||||
if lowered.startswith("ws://") or lowered.startswith("wss://"):
|
||||
if lowered.startswith(("ws://", "wss://")):
|
||||
if raw.count(":") == 2 and raw.rstrip("/").rsplit(":", 1)[-1].isdigit() and "/" not in raw.split(":", 2)[-1]:
|
||||
discovery_url = ("http://" if lowered.startswith("ws://") else "https://") + raw.split("://", 1)[1]
|
||||
else:
|
||||
|
|
@ -458,8 +458,6 @@ def _browser_cleanup_thread_worker():
|
|||
Runs every 30 seconds and checks for sessions that haven't been used
|
||||
within the BROWSER_SESSION_INACTIVITY_TIMEOUT period.
|
||||
"""
|
||||
global _cleanup_running
|
||||
|
||||
while _cleanup_running:
|
||||
try:
|
||||
_cleanup_inactive_browser_sessions()
|
||||
|
|
|
|||
|
|
@ -693,7 +693,6 @@ def _execute_remote(
|
|||
the remote environment, and tool calls are proxied through a polling
|
||||
thread that communicates via request/response files.
|
||||
"""
|
||||
from tools.terminal_tool import _interrupt_event
|
||||
|
||||
_cfg = _load_config()
|
||||
timeout = _cfg.get("timeout", DEFAULT_TIMEOUT)
|
||||
|
|
|
|||
|
|
@ -150,7 +150,6 @@ def _validate_cron_script_path(script: Optional[str]) -> Optional[str]:
|
|||
if not script or not script.strip():
|
||||
return None # empty/None = clearing the field, always OK
|
||||
|
||||
from pathlib import Path
|
||||
from hermes_constants import get_hermes_home
|
||||
|
||||
raw = script.strip()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from hermes_constants import get_hermes_home
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ import shlex
|
|||
import threading
|
||||
import uuid
|
||||
import warnings
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
from tools.environments.base import BaseEnvironment
|
||||
from tools.interrupt import is_interrupted
|
||||
|
|
|
|||
|
|
@ -11,11 +11,10 @@ import os
|
|||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import threading
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Dict, Optional
|
||||
|
||||
from hermes_constants import get_hermes_home
|
||||
from tools.environments.base import BaseEnvironment
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ import threading
|
|||
import webbrowser
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -54,7 +54,7 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
_OAUTH_AVAILABLE = False
|
||||
try:
|
||||
from mcp.client.auth import OAuthClientProvider, TokenStorage
|
||||
from mcp.client.auth import OAuthClientProvider
|
||||
from mcp.shared.auth import (
|
||||
OAuthClientInformationFull,
|
||||
OAuthClientMetadata,
|
||||
|
|
@ -320,7 +320,6 @@ async def _wait_for_callback() -> tuple[str, str | None]:
|
|||
OAuthNonInteractiveError: If the callback times out (no user present
|
||||
to complete the browser auth).
|
||||
"""
|
||||
global _oauth_port
|
||||
assert _oauth_port is not None, "OAuth callback port not set"
|
||||
|
||||
# The callback server is already running (started in build_oauth_auth).
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ class MemoryStore:
|
|||
entries = self._entries_for(target)
|
||||
matches = [(i, e) for i, e in enumerate(entries) if old_text in e]
|
||||
|
||||
if len(matches) == 0:
|
||||
if not matches:
|
||||
return {"success": False, "error": f"No entry matched '{old_text}'."}
|
||||
|
||||
if len(matches) > 1:
|
||||
|
|
@ -310,7 +310,7 @@ class MemoryStore:
|
|||
entries = self._entries_for(target)
|
||||
matches = [(i, e) for i, e in enumerate(entries) if old_text in e]
|
||||
|
||||
if len(matches) == 0:
|
||||
if not matches:
|
||||
return {"success": False, "error": f"No entry matched '{old_text}'."}
|
||||
|
||||
if len(matches) > 1:
|
||||
|
|
|
|||
|
|
@ -567,7 +567,7 @@ async def rl_select_environment(name: str) -> str:
|
|||
|
||||
TIP: Read the returned file_path to understand how the environment works.
|
||||
"""
|
||||
global _current_env, _current_config, _env_config_cache
|
||||
global _current_env, _current_config
|
||||
|
||||
_initialize_environments()
|
||||
|
||||
|
|
@ -673,8 +673,6 @@ async def rl_edit_config(field: str, value: Any) -> str:
|
|||
Returns:
|
||||
JSON string with updated config or error message
|
||||
"""
|
||||
global _current_config
|
||||
|
||||
if not _current_env:
|
||||
return json.dumps({
|
||||
"error": "No environment selected. Use rl_select_environment(name) first.",
|
||||
|
|
@ -727,8 +725,6 @@ async def rl_start_training() -> str:
|
|||
Returns:
|
||||
JSON string with run_id and initial status
|
||||
"""
|
||||
global _active_runs
|
||||
|
||||
if not _current_env:
|
||||
return json.dumps({
|
||||
"error": "No environment selected. Use rl_select_environment(name) first.",
|
||||
|
|
@ -829,8 +825,6 @@ async def rl_check_status(run_id: str) -> str:
|
|||
Returns:
|
||||
JSON string with run status and metrics
|
||||
"""
|
||||
global _last_status_check
|
||||
|
||||
# Check rate limiting
|
||||
now = time.time()
|
||||
if run_id in _last_status_check:
|
||||
|
|
@ -1311,7 +1305,7 @@ async def rl_test_inference(
|
|||
"avg_accuracy": round(
|
||||
sum(m.get("accuracy", 0) for m in working_models) / len(working_models), 3
|
||||
) if working_models else 0,
|
||||
"environment_working": len(working_models) > 0,
|
||||
"environment_working": bool(working_models),
|
||||
"output_directory": str(test_output_dir),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -432,7 +432,7 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No
|
|||
else:
|
||||
# Reuse the gateway adapter's format_message for markdown→MarkdownV2
|
||||
try:
|
||||
from gateway.platforms.telegram import TelegramAdapter, _strip_mdv2
|
||||
from gateway.platforms.telegram import TelegramAdapter
|
||||
_adapter = TelegramAdapter.__new__(TelegramAdapter)
|
||||
formatted = _adapter.format_message(message)
|
||||
except Exception:
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ class GitHubSource(SkillSource):
|
|||
continue
|
||||
|
||||
dir_name = entry["name"]
|
||||
if dir_name.startswith(".") or dir_name.startswith("_"):
|
||||
if dir_name.startswith((".", "_")):
|
||||
continue
|
||||
|
||||
prefix = path.rstrip("/")
|
||||
|
|
@ -1163,7 +1163,7 @@ class SkillsShSource(SkillSource):
|
|||
if entry.get("type") != "dir":
|
||||
continue
|
||||
dir_name = entry["name"]
|
||||
if dir_name.startswith(".") or dir_name.startswith("_"):
|
||||
if dir_name.startswith((".", "_")):
|
||||
continue
|
||||
if dir_name in ("skills", ".agents", ".claude"):
|
||||
continue # already tried
|
||||
|
|
@ -1382,7 +1382,7 @@ class ClawHubSource(SkillSource):
|
|||
if isinstance(tags, list):
|
||||
return [str(t) for t in tags]
|
||||
if isinstance(tags, dict):
|
||||
return [str(k) for k in tags.keys() if str(k) != "latest"]
|
||||
return [str(k) for k in tags if str(k) != "latest"]
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -72,12 +72,10 @@ import logging
|
|||
from hermes_constants import get_hermes_home
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, List, Optional, Set, Tuple
|
||||
|
||||
import yaml
|
||||
from tools.registry import registry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
|||
|
|
@ -720,8 +720,6 @@ def _create_environment(env_type: str, image: str, cwd: str, timeout: int,
|
|||
|
||||
def _cleanup_inactive_envs(lifetime_seconds: int = 300):
|
||||
"""Clean up environments that have been inactive for longer than lifetime_seconds."""
|
||||
global _active_environments, _last_activity
|
||||
|
||||
current_time = time.time()
|
||||
|
||||
# Check the process registry -- skip cleanup for sandboxes with active
|
||||
|
|
@ -784,8 +782,6 @@ def _cleanup_inactive_envs(lifetime_seconds: int = 300):
|
|||
|
||||
def _cleanup_thread_worker():
|
||||
"""Background thread worker that periodically cleans up inactive environments."""
|
||||
global _cleanup_running
|
||||
|
||||
while _cleanup_running:
|
||||
try:
|
||||
config = _get_env_config()
|
||||
|
|
@ -831,7 +827,7 @@ def get_active_environments_info() -> Dict[str, Any]:
|
|||
|
||||
# Calculate total disk usage (per-task to avoid double-counting)
|
||||
total_size = 0
|
||||
for task_id in _active_environments.keys():
|
||||
for task_id in _active_environments:
|
||||
scratch_dir = _get_scratch_dir()
|
||||
pattern = f"hermes-*{task_id[:8]}*"
|
||||
import glob
|
||||
|
|
@ -848,8 +844,6 @@ def get_active_environments_info() -> Dict[str, Any]:
|
|||
|
||||
def cleanup_all_environments():
|
||||
"""Clean up ALL active environments. Use with caution."""
|
||||
global _active_environments, _last_activity
|
||||
|
||||
task_ids = list(_active_environments.keys())
|
||||
cleaned = 0
|
||||
|
||||
|
|
@ -877,8 +871,6 @@ def cleanup_all_environments():
|
|||
|
||||
def cleanup_vm(task_id: str):
|
||||
"""Manually clean up a specific environment by task_id."""
|
||||
global _active_environments, _last_activity
|
||||
|
||||
# Remove from tracking dicts while holding the lock, but defer the
|
||||
# actual (potentially slow) env.cleanup() call to outside the lock
|
||||
# so other tool calls aren't blocked.
|
||||
|
|
@ -1043,8 +1035,6 @@ def terminal_tool(
|
|||
# Force run after user confirmation
|
||||
# Note: force parameter is internal only, not exposed to model API
|
||||
"""
|
||||
global _active_environments, _last_activity
|
||||
|
||||
try:
|
||||
# Get configuration
|
||||
config = _get_env_config()
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class TodoStore:
|
|||
|
||||
def has_items(self) -> bool:
|
||||
"""Check if there are any items in the list."""
|
||||
return len(self._items) > 0
|
||||
return bool(self._items)
|
||||
|
||||
def format_for_injection(self) -> Optional[str]:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -550,7 +550,6 @@ def text_to_speech_tool(
|
|||
if edge_available:
|
||||
logger.info("Generating speech with Edge TTS...")
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
import concurrent.futures
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
||||
pool.submit(
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ def _validate_image_url(url: str) -> bool:
|
|||
return False
|
||||
|
||||
# Basic HTTP/HTTPS URL check
|
||||
if not (url.startswith("http://") or url.startswith("https://")):
|
||||
if not url.startswith(("http://", "https://")):
|
||||
return False
|
||||
|
||||
# Parse to ensure we at least have a network location; still allow URLs
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ def detect_audio_environment() -> dict:
|
|||
)
|
||||
|
||||
return {
|
||||
"available": len(warnings) == 0,
|
||||
"available": not warnings,
|
||||
"warnings": warnings,
|
||||
"notices": notices,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ from __future__ import annotations
|
|||
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
|
|
|||
|
|
@ -592,7 +592,7 @@ def get_toolset_info(name: str) -> Dict[str, Any]:
|
|||
"includes": toolset["includes"],
|
||||
"resolved_tools": resolved_tools,
|
||||
"tool_count": len(resolved_tools),
|
||||
"is_composite": len(toolset["includes"]) > 0
|
||||
"is_composite": bool(toolset["includes"])
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ Usage:
|
|||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import yaml
|
||||
import logging
|
||||
|
|
@ -350,7 +349,6 @@ class TrajectoryCompressor:
|
|||
which handles auth, headers, and provider detection internally.
|
||||
For custom endpoints, falls back to raw client construction.
|
||||
"""
|
||||
from agent.auxiliary_client import call_llm, async_call_llm
|
||||
|
||||
provider = self._detect_provider()
|
||||
if provider:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue