feat: add hermes gateway llm adapter
This commit is contained in:
108
custom_agent.py
108
custom_agent.py
@ -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(
|
||||
|
||||
Reference in New Issue
Block a user