Metadata-Version: 2.4
Name: huace-aigc-oss-proxy-client
Version: 0.1.4
Summary: 华策 AIGC OSS Proxy Client - 统一 RustFS / 阿里云 OSS / 七牛云，支持下载降级与上传路由
Author-email: Huace <support@huace.com>
License: MIT
Project-URL: Homepage, https://github.com/huace/huace-aigc-oss-proxy-client
Project-URL: Repository, https://github.com/huace/huace-aigc-oss-proxy-client
Keywords: aigc,oss,huace,sdk,rustfs,aliyun,s3
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Provides-Extra: aliyun
Requires-Dist: oss2>=2.18.0; extra == "aliyun"
Provides-Extra: rustfs
Requires-Dist: boto3>=1.34.0; extra == "rustfs"
Provides-Extra: qiniu
Requires-Dist: qiniu>=7.12.0; extra == "qiniu"
Requires-Dist: requests>=2.20.0; extra == "qiniu"
Provides-Extra: all
Requires-Dist: oss2>=2.18.0; extra == "all"
Requires-Dist: boto3>=1.34.0; extra == "all"
Requires-Dist: qiniu>=7.12.0; extra == "all"
Requires-Dist: requests>=2.20.0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-env>=1.0.0; extra == "dev"
Requires-Dist: moto[s3]>=5.0.0; extra == "dev"
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"

# AIGC OSS Proxy Python SDK

