feat(plugins): add local_sqlite_telemetry plugin + on_turn hooks - #22095
feat(plugins): add local_sqlite_telemetry plugin + on_turn hooks#22095Phoenix1819 wants to merge 1 commit into
Conversation
|
Two issues worth flagging: 1. Hardcoded "phoenix" profile fallback In _DB_PATH = os.path.join(os.path.expanduser("~/.hermes"), "profiles", "phoenix", "telemetry.db")This will silently write telemetry to the wrong location for any user whose active profile is not "phoenix". The same hardcode appears three more times in Suggested fix for def _get_db_path() -> str:
global _DB_PATH
if _DB_PATH is None:
hermes_home = os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes"))
profile = os.environ.get("HERMES_PROFILE", "")
if not profile:
# Try to detect from HERMES_HOME basename
base = os.path.basename(hermes_home)
profile = base if base and base != ".hermes" else "default"
profiles_dir = os.path.join(os.path.expanduser("~/.hermes"), "profiles", profile)
if os.path.isdir(profiles_dir):
_DB_PATH = os.path.join(profiles_dir, "telemetry.db")
else:
_DB_PATH = os.path.join(os.path.expanduser("~/.hermes"), "telemetry.db")
return _DB_PATH2. The docstring and
This means the hook silently never fires. Either add the implementation or remove it from the docstring/KNOWN_HOOKS to avoid confusion. |
|
@liuhao1024 Thanks for the review. Both issues fixed in the amended commit:
Also fixed a latent bug in telemetry_cli.py report() where undefined cursor variable c was used instead of conn. Commit amended and force-pushed. |
89763d0 to
29f72fc
Compare
16dea53 to
ed6d1eb
Compare
ed6d1eb to
27ca59a
Compare
|
Fork CI Results (independent validation on Summary: 21654 passed, 9 failed, 57 skipped (~12 min) All 9 failures are pre-existing baseline issues unrelated to this PR:
Zero regressions introduced by this branch. |
Summary
This PR introduces local_sqlite_telemetry, a zero-dependency, zero-credential telemetry plugin for Hermes. It stores LLM calls, tool calls, context pressure events, and session summaries to a local SQLite database per profile.
Problem
Currently, Hermes has no built-in telemetry for operators who want to:
External telemetry (Langfuse, OpenTelemetry) requires credentials and network egress. A local-first option is needed for privacy-conscious or air-gapped deployments.
Solution
A new plugin under
plugins/observability/local_sqlite_telemetry/that writes to~/.hermes/profiles/<profile>/telemetry.db.Schema
llm_callstool_callscontext_pressuresession_summaryHooks consumed
on_session_start/on_session_endpre_llm_call/post_llm_callpre_tool_call/post_tool_callon_turn_start/on_turn_end← new hooks added in this PRNew hooks
To support per-turn tracking, two lifecycle hooks are added to
VALID_HOOKSinhermes_cli/plugins.py:on_turn_start— fired at the beginning of each agent turn (before tool/LLM calls)on_turn_end— fired at the end of each agent turn (after all processing)Both receive the session ID and turn context. They are intentionally minimal — no core agent logic is modified beyond firing the hook.
CLI helper
telemetry_cli.pyprovides ahermes telemetrysubcommand:hermes telemetry summary --days 7 hermes telemetry tools --top 10 hermes telemetry costs --model kimi-k2.6 hermes telemetry export --format csv --output usage.csvWeekly digest
weekly_digest.pygenerates a markdown report suitable for cron:python -m plugins.observability.local_sqlite_telemetry.weekly_digest \ --profile phoenix --output ~/weekly-report.mdBackwards Compatibility
config.yamlplugins listsqlite3)on_turn_start/on_turn_endhooks are no-ops unless a plugin registers themTesting
on_session_startDirectory layout