hermes-deploy commited on
Commit ·
f8efeff
1
Parent(s): b78602b
v4: fix prisma crash (ignore stale DATABASE_URL) + telegram relay support
Browse files- .env.example +2 -1
- SETUP.md +27 -1
- cloudflare-worker/telegram-relay-worker.js +44 -0
- entrypoint.sh +6 -0
- gen_hermes_config.py +15 -1
.env.example
CHANGED
|
@@ -3,7 +3,8 @@
|
|
| 3 |
TELEGRAM_BOT_TOKEN=123456:replace-me-from-botfather
|
| 4 |
TELEGRAM_ALLOW_FROM=123456789
|
| 5 |
LITELLM_MASTER_KEY=sk-replace-me-random
|
| 6 |
-
DEFAULT_TIER=fast
|
|
|
|
| 7 |
|
| 8 |
GEMINI_API_KEY_1=AIza...key1
|
| 9 |
GEMINI_API_KEY_2=AIza...key2
|
|
|
|
| 3 |
TELEGRAM_BOT_TOKEN=123456:replace-me-from-botfather
|
| 4 |
TELEGRAM_ALLOW_FROM=123456789
|
| 5 |
LITELLM_MASTER_KEY=sk-replace-me-random
|
| 6 |
+
# DEFAULT_TIER=fast
|
| 7 |
+
# TELEGRAM_RELAY_URL=https://your-worker.workers.dev (Telegram relay, see SETUP §6b)
|
| 8 |
|
| 9 |
GEMINI_API_KEY_1=AIza...key1
|
| 10 |
GEMINI_API_KEY_2=AIza...key2
|
SETUP.md
CHANGED
|
@@ -59,6 +59,10 @@ Optional BotFather polish: `/setdescription`, `/setuserpic`, `/setcommands`, and
|
|
| 59 |
| `NVIDIA_API_KEY_1` … `_N` | recommended | your NVIDIA keys |
|
| 60 |
| `GROQ_API_KEY`, `CEREBRAS_API_KEY`, `MISTRAL_API_KEY`, `OPENROUTER_API_KEY`, `GITHUB_MODELS_TOKEN`, `MODELSCOPE_API_KEY`, `SAMBANOVA_API_KEY`, `COHERE_API_KEY`, `ZAI_API_KEY` | optional | each adds runway; Groq also unlocks voice-memo transcription |
|
| 61 |
| `DEFAULT_TIER` | optional | `fast` (default) or `smart` |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
Boot logs show `[ ok ]` per provider detected, then `fast deployments: N | smart deployments: N`.
|
| 64 |
|
|
@@ -70,6 +74,26 @@ Boot logs show `[ ok ]` per provider detected, then `fast deployments: N | smart
|
|
| 70 |
4. **Big-brain mode:** `/model custom:router:smart` → ask something hard → `/model custom:router:fast`
|
| 71 |
5. Useful Hermes commands: `/new` (fresh session), `/sethome` (cron deliveries land here), `/status`, `/commands`
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
## §6 — 24/7 keep-alive (3 free layers)
|
| 74 |
|
| 75 |
1. **UptimeRobot:** free account → HTTP(s) monitor → `https://<YOU>-<SPACE>.hf.space/healthz` → 5-min interval
|
|
@@ -80,7 +104,9 @@ Boot logs show `[ ok ]` per provider detected, then `fast deployments: N | smart
|
|
| 80 |
|
| 81 |
| Symptom | Fix |
|
| 82 |
|---|---|
|
| 83 |
-
| Bot silent in DMs | `TELEGRAM_ALLOW_FROM` must be your **numeric** ID
|
|
|
|
|
|
|
| 84 |
| Status page says router "starting" forever | Check Space logs for `[skip]/[ ok ]` lines — no valid keys? Add secrets → Restart |
|
| 85 |
| Replies fail with auth errors | `LITELLM_MASTER_KEY` must be set and non-empty |
|
| 86 |
| Answers suddenly dumber | Some keys cooling down; normal. More keys → less of this |
|
|
|
|
| 59 |
| `NVIDIA_API_KEY_1` … `_N` | recommended | your NVIDIA keys |
|
| 60 |
| `GROQ_API_KEY`, `CEREBRAS_API_KEY`, `MISTRAL_API_KEY`, `OPENROUTER_API_KEY`, `GITHUB_MODELS_TOKEN`, `MODELSCOPE_API_KEY`, `SAMBANOVA_API_KEY`, `COHERE_API_KEY`, `ZAI_API_KEY` | optional | each adds runway; Groq also unlocks voice-memo transcription |
|
| 61 |
| `DEFAULT_TIER` | optional | `fast` (default) or `smart` |
|
| 62 |
+
| `TELEGRAM_RELAY_URL` | ✅ **required on HF** | your Cloudflare Worker URL — see §5b |
|
| 63 |
+
|
| 64 |
+
⚠️ If you have an old **`DATABASE_URL`** secret from another project: delete it
|
| 65 |
+
(or leave it — the router now ignores it on purpose; §7).
|
| 66 |
|
| 67 |
Boot logs show `[ ok ]` per provider detected, then `fast deployments: N | smart deployments: N`.
|
| 68 |
|
|
|
|
| 74 |
4. **Big-brain mode:** `/model custom:router:smart` → ask something hard → `/model custom:router:fast`
|
| 75 |
5. Useful Hermes commands: `/new` (fresh session), `/sethome` (cron deliveries land here), `/status`, `/commands`
|
| 76 |
|
| 77 |
+
## §5b — Telegram relay (REQUIRED — HF blocks api.telegram.org)
|
| 78 |
+
|
| 79 |
+
HF Spaces' outbound network times out on `api.telegram.org` (anti-abuse policy;
|
| 80 |
+
measured & confirmed). The clean fix is your **own free Cloudflare Worker relay**:
|
| 81 |
+
|
| 82 |
+
1. [cloudflare.com](https://cloudflare.com) → free sign-up (no card)
|
| 83 |
+
2. Dashboard → **Compute (Workers)** → **Create** → **Create Worker** → name it
|
| 84 |
+
e.g. `tg-relay` → **Deploy**
|
| 85 |
+
3. **Edit code** → delete the template → paste **the entire content of
|
| 86 |
+
`cloudflare-worker/telegram-relay-worker.js`** from this repo → **Deploy**
|
| 87 |
+
4. Copy the worker URL it shows: `https://tg-relay.<you>.workers.dev`
|
| 88 |
+
5. Space → **Settings → Variables and secrets** → new secret:
|
| 89 |
+
`TELEGRAM_RELAY_URL` = that URL (no trailing slash)
|
| 90 |
+
6. **Restart Space**. Boot logs should print `==> Telegram relay enabled via ...`
|
| 91 |
+
and the gateway should connect on attempt 1.
|
| 92 |
+
|
| 93 |
+
The worker only relays Bot API calls to your own bot (token never stored in the
|
| 94 |
+
worker — it travels inside the request path, same as normal Bot API usage).
|
| 95 |
+
Delete the worker anytime to cut the cord.
|
| 96 |
+
|
| 97 |
## §6 — 24/7 keep-alive (3 free layers)
|
| 98 |
|
| 99 |
1. **UptimeRobot:** free account → HTTP(s) monitor → `https://<YOU>-<SPACE>.hf.space/healthz` → 5-min interval
|
|
|
|
| 104 |
|
| 105 |
| Symptom | Fix |
|
| 106 |
|---|---|
|
| 107 |
+
| Bot silent in DMs | ① `TELEGRAM_ALLOW_FROM` must be your **numeric** ID ② logs show `telegram connect timed out` → set up the relay, §5b |
|
| 108 |
+
| Logs: `telegram connect timed out after 45s` | **HF blocks api.telegram.org.** Not your token. Do §5b (Cloudflare relay) |
|
| 109 |
+
| Logs: `prisma` / `database connection` in router step | Stale `DATABASE_URL` secret — already neutralized in entrypoint (deleting the secret is cosmetic) |
|
| 110 |
| Status page says router "starting" forever | Check Space logs for `[skip]/[ ok ]` lines — no valid keys? Add secrets → Restart |
|
| 111 |
| Replies fail with auth errors | `LITELLM_MASTER_KEY` must be set and non-empty |
|
| 112 |
| Answers suddenly dumber | Some keys cooling down; normal. More keys → less of this |
|
cloudflare-worker/telegram-relay-worker.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ─────────────────────────────────────────────────────────────
|
| 2 |
+
// Telegram Bot API relay — Cloudflare Worker (free tier, no card)
|
| 3 |
+
// Deploy this, then set Space secret:
|
| 4 |
+
// TELEGRAM_RELAY_URL = https://<your-subdomain>.workers.dev
|
| 5 |
+
// (no trailing slash). Hermes will then call the Bot API THROUGH
|
| 6 |
+
// this worker — because api.telegram.org is unreachable from HF Spaces.
|
| 7 |
+
//
|
| 8 |
+
// Handles: POST/GET /bot<token>/<method>
|
| 9 |
+
// GET /file/bot<token>/<path> (file downloads)
|
| 10 |
+
// Long-polling (getUpdates) works on the free plan.
|
| 11 |
+
// ─────────────────────────────────────────────────────────────
|
| 12 |
+
export default {
|
| 13 |
+
async fetch(request) {
|
| 14 |
+
const url = new URL(request.url);
|
| 15 |
+
|
| 16 |
+
// health check for your browser / UptimeRobot
|
| 17 |
+
if (url.pathname === "/" || url.pathname === "/healthz") {
|
| 18 |
+
return new Response("telegram-relay alive\n", { status: 200 });
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
// only Bot API shapes are relayed — everything else gets a 404
|
| 22 |
+
if (!url.pathname.startsWith("/bot") && !url.pathname.startsWith("/file/bot")) {
|
| 23 |
+
return new Response("not found\n", { status: 404 });
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
const target = "https://api.telegram.org" + url.pathname + url.search;
|
| 27 |
+
const isBodyless = request.method === "GET" || request.method === "HEAD";
|
| 28 |
+
|
| 29 |
+
const resp = await fetch(target, {
|
| 30 |
+
method: request.method,
|
| 31 |
+
headers: request.headers,
|
| 32 |
+
body: isBodyless ? undefined : request.body,
|
| 33 |
+
redirect: "follow",
|
| 34 |
+
});
|
| 35 |
+
|
| 36 |
+
return new Response(resp.body, {
|
| 37 |
+
status: resp.status,
|
| 38 |
+
headers: {
|
| 39 |
+
"content-type": resp.headers.get("content-type") || "application/json",
|
| 40 |
+
"access-control-allow-origin": "*",
|
| 41 |
+
},
|
| 42 |
+
});
|
| 43 |
+
},
|
| 44 |
+
};
|
entrypoint.sh
CHANGED
|
@@ -15,6 +15,12 @@ echo "==> [3/5] Starting status/health page on 0.0.0.0:7860 ..."
|
|
| 15 |
python3 health_server.py &
|
| 16 |
|
| 17 |
echo "==> [4/5] Starting LiteLLM router on 127.0.0.1:4000 ..."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
litellm --config "${LITELLM_CONFIG_OUT:-litellm.generated.yaml}" \
|
| 19 |
--host 127.0.0.1 --port 4000 > /tmp/litellm.log 2>&1 &
|
| 20 |
|
|
|
|
| 15 |
python3 health_server.py &
|
| 16 |
|
| 17 |
echo "==> [4/5] Starting LiteLLM router on 127.0.0.1:4000 ..."
|
| 18 |
+
# A stale DATABASE_URL (from other projects) makes LiteLLM attempt a Prisma DB
|
| 19 |
+
# connect at boot and DIE. We run config-only, so drop it unless opted in.
|
| 20 |
+
if [ "${LITELLM_USE_DB:-0}" != "1" ] && [ -n "${DATABASE_URL:-}" ]; then
|
| 21 |
+
echo " (ignoring env DATABASE_URL — router runs without a database)"
|
| 22 |
+
unset DATABASE_URL
|
| 23 |
+
fi
|
| 24 |
litellm --config "${LITELLM_CONFIG_OUT:-litellm.generated.yaml}" \
|
| 25 |
--host 127.0.0.1 --port 4000 > /tmp/litellm.log 2>&1 &
|
| 26 |
|
gen_hermes_config.py
CHANGED
|
@@ -50,8 +50,22 @@ stt:
|
|
| 50 |
enabled: true # local faster-whisper by default; set GROQ_API_KEY for groq whisper
|
| 51 |
"""
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
with open(os.path.join(HERMES_HOME, "config.yaml"), "w") as f:
|
| 54 |
-
f.write(config_yaml)
|
| 55 |
|
| 56 |
# .env — Hermes' documented manual Telegram configuration
|
| 57 |
env_lines = [
|
|
|
|
| 50 |
enabled: true # local faster-whisper by default; set GROQ_API_KEY for groq whisper
|
| 51 |
"""
|
| 52 |
|
| 53 |
+
relay = os.environ.get("TELEGRAM_RELAY_URL", "").strip().rstrip("/")
|
| 54 |
+
relay_block = ""
|
| 55 |
+
if relay:
|
| 56 |
+
# api.telegram.org is blocked from HF Spaces egress -> route the Bot API
|
| 57 |
+
# through the user's own free Cloudflare Worker relay instead.
|
| 58 |
+
relay_block = f"""
|
| 59 |
+
platforms:
|
| 60 |
+
telegram:
|
| 61 |
+
extra:
|
| 62 |
+
base_url: "{relay}/bot"
|
| 63 |
+
base_file_url: "{relay}/file/bot"
|
| 64 |
+
"""
|
| 65 |
+
print(f"==> Telegram relay enabled via {relay}", file=sys.stderr)
|
| 66 |
+
|
| 67 |
with open(os.path.join(HERMES_HOME, "config.yaml"), "w") as f:
|
| 68 |
+
f.write(config_yaml + relay_block)
|
| 69 |
|
| 70 |
# .env — Hermes' documented manual Telegram configuration
|
| 71 |
env_lines = [
|