Azure • Haberler • Microsoft

ComfyUI’da fal ve Foundry ile sinematik video

comfyui-foundry-cover

fal MiniMax H3 Max ve Foundry görsel modellerini anahtarsız Entra ID ile ComfyUI’a ekleme: dağıtım, rol ataması, node kodu ve görselden video hattı.

Microsoft, yeni fal MiniMax H3 Max video modelini ve Foundry görsel modellerini (gpt-image, FLUX, MAI Image) iki özel node ve anahtarsız Microsoft Entra ID kimlik doğrulamasıyla ComfyUI’a getirmeyi anlatan bir rehber yayımladı. Sonuç, metinden sinematik bir görselden videoya hattı. GPU ya da API anahtarı gerekmiyor.

ComfyUI, birçok yaratıcı ekibin üretken modelleri zincirlemek için kullandığı node tabanlı bir tuval. Rehberde şu adımlar var: modelleri tek bir Foundry projesinde dağıtmak, ComfyUI’a Entra ID ile anahtarsız erişim vermek, Foundry node’larını eklemek ve bir metin prompt’unu gerçekçi, 5 saniyelik sinematik bir çekime dönüştüren hattı kurmak. Rehber, ComfyUI’ın kurulu olduğunu varsayıyor; Windows, macOS, Linux ya da Azure’daki bir VM üzerinde çalışabilir.

Foundry görsel modeli ve fal MiniMax H3 Max ile üretilen sinematik sahne
Foundry görsel modeli ve fal MiniMax H3 Max ile üretilen sinematik sahne

Nasıl çalışıyor?

ComfyUI node'ları Foundry API'lerini doğrudan çağırıyor
ComfyUI node’ları Foundry API’lerini doğrudan çağırıyor

Node’lar Foundry API’lerini doğrudan çağırıyor. Üretim Foundry’de yapıldığı ve ComfyUI yalnızca çağrıları yönettiği için yalnızca CPU’lu bir makine yeterli. Node’lar, ComfyUI’ın çalıştığı yerdeki kimlikten Entra ID token’ı alıyor: kendi makinenizde az login oturumunuz, Azure’da ise bir managed identity. Paylaşılan iş akışlarında saklanacak, yenilenecek ya da sızacak API anahtarı yok.

Adım 1: Modelleri Foundry projesinde dağıtın

Projenizin arkasındaki Foundry kaynağına en az bir görsel modeli ve fal video modelini dağıtın. Portalda model kataloğunu açıp modeli bulun (Inference tasks filtresinde Text to image seçilebilir), Deploy’u seçip varsayılan ayarları koruyun.

Foundry portalında model kataloğundan dağıtım
Foundry portalında model kataloğundan dağıtım

Komut satırını tercih ederseniz her model için şu komutu çalıştırın:

az cognitiveservices account deployment create \
 --resource-group <resource-group> \
 --name <foundry-resource> \
 --deployment-name <deployment-name> \
 --model-name <model-name> \
 --model-format <model-format> \
 --model-version <model-version> \
 --sku-name GlobalStandard \
 --sku-capacity <capacity>
Model–model-name–model-format
gpt-imagegpt-image-2.5-sunburstOpenAI
FLUXFLUX.2-proBlack Forest Labs
MAI ImageMAI-Image-2.6Microsoft
fal MiniMax H3 Max (video)Fal-MiniMax-H3-Maxfal.ai
  • Kullanılabilir <model-version> değerini bulmak için: az cognitiveservices account list-models -g <resource-group> -n <foundry-resource> --query "[?name=='<model-name>'].version" -o tsv
  • Her görsel dağıtımına modelin adını verin (örneğin FLUX.2-pro); node’lar model ailesini adından tanıyor.
  • Video dağıtımının adı serbest; rehberde minimax-h3 kullanılıyor.
Projede listelenen dağıtımlar
Projede listelenen dağıtımlar

Son olarak Foundry portalında projenin Overview sayfasından proje uç noktasını kopyalayın. Biçimi: https://<foundry-resource>.services.ai.azure.com/api/projects/<project>

