first commit
This commit is contained in:
@@ -0,0 +1,646 @@
|
||||
"""
|
||||
Mô-đun Quản lý cấu hình - Hỗ trợ mã hóa thông tin nhạy cảm, quản lý phiên bản, xác thực
|
||||
|
||||
Bản quyền © 2026 Công ty TNHH Công nghệ An ninh mạng Huyễn Thành Tân Cương (Công nghệ Huyễn Thành)
|
||||
Tác giả: Huyễn Thành
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from typing import List, Dict, Any, Optional, Union
|
||||
from dataclasses import dataclass, asdict
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
import logging
|
||||
from locales.i18n import t
|
||||
from database import get_db
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Giới hạn kích thước tệp tối đa (50MB)
|
||||
MAX_FILE_SIZE = 50 * 1024 * 1024
|
||||
|
||||
# Các định dạng tệp cấu hình được hỗ trợ
|
||||
SUPPORTED_CONFIG_FORMATS = [".json", ".yaml", ".yml"]
|
||||
|
||||
# Cấu hình nhà cung cấp API
|
||||
API_PROVIDERS = {
|
||||
"openai": {
|
||||
"name": "OpenAI",
|
||||
"default_model": "gpt-4o-mini",
|
||||
"base_url": "https://api.openai.com/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "OpenAI API chính thức"
|
||||
},
|
||||
"openai_compatible": {
|
||||
"name": "OpenAI (Giao diện tương thích)",
|
||||
"default_model": "gpt-3.5-turbo",
|
||||
"base_url": "",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": True,
|
||||
"description": "Dịch vụ bên thứ ba tương thích với định dạng OpenAI API"
|
||||
},
|
||||
"anthropic": {
|
||||
"name": "Anthropic",
|
||||
"default_model": "claude-3-5-sonnet-20241022",
|
||||
"base_url": "https://api.anthropic.com",
|
||||
"api_key_field": "x-api-key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Mô hình Claude"
|
||||
},
|
||||
"google": {
|
||||
"name": "Google",
|
||||
"default_model": "gemini-1.5-pro",
|
||||
"base_url": "https://generativelanguage.googleapis.com",
|
||||
"api_key_field": "key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Mô hình Gemini"
|
||||
},
|
||||
"alibaba": {
|
||||
"name": "Alibaba DashScope",
|
||||
"default_model": "qwen-turbo",
|
||||
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Dòng Qwen"
|
||||
},
|
||||
"deepseek": {
|
||||
"name": "DeepSeek",
|
||||
"default_model": "deepseek-chat",
|
||||
"base_url": "https://api.deepseek.com/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "DeepSeek-V3"
|
||||
},
|
||||
"zhipu": {
|
||||
"name": "Zhipu AI",
|
||||
"default_model": "glm-4",
|
||||
"base_url": "https://open.bigmodel.cn/api/paas/v4",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Dòng GLM"
|
||||
},
|
||||
"groq": {
|
||||
"name": "Groq",
|
||||
"default_model": "llama3-70b-8192",
|
||||
"base_url": "https://api.groq.com/openai/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Llama3, Mixtral"
|
||||
},
|
||||
"together": {
|
||||
"name": "Together AI",
|
||||
"default_model": "meta-llama/Llama-3-70b-chat-hf",
|
||||
"base_url": "https://api.together.xyz/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Llama, Qwen"
|
||||
},
|
||||
"fireworks": {
|
||||
"name": "Fireworks AI",
|
||||
"default_model": "accounts/fireworks/models/llama-v3-70b-instruct",
|
||||
"base_url": "https://api.fireworks.ai/inference/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Llama, Mixtral"
|
||||
},
|
||||
"mistral": {
|
||||
"name": "Mistral AI",
|
||||
"default_model": "mistral-large-latest",
|
||||
"base_url": "https://api.mistral.ai/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Mistral Large, Pixtral"
|
||||
},
|
||||
"openrouter": {
|
||||
"name": "OpenRouter",
|
||||
"default_model": "anthropic/claude-3.5-sonnet",
|
||||
"base_url": "https://openrouter.ai/api/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Tổng hợp đa mô hình (GPT, Claude, v.v.)"
|
||||
},
|
||||
"deepinfra": {
|
||||
"name": "DeepInfra",
|
||||
"default_model": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
||||
"base_url": "https://api.deepinfra.com/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Lưu trữ mô hình mã nguồn mở"
|
||||
},
|
||||
"anyscale": {
|
||||
"name": "Anyscale Endpoints",
|
||||
"default_model": "meta-llama/Llama-3-70b-chat-hf",
|
||||
"base_url": "https://api.endpoints.anyscale.com/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Llama, Mistral"
|
||||
},
|
||||
"perplexity": {
|
||||
"name": "Perplexity AI",
|
||||
"default_model": "llama-3.1-sonar-small-128k-online",
|
||||
"base_url": "https://api.perplexity.ai",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Sonar, Llama"
|
||||
},
|
||||
"hyperbolic": {
|
||||
"name": "Hyperbolic",
|
||||
"default_model": "meta-llama/Meta-Llama-3.1-70B-Instruct",
|
||||
"base_url": "https://api.hyperbolic.xyz/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Mô hình mã nguồn mở"
|
||||
},
|
||||
"siliconflow": {
|
||||
"name": "SiliconFlow",
|
||||
"default_model": "Qwen/Qwen2.5-72B-Instruct",
|
||||
"base_url": "https://api.siliconflow.cn/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Qwen, Llama"
|
||||
},
|
||||
"moonshot": {
|
||||
"name": "Moonshot AI (Kimi)",
|
||||
"default_model": "moonshot-v1-8k",
|
||||
"base_url": "https://api.moonshot.ai/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Dòng Kimi"
|
||||
},
|
||||
"novita": {
|
||||
"name": "Novita AI",
|
||||
"default_model": "meta-llama/Meta-Llama-3.1-70B-Instruct",
|
||||
"base_url": "https://api.novita.ai/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Lưu trữ mô hình mã nguồn mở"
|
||||
},
|
||||
"baichuan": {
|
||||
"name": "Baichuan AI",
|
||||
"default_model": "Baichuan4",
|
||||
"base_url": "https://api.baichuan-ai.com/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Dòng Baichuan"
|
||||
},
|
||||
"cerebras": {
|
||||
"name": "Cerebras",
|
||||
"default_model": "llama3.1-70b",
|
||||
"base_url": "https://api.cerebras.ai/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Dòng Llama"
|
||||
},
|
||||
"sambanova": {
|
||||
"name": "SambaNova",
|
||||
"default_model": "Meta-Llama-3.1-70B-Instruct",
|
||||
"base_url": "https://api.sambanova.ai/v1",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Dòng Llama"
|
||||
},
|
||||
"volcengine": {
|
||||
"name": "Volcengine (Doubao)",
|
||||
"default_model": "doubao-pro-4k",
|
||||
"base_url": "https://ark.volcengine.com/api/v3",
|
||||
"api_key_field": "api_key",
|
||||
"requires_custom_url": False,
|
||||
"description": "Dòng Doubao"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class Backend:
|
||||
"""Lớp dữ liệu cấu hình phụ trợ"""
|
||||
name: str
|
||||
type: str
|
||||
base_url: str
|
||||
api_key: str
|
||||
model: str
|
||||
enabled: bool = True
|
||||
timeout: int = 120
|
||||
retry_times: int = 3
|
||||
is_default: bool = False
|
||||
|
||||
def validate(self) -> tuple[bool, str]:
|
||||
"""Xác minh tính hợp lệ của cấu hình"""
|
||||
if not self.name or not self.name.strip():
|
||||
return False, t("config.backend_name_empty")
|
||||
if self.type not in ["ollama", "openai", "claude", "other"]:
|
||||
return False, t("config.unsupported_type", type=self.type)
|
||||
if not self.base_url or not self.base_url.strip().startswith(("http://", "https://")):
|
||||
return False, t("config.base_url_invalid")
|
||||
# loại ollama cho phép api_key trống
|
||||
if self.type != "ollama" and (not self.api_key or not self.api_key.strip()):
|
||||
return False, t("config.api_key_empty")
|
||||
if not self.model or not self.model.strip():
|
||||
return False, t("config.model_empty")
|
||||
if self.timeout < 5 or self.timeout > 10000:
|
||||
return False, t("config.timeout_range")
|
||||
if self.retry_times < 1 or self.retry_times > 10:
|
||||
return False, t("config.retry_range")
|
||||
return True, "OK"
|
||||
|
||||
|
||||
@dataclass
|
||||
class GenerationConfig:
|
||||
"""Cấu hình tham số tạo"""
|
||||
temperature: float = 0.7
|
||||
top_p: float = 0.9
|
||||
top_k: int = 40
|
||||
max_tokens: int = 16384
|
||||
chapter_target_words: int = 4000
|
||||
writing_style: str = "Trôi chảy tự nhiên, tình tiết chặt chẽ, miêu tả nhân vật tinh tế"
|
||||
writing_tone: str = "Trung lập"
|
||||
character_development: str = "Chi tiết"
|
||||
plot_complexity: str = "Trung bình"
|
||||
|
||||
def validate(self) -> tuple[bool, str]:
|
||||
"""Xác minh tính hợp lệ của các tham số"""
|
||||
if not 0.1 <= self.temperature <= 2.0:
|
||||
return False, t("config.temp_range")
|
||||
if not 0.1 <= self.top_p <= 1.0:
|
||||
return False, t("config.top_p_range")
|
||||
if self.max_tokens < 100 or self.max_tokens > 100000:
|
||||
return False, t("config.max_tokens_range")
|
||||
if self.chapter_target_words < 500 or self.chapter_target_words > 65536:
|
||||
return False, t("config.chapter_words_range")
|
||||
return True, "OK"
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
"""Trình quản lý cấu hình - Chế độ đơn"""
|
||||
_instance: Optional["ConfigManager"] = None
|
||||
|
||||
def __new__(cls) -> "ConfigManager":
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
cls._instance._initialized = False
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
if self._initialized:
|
||||
return
|
||||
|
||||
self.backends: List[Backend] = []
|
||||
self.generation: GenerationConfig = GenerationConfig()
|
||||
self.version: str = "4.0.0"
|
||||
self.last_modified: str = datetime.now().isoformat()
|
||||
self._load()
|
||||
self._initialized = True
|
||||
|
||||
def _load(self) -> None:
|
||||
"""Tải cấu hình từ SQLite"""
|
||||
try:
|
||||
conn = get_db()
|
||||
|
||||
# Tải backends
|
||||
rows = conn.execute("SELECT * FROM backends ORDER BY id").fetchall()
|
||||
if rows:
|
||||
for row in rows:
|
||||
try:
|
||||
backend = Backend(
|
||||
name=row["name"],
|
||||
type=row["type"],
|
||||
base_url=row["base_url"],
|
||||
api_key=row["api_key"],
|
||||
model=row["model"],
|
||||
enabled=bool(row["enabled"]),
|
||||
timeout=row["timeout"],
|
||||
retry_times=row["retry_times"],
|
||||
is_default=bool(row["is_default"])
|
||||
)
|
||||
valid, msg = backend.validate()
|
||||
if valid:
|
||||
self.backends.append(backend)
|
||||
else:
|
||||
logger.warning(f"Skip invalid backend {backend.name}: {msg}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Load backend config failed: {e}")
|
||||
|
||||
# Tải generation config
|
||||
gen_row = conn.execute("SELECT value FROM config WHERE key = 'generation'").fetchone()
|
||||
if gen_row:
|
||||
try:
|
||||
gen_data = json.loads(gen_row["value"])
|
||||
gen_data = {k: v for k, v in gen_data.items()
|
||||
if k in GenerationConfig.__dataclass_fields__}
|
||||
self.generation = GenerationConfig(**gen_data)
|
||||
except Exception as e:
|
||||
logger.warning(f"Load generation config failed: {e}")
|
||||
|
||||
# Tải version
|
||||
ver_row = conn.execute("SELECT value FROM config WHERE key = 'version'").fetchone()
|
||||
if ver_row:
|
||||
self.version = ver_row["value"]
|
||||
|
||||
mod_row = conn.execute("SELECT value FROM config WHERE key = 'last_modified'").fetchone()
|
||||
if mod_row:
|
||||
self.last_modified = mod_row["value"]
|
||||
|
||||
if rows or gen_row:
|
||||
logger.info("Config loaded from database")
|
||||
else:
|
||||
logger.info("No config in database, using defaults")
|
||||
self._init_default()
|
||||
except Exception as e:
|
||||
logger.error(f"Config load failed: {e}")
|
||||
self._init_default()
|
||||
|
||||
def _init_default(self) -> None:
|
||||
"""Khởi tạo cấu hình mặc định"""
|
||||
self.backends = [
|
||||
Backend(
|
||||
name=t("config.default_backend_name"),
|
||||
type="ollama",
|
||||
base_url="http://localhost:11434/v1",
|
||||
api_key="ollama",
|
||||
model="llama3.1:latest"
|
||||
)
|
||||
]
|
||||
self.generation = GenerationConfig()
|
||||
self.save()
|
||||
|
||||
def save(self) -> tuple[bool, str]:
|
||||
"""Lưu cấu hình vào SQLite"""
|
||||
try:
|
||||
conn = get_db()
|
||||
now = datetime.now().isoformat()
|
||||
|
||||
# Tạo bản sao lưu
|
||||
backup_data = {
|
||||
"version": self.version,
|
||||
"last_modified": self.last_modified,
|
||||
"backends": [asdict(b) for b in self.backends],
|
||||
"generation": asdict(self.generation),
|
||||
}
|
||||
conn.execute(
|
||||
"INSERT INTO config_backups (data, created_at) VALUES (?, ?)",
|
||||
(json.dumps(backup_data, ensure_ascii=False), now)
|
||||
)
|
||||
|
||||
# Xóa backends cũ và insert lại
|
||||
conn.execute("DELETE FROM backends")
|
||||
for b in self.backends:
|
||||
conn.execute("""
|
||||
INSERT INTO backends
|
||||
(name, type, base_url, api_key, model, enabled, timeout, retry_times, is_default, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
b.name, b.type, b.base_url, b.api_key, b.model,
|
||||
1 if b.enabled else 0, b.timeout, b.retry_times,
|
||||
1 if b.is_default else 0, now, now
|
||||
))
|
||||
|
||||
# Lưu generation config
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO config (key, value, updated_at) VALUES (?, ?, ?)",
|
||||
("generation", json.dumps(asdict(self.generation), ensure_ascii=False), now)
|
||||
)
|
||||
|
||||
# Lưu version + last_modified
|
||||
self.last_modified = now
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO config (key, value, updated_at) VALUES (?, ?, ?)",
|
||||
("version", self.version, now)
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO config (key, value, updated_at) VALUES (?, ?, ?)",
|
||||
("last_modified", now, now)
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
logger.info("Config saved to database")
|
||||
return True, t("config.config_save_success")
|
||||
except Exception as e:
|
||||
logger.error(f"Config save failed: {e}")
|
||||
return False, t("config.config_save_failed", error=str(e))
|
||||
|
||||
def add_backend(self, backend: Backend) -> tuple[bool, str]:
|
||||
"""Thêm backend"""
|
||||
valid, msg = backend.validate()
|
||||
if not valid:
|
||||
return False, msg
|
||||
|
||||
# Kiểm tra sự trùng lặp
|
||||
if any(b.name == backend.name for b in self.backends):
|
||||
return False, t("config.backend_exists", name=backend.name)
|
||||
|
||||
self.backends.append(backend)
|
||||
success, msg = self.save()
|
||||
return success, msg if not success else t("config.backend_add_success")
|
||||
|
||||
def update_backend(self, name: str, **kwargs) -> tuple[bool, str]:
|
||||
"""Cập nhật cấu hình phụ trợ"""
|
||||
for backend in self.backends:
|
||||
if backend.name == name:
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(backend, key):
|
||||
setattr(backend, key, value)
|
||||
|
||||
valid, msg = backend.validate()
|
||||
if not valid:
|
||||
return False, msg
|
||||
|
||||
success, msg = self.save()
|
||||
return success, msg if not success else t("config.backend_update_success")
|
||||
|
||||
return False, t("config.backend_not_found", name=name)
|
||||
|
||||
def delete_backend(self, name: str) -> tuple[bool, str]:
|
||||
"""Xóa backend"""
|
||||
self.backends = [b for b in self.backends if b.name != name]
|
||||
success, msg = self.save()
|
||||
return success, msg if not success else t("config.backend_delete_success", name=name)
|
||||
|
||||
def set_default_backend(self, name: str) -> tuple[bool, str]:
|
||||
"""Đặt giao diện làm mặc định"""
|
||||
found = False
|
||||
for backend in self.backends:
|
||||
if backend.name == name:
|
||||
backend.is_default = True
|
||||
found = True
|
||||
else:
|
||||
backend.is_default = False
|
||||
|
||||
if found:
|
||||
success, msg = self.save()
|
||||
return success, msg if not success else "Success"
|
||||
return False, t("config.backend_not_found", name=name)
|
||||
|
||||
def get_enabled_backends(self) -> List[Backend]:
|
||||
"""Nhận tất cả các phụ trợ được kích hoạt"""
|
||||
return [b for b in self.backends if b.enabled]
|
||||
|
||||
def update_generation_config(self, **kwargs) -> tuple[bool, str]:
|
||||
"""Cập nhật cấu hình bản dựng"""
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(self.generation, key):
|
||||
setattr(self.generation, key, value)
|
||||
|
||||
valid, msg = self.generation.validate()
|
||||
if not valid:
|
||||
return False, msg
|
||||
|
||||
success, msg = self.save()
|
||||
return success, msg if not success else t("config.gen_params_update_success")
|
||||
|
||||
def export_config(self, filepath: str) -> tuple[bool, str]:
|
||||
"""Xuất cấu hình (không chứa thông tin nhạy cảm)"""
|
||||
try:
|
||||
data = {
|
||||
"version": self.version,
|
||||
"backends": [{"name": b.name, "type": b.type, "model": b.model}
|
||||
for b in self.backends],
|
||||
"generation": asdict(self.generation),
|
||||
}
|
||||
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=4)
|
||||
|
||||
return True, t("config.config_export_success", filepath=filepath)
|
||||
except Exception as e:
|
||||
return False, t("config.config_export_failed", error=str(e))
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Chuyển đổi cấu hình sang định dạng từ điển"""
|
||||
return {
|
||||
"backends": [asdict(b) for b in self.backends],
|
||||
"generation": asdict(self.generation),
|
||||
"system": {
|
||||
"logging": {
|
||||
"level": "INFO",
|
||||
"file": "logs/novel_generator.log",
|
||||
"console_output": True
|
||||
},
|
||||
"concurrency": {
|
||||
"max_workers": 4,
|
||||
"request_timeout": 30
|
||||
},
|
||||
"cache": {
|
||||
"enabled": True,
|
||||
"type": "file",
|
||||
"location": "cache",
|
||||
"ttl": 3600
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"default_format": "markdown",
|
||||
"output_directory": "output",
|
||||
"supported_formats": ["markdown", "pdf", "docx", "txt", "epub"]
|
||||
},
|
||||
"ui": {
|
||||
"theme": "light",
|
||||
"language": "zh-CN",
|
||||
"editor": {
|
||||
"font_size": 14,
|
||||
"font_family": "Microsoft YaHei, sans-serif",
|
||||
"tab_size": 2,
|
||||
"word_wrap": True
|
||||
}
|
||||
},
|
||||
"project": {
|
||||
"auto_save": {
|
||||
"enabled": True,
|
||||
"interval": 300,
|
||||
"backup_count": 5
|
||||
},
|
||||
"backup": {
|
||||
"enabled": True,
|
||||
"location": "backups",
|
||||
"schedule": "daily",
|
||||
"keep_days": 30
|
||||
},
|
||||
"templates": {
|
||||
"enabled": True,
|
||||
"location": "project_templates",
|
||||
"default_template": "standard_novel"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"enabled": True,
|
||||
"directory": "plugins",
|
||||
"auto_load": True,
|
||||
"enabled_plugins": [
|
||||
"style_analyzer",
|
||||
"grammar_checker",
|
||||
"character_tracker",
|
||||
"plot_generator"
|
||||
]
|
||||
},
|
||||
"advanced": {
|
||||
"performance": {
|
||||
"enable_profiling": False,
|
||||
"memory_limit": "1GB",
|
||||
"cpu_limit": 80
|
||||
},
|
||||
"debug": {
|
||||
"show_errors": False,
|
||||
"debug_mode": False,
|
||||
"trace_requests": False
|
||||
},
|
||||
"monitoring": {
|
||||
"enabled": False,
|
||||
"metrics_port": 8080,
|
||||
"health_check_interval": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_api_providers() -> Dict[str, Dict[str, Any]]:
|
||||
"""Nhận tất cả cấu hình của nhà cung cấp API"""
|
||||
return API_PROVIDERS
|
||||
|
||||
@staticmethod
|
||||
def get_api_provider_choices() -> List[str]:
|
||||
"""Nhận danh sách lựa chọn nhà cung cấp API"""
|
||||
return [provider["name"] for provider in API_PROVIDERS.values()]
|
||||
|
||||
@staticmethod
|
||||
def get_api_provider_info(provider_key: str) -> Optional[Dict[str, Any]]:
|
||||
"""Nhận thông tin nhà cung cấp dựa trên khóa nhà cung cấp"""
|
||||
return API_PROVIDERS.get(provider_key)
|
||||
|
||||
@staticmethod
|
||||
def get_api_provider_key_by_name(provider_name: str) -> Optional[str]:
|
||||
"""Nhận khóa nhà cung cấp dựa trên tên nhà cung cấp"""
|
||||
for key, provider in API_PROVIDERS.items():
|
||||
if provider["name"] == provider_name:
|
||||
return key
|
||||
return None
|
||||
|
||||
|
||||
def load_config(config_path: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Tải cấu hình từ database hoặc file chỉ định
|
||||
|
||||
Args:
|
||||
config_path: đường dẫn file cấu hình (hỗ trợ import từ file)
|
||||
|
||||
Returns:
|
||||
Từ điển cấu hình
|
||||
"""
|
||||
if config_path:
|
||||
# Import từ file chỉ định
|
||||
if not os.path.exists(config_path):
|
||||
raise FileNotFoundError(t("config.config_file_missing", path=config_path))
|
||||
|
||||
file_ext = os.path.splitext(config_path)[1].lower()
|
||||
if file_ext == ".json":
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
else:
|
||||
raise ValueError(t("config.config_format_unsupported", ext=file_ext))
|
||||
else:
|
||||
return get_config().to_dict()
|
||||
|
||||
def get_config() -> ConfigManager:
|
||||
"""Nhận phiên bản cấu hình toàn cầu"""
|
||||
return ConfigManager()
|
||||
|
||||
def get_config_manager() -> ConfigManager:
|
||||
"""Nhận phiên bản trình quản lý cấu hình toàn cầu (bí danh)"""
|
||||
return get_config()
|
||||
Reference in New Issue
Block a user