hermes-agent/tests/gateway
Teknium dd60bcbfb7
feat: OpenAI-compatible API server + WhatsApp configurable reply prefix (#1756)
* feat: OpenAI-compatible API server platform adapter

Salvaged from PR #956, updated for current main.

Adds an HTTP API server as a gateway platform adapter that exposes
hermes-agent via the OpenAI Chat Completions and Responses APIs.
Any OpenAI-compatible frontend (Open WebUI, LobeChat, LibreChat,
AnythingLLM, NextChat, ChatBox, etc.) can connect by pointing at
http://localhost:8642/v1.

Endpoints:
- POST /v1/chat/completions  — stateless Chat Completions API
- POST /v1/responses         — stateful Responses API with chaining
- GET  /v1/responses/{id}    — retrieve stored response
- DELETE /v1/responses/{id}  — delete stored response
- GET  /v1/models            — list hermes-agent as available model
- GET  /health               — health check

Features:
- Real SSE streaming via stream_delta_callback (uses main's streaming)
- In-memory LRU response store for Responses API conversation chaining
- Named conversations via 'conversation' parameter
- Bearer token auth (optional, via API_SERVER_KEY)
- CORS support for browser-based frontends
- System prompt layering (frontend system messages on top of core)
- Real token usage tracking in responses

Integration points:
- Platform.API_SERVER in gateway/config.py
- _create_adapter() branch in gateway/run.py
- API_SERVER_* env vars in hermes_cli/config.py
- Env var overrides in gateway/config.py _apply_env_overrides()

Changes vs original PR #956:
- Removed streaming infrastructure (already on main via stream_consumer.py)
- Removed Telegram reply_to_mode (separate feature, not included)
- Updated _resolve_model() -> _resolve_gateway_model()
- Updated stream_callback -> stream_delta_callback
- Updated connect()/disconnect() to use _mark_connected()/_mark_disconnected()
- Adapted to current Platform enum (includes MATTERMOST, MATRIX, DINGTALK)

Tests: 72 new tests, all passing
Docs: API server guide, Open WebUI integration guide, env var reference

* feat(whatsapp): make reply prefix configurable via config.yaml

Reworked from PR #1764 (ifrederico) to use config.yaml instead of .env.

The WhatsApp bridge prepends a header to every outgoing message.
This was hardcoded to '⚕ *Hermes Agent*'. Users can now customize
or disable it via config.yaml:

  whatsapp:
    reply_prefix: ''                     # disable header
    reply_prefix: '🤖 *My Bot*\n───\n'  # custom prefix

How it works:
- load_gateway_config() reads whatsapp.reply_prefix from config.yaml
  and stores it in PlatformConfig.extra['reply_prefix']
- WhatsAppAdapter reads it from config.extra at init
- When spawning bridge.js, the adapter passes it as
  WHATSAPP_REPLY_PREFIX in the subprocess environment
- bridge.js handles undefined (default), empty (no header),
  or custom values with \\n escape support
- Self-chat echo suppression uses the configured prefix

Also fixes _config_version: was 9 but ENV_VARS_BY_VERSION had a
key 10 (TAVILY_API_KEY), so existing users at v9 would never be
prompted for Tavily. Bumped to 10 to close the gap. Added a
regression test to prevent this from happening again.

Credit: ifrederico (PR #1764) for the bridge.js implementation
and the config version gap discovery.

---------

Co-authored-by: Test <test@test.com>
2026-03-17 10:44:37 -07:00
..
__init__.py test: reorganize test structure and add missing unit tests 2026-02-26 03:20:08 +03:00
test_api_server.py feat: OpenAI-compatible API server + WhatsApp configurable reply prefix (#1756) 2026-03-17 10:44:37 -07:00
test_async_memory_flush.py feat(gateway): proactive async memory flush on session expiry 2026-03-07 11:27:50 -08:00
test_background_command.py refactor: centralize slash command registry (#1603) 2026-03-16 23:21:03 -07:00
test_background_process_notifications.py fix(gateway): persist watcher metadata in checkpoint for crash recovery (#1706) 2026-03-17 03:52:15 -07:00
test_base_topic_sessions.py fix(gateway): isolate telegram forum topic sessions 2026-03-11 09:15:34 +01:00
test_channel_directory.py fix: finish HERMES_HOME path cleanup 2026-03-13 21:35:07 -07:00
test_config.py Merge pull request #1495 from NousResearch/fix/814-group-session-isolation 2026-03-16 00:25:43 -07:00
test_config_cwd_bridge.py refactor(cli): implement approval locking mechanism to serialize concurrent requests 2026-03-13 23:59:18 -07:00
test_delivery.py fix: salvage gateway dedup and executor cleanup from PR #993 2026-03-14 11:03:20 -07:00
test_dingtalk.py feat(gateway): add DingTalk platform adapter (#1685) 2026-03-17 03:04:58 -07:00
test_discord_bot_filter.py feat(discord): add DISCORD_ALLOW_BOTS config for bot message filtering (inspired by openclaw) 2026-03-09 02:20:57 -07:00
test_discord_free_response.py feat(discord): auto-thread on @mention + skip mention in bot threads 2026-03-15 07:59:55 -07:00
test_discord_imports.py fix: defer discord adapter annotations 2026-03-14 09:32:05 -07:00
test_discord_media_metadata.py feat(discord): add /thread command, auto_thread config, and media metadata fix (#1178) 2026-03-13 08:52:54 -07:00
test_discord_opus.py fix: add macOS Homebrew Opus fallback and fix shutdown dict iteration 2026-03-14 14:27:21 +03:00
test_discord_send.py fix: align salvaged Discord send test mock with current slash-command API 2026-03-14 21:44:50 -07:00
test_discord_slash_commands.py feat(discord): auto-thread on @mention + skip mention in bot threads 2026-03-15 07:59:55 -07:00
test_discord_thread_persistence.py fix(discord): persist thread participation across gateway restarts 2026-03-17 02:26:34 -07:00
test_document_cache.py fix(security): patch path traversal, size bypass, and prompt injection in document processing 2026-02-27 11:53:46 -05:00
test_email.py fix: verify SMTP TLS in send_message_tool 2026-03-14 06:31:52 -07:00
test_extract_local_files.py feat: auto-detect local file paths in gateway responses for native media delivery (#1640) 2026-03-17 01:47:34 -07:00
test_gateway_shutdown.py fix(gateway): cancel active runs during shutdown 2026-03-15 04:21:50 -07:00
test_homeassistant.py fix: Home Assistant event filtering now closed by default (#1169) 2026-03-13 07:40:38 -07:00
test_honcho_lifecycle.py fix(honcho): isolate session routing for multi-user gateway (#1500) 2026-03-16 00:23:47 -07:00
test_hooks.py test: strengthen assertions across 7 test files (batch 1) 2026-03-05 18:39:37 -08:00
test_interrupt_key_match.py test(gateway): cover photo burst interrupt regressions 2026-03-15 03:50:45 -07:00
test_matrix.py test: add comprehensive tests for Mattermost and Matrix adapters 2026-03-17 03:18:16 -07:00
test_mattermost.py test: add comprehensive tests for Mattermost and Matrix adapters 2026-03-17 03:18:16 -07:00
test_media_extraction.py fix(gateway): prevent TTS voice messages from accumulating across turns 2026-02-28 03:38:27 -05:00
test_mirror.py fix(gateway): isolate telegram forum topic sessions 2026-03-11 09:15:34 +01:00
test_pairing.py test: strengthen assertions across 7 test files (batch 1) 2026-03-05 18:39:37 -08:00
test_pii_redaction.py fix(privacy): skip PII redaction on Discord/Slack (mentions need real IDs) 2026-03-16 05:58:34 -07:00
test_plan_command.py fix: save /plan output in workspace (#1381) 2026-03-14 21:28:51 -07:00
test_platform_base.py fix: complete send_message MEDIA delivery salvage 2026-03-14 04:02:03 -07:00
test_reasoning_command.py feat(gateway): add reasoning hot reload 2026-03-14 02:42:47 -07:00
test_resume_command.py fix(honcho): isolate session routing for multi-user gateway (#1500) 2026-03-16 00:23:47 -07:00
test_retry_replacement.py test: lock retry replacement semantics 2026-03-14 21:19:22 -07:00
test_retry_response.py test(gateway): add regression test for /retry response fix 2026-03-10 23:34:52 -07:00
test_run_progress_topics.py fix(test): add missing _voice_mode attr to GatewayRunner test stubs 2026-03-14 14:27:20 +03:00
test_runner_fatal_adapter.py fix(gateway): harden Telegram polling conflict handling 2026-03-14 12:11:23 -07:00
test_runner_startup_failures.py fix(gateway): restart on retryable startup failures (#1517) 2026-03-16 05:26:31 -07:00
test_send_image_file.py fix(discord): preserve native document and video attachment support 2026-03-14 22:01:02 -07:00
test_session.py fix(session): skip corrupt lines in load_transcript instead of crashing (#1744) 2026-03-17 05:18:12 -07:00
test_session_env.py fix: preserve thread context for cronjob deliver=origin 2026-03-15 06:57:00 -07:00
test_session_hygiene.py fix(test): add missing _voice_mode attr to GatewayRunner test stubs 2026-03-14 14:27:20 +03:00
test_signal.py fix: Signal adapter parity pass — integration gaps, clawdbot features, env var simplification 2026-03-08 21:00:21 -07:00
test_slack.py test: fix gateway async test event loop usage 2026-03-14 02:52:47 -07:00
test_sms.py feat: add SMS (Twilio) platform adapter 2026-03-17 03:14:53 -07:00
test_ssl_certs.py fix(gateway): SSL certificate auto-detection for NixOS and non-standard systems 2026-03-15 23:04:34 -07:00
test_status.py test: add regression test for stale PID in gateway_state.json (#1631) 2026-03-17 01:35:02 -07:00
test_status_command.py feat: add route-aware pricing estimates (#1695) 2026-03-17 03:44:44 -07:00
test_sticker_cache.py test: add unit tests for 8 untested modules (batch 3) (#191) 2026-03-01 05:28:12 -08:00
test_stt_config.py fix: restore local STT fallback for gateway voice notes 2026-03-15 21:51:40 -07:00
test_telegram_conflict.py fix(gateway): restart on retryable startup failures (#1517) 2026-03-16 05:26:31 -07:00
test_telegram_documents.py test(gateway): cover photo burst interrupt regressions 2026-03-15 03:50:45 -07:00
test_telegram_format.py fix(telegram): escape chunk indicators in MarkdownV2 (#1478) 2026-03-15 19:27:15 -07:00
test_telegram_photo_interrupts.py test(gateway): cover photo burst interrupt regressions 2026-03-15 03:50:45 -07:00
test_telegram_text_batching.py fix(telegram): aggregate split text messages before dispatching (#1674) 2026-03-17 02:49:57 -07:00
test_title_command.py fix(test): add missing _voice_mode attr to GatewayRunner test stubs 2026-03-14 14:27:20 +03:00
test_transcript_offset.py fix(gateway): use filtered history length for transcript message extraction 2026-03-04 21:34:40 +03:00
test_update_command.py fix(gateway): fall back to sys.executable -m hermes_cli.main when hermes not on PATH 2026-03-14 12:15:51 -07:00
test_voice_command.py refactor: centralize slash command registry (#1603) 2026-03-16 23:21:03 -07:00
test_whatsapp_connect.py feat: OpenAI-compatible API server + WhatsApp configurable reply prefix (#1756) 2026-03-17 10:44:37 -07:00
test_whatsapp_reply_prefix.py feat: OpenAI-compatible API server + WhatsApp configurable reply prefix (#1756) 2026-03-17 10:44:37 -07:00