feat(web-console): cherry-pick React web console GUI from gary-the-ai fork
Some checks failed
Forge CI / smoke-and-build (pull_request) Failing after 59s
Some checks failed
Forge CI / smoke-and-build (pull_request) Failing after 59s
Cherry-pick the Hermes Web Console from gary-the-ai/hermes-web-console-gui. React + TypeScript frontend with Vite, Python aiohttp backend API. Components: - web_console/ — React frontend (chat, sessions, memory, settings, skills, gateway config, cron, workspace, tools, browser, insights pages) - gateway/web_console/ — Python backend API (23 endpoints, SSE event bus, 11 service modules) - gateway/platforms/api_server_ui.py — embedded browser UI for API server - gateway/platforms/api_server.py — route registration refactored into _register_routes(), web console mounted via maybe_register_web_console() - run-gui.sh / setup-gui.sh — one-command launch and setup scripts - tests/gateway/test_api_server_gui_mount.py — 4 integration tests (passing) - tests/web_console/ — 13 backend test files (51 passing) - docs/plans/ — implementation plan, API schema, frontend architecture Fix: added missing ModelContextError class and CRON_MIN_CONTEXT_TOKENS to cron/scheduler.py (pre-existing import bug). Closes #325
This commit is contained in:
53
gateway/web_console/api/usage.py
Normal file
53
gateway/web_console/api/usage.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import logging
|
||||
from aiohttp import web
|
||||
|
||||
logger = logging.getLogger("usage_api")
|
||||
|
||||
async def handle_get_usage_insights(request: web.Request) -> web.Response:
|
||||
try:
|
||||
days = int(request.query.get("days", 30))
|
||||
source = request.query.get("source")
|
||||
|
||||
from hermes_state import SessionDB
|
||||
from agent.insights import InsightsEngine
|
||||
|
||||
db = SessionDB()
|
||||
engine = InsightsEngine(db)
|
||||
report = engine.generate(days=days, source=source)
|
||||
db.close()
|
||||
|
||||
return web.json_response({"ok": True, "report": report})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch usage insights: {e}")
|
||||
return web.json_response({"ok": False, "error": str(e)}, status=500)
|
||||
|
||||
async def handle_get_session_usage(request: web.Request) -> web.Response:
|
||||
try:
|
||||
session_id = request.match_info.get("id")
|
||||
if not session_id:
|
||||
return web.json_response({"ok": False, "error": "Session ID required"}, status=400)
|
||||
|
||||
from hermes_state import SessionDB
|
||||
db = SessionDB()
|
||||
try:
|
||||
session = db.get_session(session_id)
|
||||
if not session:
|
||||
return web.json_response({"ok": False, "error": "Not found"}, status=404)
|
||||
|
||||
return web.json_response({"ok": True, "session_usage": {
|
||||
"input_tokens": getattr(session, "input_tokens", 0) or 0,
|
||||
"output_tokens": getattr(session, "output_tokens", 0) or 0,
|
||||
"cache_read_tokens": getattr(session, "cache_read_tokens", 0) or 0,
|
||||
"cache_write_tokens": getattr(session, "cache_write_tokens", 0) or 0,
|
||||
"total_tokens": getattr(session, "total_tokens", 0) or 0,
|
||||
"estimated_cost_usd": getattr(session, "estimated_cost_usd", 0.0) or 0.0,
|
||||
}})
|
||||
finally:
|
||||
db.close()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch session usage: {e}")
|
||||
return web.json_response({"ok": False, "error": str(e)}, status=500)
|
||||
|
||||
def register_usage_api_routes(app: web.Application) -> None:
|
||||
app.router.add_get("/api/gui/usage/insights", handle_get_usage_insights)
|
||||
app.router.add_get("/api/gui/usage/session/{id}", handle_get_session_usage)
|
||||
Reference in New Issue
Block a user