Skip to content

Commit bc22765

Browse files
committed
docs: README 改为英文,安装入口指向 PyPI
1 parent ece1b39 commit bc22765

1 file changed

Lines changed: 65 additions & 60 deletions

File tree

README.md

Lines changed: 65 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
# Qoder Cloud Agents Python SDK
22

3-
Python 3.10+,同步与原生异步客户端,支持类型化响应、自动分页、SSE 和文件传输。
3+
Synchronous and natively asynchronous clients for Python 3.10+, with typed responses, automatic pagination, SSE streaming, and file transfer.
44

5-
完整 API 参考:[Forward API](docs/forward-api.md) · [Managed API](docs/managed-api.md),包含初始化、同步/异步调用、分页、SSE、文件传输,以及全部资源方法的参数、返回类型与 HTTP 路由。
5+
Full API reference: [Forward API](docs/forward-api.md) · [Managed API](docs/managed-api.md) — covering initialization, sync and async calls, pagination, SSE, file transfer, and the parameters, return types, and HTTP routes of every resource method.
66

7-
## 安装与配置
7+
## Installation and configuration
8+
9+
```bash
10+
python -m pip install qca
11+
```
12+
13+
The package is still in pre-release, so the command above resolves to the latest `0.0.1.devN` build. To work from a checkout of this repository instead:
814

915
```bash
10-
# 在本仓库中安装
1116
python -m pip install .
12-
# 开发环境
17+
# Development environment
1318
python -m pip install -e '.[dev]'
1419
```
1520

@@ -26,15 +31,15 @@ with Managed() as client:
2631
print(agent.id, agent.name)
2732
```
2833

29-
也可使用 `from qca.forward import Client` `from qca.managed import Client`。两种模式独立实例化,使用各自的资源与类型。
34+
`from qca.forward import Client` and `from qca.managed import Client` are equivalent entry points. The two modes are instantiated independently and use their own resources and types.
3035

31-
| 配置 | Forward | Managed |
36+
| Setting | Forward | Managed |
3237
|---|---|---|
33-
| 令牌 | `QODER_ACCESS_TOKEN` | `QODER_ACCESS_TOKEN` |
34-
| API 根地址 | `QODER_FORWARD_BASE_URL` | `QODER_BASE_URL` |
35-
| 默认地址 | `https://api.qoder.com/api/v1/forward/` | `https://api.qoder.com/api/v1/cloud/` |
38+
| Token | `QODER_ACCESS_TOKEN` | `QODER_ACCESS_TOKEN` |
39+
| API base URL | `QODER_FORWARD_BASE_URL` | `QODER_BASE_URL` |
40+
| Default base URL | `https://api.qoder.com/api/v1/forward/` | `https://api.qoder.com/api/v1/cloud/` |
3641

37-
显式参数优先于环境变量。客户端不读取 `.env`;只有 examples 读取 `.env.live`。CN 环境需要显式配置对应根地址:
42+
Explicit arguments take precedence over environment variables. The clients never read `.env`; only the examples load `.env.live`. The China endpoints have to be configured explicitly:
3843

3944
```python
4045
client = Forward(
@@ -45,20 +50,20 @@ client = Forward(
4550
)
4651
```
4752

48-
## 会话
53+
## Sessions
4954

50-
Forward 通过 Identity Template 创建 Session,Managed 通过 Agent 和 Environment 创建 Session:
55+
Forward creates a Session from an Identity and a Template; Managed creates one from an Agent and an Environment:
5156

5257
```python
5358
from qca import Forward
5459

5560
with Forward() as client:
5661
environment = client.environments.create(name="demo", config={"type": "cloud"})
57-
identity = client.identities.create(external_id="example-user", name="示例用户")
62+
identity = client.identities.create(external_id="example-user", name="Example User")
5863
template = client.templates.create(
5964
name="assistant", environment_id=environment.id,
60-
model="ultimate", # 使用当前账号已启用的模型
61-
system="根据可读取的资料回答问题。",
65+
model="ultimate", # use a model enabled for the current account
66+
system="Answer questions from the material you can read.",
6267
tools=[{"type": "agent_toolset_20260401"}],
6368
)
6469
session = client.sessions.create(identity_id=identity.id, template_id=template.id)
@@ -72,25 +77,25 @@ with Managed() as client:
7277
environment = client.environments.create(name="demo", config={"type": "cloud"})
7378
agent = client.agents.create(
7479
name="assistant", model={"id": "ultimate"},
75-
system="根据可读取的资料回答问题。",
80+
system="Answer questions from the material you can read.",
7681
tools=[{"type": "agent_toolset_20260401"}],
7782
)
7883
session = client.sessions.create(environment_id=environment.id, agent=agent.id)
7984
print(session.id)
8085
```
8186

82-
片段会创建资源。包含执行断言和清理的完整用例见 [examples](examples/README.md)Forward 还提供 ScheduleBatchChannelManaged 提供 DeploymentDream、自托管环境 Work API
87+
These snippets create real resources. For complete scenarios with execution assertions and cleanup, see [examples](examples/README.md). Forward additionally offers Schedule, Batch, and Channel; Managed offers Deployment, Dream, and the Work API for self-hosted environments.
8388

84-
## 消息与 SSE
89+
## Messages and SSE
8590

86-
下列代码适用于两种客户端。在同一段对话中复用 `session_id`,同一条逻辑消息的 HTTP 重试复用幂等键。
91+
The code below works with either client. Reuse `session_id` throughout a conversation, and reuse one idempotency key across HTTP retries of the same logical message.
8792

8893
```python
8994
from uuid import uuid4
9095

9196
sent = client.sessions.events.send(
9297
session_id,
93-
events=[{"type": "user.message", "content": [{"type": "text", "text": "你好"}]}],
98+
events=[{"type": "user.message", "content": [{"type": "text", "text": "Hello"}]}],
9499
extra_headers={"Idempotency-Key": uuid4().hex},
95100
)
96101

@@ -109,11 +114,11 @@ with client.sessions.events.stream(
109114
raise RuntimeError(f"Session stopped: {event.type}")
110115
```
111116

112-
SDK 不自动重连 SSE。保存 `stream.last_event_id`,重连时通过 `Last-Event-ID` 恢复,不要重发已被接收的消息。`event_start``event_delta` 是预览,最终事件会再次包含完整内容;同一个 ID 的增量事件不会被去重。idle 可能表示等待确认或达到预算,业务成功还需检查 `stop_reason` 和最终回复。
117+
The SDK does not reconnect a stream on its own. Persist `stream.last_event_id` and resume through the `Last-Event-ID` header rather than resending messages the server already accepted. `event_start` and `event_delta` are previews: the final event carries the complete content again, and delta events sharing an ID are not deduplicated. An idle status can also mean the session is waiting for a confirmation or has reached its budget, so check `stop_reason` and the final reply before treating a run as successful.
113118

114-
## 异步
119+
## Async
115120

116-
异步客户端使用 `httpx.AsyncClient`,请求、重试等待、SSE 读取均为原生异步 I/O
121+
The async clients are built on `httpx.AsyncClient`; requests, retry backoff, and SSE reads are all native async I/O.
117122

118123
```python
119124
import asyncio
@@ -129,72 +134,72 @@ async def main():
129134
asyncio.run(main())
130135
```
131136

132-
异步流使用 `async with await client.sessions.events.stream(...)`。完整片段见 [Forward 异步示例](examples/forward/async_session.py)[Managed 异步示例](examples/managed/async_session.py)。本地文件读取通过线程执行,网络请求直接使用异步 HTTP 客户端。
137+
Async streams use `async with await client.sessions.events.stream(...)`. Complete snippets are in the [Forward async example](examples/forward/async_session.py) and the [Managed async example](examples/managed/async_session.py). Local files are read in a worker thread; network requests go directly through the async HTTP client.
133138

134-
## 参数与响应
139+
## Parameters and responses
135140

136-
方法使用 snake_case、关键字参数和类型注解。嵌套资源的目标 ID 可以作为位置参数,祖先 ID 必须具名:
141+
Methods use snake_case names, keyword arguments, and type annotations. The target ID of a nested resource may be positional, while ancestor IDs must be named:
137142

138143
```python
139144
credential = client.vaults.credentials.retrieve("credential-id", vault_id="vault-id")
140145
memory = client.memory_stores.memories.retrieve("memory-id", memory_store_id="store-id")
141146
```
142147

143-
各 mode 的 `types/*_params.py` 使用 `TypedDict` 定义请求。嵌套参数直接传普通字典;联合类型直接传对应的字符串、字典或列表。响应是 Pydantic 模型,可以直接访问字段,未知字段也会保留。
148+
Requests are described by `TypedDict`s in each mode's `types/*_params.py`. Pass plain dicts for nested parameters, and pass the matching string, dict, or list for unions. Responses are Pydantic models: fields are accessed directly, and unknown fields are preserved.
144149

