feat: add hermes gateway llm adapter

This commit is contained in:
0Xiao0
2026-06-01 16:50:00 +08:00
parent 7efd9eba98
commit e7529dc47b
4 changed files with 742 additions and 31 deletions

View File

@ -9,6 +9,7 @@ from dataclasses import dataclass
from pathlib import Path
from dotenv import load_dotenv
from hermes_gateway import GatewaySessionState, HermesGatewayLLM
from memory import MemoryRecallClient
from tts import BlackboxTTS
@ -638,12 +639,20 @@ async def entrypoint(ctx: JobContext) -> None:
LLM_BASE_URL = os.getenv("CUSTOM_LLM_BASE_URL")
LLM_MODEL = os.getenv("CUSTOM_LLM_MODEL", "qwen-max")
LLM_API_KEY = os.getenv("CUSTOM_LLM_API_KEY")
LLM_PROVIDER = os.getenv("CUSTOM_LLM_PROVIDER", "openai").strip().lower()
TEXT_LLM_MODEL = os.getenv("CUSTOM_TEXT_LLM_MODEL", LLM_MODEL)
VISION_LLM_MODEL = os.getenv("CUSTOM_VISION_LLM_MODEL", LLM_MODEL)
INPUT_MODE = _normalize_input_mode(os.getenv("CUSTOM_AGENT_INPUT_MODE"))
if not LLM_API_KEY:
if LLM_PROVIDER not in {"openai", "openai-compatible", "hermes", "hermes_gateway", "openclaw"}:
raise RuntimeError(f"Unsupported CUSTOM_LLM_PROVIDER={LLM_PROVIDER!r}")
if LLM_PROVIDER in {"openai", "openai-compatible"} and not LLM_API_KEY:
raise RuntimeError(f"CUSTOM_LLM_API_KEY is not set in {CUSTOM_ENV_PATH}")
logger.info("Using LLM model=%s base_url=%s", LLM_MODEL, LLM_BASE_URL or "OpenAI default")
logger.info(
"Using LLM provider=%s model=%s base_url=%s",
LLM_PROVIDER,
LLM_MODEL,
LLM_BASE_URL or "OpenAI default",
)
TTS_URL = os.getenv("CUSTOM_TTS_URL") or os.getenv(
"VOXCPM_TTS_URL", "http://localhost:5000/tts-blackbox"
@ -668,38 +677,75 @@ async def entrypoint(ctx: JobContext) -> None:
)
stt_stream = stt.StreamAdapter(stt=blackbox_stt, vad=ctx.proc.userdata["vad"])
import httpx
from openai import AsyncClient as OpenAIAsyncClient
if LLM_PROVIDER in {"hermes", "hermes_gateway", "openclaw"}:
gateway_url = os.getenv("CUSTOM_HERMES_GATEWAY_URL", "").strip()
if not gateway_url:
raise RuntimeError(f"CUSTOM_HERMES_GATEWAY_URL is not set in {CUSTOM_ENV_PATH}")
# OpenAI-compatible endpoints can be used by setting CUSTOM_LLM_BASE_URL.
http_client = httpx.AsyncClient(verify=_env_bool("CUSTOM_LLM_VERIFY_SSL", False))
if LLM_BASE_URL:
openai_client = OpenAIAsyncClient(
api_key=LLM_API_KEY,
base_url=LLM_BASE_URL,
http_client=http_client,
hermes_agent_id = os.getenv("CUSTOM_HERMES_AGENT_ID") or None
hermes_session_mode = os.getenv("CUSTOM_HERMES_SESSION_MODE", "per_room").strip().lower()
if hermes_session_mode != "per_room":
raise RuntimeError("CUSTOM_HERMES_SESSION_MODE must be per_room")
hermes_token = (
os.getenv("CUSTOM_HERMES_API_KEY")
or os.getenv("CUSTOM_HERMES_TOKEN")
or LLM_API_KEY
or None
)
hermes_state = GatewaySessionState(
room_name=ctx.room.name,
agent_id=hermes_agent_id,
session_mode=hermes_session_mode,
)
base_llm = HermesGatewayLLM(
url=gateway_url,
token=hermes_token,
state=hermes_state,
agent_id=hermes_agent_id,
model_name=os.getenv("CUSTOM_HERMES_MODEL", "hermes-agent"),
request_timeout=_env_float("CUSTOM_HERMES_REQUEST_TIMEOUT", 30.0),
)
text_llm = base_llm
vision_llm = base_llm
logger.info(
"Using Hermes/OpenClaw gateway url=%s agent_id=%s session_key=%s",
gateway_url,
hermes_agent_id or "default",
hermes_state.session_key,
)
else:
openai_client = OpenAIAsyncClient(
api_key=LLM_API_KEY,
http_client=http_client,
)
import httpx
from openai import AsyncClient as OpenAIAsyncClient
base_llm = openai.LLM(
model=LLM_MODEL,
client=openai_client,
)
text_llm = (
openai.LLM(model=TEXT_LLM_MODEL, client=openai_client)
if TEXT_LLM_MODEL != LLM_MODEL
else base_llm
)
vision_llm = (
openai.LLM(model=VISION_LLM_MODEL, client=openai_client)
if VISION_LLM_MODEL != LLM_MODEL
else base_llm
)
# OpenAI-compatible endpoints can be used by setting CUSTOM_LLM_BASE_URL.
http_client = httpx.AsyncClient(verify=_env_bool("CUSTOM_LLM_VERIFY_SSL", False))
if LLM_BASE_URL:
openai_client = OpenAIAsyncClient(
api_key=LLM_API_KEY,
base_url=LLM_BASE_URL,
http_client=http_client,
)
else:
openai_client = OpenAIAsyncClient(
api_key=LLM_API_KEY,
http_client=http_client,
)
base_llm = openai.LLM(
model=LLM_MODEL,
client=openai_client,
)
text_llm = (
openai.LLM(model=TEXT_LLM_MODEL, client=openai_client)
if TEXT_LLM_MODEL != LLM_MODEL
else base_llm
)
vision_llm = (
openai.LLM(model=VISION_LLM_MODEL, client=openai_client)
if VISION_LLM_MODEL != LLM_MODEL
else base_llm
)
vision_store = VisionFrameStore(
max_age_seconds=_env_float("CUSTOM_VISION_FRAME_MAX_AGE_SECONDS", 8.0)
)
@ -707,7 +753,7 @@ async def entrypoint(ctx: JobContext) -> None:
session: AgentSession = AgentSession(
# 1. Custom ASR blackbox with StreamAdapter
stt=stt_stream,
# 2. OpenAI-compatible LLM, e.g. MiniMax, Qwen, or OpenAI.
# 2. LLM backend, OpenAI-compatible or Hermes/OpenClaw gateway.
llm=base_llm,
# 3. TTS blackbox
tts=BlackboxTTS(