feat: improve library and audiobook workflow

This commit is contained in:
Курнат Андрей
2026-09-25 06:17:13 +03:00
parent a4d139009f
commit fadcf1da22
70 changed files with 2683 additions and 733 deletions
@@ -0,0 +1,82 @@
import asyncio
import json
import sys
import unittest
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'backend'))
from services.cmp_multigpu import warm_chapters
class DynamicQueueTests(unittest.IsolatedAsyncioTestCase):
async def test_free_gpu_continues_and_reports_before_slow_gpu_finishes(self):
slow = asyncio.Event()
dispatched = []
active = set()
class Response:
def raise_for_status(self): pass
def json(self): return {'duration_s': 1, 'cached': False}
class Client:
def __init__(self, **kwargs): pass
async def __aenter__(self): return self
async def __aexit__(self, *args): pass
async def post(self, url, json):
self_outer.assertNotIn(url, active)
active.add(url)
index = json['chapter_index']
dispatched.append((url, index))
try:
if index == 0:
await slow.wait()
else:
await asyncio.sleep(.001)
return Response()
finally:
active.remove(url)
self_outer = self
with patch('services.cmp_multigpu.httpx.AsyncClient', Client):
stream = warm_chapters('text', 6, ['gpu0', 'gpu1', 'gpu2'], {})
try:
first = await asyncio.wait_for(anext(stream), .3)
self.assertTrue(first.startswith('data:'))
self.assertFalse(slow.is_set())
while len(dispatched) < 6:
await asyncio.wait_for(anext(stream), .3)
self.assertTrue(any(index >= 3 and url != 'gpu0/audiobook/preview' for url, index in dispatched))
slow.set()
remaining = [event async for event in stream]
self.assertTrue(any('"index": 0' in event for event in remaining))
self.assertEqual(list(range(6)), sorted(index for _, index in dispatched))
finally:
slow.set()
await stream.aclose()
self.assertFalse(active)
async def test_closing_stream_cancels_inflight_requests(self):
stopped = asyncio.Event()
class Response:
def raise_for_status(self): pass
def json(self): return {'duration_s': 1}
class Client:
def __init__(self, **kwargs): pass
async def __aenter__(self): return self
async def __aexit__(self, *args): pass
async def post(self, url, json):
if json['chapter_index'] == 0:
try: await asyncio.Event().wait()
finally: stopped.set()
return Response()
with patch('services.cmp_multigpu.httpx.AsyncClient', Client):
stream = warm_chapters('text', 2, ['gpu0', 'gpu1'], {})
try:
await asyncio.wait_for(anext(stream), .3)
finally:
await stream.aclose()
self.assertTrue(stopped.is_set())
if __name__ == '__main__': unittest.main()