145150
```python
146151
from qca import NOT_GIVEN
147152

148-
client.identities.update("identity-id", name=NOT_GIVEN) # 不发送 name
149-
client.identities.update("identity-id", name=None) # 发送 null
150-
client.identities.update("identity-id", enabled=False) # 保留 false
153+
client.identities.update("identity-id", name=NOT_GIVEN) # omits name from the request
154+
client.identities.update("identity-id", name=None) # sends null
155+
client.identities.update("identity-id", enabled=False) # keeps false
151156

152157
identity = client.identities.retrieve("identity-id")
153158
print(identity.to_dict())
154159
print(identity.to_json())
155160
print(identity._request_id)
156-
print("name" in identity.model_fields_set) # 区分缺失与 null
161+
print("name" in identity.model_fields_set) # tells a missing field from null
157162
```
158163

159-
能否清空字段由服务端决定。方法均支持 `extra_headers``extra_query``extra_body``timeout`extra 值优先于方法参数。空数组、空对象、0、false 均保留。
164+
Whether a field can be cleared is decided by the server. Every method accepts `extra_headers`, `extra_query`, `extra_body`, and `timeout`; extra values take precedence over method arguments. Empty arrays, empty objects, `0`, and `false` are all preserved.
160165

161-
## 分页
166+
## Pagination
162167

163168
```python
164169
page = client.sessions.list(limit=20)
165-
print(page.data) # 当前页
166-
for session in page: # 自动获取后续页
170+
print(page.data) # current page
171+
for session in page: # fetches subsequent pages automatically
167172
print(session.id)
168173
for page in client.sessions.list().iter_pages():
169174
print(len(page.data))
170175
```
171176

172-
SDK 按 Go API 区分 `after_id` / `before_id` `next_page` 分页,后续请求保留过滤条件。游标不前进或循环时抛出异常。非分页列表响应(例如 Models)通过 `.data` 访问。
177+
Following the API, the SDK distinguishes `after_id` / `before_id` cursors from `next_page` pagination and carries filters into subsequent requests. It raises if a cursor stops advancing or starts looping. Non-paginated list responses, such as Models, are read through `.data`.
173178

174-
## 错误、超时与重试
179+
## Errors, timeouts, and retries
175180

176181
```python
177182
from qca import APIConnectionError, APIStatusError, APITimeoutError
178183

179184
try:
180185
session = client.sessions.retrieve("sess-id", timeout=10)
181186
except APITimeoutError:
182-
print("请求超时")
187+
print("The request timed out")
183188
except APIConnectionError:
184-
print("网络连接失败")
189+
print("The connection failed")
185190
except APIStatusError as exc:
186191
print(exc.status_code, exc.code, exc.type, exc.request_id)
187192
```
188193

189-
HTTP 状态分别对应 `BadRequestError``AuthenticationError``PermissionDeniedError``NotFoundError``ConflictError``UnprocessableEntityError``RateLimitError``InternalServerError`。非 JSON 错误正文保留在 `.body`。响应无法解码为声明类型时抛出 `APIResponseValidationError`
194+
HTTP statuses map to `BadRequestError`, `AuthenticationError`, `PermissionDeniedError`, `NotFoundError`, `ConflictError`, `UnprocessableEntityError`, `RateLimitError`, and `InternalServerError`. Non-JSON error bodies are kept in `.body`. When a response cannot be decoded into its declared type, `APIResponseValidationError` is raised.
190195

191-
默认连接超时 10 秒,其余 HTTP 阶段 60 秒。可传浮点秒数、`httpx.Timeout``None`;超时按 HTTP 阶段和单次尝试计算。端到端任务期限由调用方管理,异步代码可用 `asyncio.wait_for`
196+
The default connect timeout is 10 seconds, and 60 seconds for the remaining HTTP phases. You can pass a float of seconds, an `httpx.Timeout`, or `None`; timeouts are measured per HTTP phase and per attempt. End-to-end deadlines are the caller's responsibility — async code can use `asyncio.wait_for`.
192197

193-
默认最多重试 2 次:GET/HEAD 或携带 `Idempotency-Key` 的请求可对连接错误、4084295xx 重试;无幂等键的其他请求仅对 429 重试;409 不自动重试。在上述约束内遵循 `x-should-retry` 和有效的 `Retry-After-Ms` / `Retry-After`,否则指数退避。已建立的 SSE 不重试。
198+
Up to 2 retries by default: GET/HEAD requests and requests carrying an `Idempotency-Key` are retried on connection errors, 408, 429, and 5xx; other requests without an idempotency key are retried on 429 only; 409 is never retried automatically. Within those constraints the SDK honors `x-should-retry` and a valid `Retry-After-Ms` / `Retry-After`, and otherwise backs off exponentially. An SSE stream that has already been established is not retried.
194199

