v1.8.0: 模块化重构 + 后台登录认证
This commit is contained in:
105
utils.py
Normal file
105
utils.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
ParamHub 工具函数
|
||||
"""
|
||||
import json
|
||||
import base64
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from config import (
|
||||
ALLOWED_EXTENSIONS, DEFAULT_CONFIG, CONFIG_FILE, IMAGES_DIR, DATA_DIR
|
||||
)
|
||||
|
||||
|
||||
def allowed_file(filename: str) -> bool:
|
||||
"""检查文件扩展名是否允许"""
|
||||
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
|
||||
def load_data(file_path: Path):
|
||||
"""加载JSON数据文件"""
|
||||
if file_path.exists():
|
||||
return json.loads(file_path.read_text(encoding='utf-8'))
|
||||
return []
|
||||
|
||||
|
||||
def save_data(file_path: Path, data):
|
||||
"""保存JSON数据到文件"""
|
||||
file_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
|
||||
|
||||
def load_config() -> dict:
|
||||
"""加载网站配置,合并默认值"""
|
||||
if CONFIG_FILE.exists():
|
||||
loaded = json.loads(CONFIG_FILE.read_text(encoding='utf-8'))
|
||||
result = DEFAULT_CONFIG.copy()
|
||||
result.update(loaded)
|
||||
return result
|
||||
return DEFAULT_CONFIG.copy()
|
||||
|
||||
|
||||
def save_config(config: dict):
|
||||
"""保存网站配置"""
|
||||
CONFIG_FILE.write_text(json.dumps(config, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
|
||||
|
||||
def get_items_file(category_id: str) -> Path:
|
||||
"""获取动态分类的数据文件路径"""
|
||||
return DATA_DIR / f'items_{category_id}.json'
|
||||
|
||||
|
||||
def get_llm_config() -> dict:
|
||||
"""从 config.json 动态读取 LLM 配置"""
|
||||
config = load_config()
|
||||
return {
|
||||
'base_url': config.get('llm_base_url', DEFAULT_CONFIG['llm_base_url']),
|
||||
'api_key': config.get('llm_api_key', DEFAULT_CONFIG['llm_api_key']),
|
||||
'model': config.get('llm_model', DEFAULT_CONFIG['llm_model']),
|
||||
'vision_model': config.get('llm_vision_model', DEFAULT_CONFIG['llm_vision_model']),
|
||||
}
|
||||
|
||||
|
||||
def parse_date_to_timestamp(date_str: str) -> float | None:
|
||||
"""将日期字符串转换为 Unix 时间戳"""
|
||||
if not date_str:
|
||||
return None
|
||||
for fmt in ['%Y-%m-%d', '%Y-%m-%d %H:%M:%S', '%Y/%m/%d']:
|
||||
try:
|
||||
return datetime.strptime(date_str, fmt).timestamp()
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def safe_sort_key(x: dict, key: str):
|
||||
"""排序时安全获取字段值,缺失时返回合理默认值"""
|
||||
val = x.get(key)
|
||||
if val is None:
|
||||
if key in ['parameters', 'context_length', 'mmlu', 'views']:
|
||||
return 0
|
||||
elif key in ['publish_date', 'created_at', 'updated_at']:
|
||||
return ''
|
||||
return ''
|
||||
return val
|
||||
|
||||
|
||||
def read_image_base64(img_src: str) -> str | None:
|
||||
"""将图片路径/base64/URL转换为base64 data URI"""
|
||||
import base64 as b64
|
||||
if img_src.startswith('data:'):
|
||||
return img_src
|
||||
if img_src.startswith('http'):
|
||||
return img_src # URL 直接传给 LLM
|
||||
# 本地路径
|
||||
try:
|
||||
img_path = IMAGES_DIR / img_src.replace('/static/uploads/', '')
|
||||
if img_path.exists():
|
||||
with open(img_path, 'rb') as f:
|
||||
img_data = b64.b64encode(f.read()).decode()
|
||||
ext = img_path.suffix.lower().lstrip('.')
|
||||
mime = f'image/{"jpeg" if ext == "jpg" else ext}'
|
||||
return f'data:{mime};base64,{img_data}'
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
Reference in New Issue
Block a user