mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +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
971542d254
commit
3eddabf53b
7 changed files with 61 additions and 47 deletions
|
|
@ -1833,8 +1833,11 @@ class BasePlatformAdapter(ABC):
|
|||
try:
|
||||
await self._run_processing_hook("on_processing_start", event)
|
||||
|
||||
# Call the handler (this can take a while with tool calls)
|
||||
response = await self._message_handler(event)
|
||||
handler = self._message_handler
|
||||
if handler is None:
|
||||
return
|
||||
|
||||
response = await handler(event)
|
||||
|
||||
# Send response if any. A None/empty response is normal when
|
||||
# streaming already delivered the text (already_sent=True) or
|
||||
|
|
|
|||
|
|
@ -4431,9 +4431,10 @@ class GatewayRunner:
|
|||
# is speaking, without needing a separate tool call.
|
||||
# -----------------------------------------------------------------
|
||||
if source.platform == Platform.DISCORD:
|
||||
from gateway.platforms.discord import DiscordAdapter
|
||||
adapter = self.adapters.get(Platform.DISCORD)
|
||||
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)
|
||||
if vc_context:
|
||||
context_prompt += f"\n\n{vc_context}"
|
||||
|
|
@ -6026,9 +6027,10 @@ class GatewayRunner:
|
|||
"all": "TTS (voice reply to all messages)",
|
||||
}
|
||||
# Append voice channel info if connected
|
||||
from gateway.platforms.discord import DiscordAdapter
|
||||
adapter = self.adapters.get(event.source.platform)
|
||||
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)
|
||||
if info:
|
||||
lines = [
|
||||
|
|
@ -6059,8 +6061,9 @@ class GatewayRunner:
|
|||
|
||||
async def _handle_voice_channel_join(self, event: MessageEvent) -> str:
|
||||
"""Join the user's current Discord voice channel."""
|
||||
from gateway.platforms.discord import DiscordAdapter
|
||||
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."
|
||||
|
||||
guild_id = self._get_guild_id(event)
|
||||
|
|
@ -6075,10 +6078,8 @@ class GatewayRunner:
|
|||
|
||||
# Wire callbacks BEFORE join so voice input arriving immediately
|
||||
# after connection is not lost.
|
||||
if hasattr(adapter, "_voice_input_callback"):
|
||||
adapter._voice_input_callback = self._handle_voice_channel_input
|
||||
if hasattr(adapter, "_on_voice_disconnect"):
|
||||
adapter._on_voice_disconnect = self._handle_voice_timeout_cleanup
|
||||
adapter._voice_input_callback = self._handle_voice_channel_input
|
||||
adapter._on_voice_disconnect = self._handle_voice_timeout_cleanup
|
||||
|
||||
try:
|
||||
success = await adapter.join_voice_channel(voice_channel)
|
||||
|
|
@ -6095,8 +6096,7 @@ class GatewayRunner:
|
|||
|
||||
if success:
|
||||
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._save_voice_modes()
|
||||
self._set_adapter_auto_tts_disabled(adapter, event.source.chat_id, disabled=False)
|
||||
|
|
@ -6110,13 +6110,14 @@ class GatewayRunner:
|
|||
|
||||
async def _handle_voice_channel_leave(self, event: MessageEvent) -> str:
|
||||
"""Leave the Discord voice channel."""
|
||||
from gateway.platforms.discord import DiscordAdapter
|
||||
adapter = self.adapters.get(event.source.platform)
|
||||
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."
|
||||
|
||||
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."
|
||||
|
||||
try:
|
||||
|
|
@ -6127,8 +6128,7 @@ class GatewayRunner:
|
|||
self._voice_mode[self._voice_key(event.source.platform, event.source.chat_id)] = "off"
|
||||
self._save_voice_modes()
|
||||
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."
|
||||
|
||||
def _handle_voice_timeout_cleanup(self, chat_id: str) -> None:
|
||||
|
|
@ -6288,13 +6288,13 @@ class GatewayRunner:
|
|||
adapter = self.adapters.get(event.source.platform)
|
||||
|
||||
# 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)
|
||||
if (guild_id
|
||||
and hasattr(adapter, "play_in_voice_channel")
|
||||
and hasattr(adapter, "is_in_voice_channel")
|
||||
and isinstance(adapter, DiscordAdapter)
|
||||
and adapter.is_in_voice_channel(guild_id)):
|
||||
await adapter.play_in_voice_channel(guild_id, actual_path)
|
||||
elif adapter and hasattr(adapter, "send_voice"):
|
||||
elif adapter:
|
||||
send_kwargs: Dict[str, Any] = {
|
||||
"chat_id": event.source.chat_id,
|
||||
"audio_path": actual_path,
|
||||
|
|
@ -10627,8 +10627,6 @@ class GatewayRunner:
|
|||
adapter = self.adapters.get(source.platform)
|
||||
if adapter and 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}
|
||||
|
||||
was_interrupted = result.get("interrupted")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import json as _json
|
|||
import logging
|
||||
import sys
|
||||
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 (
|
||||
|
|
@ -1098,13 +1098,19 @@ def _detect_active_provider_index(providers: list, config: dict) -> int:
|
|||
# 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."""
|
||||
from tools.image_generation_tool import FAL_MODELS, DEFAULT_MODEL
|
||||
return FAL_MODELS, DEFAULT_MODEL
|
||||
|
||||
|
||||
IMAGEGEN_BACKENDS = {
|
||||
IMAGEGEN_BACKENDS: Dict[str, _ImagegenBackend] = {
|
||||
"fal": {
|
||||
"display": "FAL.ai",
|
||||
"config_key": "image_gen",
|
||||
|
|
|
|||
|
|
@ -1130,7 +1130,7 @@ def main(
|
|||
num_workers: int = 4,
|
||||
resume: bool = False,
|
||||
verbose: bool = False,
|
||||
list_distributions: bool = False,
|
||||
show_distributions: bool = False,
|
||||
ephemeral_system_prompt: str = None,
|
||||
log_prefix_chars: int = 100,
|
||||
providers_allowed: str = None,
|
||||
|
|
@ -1158,7 +1158,7 @@ def main(
|
|||
num_workers (int): Number of parallel worker processes (default: 4)
|
||||
resume (bool): Resume from checkpoint if run was interrupted (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)
|
||||
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")
|
||||
|
|
@ -1190,10 +1190,10 @@ def main(
|
|||
--prefill_messages_file=configs/prefill_opus.json
|
||||
|
||||
# List available distributions
|
||||
python batch_runner.py --list_distributions
|
||||
python batch_runner.py --show_distributions
|
||||
"""
|
||||
# Handle list distributions
|
||||
if list_distributions:
|
||||
if show_distributions:
|
||||
from toolset_distributions import print_distribution_info
|
||||
|
||||
print("📊 Available Toolset Distributions")
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import sys
|
|||
import threading
|
||||
import time
|
||||
import unicodedata
|
||||
from typing import Optional
|
||||
from typing import Any, Callable, Dict, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -228,10 +228,10 @@ class _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.
|
||||
|
||||
The callback signature is ``cb(approval_data: dict) -> None`` where
|
||||
|
|
|
|||
|
|
@ -26,10 +26,11 @@ import os
|
|||
import datetime
|
||||
import threading
|
||||
import uuid
|
||||
from typing import Any, Dict, Optional, Union
|
||||
from typing import Any, Callable, Dict, Optional, Type, Union
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import fal_client
|
||||
import httpx
|
||||
|
||||
from tools.debug_helpers import DebugSession
|
||||
from tools.managed_tool_gateway import resolve_managed_tool_gateway
|
||||
|
|
@ -348,21 +349,27 @@ class _ManagedFalSyncClient:
|
|||
|
||||
self._queue_url_format = _normalize_fal_queue_url_format(queue_run_origin)
|
||||
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")
|
||||
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")
|
||||
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")
|
||||
|
||||
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(
|
||||
self,
|
||||
application: str,
|
||||
|
|
|
|||
|
|
@ -1720,8 +1720,8 @@ async def web_crawl_tool(
|
|||
metadata = {}
|
||||
|
||||
# Extract data from the item
|
||||
if hasattr(item, 'model_dump'):
|
||||
# Pydantic model - use model_dump to get dict
|
||||
from pydantic import BaseModel
|
||||
if isinstance(item, BaseModel):
|
||||
item_dict = item.model_dump()
|
||||
content_markdown = item_dict.get('markdown')
|
||||
content_html = item_dict.get('html')
|
||||
|
|
@ -1730,15 +1730,15 @@ async def web_crawl_tool(
|
|||
# Regular object with attributes
|
||||
content_markdown = getattr(item, 'markdown', None)
|
||||
content_html = getattr(item, 'html', None)
|
||||
|
||||
|
||||
# Handle metadata - convert to dict if it's an object
|
||||
metadata_obj = getattr(item, 'metadata', {})
|
||||
if hasattr(metadata_obj, 'model_dump'):
|
||||
if isinstance(metadata_obj, BaseModel):
|
||||
metadata = metadata_obj.model_dump()
|
||||
elif hasattr(metadata_obj, '__dict__'):
|
||||
metadata = metadata_obj.__dict__
|
||||
elif isinstance(metadata_obj, dict):
|
||||
metadata = metadata_obj
|
||||
elif hasattr(metadata_obj, '__dict__'):
|
||||
metadata = metadata_obj.__dict__
|
||||
else:
|
||||
metadata = {}
|
||||
elif isinstance(item, dict):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue