Files
facefusion/facefusion/node.py
T
henryruhs 5fe245b1fa Add node-based architecture with workflow UI
- Node decorator system (@node) with NodeContext for composable processing
- Node registry with typed input/output ports (image, json)
- API endpoints: GET /nodes (list), POST /nodes/{name} (execute)
- Nodes: face_detector, face_landmarker, face_debugger, face_enhancer, face_swapper
- Workflow UI served as static HTML from /
- Auto-executing nodes, type-enforced connections, session persistence

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:32:43 +02:00

102 lines
2.3 KiB
Python

import base64
from dataclasses import dataclass
from functools import wraps
from typing import Any, Callable, Dict, List, Optional, Type
import cv2
import numpy
from numpy.typing import NDArray
@dataclass
class NodePort:
name : str
type : str # 'image', 'json', 'faces'
label : str = ''
@dataclass
class NodeSchema:
name : str
inputs : List[NodePort]
outputs : List[NodePort]
state_keys : List[str]
description : str = ''
@dataclass
class RegisteredNode:
schema : NodeSchema
fn : Callable
NODE_REGISTRY : Dict[str, RegisteredNode] = {}
class NodeContext:
def __init__(self, state : Dict[str, Any]) -> None:
self._state = dict(state)
def get_item(self, key : str) -> Any:
value = self._state.get(key)
if value is None:
from facefusion import state_manager
value = state_manager.get_item(key)
return value
def __getitem__(self, key : str) -> Any:
return self.get_item(key)
def __contains__(self, key : str) -> bool:
return key in self._state
def to_dict(self) -> Dict[str, Any]:
return dict(self._state)
def node(name : str, inputs : List[NodePort], outputs : List[NodePort], state_keys : List[str], description : str = '') -> Callable:
def decorator(fn : Callable) -> Callable:
schema = NodeSchema(
name = name,
inputs = inputs,
outputs = outputs,
state_keys = state_keys,
description = description
)
@wraps(fn)
def wrapper(inputs_dict : Dict[str, Any], ctx : Optional[NodeContext] = None) -> Dict[str, Any]:
if ctx is None:
from facefusion import state_manager
state_snapshot = { key: state_manager.get_item(key) for key in state_keys }
ctx = NodeContext(state_snapshot)
return fn(inputs_dict, ctx)
wrapper.__node_schema__ = schema
NODE_REGISTRY[name] = RegisteredNode(schema = schema, fn = wrapper)
return wrapper
return decorator
def get_node(name : str) -> Optional[RegisteredNode]:
return NODE_REGISTRY.get(name)
def get_all_nodes() -> Dict[str, RegisteredNode]:
return NODE_REGISTRY
def decode_vision_frame(b64_string : str) -> NDArray[Any]:
image_bytes = base64.b64decode(b64_string)
return cv2.imdecode(numpy.frombuffer(image_bytes, numpy.uint8), cv2.IMREAD_COLOR)
def encode_vision_frame(frame : NDArray[Any], fmt : str = '.jpg') -> str:
_, buffer = cv2.imencode(fmt, frame)
return base64.b64encode(buffer.tobytes()).decode('utf-8')