mirror of
https://github.com/jxxghp/MoviePilot-Plugins.git
synced 2026-03-27 10:05:57 +00:00
148 lines
5.2 KiB
Python
148 lines
5.2 KiB
Python
import time
|
||
import random
|
||
from typing import List, Union
|
||
|
||
import openai
|
||
from cacheout import Cache
|
||
|
||
OpenAISessionCache = Cache(maxsize=100, ttl=3600, timer=time.time, default=None)
|
||
|
||
|
||
class OpenAi:
|
||
_api_key: str = None
|
||
_api_url: str = None
|
||
_model: str = "gpt-3.5-turbo"
|
||
|
||
def __init__(self, api_key: str = None, api_url: str = None, proxy: dict = None, model: str = None,
|
||
compatible: bool = False):
|
||
self._api_key = api_key
|
||
self._api_url = api_url
|
||
openai.api_base = self._api_url if compatible else self._api_url + "/v1"
|
||
openai.api_key = self._api_key
|
||
if proxy and proxy.get("https"):
|
||
openai.proxy = proxy.get("https")
|
||
if model:
|
||
self._model = model
|
||
|
||
@staticmethod
|
||
def __save_session(session_id: str, message: str):
|
||
"""
|
||
保存会话
|
||
:param session_id: 会话ID
|
||
:param message: 消息
|
||
:return:
|
||
"""
|
||
seasion = OpenAISessionCache.get(session_id)
|
||
if seasion:
|
||
seasion.append({
|
||
"role": "assistant",
|
||
"content": message
|
||
})
|
||
OpenAISessionCache.set(session_id, seasion)
|
||
|
||
@staticmethod
|
||
def __get_session(session_id: str, message: str) -> List[dict]:
|
||
"""
|
||
获取会话
|
||
:param session_id: 会话ID
|
||
:return: 会话上下文
|
||
"""
|
||
seasion = OpenAISessionCache.get(session_id)
|
||
if seasion:
|
||
seasion.append({
|
||
"role": "user",
|
||
"content": message
|
||
})
|
||
else:
|
||
seasion = [
|
||
{
|
||
"role": "system",
|
||
"content": "请在接下来的对话中请使用中文回复,并且内容尽可能详细。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": message
|
||
}]
|
||
OpenAISessionCache.set(session_id, seasion)
|
||
return seasion
|
||
|
||
def __get_model(self, message: Union[str, List[dict]],
|
||
prompt: str = None,
|
||
user: str = "MoviePilot",
|
||
**kwargs):
|
||
"""
|
||
获取模型
|
||
"""
|
||
if not isinstance(message, list):
|
||
if prompt:
|
||
message = [
|
||
{
|
||
"role": "system",
|
||
"content": prompt
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": message
|
||
}
|
||
]
|
||
else:
|
||
message = [
|
||
{
|
||
"role": "user",
|
||
"content": message
|
||
}
|
||
]
|
||
return self.client.chat.completions.create(
|
||
model=self._model,
|
||
user=user,
|
||
messages=message,
|
||
**kwargs
|
||
)
|
||
|
||
@staticmethod
|
||
def __clear_session(session_id: str):
|
||
"""
|
||
清除会话
|
||
:param session_id: 会话ID
|
||
:return:
|
||
"""
|
||
if OpenAISessionCache.get(session_id):
|
||
OpenAISessionCache.delete(session_id)
|
||
|
||
def translate_to_zh(self, text: str, context: str = None, max_retries: int = 3):
|
||
"""
|
||
翻译为中文
|
||
:param text: 输入文本
|
||
:param context: 翻译上下文
|
||
:param max_retries: 最大重试次数
|
||
"""
|
||
system_prompt = """您是一位专业字幕翻译专家,请严格遵循以下规则:
|
||
1. 将原文精准翻译为简体中文,保持原文本意
|
||
2. 使用自然的口语化表达,符合中文观影习惯
|
||
3. 结合上下文语境,人物称谓、专业术语、情感语气在上下文中保持连贯
|
||
4. 按行翻译待译内容。翻译结果不要包括上下文。
|
||
5. 输出内容必须仅包括译文。不要输出任何开场白,解释说明或总结"""
|
||
user_prompt = f"翻译上下文:\n{context}\n\n需要翻译的内容:\n{text}" if context else f"请翻译:\n{text}"
|
||
|
||
last_error = ""
|
||
for attempt in range(max_retries + 1):
|
||
try:
|
||
completion = self.__get_model(prompt=system_prompt,
|
||
message=user_prompt,
|
||
temperature=0.2,
|
||
top_p=0.9)
|
||
result = completion.choices[0].message.content.strip()
|
||
return True, result
|
||
except Exception as e:
|
||
last_error = str(e)
|
||
if attempt < max_retries:
|
||
# 使用指数退避和随机抖动,避免多个请求同时重试
|
||
base_delay = 2 ** attempt # 指数退避: 1s, 2s, 4s...
|
||
jitter = random.uniform(0.1, 0.9) # 随机抖动: 0.1-0.9秒
|
||
sleep_time = base_delay + jitter
|
||
print(f"翻译请求失败 (第{attempt + 1}次尝试):{last_error},{sleep_time:.1f}秒后重试...")
|
||
time.sleep(sleep_time)
|
||
else:
|
||
print(f"翻译请求失败 (已重试{max_retries}次):{last_error}")
|
||
return False, f"{last_error}"
|