重构配置逻辑

This commit is contained in:
puke
2025-10-27 22:28:36 +08:00
committed by puke
parent 363aefed4a
commit f2551b5f9c
12 changed files with 296 additions and 583 deletions

2
.gitignore vendored
View File

@@ -41,6 +41,8 @@ Thumbs.db
# Config files with sensitive data
config.yaml
config.yaml.bak
*.yaml.bak
.env
# Logs

View File

@@ -1,108 +1,29 @@
# ReelForge Configuration Example
# 复制此文件为 config.yaml 并填入你的配置
# ReelForge Configuration
# Copy this file to config.yaml and fill in your settings
# ⚠️ Never commit config.yaml to Git!
# Project name
project_name: ReelForge
# ==================== LLM Configuration ====================
# Simple 3-field configuration - works with any OpenAI SDK compatible LLM
#
# Popular choices (copy one of these):
#
# Qwen Max (推荐中文):
# api_key: "sk-xxx"
# base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1"
# model: "qwen-max"
# # Get API key: https://dashscope.console.aliyun.com/apiKey
#
# OpenAI GPT-4o:
# api_key: "sk-xxx"
# base_url: "https://api.openai.com/v1"
# model: "gpt-4o"
# # Get API key: https://platform.openai.com/api-keys
#
# Claude Sonnet 4:
# api_key: "sk-ant-xxx"
# base_url: "https://api.anthropic.com/v1/"
# model: "claude-sonnet-4-5"
# # Get API key: https://console.anthropic.com/settings/keys
#
# DeepSeek (超高性价比):
# api_key: "sk-xxx"
# base_url: "https://api.deepseek.com"
# model: "deepseek-chat"
# # Get API key: https://platform.deepseek.com/api_keys
#
# Ollama (本地免费):
# api_key: "ollama" # Any value works
# base_url: "http://localhost:11434/v1"
# model: "llama3.2"
# # Install: https://ollama.com/download
# # Then: ollama pull llama3.2
#
# Supports any OpenAI SDK compatible API
llm:
api_key: "" # Fill in your API key
base_url: "" # LLM API endpoint
model: "" # Model name
api_key: ""
base_url: ""
model: ""
# Popular presets:
# Qwen Max: base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1" model: "qwen-max"
# OpenAI GPT-4o: base_url: "https://api.openai.com/v1" model: "gpt-4o"
# DeepSeek: base_url: "https://api.deepseek.com" model: "deepseek-chat"
# Ollama (Local): base_url: "http://localhost:11434/v1" model: "llama3.2"
# ==================== TTS Configuration ====================
# TTS supports two modes:
# 1. Edge TTS (default) - Free local SDK, no setup needed
# 2. ComfyUI Workflow - Workflow-based, requires ComfyUI
#
# Configuration (optional):
tts:
default_workflow: "edge" # Default: "edge" (Edge TTS) or "tts_default.json" (ComfyUI workflow)
# comfyui_url: http://127.0.0.1:8188 # Only needed for ComfyUI workflows
# Usage in code:
# await reelforge.tts(text="hello") # Uses default (edge)
# await reelforge.tts(text="hello", workflow="edge") # Explicitly use Edge TTS
# await reelforge.tts(text="hello", workflow="tts_custom.json") # Use ComfyUI workflow
default: edge # "edge" (free) or "tts_xxx.json" (ComfyUI workflow)
# ==================== Image Generation Configuration ====================
# Image generation uses ComfyUI workflows
# Workflows are auto-discovered from workflows/image_*.json files
image:
default: default # Default preset name (uses workflows/image_default.json)
comfyui_url: http://127.0.0.1:8188 # Local ComfyUI server
# runninghub_api_key: "" # Optional: RunningHub cloud API key
# Prompt prefix - automatically added to all image prompts
# Leave empty ("") if you don't want any prefix
default: image_default.json
comfyui_url: http://127.0.0.1:8188
runninghub_api_key: ""
prompt_prefix: "Pure white background, minimalist illustration, matchstick figure style, black and white line drawing, simple clean lines"
# Common examples:
# prompt_prefix: "" # No prefix
# prompt_prefix: "anime style, vibrant colors, cel shading" # Anime style
# prompt_prefix: "watercolor painting, soft edges, artistic" # Watercolor
# prompt_prefix: "photorealistic, 8k, professional photography" # Realistic
# ==================== Notes ====================
# 1. LLM Configuration:
# - Simple 3-field config: api_key, base_url, model
# - Works with ANY OpenAI SDK compatible LLM
# - Popular choices listed in comments above
# - Switch LLM: just copy-paste different values from comments
# - WebUI provides quick preset selection
#
# 2. TTS Configuration:
# - Two modes: Edge TTS (default, free) or ComfyUI Workflow
# - Edge TTS: No setup needed, just use default
# - ComfyUI: Create workflow files in workflows/tts_*.json
# - Override in code: await reelforge.tts(text="...", workflow="edge" or "tts_xxx.json")
#
# 3. Image Generation:
# - Add workflow files: workflows/image_*.json
# - Auto-discovered presets: workflows/image_flux.json -> preset="flux"
# - Default preset: workflows/image_default.json
#
# 4. Ollama (Recommended for Privacy):
# - FREE: No API costs
# - PRIVATE: Data never leaves your machine
# - Install: https://ollama.com/download
# - Usage: ollama pull llama3.2
#
# 5. Security:
# - Never commit config.yaml to version control
# - All sensitive data (API keys) should stay local

View File

@@ -1,7 +1,7 @@
"""
ReelForge - AI-powered video generator with pluggable capabilities
ReelForge - AI-powered video generator
Convention-based capability system using FastMCP and LiteLLM.
Convention-based system with unified configuration management.
Usage:
from reelforge import reelforge
@@ -18,8 +18,9 @@ Usage:
"""
from reelforge.service import ReelForgeCore, reelforge
from reelforge.config import config_manager
__version__ = "0.1.0"
__all__ = ["ReelForgeCore", "reelforge"]
__all__ = ["ReelForgeCore", "reelforge", "config_manager"]

View File

@@ -1,80 +0,0 @@
"""
Configuration system for ReelForge
"""
import os
from pathlib import Path
from typing import Any
import yaml
from loguru import logger
def load_config(config_path: str = "config.yaml") -> dict[str, Any]:
"""
Load configuration from YAML file
User-friendly flat format (no internal conversion):
project_name: ReelForge
llm:
api_key: xxx
base_url: xxx
model: xxx
tts:
default: edge
edge: null
image:
default: comfykit
comfykit:
comfyui_url: http://xxx
Args:
config_path: Path to config file (default: config.yaml)
Returns:
Configuration dict (as-is from YAML)
"""
# Check if config file exists
config_file = Path(config_path)
if not config_file.exists():
logger.warning(f"Config file not found: {config_path}")
logger.info("Using default configuration")
return _get_default_config()
# Load config
logger.info(f"Loading config from: {config_path}")
with open(config_file, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
# Handle None (empty YAML file)
if config is None:
config = {}
# Ensure project_name exists
if "project_name" not in config:
config["project_name"] = "ReelForge"
return config
def _get_default_config() -> dict[str, Any]:
"""Get default configuration"""
return {
"project_name": "ReelForge",
"llm": {
"api_key": "",
"base_url": "",
"model": ""
},
"tts": {
"default": "edge",
"edge": None
},
"image": {
"default": "comfykit",
"comfykit": {
"comfyui_url": "http://127.0.0.1:8188"
}
}
}

View File

@@ -0,0 +1,37 @@
"""
ReelForge Configuration System
Unified configuration management with Pydantic validation.
Usage:
from reelforge.config import config_manager
# Access config (type-safe)
api_key = config_manager.config.llm.api_key
# Update config
config_manager.update({"llm": {"api_key": "xxx"}})
config_manager.save()
# Validate
if config_manager.validate():
print("Config is valid!")
"""
from .schema import ReelForgeConfig, LLMConfig, TTSConfig, ImageConfig
from .manager import ConfigManager
from .loader import load_config_dict, save_config_dict
# Global singleton instance
config_manager = ConfigManager()
__all__ = [
"ReelForgeConfig",
"LLMConfig",
"TTSConfig",
"ImageConfig",
"ConfigManager",
"config_manager",
"load_config_dict",
"save_config_dict",
]

View File

@@ -0,0 +1,53 @@
"""
Configuration loader - Pure YAML
Handles loading and saving configuration from/to YAML files.
"""
from pathlib import Path
import yaml
from loguru import logger
def load_config_dict(config_path: str = "config.yaml") -> dict:
"""
Load configuration from YAML file
Args:
config_path: Path to config file
Returns:
Configuration dictionary
"""
config_file = Path(config_path)
if not config_file.exists():
logger.warning(f"Config file not found: {config_path}")
logger.info("Using default configuration")
return {}
try:
with open(config_file, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f) or {}
logger.info(f"Configuration loaded from {config_path}")
return data
except Exception as e:
logger.error(f"Failed to load config: {e}")
return {}
def save_config_dict(config: dict, config_path: str = "config.yaml"):
"""
Save configuration to YAML file
Args:
config: Configuration dictionary
config_path: Path to config file
"""
try:
with open(config_path, 'w', encoding='utf-8') as f:
yaml.dump(config, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
logger.info(f"Configuration saved to {config_path}")
except Exception as e:
logger.error(f"Failed to save config: {e}")
raise

119
reelforge/config/manager.py Normal file
View File

@@ -0,0 +1,119 @@
"""
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 ReelForgeConfig
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: ReelForgeConfig = self._load()
self._initialized = True
def _load(self) -> ReelForgeConfig:
"""Load configuration from file"""
data = load_config_dict(str(self.config_path))
return ReelForgeConfig(**data)
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 = ReelForgeConfig(**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_image_config(self) -> dict:
"""Get image configuration as dict"""
return {
"default": self.config.image.default,
"comfyui_url": self.config.image.comfyui_url,
"runninghub_api_key": self.config.image.runninghub_api_key,
"prompt_prefix": self.config.image.prompt_prefix,
}
def set_image_config(
self,
comfyui_url: Optional[str] = None,
runninghub_api_key: Optional[str] = None
):
"""Set image configuration"""
updates = {}
if comfyui_url is not None:
updates["comfyui_url"] = comfyui_url
if runninghub_api_key is not None:
updates["runninghub_api_key"] = runninghub_api_key
if updates:
self.update({"image": updates})

View File

@@ -0,0 +1,54 @@
"""
Configuration schema with Pydantic models
Single source of truth for all configuration defaults and validation.
"""
from pydantic import BaseModel, Field
class LLMConfig(BaseModel):
"""LLM configuration"""
api_key: str = Field(default="", description="LLM API Key")
base_url: str = Field(default="", description="LLM API Base URL")
model: str = Field(default="", description="LLM Model Name")
class TTSConfig(BaseModel):
"""TTS configuration"""
default: str = Field(default="edge", description="Default TTS workflow")
class ImageConfig(BaseModel):
"""Image generation configuration"""
default: str = Field(default="image_default.json", description="Default image workflow")
comfyui_url: str = Field(default="http://127.0.0.1:8188", description="ComfyUI Server URL")
runninghub_api_key: str = Field(default="", description="RunningHub API Key (optional)")
prompt_prefix: str = Field(
default="Pure white background, minimalist illustration, matchstick figure style, black and white line drawing, simple clean lines",
description="Prompt prefix for all image generation"
)
class ReelForgeConfig(BaseModel):
"""ReelForge main configuration"""
project_name: str = Field(default="ReelForge", description="Project name")
llm: LLMConfig = Field(default_factory=LLMConfig)
tts: TTSConfig = Field(default_factory=TTSConfig)
image: ImageConfig = Field(default_factory=ImageConfig)
def is_llm_configured(self) -> bool:
"""Check if LLM is properly configured"""
return bool(
self.llm.api_key and self.llm.api_key.strip() and
self.llm.base_url and self.llm.base_url.strip() and
self.llm.model and self.llm.model.strip()
)
def validate_required(self) -> bool:
"""Validate required configuration"""
return self.is_llm_configured()
def to_dict(self) -> dict:
"""Convert to dictionary (for backward compatibility)"""
return self.model_dump()

View File

@@ -1,269 +0,0 @@
"""
Configuration manager for WebUI
Handles loading, saving, and validating configuration
from the web interface.
"""
from pathlib import Path
from typing import Dict, Any, Optional, List
import yaml
from loguru import logger
class ConfigStatus:
"""Configuration validation status"""
def __init__(self):
self.is_complete: bool = True
self.missing_fields: List[str] = []
self.warnings: List[str] = []
def add_missing(self, field: str):
"""Add a missing required field"""
self.is_complete = False
self.missing_fields.append(field)
def add_warning(self, message: str):
"""Add a warning message"""
self.warnings.append(message)
class ConfigManager:
"""Manage configuration for WebUI"""
def __init__(self, config_path: str = "config.yaml"):
self.config_path = Path(config_path)
self.config: Optional[Dict[str, Any]] = None
def load_or_create_default(self) -> Dict[str, Any]:
"""
Load config from file, or create default if not exists
Returns:
Configuration dictionary
"""
if not self.config_path.exists():
logger.warning("Config file not found, creating default")
self.config = self._create_default_config()
self.save()
else:
with open(self.config_path, "r", encoding="utf-8") as f:
self.config = yaml.safe_load(f)
logger.info(f"Configuration loaded from {self.config_path}")
return self.config
def save(self):
"""Save config to file"""
with open(self.config_path, "w", encoding="utf-8") as f:
yaml.dump(self.config, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
logger.info(f"Configuration saved to {self.config_path}")
def update_from_ui(self, ui_values: Dict[str, Any]):
"""
Update config from UI form values
Args:
ui_values: Dictionary of form values from Streamlit
"""
# Update LLM configuration
if "llm_provider" in ui_values:
provider = ui_values["llm_provider"]
self.config["llm"]["default"] = provider
# Ensure provider config exists
if provider not in self.config["llm"]:
self.config["llm"][provider] = {}
# Update provider-specific config
if f"llm_{provider}_api_key" in ui_values and ui_values[f"llm_{provider}_api_key"]:
self.config["llm"][provider]["api_key"] = ui_values[f"llm_{provider}_api_key"]
if f"llm_{provider}_base_url" in ui_values and ui_values[f"llm_{provider}_base_url"]:
self.config["llm"][provider]["base_url"] = ui_values[f"llm_{provider}_base_url"]
if f"llm_{provider}_model" in ui_values and ui_values[f"llm_{provider}_model"]:
self.config["llm"][provider]["model"] = ui_values[f"llm_{provider}_model"]
# Update TTS configuration
if "tts_provider" in ui_values:
self.config["tts"]["default"] = ui_values["tts_provider"]
# Update Image configuration
if "image_provider" in ui_values:
self.config["image"]["default"] = ui_values["image_provider"]
# Update ComfyKit configuration
if "comfykit_mode" in ui_values:
if "comfykit" not in self.config["image"]:
self.config["image"]["comfykit"] = {}
mode = ui_values["comfykit_mode"]
if mode == "local":
if "comfyui_url" in ui_values:
self.config["image"]["comfykit"]["comfyui_url"] = ui_values["comfyui_url"]
# Remove cloud config
self.config["image"]["comfykit"].pop("runninghub_api_key", None)
else: # cloud
if "runninghub_api_key" in ui_values:
self.config["image"]["comfykit"]["runninghub_api_key"] = ui_values["runninghub_api_key"]
# Remove local config
self.config["image"]["comfykit"].pop("comfyui_url", None)
self.save()
def _is_valid_api_key(self, api_key: str) -> bool:
"""
Check if an API key is valid (not empty or placeholder)
Args:
api_key: API key to validate
Returns:
True if valid, False if empty or placeholder
"""
if not api_key:
return False
# Remove whitespace
api_key = api_key.strip()
if not api_key:
return False
# Common placeholders to reject
invalid_patterns = [
"your_", # your_openai_api_key_here, your_dashscope_api_key_here
"replace", # replace_me, replace_with_your_key
"placeholder",
"example_key",
]
api_key_lower = api_key.lower()
for pattern in invalid_patterns:
if pattern in api_key_lower:
return False
# Accept any non-empty string (allow short keys for testing/custom services)
return True
def validate(self) -> ConfigStatus:
"""
Validate configuration completeness (simple 3-field format)
Returns:
ConfigStatus with validation results
"""
status = ConfigStatus()
# Check LLM configuration (simple format)
llm_config = self.config.get("llm", {})
# Check API key
api_key = llm_config.get("api_key", "")
if not self._is_valid_api_key(api_key):
status.add_missing("llm.api_key")
# Check base_url
base_url = llm_config.get("base_url", "")
if not base_url or not base_url.strip():
status.add_missing("llm.base_url")
# Check model
model = llm_config.get("model", "")
if not model or not model.strip():
status.add_missing("llm.model")
return status
def get_comfykit_mode(self) -> str:
"""
Get current ComfyKit mode
Returns:
"local" or "cloud"
"""
comfykit_config = self.config.get("image", {}).get("comfykit", {})
if comfykit_config.get("runninghub_api_key"):
return "cloud"
else:
return "local"
def get_llm_config(self) -> Dict[str, str]:
"""
Get LLM configuration (simple 3-field format)
Returns:
Dict with api_key, base_url, model
"""
if self.config is None:
return {"api_key": "", "base_url": "", "model": ""}
llm_config = self.config.get("llm", {})
return {
"api_key": llm_config.get("api_key", ""),
"base_url": llm_config.get("base_url", ""),
"model": llm_config.get("model", ""),
}
def set_llm_config(self, api_key: str, base_url: str, model: str):
"""
Set LLM configuration
Args:
api_key: API key
base_url: Base URL
model: Model name
"""
self.config["llm"] = {
"api_key": api_key,
"base_url": base_url,
"model": model,
}
def is_llm_configured(self) -> bool:
"""
Check if LLM is configured
Returns:
True if all three fields are non-empty
"""
llm_config = self.get_llm_config()
return bool(
llm_config["api_key"] and llm_config["api_key"].strip() and
llm_config["base_url"] and llm_config["base_url"].strip() and
llm_config["model"] and llm_config["model"].strip()
)
def get_tts_providers(self) -> List[str]:
"""Get list of available TTS providers"""
return ["edge"]
def get_image_providers(self) -> List[str]:
"""Get list of available image providers"""
return ["comfykit"]
def _create_default_config(self) -> Dict[str, Any]:
"""Create default configuration"""
return {
"project_name": "ReelForge",
"llm": {
"api_key": "", # User must fill in
"base_url": "", # User must fill in
"model": "", # User must fill in
},
"tts": {
"default": "edge", # Edge TTS is free
"edge": {}
},
"image": {
"default": "comfykit",
"comfykit": {
"comfyui_url": "http://127.0.0.1:8188"
}
},
"mcp_servers": []
}

View File

@@ -8,7 +8,7 @@ from typing import Optional
from loguru import logger
from reelforge.config import load_config
from reelforge.config import config_manager
from reelforge.services.llm_service import LLMService
from reelforge.services.tts_service import TTSService
from reelforge.services.image import ImageService
@@ -55,7 +55,8 @@ class ReelForgeCore:
Args:
config_path: Path to configuration file
"""
self.config = load_config(config_path)
# Use global config manager singleton
self.config = config_manager.config.to_dict()
self._initialized = False
# Core services (initialized in initialize())

View File

@@ -1,114 +0,0 @@
"""
Lightweight configuration utility for Web UI
Simple wrapper around config.yaml without heavy dependencies.
"""
from pathlib import Path
from typing import Dict, Any, Optional
import yaml
from loguru import logger
class WebConfig:
"""Lightweight configuration manager for Web UI"""
def __init__(self, config_path: str = "config.yaml"):
self.config_path = Path(config_path)
self.config: Dict[str, Any] = {}
self.load()
def load(self):
"""Load configuration from file"""
if not self.config_path.exists():
logger.warning(f"Config file not found: {self.config_path}, using default")
self.config = self._create_default_config()
self.save()
else:
try:
with open(self.config_path, "r", encoding="utf-8") as f:
self.config = yaml.safe_load(f) or {}
logger.info(f"Configuration loaded from {self.config_path}")
except Exception as e:
logger.error(f"Failed to load config: {e}")
self.config = self._create_default_config()
def save(self):
"""Save configuration to file"""
try:
with open(self.config_path, "w", encoding="utf-8") as f:
yaml.dump(self.config, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
logger.info(f"Configuration saved to {self.config_path}")
except Exception as e:
logger.error(f"Failed to save config: {e}")
def validate(self) -> bool:
"""
Validate configuration completeness
Returns:
True if required fields are present
"""
# Check LLM configuration (required)
llm_config = self.config.get("llm", {})
if not all([
llm_config.get("api_key"),
llm_config.get("base_url"),
llm_config.get("model")
]):
return False
return True
def get_llm_config(self) -> Dict[str, str]:
"""Get LLM configuration"""
llm = self.config.get("llm", {})
return {
"api_key": llm.get("api_key", ""),
"base_url": llm.get("base_url", ""),
"model": llm.get("model", "")
}
def set_llm_config(self, api_key: str, base_url: str, model: str):
"""Set LLM configuration"""
if "llm" not in self.config:
self.config["llm"] = {}
self.config["llm"]["api_key"] = api_key
self.config["llm"]["base_url"] = base_url
self.config["llm"]["model"] = model
def get_image_config(self) -> Dict[str, Any]:
"""Get image generation configuration"""
return self.config.get("image", {})
def set_image_config(self, comfyui_url: Optional[str] = None, runninghub_api_key: Optional[str] = None):
"""Set image generation configuration"""
if "image" not in self.config:
self.config["image"] = {}
if comfyui_url is not None:
self.config["image"]["comfyui_url"] = comfyui_url
if runninghub_api_key is not None:
self.config["image"]["runninghub_api_key"] = runninghub_api_key
def _create_default_config(self) -> Dict[str, Any]:
"""Create default configuration"""
return {
"project_name": "ReelForge",
"llm": {
"api_key": "",
"base_url": "",
"model": ""
},
"tts": {
"default_workflow": "edge"
},
"image": {
"comfyui_url": "http://127.0.0.1:8188",
"runninghub_api_key": "",
"prompt_prefix": "Pure white background, minimalist illustration, matchstick figure style, black and white line drawing, simple clean lines"
}
}

26
web.py
View File

@@ -14,7 +14,7 @@ from loguru import logger
# Import i18n and config manager
from reelforge.i18n import load_locales, set_language, tr, get_available_languages
from reelforge.utils.web_config import WebConfig
from reelforge.config import config_manager
from reelforge.models.progress import ProgressEvent
# Setup page config (must be first)
@@ -47,9 +47,7 @@ def safe_rerun():
# Configuration & i18n Initialization
# ============================================================================
def get_config_manager():
"""Get WebConfig instance (no caching - always fresh)"""
return WebConfig()
# Config manager is already a global singleton, use it directly
def init_i18n():
@@ -127,7 +125,7 @@ def init_session_state():
# System Configuration (Required)
# ============================================================================
def render_advanced_settings(config_manager: WebConfig):
def render_advanced_settings():
"""Render system configuration (required) with 2-column layout"""
# Check if system is configured
is_configured = config_manager.validate()
@@ -300,17 +298,9 @@ def render_advanced_settings(config_manager: WebConfig):
with col2:
if st.button(tr("btn.reset_config"), use_container_width=True, key="reset_config_btn"):
# Reset to default by creating new config
config_manager.config = {
"project_name": "ReelForge",
"llm": {"api_key": "", "base_url": "", "model": ""},
"tts": {"default_workflow": "edge"},
"image": {
"comfyui_url": "http://127.0.0.1:8188",
"runninghub_api_key": "",
"prompt_prefix": "Pure white background, minimalist illustration, matchstick figure style, black and white line drawing, simple clean lines"
}
}
# Reset to default
from reelforge.config.schema import ReelForgeConfig
config_manager.config = ReelForgeConfig()
config_manager.save()
st.success(tr("status.config_reset"))
safe_rerun()
@@ -351,8 +341,6 @@ def main():
init_session_state()
init_i18n()
config_manager = get_config_manager()
# Top bar: Title + Language selector
col1, col2 = st.columns([4, 1])
with col1:
@@ -367,7 +355,7 @@ def main():
# System Configuration (Required)
# Auto-expands if not configured, collapses if configured
# ========================================================================
render_advanced_settings(config_manager)
render_advanced_settings()
# Three-column layout
left_col, middle_col, right_col = st.columns([1, 1, 1])