195-
`client.with_options(max_retries=0, timeout=20)` 返回独立配置的客户端,复用同一个 HTTP 连接池;关闭任一客户端会关闭这个池。
200+
`client.with_options(max_retries=0, timeout=20)` returns a separately configured client that shares the same HTTP connection pool; closing either client closes that pool.
196201

197-
## 文件和自定义 HTTP
202+
## Files and custom HTTP
198203

199204
```python
200205
from pathlib import Path
@@ -205,9 +210,9 @@ with client.files.download(file.id) as content:
205210
content.write_to_file("downloaded.txt")
206211
```
207212

208-
上传支持 bytes、二进制文件对象、Path`(文件名, 内容[, MIME 类型])`。调用方传入的文件对象由调用方关闭;上传内容会缓存以便重试。metadata 使用 JSON 编码,Skill 相对路径保留在 multipart 文件名中。
213+
Uploads accept bytes, binary file objects, `Path`, and `(filename, content[, MIME type])`. File objects you provide stay yours to close; upload content is buffered so it can be replayed on retry. Metadata is JSON-encoded, and Skill relative paths are preserved in the multipart filename.
209214

210-
Files 下载先获取临时链接,再流式读取存储地址;API 认证、默认请求头、Cookie 不会发送到存储主机。Skill Version 下载直接返回 API 的二进制响应。异步下载使用 `await client.files.download(...)``await response.write_to_file(...)`
215+
A file download first obtains a temporary link and then streams from the storage endpoint; API credentials, default headers, and cookies are not sent to the storage host. A Skill version download returns the API's binary response directly. Async downloads use `await client.files.download(...)` and `await response.write_to_file(...)`.
211216

212217
```python
213218
import httpx
@@ -219,37 +224,37 @@ with Forward(http_client=httpx.Client(proxy="http://localhost:8080")) as client:
219224
models = raw.parse()
220225
```
221226

222-
异步客户端可传 `httpx.AsyncClient`,异步 raw response 使用 `await raw.parse()`。动态令牌提供者传到 `credential=`,每次 HTTP 尝试调用 `get_token()`;异步客户端也接受异步 `get_token()`。静态 access_token 优先于提供者,显式 Authorization 请求头优先于两者。
227+
Async clients accept an `httpx.AsyncClient`, and async raw responses are parsed with `await raw.parse()`. A dynamic token provider is passed as `credential=` and its `get_token()` is called on every HTTP attempt; async clients also accept an async `get_token()`. A static `access_token` takes precedence over a provider, and an explicit `Authorization` header takes precedence over both.
223228

224-
需要先查看响应头再读取正文时使用 `with_streaming_response`。退出上下文时关闭连接:
229+
Use `with_streaming_response` when the response headers have to be inspected before the body is read. The connection is closed when the context exits:
225230

226231
```python
227232
with client.models.with_streaming_response.list() as response:
228233
print(response.headers)
229234
models = response.parse()
230235
```
231236

232-
异步版本使用 `async with client.models.with_streaming_response.list()`,通过 `await response.parse()` 解析正文;也可以按块迭代 `iter_bytes()` / `iter_lines()`
237+
The async form is `async with client.models.with_streaming_response.list()`, with the body parsed through `await response.parse()`; the payload can also be iterated in chunks with `iter_bytes()` / `iter_lines()`.
233238

234-
## 目录
239+
## Project layout
235240

236241
```text
237242
src/qca/
238243
__init__.py
239-
common/ # HTTP、鉴权、错误、分页、上传/下载、SSE
244+
common/ # HTTP, auth, errors, pagination, upload/download, SSE
240245
forward/
241246
_client.py
242-
resources/ # identities/configssessions/events
243-
types/ # 请求 TypedDict、响应模型
247+
resources/ # identities/configs, sessions/events, and so on
248+
types/ # request TypedDicts, response models
244249
managed/
245250
_client.py
246-
resources/ # agentsdeploymentsenvironments/work
251+
resources/ # agents, deployments, environments/work, and so on
247252
types/
248-
tests/ # 资源面、文档契约、公共层与模拟执行场景
249-
examples/ # 每个场景一个文件;各 mode 6 个同步场景、异步片段及 live 测试
250-
docs/ # Forward / Managed 完整 API 参考
253+
tests/ # resource surface, documentation contracts, common layer, simulated execution
254+
examples/ # one file per scenario; each mode ships 6 sync scenarios, async snippets, and live tests
255+
docs/ # complete Forward / Managed API reference
251256
```
252257

253-
## 许可证
258+
## License
254259

255-
本项目基于 [MIT License](LICENSE) 开源。
260+
Released under the [MIT License](LICENSE).

0 commit comments

Comments
 (0)