Adım 2: ComfyUI’a anahtarsız erişim verin

Node’lar, ComfyUI’ın çalıştığı makinedeki Entra ID kimliğini kullanan DefaultAzureCredential ile çalışıyor. Bu kimliğin Foundry kaynağında Cognitive Services User rolüne sahip olması gerekiyor; Foundry User rolü de çalışıyor.

ComfyUI kendi makinenizde çalışıyorsa: Azure CLI’ı kurun, ComfyUI’ı başlatan işletim sistemi kullanıcısıyla az login yapın ve rolü atayın:

az login 

FOUNDRY_ID=$(az cognitiveservices account show -g <resource-group> -n <foundry-resource> --query id -o tsv)
az role assignment create --assignee-object-id $(az ad signed-in-user show --query id -o tsv) \
 --assignee-principal-type User --role "Cognitive Services User" --scope $FOUNDRY_ID

Rol atama komutları Bash söz dizimi kullanıyor; Windows’ta Git Bash ya da Azure Cloud Shell’de çalıştırılabilir. Yalnızca az login ComfyUI makinesinde çalışmalı.

ComfyUI Azure’da çalışıyorsa: Kişisel oturum yerine managed identity kullanın. Örneğin bir VM’de:

az vm identity assign -g <vm-resource-group> -n <vm-name> 
PRINCIPAL_ID=$(az vm show -g <vm-resource-group> -n <vm-name> --query identity.principalId -o tsv) 
FOUNDRY_ID=$(az cognitiveservices account show -g <resource-group> -n <foundry-resource> --query id -o tsv)

az role assignment create --assignee-object-id $PRINCIPAL_ID \
 --assignee-principal-type ServicePrincipal --role "Cognitive Services User" --scope $FOUNDRY_ID
Foundry kaynağında rol ataması
Foundry kaynağında rol ataması

Rol atamak için kaynakta Owner ya da User Access Administrator rolü gerekiyor; yoksa Azure yöneticinizden isteyin. Atamaların yayılması birkaç dakika sürebilir; ilk çalıştırmada 401 ya da 403 alırsanız bekleyip tekrar deneyin.

Adım 3: Foundry node’larını ComfyUI’a ekleyin

Bir ComfyUI özel node’u, ComfyUI’ın custom_nodes klasöründeki bir Python klasöründen ibaret. İki dosyalı bir klasör oluşturup iki Python paketi kuracak, bir ortam değişkeni ayarlayacak ve ComfyUI’ı yeniden başlatacaksınız.

3.1 ComfyUI klasörünü ve Python’unu bulun:

Kurulumcustom_nodes nerede?ComfyUI’ı çalıştıran Python
ComfyUI Desktop (Windows, macOS)Instance’ta ⋮, sonra Manage → About; kurulum yolunu açın, custom_nodes içindeManage → Terminal, instance klasöründe bir kabuk açar
Windows portableComfyUI_windows_portable\ComfyUI\custom_nodesComfyUI_windows_portable\python_embeded\python.exe
Elle kurulum (git clone)ComfyUI/custom_nodesComfyUI’ı başlattığınız sanal ortam, örneğin ComfyUI/.venv

ComfyUI klasöründe bir terminal açın (elle kurulumda önce sanal ortamı etkinleştirin) ve python‘un sistemin değil ComfyUI’ın Python’u olduğunu kontrol edin. Yazdırılan yol ComfyUI kurulumunun içinde olmalı; değilse aşağıdaki komutlarda tablodaki tam yolu kullanın.

python -c "import sys; print(sys.executable)"

3.2 Node klasörünü ve dosyalarını oluşturun. Sonuçta klasör şöyle görünmeli:

ComfyUI/
└── custom_nodes/
    └── ComfyUI-Foundry/
        ├── __init__.py
        └── nodes.py

custom_nodes içinde ComfyUI-Foundry adlı bir klasör oluşturun. İçine bu tek satırlık __init__.py dosyasını ekleyin; ComfyUI’a klasörün hangi node’ları sağladığını söylüyor:

