feat: connect to beaver
This commit is contained in:
35
.env.example
35
.env.example
@ -5,17 +5,10 @@ LIVEKIT_API_SECRET=
|
||||
CUSTOM_AGENT_NAME=my-agent
|
||||
|
||||
# Beaver terminal text WebSocket
|
||||
BEAVER_WS_URL=ws://127.0.0.1:8080/api/channels/terminal-dev/ws
|
||||
BEAVER_WS_URL=ws://terminaltest.1localhost.nip.io:8088/api/channels/terminal-dev/ws
|
||||
TERMINAL_PEER_ID=device-001
|
||||
TERMINAL_DEVICE_NAME=desk-terminal
|
||||
|
||||
# Beaver as a LiveKit LLM backend: asr -> beaver -> tts.
|
||||
# CUSTOM_LLM_PROVIDER=beaver
|
||||
# CUSTOM_BEAVER_WS_URL=ws://127.0.0.1:8080/api/channels/terminal-dev/ws
|
||||
# CUSTOM_BEAVER_PEER_ID=livekit-agent-001
|
||||
# CUSTOM_BEAVER_DEVICE_NAME=livekit-custom-agent
|
||||
# CUSTOM_BEAVER_MODEL=beaver-terminal
|
||||
|
||||
# ASR blackbox
|
||||
CUSTOM_ASR_URL=http://localhost:5000/asr-blackbox
|
||||
CUSTOM_ASR_MODEL=qwen
|
||||
@ -26,35 +19,27 @@ CUSTOM_ASR_ITN=
|
||||
CUSTOM_ASR_CHUNK_MODE=
|
||||
|
||||
# LLM backend: openai/openai-compatible or hermes_gateway/openclaw.
|
||||
CUSTOM_LLM_PROVIDER=openai
|
||||
CUSTOM_LLM_PROVIDER=beaver
|
||||
CUSTOM_BEAVER_WARMUP_TEXT=初始化连接,请简短回复 ready
|
||||
|
||||
# OpenAI-compatible LLM
|
||||
# CUSTOM_LLM_BASE_URL=https://oai.bwgdi.com/v1
|
||||
# CUSTOM_LLM_MODEL=Qwen3.6-35B
|
||||
# CUSTOM_LLM_API_KEY=
|
||||
# CUSTOM_LLM_API_KEY=sk-
|
||||
# CUSTOM_LLM_VERIFY_SSL=false
|
||||
|
||||
CUSTOM_LLM_BASE_URL=http://localhost/v1
|
||||
CUSTOM_LLM_MODEL=Qwen-VL
|
||||
CUSTOM_LLM_API_KEY=
|
||||
CUSTOM_LLM_BASE_URL=http:/localhost/v1
|
||||
CUSTOM_LLM_MODEL=Mistral-Medium-3.5-128B
|
||||
CUSTOM_LLM_API_KEY=sk-
|
||||
CUSTOM_LLM_VERIFY_SSL=false
|
||||
CUSTOM_SAVE_MODEL_IMAGES=false
|
||||
CUSTOM_SAVE_MODEL_IMAGES=true
|
||||
|
||||
# CUSTOM_TEXT_LLM_MODEL=
|
||||
# CUSTOM_VISION_LLM_MODEL=
|
||||
|
||||
# Hermes Agent via OpenClaw Gateway WebSocket, one Gateway session per LiveKit room.
|
||||
# CUSTOM_LLM_PROVIDER=hermes_gateway
|
||||
# CUSTOM_HERMES_GATEWAY_URL=ws://localhost:1977/ws
|
||||
# CUSTOM_HERMES_AGENT_ID=
|
||||
# CUSTOM_HERMES_API_KEY=
|
||||
# CUSTOM_HERMES_SESSION_MODE=per_room
|
||||
# CUSTOM_HERMES_MODEL=hermes-agent
|
||||
# CUSTOM_HERMES_REQUEST_TIMEOUT=30
|
||||
|
||||
# CUSTOM_LLM_BASE_URL=https://api.deepseek.com
|
||||
# CUSTOM_LLM_MODEL=deepseek-v4-flash
|
||||
# CUSTOM_LLM_API_KEY=
|
||||
# CUSTOM_LLM_API_KEY=sk-
|
||||
# CUSTOM_LLM_VERIFY_SSL=false
|
||||
|
||||
|
||||
@ -95,4 +80,4 @@ CUSTOM_MEMORY_URL=http://localhost:8766/api/room_graph
|
||||
CUSTOM_MEMORY_TIMEOUT=2
|
||||
CUSTOM_MEMORY_MAX_CHARS=2000
|
||||
CUSTOM_MEMORY_API_KEY=
|
||||
CUSTOM_PREEMPTIVE_GENERATION=true
|
||||
CUSTOM_PREEMPTIVE_GENERATION=false
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from beaver_terminal_client import BeaverTerminalClient
|
||||
try:
|
||||
from beaver_terminal_client import BeaverTerminalClient
|
||||
except ModuleNotFoundError:
|
||||
from custom.beaver_terminal_client import BeaverTerminalClient
|
||||
from livekit.agents import llm
|
||||
from livekit.agents.types import (
|
||||
DEFAULT_API_CONNECT_OPTIONS,
|
||||
@ -14,6 +18,9 @@ from livekit.agents.types import (
|
||||
)
|
||||
from livekit.agents.utils import shortuuid
|
||||
|
||||
logger = logging.getLogger("beaver-llm")
|
||||
|
||||
|
||||
def latest_user_text(chat_ctx: llm.ChatContext) -> str:
|
||||
for message in reversed(chat_ctx.messages()):
|
||||
if message.role != "user":
|
||||
@ -49,6 +56,27 @@ class BeaverLLM(llm.LLM):
|
||||
def provider(self) -> str:
|
||||
return "beaver"
|
||||
|
||||
@property
|
||||
def session_id(self) -> str | None:
|
||||
return self._client.session_id
|
||||
|
||||
async def connect(self, *, warmup_text: str | None = None) -> str | None:
|
||||
warmup_reply: str | None = None
|
||||
async with self._lock:
|
||||
await self._client.connect()
|
||||
if warmup_text and warmup_text.strip():
|
||||
warmup_reply = await self._client.send_text(warmup_text.strip())
|
||||
|
||||
if warmup_reply is None:
|
||||
logger.info("Beaver handshake completed session_id=%s", self.session_id)
|
||||
else:
|
||||
logger.info(
|
||||
"Beaver handshake warmup completed session_id=%s reply_len=%s",
|
||||
self.session_id,
|
||||
len(warmup_reply),
|
||||
)
|
||||
return warmup_reply
|
||||
|
||||
def chat(
|
||||
self,
|
||||
*,
|
||||
|
||||
@ -690,10 +690,7 @@ async def entrypoint(ctx: JobContext) -> None:
|
||||
if not beaver_url:
|
||||
raise RuntimeError(f"CUSTOM_BEAVER_WS_URL or BEAVER_WS_URL is not set in {CUSTOM_ENV_PATH}")
|
||||
|
||||
beaver_peer_id = (
|
||||
_first_env("CUSTOM_BEAVER_PEER_ID", "BEAVER_PEER_ID", "TERMINAL_PEER_ID")
|
||||
or f"livekit-{ctx.room.name}"
|
||||
)
|
||||
beaver_peer_id = _first_env("CUSTOM_BEAVER_PEER_ID", "BEAVER_PEER_ID") or f"livekit-{ctx.room.name}"
|
||||
beaver_device_name = (
|
||||
_first_env("CUSTOM_BEAVER_DEVICE_NAME", "BEAVER_DEVICE_NAME", "TERMINAL_DEVICE_NAME")
|
||||
or "livekit-custom-agent"
|
||||
@ -704,14 +701,19 @@ async def entrypoint(ctx: JobContext) -> None:
|
||||
device_name=beaver_device_name,
|
||||
model_name=os.getenv("CUSTOM_BEAVER_MODEL", "beaver-terminal"),
|
||||
)
|
||||
beaver_warmup_text = os.getenv("CUSTOM_BEAVER_WARMUP_TEXT")
|
||||
warmup_reply = await base_llm.connect(warmup_text=beaver_warmup_text)
|
||||
text_llm = base_llm
|
||||
vision_llm = base_llm
|
||||
logger.info(
|
||||
"Using Beaver gateway url=%s peer_id=%s device_name=%s room=%s",
|
||||
"Using Beaver gateway url=%s peer_id=%s device_name=%s room=%s session_id=%s warmup=%s warmup_reply_len=%s",
|
||||
beaver_url,
|
||||
beaver_peer_id,
|
||||
beaver_device_name,
|
||||
ctx.room.name,
|
||||
base_llm.session_id,
|
||||
bool(beaver_warmup_text and beaver_warmup_text.strip()),
|
||||
len(warmup_reply) if warmup_reply is not None else 0,
|
||||
)
|
||||
elif LLM_PROVIDER in {"hermes", "hermes_gateway", "openclaw"}:
|
||||
gateway_url = os.getenv("CUSTOM_HERMES_GATEWAY_URL", "").strip()
|
||||
@ -809,7 +811,7 @@ async def entrypoint(ctx: JobContext) -> None:
|
||||
"false_interruption_timeout": 1.0,
|
||||
},
|
||||
),
|
||||
preemptive_generation=_env_bool("CUSTOM_PREEMPTIVE_GENERATION", True),
|
||||
preemptive_generation=_env_bool("CUSTOM_PREEMPTIVE_GENERATION", LLM_PROVIDER != "beaver"),
|
||||
aec_warmup_duration=3.0,
|
||||
tts_text_transforms=[
|
||||
"filter_emoji",
|
||||
|
||||
Reference in New Issue
Block a user