diff --git a/TensorStack.Python/Config/EnvironmentConfig.cs b/TensorStack.Python/Config/EnvironmentConfig.cs
index 7545cae..d9731fc 100644
--- a/TensorStack.Python/Config/EnvironmentConfig.cs
+++ b/TensorStack.Python/Config/EnvironmentConfig.cs
@@ -17,9 +17,9 @@ public record EnvironmentConfig
[
"typing==3.7.4.3",
"wheel==0.46.3",
- "transformers==4.57.6",
+ "transformers==5.5.4",
"accelerate==1.13.0",
- "diffusers@https://github.com/huggingface/diffusers/archive/d0c9cbad28d7d3bba28db94622e13500c4179075.zip",
+ "diffusers@https://github.com/huggingface/diffusers/archive/f3d42be118f9af7ed9697b686fba09a8bdcd71d1.zip",
"protobuf==7.34.1",
"sentencepiece==0.2.1",
"ftfy==6.3.1",
@@ -30,7 +30,8 @@ public record EnvironmentConfig
"gguf==0.18.0",
"av==17.0.1",
"optimum-quanto==0.2.7",
- "bitsandbytes==0.49.2"
+ "bitsandbytes==0.49.2",
+ "soundfile==0.13.1"
];
diff --git a/TensorStack.Python/Pipelines/IdeogramPipeline.py b/TensorStack.Python/Pipelines/IdeogramPipeline.py
new file mode 100644
index 0000000..3ad2aa2
--- /dev/null
+++ b/TensorStack.Python/Pipelines/IdeogramPipeline.py
@@ -0,0 +1,467 @@
+import tensorstack.utils as Utils
+import tensorstack.data_objects as DataObjects
+import tensorstack.quantization as Quantization
+from tensorstack.enums import ProcessType, QuantTarget
+Utils.redirect_output()
+Utils.create_services()
+
+import torch
+import numpy as np
+from pathlib import Path
+from threading import Event
+from collections.abc import Buffer
+from typing import Dict, Sequence, List, Tuple, Optional, Any
+from transformers import Qwen2Tokenizer, Qwen3VLModel
+from diffusers import (
+ AutoencoderKLFlux2,
+ Ideogram4Transformer2DModel,
+ Ideogram4Pipeline
+)
+
+# Globals
+_config = None
+_model_config = None
+_pipeline = None
+_processType = None
+_execution_device = None
+_device_map = None
+_pipeline_device_map = None
+_control_net_name = None
+_control_net_cache = None
+_generator = None
+_isMemoryOffload = False
+_prompt_cache_key = None
+_prompt_cache_value = None
+_cancel_event = Event()
+_stopwatch = None
+_pipelineMap = {
+ ProcessType.TextToImage: Ideogram4Pipeline,
+}
+
+
+#------------------------------------------------
+# Load Pipeline
+#------------------------------------------------
+def load(config_args: Dict[str, Any]) -> bool:
+ global _config, _pipeline, _generator, _processType, _execution_device, _isMemoryOffload
+
+ # Config
+ _config = DataObjects.PipelineConfig(**config_args)
+ _execution_device = Utils.get_execution_device(_config)
+ _generator = torch.Generator(device=_execution_device)
+ _processType = _config.process_type
+
+ # Initialize Pipeline
+ _pipeline = initialize(_config)
+
+ # Load Lora
+ Utils.load_lora_weights(_pipeline, _config)
+
+ # Memory
+ _isMemoryOffload = Utils.configure_pipeline_memory(_pipeline, _execution_device, _config)
+ Utils.trim_memory(_isMemoryOffload)
+ return True
+
+
+#------------------------------------------------
+# Reload Pipeline - ProcessType, LoraAdapters and ControlNet are the only options that can be modified
+#------------------------------------------------
+def reload(config_args: Dict[str, Any]) -> bool:
+ global _config, _pipeline, _processType
+
+ # Config
+ _config = DataObjects.PipelineConfig(**config_args)
+ _processType = _config.process_type
+
+ # Rebuild Pipeline
+ _pipeline.unload_lora_weights()
+ _pipeline = create_pipeline(_config)
+
+ # Load Lora
+ Utils.load_lora_weights(_pipeline, _config)
+
+ # Memory
+ Utils.configure_pipeline_memory(_pipeline, _execution_device, _config)
+ Utils.trim_memory(_isMemoryOffload)
+ return True
+
+
+#------------------------------------------------
+# Switch Pipeline - ProcessType
+#------------------------------------------------
+def switch(process_type: ProcessType) -> bool:
+ global _pipeline, _processType
+
+ # Switch Pipeline
+ current = _processType
+ _processType = process_type
+ _pipeline = create_pipeline(_config)
+
+ print(f"[Generate] Switched pipeline: {current} => {process_type}")
+ return True
+
+
+#------------------------------------------------
+# Cancel Generation
+#------------------------------------------------
+def generateCancel() -> None:
+ _cancel_event.set()
+
+
+#------------------------------------------------
+# Unload Pipline
+#------------------------------------------------
+def unload() -> bool:
+ global _pipeline, _prompt_cache_key, _prompt_cache_value
+ _pipeline = None
+ _prompt_cache_key = None
+ _prompt_cache_value = None
+ Utils.trim_memory(_isMemoryOffload)
+ return True
+
+
+#------------------------------------------------
+# Get the notifications
+#------------------------------------------------
+def getNotifications() -> list[(str, Buffer)]:
+ return Utils.notification_get()
+
+
+#------------------------------------------------
+# Get the log entires
+#------------------------------------------------
+def getLogs() -> list[str]:
+ return Utils.get_output()
+
+
+#------------------------------------------------
+# Diffusers pipeline callback to capture step artifacts
+#------------------------------------------------
+def _progress_callback(pipe, step: int, total_steps: int, info: Dict):
+ if _cancel_event.is_set():
+ pipe._interrupt = True
+ raise Exception("Operation Canceled")
+
+ steps = pipe._num_timesteps
+ elapsed = _stopwatch.reset()
+ step_latents = info.get("latents")
+ step_latents = step_latents.float().cpu() if step_latents is not None else []
+ Utils.notification_push(key="Generate", subkey="Step", value=step + 1, maximum=steps, elapsed=elapsed, tensor=step_latents)
+ return info
+
+
+#------------------------------------------------
+# Initialize Pipeline
+#------------------------------------------------
+def initialize(config: DataObjects.PipelineConfig):
+ global _model_config, _device_map, _pipeline_device_map
+
+ _device_map = Utils.get_device_map(config, _execution_device)
+ _pipeline_device_map = Utils.get_pipeline_device_map(config, _execution_device)
+ _model_config = Utils.get_model_config(__file__, config)
+ return create_pipeline(config)
+
+
+#------------------------------------------------
+# Load Qwen2Tokenizer
+#------------------------------------------------
+def load_tokenizer(config: DataObjects.PipelineConfig, pipeline_kwargs: Dict[str, str]):
+ if _pipeline and _pipeline.tokenizer:
+ print(f"[Load] Loading Cached Tokenizer")
+ return _pipeline.tokenizer
+
+ tokenizer_path: Path = _model_config["tokenizer"]
+ tokenizer_config: Path = _model_config["tokenizer_config"]
+
+ # 1. Load from pretrained folder
+ print(f"[Load] Loading Pretrained Tokenizer")
+ tokenizer = Qwen2Tokenizer.from_pretrained(
+ tokenizer_path,
+ config=tokenizer_config,
+ dtype=config.data_type,
+ **pipeline_kwargs
+ )
+ return tokenizer
+
+
+#------------------------------------------------
+# Load Qwen3VLModel
+#------------------------------------------------
+def load_text_encoder(config: DataObjects.PipelineConfig, pipeline_kwargs: Dict[str, str]):
+ if _pipeline and _pipeline.text_encoder:
+ print(f"[Load] Loading Cached TextEncoder")
+ return _pipeline.text_encoder
+
+ text_encoder_path: Path = _model_config["text_encoder"]
+ text_encoder_config: Path = _model_config["text_encoder_config"]
+
+ # 1. Load from pretrained folder
+ print(f"[Load] Loading Pretrained TextEncoder")
+ text_encoder = Qwen3VLModel.from_pretrained(
+ text_encoder_path,
+ config=text_encoder_config,
+ dtype=config.data_type,
+ device_map=_device_map,
+ quantization_config=Quantization.auto_pretrained_config(config, QuantTarget.TEXT_ENCODER),
+ **pipeline_kwargs
+ )
+ Utils.trim_memory(True)
+ return text_encoder
+
+
+#------------------------------------------------
+# Load Ideogram4Transformer2DModel
+#------------------------------------------------
+def load_transformer(config: DataObjects.PipelineConfig, pipeline_kwargs: Dict[str, str]):
+ if _pipeline and _pipeline.transformer:
+ print(f"[Load] Loading Cached Transformer")
+ return _pipeline.transformer
+
+ transformer_path: Path = _model_config["transformer"]
+ transformer_config: Path = _model_config["transformer_config"]
+
+ # 1. Load from single file
+ if transformer_path.is_file():
+ is_gguf = Utils.isGGUF(transformer_path)
+ print(f"[Load] Loading File Transformer")
+ transformer = Ideogram4Transformer2DModel.from_single_file(
+ str(transformer_path),
+ config=str(transformer_config),
+ torch_dtype=config.data_type,
+ device_map=_device_map,
+ quantization_config=Quantization.auto_single_file_config(config, QuantTarget.TRANSFORMER, is_gguf),
+ **pipeline_kwargs
+ )
+ Quantization.quantize_model(config, transformer, is_gguf)
+ Utils.trim_memory(True)
+ return transformer
+
+ # 2. Load from pretrained folder
+ print(f"[Load] Loading Pretrained Transformer")
+ transformer = Ideogram4Transformer2DModel.from_pretrained(
+ str(transformer_path),
+ torch_dtype=config.data_type,
+ device_map=_device_map,
+ quantization_config=Quantization.auto_pretrained_config(config, QuantTarget.TRANSFORMER),
+ **pipeline_kwargs
+ )
+ Utils.trim_memory(True)
+ return transformer
+
+
+#------------------------------------------------
+# Load Ideogram4Transformer2DModel
+#------------------------------------------------
+def load_unconditional_transformer(config: DataObjects.PipelineConfig, pipeline_kwargs: Dict[str, str]):
+ if _pipeline and _pipeline.unconditional_transformer:
+ print(f"[Load] Loading Cached Unconditional Transformer")
+ return _pipeline.unconditional_transformer
+
+ transformer_path: Path = _model_config["transformer_2"]
+ transformer_config: Path = _model_config["transformer_2_config"]
+
+ # 1. Load from single file
+ if transformer_path.is_file():
+ is_gguf = Utils.isGGUF(transformer_path)
+ print(f"[Load] Loading File Unconditional Transformer")
+ transformer = Ideogram4Transformer2DModel.from_single_file(
+ str(transformer_path),
+ config=str(transformer_config),
+ torch_dtype=config.data_type,
+ device_map=_device_map,
+ quantization_config=Quantization.auto_single_file_config(config, QuantTarget.TRANSFORMER, is_gguf),
+ **pipeline_kwargs
+ )
+ Quantization.quantize_model(config, transformer, is_gguf)
+ Utils.trim_memory(True)
+ return transformer
+
+ # 2. Load from pretrained folder
+ print(f"[Load] Loading Pretrained Unconditional Transformer")
+ transformer = Ideogram4Transformer2DModel.from_pretrained(
+ str(transformer_path),
+ torch_dtype=config.data_type,
+ device_map=_device_map,
+ quantization_config=Quantization.auto_pretrained_config(config, QuantTarget.TRANSFORMER),
+ **pipeline_kwargs
+ )
+ Utils.trim_memory(True)
+ return transformer
+
+
+#------------------------------------------------
+# Load AutoencoderKLFlux2
+#------------------------------------------------
+def load_vae(config: DataObjects.PipelineConfig, pipeline_kwargs: Dict[str, str]):
+ if _pipeline and _pipeline.vae:
+ print(f"[Load] Loading Cached Vae")
+ return _pipeline.vae
+
+ vae_path: Path = _model_config["vae"]
+ vae_config: Path = _model_config["vae_config"]
+ single_path: Path = _model_config["single_file"]
+ template_path: Path = _model_config["template"]
+
+ # 1. Load from single file
+ if vae_path.is_file():
+ print(f"[Load] Loading SingleFile Vae")
+ auto_encoder = AutoencoderKLFlux2.from_single_file(
+ str(vae_path),
+ config=str(vae_config),
+ torch_dtype=config.data_type,
+ device_map=_device_map,
+ **pipeline_kwargs
+ )
+ Utils.trim_memory(True)
+ return auto_encoder
+
+ # 2. Load component from single file
+ if single_path and single_path.is_file():
+ print(f"[Load] Loading Component Vae")
+ auto_encoder = Utils.from_component(Ideogram4Pipeline, "vae", single_path, template_path, _device_map, config.data_type)
+ if auto_encoder:
+ Utils.trim_memory(True)
+ return auto_encoder
+
+ # 3. Load from pretrained folder
+ print(f"[Load] Loading Pretrained Vae")
+ auto_encoder = AutoencoderKLFlux2.from_pretrained(
+ str(vae_path),
+ torch_dtype=config.data_type,
+ device_map=_device_map,
+ **pipeline_kwargs
+ )
+ Utils.trim_memory(True)
+ return auto_encoder
+
+
+#------------------------------------------------
+# Load ControlNetModel
+#------------------------------------------------
+def load_control_net(config: DataObjects.PipelineConfig, pipeline_kwargs: Dict[str, str]):
+ global _control_net_name, _control_net_cache
+
+ if _control_net_cache and _control_net_name == config.control_net.name:
+ print(f"[Load] Loading Cached ControlNet")
+ return _control_net_cache
+
+ if config.control_net.name is None:
+ _control_net_name = None
+ _control_net_cache = None
+ return None
+
+ # print(f"[Load] Loading Pretrained ControlNet")
+ # _control_net_name = config.control_net.name
+ # _control_net_cache = ControlNetModel.from_pretrained(
+ # config.control_net.path,
+ # torch_dtype=config.data_type,
+ # device_map=_device_map,
+ # **pipeline_kwargs
+ # )
+ return _control_net_cache
+
+
+#------------------------------------------------
+# Create a new pipeline
+#------------------------------------------------
+def create_pipeline(config: DataObjects.PipelineConfig):
+ template_path: Path = _model_config["template"]
+ pipeline_kwargs = {
+ "variant": config.variant,
+ "use_safetensors":True,
+ "low_cpu_mem_usage":True,
+ "local_files_only":True,
+ }
+
+ # Load Models
+ tokenizer = load_tokenizer(config, pipeline_kwargs)
+ text_encoder = load_text_encoder(config, pipeline_kwargs)
+ transformer = load_transformer(config, pipeline_kwargs)
+ unconditional_transformer = load_unconditional_transformer(config, pipeline_kwargs)
+ vae = load_vae(config, pipeline_kwargs)
+ control_net = load_control_net(config, pipeline_kwargs)
+ if control_net is not None:
+ pipeline_kwargs.update({"controlnet": control_net})
+
+ # Build Pipeline
+ pipeline = _pipelineMap[_processType]
+ return pipeline.from_pretrained(
+ template_path,
+ tokenizer=tokenizer,
+ text_encoder=text_encoder,
+ transformer=transformer,
+ unconditional_transformer=unconditional_transformer,
+ vae=vae,
+ torch_dtype=config.data_type,
+ device_map=_pipeline_device_map,
+ **pipeline_kwargs
+ )
+
+
+#------------------------------------------------
+# Generate Image/Video
+#------------------------------------------------
+def generate(
+ inference_args: Dict[str, Any],
+ input_tensors: Optional[List[Tuple[Sequence[float],Sequence[int]]]] = None,
+ control_tensors: Optional[List[Tuple[Sequence[float],Sequence[int]]]] = None,
+ ) -> Sequence[Buffer]:
+ global _prompt_cache_key, _prompt_cache_value, _stopwatch
+ _cancel_event.clear()
+ _pipeline._interrupt = False
+ _stopwatch = Utils.Stopwatch()
+ _stopwatch.start()
+
+ # Input Images
+ images = Utils.prepare_images(input_tensors)
+ image_count = Utils.get_len(images)
+ control_images = Utils.prepare_images(control_tensors)
+ control_image_count = Utils.get_len(control_images)
+ print(f"[Generate] Input Received - Tensors: {image_count}, Control Tensors: {control_image_count}")
+
+ # Options
+ options = DataObjects.PipelineOptions(**inference_args)
+
+ # Scheduler
+ _pipeline.scheduler = Utils.create_scheduler(options.scheduler_options)
+
+ # AutoEncoder
+ Utils.configure_vae_memory(_pipeline, options.enable_vae_tiling, options.enable_vae_slicing)
+
+ # Lora Adapters
+ Utils.set_lora_weights(_pipeline, options)
+
+ # Notify
+ Utils.notification_push(key="Generate", subkey="Initialize", elapsed=_stopwatch.reset())
+
+ # Prompt Cache
+
+ # Notify
+ Utils.notification_push(key="Generate", subkey="Encode", elapsed=_stopwatch.reset())
+
+ # Pipeline Options
+ pipeline_options = {
+ "prompt": options.prompt,
+ "height": options.height,
+ "width": options.width,
+ "generator": _generator.manual_seed(options.seed),
+ #"guidance_scale": options.guidance_scale,
+ "num_inference_steps": options.steps,
+ "output_type": "np",
+ "callback_on_step_end": _progress_callback,
+ "callback_on_step_end_tensor_inputs": ["latents"],
+ }
+
+ # Run Pipeline
+ output = _pipeline(**pipeline_options)[0]
+
+ # (Batch, Channel, Height, Width)
+ output = output.transpose(0, 3, 1, 2).astype(np.float32)
+
+ # Notify
+ Utils.notification_push(key="Generate", subkey="Decode", elapsed = _stopwatch.reset())
+ Utils.notification_push(key="Generate", subkey="Complete", elapsed = _stopwatch.stop())
+
+ # Cleanup
+ Utils.trim_memory(_isMemoryOffload)
+ return [ np.ascontiguousarray(output) ]
\ No newline at end of file
diff --git a/TensorStack.Python/Pipelines/Templates/Ideogram4/model_index.json b/TensorStack.Python/Pipelines/Templates/Ideogram4/model_index.json
new file mode 100644
index 0000000..93aa0b4
--- /dev/null
+++ b/TensorStack.Python/Pipelines/Templates/Ideogram4/model_index.json
@@ -0,0 +1,28 @@
+{
+ "_class_name": "Ideogram4Pipeline",
+ "_diffusers_version": "0.39.0.dev0",
+ "scheduler": [
+ "diffusers",
+ "FlowMatchEulerDiscreteScheduler"
+ ],
+ "text_encoder": [
+ "transformers",
+ "Qwen3VLModel"
+ ],
+ "tokenizer": [
+ "transformers",
+ "Qwen2Tokenizer"
+ ],
+ "transformer": [
+ "diffusers",
+ "Ideogram4Transformer2DModel"
+ ],
+ "unconditional_transformer": [
+ "diffusers",
+ "Ideogram4Transformer2DModel"
+ ],
+ "vae": [
+ "diffusers",
+ "AutoencoderKLFlux2"
+ ]
+}
diff --git a/TensorStack.Python/Pipelines/Templates/Ideogram4/scheduler/scheduler_config.json b/TensorStack.Python/Pipelines/Templates/Ideogram4/scheduler/scheduler_config.json
new file mode 100644
index 0000000..33c1410
--- /dev/null
+++ b/TensorStack.Python/Pipelines/Templates/Ideogram4/scheduler/scheduler_config.json
@@ -0,0 +1,18 @@
+{
+ "_class_name": "FlowMatchEulerDiscreteScheduler",
+ "_diffusers_version": "0.39.0.dev0",
+ "base_image_seq_len": 256,
+ "base_shift": 0.5,
+ "invert_sigmas": false,
+ "max_image_seq_len": 4096,
+ "max_shift": 1.15,
+ "num_train_timesteps": 1000,
+ "shift": 1.0,
+ "shift_terminal": null,
+ "stochastic_sampling": false,
+ "time_shift_type": "exponential",
+ "use_beta_sigmas": false,
+ "use_dynamic_shifting": false,
+ "use_exponential_sigmas": false,
+ "use_karras_sigmas": false
+}
diff --git a/TensorStack.Python/Pipelines/Templates/Ideogram4/text_encoder/config.json b/TensorStack.Python/Pipelines/Templates/Ideogram4/text_encoder/config.json
new file mode 100644
index 0000000..180cbe5
--- /dev/null
+++ b/TensorStack.Python/Pipelines/Templates/Ideogram4/text_encoder/config.json
@@ -0,0 +1,65 @@
+{
+ "architectures": [
+ "Qwen3VLModel"
+ ],
+ "dtype": "bfloat16",
+ "image_token_id": 151655,
+ "model_type": "qwen3_vl",
+ "text_config": {
+ "attention_bias": false,
+ "attention_dropout": 0.0,
+ "bos_token_id": 151643,
+ "dtype": "bfloat16",
+ "eos_token_id": 151645,
+ "head_dim": 128,
+ "hidden_act": "silu",
+ "hidden_size": 4096,
+ "initializer_range": 0.02,
+ "intermediate_size": 12288,
+ "max_position_embeddings": 262144,
+ "model_type": "qwen3_vl_text",
+ "num_attention_heads": 32,
+ "num_hidden_layers": 36,
+ "num_key_value_heads": 8,
+ "pad_token_id": null,
+ "rms_norm_eps": 1e-06,
+ "rope_parameters": {
+ "mrope_interleaved": true,
+ "mrope_section": [
+ 24,
+ 20,
+ 20
+ ],
+ "rope_theta": 5000000,
+ "rope_type": "default"
+ },
+ "use_cache": true,
+ "vocab_size": 151936
+ },
+ "tie_word_embeddings": false,
+ "transformers_version": "5.8.0",
+ "video_token_id": 151656,
+ "vision_config": {
+ "deepstack_visual_indexes": [
+ 8,
+ 16,
+ 24
+ ],
+ "depth": 27,
+ "dtype": "bfloat16",
+ "hidden_act": "gelu_pytorch_tanh",
+ "hidden_size": 1152,
+ "in_channels": 3,
+ "initializer_range": 0.02,
+ "intermediate_size": 4304,
+ "model_type": "qwen3_vl_vision",
+ "num_heads": 16,
+ "num_position_embeddings": 2304,
+ "out_hidden_size": 4096,
+ "patch_size": 16,
+ "spatial_merge_size": 2,
+ "temporal_patch_size": 2
+ },
+ "vision_end_token_id": 151653,
+ "vision_start_token_id": 151652
+}
\ No newline at end of file
diff --git a/TensorStack.Python/Pipelines/Templates/Ideogram4/tokenizer/tokenizer_config.json b/TensorStack.Python/Pipelines/Templates/Ideogram4/tokenizer/tokenizer_config.json
new file mode 100644
index 0000000..2531cbf
--- /dev/null
+++ b/TensorStack.Python/Pipelines/Templates/Ideogram4/tokenizer/tokenizer_config.json
@@ -0,0 +1,30 @@
+{
+ "add_prefix_space": false,
+ "backend": "tokenizers",
+ "bos_token": null,
+ "clean_up_tokenization_spaces": false,
+ "eos_token": "<|im_end|>",
+ "errors": "replace",
+ "extra_special_tokens": [
+ "<|im_start|>",
+ "<|im_end|>",
+ "<|object_ref_start|>",
+ "<|object_ref_end|>",
+ "<|box_start|>",
+ "<|box_end|>",
+ "<|quad_start|>",
+ "<|quad_end|>",
+ "<|vision_start|>",
+ "<|vision_end|>",
+ "<|vision_pad|>",
+ "<|image_pad|>",
+ "<|video_pad|>"
+ ],
+ "is_local": true,
+ "local_files_only": false,
+ "model_max_length": 262144,
+ "pad_token": "<|endoftext|>",
+ "split_special_tokens": false,
+ "tokenizer_class": "Qwen2Tokenizer",
+ "unk_token": null
+}
diff --git a/TensorStack.Python/Pipelines/Templates/Ideogram4/transformer/config.json b/TensorStack.Python/Pipelines/Templates/Ideogram4/transformer/config.json
new file mode 100644
index 0000000..ef3ed30
--- /dev/null
+++ b/TensorStack.Python/Pipelines/Templates/Ideogram4/transformer/config.json
@@ -0,0 +1,18 @@
+{
+ "_class_name": "Ideogram4Transformer2DModel",
+ "_diffusers_version": "0.39.0.dev0",
+ "adaln_dim": 512,
+ "attention_head_dim": 256,
+ "in_channels": 128,
+ "intermediate_size": 12288,
+ "llm_features_dim": 53248,
+ "mrope_section": [
+ 24,
+ 20,
+ 20
+ ],
+ "norm_eps": 0.00001,
+ "num_attention_heads": 18,
+ "num_layers": 34,
+ "rope_theta": 5000000
+}
diff --git a/TensorStack.Python/Pipelines/Templates/Ideogram4/unconditional_transformer/config.json b/TensorStack.Python/Pipelines/Templates/Ideogram4/unconditional_transformer/config.json
new file mode 100644
index 0000000..ef3ed30
--- /dev/null
+++ b/TensorStack.Python/Pipelines/Templates/Ideogram4/unconditional_transformer/config.json
@@ -0,0 +1,18 @@
+{
+ "_class_name": "Ideogram4Transformer2DModel",
+ "_diffusers_version": "0.39.0.dev0",
+ "adaln_dim": 512,
+ "attention_head_dim": 256,
+ "in_channels": 128,
+ "intermediate_size": 12288,
+ "llm_features_dim": 53248,
+ "mrope_section": [
+ 24,
+ 20,
+ 20
+ ],
+ "norm_eps": 0.00001,
+ "num_attention_heads": 18,
+ "num_layers": 34,
+ "rope_theta": 5000000
+}
diff --git a/TensorStack.Python/Pipelines/Templates/Ideogram4/vae/config.json b/TensorStack.Python/Pipelines/Templates/Ideogram4/vae/config.json
new file mode 100644
index 0000000..9d324c9
--- /dev/null
+++ b/TensorStack.Python/Pipelines/Templates/Ideogram4/vae/config.json
@@ -0,0 +1,40 @@
+{
+ "_class_name": "AutoencoderKLFlux2",
+ "_diffusers_version": "0.39.0.dev0",
+ "act_fn": "silu",
+ "batch_norm_eps": 0.0001,
+ "batch_norm_momentum": 0.1,
+ "block_out_channels": [
+ 128,
+ 256,
+ 512,
+ 512
+ ],
+ "decoder_block_out_channels": null,
+ "down_block_types": [
+ "DownEncoderBlock2D",
+ "DownEncoderBlock2D",
+ "DownEncoderBlock2D",
+ "DownEncoderBlock2D"
+ ],
+ "force_upcast": true,
+ "in_channels": 3,
+ "latent_channels": 32,
+ "layers_per_block": 2,
+ "mid_block_add_attention": true,
+ "norm_num_groups": 32,
+ "out_channels": 3,
+ "patch_size": [
+ 2,
+ 2
+ ],
+ "sample_size": 1024,
+ "up_block_types": [
+ "UpDecoderBlock2D",
+ "UpDecoderBlock2D",
+ "UpDecoderBlock2D",
+ "UpDecoderBlock2D"
+ ],
+ "use_post_quant_conv": true,
+ "use_quant_conv": true
+}
diff --git a/TensorStack.Python/TensorStack.Python.csproj b/TensorStack.Python/TensorStack.Python.csproj
index 21fd455..cfc1f42 100644
--- a/TensorStack.Python/TensorStack.Python.csproj
+++ b/TensorStack.Python/TensorStack.Python.csproj
@@ -105,6 +105,12 @@
+
+
+
+
+
+