diff --git a/docutranslate/agents/agent.py b/docutranslate/agents/agent.py index 8b9143a..2e40a69 100644 --- a/docutranslate/agents/agent.py +++ b/docutranslate/agents/agent.py @@ -1,4 +1,5 @@ import asyncio +import time from concurrent.futures import ThreadPoolExecutor from threading import Lock from typing import TypedDict @@ -7,6 +8,8 @@ import httpx from docutranslate.logger import translater_logger +MAX_RETRY_COUNT = 3 + class AgentArgs(TypedDict, total=False): baseurl: str @@ -39,14 +42,17 @@ class Agent: def __init__(self, baseurl: str = "", key: str = "xx", model_id: str = "", system_prompt: str = "", temperature=0.7, max_concurrent=15, timeout: int = TIMEOUT): self.baseurl = baseurl.strip() + if self.baseurl.endswith("/"): + self.baseurl = self.baseurl[:-1] self.key = key.strip() self.model_id = model_id.strip() self.system_prompt = system_prompt self.temperature = temperature - self.client = httpx.Client(trust_env=False,proxy=None,verify=False) - self.client_async = httpx.AsyncClient(trust_env=False,proxy=None,verify=False) + self.client = httpx.Client(trust_env=False, proxy=None, verify=False) + self.client_async = httpx.AsyncClient(trust_env=False, proxy=None, verify=False) self.max_concurrent = max_concurrent self.timeout = timeout + def _prepare_request_data(self, prompt: str, system_prompt: str, temperature=None, top_p=0.9): if temperature is None: temperature = self.temperature @@ -64,14 +70,13 @@ class Agent: } return headers, data - async def send_async(self, prompt: str, system_prompt: None | str = None) -> str: + async def send_async(self, prompt: str, system_prompt: None | str = None, retry=True, retry_count=0) -> str: if system_prompt is None: system_prompt = self.system_prompt - - """Sends a single prompt asynchronously.""" + if prompt.strip() == "": + return prompt headers, data = self._prepare_request_data(prompt, system_prompt) - if self.baseurl.endswith("/"): - self.baseurl = self.baseurl[:-1] + try: response = await self.client_async.post( f"{self.baseurl}/chat/completions", @@ -83,12 +88,19 @@ class Agent: result = response.json()["choices"][0]["message"]["content"] return result except httpx.HTTPStatusError as e: - print(f"AI请求错误,prompt:{prompt}\n") - raise Exception(f"AI请求错误 (async): {e.response.status_code} - {e.response.text}") from e + translater_logger.error(f"AI请求错误 (async): {e.response.status_code} - {e.response.text}") except httpx.RequestError as e: - raise Exception(f"AI请求连接错误 (async): {e}") from e + translater_logger.warning(Exception(f"AI请求连接错误 (async): {repr(e)}\nprompt:{prompt}")) except (KeyError, IndexError) as e: - raise Exception(f"AI响应格式错误 (async): {e}") from e + translater_logger.error(f"AI响应格式错误 (async): {repr(e)}") + return "" + if retry and retry_count < MAX_RETRY_COUNT: + translater_logger.info(f"正在重试,重试次数{retry_count}") + await asyncio.sleep(0.5) + return await self.send_async(prompt, system_prompt, retry=True, retry_count=retry_count + 1) + else: + translater_logger.error(f"达到重试次数上限") + return "" async def send_prompts_async( self, @@ -122,14 +134,12 @@ class Agent: results = await asyncio.gather(*tasks, return_exceptions=False) return results - def send(self, prompt: str, system_prompt: None | str = None) -> str: + def send(self, prompt: str, system_prompt: None | str = None, retry=True, retry_count=0) -> str: if system_prompt is None: system_prompt = self.system_prompt - - """Sends a single prompt asynchronously.""" + if prompt.strip() == "": + return prompt headers, data = self._prepare_request_data(prompt, system_prompt) - if self.baseurl.endswith("/"): - self.baseurl = self.baseurl[:-1] try: response = self.client.post( f"{self.baseurl}/chat/completions", @@ -141,11 +151,19 @@ class Agent: result = response.json()["choices"][0]["message"]["content"] return result except httpx.HTTPStatusError as e: - raise Exception(f"AI请求错误 (async): {e.response.status_code} - {e.response.text}") from e + translater_logger.error(f"AI请求错误 (sync): {e.response.status_code} - {e.response.text}") except httpx.RequestError as e: - raise Exception(f"AI请求连接错误 (async): {e}") from e + translater_logger.warning(f"AI请求连接错误 (sync): {repr(e)}\nprompt:{prompt}") except (KeyError, IndexError) as e: - raise Exception(f"AI响应格式错误 (async): {e}") from e + translater_logger.error(f"AI响应格式错误 (sync): {repr(e)}") + return "" + if retry and retry_count < MAX_RETRY_COUNT: + translater_logger.info(f"正在重试,重试次数{retry_count}") + time.sleep(0.5) + return self.send(prompt, system_prompt, retry=True, retry_count=retry_count + 1) + else: + translater_logger.error(f"达到重试次数上限") + return "" def _send_prompt_count(self, prompt: str, system_prompt: None | str, count: PromptsCount) -> str: result = self.send(prompt, system_prompt) diff --git a/docutranslate/translater.py b/docutranslate/translater.py index 35b32d7..9f5e7ce 100644 --- a/docutranslate/translater.py +++ b/docutranslate/translater.py @@ -320,8 +320,8 @@ class FileTranslater: pico = f"" html_template = resource_path("template/markdown.html").read_text(encoding='utf-8') katex_css = f"" if not cdn else r"""""" - katex_js = f"" if not cdn else r"""""" - auto_render = f'' if not cdn else r"""""" + katex_js = f"" if not cdn else r"""""" + auto_render = f'' if not cdn else r"""""" # language=javascript renderMathInElement = r"""