Z User commited on
Commit
c310c5b
·
0 Parent(s):

Hermes Bot: initial deployment with Feishu gateway

Browse files
Files changed (5) hide show
  1. Dockerfile +45 -0
  2. README.md +17 -0
  3. SOUL.md +20 -0
  4. config.yaml +21 -0
  5. entry.py +115 -0
Dockerfile ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12-slim
2
+
3
+ # System deps
4
+ RUN apt-get update && apt-get install -y --no-install-recommends \
5
+ git curl gnupg2 \
6
+ && rm -rf /var/lib/apt/lists/*
7
+
8
+ # Create persistent data directories
9
+ RUN mkdir -p /data/hermes/{sessions,memories,uploads,logs,palace,skills}
10
+
11
+ WORKDIR /app
12
+
13
+ # Clone hermes-agent
14
+ RUN git clone --depth 1 https://github.com/NousResearch/hermes-agent.git /app/hermes-agent
15
+
16
+ # Build venv
17
+ RUN python3 -m venv /app/venv
18
+ ENV PATH="/app/venv/bin:$PATH"
19
+ RUN pip install --quiet --upgrade pip && \
20
+ pip install --quiet -e "/app/hermes-agent[feishu,mcp,cron,pty]" 2>&1 | tail -5
21
+
22
+ # Create hermes home with symlinks to persistent storage
23
+ RUN mkdir -p /root/.hermes && \
24
+ ln -sf /data/hermes/sessions /root/.hermes/sessions && \
25
+ ln -sf /data/hermes/memories /root/.hermes/memories && \
26
+ ln -sf /data/hermes/uploads /root/.hermes/uploads && \
27
+ ln -sf /data/hermes/logs /root/.hermes/logs && \
28
+ ln -sf /data/hermes/palace /root/.hermes/palace && \
29
+ ln -sf /data/hermes/skills /root/.hermes/skills
30
+
31
+ # Copy config files
32
+ COPY config.yaml /root/.hermes/config.yaml
33
+ COPY SOUL.md /root/.hermes/SOUL.md
34
+ COPY .env /root/.hermes/.env
35
+ COPY entry.py /app/entry.py
36
+
37
+ RUN chmod 600 /root/.hermes/.env
38
+
39
+ # Expose port (for HF health check, not actually serving HTTP)
40
+ EXPOSE 7860
41
+
42
+ ENV HERMES_ACCEPT_HOOKS=1
43
+ ENV MEMPALACE_PALACE_PATH=/data/hermes/palace
44
+
45
+ CMD ["python3", "/app/entry.py"]
README.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Hermes Bot
3
+ emoji: ⚕️
4
+ colorFrom: green
5
+ colorTo: blue
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: false
9
+ ---
10
+
11
+ # Hermes Agent — Feishu Bot
12
+
13
+ AI assistant powered by [Hermes Agent](https://github.com/NousResearch/hermes-agent) with Feishu integration.
14
+
15
+ **Model:** inclusionai/ling-2.6-1t:free (OpenRouter)
16
+
17
+ > ⚠️ This Space uses Persistent Storage for session/memory persistence.
SOUL.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are Hermes Agent, an intelligent AI assistant created by Nous Research. You are helpful, knowledgeable, and direct. You assist users with a wide range of tasks including answering questions, writing and editing code, analyzing information, creative work, and executing actions via your tools. You communicate clearly, admit uncertainty when appropriate, and prioritize being genuinely useful over being verbose.
2
+
3
+ ## Memory Iron Rules (Must follow every conversation turn)
4
+
5
+ ### Rule 1: Load memory at conversation start
6
+ - When receiving a user message, first check memory tools for user-related info
7
+ - Review previous conversation context and user preferences
8
+
9
+ ### Rule 2: Save key information immediately
10
+ - User's personal info (name, preferences, projects, etc.) -> save to memory immediately
11
+ - Important conversation conclusions and decisions -> save immediately
12
+ - Don't wait until conversation ends to save
13
+
14
+ ### Rule 3: Check for missed saves before ending
15
+ - Before the last reply, check if any important info hasn't been saved
16
+ - Ensure all key memories are persisted
17
+
18
+ ### Rule 4: Memory usage norms
19
+ - Call memory-related tools at least once per conversation turn (read or write)
20
+ - Reference memory content naturally in replies, don't force "based on memory..."
config.yaml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model: inclusionai/ling-2.6-1t:free
2
+ provider: openrouter
3
+ max_turns: 90
4
+ platforms:
5
+ feishu:
6
+ enabled: true
7
+ extra:
8
+ domain: feishu
9
+ connection_mode: websocket
10
+ memory:
11
+ provider: none
12
+ compress:
13
+ enabled: true
14
+ threshold: 50
15
+ target_ratio: 20
16
+ protect_last: 20
17
+ no_mcp: true
18
+ terminal:
19
+ backend: local
20
+ timeout: 180
21
+ timezone: Asia/Shanghai
entry.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Hermes Agent entry point for HuggingFace Space.
3
+
4
+ - Starts a trivial HTTP server on port 7860 (HF health check).
5
+ - Launches the Hermes Gateway in a background thread.
6
+ - Keeps running until interrupted.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import threading
12
+ import time
13
+ import logging
14
+ from http.server import HTTPServer, BaseHTTPRequestHandler
15
+
16
+ logging.basicConfig(
17
+ level=logging.INFO,
18
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
19
+ )
20
+ logger = logging.getLogger("entry")
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Health-check server (HF requires port 7860 to be listening)
24
+ # ---------------------------------------------------------------------------
25
+
26
+ class _HealthHandler(BaseHTTPRequestHandler):
27
+ def do_GET(self):
28
+ self.send_response(200)
29
+ self.send_header("Content-Type", "text/plain")
30
+ self.end_headers()
31
+ self.wfile.write(b"Hermes Gateway is running")
32
+
33
+ def log_message(self, fmt, *args):
34
+ pass # silence health-check logs
35
+
36
+
37
+ def _start_health_server(port=7860):
38
+ server = HTTPServer(("0.0.0.0", port), _HealthHandler)
39
+ logger.info("Health-check server listening on :%d", port)
40
+ server.serve_forever()
41
+
42
+
43
+ # ---------------------------------------------------------------------------
44
+ # Memory health check
45
+ # ---------------------------------------------------------------------------
46
+
47
+ def check_memory_health():
48
+ """Verify persistent storage symlinks are intact."""
49
+ hermes_home = os.path.expanduser("~/.hermes")
50
+ issues = []
51
+ for subdir in ("sessions", "memories", "uploads"):
52
+ target = os.path.join(hermes_home, subdir)
53
+ if os.path.islink(target):
54
+ real = os.path.realpath(target)
55
+ if not os.path.isdir(real):
56
+ issues.append(f"{subdir} -> {real} (target missing)")
57
+ else:
58
+ logger.info("Symlink OK: %s -> %s", subdir, real)
59
+ elif os.path.isdir(target):
60
+ logger.warning("NOT a symlink: %s (data may be lost on rebuild)", target)
61
+ else:
62
+ issues.append(f"{subdir} missing")
63
+
64
+ # Check persistent volume writable
65
+ test_path = "/data/hermes/logs/write_test"
66
+ try:
67
+ os.makedirs(os.path.dirname(test_path), exist_ok=True)
68
+ with open(test_path, "w") as f:
69
+ f.write("ok")
70
+ os.remove(test_path)
71
+ logger.info("Persistent storage /data/hermes is writable")
72
+ except Exception as e:
73
+ issues.append(f"Cannot write to /data/hermes: {e}")
74
+
75
+ if issues:
76
+ logger.warning("Memory health issues: %s", "; ".join(issues))
77
+ else:
78
+ logger.info("Memory health check PASSED")
79
+
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # Main
83
+ # ---------------------------------------------------------------------------
84
+
85
+ def main():
86
+ logger.info("=== Hermes Agent — HuggingFace Space Entry ===")
87
+
88
+ # Check persistent storage
89
+ check_memory_health()
90
+
91
+ # Start health-check server in background
92
+ health_thread = threading.Thread(target=_start_health_server, daemon=True)
93
+ health_thread.start()
94
+
95
+ # Launch Hermes Gateway
96
+ from hermes_cli.main import cli
97
+ logger.info("Launching Hermes Gateway...")
98
+
99
+ # The gateway blocks, so we run it in the main thread
100
+ try:
101
+ sys.argv = ["hermes", "gateway", "run", "-v"]
102
+ cli()
103
+ except KeyboardInterrupt:
104
+ logger.info("Shutting down...")
105
+ except SystemExit as e:
106
+ if e.code != 0:
107
+ logger.error("Gateway exited with code %s", e.code)
108
+ raise
109
+ except Exception as e:
110
+ logger.error("Gateway crashed: %s", e, exc_info=True)
111
+ raise
112
+
113
+
114
+ if __name__ == "__main__":
115
+ main()