feat: harden article summary timeout and fallback
This commit is contained in:
@@ -300,6 +300,7 @@ python scripts/apply_term_suggestions.py ^
|
|||||||
- `ARTICLE_SUMMARY_LLM_API_KEY`
|
- `ARTICLE_SUMMARY_LLM_API_KEY`
|
||||||
|
|
||||||
如果这些变量未设置,单篇总结会回退使用主流程中的 `LLM_*` / `OPENAI_*` 配置。
|
如果这些变量未设置,单篇总结会回退使用主流程中的 `LLM_*` / `OPENAI_*` 配置。
|
||||||
|
如果显式指定 DeepSeek 作为单篇总结模型且请求超时或失败,当前实现会自动再用主流程默认模型配置重试一次。
|
||||||
|
|
||||||
示例(PowerShell 风格):
|
示例(PowerShell 风格):
|
||||||
|
|
||||||
@@ -316,7 +317,8 @@ set ARTICLE_SUMMARY_LLM_MODEL=deepseek-chat
|
|||||||
python scripts/run_article_summaries.py ^
|
python scripts/run_article_summaries.py ^
|
||||||
--extracted outputs/freshrss/extracted/freshrss.extracted.json ^
|
--extracted outputs/freshrss/extracted/freshrss.extracted.json ^
|
||||||
--ids 12345 67890 ^
|
--ids 12345 67890 ^
|
||||||
--output-dir outputs/freshrss/single_summaries
|
--output-dir outputs/freshrss/single_summaries ^
|
||||||
|
--timeout 120
|
||||||
```
|
```
|
||||||
|
|
||||||
也可以通过 `summary_mcp.server` 暴露的 MCP 工具 `generate_article_summaries` 调用:
|
也可以通过 `summary_mcp.server` 暴露的 MCP 工具 `generate_article_summaries` 调用:
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def main() -> None:
|
|||||||
help="Directory to write per-article Markdown summaries",
|
help="Directory to write per-article Markdown summaries",
|
||||||
)
|
)
|
||||||
parser.add_argument("--max-retries", type=int, default=2, help="Maximum LLM retry attempts per article")
|
parser.add_argument("--max-retries", type=int, default=2, help="Maximum LLM retry attempts per article")
|
||||||
parser.add_argument("--timeout", type=float, default=60.0, help="LLM request timeout in seconds")
|
parser.add_argument("--timeout", type=float, default=120.0, help="LLM request timeout in seconds")
|
||||||
parser.add_argument("--api-key", type=str, default=None, help="Override article-summary LLM API key")
|
parser.add_argument("--api-key", type=str, default=None, help="Override article-summary LLM API key")
|
||||||
parser.add_argument("--model", type=str, default=None, help="Override article-summary LLM model")
|
parser.add_argument("--model", type=str, default=None, help="Override article-summary LLM model")
|
||||||
parser.add_argument("--api-url", type=str, default=None, help="Override article-summary LLM API URL/base URL")
|
parser.add_argument("--api-url", type=str, default=None, help="Override article-summary LLM API URL/base URL")
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ def generate_article_summaries(
|
|||||||
selected_ids: list[str],
|
selected_ids: list[str],
|
||||||
output_dir: str | None = None,
|
output_dir: str | None = None,
|
||||||
max_retries: int = 2,
|
max_retries: int = 2,
|
||||||
timeout_seconds: float = 60.0,
|
timeout_seconds: float = 120.0,
|
||||||
llm_api_key: str | None = None,
|
llm_api_key: str | None = None,
|
||||||
llm_model: str | None = None,
|
llm_model: str | None = None,
|
||||||
llm_api_url: str | None = None,
|
llm_api_url: str | None = None,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from dataclasses import dataclass
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, Mapping, Sequence
|
from typing import Iterable, Mapping, Sequence
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
from summary_mcp.core.summary_loop import run_loop_payload
|
from summary_mcp.core.summary_loop import run_loop_payload
|
||||||
from summary_mcp.validators.article_summary import validate_article_summary_payload
|
from summary_mcp.validators.article_summary import validate_article_summary_payload
|
||||||
|
|
||||||
@@ -18,7 +20,7 @@ DEFAULT_PROMPT_PATH = OUTPUT_ROOT / "prompts" / "article-summary-prompt.txt"
|
|||||||
class ArticleSummaryConfig:
|
class ArticleSummaryConfig:
|
||||||
prompt_path: Path = DEFAULT_PROMPT_PATH
|
prompt_path: Path = DEFAULT_PROMPT_PATH
|
||||||
max_retries: int = 2
|
max_retries: int = 2
|
||||||
timeout_seconds: float = 60.0
|
timeout_seconds: float = 120.0
|
||||||
llm_api_key: str | None = None
|
llm_api_key: str | None = None
|
||||||
llm_model: str | None = None
|
llm_model: str | None = None
|
||||||
llm_api_url: str | None = None
|
llm_api_url: str | None = None
|
||||||
@@ -187,17 +189,45 @@ def summarize_selected_articles(
|
|||||||
"warnings": warnings,
|
"warnings": warnings,
|
||||||
}
|
}
|
||||||
|
|
||||||
summary_exit_code, summary_payload, _ = run_loop_payload(
|
summary_exit_code = 1
|
||||||
extracted_payload=extracted_payload,
|
summary_payload = None
|
||||||
prompt_path=cfg.prompt_path,
|
|
||||||
max_retries=cfg.max_retries,
|
try:
|
||||||
timeout_seconds=cfg.timeout_seconds,
|
summary_exit_code, summary_payload, _ = run_loop_payload(
|
||||||
api_key=resolved_api_key,
|
extracted_payload=extracted_payload,
|
||||||
model=resolved_model,
|
prompt_path=cfg.prompt_path,
|
||||||
api_url=resolved_api_url,
|
max_retries=cfg.max_retries,
|
||||||
output_path=None,
|
timeout_seconds=cfg.timeout_seconds,
|
||||||
validator=validate_article_summary_payload,
|
api_key=resolved_api_key,
|
||||||
|
model=resolved_model,
|
||||||
|
api_url=resolved_api_url,
|
||||||
|
output_path=None,
|
||||||
|
validator=validate_article_summary_payload,
|
||||||
|
)
|
||||||
|
except httpx.TimeoutException:
|
||||||
|
summary_exit_code = 1
|
||||||
|
summary_payload = None
|
||||||
|
|
||||||
|
should_fallback = (
|
||||||
|
(summary_exit_code != 0 or summary_payload is None)
|
||||||
|
and resolved_model is not None
|
||||||
|
and resolved_model == (cfg.llm_model or model or resolved_model)
|
||||||
|
and resolved_model.startswith("deepseek")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if should_fallback:
|
||||||
|
summary_exit_code, summary_payload, _ = run_loop_payload(
|
||||||
|
extracted_payload=extracted_payload,
|
||||||
|
prompt_path=cfg.prompt_path,
|
||||||
|
max_retries=cfg.max_retries,
|
||||||
|
timeout_seconds=cfg.timeout_seconds,
|
||||||
|
api_key=None,
|
||||||
|
model=None,
|
||||||
|
api_url=None,
|
||||||
|
output_path=None,
|
||||||
|
validator=validate_article_summary_payload,
|
||||||
|
)
|
||||||
|
|
||||||
if summary_exit_code != 0 or summary_payload is None:
|
if summary_exit_code != 0 or summary_payload is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user