| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- # Copyright (C) 2025 AIDC-AI
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- # http://www.apache.org/licenses/LICENSE-2.0
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """
- Configuration Manager - Singleton pattern
- Provides unified access to configuration with automatic validation.
- """
- from pathlib import Path
- from typing import Any, Optional
- from loguru import logger
- from .schema import PixelleVideoConfig
- from .loader import load_config_dict, save_config_dict
- class ConfigManager:
- """
- Configuration Manager (Singleton)
-
- Provides unified access to configuration with automatic validation.
- """
- _instance: Optional['ConfigManager'] = None
-
- def __new__(cls, config_path: str = "config.yaml"):
- if cls._instance is None:
- cls._instance = super().__new__(cls)
- return cls._instance
-
- def __init__(self, config_path: str = "config.yaml"):
- # Only initialize once
- if hasattr(self, '_initialized'):
- return
-
- self.config_path = Path(config_path)
- self.config: PixelleVideoConfig = self._load()
- self._initialized = True
-
- def _load(self) -> PixelleVideoConfig:
- """Load configuration from file"""
- data = load_config_dict(str(self.config_path))
- config = PixelleVideoConfig(**data)
-
- # Validate template path exists
- self._validate_template(config.template.default_template)
-
- return config
-
- def _validate_template(self, template_path: str):
- """Validate that the configured template exists"""
- from pixelle_video.utils.template_util import resolve_template_path
-
- try:
- # Try to resolve the template path
- resolved_path = resolve_template_path(template_path)
- logger.debug(f"Template validation passed: {template_path} -> {resolved_path}")
- except FileNotFoundError as e:
- logger.warning(
- f"Configured default template '{template_path}' not found. "
- f"Will fall back to '1080x1920/default.html' if needed. Error: {e}"
- )
-
- def reload(self):
- """Reload configuration from file"""
- self.config = self._load()
- logger.info("Configuration reloaded")
-
- def save(self):
- """Save current configuration to file"""
- save_config_dict(self.config.to_dict(), str(self.config_path))
-
- def update(self, updates: dict):
- """
- Update configuration with new values
-
- Args:
- updates: Dictionary of updates (e.g., {"llm": {"api_key": "xxx"}})
- """
- current = self.config.to_dict()
-
- # Deep merge
- def deep_merge(base: dict, updates: dict) -> dict:
- for key, value in updates.items():
- if key in base and isinstance(base[key], dict) and isinstance(value, dict):
- deep_merge(base[key], value)
- else:
- base[key] = value
- return base
-
- merged = deep_merge(current, updates)
- self.config = PixelleVideoConfig(**merged)
-
- def get(self, key: str, default: Any = None) -> Any:
- """Dict-like access (for backward compatibility)"""
- return self.config.to_dict().get(key, default)
-
- def validate(self) -> bool:
- """Validate configuration completeness"""
- return self.config.validate_required()
-
- def get_llm_config(self) -> dict:
- """Get LLM configuration as dict"""
- return {
- "api_key": self.config.llm.api_key,
- "base_url": self.config.llm.base_url,
- "model": self.config.llm.model,
- }
-
- def set_llm_config(self, api_key: str, base_url: str, model: str):
- """Set LLM configuration"""
- self.update({
- "llm": {
- "api_key": api_key,
- "base_url": base_url,
- "model": model,
- }
- })
-
- def get_comfyui_config(self) -> dict:
- """Get ComfyUI configuration as dict"""
- return {
- "comfyui_url": self.config.comfyui.comfyui_url,
- "comfyui_api_key": self.config.comfyui.comfyui_api_key,
- "runninghub_api_key": self.config.comfyui.runninghub_api_key,
- "runninghub_concurrent_limit": self.config.comfyui.runninghub_concurrent_limit,
- "runninghub_instance_type": self.config.comfyui.runninghub_instance_type,
- "tts": {
- "default_workflow": self.config.comfyui.tts.default_workflow,
- },
- "image": {
- "default_workflow": self.config.comfyui.image.default_workflow,
- "prompt_prefix": self.config.comfyui.image.prompt_prefix,
- },
- "video": {
- "default_workflow": self.config.comfyui.video.default_workflow,
- "prompt_prefix": self.config.comfyui.video.prompt_prefix,
- }
- }
-
- def set_comfyui_config(
- self,
- comfyui_url: Optional[str] = None,
- comfyui_api_key: Optional[str] = None,
- runninghub_api_key: Optional[str] = None,
- runninghub_concurrent_limit: Optional[int] = None,
- runninghub_instance_type: Optional[str] = None
- ):
- """Set ComfyUI global configuration"""
- updates = {}
- if comfyui_url is not None:
- updates["comfyui_url"] = comfyui_url
- if comfyui_api_key is not None:
- updates["comfyui_api_key"] = comfyui_api_key
- if runninghub_api_key is not None:
- updates["runninghub_api_key"] = runninghub_api_key
- if runninghub_concurrent_limit is not None:
- updates["runninghub_concurrent_limit"] = runninghub_concurrent_limit
- if runninghub_instance_type is not None:
- # Empty string means disable (treat as None for storage)
- updates["runninghub_instance_type"] = runninghub_instance_type if runninghub_instance_type else None
-
- if updates:
- self.update({"comfyui": updates})
|