client.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. """Aliyun OSS client for uploading agent run artifacts."""
  2. from __future__ import annotations
  3. from pathlib import Path
  4. import oss2
  5. from supply_infra.config import InfraSettings, get_infra_settings
  6. class OssClient:
  7. """Thin wrapper around oss2 Bucket."""
  8. def __init__(
  9. self,
  10. settings: InfraSettings | None = None,
  11. *,
  12. root_prefix: str | None = None,
  13. ) -> None:
  14. self.settings = settings or get_infra_settings()
  15. if not self.settings.aliyun_oss_access_key_id:
  16. raise ValueError("ALIYUN_OSS_ACCESS_KEY_ID is not configured")
  17. if not self.settings.aliyun_oss_access_key_secret:
  18. raise ValueError("ALIYUN_OSS_ACCESS_KEY_SECRET is not configured")
  19. endpoint = f"https://oss-{self.settings.aliyun_oss_region}.aliyuncs.com"
  20. auth = oss2.Auth(
  21. self.settings.aliyun_oss_access_key_id,
  22. self.settings.aliyun_oss_access_key_secret,
  23. )
  24. self._bucket = oss2.Bucket(
  25. auth,
  26. endpoint,
  27. self.settings.aliyun_oss_bucket,
  28. connect_timeout=self.settings.aliyun_oss_connect_timeout_seconds,
  29. )
  30. prefix = root_prefix if root_prefix is not None else self.settings.aliyun_oss_root_prefix
  31. self._root = prefix.strip("/")
  32. self._public_base = self.settings.aliyun_oss_public_base_url.rstrip("/")
  33. def object_key(self, *parts: str) -> str:
  34. cleaned = [self._root] if self._root else []
  35. for part in parts:
  36. p = str(part).strip("/")
  37. if p:
  38. cleaned.append(p)
  39. return "/".join(cleaned)
  40. def public_url(self, object_key: str) -> str:
  41. key = object_key.lstrip("/")
  42. return f"{self._public_base}/{key}"
  43. def upload_file(self, local_path: Path | str, object_key: str) -> str:
  44. """Upload a local file and return its public CDN URL."""
  45. path = Path(local_path)
  46. if not path.is_file():
  47. raise FileNotFoundError(f"File not found: {path}")
  48. self._bucket.put_object_from_file(object_key, str(path))
  49. return self.public_url(object_key)