diff --git a/app/services/SDE/prompt.py b/app/services/SDE/prompt.py deleted file mode 100644 index 2ee9ea8..0000000 --- a/app/services/SDE/prompt.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- - -''' -@Project: NarratoAI -@File : prompt -@Author : 小林同学 -@Date : 2025/5/9 上午12:57 -''' -# 字幕剧情分析提示词 -subtitle_plot_analysis_v1 = """ -# 角色 -你是一位专业的剧本分析师和剧情概括助手。 - -# 任务 -我将为你提供一部短剧的完整字幕文本。请你基于这些字幕,完成以下任务: -1. **整体剧情分析**:简要概括整个短剧的核心剧情脉络、主要冲突和结局(如果有的话)。 -2. **分段剧情解析与时间戳定位**: - * 将整个短剧划分为若干个关键的剧情段落(例如:开端、发展、转折、高潮、结局,或根据具体情节自然划分)。 - * 段落数应该与字幕长度成正比。 - * 对于每一个剧情段落: - * **概括该段落的主要内容**:用简洁的语言描述这段剧情发生了什么。 - * **标注对应的时间戳范围**:明确指出该剧情段落对应的开始字幕时间戳和结束字幕时间戳。请直接从字幕中提取时间信息。 - -# 输入格式 -字幕内容通常包含时间戳和对话,例如: -``` -00:00:05,000 --> 00:00:10,000 -[角色A]: 你好吗? -00:00:10,500 --> 00:00:15,000 -[角色B]: 我很好,谢谢。发生了一些有趣的事情。 -... (更多字幕内容) ... -``` -我将把实际字幕粘贴在下方。 - -# 输出格式要求 -请按照以下格式清晰地呈现分析结果: - -**一、整体剧情概括:** -[此处填写对整个短剧剧情的概括] - -**二、分段剧情解析:** - -**剧情段落 1:[段落主题/概括,例如:主角登场与背景介绍]** -* **时间戳:** [开始时间戳] --> [结束时间戳] -* **内容概要:** [对这段剧情的详细描述] - -**剧情段落 2:[段落主题/概括,例如:第一个冲突出现]** -* **时间戳:** [开始时间戳] --> [结束时间戳] -* **内容概要:** [对这段剧情的详细描述] - -... (根据实际剧情段落数量继续) ... - -**剧情段落 N:[段落主题/概括,例如:结局与反思]** -* **时间戳:** [开始时间戳] --> [结束时间戳] -* **内容概要:** [对这段剧情的详细描述] - -# 注意事项 -* 请确保时间戳的准确性,直接引用字幕中的时间。 -* 剧情段落的划分应合乎逻辑,能够反映剧情的起承转合。 -* 语言表达应简洁、准确、客观。 - -# 限制 -1. 严禁输出与分析结果无关的内容 -2. - -# 请处理以下字幕: -""" - -plot_writing = """ -我是一个影视解说up主,需要为我的粉丝讲解短剧《%s》的剧情,目前正在解说剧情,希望能让粉丝通过我的解说了解剧情,并且产生 继续观看的兴趣,请生成一篇解说脚本,包含解说文案,以及穿插原声的片段,下面中的内容是短剧的剧情概述: - - -%s - - -请严格按照以下JSON格式输出,不要添加任何其他文字、说明或代码块标记: - -{ - "items": [ - { - "_id": 1, - "timestamp": "00:00:05,390-00:00:10,430", - "picture": "剧情描述或者备注", - "narration": "解说文案,如果片段为穿插的原片片段,可以直接使用 ‘播放原片+_id‘ 进行占位", - "OST": 0 - } - ] -} - -重要要求: -1. 必须输出有效的JSON格式,不能包含注释 -2. OST字段必须是数字:0表示解说片段,1表示原片片段 -3. _id必须是递增的数字 -4. 只输出JSON内容,不要输出任何说明文字 -5. 不要使用代码块标记(如```json) -6. 解说文案使用简体中文 -7. 严禁虚构剧情,所有内容只能从中摘取 -8. 严禁虚构时间戳,所有时间戳只能从中摘取 -""" \ No newline at end of file diff --git a/app/services/SDP/generate_script_short.py b/app/services/SDP/generate_script_short.py index caaad93..713d26c 100644 --- a/app/services/SDP/generate_script_short.py +++ b/app/services/SDP/generate_script_short.py @@ -6,12 +6,17 @@ from .utils.step1_subtitle_analyzer_openai import analyze_subtitle from .utils.step5_merge_script import merge_script -def generate_script(srt_path: str, api_key: str, model_name: str, output_path: str, base_url: str = None, custom_clips: int = 5): +def generate_script(srt_path: str, api_key: str, model_name: str, output_path: str, base_url: str = None, custom_clips: int = 5, provider: str = None): """生成视频混剪脚本 Args: srt_path: 字幕文件路径 + api_key: API密钥 + model_name: 模型名称 output_path: 输出文件路径,可选 + base_url: API基础URL + custom_clips: 自定义片段数量 + provider: LLM服务提供商 Returns: str: 生成的脚本内容 @@ -27,7 +32,8 @@ def generate_script(srt_path: str, api_key: str, model_name: str, output_path: s api_key=api_key, model_name=model_name, base_url=base_url, - custom_clips=custom_clips + custom_clips=custom_clips, + provider=provider ) # 合并生成最终脚本 diff --git a/app/services/SDP/utils/step1_subtitle_analyzer_openai.py b/app/services/SDP/utils/step1_subtitle_analyzer_openai.py index b8f4d18..8752d38 100644 --- a/app/services/SDP/utils/step1_subtitle_analyzer_openai.py +++ b/app/services/SDP/utils/step1_subtitle_analyzer_openai.py @@ -1,14 +1,18 @@ """ -使用OpenAI API,分析字幕文件,返回剧情梗概和爆点 +使用统一LLM服务,分析字幕文件,返回剧情梗概和爆点 """ import traceback -from openai import OpenAI, BadRequestError -import os import json +import asyncio +from loguru import logger from .utils import load_srt # 导入新的提示词管理系统 from app.services.prompts import PromptManager +# 导入统一LLM服务 +from app.services.llm.unified_service import UnifiedLLMService +# 导入安全的异步执行函数 +from app.services.llm.migration_adapter import _run_async_safely def analyze_subtitle( @@ -16,15 +20,18 @@ def analyze_subtitle( model_name: str, api_key: str = None, base_url: str = None, - custom_clips: int = 5 + custom_clips: int = 5, + provider: str = None ) -> dict: """分析字幕内容,返回完整的分析结果 Args: srt_path (str): SRT字幕文件路径 + model_name (str): 大模型名称 api_key (str, optional): 大模型API密钥. Defaults to None. - model_name (str, optional): 大模型名称. Defaults to "gpt-4o-2024-11-20". base_url (str, optional): 大模型API基础URL. Defaults to None. + custom_clips (int): 需要提取的片段数量. Defaults to 5. + provider (str, optional): LLM服务提供商. Defaults to None. Returns: dict: 包含剧情梗概和结构化的时间段分析的字典 @@ -34,18 +41,21 @@ def analyze_subtitle( subtitles = load_srt(srt_path) subtitle_content = "\n".join([f"{sub['timestamp']}\n{sub['text']}" for sub in subtitles]) - # 初始化客户端 - global client - if "deepseek" in model_name.lower(): - client = OpenAI( - api_key=api_key or os.getenv('DeepSeek_API_KEY'), - base_url="https://api.siliconflow.cn/v1" # 使用第三方 硅基流动 API - ) - else: - client = OpenAI( - api_key=api_key or os.getenv('OPENAI_API_KEY'), - base_url=base_url - ) + # 初始化统一LLM服务 + llm_service = UnifiedLLMService() + + # 如果没有指定provider,根据model_name推断 + if not provider: + if "deepseek" in model_name.lower(): + provider = "deepseek" + elif "gpt" in model_name.lower(): + provider = "openai" + elif "gemini" in model_name.lower(): + provider = "gemini" + else: + provider = "openai" # 默认使用openai + + logger.info(f"使用LLM服务分析字幕,提供商: {provider}, 模型: {model_name}") # 使用新的提示词管理系统 subtitle_analysis_prompt = PromptManager.get_prompt( @@ -57,35 +67,27 @@ def analyze_subtitle( } ) - messages = [ - { - "role": "system", - "content": "你是一名短剧编剧和内容分析师,擅长从字幕中提取剧情要点和关键情节。" - }, - { - "role": "user", - "content": subtitle_analysis_prompt - } - ] - # DeepSeek R1 和 V3 不支持 response_format=json_object - try: - completion = client.chat.completions.create( - model=model_name, - messages=messages, - response_format={"type": "json_object"} - ) - summary_data = json.loads(completion.choices[0].message.content) - except BadRequestError as e: - completion = client.chat.completions.create( - model=model_name, - messages=messages - ) - # 去除 completion 字符串前的 ```json 和 结尾的 ``` - completion = completion.choices[0].message.content.replace("```json", "").replace("```", "") - summary_data = json.loads(completion) - except Exception as e: - raise Exception(f"大模型解析发生错误:{str(e)}\n{traceback.format_exc()}") + # 使用统一LLM服务生成文本 + logger.info("开始分析字幕内容...") + response = _run_async_safely( + UnifiedLLMService.generate_text, + prompt=subtitle_analysis_prompt, + provider=provider, + model=model_name, + api_key=api_key, + base_url=base_url, + temperature=0.1, # 使用较低的温度以获得更稳定的结果 + max_tokens=4000 + ) + # 解析JSON响应 + from webui.tools.generate_short_summary import parse_and_fix_json + summary_data = parse_and_fix_json(response) + + if not summary_data: + raise Exception("无法解析LLM返回的JSON数据") + + logger.info(f"字幕分析完成,找到 {len(summary_data.get('plot_titles', []))} 个关键情节") print(json.dumps(summary_data, indent=4, ensure_ascii=False)) # 构建爆点标题列表 @@ -105,42 +107,37 @@ def analyze_subtitle( } ) - messages = [ - { - "role": "system", - "content": "你是一名短剧编剧,非常擅长根据字幕中分析视频中关键剧情出现的具体时间段。" - }, - { - "role": "user", - "content": plot_extraction_prompt - } - ] - # DeepSeek R1 和 V3 不支持 response_format=json_object - try: - completion = client.chat.completions.create( - model=model_name, - messages=messages, - response_format={"type": "json_object"} - ) - plot_points_data = json.loads(completion.choices[0].message.content) - except BadRequestError as e: - completion = client.chat.completions.create( - model=model_name, - messages=messages - ) - # 去除 completion 字符串前的 ```json 和 结尾的 ``` - completion = completion.choices[0].message.content.replace("```json", "").replace("```", "") - plot_points_data = json.loads(completion) - except Exception as e: - raise Exception(f"大模型解析错误:{str(e)}\n{traceback.format_exc()}") + # 使用统一LLM服务进行爆点时间段分析 + logger.info("开始分析爆点时间段...") + response = _run_async_safely( + UnifiedLLMService.generate_text, + prompt=plot_extraction_prompt, + provider=provider, + model=model_name, + api_key=api_key, + base_url=base_url, + temperature=0.1, + max_tokens=4000 + ) - print(json.dumps(plot_points_data, indent=4, ensure_ascii=False)) + # 解析JSON响应 + plot_data = parse_and_fix_json(response) + + if not plot_data: + raise Exception("无法解析爆点分析的JSON数据") + + logger.info(f"爆点分析完成,找到 {len(plot_data.get('plot_points', []))} 个时间段") # 合并结果 - return { - "plot_summary": summary_data, - "plot_points": plot_points_data["plot_points"] + result = { + "summary": summary_data.get("summary", ""), + "plot_titles": summary_data.get("plot_titles", []), + "plot_points": plot_data.get("plot_points", []) } + return result + except Exception as e: + logger.error(f"分析字幕时发生错误: {str(e)}") raise Exception(f"分析字幕时发生错误:{str(e)}\n{traceback.format_exc()}") + diff --git a/webui/tools/generate_script_short.py b/webui/tools/generate_script_short.py index c4508d9..5c4ce9d 100644 --- a/webui/tools/generate_script_short.py +++ b/webui/tools/generate_script_short.py @@ -69,6 +69,7 @@ def generate_script_short(tr, params, custom_clips=5): model_name=text_model, base_url=text_base_url, custom_clips=custom_clips, + provider=text_provider ) if script is None: