pattern_topic_build.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. """Read-only adapter for the Pattern topic-build detail API."""
  2. from __future__ import annotations
  3. from dataclasses import dataclass
  4. from pathlib import Path
  5. from typing import Any, Callable
  6. import httpx
  7. from core.config import env_value, load_env_file
  8. class TopicBuildSourceError(RuntimeError):
  9. """Raised when the upstream topic-build API cannot provide a valid snapshot."""
  10. @dataclass(frozen=True)
  11. class PatternTopicBuildConfig:
  12. base_url: str = "https://pattern.aiddit.com"
  13. timeout_seconds: float = 30.0
  14. @classmethod
  15. def from_env(cls, env_file: str | Path = ".env") -> "PatternTopicBuildConfig":
  16. file_env = load_env_file(env_file)
  17. return cls(
  18. base_url=env_value(
  19. "PATTERN_API_BASE_URL",
  20. file_env,
  21. "https://pattern.aiddit.com",
  22. ).rstrip("/"),
  23. timeout_seconds=float(
  24. env_value("PATTERN_API_TIMEOUT_SECONDS", file_env, "30")
  25. ),
  26. )
  27. class PatternTopicBuildSource:
  28. def __init__(
  29. self,
  30. config: PatternTopicBuildConfig,
  31. *,
  32. http_get: Callable[..., Any] = httpx.get,
  33. ) -> None:
  34. self.config = config
  35. self.http_get = http_get
  36. def fetch(self, topic_build_id: int) -> dict[str, Any]:
  37. url = f"{self.config.base_url}/api/pattern/topic_builds/{topic_build_id}"
  38. try:
  39. response = self.http_get(url, timeout=self.config.timeout_seconds)
  40. response.raise_for_status()
  41. payload = response.json()
  42. except (httpx.HTTPError, ValueError, TypeError) as exc:
  43. raise TopicBuildSourceError(f"选题接口读取失败:{exc}") from exc
  44. if not isinstance(payload, dict) or payload.get("success") is not True:
  45. raise TopicBuildSourceError("选题接口没有返回 success=true")
  46. topics = payload.get("topics")
  47. if not isinstance(topics, list) or not topics:
  48. raise TopicBuildSourceError("这次选题构建没有可用的 topics")
  49. payload = dict(payload)
  50. payload["source_url"] = url
  51. return payload