mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-08 03:01:47 +00:00
fix: resolve all call-non-callable ty diagnostics across codebase
Replace hasattr() duck-typing with isinstance() checks for DiscordAdapter in gateway/run.py, add TypedDict for IMAGEGEN_BACKENDS in tools_config.py, properly type fal_client getattr'd callables in image_generation_tool.py, fix dict[str, object] → Callable annotation in approval.py, use isinstance(BaseModel) in web_tools.py, capture _message_handler to local in base.py, rename shadowed list_distributions parameter in batch_runner.py, and remove dead queue_message branch.
This commit is contained in:
parent
ca2b6a529e
commit
f8d2365795
7 changed files with 61 additions and 47 deletions
|
|
@ -1828,8 +1828,11 @@ class BasePlatformAdapter(ABC):
|
||||||
try:
|
try:
|
||||||
await self._run_processing_hook("on_processing_start", event)
|
await self._run_processing_hook("on_processing_start", event)
|
||||||
|
|
||||||
# Call the handler (this can take a while with tool calls)
|
handler = self._message_handler
|
||||||
response = await self._message_handler(event)
|
if handler is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
response = await handler(event)
|
||||||
|
|
||||||
# Send response if any. A None/empty response is normal when
|
# Send response if any. A None/empty response is normal when
|
||||||
# streaming already delivered the text (already_sent=True) or
|
# streaming already delivered the text (already_sent=True) or
|
||||||
|
|
|
||||||
|
|
@ -4358,9 +4358,10 @@ class GatewayRunner:
|
||||||
# is speaking, without needing a separate tool call.
|
# is speaking, without needing a separate tool call.
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
if source.platform == Platform.DISCORD:
|
if source.platform == Platform.DISCORD:
|
||||||
|
from gateway.platforms.discord import DiscordAdapter
|
||||||
adapter = self.adapters.get(Platform.DISCORD)
|
adapter = self.adapters.get(Platform.DISCORD)
|
||||||
guild_id = self._get_guild_id(event)
|
guild_id = self._get_guild_id(event)
|
||||||
if guild_id and adapter and hasattr(adapter, "get_voice_channel_context"):
|
if guild_id and isinstance(adapter, DiscordAdapter):
|
||||||
vc_context = adapter.get_voice_channel_context(guild_id)
|
vc_context = adapter.get_voice_channel_context(guild_id)
|
||||||
if vc_context:
|
if vc_context:
|
||||||
context_prompt += f"\n\n{vc_context}"
|
context_prompt += f"\n\n{vc_context}"
|
||||||
|
|
@ -5946,9 +5947,10 @@ class GatewayRunner:
|
||||||
"all": "TTS (voice reply to all messages)",
|
"all": "TTS (voice reply to all messages)",
|
||||||
}
|
}
|
||||||
# Append voice channel info if connected
|
# Append voice channel info if connected
|
||||||
|
from gateway.platforms.discord import DiscordAdapter
|
||||||
adapter = self.adapters.get(event.source.platform)
|
adapter = self.adapters.get(event.source.platform)
|
||||||
guild_id = self._get_guild_id(event)
|
guild_id = self._get_guild_id(event)
|
||||||
if guild_id and hasattr(adapter, "get_voice_channel_info"):
|
if guild_id and isinstance(adapter, DiscordAdapter):
|
||||||
info = adapter.get_voice_channel_info(guild_id)
|
info = adapter.get_voice_channel_info(guild_id)
|
||||||
if info:
|
if info:
|
||||||
lines = [
|
lines = [
|
||||||
|
|
@ -5979,8 +5981,9 @@ class GatewayRunner:
|
||||||
|
|
||||||
async def _handle_voice_channel_join(self, event: MessageEvent) -> str:
|
async def _handle_voice_channel_join(self, event: MessageEvent) -> str:
|
||||||
"""Join the user's current Discord voice channel."""
|
"""Join the user's current Discord voice channel."""
|
||||||
|
from gateway.platforms.discord import DiscordAdapter
|
||||||
adapter = self.adapters.get(event.source.platform)
|
adapter = self.adapters.get(event.source.platform)
|
||||||
if not hasattr(adapter, "join_voice_channel"):
|
if not isinstance(adapter, DiscordAdapter):
|
||||||
return "Voice channels are not supported on this platform."
|
return "Voice channels are not supported on this platform."
|
||||||
|
|
||||||
guild_id = self._get_guild_id(event)
|
guild_id = self._get_guild_id(event)
|
||||||
|
|
@ -5995,10 +5998,8 @@ class GatewayRunner:
|
||||||
|
|
||||||
# Wire callbacks BEFORE join so voice input arriving immediately
|
# Wire callbacks BEFORE join so voice input arriving immediately
|
||||||
# after connection is not lost.
|
# after connection is not lost.
|
||||||
if hasattr(adapter, "_voice_input_callback"):
|
adapter._voice_input_callback = self._handle_voice_channel_input
|
||||||
adapter._voice_input_callback = self._handle_voice_channel_input
|
adapter._on_voice_disconnect = self._handle_voice_timeout_cleanup
|
||||||
if hasattr(adapter, "_on_voice_disconnect"):
|
|
||||||
adapter._on_voice_disconnect = self._handle_voice_timeout_cleanup
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
success = await adapter.join_voice_channel(voice_channel)
|
success = await adapter.join_voice_channel(voice_channel)
|
||||||
|
|
@ -6015,8 +6016,7 @@ class GatewayRunner:
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
adapter._voice_text_channels[guild_id] = int(event.source.chat_id)
|
adapter._voice_text_channels[guild_id] = int(event.source.chat_id)
|
||||||
if hasattr(adapter, "_voice_sources"):
|
adapter._voice_sources[guild_id] = event.source.to_dict()
|
||||||
adapter._voice_sources[guild_id] = event.source.to_dict()
|
|
||||||
self._voice_mode[self._voice_key(event.source.platform, event.source.chat_id)] = "all"
|
self._voice_mode[self._voice_key(event.source.platform, event.source.chat_id)] = "all"
|
||||||
self._save_voice_modes()
|
self._save_voice_modes()
|
||||||
self._set_adapter_auto_tts_disabled(adapter, event.source.chat_id, disabled=False)
|
self._set_adapter_auto_tts_disabled(adapter, event.source.chat_id, disabled=False)
|
||||||
|
|
@ -6030,13 +6030,14 @@ class GatewayRunner:
|
||||||
|
|
||||||
async def _handle_voice_channel_leave(self, event: MessageEvent) -> str:
|
async def _handle_voice_channel_leave(self, event: MessageEvent) -> str:
|
||||||
"""Leave the Discord voice channel."""
|
"""Leave the Discord voice channel."""
|
||||||
|
from gateway.platforms.discord import DiscordAdapter
|
||||||
adapter = self.adapters.get(event.source.platform)
|
adapter = self.adapters.get(event.source.platform)
|
||||||
guild_id = self._get_guild_id(event)
|
guild_id = self._get_guild_id(event)
|
||||||
|
|
||||||
if not guild_id or not hasattr(adapter, "leave_voice_channel"):
|
if not guild_id or not isinstance(adapter, DiscordAdapter):
|
||||||
return "Not in a voice channel."
|
return "Not in a voice channel."
|
||||||
|
|
||||||
if not hasattr(adapter, "is_in_voice_channel") or not adapter.is_in_voice_channel(guild_id):
|
if not adapter.is_in_voice_channel(guild_id):
|
||||||
return "Not in a voice channel."
|
return "Not in a voice channel."
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -6047,8 +6048,7 @@ class GatewayRunner:
|
||||||
self._voice_mode[self._voice_key(event.source.platform, event.source.chat_id)] = "off"
|
self._voice_mode[self._voice_key(event.source.platform, event.source.chat_id)] = "off"
|
||||||
self._save_voice_modes()
|
self._save_voice_modes()
|
||||||
self._set_adapter_auto_tts_disabled(adapter, event.source.chat_id, disabled=True)
|
self._set_adapter_auto_tts_disabled(adapter, event.source.chat_id, disabled=True)
|
||||||
if hasattr(adapter, "_voice_input_callback"):
|
adapter._voice_input_callback = None
|
||||||
adapter._voice_input_callback = None
|
|
||||||
return "Left voice channel."
|
return "Left voice channel."
|
||||||
|
|
||||||
def _handle_voice_timeout_cleanup(self, chat_id: str) -> None:
|
def _handle_voice_timeout_cleanup(self, chat_id: str) -> None:
|
||||||
|
|
@ -6208,13 +6208,13 @@ class GatewayRunner:
|
||||||
adapter = self.adapters.get(event.source.platform)
|
adapter = self.adapters.get(event.source.platform)
|
||||||
|
|
||||||
# If connected to a voice channel, play there instead of sending a file
|
# If connected to a voice channel, play there instead of sending a file
|
||||||
|
from gateway.platforms.discord import DiscordAdapter
|
||||||
guild_id = self._get_guild_id(event)
|
guild_id = self._get_guild_id(event)
|
||||||
if (guild_id
|
if (guild_id
|
||||||
and hasattr(adapter, "play_in_voice_channel")
|
and isinstance(adapter, DiscordAdapter)
|
||||||
and hasattr(adapter, "is_in_voice_channel")
|
|
||||||
and adapter.is_in_voice_channel(guild_id)):
|
and adapter.is_in_voice_channel(guild_id)):
|
||||||
await adapter.play_in_voice_channel(guild_id, actual_path)
|
await adapter.play_in_voice_channel(guild_id, actual_path)
|
||||||
elif adapter and hasattr(adapter, "send_voice"):
|
elif adapter:
|
||||||
send_kwargs: Dict[str, Any] = {
|
send_kwargs: Dict[str, Any] = {
|
||||||
"chat_id": event.source.chat_id,
|
"chat_id": event.source.chat_id,
|
||||||
"audio_path": actual_path,
|
"audio_path": actual_path,
|
||||||
|
|
@ -10468,8 +10468,6 @@ class GatewayRunner:
|
||||||
adapter = self.adapters.get(source.platform)
|
adapter = self.adapters.get(source.platform)
|
||||||
if adapter and pending_event:
|
if adapter and pending_event:
|
||||||
merge_pending_message_event(adapter._pending_messages, session_key, pending_event)
|
merge_pending_message_event(adapter._pending_messages, session_key, pending_event)
|
||||||
elif adapter and hasattr(adapter, 'queue_message'):
|
|
||||||
adapter.queue_message(session_key, pending)
|
|
||||||
return result_holder[0] or {"final_response": response, "messages": history}
|
return result_holder[0] or {"final_response": response, "messages": history}
|
||||||
|
|
||||||
was_interrupted = result.get("interrupted")
|
was_interrupted = result.get("interrupted")
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import json as _json
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional, Set
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypedDict
|
||||||
|
|
||||||
|
|
||||||
from hermes_cli.config import (
|
from hermes_cli.config import (
|
||||||
|
|
@ -970,13 +970,19 @@ def _detect_active_provider_index(providers: list, config: dict) -> int:
|
||||||
# right catalog at picker time.
|
# right catalog at picker time.
|
||||||
|
|
||||||
|
|
||||||
def _fal_model_catalog():
|
class _ImagegenBackend(TypedDict):
|
||||||
|
display: str
|
||||||
|
config_key: str
|
||||||
|
catalog_fn: Callable[[], Tuple[Dict[str, Dict[str, Any]], str]]
|
||||||
|
|
||||||
|
|
||||||
|
def _fal_model_catalog() -> Tuple[Dict[str, Dict[str, Any]], str]:
|
||||||
"""Lazy-load the FAL model catalog from the tool module."""
|
"""Lazy-load the FAL model catalog from the tool module."""
|
||||||
from tools.image_generation_tool import FAL_MODELS, DEFAULT_MODEL
|
from tools.image_generation_tool import FAL_MODELS, DEFAULT_MODEL
|
||||||
return FAL_MODELS, DEFAULT_MODEL
|
return FAL_MODELS, DEFAULT_MODEL
|
||||||
|
|
||||||
|
|
||||||
IMAGEGEN_BACKENDS = {
|
IMAGEGEN_BACKENDS: Dict[str, _ImagegenBackend] = {
|
||||||
"fal": {
|
"fal": {
|
||||||
"display": "FAL.ai",
|
"display": "FAL.ai",
|
||||||
"config_key": "image_gen",
|
"config_key": "image_gen",
|
||||||
|
|
|
||||||
|
|
@ -1130,7 +1130,7 @@ def main(
|
||||||
num_workers: int = 4,
|
num_workers: int = 4,
|
||||||
resume: bool = False,
|
resume: bool = False,
|
||||||
verbose: bool = False,
|
verbose: bool = False,
|
||||||
list_distributions: bool = False,
|
show_distributions: bool = False,
|
||||||
ephemeral_system_prompt: str = None,
|
ephemeral_system_prompt: str = None,
|
||||||
log_prefix_chars: int = 100,
|
log_prefix_chars: int = 100,
|
||||||
providers_allowed: str = None,
|
providers_allowed: str = None,
|
||||||
|
|
@ -1158,7 +1158,7 @@ def main(
|
||||||
num_workers (int): Number of parallel worker processes (default: 4)
|
num_workers (int): Number of parallel worker processes (default: 4)
|
||||||
resume (bool): Resume from checkpoint if run was interrupted (default: False)
|
resume (bool): Resume from checkpoint if run was interrupted (default: False)
|
||||||
verbose (bool): Enable verbose logging (default: False)
|
verbose (bool): Enable verbose logging (default: False)
|
||||||
list_distributions (bool): List available toolset distributions and exit
|
show_distributions (bool): List available toolset distributions and exit
|
||||||
ephemeral_system_prompt (str): System prompt used during agent execution but NOT saved to trajectories (optional)
|
ephemeral_system_prompt (str): System prompt used during agent execution but NOT saved to trajectories (optional)
|
||||||
log_prefix_chars (int): Number of characters to show in log previews for tool calls/responses (default: 20)
|
log_prefix_chars (int): Number of characters to show in log previews for tool calls/responses (default: 20)
|
||||||
providers_allowed (str): Comma-separated list of OpenRouter providers to allow (e.g. "anthropic,openai")
|
providers_allowed (str): Comma-separated list of OpenRouter providers to allow (e.g. "anthropic,openai")
|
||||||
|
|
@ -1190,10 +1190,10 @@ def main(
|
||||||
--prefill_messages_file=configs/prefill_opus.json
|
--prefill_messages_file=configs/prefill_opus.json
|
||||||
|
|
||||||
# List available distributions
|
# List available distributions
|
||||||
python batch_runner.py --list_distributions
|
python batch_runner.py --show_distributions
|
||||||
"""
|
"""
|
||||||
# Handle list distributions
|
# Handle list distributions
|
||||||
if list_distributions:
|
if show_distributions:
|
||||||
from toolset_distributions import print_distribution_info
|
from toolset_distributions import print_distribution_info
|
||||||
|
|
||||||
print("📊 Available Toolset Distributions")
|
print("📊 Available Toolset Distributions")
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from typing import Optional
|
from typing import Any, Callable, Dict, Optional
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -228,10 +228,10 @@ class _ApprovalEntry:
|
||||||
|
|
||||||
|
|
||||||
_gateway_queues: dict[str, list] = {} # session_key → [_ApprovalEntry, …]
|
_gateway_queues: dict[str, list] = {} # session_key → [_ApprovalEntry, …]
|
||||||
_gateway_notify_cbs: dict[str, object] = {} # session_key → callable(approval_data)
|
_gateway_notify_cbs: Dict[str, Callable[[Dict[str, Any]], None]] = {}
|
||||||
|
|
||||||
|
|
||||||
def register_gateway_notify(session_key: str, cb) -> None:
|
def register_gateway_notify(session_key: str, cb: Callable[[Dict[str, Any]], None]) -> None:
|
||||||
"""Register a per-session callback for sending approval requests to the user.
|
"""Register a per-session callback for sending approval requests to the user.
|
||||||
|
|
||||||
The callback signature is ``cb(approval_data: dict) -> None`` where
|
The callback signature is ``cb(approval_data: dict) -> None`` where
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,11 @@ import os
|
||||||
import datetime
|
import datetime
|
||||||
import threading
|
import threading
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Any, Dict, Optional, Union
|
from typing import Any, Callable, Dict, Optional, Type, Union
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
import fal_client
|
import fal_client
|
||||||
|
import httpx
|
||||||
|
|
||||||
from tools.debug_helpers import DebugSession
|
from tools.debug_helpers import DebugSession
|
||||||
from tools.managed_tool_gateway import resolve_managed_tool_gateway
|
from tools.managed_tool_gateway import resolve_managed_tool_gateway
|
||||||
|
|
@ -312,21 +313,27 @@ class _ManagedFalSyncClient:
|
||||||
|
|
||||||
self._queue_url_format = _normalize_fal_queue_url_format(queue_run_origin)
|
self._queue_url_format = _normalize_fal_queue_url_format(queue_run_origin)
|
||||||
self._sync_client = sync_client_class(key=key)
|
self._sync_client = sync_client_class(key=key)
|
||||||
self._http_client = getattr(self._sync_client, "_client", None)
|
|
||||||
self._maybe_retry_request = getattr(client_module, "_maybe_retry_request", None)
|
|
||||||
self._raise_for_status = getattr(client_module, "_raise_for_status", None)
|
|
||||||
self._request_handle_class = getattr(client_module, "SyncRequestHandle", None)
|
|
||||||
self._add_hint_header = getattr(client_module, "add_hint_header", None)
|
|
||||||
self._add_priority_header = getattr(client_module, "add_priority_header", None)
|
|
||||||
self._add_timeout_header = getattr(client_module, "add_timeout_header", None)
|
|
||||||
|
|
||||||
if self._http_client is None:
|
http_client: Optional[httpx.Client] = getattr(self._sync_client, "_client", None)
|
||||||
|
maybe_retry: Optional[Callable[..., httpx.Response]] = getattr(client_module, "_maybe_retry_request", None)
|
||||||
|
raise_for_status: Optional[Callable[[httpx.Response], None]] = getattr(client_module, "_raise_for_status", None)
|
||||||
|
request_handle_class: Optional[Type[Any]] = getattr(client_module, "SyncRequestHandle", None)
|
||||||
|
|
||||||
|
if http_client is None:
|
||||||
raise RuntimeError("fal_client.SyncClient._client is required for managed FAL gateway mode")
|
raise RuntimeError("fal_client.SyncClient._client is required for managed FAL gateway mode")
|
||||||
if self._maybe_retry_request is None or self._raise_for_status is None:
|
if maybe_retry is None or raise_for_status is None:
|
||||||
raise RuntimeError("fal_client.client request helpers are required for managed FAL gateway mode")
|
raise RuntimeError("fal_client.client request helpers are required for managed FAL gateway mode")
|
||||||
if self._request_handle_class is None:
|
if request_handle_class is None:
|
||||||
raise RuntimeError("fal_client.client.SyncRequestHandle is required for managed FAL gateway mode")
|
raise RuntimeError("fal_client.client.SyncRequestHandle is required for managed FAL gateway mode")
|
||||||
|
|
||||||
|
self._http_client: httpx.Client = http_client
|
||||||
|
self._maybe_retry_request: Callable[..., httpx.Response] = maybe_retry
|
||||||
|
self._raise_for_status: Callable[[httpx.Response], None] = raise_for_status
|
||||||
|
self._request_handle_class: Type[Any] = request_handle_class
|
||||||
|
self._add_hint_header: Optional[Callable[..., Any]] = getattr(client_module, "add_hint_header", None)
|
||||||
|
self._add_priority_header: Optional[Callable[..., Any]] = getattr(client_module, "add_priority_header", None)
|
||||||
|
self._add_timeout_header: Optional[Callable[..., Any]] = getattr(client_module, "add_timeout_header", None)
|
||||||
|
|
||||||
def submit(
|
def submit(
|
||||||
self,
|
self,
|
||||||
application: str,
|
application: str,
|
||||||
|
|
|
||||||
|
|
@ -1720,8 +1720,8 @@ async def web_crawl_tool(
|
||||||
metadata = {}
|
metadata = {}
|
||||||
|
|
||||||
# Extract data from the item
|
# Extract data from the item
|
||||||
if hasattr(item, 'model_dump'):
|
from pydantic import BaseModel
|
||||||
# Pydantic model - use model_dump to get dict
|
if isinstance(item, BaseModel):
|
||||||
item_dict = item.model_dump()
|
item_dict = item.model_dump()
|
||||||
content_markdown = item_dict.get('markdown')
|
content_markdown = item_dict.get('markdown')
|
||||||
content_html = item_dict.get('html')
|
content_html = item_dict.get('html')
|
||||||
|
|
@ -1733,12 +1733,12 @@ async def web_crawl_tool(
|
||||||
|
|
||||||
# Handle metadata - convert to dict if it's an object
|
# Handle metadata - convert to dict if it's an object
|
||||||
metadata_obj = getattr(item, 'metadata', {})
|
metadata_obj = getattr(item, 'metadata', {})
|
||||||
if hasattr(metadata_obj, 'model_dump'):
|
if isinstance(metadata_obj, BaseModel):
|
||||||
metadata = metadata_obj.model_dump()
|
metadata = metadata_obj.model_dump()
|
||||||
elif hasattr(metadata_obj, '__dict__'):
|
|
||||||
metadata = metadata_obj.__dict__
|
|
||||||
elif isinstance(metadata_obj, dict):
|
elif isinstance(metadata_obj, dict):
|
||||||
metadata = metadata_obj
|
metadata = metadata_obj
|
||||||
|
elif hasattr(metadata_obj, '__dict__'):
|
||||||
|
metadata = metadata_obj.__dict__
|
||||||
else:
|
else:
|
||||||
metadata = {}
|
metadata = {}
|
||||||
elif isinstance(item, dict):
|
elif isinstance(item, dict):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue