Skip to content

feat: activate plugin lifecycle hooks (pre/post_llm_call, session start/end)#2823

Closed
nicoloboschi wants to merge 2 commits into
NousResearch:mainfrom
nicoloboschi:feat/activate-plugin-lifecycle-hooks
Closed

feat: activate plugin lifecycle hooks (pre/post_llm_call, session start/end)#2823
nicoloboschi wants to merge 2 commits into
NousResearch:mainfrom
nicoloboschi:feat/activate-plugin-lifecycle-hooks

Conversation

@nicoloboschi

Copy link
Copy Markdown
Contributor

Summary

  • Activates the four lifecycle hooks that were defined in the plugin system but never invoked: on_session_start, pre_llm_call, post_llm_call, on_session_end
  • pre_llm_call can return {"context": "..."} to inject context into the ephemeral system prompt — enabling memory plugins to provide recalled context without core changes
  • invoke_hook() now returns collected non-None callback results (backward compatible — existing hooks returning None are unaffected)
  • Hook failures are logged as warnings instead of silently swallowed

Motivation

This enables external memory systems (e.g. Hindsight) to integrate as pip-installable plugins that hook every conversation turn, rather than requiring core integrations that add bloat. See discussion on #1811.

Hook semantics

Hook When Can return context?
on_session_start New session created (first turn) No
pre_llm_call Once per turn, before LLM loop Yes — {"context": "..."}
post_llm_call Once per turn, after LLM loop No
on_session_end End of run_conversation() No

Changes

  • hermes_cli/plugins.pyinvoke_hook() returns List[Any] of non-None results
  • run_agent.py — invoke all four hooks at the appropriate lifecycle points
  • website/docs/guides/build-a-hermes-plugin.md — updated hook reference table
  • tests/test_plugins.py — added tests for return value collection

Test plan

  • All existing plugin tests pass
  • New tests verify return value collection and None exclusion
  • Manual: install a plugin that registers pre_llm_call, verify context appears in system prompt

…rt/end)

The plugin system defined six lifecycle hooks but only pre_tool_call and
post_tool_call were invoked.  This activates the remaining four so that
external plugins (e.g. memory systems) can hook into the conversation
loop without touching core code.

Hook semantics:
- on_session_start: fires once when a new session is created
- pre_llm_call: fires once per turn before the tool-calling loop;
  plugins can return {"context": "..."} to inject into the ephemeral
  system prompt (not cached, not persisted)
- post_llm_call: fires once per turn after the loop completes, with
  user_message and assistant_response for sync/storage
- on_session_end: fires at the end of every run_conversation call

invoke_hook() now returns a list of non-None callback return values,
enabling pre_llm_call context injection while remaining backward
compatible (existing hooks that return None are unaffected).
Pass platform to pre_llm_call and post_llm_call so plugins can tailor
behavior per platform (cli, telegram, discord, etc.).

Pass conversation_history to post_llm_call so plugins can see tool
calls and intermediate messages from the turn, not just the final
response text.
teknium1 pushed a commit that referenced this pull request Mar 28, 2026
…rt/end)

The plugin system defined six lifecycle hooks but only pre_tool_call and
post_tool_call were invoked.  This activates the remaining four so that
external plugins (e.g. memory systems) can hook into the conversation
loop without touching core code.

Hook semantics:
- on_session_start: fires once when a new session is created
- pre_llm_call: fires once per turn before the tool-calling loop;
  plugins can return {"context": "..."} to inject into the ephemeral
  system prompt (not cached, not persisted)
- post_llm_call: fires once per turn after the loop completes, with
  user_message and assistant_response for sync/storage
- on_session_end: fires at the end of every run_conversation call

invoke_hook() now returns a list of non-None callback return values,
enabling pre_llm_call context injection while remaining backward
compatible (existing hooks that return None are unaffected).

Salvaged from PR #2823.
teknium1 added a commit that referenced this pull request Mar 28, 2026
…rt/end) (#3542)

The plugin system defined six lifecycle hooks but only pre_tool_call and
post_tool_call were invoked.  This activates the remaining four so that
external plugins (e.g. memory systems) can hook into the conversation
loop without touching core code.

Hook semantics:
- on_session_start: fires once when a new session is created
- pre_llm_call: fires once per turn before the tool-calling loop;
  plugins can return {"context": "..."} to inject into the ephemeral
  system prompt (not cached, not persisted)
- post_llm_call: fires once per turn after the loop completes, with
  user_message and assistant_response for sync/storage
- on_session_end: fires at the end of every run_conversation call

invoke_hook() now returns a list of non-None callback return values,
enabling pre_llm_call context injection while remaining backward
compatible (existing hooks that return None are unaffected).

Salvaged from PR #2823.

Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #3542. Your commits were cherry-picked onto current main with authorship preserved. Follow-up additions: conversation_history passed as a shallow copy to prevent plugin mutation, added model/platform to on_session_end for consistency, updated docs to remove (planned) markers. Thanks for the contribution!

@teknium1 teknium1 closed this Mar 28, 2026
angelburgosrosado pushed a commit to angelburgosrosado/hermes-agent that referenced this pull request Apr 27, 2026
…rt/end) (NousResearch#3542)

The plugin system defined six lifecycle hooks but only pre_tool_call and
post_tool_call were invoked.  This activates the remaining four so that
external plugins (e.g. memory systems) can hook into the conversation
loop without touching core code.

Hook semantics:
- on_session_start: fires once when a new session is created
- pre_llm_call: fires once per turn before the tool-calling loop;
  plugins can return {"context": "..."} to inject into the ephemeral
  system prompt (not cached, not persisted)
- post_llm_call: fires once per turn after the loop completes, with
  user_message and assistant_response for sync/storage
- on_session_end: fires at the end of every run_conversation call

invoke_hook() now returns a list of non-None callback return values,
enabling pre_llm_call context injection while remaining backward
compatible (existing hooks that return None are unaffected).

Salvaged from PR NousResearch#2823.

Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…rt/end) (NousResearch#3542)

The plugin system defined six lifecycle hooks but only pre_tool_call and
post_tool_call were invoked.  This activates the remaining four so that
external plugins (e.g. memory systems) can hook into the conversation
loop without touching core code.

Hook semantics:
- on_session_start: fires once when a new session is created
- pre_llm_call: fires once per turn before the tool-calling loop;
  plugins can return {"context": "..."} to inject into the ephemeral
  system prompt (not cached, not persisted)
- post_llm_call: fires once per turn after the loop completes, with
  user_message and assistant_response for sync/storage
- on_session_end: fires at the end of every run_conversation call

invoke_hook() now returns a list of non-None callback return values,
enabling pre_llm_call context injection while remaining backward
compatible (existing hooks that return None are unaffected).

Salvaged from PR NousResearch#2823.

Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
olympus-terminal pushed a commit to olympus-terminal/hermes-agent that referenced this pull request May 16, 2026
…rt/end) (NousResearch#3542)

The plugin system defined six lifecycle hooks but only pre_tool_call and
post_tool_call were invoked.  This activates the remaining four so that
external plugins (e.g. memory systems) can hook into the conversation
loop without touching core code.

Hook semantics:
- on_session_start: fires once when a new session is created
- pre_llm_call: fires once per turn before the tool-calling loop;
  plugins can return {"context": "..."} to inject into the ephemeral
  system prompt (not cached, not persisted)
- post_llm_call: fires once per turn after the loop completes, with
  user_message and assistant_response for sync/storage
- on_session_end: fires at the end of every run_conversation call

invoke_hook() now returns a list of non-None callback return values,
enabling pre_llm_call context injection while remaining backward
compatible (existing hooks that return None are unaffected).

Salvaged from PR NousResearch#2823.

Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…rt/end) (NousResearch#3542)

The plugin system defined six lifecycle hooks but only pre_tool_call and
post_tool_call were invoked.  This activates the remaining four so that
external plugins (e.g. memory systems) can hook into the conversation
loop without touching core code.

Hook semantics:
- on_session_start: fires once when a new session is created
- pre_llm_call: fires once per turn before the tool-calling loop;
  plugins can return {"context": "..."} to inject into the ephemeral
  system prompt (not cached, not persisted)
- post_llm_call: fires once per turn after the loop completes, with
  user_message and assistant_response for sync/storage
- on_session_end: fires at the end of every run_conversation call

invoke_hook() now returns a list of non-None callback return values,
enabling pre_llm_call context injection while remaining backward
compatible (existing hooks that return None are unaffected).

Salvaged from PR NousResearch#2823.

Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
…rt/end) (NousResearch#3542)

The plugin system defined six lifecycle hooks but only pre_tool_call and
post_tool_call were invoked.  This activates the remaining four so that
external plugins (e.g. memory systems) can hook into the conversation
loop without touching core code.

Hook semantics:
- on_session_start: fires once when a new session is created
- pre_llm_call: fires once per turn before the tool-calling loop;
  plugins can return {"context": "..."} to inject into the ephemeral
  system prompt (not cached, not persisted)
- post_llm_call: fires once per turn after the loop completes, with
  user_message and assistant_response for sync/storage
- on_session_end: fires at the end of every run_conversation call

invoke_hook() now returns a list of non-None callback return values,
enabling pre_llm_call context injection while remaining backward
compatible (existing hooks that return None are unaffected).

Salvaged from PR NousResearch#2823.

Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants