Rebuild & Deploy / rebuild (push) Successful in 3m18s
- Point the agent at OpenWebUI's OpenAI-compatible API (LLM_BASE_URL,
LLM_API_KEY, LLM_MODEL) with GEMMA_* env fallbacks
- Add list_available_models() fetching {base_url}/models, filtering
non-chat ids (embedding/reranker)
- Rename GemmaLLM -> HopeLLM with update_options(model=...) for runtime
model switching (mutates _opts.model) and instance-based compaction
- Data channel: agent publishes {type: models} after connect and on
get_models; set_model switches live and acks model_changed
- Web UI: Brain section in the settings sheet with a model picker that
persists the choice in localStorage
- Update tests, CI workflow env, and docs for the new endpoint
95 lines
3.2 KiB
Python
95 lines
3.2 KiB
Python
"""Tests for HopeLLM context compaction (agent.HopeLLM._compact_context).
|
|
|
|
Compaction runs a real side LLM call to the configured endpoint, so these
|
|
are marked slow. The agent module is imported inside the container via
|
|
docker exec.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from conftest import LLM_BASE_URL, LLM_MODEL, docker_exec_python
|
|
|
|
# {num_messages} and {do_chat} are filled per test; all other braces are literal.
|
|
TEMPLATE = '''
|
|
import logging, os, sys
|
|
from livekit.agents import llm as lk_llm
|
|
sys.path.insert(0, "/opt/voice-agent")
|
|
from agent import HopeLLM
|
|
|
|
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
|
|
|
|
llm = HopeLLM(
|
|
model="{model}",
|
|
base_url="{base_url}",
|
|
api_key=os.environ.get("LLM_API_KEY", "not-needed"),
|
|
max_completion_tokens=100,
|
|
extra_body={{"chat_template_kwargs": {{"enable_thinking": False}}}},
|
|
)
|
|
|
|
items = [lk_llm.ChatMessage(role="system", content=["You are Hope, a voice assistant."])]
|
|
for i in range({num_messages}):
|
|
items.append(lk_llm.ChatMessage(role="user", content=["user message number " + str(i)]))
|
|
items.append(lk_llm.ChatMessage(role="assistant", content=["agent reply number " + str(i)]))
|
|
|
|
ctx = lk_llm.ChatContext(items)
|
|
print("BUILT ctx with " + str(len(ctx.items)) + " items")
|
|
|
|
import asyncio
|
|
|
|
async def main():
|
|
try:
|
|
compacted = llm._compact_context(ctx, None)
|
|
except Exception as e:
|
|
print("COMPACT_ERROR " + repr(e))
|
|
return
|
|
print("AFTER items=" + str(len(compacted.items)))
|
|
for it in compacted.items:
|
|
role = getattr(it, "role", None)
|
|
content = HopeLLM._msg_content_text(it)
|
|
print("ITEM " + str(role) + ": " + content[:120])
|
|
|
|
{do_chat}
|
|
|
|
asyncio.run(main())
|
|
'''
|
|
|
|
CHAT_SNIPPET = ' stream = llm.chat(chat_ctx=compacted)\n collected = await stream.collect()\n print("CHAT_OK text_len=" + str(len(collected.text)))'
|
|
|
|
|
|
def _run(num_messages: int, *, do_chat: bool = False) -> str:
|
|
code = TEMPLATE.format(
|
|
model=LLM_MODEL,
|
|
base_url=LLM_BASE_URL,
|
|
num_messages=num_messages,
|
|
do_chat=CHAT_SNIPPET if do_chat else "pass",
|
|
)
|
|
proc = docker_exec_python(code, timeout=120, check=False)
|
|
return proc.stdout + (("\n" + proc.stderr) if proc.stderr else "")
|
|
|
|
|
|
@pytest.mark.slow
|
|
def test_compaction_triggers_on_size():
|
|
"""A context with > 24 items must compact without crashing and chat() must still work."""
|
|
# 13 pairs = 26 non-system + 1 system = 27 items (> threshold of 24)
|
|
out = _run(13, do_chat=True)
|
|
assert "BUILT ctx with 27 items" in out
|
|
assert "Context compacted:" in out
|
|
assert "CHAT_OK text_len=" in out
|
|
|
|
|
|
@pytest.mark.slow
|
|
def test_compaction_preserves_system_prompt():
|
|
out = _run(28)
|
|
lines = [l for l in out.splitlines() if l.startswith("ITEM system:")]
|
|
assert lines, f"no system message survived compaction:\n{out}"
|
|
assert "You are Hope" in lines[0]
|
|
|
|
|
|
def test_short_context_no_compaction():
|
|
"""Context with non_sys <= KEEP_RECENT must NOT be compacted."""
|
|
# system + 4 user/assistant pairs = 9 items, non_sys=8 <= KEEP_RECENT(10)
|
|
out = _run(4)
|
|
assert "BUILT ctx with 9 items" in out
|
|
# _compact_context should return the context unchanged (early return)
|
|
assert "AFTER items=9" in out, f"expected no compaction:\n{out}"
|