p0aiapi 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- p0aiapi-0.1.0/.github/workflows/ci.yml +21 -0
- p0aiapi-0.1.0/.github/workflows/publish.yml +49 -0
- p0aiapi-0.1.0/.gitignore +14 -0
- p0aiapi-0.1.0/PKG-INFO +227 -0
- p0aiapi-0.1.0/README.md +204 -0
- p0aiapi-0.1.0/docs/backend-compatibility.md +54 -0
- p0aiapi-0.1.0/docs/design.md +35 -0
- p0aiapi-0.1.0/docs/publishing.md +73 -0
- p0aiapi-0.1.0/docs/yapi-coverage.json +5960 -0
- p0aiapi-0.1.0/docs/yapi-coverage.md +87 -0
- p0aiapi-0.1.0/docs/yapi-inventory-2026-10-08.json +298 -0
- p0aiapi-0.1.0/examples/offline_demo.py +28 -0
- p0aiapi-0.1.0/pyproject.toml +46 -0
- p0aiapi-0.1.0/src/p0aiapi/__init__.py +46 -0
- p0aiapi-0.1.0/src/p0aiapi/__main__.py +3 -0
- p0aiapi-0.1.0/src/p0aiapi/audio.py +117 -0
- p0aiapi-0.1.0/src/p0aiapi/catalog.py +21 -0
- p0aiapi-0.1.0/src/p0aiapi/cli.py +78 -0
- p0aiapi-0.1.0/src/p0aiapi/client.py +498 -0
- p0aiapi-0.1.0/src/p0aiapi/exceptions.py +60 -0
- p0aiapi-0.1.0/src/p0aiapi/models.py +11 -0
- p0aiapi-0.1.0/src/p0aiapi/openapi.py +406 -0
- p0aiapi-0.1.0/src/p0aiapi/py.typed +0 -0
- p0aiapi-0.1.0/src/p0aiapi/resources.py +245 -0
- p0aiapi-0.1.0/src/p0aiapi/schemas/yapi-8.openapi.json +1826 -0
- p0aiapi-0.1.0/src/p0aiapi/streaming.py +194 -0
- p0aiapi-0.1.0/tests/test_audio.py +210 -0
- p0aiapi-0.1.0/tests/test_catalog.py +347 -0
- p0aiapi-0.1.0/tests/test_cli.py +171 -0
- p0aiapi-0.1.0/tests/test_client.py +670 -0
- p0aiapi-0.1.0/tests/test_integration.py +252 -0
- p0aiapi-0.1.0/tests/test_multipart_headers.py +148 -0
- p0aiapi-0.1.0/tests/test_openapi.py +364 -0
- p0aiapi-0.1.0/tests/test_package.py +124 -0
- p0aiapi-0.1.0/tests/test_resources.py +257 -0
- p0aiapi-0.1.0/tests/test_streaming.py +385 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Test and build
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
permissions:
|
|
4
|
+
contents: read
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: ${{ matrix.python }}
|
|
16
|
+
- run: python -m pip install -e '.[dev]'
|
|
17
|
+
- run: ruff check .
|
|
18
|
+
- run: ruff format --check .
|
|
19
|
+
- run: pytest
|
|
20
|
+
- run: python -m build
|
|
21
|
+
- run: python -m twine check dist/*
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Publish package
|
|
2
|
+
on:
|
|
3
|
+
workflow_dispatch:
|
|
4
|
+
inputs:
|
|
5
|
+
repository:
|
|
6
|
+
description: Package index
|
|
7
|
+
required: true
|
|
8
|
+
default: testpypi
|
|
9
|
+
type: choice
|
|
10
|
+
options: [testpypi, pypi]
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: '3.13'
|
|
21
|
+
- run: python -m pip install -e '.[dev]'
|
|
22
|
+
- run: ruff check .
|
|
23
|
+
- run: ruff format --check .
|
|
24
|
+
- run: pytest
|
|
25
|
+
- run: python -m build
|
|
26
|
+
- run: python -m twine check dist/*
|
|
27
|
+
- uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: distributions
|
|
30
|
+
path: dist/
|
|
31
|
+
publish:
|
|
32
|
+
needs: build
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
environment: ${{ inputs.repository }}
|
|
35
|
+
permissions:
|
|
36
|
+
id-token: write
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/download-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: distributions
|
|
41
|
+
path: dist/
|
|
42
|
+
- name: Publish to TestPyPI
|
|
43
|
+
if: inputs.repository == 'testpypi'
|
|
44
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
45
|
+
with:
|
|
46
|
+
repository-url: https://test.pypi.org/legacy/
|
|
47
|
+
- name: Publish to PyPI
|
|
48
|
+
if: inputs.repository == 'pypi'
|
|
49
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
p0aiapi-0.1.0/.gitignore
ADDED
p0aiapi-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: p0aiapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Sync and async Python client for the P0 AI API, with OpenAPI-driven calls
|
|
5
|
+
Keywords: aiapi,openapi,p0,sdk
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Typing :: Typed
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Requires-Dist: httpx<1,>=0.27
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest-asyncio<2,>=0.24; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest<10,>=8; extra == 'dev'
|
|
20
|
+
Requires-Dist: ruff>=0.9; extra == 'dev'
|
|
21
|
+
Requires-Dist: twine>=6; extra == 'dev'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# p0aiapi
|
|
25
|
+
|
|
26
|
+
P0 API 的 Python SDK,支持 Python 3.10+、同步 / asyncio 调用、任务轮询、音色上传、SSE 和 OpenAPI JSON 动态调用。运行时仅依赖 `httpx`。
|
|
27
|
+
|
|
28
|
+
发行包和导入名称均为 `p0aiapi`,版本 `0.1.0`。默认 API 地址为 **`https://p0-api.inaiai.com`**。
|
|
29
|
+
|
|
30
|
+
下面的服务接口均见于 [YApi 项目 8](https://yapi.inaiai.com/project/8/interface/api)。包内接口配置结合 YApi 与本地 backend 源码维护;接口是否可用仍取决于实际部署版本。SDK 不会把文档路径自动替换成其他接口。
|
|
31
|
+
|
|
32
|
+
## 安装与快速开始
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
python -m pip install p0aiapi
|
|
36
|
+
export P0_API_KEY='你的 API key'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
本地开发可使用 `python -m pip install -e .`。
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from p0aiapi import Client
|
|
43
|
+
|
|
44
|
+
with Client() as client:
|
|
45
|
+
task = client.audio.generate("你好,欢迎使用 P0 API。")
|
|
46
|
+
result = client.wait_for_task(task["task_id"], timeout=600, poll_interval=10)
|
|
47
|
+
print(result.get("out_data"))
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
需要连接其他部署时,设置 `P0_API_BASE_URL`,或传入 `Client(base_url="https://your-api.example.com", api_key="...")`。显式参数优先于环境变量,未设置地址时使用 `https://p0-api.inaiai.com`。地址填写服务根地址;反向代理的路径前缀会被保留。
|
|
51
|
+
|
|
52
|
+
认证使用 **`apikey` 请求头**。`api_key=` / `P0_API_KEY` 设置该请求头;可选的 `token=` / `P0_API_TOKEN` 额外发送 `Authorization: Bearer ...`,不替代 apikey。SDK 不内置密钥,初始化不会访问服务器。使用 `with` 管理连接,或在结束时调用 `close()`。
|
|
53
|
+
|
|
54
|
+
## 异步调用
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import asyncio
|
|
58
|
+
from p0aiapi import AsyncClient
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def main():
|
|
62
|
+
async with AsyncClient() as client:
|
|
63
|
+
task = await client.audio.generate("你好,这是异步语音生成示例。")
|
|
64
|
+
result = await client.wait_for_task(task["task_id"], timeout=600, poll_interval=10)
|
|
65
|
+
print(result)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
异步客户端在同一个 asyncio 事件循环中复用,结束时使用 `async with` 或 `await client.aclose()`。
|
|
72
|
+
|
|
73
|
+
## 常用接口
|
|
74
|
+
|
|
75
|
+
同步和异步客户端的方法同名;异步请求方法需要 `await`。
|
|
76
|
+
|
|
77
|
+
| 方法 | 用途 |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `client.images.generate(prompt, **fields)` | 文生图 |
|
|
80
|
+
| `client.novels.generate(description, **fields)` | 提交小说生成任务 |
|
|
81
|
+
| `client.novels.continue_from(file_url, **fields)` | 从公网 TXT / Markdown 文件续写 |
|
|
82
|
+
| `client.audio.list_voices()` | TTS v1 音色列表 |
|
|
83
|
+
| `client.audio.generate(text, **fields)` | TTS v1 语音生成 |
|
|
84
|
+
| `client.audio.upload_voice(spk_id, prompt_text, path)` | 上传音色样本及对应文本 |
|
|
85
|
+
| `client.tts.list_speakers()` | TTS v2 音色列表 |
|
|
86
|
+
| `client.tts.generate(text, spk_id, **fields)` | TTS v2 语音生成 |
|
|
87
|
+
| `client.tasks.get(task_id)` | 查询任务记录 |
|
|
88
|
+
| `client.tasks.batch([task_id, ...])` | 批量查询任务记录,发送裸 JSON 数组 |
|
|
89
|
+
| `client.tasks.events([task_id, ...])` | 逐条读取 SSE 任务事件 |
|
|
90
|
+
| `client.wait_for_task(task_id, timeout=600)` | 本地轮询,等待任务结束 |
|
|
91
|
+
|
|
92
|
+
生成方法使用后端要求的表单编码。`**fields` 透传可选参数并省略 `None`,例如具体接口支持的 `task_id`、`bid`、`app_id`、`notify_url`。实际字段和默认值以对应接口为准,响应保留原始结构及新增字段。
|
|
93
|
+
|
|
94
|
+
音色上传使用 multipart,SDK 会关闭自己打开的文件;直接调用 `request(files=...)` 时由调用方管理文件句柄。
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
with Client() as client:
|
|
98
|
+
voice = client.audio.upload_voice("my-voice", "音频中的原文", "sample.wav")
|
|
99
|
+
print(voice)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
任务查询使用 `/api/public/task`;同一 `task_id` 存在多条记录时,返回记录由服务端决定。批量查询使用 YApi [3469](https://yapi.inaiai.com/project/8/interface/api/3469) 的 `/api/public/task/details`:传入非空 `task_id` 列表,成功响应直接返回任务对象数组。未命中的 ID 被省略,结果顺序可能与输入不同,同一 ID 可能返回多条记录;所有 ID 均未命中时返回 404。请按 `task_id` 关联结果,不能用内部 `uuid` 或业务 `bid` 替代。
|
|
103
|
+
|
|
104
|
+
## OpenAPI 接口配置
|
|
105
|
+
|
|
106
|
+
### 使用包内配置
|
|
107
|
+
|
|
108
|
+
`load_catalog()` 离线加载随包安装的接口配置,使用 `yapi_接口ID` 作为 operationId。配置只包含本版本支持的 YApi 接口,请求参数按本地 backend 签名校正,不包含文档中的密钥和示例值。
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from p0aiapi import Client, load_catalog
|
|
112
|
+
|
|
113
|
+
with Client(openapi=load_catalog()) as client:
|
|
114
|
+
voices = client.call("yapi_2526")
|
|
115
|
+
tasks = client.call_endpoint("POST", "/api/public/task/details", json=["task-1", "task-2"])
|
|
116
|
+
print(voices, tasks)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
p0aiapi operations
|
|
121
|
+
p0aiapi export-catalog --output openapi.catalog.json
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`call_endpoint()` 通过方法和路径模板定位已加载的操作。动态调用显式区分 `path_params`、`params`(query)、`headers`、`cookies`、`json`、`data`(form)和 `files`。表单接口使用 `data`,不能改用 `json`。动态调用共用客户端认证、超时和错误处理。
|
|
125
|
+
|
|
126
|
+
### 下载部署配置
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
p0aiapi fetch-schema --output openapi.json
|
|
130
|
+
p0aiapi operations openapi.json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
CLI 同样使用默认 API 地址和 `P0_API_BASE_URL` / `P0_API_KEY` / `P0_API_TOKEN`。文档路径默认 `/openapi.json`,可通过 `--path` 指定。下载检查版本和操作目录结构后原子替换文件,失败保留原文件。
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from p0aiapi import Client
|
|
137
|
+
|
|
138
|
+
with Client(openapi="openapi.json") as client:
|
|
139
|
+
print(list(client.schema.operations))
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
也可传入字典或 `OpenAPISchema`。`client.load_openapi("new.json")` 从本地更新;`client.refresh_openapi()` / `await client.refresh_openapi()` 显式从当前部署下载。加载或刷新会替换当前配置,验证失败时保留原配置;部署文档可能隐藏部分路由,不自动与包内目录合并。
|
|
143
|
+
|
|
144
|
+
### 升级前比较
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
p0aiapi fetch-schema --output openapi.next.json
|
|
148
|
+
p0aiapi diff openapi.json openapi.next.json --fail-on-change
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
输出 `added` / `removed` / `changed` operationId 列表,包含引用模型变化。`--fail-on-change` 遇到任何差异返回退出码 2,但不自动判定业务兼容性。生产项目建议把快照与代码一起版本控制,测试后升级。
|
|
152
|
+
|
|
153
|
+
支持 OpenAPI 3.0 / 3.1 JSON、本地 `$ref`、JSON / URL-encoded / multipart 请求、简单路径参数和普通 query 数组。不支持 YAML、Swagger 2.0 或 deepObject 等复杂序列化;不做完整 JSON Schema 校验,也不生成接口专属类型。便利方法不依赖 OpenAPI 配置,响应不按文档强制转换类型。
|
|
154
|
+
|
|
155
|
+
`request_raw()` 返回完整读取的 `httpx.Response`;`request()` 根据响应类型返回 JSON、文本、字节或 `None`。API 请求地址由客户端的 `base_url` 决定,schema 的 `servers` 不覆盖它。
|
|
156
|
+
|
|
157
|
+
## SSE 任务事件
|
|
158
|
+
|
|
159
|
+
使用 `tasks.events()`、`stream_events()` 或 `stream_call()` 增量读取事件。普通 `request()` / `call()` 会等待响应结束。流式读取不自动重试或重连,`timeout=60` 可设置读取空闲超时。
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from contextlib import closing
|
|
163
|
+
from p0aiapi import Client
|
|
164
|
+
|
|
165
|
+
with Client() as client:
|
|
166
|
+
with closing(client.tasks.events(["task-1", "task-2"], timeout=60)) as events:
|
|
167
|
+
for event in events:
|
|
168
|
+
print(event.event, event.data)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
异步版本返回异步迭代器,创建时不加 `await`:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from contextlib import aclosing
|
|
175
|
+
from p0aiapi import AsyncClient
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
async def watch():
|
|
179
|
+
async with AsyncClient() as client:
|
|
180
|
+
async with aclosing(client.tasks.events(["task-1"])) as events:
|
|
181
|
+
async for event in events:
|
|
182
|
+
print(event.data)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
迭代结束、异常或显式 `close()` / `aclose()` 会释放连接;提前退出循环时使用上面的上下文管理器。`ServerSentEvent` 保留原始 `data`、事件名称、id 和 retry;仅在数据确实为 JSON 时调用 `event.json()`。服务端事件名称可能与任务终态不同,最终结果使用任务 `status` 判断或调用 `wait_for_task()`。
|
|
186
|
+
|
|
187
|
+
## 错误、重试和任务状态
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from p0aiapi import APIError, Client, TaskFailedError, TaskTimeoutError
|
|
191
|
+
|
|
192
|
+
with Client() as client:
|
|
193
|
+
try:
|
|
194
|
+
task = client.audio.generate("你好,世界。")
|
|
195
|
+
result = client.wait_for_task(task["task_id"], timeout=600, poll_interval=10)
|
|
196
|
+
except APIError as exc:
|
|
197
|
+
print(exc.status_code)
|
|
198
|
+
if isinstance(exc.body, dict) and exc.body.get("submission_uncertain"):
|
|
199
|
+
print("任务可能已入队,请先查询:", exc.body.get("task_id"))
|
|
200
|
+
except TaskFailedError as exc:
|
|
201
|
+
print(exc.task.get("error"))
|
|
202
|
+
except TaskTimeoutError as exc:
|
|
203
|
+
print("停止本地等待,后端任务仍在执行", exc.last_task)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- HTTP 错误:`AuthenticationError`(401/403)、`NotFoundError`(404)、`ValidationError`(422)、`RateLimitError`(429)、`ServerError`(5xx)均继承 `APIError`,保留 `.body`、`.response`、`.request_id`。默认异常文字不回显凭据或响应内容。
|
|
207
|
+
- 连接及 HTTP 超时为 `TransportError`;坏 JSON、解压失败或不适用的响应为 `P0ResponseError`。这些错误及任务错误继承 `P0Error`。本地配置错误和 `SchemaError` 继承 `ValueError`。
|
|
208
|
+
- 无请求体的 GET/HEAD/OPTIONS 默认最多重试 2 次,覆盖连接故障和 408/429/500/502/503/504;读取 `Retry-After` 并退避,单次最多等待 30 秒。POST 等写请求不自动重试。若返回 `submission_uncertain=true`,应先查询任务,避免重复提交。
|
|
209
|
+
- 状态 `0` 等待、`1` 处理、`3` 水印处理中,继续等待;`2` 成功;`-1` 抛出 `TaskFailedError`,或通过 `raise_on_failure=False` 返回失败记录。数字字符串也受支持,未知状态继续等待至超时。
|
|
210
|
+
- 轮询在总预算内处理临时连接故障及上述可重试 HTTP 状态;404 和认证错误立即抛出。任务查询可能缓存约 10 秒,建议 `poll_interval=10`。超时只停止本地等待,不取消远端任务。
|
|
211
|
+
- 同步 HTTP 超时按网络阶段计算,正在执行的请求可能超过轮询截止时间;异步等待使用总 deadline,可被调用方取消。
|
|
212
|
+
- API 路径必须为 `/...`;不接受外部 URL,也不自动跟随跳转。下载外部结果请使用独立的 HTTP 客户端。
|
|
213
|
+
|
|
214
|
+
## 开发和发布
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
python -m pip install -e '.[dev]'
|
|
218
|
+
pytest
|
|
219
|
+
ruff check .
|
|
220
|
+
ruff format --check .
|
|
221
|
+
python -m build
|
|
222
|
+
python -m twine check dist/p0aiapi-0.1.0-py3-none-any.whl dist/p0aiapi-0.1.0.tar.gz
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
测试使用模拟 HTTP 验证请求、响应、任务状态和错误处理,不提交真实生成任务。离线演示:`python examples/offline_demo.py`。
|
|
226
|
+
|
|
227
|
+
[发布步骤](docs/publishing.md) · [架构及接口维护](docs/design.md)。GitHub Actions 包含测试矩阵和手动 TestPyPI / PyPI 发布流程。
|
p0aiapi-0.1.0/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# p0aiapi
|
|
2
|
+
|
|
3
|
+
P0 API 的 Python SDK,支持 Python 3.10+、同步 / asyncio 调用、任务轮询、音色上传、SSE 和 OpenAPI JSON 动态调用。运行时仅依赖 `httpx`。
|
|
4
|
+
|
|
5
|
+
发行包和导入名称均为 `p0aiapi`,版本 `0.1.0`。默认 API 地址为 **`https://p0-api.inaiai.com`**。
|
|
6
|
+
|
|
7
|
+
下面的服务接口均见于 [YApi 项目 8](https://yapi.inaiai.com/project/8/interface/api)。包内接口配置结合 YApi 与本地 backend 源码维护;接口是否可用仍取决于实际部署版本。SDK 不会把文档路径自动替换成其他接口。
|
|
8
|
+
|
|
9
|
+
## 安装与快速开始
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
python -m pip install p0aiapi
|
|
13
|
+
export P0_API_KEY='你的 API key'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
本地开发可使用 `python -m pip install -e .`。
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from p0aiapi import Client
|
|
20
|
+
|
|
21
|
+
with Client() as client:
|
|
22
|
+
task = client.audio.generate("你好,欢迎使用 P0 API。")
|
|
23
|
+
result = client.wait_for_task(task["task_id"], timeout=600, poll_interval=10)
|
|
24
|
+
print(result.get("out_data"))
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
需要连接其他部署时,设置 `P0_API_BASE_URL`,或传入 `Client(base_url="https://your-api.example.com", api_key="...")`。显式参数优先于环境变量,未设置地址时使用 `https://p0-api.inaiai.com`。地址填写服务根地址;反向代理的路径前缀会被保留。
|
|
28
|
+
|
|
29
|
+
认证使用 **`apikey` 请求头**。`api_key=` / `P0_API_KEY` 设置该请求头;可选的 `token=` / `P0_API_TOKEN` 额外发送 `Authorization: Bearer ...`,不替代 apikey。SDK 不内置密钥,初始化不会访问服务器。使用 `with` 管理连接,或在结束时调用 `close()`。
|
|
30
|
+
|
|
31
|
+
## 异步调用
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
from p0aiapi import AsyncClient
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def main():
|
|
39
|
+
async with AsyncClient() as client:
|
|
40
|
+
task = await client.audio.generate("你好,这是异步语音生成示例。")
|
|
41
|
+
result = await client.wait_for_task(task["task_id"], timeout=600, poll_interval=10)
|
|
42
|
+
print(result)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
asyncio.run(main())
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
异步客户端在同一个 asyncio 事件循环中复用,结束时使用 `async with` 或 `await client.aclose()`。
|
|
49
|
+
|
|
50
|
+
## 常用接口
|
|
51
|
+
|
|
52
|
+
同步和异步客户端的方法同名;异步请求方法需要 `await`。
|
|
53
|
+
|
|
54
|
+
| 方法 | 用途 |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `client.images.generate(prompt, **fields)` | 文生图 |
|
|
57
|
+
| `client.novels.generate(description, **fields)` | 提交小说生成任务 |
|
|
58
|
+
| `client.novels.continue_from(file_url, **fields)` | 从公网 TXT / Markdown 文件续写 |
|
|
59
|
+
| `client.audio.list_voices()` | TTS v1 音色列表 |
|
|
60
|
+
| `client.audio.generate(text, **fields)` | TTS v1 语音生成 |
|
|
61
|
+
| `client.audio.upload_voice(spk_id, prompt_text, path)` | 上传音色样本及对应文本 |
|
|
62
|
+
| `client.tts.list_speakers()` | TTS v2 音色列表 |
|
|
63
|
+
| `client.tts.generate(text, spk_id, **fields)` | TTS v2 语音生成 |
|
|
64
|
+
| `client.tasks.get(task_id)` | 查询任务记录 |
|
|
65
|
+
| `client.tasks.batch([task_id, ...])` | 批量查询任务记录,发送裸 JSON 数组 |
|
|
66
|
+
| `client.tasks.events([task_id, ...])` | 逐条读取 SSE 任务事件 |
|
|
67
|
+
| `client.wait_for_task(task_id, timeout=600)` | 本地轮询,等待任务结束 |
|
|
68
|
+
|
|
69
|
+
生成方法使用后端要求的表单编码。`**fields` 透传可选参数并省略 `None`,例如具体接口支持的 `task_id`、`bid`、`app_id`、`notify_url`。实际字段和默认值以对应接口为准,响应保留原始结构及新增字段。
|
|
70
|
+
|
|
71
|
+
音色上传使用 multipart,SDK 会关闭自己打开的文件;直接调用 `request(files=...)` 时由调用方管理文件句柄。
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
with Client() as client:
|
|
75
|
+
voice = client.audio.upload_voice("my-voice", "音频中的原文", "sample.wav")
|
|
76
|
+
print(voice)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
任务查询使用 `/api/public/task`;同一 `task_id` 存在多条记录时,返回记录由服务端决定。批量查询使用 YApi [3469](https://yapi.inaiai.com/project/8/interface/api/3469) 的 `/api/public/task/details`:传入非空 `task_id` 列表,成功响应直接返回任务对象数组。未命中的 ID 被省略,结果顺序可能与输入不同,同一 ID 可能返回多条记录;所有 ID 均未命中时返回 404。请按 `task_id` 关联结果,不能用内部 `uuid` 或业务 `bid` 替代。
|
|
80
|
+
|
|
81
|
+
## OpenAPI 接口配置
|
|
82
|
+
|
|
83
|
+
### 使用包内配置
|
|
84
|
+
|
|
85
|
+
`load_catalog()` 离线加载随包安装的接口配置,使用 `yapi_接口ID` 作为 operationId。配置只包含本版本支持的 YApi 接口,请求参数按本地 backend 签名校正,不包含文档中的密钥和示例值。
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from p0aiapi import Client, load_catalog
|
|
89
|
+
|
|
90
|
+
with Client(openapi=load_catalog()) as client:
|
|
91
|
+
voices = client.call("yapi_2526")
|
|
92
|
+
tasks = client.call_endpoint("POST", "/api/public/task/details", json=["task-1", "task-2"])
|
|
93
|
+
print(voices, tasks)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
p0aiapi operations
|
|
98
|
+
p0aiapi export-catalog --output openapi.catalog.json
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`call_endpoint()` 通过方法和路径模板定位已加载的操作。动态调用显式区分 `path_params`、`params`(query)、`headers`、`cookies`、`json`、`data`(form)和 `files`。表单接口使用 `data`,不能改用 `json`。动态调用共用客户端认证、超时和错误处理。
|
|
102
|
+
|
|
103
|
+
### 下载部署配置
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
p0aiapi fetch-schema --output openapi.json
|
|
107
|
+
p0aiapi operations openapi.json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
CLI 同样使用默认 API 地址和 `P0_API_BASE_URL` / `P0_API_KEY` / `P0_API_TOKEN`。文档路径默认 `/openapi.json`,可通过 `--path` 指定。下载检查版本和操作目录结构后原子替换文件,失败保留原文件。
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from p0aiapi import Client
|
|
114
|
+
|
|
115
|
+
with Client(openapi="openapi.json") as client:
|
|
116
|
+
print(list(client.schema.operations))
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
也可传入字典或 `OpenAPISchema`。`client.load_openapi("new.json")` 从本地更新;`client.refresh_openapi()` / `await client.refresh_openapi()` 显式从当前部署下载。加载或刷新会替换当前配置,验证失败时保留原配置;部署文档可能隐藏部分路由,不自动与包内目录合并。
|
|
120
|
+
|
|
121
|
+
### 升级前比较
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
p0aiapi fetch-schema --output openapi.next.json
|
|
125
|
+
p0aiapi diff openapi.json openapi.next.json --fail-on-change
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
输出 `added` / `removed` / `changed` operationId 列表,包含引用模型变化。`--fail-on-change` 遇到任何差异返回退出码 2,但不自动判定业务兼容性。生产项目建议把快照与代码一起版本控制,测试后升级。
|
|
129
|
+
|
|
130
|
+
支持 OpenAPI 3.0 / 3.1 JSON、本地 `$ref`、JSON / URL-encoded / multipart 请求、简单路径参数和普通 query 数组。不支持 YAML、Swagger 2.0 或 deepObject 等复杂序列化;不做完整 JSON Schema 校验,也不生成接口专属类型。便利方法不依赖 OpenAPI 配置,响应不按文档强制转换类型。
|
|
131
|
+
|
|
132
|
+
`request_raw()` 返回完整读取的 `httpx.Response`;`request()` 根据响应类型返回 JSON、文本、字节或 `None`。API 请求地址由客户端的 `base_url` 决定,schema 的 `servers` 不覆盖它。
|
|
133
|
+
|
|
134
|
+
## SSE 任务事件
|
|
135
|
+
|
|
136
|
+
使用 `tasks.events()`、`stream_events()` 或 `stream_call()` 增量读取事件。普通 `request()` / `call()` 会等待响应结束。流式读取不自动重试或重连,`timeout=60` 可设置读取空闲超时。
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from contextlib import closing
|
|
140
|
+
from p0aiapi import Client
|
|
141
|
+
|
|
142
|
+
with Client() as client:
|
|
143
|
+
with closing(client.tasks.events(["task-1", "task-2"], timeout=60)) as events:
|
|
144
|
+
for event in events:
|
|
145
|
+
print(event.event, event.data)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
异步版本返回异步迭代器,创建时不加 `await`:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from contextlib import aclosing
|
|
152
|
+
from p0aiapi import AsyncClient
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
async def watch():
|
|
156
|
+
async with AsyncClient() as client:
|
|
157
|
+
async with aclosing(client.tasks.events(["task-1"])) as events:
|
|
158
|
+
async for event in events:
|
|
159
|
+
print(event.data)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
迭代结束、异常或显式 `close()` / `aclose()` 会释放连接;提前退出循环时使用上面的上下文管理器。`ServerSentEvent` 保留原始 `data`、事件名称、id 和 retry;仅在数据确实为 JSON 时调用 `event.json()`。服务端事件名称可能与任务终态不同,最终结果使用任务 `status` 判断或调用 `wait_for_task()`。
|
|
163
|
+
|
|
164
|
+
## 错误、重试和任务状态
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from p0aiapi import APIError, Client, TaskFailedError, TaskTimeoutError
|
|
168
|
+
|
|
169
|
+
with Client() as client:
|
|
170
|
+
try:
|
|
171
|
+
task = client.audio.generate("你好,世界。")
|
|
172
|
+
result = client.wait_for_task(task["task_id"], timeout=600, poll_interval=10)
|
|
173
|
+
except APIError as exc:
|
|
174
|
+
print(exc.status_code)
|
|
175
|
+
if isinstance(exc.body, dict) and exc.body.get("submission_uncertain"):
|
|
176
|
+
print("任务可能已入队,请先查询:", exc.body.get("task_id"))
|
|
177
|
+
except TaskFailedError as exc:
|
|
178
|
+
print(exc.task.get("error"))
|
|
179
|
+
except TaskTimeoutError as exc:
|
|
180
|
+
print("停止本地等待,后端任务仍在执行", exc.last_task)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- HTTP 错误:`AuthenticationError`(401/403)、`NotFoundError`(404)、`ValidationError`(422)、`RateLimitError`(429)、`ServerError`(5xx)均继承 `APIError`,保留 `.body`、`.response`、`.request_id`。默认异常文字不回显凭据或响应内容。
|
|
184
|
+
- 连接及 HTTP 超时为 `TransportError`;坏 JSON、解压失败或不适用的响应为 `P0ResponseError`。这些错误及任务错误继承 `P0Error`。本地配置错误和 `SchemaError` 继承 `ValueError`。
|
|
185
|
+
- 无请求体的 GET/HEAD/OPTIONS 默认最多重试 2 次,覆盖连接故障和 408/429/500/502/503/504;读取 `Retry-After` 并退避,单次最多等待 30 秒。POST 等写请求不自动重试。若返回 `submission_uncertain=true`,应先查询任务,避免重复提交。
|
|
186
|
+
- 状态 `0` 等待、`1` 处理、`3` 水印处理中,继续等待;`2` 成功;`-1` 抛出 `TaskFailedError`,或通过 `raise_on_failure=False` 返回失败记录。数字字符串也受支持,未知状态继续等待至超时。
|
|
187
|
+
- 轮询在总预算内处理临时连接故障及上述可重试 HTTP 状态;404 和认证错误立即抛出。任务查询可能缓存约 10 秒,建议 `poll_interval=10`。超时只停止本地等待,不取消远端任务。
|
|
188
|
+
- 同步 HTTP 超时按网络阶段计算,正在执行的请求可能超过轮询截止时间;异步等待使用总 deadline,可被调用方取消。
|
|
189
|
+
- API 路径必须为 `/...`;不接受外部 URL,也不自动跟随跳转。下载外部结果请使用独立的 HTTP 客户端。
|
|
190
|
+
|
|
191
|
+
## 开发和发布
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
python -m pip install -e '.[dev]'
|
|
195
|
+
pytest
|
|
196
|
+
ruff check .
|
|
197
|
+
ruff format --check .
|
|
198
|
+
python -m build
|
|
199
|
+
python -m twine check dist/p0aiapi-0.1.0-py3-none-any.whl dist/p0aiapi-0.1.0.tar.gz
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
测试使用模拟 HTTP 验证请求、响应、任务状态和错误处理,不提交真实生成任务。离线演示:`python examples/offline_demo.py`。
|
|
203
|
+
|
|
204
|
+
[发布步骤](docs/publishing.md) · [架构及接口维护](docs/design.md)。GitHub Actions 包含测试矩阵和手动 TestPyPI / PyPI 发布流程。
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# backend 适配核对(2026-10-08)
|
|
2
|
+
|
|
3
|
+
本地 `/Users/allenflux/PycharmProjects/backend` 已具备 SDK 所用的主要请求契约,121 个相关离线测试通过。默认域名的线上 OpenAPI 与本地公开契约有明确差异,部署仍需核对或同步;不能据此认定线上已完成全部适配,也不能仅因接口未出现在 OpenAPI 就认定路由不存在。
|
|
4
|
+
|
|
5
|
+
本次仅读取后端源码并使用内存、AST 提取和 mock 测试,没有修改 backend,没有创建线上生成任务,没有调用真实模型、MQ、数据库或音色服务。线上检查只读取 [默认服务的 OpenAPI](https://p0-api.inaiai.com/openapi.json)(HTTP 200,20 个 paths,`info.title=API`,`info.version=0.0.0`)。
|
|
6
|
+
|
|
7
|
+
## 本次 SDK 已完成的适配
|
|
8
|
+
|
|
9
|
+
- `tasks.get()` 和 `wait_for_task()` 使用 `GET /api/public/task`;`tasks.batch()` 使用 `POST /api/public/task/details`,请求体为裸 JSON 字符串数组。同步、异步客户端保持一致。这两个 public 接口同时存在于 YApi、线上公开 schema 和本地后端。
|
|
10
|
+
- public 单任务查询没有按时间选最新记录的保证:它直接调用 `storage.find_one()`。SDK 已移除“总是返回最新任务”的表述。证据:[public 查询](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:654)、[缓存查询实现](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:687)、[存储实现](/Users/allenflux/PycharmProjects/backend/src/storage/storage.py:51)、[批量请求](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:721)。
|
|
11
|
+
- `api_key` 以 `apikey` 请求头发送,匹配[后端鉴权](/Users/allenflux/PycharmProjects/backend/src/middleware/auth.py:173)。只提供 Bearer token 不能替代此后端要求的 `apikey`。
|
|
12
|
+
- 当前 27 个内置 YApi catalog 操作均可对应本地已注册的路由。路由注册证据:[server](/Users/allenflux/PycharmProjects/backend/src/server.py:196)。表单字段、音频 v1 multipart 文件名、批量 JSON 数组、任务 `task_id`/`uuid` 分离均与本地契约吻合。
|
|
13
|
+
|
|
14
|
+
## 线上部署需要核对的部分
|
|
15
|
+
|
|
16
|
+
| 接口或能力 | 本地证据 | 线上公开 schema |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| 图片生成和图生图 | [workflow.py:1622](/Users/allenflux/PycharmProjects/backend/src/routers/workflow.py:1622)、[workflow.py:1742](/Users/allenflux/PycharmProjects/backend/src/routers/workflow.py:1742) | 无 `/api/public/generate/text2image`、`/api/public/generate/image2image`;本地均公开 |
|
|
19
|
+
| TTS v2 生成、音色列表 | [public.py:2736](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:2736)、[public.py:2660](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:2660) | 无 `/api/public/generate/ttsv2`、`/api/public/generate/ttsv2/speakers`;本地均公开 |
|
|
20
|
+
| 小说续写 | [public.py:2363](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:2363) | 无 `/api/public/continue/novel`;本地公开 |
|
|
21
|
+
| 按 UUID 查询任务 | [public.py:673](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:673) | 无 `/api/public/task/uuid`;本地公开 |
|
|
22
|
+
| workflow 健康检查、按业务编号查询等 | [workflow.py:102](/Users/allenflux/PycharmProjects/backend/src/routers/workflow.py:102)、[workflow.py:2187](/Users/allenflux/PycharmProjects/backend/src/routers/workflow.py:2187) | 无任何 `/api/workflow/*` 路径 |
|
|
23
|
+
| 文件上传、下载和分页任务列表 | [utils.py:77](/Users/allenflux/PycharmProjects/backend/src/routers/utils.py:77)、[tasks.py:84](/Users/allenflux/PycharmProjects/backend/src/routers/tasks.py:84) | 本地明确 `include_in_schema=False`;schema 无法确认这些隐藏路由的部署状态 |
|
|
24
|
+
|
|
25
|
+
字段也有差异:线上 `POST /api/public/generate/audio` 公开的表单字段为 `text`、`spk_id`、`bid`、`app_id`、`notify_url`,没有本地新增的 `task_id`、`hash_key`。线上小说生成 schema 没有本地新增的 `task_id`、`hash_key`、`is_encrypt`。因此,传入自定义 `task_id` 的线上行为仍需确认。对应本地证据:[音频](/Users/allenflux/PycharmProjects/backend/src/routers/workflow.py:3658)、[小说](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:2258)。
|
|
26
|
+
|
|
27
|
+
SSE 源码把任务字典直接作为事件 `data`,SDK 保留原始字符串,不承诺 `event.json()` 必定成功。证据:[任务 SSE](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:810)。
|
|
28
|
+
|
|
29
|
+
另一个需修的本地后端问题:TTS v2 把调用方 `api_key` 放入持久化任务,并原样返回。使用虚构密钥和完全 mock 的路由探针已复现;免鉴权任务批量、列表、SSE 查询也未清理此字段。发布对应后端前应过滤响应中的 `api_key`。证据:[TTS v2](/Users/allenflux/PycharmProjects/backend/src/routers/public.py:2772)、[任务序列化](/Users/allenflux/PycharmProjects/backend/src/document.py:43)、[免鉴权查询路径](/Users/allenflux/PycharmProjects/backend/src/middleware/auth.py:23)。
|
|
30
|
+
|
|
31
|
+
## 离线测试结果与边界
|
|
32
|
+
|
|
33
|
+
从 backend 目录执行:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
.venv/bin/python -B -m pytest -q -p no:cacheprovider \
|
|
37
|
+
tests/test_workflow_task_queries.py \
|
|
38
|
+
tests/test_public_task_identity.py \
|
|
39
|
+
tests/test_task_uuid_isolation.py \
|
|
40
|
+
tests/test_tasks_list.py \
|
|
41
|
+
tests/test_tts_v1_route_migration_contract.py \
|
|
42
|
+
tests/test_novel_continuation_api.py \
|
|
43
|
+
tests/test_novel_generation_limits.py
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
结果:**108 passed**,1 条 Starlette/AnyIO 弃用警告。覆盖任务查询、批量请求、任务身份、分页、TTS v1 迁移、小说续写与字数限制。
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
.venv/bin/python -B -m pytest -q -p no:cacheprovider tests/test_tts_v1_s3.py
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
结果:**13 passed**,使用 fake S3 验证音色列表、存储和历史名称适配。
|
|
53
|
+
|
|
54
|
+
额外尝试 `tests/test_novel_file.py`、`tests/test_novel_file_security.py` 时,本地 backend `.venv` 缺少 `aiohttp`,两个模块无法收集;环境也缺少 `sse_starlette`,未完成完整 SSE 运行验证。未安装依赖、启动完整 server 或验证生产业务执行,因此测试通过范围不包含完整部署和线上生成结果。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# API 更新与兼容性设计
|
|
2
|
+
|
|
3
|
+
## 客户端、便利方法与 OpenAPI 快照
|
|
4
|
+
|
|
5
|
+
客户端负责连接、认证、超时、读取重试、错误和任务等待。`resources.py`、`audio.py` 为常用 YApi 接口提供稳定名称;`openapi.py` 读取部署导出的 JSON,通过 operationId 或 method/path 调用接口;`streaming.py` 增量解析 SSE,并管理流连接的关闭。
|
|
6
|
+
|
|
7
|
+
默认地址为 `https://p0-api.inaiai.com`,可通过 `P0_API_BASE_URL` 或显式 `base_url` 覆盖。apikey 由 `api_key` / `P0_API_KEY` 设置,可选 bearer token 由 `token` / `P0_API_TOKEN` 设置。SDK 不保存实际密钥。
|
|
8
|
+
|
|
9
|
+
后端接口仍在变化,部分响应模型与真实数据有类型差异。因此响应保留原始字典和新增字段,当前没有为全部接口生成 Python 类。后续契约稳定后可增加类型层。
|
|
10
|
+
|
|
11
|
+
OpenAPI 本地加载不访问网络,只有显式 refresh 才下载。schema 的 servers 不参与目的地址选择,外部引用不下载。先检查新文档的版本和操作目录结构,再替换现有目录,失败保留旧版本。正文及响应 schema 不做完整校验,不能把加载成功视为完整契约校验通过。
|
|
12
|
+
|
|
13
|
+
## 包内接口配置
|
|
14
|
+
|
|
15
|
+
`schemas/yapi-8.openapi.json` 包含本版本支持的 YApi 接口子集,使用稳定的 `yapi_ID` 命名及本地 backend 的真实参数位置。2026-10-08 通过 YApi 当前分类页面复核接口 ID、方法和路径,新增批量任务查询 `yapi_3469`。其余参数定义沿用 2026-10-01 的脱敏快照并结合源码校正。文档中的密钥及示例值不进入发行包。
|
|
16
|
+
|
|
17
|
+
任务便利方法与 YApi 的 public 路径一致:单条查询使用 `/api/public/task`,批量查询使用 `/api/public/task/details`。批量查询发送裸 JSON 字符串数组,响应保留 public 接口的原始结构;不借用 workflow 路由的字段清理、结果转换或记录排序行为。单条查询不保证同一 task_id 的最新记录,批量查询可能返回同一 task_id 的多条记录。
|
|
18
|
+
|
|
19
|
+
可以通过 `load_catalog()` 离线加载内置配置,也可从实际部署下载 `/openapi.json` 并保存自己的快照。加载或刷新会替换当前配置,不自动合并隐藏或删除的接口。内置配置不代表 backend 的全部路由,也不证明线上可用;部署版本及配置决定实际可用接口。SDK 升级不会覆盖用户保存的快照。
|
|
20
|
+
|
|
21
|
+
本次本地 backend 与线上部署的对照结果见 [后端适配检查](backend-compatibility.md)。SDK 的安装和运行不依赖 sibling backend 目录。
|
|
22
|
+
|
|
23
|
+
SSE 使用同一认证和部署地址,不自动重连。解析器保留原始数据、事件名称及 id/retry,最终状态判断由普通任务查询及 `wait_for_task()` 负责。
|
|
24
|
+
|
|
25
|
+
## 后端维护建议
|
|
26
|
+
|
|
27
|
+
1. 为公开接口显式设置稳定 `operation_id`,避免 FastAPI 自动 ID 随函数重构变化;也可按 method/path 从 SDK 操作目录查找。
|
|
28
|
+
2. 在 OpenAPI security 中声明 apikey,确保希望集成的接口进入部署文档。
|
|
29
|
+
3. 对齐真实响应模型,包括数字 status、历史 fee 类型和 out_data 的多种形态。
|
|
30
|
+
4. 发布时导出 schema 并运行差异检查。当前 diff 能发现参数、响应和引用模型变化,但不判断业务兼容性。
|
|
31
|
+
5. 对删除接口、新增必填字段及改变语义设置版本或弃用周期。
|
|
32
|
+
|
|
33
|
+
## 验证界限
|
|
34
|
+
|
|
35
|
+
本地测试通过 HTTPX MockTransport 验证认证头、表单、JSON、multipart、任务状态、重试、取消、响应解压和 OpenAPI 解析,不调用真实模型、队列或数据库。上线前结合实际部署的 schema 与测试账户联调;静态路由检查不能替代生成、文件读取和任务回调的运行验证。
|