from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS

Aynı klasörde nodes.py oluşturup aşağıdaki kodu yapıştırın. Visual Studio Code gibi bir kod editörü kullanın; Windows Not Defteri’ni kullanırsanız “Farklı kaydet türü”nü Tüm dosyalar yapın, aksi halde dosya nodes.py.txt olarak kaydedilir ve ComfyUI onu yok sayar.

NodeNe çağırıyor?
Foundry Image GenerateDağıtıma uygun görsel API’si: gpt-image için /openai/v1/images/generations, FLUX için /providers/blackforestlabs/v1/…, MAI Image için /mai/v1/images/generations
fal MiniMax H3 Max Video (Foundry)/providers/falai/v1/minimax/h3-max/image-to-video; görsel bağlı değilse text-to-video, referans görsel ya da video bağlıysa reference-to-video. fal işleri asenkron olduğu için node video hazır olana kadar sorgulayıp MP4’ü kaydediyor.
"""ComfyUI nodes for Microsoft Foundry image models and fal video models on Foundry.
Keyless: DefaultAzureCredential uses your az login sign-in, or a managed identity on Azure."""

import base64, io, os, re, time, uuid

import numpy as np
import requests
import torch
from azure.identity import DefaultAzureCredential
from PIL import Image

import folder_paths

try:
    from comfy_api.input_impl import VideoFromFile
except ImportError:  # older ComfyUI builds without the VIDEO type
    VideoFromFile = None

SCOPE = "https://cognitiveservices.azure.com/.default"
_credential = None


def _auth():
    global _credential
    if _credential is None:
        _credential = DefaultAzureCredential(exclude_interactive_browser_credential=True)
    return {"Authorization": "Bearer " + _credential.get_token(SCOPE).token}


def _base_url(override=""):
    # Accepts a project endpoint (https://<resource>.services.ai.azure.com/api/projects/<project>)
    # or the resource endpoint; model APIs are served from the resource root.
    url = (override or os.environ.get("FOUNDRY_ENDPOINT", "")).strip().rstrip("/")
    if not url:
        raise ValueError("Set the FOUNDRY_ENDPOINT environment variable.")
    return re.sub(r"/api/projects/[^/]+$", "", url)


def _to_tensor(img):
    return torch.from_numpy(np.asarray(img.convert("RGB"), dtype=np.float32) / 255.0)[None, ...]


def _to_data_uri(image, index=0):
    buf = io.BytesIO()
    Image.fromarray((image[index].cpu().numpy() * 255).clip(0, 255).astype(np.uint8)).save(buf, "JPEG", quality=92)
    return "data:image/jpeg;base64," + base64.b64encode(buf.getvalue()).decode()


def _video_to_data_uri(video):
    buf = io.BytesIO()
    video.save_to(buf, format="mp4")  # keeps any trim applied in ComfyUI
    return "data:video/mp4;base64," + base64.b64encode(buf.getvalue()).decode()


FLUX_PATHS = {"flux.2-pro": "flux-2-pro", "flux.2-flex": "flux-2-flex",
              "flux.1-kontext-pro": "flux-kontext-pro", "flux-1.1-pro": "flux-pro-1.1"}


def _family(deployment, family):
    name = deployment.lower()
    if family != "auto":
        return family
    if "flux" in name:
        return "flux"
    if name.startswith(("mai-image", "mai_image")):
        return "mai"
    return "gpt-image"


class FoundryImageGenerate:
    """Text-to-image with a Foundry deployment: gpt-image, FLUX or MAI Image."""

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "prompt": ("STRING", {"multiline": True, "default": ""}),
                "deployment": ("STRING", {"default": "gpt-image-2.5-sunburst"}),
                "size": (["1536x1024", "1024x1024", "1024x1536", "auto"], {"default": "1536x1024"}),
                "quality": (["high", "medium", "low", "auto"], {"default": "high"}),
                "seed": ("INT", {"default": 0, "min": 0, "max": 2**31 - 1}),
            },
            "optional": {
                "model_family": (["auto", "gpt-image", "flux", "mai"], {"default": "auto"}),
                "endpoint": ("STRING", {"default": "", "tooltip": "Empty = FOUNDRY_ENDPOINT"}),
            },
        }

    RETURN_TYPES = ("IMAGE",)
    FUNCTION = "generate"
    CATEGORY = "Microsoft Foundry"

    def generate(self, prompt, deployment, size, quality, seed, model_family="auto", endpoint=""):
        family, base = _family(deployment, model_family), _base_url(endpoint)
        dims = {} if size == "auto" else dict(zip(("width", "height"), map(int, size.split("x"))))
        if family == "flux":
            path = FLUX_PATHS.get(deployment.lower(), "flux-2-pro")
            url = f"{base}/providers/blackforestlabs/v1/{path}?api-version=preview"
            body = {"model": deployment, "prompt": prompt, "seed": seed, "output_format": "png", **dims}
        elif family == "mai":
            url = f"{base}/mai/v1/images/generations"
            body = {"model": deployment, "prompt": prompt, **dims}
        else:
            url = f"{base}/openai/v1/images/generations"
            body = {"model": deployment, "prompt": prompt, "size": size, "quality": quality, "n": 1}

        r = requests.post(url, json=body, headers=_auth(), timeout=300)
        if not r.ok:
            raise RuntimeError(f"{family} image generation failed ({r.status_code}): {r.text[:500]}")
        raw = base64.b64decode(r.json()["data"][0]["b64_json"])
        return (_to_tensor(Image.open(io.BytesIO(raw))),)


class FalH3Video:
    """fal MiniMax H3 Max on Foundry: image-to-video, or text-to-video when no image is connected.
    Connect reference images and/or a reference video to use reference-to-video."""

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "prompt": ("STRING", {"multiline": True, "default": ""}),
                "deployment": ("STRING", {"default": "minimax-h3"}),
                "duration": ("INT", {"default": 5, "min": 1, "max": 10}),
                "resolution": (["768P", "480P", "1080P"], {"default": "768P"}),
            },
            "optional": {
                "image": ("IMAGE",),
                "reference_images": ("IMAGE", {"tooltip": "Image 1, Image 2, ... in batch order"}),
                "reference_video": ("VIDEO", {"tooltip": "Video 1, 2-15 seconds"}),
                "endpoint": ("STRING", {"default": "", "tooltip": "Empty = FOUNDRY_ENDPOINT"}),
            },
        }

    RETURN_TYPES = ("VIDEO", "STRING") if VideoFromFile else ("STRING",)
    RETURN_NAMES = ("video", "path") if VideoFromFile else ("path",)
    FUNCTION = "generate"
    OUTPUT_NODE = True
    CATEGORY = "Microsoft Foundry"

    def generate(self, prompt, deployment, duration, resolution, image=None, endpoint="",
                 reference_images=None, reference_video=None):
        base = _base_url(endpoint) + "/providers/falai/v1/minimax/h3-max"
        body = {"model": deployment, "prompt": prompt, "duration": duration,
                "resolution": resolution, "prompt_expansion_mode": "disabled"}
        route = "text-to-video"
        if image is not None:
            body["image_url"], route = _to_data_uri(image), "image-to-video"
        if reference_images is not None:
            body["reference_image_urls"] = [_to_data_uri(reference_images, i) for i in range(len(reference_images))]
            route = "reference-to-video"
        if reference_video is not None:
            body["reference_video_urls"], route = [_video_to_data_uri(reference_video)], "reference-to-video"

        r = requests.post(f"{base}/{route}", json=body, headers=_auth(), timeout=120)
        if not r.ok:
            raise RuntimeError(f"fal submit failed ({r.status_code}): {r.text[:500]}")
        request_id = r.json()["request_id"]

        deadline = time.time() + 900
        while True:  # fal jobs are async: poll until done
            status = requests.get(f"{base}/requests/{request_id}/status", headers=_auth(), timeout=60).json()
            if status.get("status") == "COMPLETED":
                break
            if status.get("status") in ("FAILED", "CANCELED", "CANCELLED", "ERROR") or time.time() > deadline:
                raise RuntimeError(f"fal request {request_id} did not complete: {status}")
            time.sleep(5)

        result = requests.get(f"{base}/requests/{request_id}", headers=_auth(), timeout=60).json()
        video = requests.get(result["video"]["url"], headers=_auth(), timeout=300)
        video.raise_for_status()
        filename = f"h3_{uuid.uuid4().hex[:8]}.mp4"
        path = os.path.join(folder_paths.get_output_directory(), filename)
        with open(path, "wb") as f:
            f.write(video.content)

        ui = {"images": [{"filename": filename, "subfolder": "", "type": "output"}], "animated": (True,)}
        return {"ui": ui, "result": (VideoFromFile(path), path) if VideoFromFile else (path,)}


NODE_CLASS_MAPPINGS = {"FoundryImageGenerate": FoundryImageGenerate, "FalH3Video": FalH3Video}
NODE_DISPLAY_NAME_MAPPINGS = {"FoundryImageGenerate": "Foundry Image Generate",
                              "FalH3Video": "fal MiniMax H3 Max Video (Foundry)"}

3.3 Python paketlerini kurun. 3.1’de açtığınız terminalden:

python -m pip install azure-identity requests

Windows portable sürümünde ComfyUI_windows_portable klasöründen şunu çalıştırın:

python_embeded\python.exe -m pip install azure-identity requests

3.4 Node’ları Foundry projenize yönlendirin. Node’lar proje uç noktasını FOUNDRY_ENDPOINT ortam değişkeninden okuyor; böylece uç nokta paylaştığınız iş akışlarına girmiyor.

ComfyUI nasıl çalışıyor?FOUNDRY_ENDPOINT nasıl ayarlanır?
ComfyUI DesktopManage → Startup Args → Environment Variables → + Add Variable
Windows (portable ya da elle)setx FOUNDRY_ENDPOINT "https://<foundry-resource>.services.ai.azure.com/api/projects/<project>", ardından yeni bir terminal açın
macOS / Linux (elle)python main.py öncesinde aynı terminalde export FOUNDRY_ENDPOINT="..."
Linux systemd servisisudo systemctl edit comfyui, ardından [Service] altına Environment=FOUNDRY_ENDPOINT=https://…

3.5 ComfyUI’ı yeniden başlatıp node’ları kontrol edin. ComfyUI özel node’ları yalnızca başlangıçta yüklüyor. Başlangıç kaydında “Import times for custom nodes” altında ComfyUI-Foundry satırını arayın.

Başlangıç kaydında ComfyUI-Foundry yüklemesi
Başlangıç kaydında ComfyUI-Foundry yüklemesi

Satırda (IMPORT FAILED) yazıyorsa hatayı yukarıda arayın. “No module named ‘azure'” hatası paketlerin farklı bir Python’a kurulduğunu gösteriyor; 3.3’ü tablodaki tam Python yoluyla tekrarlayın. ComfyUI arayüzünü yenileyip Nodes panelini açtığınızda iki node Microsoft Foundry kategorisinde görünmeli.

Nodes panelinde Microsoft Foundry kategorisi
Nodes panelinde Microsoft Foundry kategorisi

Adım 4: Sinematik sahne hattını kurun

Görsel modeli sinematik bir anahtar kare üretiyor, video modeli de onu canlandırıyor. Tuvale üç node ekleyip şöyle bağlayın: Foundry Image Generate IMAGE → Save Image images ve Foundry Image Generate IMAGE → fal MiniMax H3 Max Video image.

Node bağlantıları: görsel üretimi, kaydetme ve video node'u
Node bağlantıları: görsel üretimi, kaydetme ve video node’u
NodeAyarDeğer
Foundry Image Generatedeployment / size / quality / seedgpt-image-2.5-sunburst / 1536×1024 / high / 42 (sabit)
fal MiniMax H3 Max Videodeployment / duration / resolutionminimax-h3 / 5 / 768P

Foundry Image Generate node’una çekimin görünümünü (lens, ışık ve renk düzeni) belirleyen bir anahtar kare prompt’u girin. Rehberdeki örnek:

Cinematic film still, shot on 35mm anamorphic lens. A lone lighthouse keeper in a weathered yellow raincoat stands on wet black rocks at dusk, facing a storm rolling in over the ocean. Huge waves crash and throw spray into the air, the lighthouse beam cuts through the rain behind him. Teal and orange color grade, volumetric light, shallow depth of field, subtle film grain, ultra realistic, 2.39:1 framing.

fal video node’una ise yalnızca kamera hareketini ve aksiyonu anlatan bir hareket prompt’u girin; sahneyi anahtar kare zaten tanımlıyor:

Slow cinematic dolly-in toward the lighthouse keeper. The lighthouse beam sweeps through the rain, waves crash against the rocks and spray drifts across the frame, his raincoat flutters in the wind. Subtle handheld camera movement, moody film look, natural motion.

Run’ı seçin. Anahtar kare birkaç saniyede geliyor, ardından fal node’u görselden videoya işi gönderip bitene kadar sorguluyor. Rehberdeki denemede tüm hat yaklaşık 80 saniye sürmüş.

Hattın ComfyUI'da çalıştırılması
Hattın ComfyUI’da çalıştırılması

Foundry görsel modelinden gelen anahtar kare (1536×1024):

Anahtar kare: fırtınada deniz feneri bekçisi
Anahtar kare: fırtınada deniz feneri bekçisi

fal MiniMax H3 Max’in ürettiği 5 saniyelik, 24 fps çekimden kareler:

Video çekiminden kareler
Video çekiminden kareler

Görsel modeli görünümü belirlediği, video modeli yalnızca hareket eklediği için çekim kareden kareye tutarlı kalıyor. İşi “anahtar kare → hareket” olarak bölmek, gerçekçi ve sanat yönetimi yapılmış bir sonuç almanın en kolay yolu.

İpuçları

  • Görsel modelini değiştirin: deployment değerini gpt-image-2.5-sunburst, FLUX.2-pro ya da MAI-Image-2.6 yapın; node ailesi addan tanıyor. Dağıtımın özel bir adı varsa model_family‘yi elle ayarlayın. MAI Image için iki kenarın da en az 768 piksel olması gerekiyor.
  • Başka bir Foundry kaynağındaki modeli kullanın: O kaynağın uç noktasını node’un isteğe bağlı endpoint alanına yapıştırın ve aynı kimliğe orada da aynı rolü verin.
  • Daha yüksek kalite: Son render için video node’unda çözünürlüğü 1080P yapın.
  • Metinden video: fal node’unun image girdisini bağlamazsanız text-to-video çağrılıyor.
  • Ucuz deneme: seed’i sabit tutun; ComfyUI girdisi değişmeyen node’ları önbelleğe aldığı için anahtar kareyi yeniden üretmeden hareket prompt’unu değiştirebilirsiniz.

Rehberin güçlü yanı, API anahtarı yerine Entra ID ve managed identity kullanması; paylaşılan ComfyUI iş akışlarında anahtar sızıntısı riskini ortadan kaldırıyor. fal modelleri şu an genel önizlemede ve ABD’de barındırılıyor; görsel ve video üretimi Foundry’de saniye ya da görsel başına ayrı faturalanıyor.

Kaynak: https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/direct-your-next-shot-with-fal-on-microsoft-foundry-and-comfyui/4570230

Yazar Hakkında

Kerem Şuğle

Solution Architect, VMware vExpert ve Microsoft sertifikalı altyapı uzmanı. VMware vSphere/vSAN/VCF, Azure, AWS, Google Cloud, enterprise sanallaştırma ve yapay zeka konularında 15+ yıl deneyim. AI/cloud dönüşümü, sovereign cloud, enterprise güvenlik ve modern altyapı mimarisi alanlarında yazıyor.

Leave a Comment