Files
fabolous005 a67238f66b prain: personas for the chats; README on quantization levels and personas
prain web/tui --persona FILE sends the file as the system message ahead of every warm-up and
answer; the page shows its name. Three examples in examples/personas (task, how to answer,
environment, how to use it). A persona costs 2.0-3.2 s once (241-467 tokens), then at most
0.3 s per conversation from the prefix cache.

README rewritten around launching: quick start, the vLLM flags, a quantization table measured
today at equal expert VRAM (bf16 0.90 / int8 2.19 / int4 5.19 tok/s; 2.3k-token prompt 69.5 /
32.3 / 20.5 s; 4 of 6 int8 and 0 of 6 int4 answers identical to bf16, one int4 answer wrong),
how to switch formats, the VRAM budget, writing personas, the chats and the monitor.
2026-10-29 23:55:36 +01:00

82 lines
3.3 KiB
Python

import fcntl
import os
import pty
import struct
import subprocess
import sys
import termios
import threading
import unittest
from prain.tui import Chat
from tests.helpers import StubVllm, wait_for
class Tui(unittest.TestCase):
def setUp(self):
self.stub = StubVllm()
self.addCleanup(self.stub.close)
def test_typing_warms_up_and_enter_streams_the_answer(self):
chat = Chat(self.stub.url, "stub-model")
chat.type("Why is the sky")
wait_for(lambda: self.stub.bodies and not chat.warmer.busy)
self.assertEqual(self.stub.bodies[0]["priority"], 1)
chat.send()
chat.type("next question") # an answer is streaming: no warm-up in "idle" mode
wait_for(lambda: chat.poll() or chat.reply is None)
self.assertEqual(chat.messages, [{"role": "user", "content": "Why is the sky"},
{"role": "assistant", "content": "Hello!"}])
self.assertEqual(len(self.stub.bodies), 2)
self.assertIn("tok/s", chat.status)
def test_a_persona_leads_every_request_but_stays_off_screen(self):
chat = Chat(self.stub.url, "stub-model", system="Be brief.")
chat.type("Why")
wait_for(lambda: self.stub.bodies and not chat.warmer.busy)
chat.send()
wait_for(lambda: chat.poll() or chat.reply is None)
system = {"role": "system", "content": "Be brief."}
self.assertEqual([b["messages"][0] for b in self.stub.bodies], [system, system])
self.assertEqual(chat.messages, [{"role": "user", "content": "Why"},
{"role": "assistant", "content": "Hello!"}])
def test_warmup_off_and_cancel_on_send(self):
off = Chat(self.stub.url, "stub-model", warmup="off")
off.type("hello")
self.assertFalse(off.warmer.busy)
self.stub.delay = 3.0
chat = Chat(self.stub.url, "stub-model")
chat.type("a slow warm-up")
wait_for(lambda: self.stub.bodies)
chat.send() # aborts the warm-up instead of queueing behind it
wait_for(lambda: not chat.warmer.busy, timeout=2.0)
def test_curses_screen_runs_in_a_real_terminal(self):
master, slave = pty.openpty()
fcntl.ioctl(slave, termios.TIOCSWINSZ, struct.pack("HHHH", 24, 80, 0, 0))
tui = subprocess.Popen([sys.executable, "-m", "prain.cli", "tui", "--url", self.stub.url],
stdin=slave, stdout=slave, stderr=slave,
env={**os.environ, "TERM": "xterm"}, start_new_session=True)
os.close(slave)
screen = bytearray()
threading.Thread(target=self._drain, args=(master, screen), daemon=True).start()
wait_for(lambda: b"stub-model" in screen) # status line drawn
os.write(master, b"hi\r")
wait_for(lambda: any(body.get("stream") and "priority" not in body
for body in self.stub.bodies))
wait_for(lambda: b"tok/s" in screen)
os.write(master, b"\x1b")
self.assertEqual(tui.wait(timeout=5), 0)
@staticmethod
def _drain(fd, screen):
while True:
try:
data = os.read(fd, 4096)
except OSError:
return
if not data:
return
screen.extend(data)