fix(plugins): await async hook callbacks instead of collecting bare coroutines
Slash-command handlers gained loop-safe awaiting in ca9a61ae38, but
`PluginManager.invoke_hook` still called `async def` hook callbacks directly:
the coroutine object was appended to the results (so `pre_llm_call` context
injection silently did nothing) and Python warned "coroutine was never
awaited". `_invoke_hook_callback` now routes every return through
`resolve_plugin_command_result`, which covers both the direct and the
timeout-bounded paths and is safe under the gateway's running loop.
Fixes #12449 (remaining hook half). Salvage of #63240 by @Bartok9, applied
one layer down so the bounded-worker path is covered too.
Co-authored-by: Bartok9 <Bartok9@users.noreply.github.com>
This commit is contained in:
@@ -150,18 +150,24 @@ def _hook_uses_callback_timeout(hook_name: str, timeout: float) -> bool:
|
||||
class PluginDispatchMixin:
|
||||
@staticmethod
|
||||
def _invoke_hook_callback(callback: Callable, payload: Dict[str, Any]) -> Any:
|
||||
"""Invoke a hook while withholding additive fields from narrow legacy callbacks."""
|
||||
"""Invoke a hook while withholding additive fields from narrow legacy callbacks.
|
||||
|
||||
An ``async def`` callback returns a coroutine; resolve it the way plugin slash commands
|
||||
are (loop-safe), otherwise the bare coroutine object is appended to the results and the
|
||||
plugin's body never runs (#12449).
|
||||
"""
|
||||
from hermes_cli.plugins import resolve_plugin_command_result
|
||||
try:
|
||||
parameters = inspect.signature(callback).parameters
|
||||
except (TypeError, ValueError):
|
||||
return callback(**payload) # no introspectable signature: historical behavior
|
||||
return resolve_plugin_command_result(callback(**payload)) # no introspectable signature
|
||||
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in parameters.values()):
|
||||
return callback(**payload)
|
||||
return resolve_plugin_command_result(callback(**payload))
|
||||
keyword_kinds = {inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY}
|
||||
return callback(**{
|
||||
return resolve_plugin_command_result(callback(**{
|
||||
name: value for name, value in payload.items()
|
||||
if name in parameters and parameters[name].kind in keyword_kinds
|
||||
})
|
||||
}))
|
||||
|
||||
def invoke_hook(self, hook_name: str, **kwargs: Any) -> List[Any]:
|
||||
"""Call all callbacks for *hook_name*; return their non-``None`` results.
|
||||
|
||||
Reference in New Issue
Block a user