feat: 优化LLM服务配置与迁移适配,并更新相关UI设置及中文翻译

This commit is contained in:
linyq 2025-11-19 20:00:08 +08:00
parent 6697535c57
commit 238c1c13f1
5 changed files with 167 additions and 74 deletions

View File

@ -217,6 +217,12 @@ class LiteLLMVisionProvider(VisionModelProvider):
if hasattr(self, '_api_base'):
completion_kwargs["api_base"] = self._api_base
# 支持动态传递 api_key 和 api_base
if "api_key" in kwargs:
completion_kwargs["api_key"] = kwargs["api_key"]
if "api_base" in kwargs:
completion_kwargs["api_base"] = kwargs["api_base"]
response = await acompletion(**completion_kwargs)
if response.choices and len(response.choices) > 0:
@ -407,6 +413,12 @@ class LiteLLMTextProvider(TextModelProvider):
if hasattr(self, '_api_base'):
completion_kwargs["api_base"] = self._api_base
# 支持动态传递 api_key 和 api_base (修复认证问题)
if "api_key" in kwargs:
completion_kwargs["api_key"] = kwargs["api_key"]
if "api_base" in kwargs:
completion_kwargs["api_base"] = kwargs["api_base"]
try:
# 调用 LiteLLM自动重试
response = await acompletion(**completion_kwargs)

View File

@ -251,7 +251,9 @@ class SubtitleAnalyzerAdapter:
UnifiedLLMService.analyze_subtitle,
subtitle_content=subtitle_content,
provider=self.provider,
temperature=1.0
temperature=1.0,
api_key=self.api_key,
api_base=self.base_url
)
return {
@ -301,7 +303,9 @@ class SubtitleAnalyzerAdapter:
system_prompt="你是一位专业的短视频解说脚本撰写专家。",
provider=self.provider,
temperature=temperature,
response_format="json"
response_format="json",
api_key=self.api_key,
api_base=self.base_url
)
# 清理JSON输出

View File

@ -5,6 +5,7 @@ import os
from app.config import config
from app.utils import utils
from loguru import logger
from app.services.llm.unified_service import UnifiedLLMService
def validate_api_key(api_key: str, provider: str) -> tuple[bool, str]:
@ -663,6 +664,8 @@ def render_vision_llm_settings(tr):
if config_changed and not validation_errors:
try:
config.save_config()
# 清除缓存,确保下次使用新配置
UnifiedLLMService.clear_cache()
if st_vision_api_key or st_vision_base_url or st_vision_model_name:
st.success(f"视频分析模型配置已保存LiteLLM")
except Exception as e:
@ -932,6 +935,8 @@ def render_text_llm_settings(tr):
if text_config_changed and not text_validation_errors:
try:
config.save_config()
# 清除缓存,确保下次使用新配置
UnifiedLLMService.clear_cache()
if st_text_api_key or st_text_base_url or st_text_model_name:
st.success(f"文案生成模型配置已保存LiteLLM")
except Exception as e:

View File

@ -49,90 +49,160 @@ def render_script_panel(tr):
def render_script_file(tr, params):
"""渲染脚本文件选择"""
script_list = [
(tr("None"), ""),
(tr("Auto Generate"), "auto"),
(tr("Short Generate"), "short"),
(tr("Short Drama Summary"), "summary"),
(tr("Upload Script"), "upload_script")
]
# 定义功能模式
MODE_FILE = "file_selection"
MODE_AUTO = "auto"
MODE_SHORT = "short"
MODE_SUMMARY = "summary"
# 获取已有脚本文件
suffix = "*.json"
script_dir = utils.script_dir()
files = glob.glob(os.path.join(script_dir, suffix))
file_list = []
# 模式选项映射
mode_options = {
tr("Select/Upload Script"): MODE_FILE,
tr("Auto Generate"): MODE_AUTO,
tr("Short Generate"): MODE_SHORT,
tr("Short Drama Summary"): MODE_SUMMARY,
}
# 获取当前状态
current_path = st.session_state.get('video_clip_json_path', '')
# 确定当前选中的模式索引
default_index = 0
mode_keys = list(mode_options.keys())
if current_path == "auto":
default_index = mode_keys.index(tr("Auto Generate"))
elif current_path == "short":
default_index = mode_keys.index(tr("Short Generate"))
elif current_path == "summary":
default_index = mode_keys.index(tr("Short Drama Summary"))
else:
default_index = mode_keys.index(tr("Select/Upload Script"))
for file in files:
file_list.append({
"name": os.path.basename(file),
"file": file,
"ctime": os.path.getctime(file)
})
# 1. 渲染功能选择下拉框
# 使用 segmented_control 替代 selectbox提供更好的视觉体验
default_mode_label = mode_keys[default_index]
# 定义回调函数来处理状态更新
def update_script_mode():
# 获取当前选中的标签
selected_label = st.session_state.script_mode_selection
if selected_label:
# 更新实际的 path 状态
new_mode = mode_options[selected_label]
st.session_state.video_clip_json_path = new_mode
params.video_clip_json_path = new_mode
else:
# 如果用户取消选择segmented_control 允许取消),恢复到默认或上一个状态
# 这里我们强制保持当前状态,或者重置为默认
st.session_state.script_mode_selection = default_mode_label
file_list.sort(key=lambda x: x["ctime"], reverse=True)
for file in file_list:
display_name = file['file'].replace(config.root_dir, "")
script_list.append((display_name, file['file']))
# 找到保存的脚本文件在列表中的索引
saved_script_path = st.session_state.get('video_clip_json_path', '')
selected_index = 0
for i, (_, path) in enumerate(script_list):
if path == saved_script_path:
selected_index = i
break
selected_script_index = st.selectbox(
tr("Script Files"),
index=selected_index,
options=range(len(script_list)),
format_func=lambda x: script_list[x][0]
# 渲染组件
selected_mode_label = st.segmented_control(
tr("Video Type"),
options=mode_keys,
default=default_mode_label,
key="script_mode_selection",
on_change=update_script_mode
)
# 处理未选择的情况虽然有default但在某些交互下可能为空
if not selected_mode_label:
selected_mode_label = default_mode_label
selected_mode = mode_options[selected_mode_label]
script_path = script_list[selected_script_index][1]
st.session_state['video_clip_json_path'] = script_path
params.video_clip_json_path = script_path
# 2. 根据选择的模式处理逻辑
if selected_mode == MODE_FILE:
# --- 文件选择模式 ---
script_list = [
(tr("None"), ""),
(tr("Upload Script"), "upload_script")
]
# 处理脚本上传
if script_path == "upload_script":
uploaded_file = st.file_uploader(
tr("Upload Script File"),
type=["json"],
accept_multiple_files=False,
# 获取已有脚本文件
suffix = "*.json"
script_dir = utils.script_dir()
files = glob.glob(os.path.join(script_dir, suffix))
file_list = []
for file in files:
file_list.append({
"name": os.path.basename(file),
"file": file,
"ctime": os.path.getctime(file)
})
file_list.sort(key=lambda x: x["ctime"], reverse=True)
for file in file_list:
display_name = file['file'].replace(config.root_dir, "")
script_list.append((display_name, file['file']))
# 找到保存的脚本文件在列表中的索引
# 如果当前path是特殊值(auto/short/summary),则重置为空
saved_script_path = current_path if current_path not in [MODE_AUTO, MODE_SHORT, MODE_SUMMARY] else ""
selected_index = 0
for i, (_, path) in enumerate(script_list):
if path == saved_script_path:
selected_index = i
break
selected_script_index = st.selectbox(
tr("Script Files"),
index=selected_index,
options=range(len(script_list)),
format_func=lambda x: script_list[x][0],
key="script_file_selection"
)
if uploaded_file is not None:
try:
# 读取上传的JSON内容并验证格式
script_content = uploaded_file.read().decode('utf-8')
json_data = json.loads(script_content)
script_path = script_list[selected_script_index][1]
st.session_state['video_clip_json_path'] = script_path
params.video_clip_json_path = script_path
# 保存到脚本目录
script_file_path = os.path.join(script_dir, uploaded_file.name)
file_name, file_extension = os.path.splitext(uploaded_file.name)
# 处理脚本上传
if script_path == "upload_script":
uploaded_file = st.file_uploader(
tr("Upload Script File"),
type=["json"],
accept_multiple_files=False,
)
# 如果文件已存在,添加时间戳
if os.path.exists(script_file_path):
timestamp = time.strftime("%Y%m%d%H%M%S")
file_name_with_timestamp = f"{file_name}_{timestamp}"
script_file_path = os.path.join(script_dir, file_name_with_timestamp + file_extension)
if uploaded_file is not None:
try:
# 读取上传的JSON内容并验证格式
script_content = uploaded_file.read().decode('utf-8')
json_data = json.loads(script_content)
# 写入文件
with open(script_file_path, "w", encoding='utf-8') as f:
json.dump(json_data, f, ensure_ascii=False, indent=2)
# 保存到脚本目录
script_file_path = os.path.join(script_dir, uploaded_file.name)
file_name, file_extension = os.path.splitext(uploaded_file.name)
# 更新状态
st.success(tr("Script Uploaded Successfully"))
st.session_state['video_clip_json_path'] = script_file_path
params.video_clip_json_path = script_file_path
time.sleep(1)
st.rerun()
# 如果文件已存在,添加时间戳
if os.path.exists(script_file_path):
timestamp = time.strftime("%Y%m%d%H%M%S")
file_name_with_timestamp = f"{file_name}_{timestamp}"
script_file_path = os.path.join(script_dir, file_name_with_timestamp + file_extension)
except json.JSONDecodeError:
st.error(tr("Invalid JSON format"))
except Exception as e:
st.error(f"{tr('Upload failed')}: {str(e)}")
# 写入文件
with open(script_file_path, "w", encoding='utf-8') as f:
json.dump(json_data, f, ensure_ascii=False, indent=2)
# 更新状态
st.success(tr("Script Uploaded Successfully"))
st.session_state['video_clip_json_path'] = script_file_path
params.video_clip_json_path = script_file_path
time.sleep(1)
st.rerun()
except json.JSONDecodeError:
st.error(tr("Invalid JSON format"))
except Exception as e:
st.error(f"{tr('Upload failed')}: {str(e)}")
else:
# --- 功能生成模式 ---
st.session_state['video_clip_json_path'] = selected_mode
params.video_clip_json_path = selected_mode
def render_video_file(tr, params):

View File

@ -161,6 +161,8 @@
"Frame Interval (seconds) (More keyframes consume more tokens)": "帧间隔 (秒) (更多关键帧消耗更多令牌)",
"Batch Size": "批处理大小",
"Batch Size (More keyframes consume more tokens)": "批处理大小, 每批处理越少消耗 token 越多",
"Short Drama Summary": "短剧解说"
"Short Drama Summary": "短剧解说",
"Video Type": "视频类型",
"Select/Upload Script": "选择/上传脚本"
}
}