Add exclusive LLM and video modes with isolated LTX worker and job API

This commit is contained in:
Mikei386
2026-09-29 13:10:23 +02:00
parent 7910307cfb
commit bec79a23aa
21 changed files with 534 additions and 18 deletions
+60
View File
@@ -0,0 +1,60 @@
import tempfile
import time
import unittest
from pathlib import Path
from unittest.mock import Mock,patch
from video import Video
from inference import Scheduler,InferenceError
class VideoTests(unittest.TestCase):
def make(self,d):
scheduler=Scheduler(Mock());profiles=Mock();profiles.status.return_value={'profiles':[dict(id='v',name='test',kind='video')]}
return Video(Path(d),profiles,Mock(),scheduler,Mock())
def wait(self,v):
v.thread.join(3);self.assertFalse(v.thread.is_alive())
def test_exclusive_mode_stops_owned_work_and_restores_llm(self):
with tempfile.TemporaryDirectory() as d:
v=self.make(d);v.select('v');v.blockers=Mock(return_value=[]);v.load=Mock()
v.switch('video');self.wait(v)
self.assertEqual(v.mode,'video');v.stop_owned.assert_called_once();v.load.assert_called_once()
with self.assertRaises(InferenceError):
with v.scheduler.lease(('chat',)):pass
with self.assertRaises(ValueError):v.select('v')
v.switch('llm');self.wait(v);self.assertEqual(v.scheduler.gpu_mode,'llm')
with v.scheduler.lease(('chat',)):pass
def test_failed_load_does_not_leave_gpu_gate_locked(self):
with tempfile.TemporaryDirectory() as d:
v=self.make(d);v.select('v');v.blockers=Mock(return_value=[]);v.load=Mock(side_effect=ValueError('busy'))
v.switch('video');self.wait(v)
self.assertEqual(v.status()['error'],'busy');self.assertEqual(v.scheduler.gpu_mode,'llm')
def test_foreign_process_prevents_spawn(self):
with tempfile.TemporaryDirectory() as d,patch('video.probe',return_value=[{'processes':1}]),patch('video.subprocess.Popen') as spawn:
v=self.make(d)
with self.assertRaises(ValueError):v.load({})
spawn.assert_not_called()
def test_request_parameters_override_defaults_and_validate(self):
with tempfile.TemporaryDirectory() as d:
v=self.make(d);v.mode='video';v.state='ready';v.profile={'id':'v','name':'test','parameters':dict(width=512,height=320,fps=24,frames=9,seed=42)}
with patch('video.threading.Thread') as thread,patch('video.shutil.disk_usage') as disk:
disk.return_value.free=100*1024**3
result=v.generate({'prompt':'synthetic','width':640})
self.assertEqual(result['parameters']['width'],640);self.assertEqual(result['parameters']['fps'],24)
self.assertNotIn('prompt',str(v.status()))
with self.assertRaises(ValueError):v.generate({'prompt':'synthetic'})
v.job=None
for extra in [{'frames':10},{'fps':float('nan')},{'width':True},{'model':'wrong'},{'steps':100}]:
with self.assertRaises(ValueError):v.generate(dict(prompt='synthetic',**extra))
def test_multiple_worker_messages_in_one_read(self):
import subprocess,sys
with tempfile.TemporaryDirectory() as d:
v=self.make(d);v.job={'id':'test'}
p=subprocess.Popen([sys.executable,'-c','import sys,time;sys.stdout.write(\'{"state":"progress","phase":"test"}\\n{"state":"complete"}\\n\');sys.stdout.flush();time.sleep(3)'],stdout=subprocess.PIPE,bufsize=0)
v.process=p
try:
self.assertEqual(v.read(p,1)['state'],'complete');self.assertEqual(v.job['phase'],'test')
finally:p.terminate();p.wait();p.stdout.close()
def test_missing_components_leave_current_mode_untouched(self):
with tempfile.TemporaryDirectory() as d:
v=self.make(d);v.select('v');v.blockers=Mock(return_value=['missing'])
with self.assertRaises(ValueError):v.switch('video')
self.assertEqual(v.scheduler.gpu_mode,'llm');v.stop_owned.assert_not_called()