[![Python Version](https://img.shields.io/pypi/pyversions/huace-aigc-oss-proxy-client.svg)](https://pypi.org/project/huace-aigc-oss-proxy-client/)

面向华策 AIGC 插件的统一对象存储 SDK：内网 **RustFS**（S3 兼容）、**阿里云 OSS**、**七牛云**，支持读路径按优先级降级、写路径按内外网指定后端。

详细设计见 [DESIGN.md](./DESIGN.md)。

## 安装

```bash
# 全量后端（RustFS + 阿里云 + 七牛）
pip install huace-aigc-oss-proxy-client[all]

# 仅阿里云 OSS
pip install huace-aigc-oss-proxy-client[aliyun]

# 仅 RustFS / S3 兼容内网（boto3）
pip install huace-aigc-oss-proxy-client[rustfs]

# 仅七牛云
pip install huace-aigc-oss-proxy-client[qiniu]
```

## 环境变量

`OssClient.from_env()` 从进程环境读取配置。可复制 [env.example](./env.example) 为 `.env` 后填入真实值。

### 路由与策略（常用）

| 变量 | 说明 | 示例 |
|------|------|------|
| `OSS_KEY_PREFIX` | 对象键统一前缀（兼容 `OSS_NAME_PREFIX`） | `plugin-tts` |
| `OSS_DOWNLOAD_PRIORITY` | 下载尝试顺序，逗号分隔 | `rustfs,aliyun` |
| `OSS_UPLOAD_INTRANET_BACKEND` | 内网上传后端 | `rustfs` |
| `OSS_UPLOAD_EXTRANET_BACKEND` | 外网上传后端 | `aliyun` |
| `OSS_FALLBACK_ENABLED` | 第一优先级无法访问时是否改用第二优先级 | `true` |
| `OSS_STICKY_SESSION_ENABLED` | 同进程记住下载成功的后端 | `true` |
| `OSS_CIRCUIT_BREAKER_ENABLED` | 是否启用熔断 | `false` |
| `OSS_MAX_OBJECT_SIZE_MB` | 单对象大小上限（MB） | `5120` |

### RustFS / S3 兼容内网

| 变量 | 说明 |
|------|------|
| `OSS_RUSTFS_ENABLED` | 是否启用 |
| `OSS_RUSTFS_ENDPOINT` | S3 API 地址 |
| `OSS_RUSTFS_ACCESS_KEY` | Access Key |
| `OSS_RUSTFS_SECRET_KEY` | Secret Key |
| `OSS_RUSTFS_BUCKET` | 桶名 |
| `OSS_RUSTFS_REGION` | 区域，默认 `us-east-1` |
| `OSS_RUSTFS_USE_SSL` | 是否 HTTPS |

### 阿里云 OSS

| 变量 | 说明 |
|------|------|
| `OSS_ALIYUN_ENABLED` | 是否启用 |
| `OSS_ALIYUN_ENDPOINT` | Endpoint（可用 `OSS_ENDPOINT`） |
| `OSS_ALIYUN_ACCESS_KEY_ID` | AK（可用 `OSS_ACCESS_KEY_ID`） |
| `OSS_ALIYUN_ACCESS_KEY_SECRET` | SK（可用 `OSS_ACCESS_KEY_SECRET`） |
| `OSS_ALIYUN_BUCKET` | 桶名（可用 `OSS_BUCKET_NAME`） |

### 七牛云（可选）

| 变量 | 说明 |
|------|------|
| `OSS_QINIU_ENABLED` | 是否启用 |
| `OSS_QINIU_ACCESS_KEY` | Access Key |
| `OSS_QINIU_SECRET_KEY` | Secret Key |
| `OSS_QINIU_BUCKET` | 空间名 |
| `OSS_QINIU_DOMAIN` | 访问域名 |

### 最小示例


```python
from huace_aigc_oss import OssClient

client = OssClient.from_env()
```

超时、重试等进阶项见 [env.example](./env.example)。

## 路由原理

### 下载 / `exists`（优先级链 + 降级）

`OSS_DOWNLOAD_PRIORITY` 定义尝试顺序，例如 `rustfs,aliyun` 表示 **第一优先级 RustFS，第二优先级阿里云**。

在 `OSS_FALLBACK_ENABLED=true`（默认）时：

1. 始终先访问 **第一优先级** 后端；
2. 若该后端 **无法完成读取**（连接失败、超时、对象不存在等），则 **改用第二优先级** 后端；
3. 若配置了第三、第四个后端，则按顺序依次尝试，直至成功或全部失败。

`exists` 与 `download` 使用同一套优先级链。`OSS_FALLBACK_ENABLED=false` 时只访问第一优先级，失败即结束。

同一进程内若某 key 曾在某后端读成功（`OSS_STICKY_SESSION_ENABLED=true`），后续该 key 会优先再试该后端，以减少重复降级。

### 上传（单后端，不降级）

| 调用方式 | 使用的后端 |
|----------|------------|
| `upload(..., mode=INTRANET)`（默认） | `OSS_UPLOAD_INTRANET_BACKEND` |
| `upload(..., mode=EXTRANET)` | `OSS_UPLOAD_EXTRANET_BACKEND` |

上传 **只写一个后端**；该后端失败时 **不会** 自动切换到另一个后端。

### 示例（`OSS_DOWNLOAD_PRIORITY=rustfs,aliyun`）

- **下载**：先 RustFS → RustFS 不可用时 **再** 阿里云。  
- **内网上传**：固定 RustFS。  
- **外网上传**（`UploadMode.EXTRANET`）：固定阿里云。

各后端需使用相同逻辑对象键（含 `OSS_KEY_PREFIX`）；路由只负责选路，不跨后端复制数据。

## 快速开始

```python
from huace_aigc_oss import OssClient, UploadMode

client = OssClient.from_env()

# 下载：先第一优先级，无法访问时用第二优先级（见「路由原理」）
local = client.download("task-1/output.wav", "/tmp/output.wav")

# 上传：仅内网指定后端，不切换第二后端
client.upload("task-1/output.wav", "/tmp/output.wav")

# 上传：强制外网后端（由 OSS_UPLOAD_EXTRANET_BACKEND 指定）
client.upload("task-1/output.wav", "/tmp/output.wav", mode=UploadMode.EXTRANET)

client.exists("task-1/output.wav")
```

## 插件集成示例

```python
from huace_aigc_oss import OssClient, UploadMode

_client = None

def get_client() -> OssClient:
    global _client
    if _client is None:
        _client = OssClient.from_env()
    return _client

def upload_local_file(local_path: str, oss_key: str, extranet: bool = False) -> str:
    mode = UploadMode.EXTRANET if extranet else UploadMode.INTRANET
    result = get_client().upload(oss_key, local_path, mode=mode)
    return result.url or oss_key
```

## 核心行为

| 操作 | 行为 |
|------|------|
| `download` | 先第一优先级；无法访问时用第二优先级（`OSS_FALLBACK_ENABLED=true`） |
| `upload` | 内网/外网各固定一个后端，失败不切换 |
| `exists` | 与 `download` 相同：第一优先级不可用时用第二优先级 |
