Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions freevideo_engine/agent/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ def generation_media(*, local=False, enhance=False):
def generate(*, local=False):
return obj(dict(kind={'const': 'generate', 'type': 'string', 'default': 'generate'},
prompt={'oneOf': [string(20000), obj({'from_job': JOB}, ('from_job',))]},
width=number(256, 4096, integer=True, multipleOf=32, default=1344),
height=number(256, 4096, integer=True, multipleOf=32, default=768),
width=number(256, 4096, integer=True, multipleOf=32, description='A multiple of 32. For a size that is not, use the nearest multiple on each side (1000 -> 992).', default=1344),
height=number(256, 4096, integer=True, multipleOf=32, description='A multiple of 32. For a size that is not, use the nearest multiple on each side (1000 -> 992).', default=768),
aspect=string(7, pattern=r'^[1-9][0-9]{0,2}:[1-9][0-9]{0,2}$', examples=sorted(ASPECTS)),
megapixels=number(.1, 16, default=.98), seconds=number(1.625, 60, default=10),
seed=dict(number(0, 2**53 - 1, integer=True), type=['integer', 'null'], default=None),
Expand Down Expand Up @@ -995,7 +995,7 @@ def image_schema(name):
'list': ('GET', '/projects', 'List editing projects shared with the editor page. Then get one to read its revision and clip IDs.'),
'get': ('GET', '/projects/{id}', 'Read a timeline and authenticated source URLs. Read its revision and clip IDs before editing.'),
'create': ('POST', '/projects', 'Create an editor timeline. Send {"project": {"name": "..."}}; the rest of an empty timeline is filled in.'),
'write': ('PUT', '/projects/{id}', 'Replace a timeline. First get the project and preserve its id and revision; recompute from error.project on project_changed.'),
'write': ('PUT', '/projects/{id}', 'Replace the whole timeline. To remove, insert, move or trim clips use freevideo_editor_operations instead. First get the project and preserve its id and revision; recompute from error.project on project_changed.'),
'operations': ('POST', '/projects/{id}/operations', 'Apply 1-64 timeline edits atomically and save once. First get revision and clip IDs; on project_changed recompute from error.project, and on clip_busy wait for generation to finish.'),
'generate': ('POST', '/projects/{id}/generate', 'Make new picture and sound for selected clips or a frame range, as the page Generate does. First get revision and clip IDs; the parts are reserved at once and fill in as the returned job finishes.'),
'export': ('POST', '/projects/{id}/export', 'Render one saved revision to an MP4 in the saved videos. First get the revision; wait for the returned job, then read its result video.'),
Expand Down
10 changes: 9 additions & 1 deletion freevideo_engine/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1771,6 +1771,13 @@ def progress(value):
self.ui.end(key, success=False)
raise
self.ui.end(key, detail='Verified and ready')
self.write_encoder_paths()

def write_encoder_paths(self):
"""The text-encoder folder map the shared encoder reads; a video model install writes it with its files."""
from .monitoring import save
save(self.root / 'encoder-paths.yaml',
{'freevideo': {'base_path': str(self.plan['encoder_dir']), 'text_encoders': 'text_encoders/'}})

def missing_files(self, python, role, *, gpu=None, wheels=None):
"""Pinned packages of an environment whose files are gone (runtime_libraries.package_files).
Expand Down Expand Up @@ -2240,7 +2247,8 @@ def check_kernels(self, python, kernel_report):
receipt = (reusable_kernel_receipt(cached_kernel, hardware_data)
if isinstance(hardware_data, dict) else None)
if receipt is None:
self.command('kernels', [python, '-m', 'freevideo_engine', 'doctor', '--probe', '--require-paths', '--out', kernel_report])
self.command('kernels', [python, '-m', 'freevideo_engine', 'doctor', '--probe', '--require-paths',
*(['--without-video-models'] if self.plan.get('only') == 'image' else []), '--out', kernel_report])
else:
save(kernel_report, receipt)
self.ui.event('setup_cache', key='kernels', detail='Reused matching GPU kernel checks')
Expand Down
4 changes: 3 additions & 1 deletion freevideo_engine/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def main():
doctor = sub.add_parser('doctor', help='Check paths, versions and installed attention kernels')
doctor.add_argument('--probe', action='store_true', help='Execute small kernels in isolated processes')
doctor.add_argument('--require-paths', action='store_true', help='Fail if model or source directories are missing')
doctor.add_argument('--without-video-models', action='store_true', help='With --require-paths: an image-only installation has no video model directories')
doctor.add_argument('--out', type=Path, help='Save the complete kernel report, including optional failures')
sub.add_parser('diagnose', help='Collect bounded local diagnostics, even after a failed setup')
sub.add_parser('optimize', help='Reuse a test report and validate bounded local tuning')
Expand Down Expand Up @@ -202,7 +203,8 @@ def main():
write_json(result)
if args.probe and not result['ready']:
raise SystemExit(1)
if args.require_paths and not all(row['exists'] for row in result['paths'].values()):
from .doctor import missing_required_paths
if args.require_paths and missing_required_paths(result, video_models=not args.without_video_models):
raise SystemExit(1)
elif args.command == 'setup':
from .install import install
Expand Down
3 changes: 3 additions & 0 deletions freevideo_engine/comfy_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,9 @@ def installation(source=None, environ=None):
'A prepared engine installation is required.') from error
required = ('root', 'python', 'cache', 'base', 'checkpoint', 'comfy_python',
'comfy_root', 'vdn_root', 'model_paths', 'model_root', 'encoder')
if isinstance(machine, dict) and machine.get('video_model') is False:
# An image-only installation has no prepared video model, so no sampling cache.
required = tuple(k for k in required if k != 'cache')
if not isinstance(machine, dict) or not machine.get('ready') or any(not machine.get(k) for k in required):
raise ValueError('FreeVideo setup is incomplete in %s. %s Existing files are reused.' % (root, repair_hint()))
identity = 'device_identity' if machine.get('device_backend') == 'mps' else 'gpu_uuid'
Expand Down
3 changes: 2 additions & 1 deletion freevideo_engine/comfy_launcher_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,8 @@ def _inspect(self, values):
_, machine = installation(source, {'FREEVIDEO_HOME': str(engine)})
# With every quality level requested, set up again only while some are missing.
from .sampling_assets import installed as sampling_installed
ready = not values.get('sampling_caches') or sampling_installed(machine)
ready = (not values.get('sampling_caches') or machine.get('video_model') is False
or sampling_installed(machine))
except (OSError, ValueError, KeyError):
pass
self.selection = dict(descriptor, engine=str(engine), source=str(source), url=url, ready=ready,
Expand Down
2 changes: 1 addition & 1 deletion freevideo_engine/comfy_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ def start_background_compile():
if os.environ.get('FREEVIDEO_BACKGROUND_COMPILE', 'auto').lower() in ('0', 'off', 'false'):
return False
root, machine = comfy_bridge.installation()
if machine.get('device_backend') == 'mps' or machine.get('gpu_vendor') == 'amd':
if machine.get('device_backend') == 'mps' or machine.get('gpu_vendor') == 'amd' or machine.get('video_model') is False:
return False
source = comfy_bridge.source_root()
environment = comfy_bridge.engine_environment(root, source)
Expand Down
6 changes: 6 additions & 0 deletions freevideo_engine/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
from . import processes, subprocess_output


def missing_required_paths(result, *, video_models=True):
"""Names of required directories that do not exist; an image-only installation has no video model to check."""
skipped = () if video_models else ('base', 'checkpoint')
return [k for k, row in result['paths'].items() if k not in skipped and not row['exists']]


def doctor(*, probe=False, hardware=None, backends=None, progress=None):
if progress is None and os.environ.get('FREEVIDEO_INSTALL_PROGRESS') == '1':
from .install_activity import step
Expand Down
8 changes: 7 additions & 1 deletion freevideo_engine/macos_doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,16 @@ def doctor(*, probe=False):
return result


def missing_required_paths(result, *, video_models=True):
skipped = () if video_models else ('base', 'checkpoint')
return [k for k, row in result['paths'].items() if k not in skipped and not row['exists']]


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--probe', action='store_true')
parser.add_argument('--require-paths', action='store_true')
parser.add_argument('--without-video-models', action='store_true')
parser.add_argument('--out', type=Path)
args = parser.parse_args()
result = doctor(probe=args.probe)
Expand All @@ -102,7 +108,7 @@ def main():
save(args.out, result)
print(json.dumps(result, indent=2))
return int((args.probe and not result['ready']) or
(args.require_paths and not all(row['exists'] for row in result['paths'].values())))
(args.require_paths and missing_required_paths(result, video_models=not args.without_video_models)))


if __name__ == '__main__':
Expand Down
3 changes: 2 additions & 1 deletion freevideo_engine/macos_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,9 @@ def dispatch(args):
from .macos_doctor import doctor
result = doctor(probe=args.probe)
write_json(result, args.out)
from .macos_doctor import missing_required_paths
if (args.probe and not result['ready']) or (args.require_paths and
not all(row['exists'] for row in result['paths'].values())):
missing_required_paths(result, video_models=not args.without_video_models)):
raise SystemExit(1)
elif args.command == 'plan':
write_json(policy(args), args.out)
Expand Down
3 changes: 2 additions & 1 deletion freevideo_engine/macos_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def check_kernels(self, python, kernel_report):
# A cheap native execution check is repeated on repair. Do not reuse
# a CUDA receipt or claim a full model benchmark from this probe.
self.command('kernels', [python, '-m', 'freevideo_engine.macos_doctor',
'--probe', '--require-paths', '--out', kernel_report])
'--probe', '--require-paths', *(['--without-video-models'] if self.plan.get('only') == 'image' else []),
'--out', kernel_report])
result = json.loads(kernel_report.read_text(encoding='utf-8'))
if not result.get('ready') or result.get('device_backend') != 'mps':
raise RuntimeError('Native GPU checks did not complete; see the retained report')
Expand Down
Loading