mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
Expose skill usage in analytics so the dashboard and insights output can
show which skills the agent loads and manages over time.
This adds skill aggregation to the InsightsEngine by extracting
`skill_view` and `skill_manage` calls from assistant tool_calls,
computing per-skill totals, and including the results in both terminal
and gateway insights formatting. It also extends the dashboard analytics
API and Analytics page to render a Top Skills table.
Terminology is aligned with the skills docs:
- Agent Loaded = `skill_view` events
- Agent Managed = `skill_manage` actions
Architecture:
- agent/insights.py collects and aggregates per-skill usage
- hermes_cli/web_server.py exposes `skills` on `/api/analytics/usage`
- web/src/lib/api.ts adds analytics skill response types
- web/src/pages/AnalyticsPage.tsx renders the Top Skills table
- web/src/i18n/{en,zh}.ts updates user-facing labels
Tests:
- tests/agent/test_insights.py covers skill aggregation and formatting
- tests/hermes_cli/test_web_server.py covers analytics API contract
including the `skills` payload
- verified with `cd web && npm run build`
Files changed:
- agent/insights.py
- hermes_cli/web_server.py
- tests/agent/test_insights.py
- tests/hermes_cli/test_web_server.py
- web/src/i18n/en.ts
- web/src/i18n/types.ts
- web/src/i18n/zh.ts
- web/src/lib/api.ts
- web/src/pages/AnalyticsPage.tsx
283 lines
8.1 KiB
TypeScript
283 lines
8.1 KiB
TypeScript
import type { Translations } from "./types";
|
|
|
|
export const en: Translations = {
|
|
common: {
|
|
save: "Save",
|
|
saving: "Saving...",
|
|
cancel: "Cancel",
|
|
close: "Close",
|
|
delete: "Delete",
|
|
refresh: "Refresh",
|
|
retry: "Retry",
|
|
search: "Search...",
|
|
loading: "Loading...",
|
|
create: "Create",
|
|
creating: "Creating...",
|
|
set: "Set",
|
|
replace: "Replace",
|
|
clear: "Clear",
|
|
live: "Live",
|
|
off: "Off",
|
|
enabled: "enabled",
|
|
disabled: "disabled",
|
|
active: "active",
|
|
inactive: "inactive",
|
|
unknown: "unknown",
|
|
untitled: "Untitled",
|
|
none: "None",
|
|
form: "Form",
|
|
noResults: "No results",
|
|
of: "of",
|
|
page: "Page",
|
|
msgs: "msgs",
|
|
tools: "tools",
|
|
match: "match",
|
|
other: "Other",
|
|
configured: "configured",
|
|
removed: "removed",
|
|
failedToToggle: "Failed to toggle",
|
|
failedToRemove: "Failed to remove",
|
|
failedToReveal: "Failed to reveal",
|
|
collapse: "Collapse",
|
|
expand: "Expand",
|
|
general: "General",
|
|
messaging: "Messaging",
|
|
},
|
|
|
|
app: {
|
|
brand: "Hermes Agent",
|
|
brandShort: "HA",
|
|
webUi: "Web UI",
|
|
footer: {
|
|
name: "Hermes Agent",
|
|
org: "Nous Research",
|
|
},
|
|
nav: {
|
|
status: "Status",
|
|
sessions: "Sessions",
|
|
analytics: "Analytics",
|
|
logs: "Logs",
|
|
cron: "Cron",
|
|
skills: "Skills",
|
|
config: "Config",
|
|
keys: "Keys",
|
|
},
|
|
},
|
|
|
|
status: {
|
|
agent: "Agent",
|
|
gateway: "Gateway",
|
|
activeSessions: "Active Sessions",
|
|
recentSessions: "Recent Sessions",
|
|
connectedPlatforms: "Connected Platforms",
|
|
running: "Running",
|
|
starting: "Starting",
|
|
failed: "Failed",
|
|
stopped: "Stopped",
|
|
connected: "Connected",
|
|
disconnected: "Disconnected",
|
|
error: "Error",
|
|
notRunning: "Not running",
|
|
startFailed: "Start failed",
|
|
pid: "PID",
|
|
runningRemote: "Running (remote)",
|
|
noneRunning: "None",
|
|
gatewayFailedToStart: "Gateway failed to start",
|
|
lastUpdate: "Last update",
|
|
platformError: "error",
|
|
platformDisconnected: "disconnected",
|
|
},
|
|
|
|
sessions: {
|
|
title: "Sessions",
|
|
searchPlaceholder: "Search message content...",
|
|
noSessions: "No sessions yet",
|
|
noMatch: "No sessions match your search",
|
|
startConversation: "Start a conversation to see it here",
|
|
noMessages: "No messages",
|
|
untitledSession: "Untitled session",
|
|
deleteSession: "Delete session",
|
|
previousPage: "Previous page",
|
|
nextPage: "Next page",
|
|
roles: {
|
|
user: "User",
|
|
assistant: "Assistant",
|
|
system: "System",
|
|
tool: "Tool",
|
|
},
|
|
},
|
|
|
|
analytics: {
|
|
period: "Period:",
|
|
totalTokens: "Total Tokens",
|
|
totalSessions: "Total Sessions",
|
|
apiCalls: "API Calls",
|
|
dailyTokenUsage: "Daily Token Usage",
|
|
dailyBreakdown: "Daily Breakdown",
|
|
perModelBreakdown: "Per-Model Breakdown",
|
|
topSkills: "Top Skills",
|
|
skill: "Skill",
|
|
loads: "Agent Loaded",
|
|
edits: "Agent Managed",
|
|
lastUsed: "Last Used",
|
|
input: "Input",
|
|
output: "Output",
|
|
total: "Total",
|
|
noUsageData: "No usage data for this period",
|
|
startSession: "Start a session to see analytics here",
|
|
date: "Date",
|
|
model: "Model",
|
|
tokens: "Tokens",
|
|
perDayAvg: "/day avg",
|
|
acrossModels: "across {count} models",
|
|
inOut: "{input} in / {output} out",
|
|
},
|
|
|
|
logs: {
|
|
title: "Logs",
|
|
autoRefresh: "Auto-refresh",
|
|
file: "File",
|
|
level: "Level",
|
|
component: "Component",
|
|
lines: "Lines",
|
|
noLogLines: "No log lines found",
|
|
},
|
|
|
|
cron: {
|
|
newJob: "New Cron Job",
|
|
nameOptional: "Name (optional)",
|
|
namePlaceholder: "e.g. Daily summary",
|
|
prompt: "Prompt",
|
|
promptPlaceholder: "What should the agent do on each run?",
|
|
schedule: "Schedule (cron expression)",
|
|
schedulePlaceholder: "0 9 * * *",
|
|
deliverTo: "Deliver to",
|
|
scheduledJobs: "Scheduled Jobs",
|
|
noJobs: "No cron jobs configured. Create one above.",
|
|
last: "Last",
|
|
next: "Next",
|
|
pause: "Pause",
|
|
resume: "Resume",
|
|
triggerNow: "Trigger now",
|
|
delivery: {
|
|
local: "Local",
|
|
telegram: "Telegram",
|
|
discord: "Discord",
|
|
slack: "Slack",
|
|
email: "Email",
|
|
},
|
|
},
|
|
|
|
skills: {
|
|
title: "Skills",
|
|
searchPlaceholder: "Search skills and toolsets...",
|
|
enabledOf: "{enabled}/{total} enabled",
|
|
all: "All",
|
|
noSkills: "No skills found. Skills are loaded from ~/.hermes/skills/",
|
|
noSkillsMatch: "No skills match your search or filter.",
|
|
skillCount: "{count} skill{s}",
|
|
resultCount: "{count} result{s}",
|
|
noDescription: "No description available.",
|
|
toolsets: "Toolsets",
|
|
toolsetLabel: "{name} toolset",
|
|
noToolsetsMatch: "No toolsets match the search.",
|
|
setupNeeded: "Setup needed",
|
|
disabledForCli: "Disabled for CLI",
|
|
more: "+{count} more",
|
|
},
|
|
|
|
config: {
|
|
configPath: "~/.hermes/config.yaml",
|
|
exportConfig: "Export config as JSON",
|
|
importConfig: "Import config from JSON",
|
|
resetDefaults: "Reset to defaults",
|
|
rawYaml: "Raw YAML Configuration",
|
|
searchResults: "Search Results",
|
|
fields: "field{s}",
|
|
noFieldsMatch: 'No fields match "{query}"',
|
|
configSaved: "Configuration saved",
|
|
yamlConfigSaved: "YAML config saved",
|
|
failedToSave: "Failed to save",
|
|
failedToSaveYaml: "Failed to save YAML",
|
|
failedToLoadRaw: "Failed to load raw config",
|
|
configImported: "Config imported — review and save",
|
|
invalidJson: "Invalid JSON file",
|
|
categories: {
|
|
general: "General",
|
|
agent: "Agent",
|
|
terminal: "Terminal",
|
|
display: "Display",
|
|
delegation: "Delegation",
|
|
memory: "Memory",
|
|
compression: "Compression",
|
|
security: "Security",
|
|
browser: "Browser",
|
|
voice: "Voice",
|
|
tts: "Text-to-Speech",
|
|
stt: "Speech-to-Text",
|
|
logging: "Logging",
|
|
discord: "Discord",
|
|
auxiliary: "Auxiliary",
|
|
},
|
|
},
|
|
|
|
env: {
|
|
description: "Manage API keys and secrets stored in",
|
|
changesNote: "Changes are saved to disk immediately. Active sessions pick up new keys automatically.",
|
|
hideAdvanced: "Hide Advanced",
|
|
showAdvanced: "Show Advanced",
|
|
llmProviders: "LLM Providers",
|
|
providersConfigured: "{configured} of {total} providers configured",
|
|
getKey: "Get key",
|
|
notConfigured: "{count} not configured",
|
|
notSet: "Not set",
|
|
keysCount: "{count} key{s}",
|
|
enterValue: "Enter value...",
|
|
replaceCurrentValue: "Replace current value ({preview})",
|
|
showValue: "Show real value",
|
|
hideValue: "Hide value",
|
|
},
|
|
|
|
oauth: {
|
|
title: "Provider Logins (OAuth)",
|
|
providerLogins: "Provider Logins (OAuth)",
|
|
description: "{connected} of {total} OAuth providers connected. Login flows currently run via the CLI; click Copy command and paste into a terminal to set up.",
|
|
connected: "Connected",
|
|
expired: "Expired",
|
|
notConnected: "Not connected. Run {command} in a terminal.",
|
|
runInTerminal: "in a terminal.",
|
|
noProviders: "No OAuth-capable providers detected.",
|
|
login: "Login",
|
|
disconnect: "Disconnect",
|
|
managedExternally: "Managed externally",
|
|
copied: "Copied ✓",
|
|
cli: "CLI",
|
|
copyCliCommand: "Copy CLI command (for external / fallback)",
|
|
connect: "Connect",
|
|
sessionExpires: "Session expires in {time}",
|
|
initiatingLogin: "Initiating login flow…",
|
|
exchangingCode: "Exchanging code for tokens…",
|
|
connectedClosing: "Connected! Closing…",
|
|
loginFailed: "Login failed.",
|
|
sessionExpired: "Session expired. Click Retry to start a new login.",
|
|
reOpenAuth: "Re-open auth page",
|
|
reOpenVerification: "Re-open verification page",
|
|
submitCode: "Submit code",
|
|
pasteCode: "Paste authorization code (with #state suffix is fine)",
|
|
waitingAuth: "Waiting for you to authorize in the browser…",
|
|
enterCodePrompt: "A new tab opened. Enter this code if prompted:",
|
|
pkceStep1: "A new tab opened to claude.ai. Sign in and click Authorize.",
|
|
pkceStep2: "Copy the authorization code shown after authorizing.",
|
|
pkceStep3: "Paste it below and submit.",
|
|
flowLabels: {
|
|
pkce: "Browser login (PKCE)",
|
|
device_code: "Device code",
|
|
external: "External CLI",
|
|
},
|
|
expiresIn: "expires in {time}",
|
|
},
|
|
|
|
language: {
|
|
switchTo: "Switch to Chinese",
|
|
},
|
|
};
|