feat: beaver first commit

This commit is contained in:
0Xiao0
2026-06-02 14:07:56 +08:00
parent 34cf1b9736
commit 0a50f25dfa
4 changed files with 41 additions and 22 deletions

View File

@ -4,7 +4,7 @@ import asyncio
from collections.abc import Sequence
from typing import Any
from custom.beaver_terminal_client import BeaverTerminalClient
from beaver_terminal_client import BeaverTerminalClient
from livekit.agents import llm
from livekit.agents.types import (
DEFAULT_API_CONNECT_OPTIONS,
@ -14,7 +14,6 @@ from livekit.agents.types import (
)
from livekit.agents.utils import shortuuid
def latest_user_text(chat_ctx: llm.ChatContext) -> str:
for message in reversed(chat_ctx.messages()):
if message.role != "user":

View File

@ -686,18 +686,16 @@ async def entrypoint(ctx: JobContext) -> None:
stt_stream = stt.StreamAdapter(stt=blackbox_stt, vad=ctx.proc.userdata["vad"])
if LLM_PROVIDER == "beaver":
beaver_url = os.getenv("CUSTOM_BEAVER_WS_URL") or os.getenv("BEAVER_WS_URL", "").strip()
beaver_url = _first_env("CUSTOM_BEAVER_WS_URL", "BEAVER_WS_URL")
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 = (
os.getenv("CUSTOM_BEAVER_PEER_ID")
or os.getenv("BEAVER_PEER_ID")
_first_env("CUSTOM_BEAVER_PEER_ID", "BEAVER_PEER_ID", "TERMINAL_PEER_ID")
or f"livekit-{ctx.room.name}"
)
beaver_device_name = (
os.getenv("CUSTOM_BEAVER_DEVICE_NAME")
or os.getenv("BEAVER_DEVICE_NAME")
_first_env("CUSTOM_BEAVER_DEVICE_NAME", "BEAVER_DEVICE_NAME", "TERMINAL_DEVICE_NAME")
or "livekit-custom-agent"
)
base_llm = BeaverLLM(
@ -709,10 +707,11 @@ async def entrypoint(ctx: JobContext) -> None:
text_llm = base_llm
vision_llm = base_llm
logger.info(
"Using Beaver gateway url=%s peer_id=%s device_name=%s",
"Using Beaver gateway url=%s peer_id=%s device_name=%s room=%s",
beaver_url,
beaver_peer_id,
beaver_device_name,
ctx.room.name,
)
elif LLM_PROVIDER in {"hermes", "hermes_gateway", "openclaw"}:
gateway_url = os.getenv("CUSTOM_HERMES_GATEWAY_URL", "").strip()
@ -1000,6 +999,14 @@ def _env_bool(name: str, default: bool) -> bool:
return default
def _first_env(*names: str) -> str | None:
for name in names:
value = os.getenv(name)
if value and value.strip():
return value.strip()
return None
def _recording_options_from_env() -> RecordingOptions:
return RecordingOptions(
audio=_env_bool("CUSTOM_RECORD_AUDIO", False),

View File

@ -3,7 +3,10 @@ import json
import aiohttp
from aiohttp import web
from custom.beaver_llm import BeaverLLM, latest_user_text
try:
from custom.beaver_llm import BeaverLLM, latest_user_text
except ModuleNotFoundError:
from beaver_llm import BeaverLLM, latest_user_text
from livekit.agents import ChatContext
@ -83,16 +86,13 @@ async def test_beaver_llm_sends_latest_user_text_and_returns_reply(
await runner.cleanup()
assert collected.text == "beaver reply"
assert received == [
{
"type": "connect",
"peer_id": "livekit-room",
"device_name": "livekit-custom-agent",
"capabilities": ["text"],
},
{
"type": "message",
"message_id": "livekit-room-000001",
"text": "hello beaver",
},
]
assert received[0] == {
"type": "connect",
"peer_id": "livekit-room",
"device_name": "livekit-custom-agent",
"capabilities": ["text"],
}
assert received[1]["type"] == "message"
assert received[1]["message_id"].startswith("livekit-room-")
assert received[1]["message_id"].endswith("-000001")
assert received[1]["text"] == "hello beaver"

View File

@ -58,6 +58,13 @@ def test_message_id_generator_uses_monotonic_peer_counter() -> None:
assert generator.counter == 9
def test_message_id_generator_can_include_nonce() -> None:
generator = MessageIdGenerator(peer_id="device-001", nonce="run12345")
assert generator.next_id() == "device-001-run12345-000001"
assert generator.next_id() == "device-001-run12345-000002"
async def test_client_connects_sends_text_and_returns_assistant_reply(
unused_tcp_port: int,
) -> None:
@ -113,6 +120,7 @@ async def test_client_connects_sends_text_and_returns_assistant_reply(
url=f"http://127.0.0.1:{unused_tcp_port}/api/channels/terminal-dev/ws",
peer_id="device-001",
device_name="desk-terminal",
message_ids=MessageIdGenerator(peer_id="device-001"),
)
try:
@ -180,6 +188,7 @@ async def test_client_returns_cached_duplicate_reply(unused_tcp_port: int) -> No
url=f"http://127.0.0.1:{unused_tcp_port}/api/channels/terminal-dev/ws",
peer_id="device-001",
device_name="desk-terminal",
message_ids=MessageIdGenerator(peer_id="device-001"),
)
try:
@ -223,6 +232,7 @@ async def test_client_raises_on_error_frames(unused_tcp_port: int) -> None:
url=f"http://127.0.0.1:{unused_tcp_port}/api/channels/terminal-dev/ws",
peer_id="device-001",
device_name="desk-terminal",
message_ids=MessageIdGenerator(peer_id="device-001"),
)
try:
@ -284,6 +294,7 @@ async def test_client_treats_assistant_finish_reason_error_as_failed_turn(
url=f"http://127.0.0.1:{unused_tcp_port}/api/channels/terminal-dev/ws",
peer_id="device-001",
device_name="desk-terminal",
message_ids=MessageIdGenerator(peer_id="device-001"),
)
try:
@ -326,6 +337,7 @@ async def test_client_ping_sends_ping_and_waits_for_pong(unused_tcp_port: int) -
url=f"http://127.0.0.1:{unused_tcp_port}/api/channels/terminal-dev/ws",
peer_id="device-001",
device_name="desk-terminal",
message_ids=MessageIdGenerator(peer_id="device-001"),
)
try:
@ -398,6 +410,7 @@ async def test_client_reconnects_with_same_peer_id_when_socket_closes_before_sen
url=f"http://127.0.0.1:{unused_tcp_port}/api/channels/terminal-dev/ws",
peer_id="device-001",
device_name="desk-terminal",
message_ids=MessageIdGenerator(peer_id="device-001"),
)
try: