mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 13:26:40 +00:00
`def h(source, /, **kwargs)` and `def h(*session_id, **kwargs)` cannot take that name by keyword, so the context value belongs in kwargs. (Greptile re-review on #140997.)
33 lines
1.6 KiB
Python
33 lines
1.6 KiB
Python
"""Compatibility-aware call boundary for plugin slash-command handlers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import inspect
|
|
from typing import Any, Callable
|
|
|
|
_KEYWORD_KINDS = (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY)
|
|
|
|
|
|
def invoke_plugin_command(handler: Callable, raw_args: str, **context: Any) -> Any:
|
|
"""Call *handler* once with *raw_args* plus the *context* keywords it declares.
|
|
|
|
Every surface passes the same context (``source``, ``session_id``, ``session_key``,
|
|
``platform``); a handler opts in by naming any of them as keyword-capable parameters, or
|
|
takes them all with ``**kwargs``. Legacy ``fn(raw_args)`` handlers and opaque signatures keep
|
|
the one-argument call. Handler exceptions propagate; they are never retried as a legacy call.
|
|
"""
|
|
try:
|
|
parameters = list(inspect.signature(handler).parameters.values())
|
|
except (TypeError, ValueError):
|
|
return handler(raw_args)
|
|
# The first positional parameter receives raw_args whatever its name (``def h(source, **kw)``
|
|
# must not get ``source`` twice).
|
|
if parameters and parameters[0].kind is not inspect.Parameter.KEYWORD_ONLY:
|
|
if parameters[0].kind is inspect.Parameter.POSITIONAL_OR_KEYWORD:
|
|
context = {k: v for k, v in context.items() if k != parameters[0].name}
|
|
parameters = parameters[1:]
|
|
if any(p.kind is inspect.Parameter.VAR_KEYWORD for p in parameters):
|
|
return handler(raw_args, **context)
|
|
accepted = {p.name for p in parameters if p.kind in _KEYWORD_KINDS}
|
|
return handler(raw_args, **{k: v for k, v in context.items() if k in accepted})
|