Preserve semantic note layouts and checkpoint on provider quota failures

This commit is contained in:
Kai Tan
2026-10-08 09:35:22 +08:00
parent 4f124a0335
commit cab13568df
9 changed files with 111 additions and 7 deletions
+16 -3
View File
@@ -5,12 +5,13 @@ import time
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Optional
from openai import AsyncOpenAI
from tenacity import retry, stop_after_attempt, wait_exponential
from openai import AsyncOpenAI, APIStatusError
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_not_exception_type
from loguru import logger
from src.common.data_model import ManifestEntry
from src.common.utils import setup_logger, ensure_directory
from src.common.exceptions import ProviderAccessError
logger = setup_logger("llm_client")
@@ -238,10 +239,15 @@ Output Format:
"""Generic chat completion."""
return await self._make_request(system_prompt, user_prompt)
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10),
retry=retry_if_not_exception_type(ProviderAccessError))
async def _make_request(self, system_prompt: str, user_prompt: str) -> str:
if getattr(self, '_access_error', None):
raise self._access_error
await self.rate_limiter.acquire()
try:
if getattr(self, '_access_error', None):
raise self._access_error
options = {"max_completion_tokens": self.max_output_tokens}
if self.temperature is not None:
options["temperature"] = self.temperature
@@ -257,6 +263,13 @@ Output Format:
if choice.finish_reason != "stop" or not choice.message.content:
raise ValueError(f"Incomplete model response: {choice.finish_reason}")
return choice.message.content.strip()
except APIStatusError as exc:
if exc.status_code in {401, 402, 403}:
reason = 'quota exhausted' if exc.status_code == 402 else 'access denied'
self._access_error = ProviderAccessError(
f'API HTTP {exc.status_code}: {reason}; restore access and resume with the saved manifest.')
raise self._access_error from None
raise
finally:
self.rate_limiter.release()