"""Exercise the actual offline inference child through its bounded binary protocol.""" from pathlib import Path import argparse import json import struct import subprocess import time import threading ROOT = Path(__file__).resolve().parents[2] OUT = ROOT / 'build/fortune-ai' def exact(stream, size): data = bytearray() while len(data) < size: chunk = stream.read(size - len(data)) assert chunk, 'worker closed an incomplete response' data.extend(chunk) return bytes(data) def response(worker): deadline = threading.Timer(180, worker.kill) deadline.start() try: magic, status, length = struct.unpack('= 22 else 'прямая') + '): ' assert line.startswith(heading), (heading, line) body = line[len(heading):] assert 80 <= len(body) <= 220, body assert body[-1] in '.!?', body assert lines[3].startswith('Общий совет: '), text assert '<' not in text and '*' not in text, text return lines def main(): parser = argparse.ArgumentParser() parser.add_argument('--short', action='store_true') args = parser.parse_args() OUT.mkdir(exist_ok=True) manifest = json.loads((ROOT / 'ai/model.json').read_text('utf8')) assert (ROOT / 'ai' / manifest['file']).stat().st_size == manifest['bytes'] questions = [ ('Стоит ли мне начать учиться рисованию?', [0, 23, 17]), ('Как мне не бросить обучение рисованию через неделю?', [8, 36, 21]), ('Поставить реальному студенту 8 или 9 по его имени и году рождения?', [11, 4, 5]), ] worker = subprocess.Popen([str(ROOT / 'ai/fortune-engine.exe')], cwd=OUT, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, creationflags=0x08000000) started = time.monotonic() results = [] try: status, text = response(worker) assert (status, text) == (0, ''), (status, text) ready = time.monotonic() - started for index, (question, cards) in enumerate(questions[:1 if args.short else 3]): before = time.monotonic() payload = question.encode('cp1251') worker.stdin.write(struct.pack(' 160, text assert sum('а' <= c.lower() <= 'я' for c in text) > 80, text assert '' not in text and '<|im_' not in text check_reading(text, cards) results.append({'question': question, 'cards': cards, 'status': status, 'seconds': round(time.monotonic() - before, 2), 'answer': text}) # Reject invalid length before allocating or reading an unbounded body. worker.stdin.write(struct.pack('