fiuai-sdk-python 0.8.1__tar.gz → 0.8.3__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.
- fiuai_sdk_python-0.8.3/PKG-INFO +768 -0
- fiuai_sdk_python-0.8.3/README.md +734 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/pyproject.toml +1 -1
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/auth/context_mgr.py +8 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/auth/header.py +3 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/auth/helper.py +11 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/auth/type.py +9 -2
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/client.py +5 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/context.py +12 -4
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/http/client.py +4 -1
- fiuai_sdk_python-0.8.1/PKG-INFO +0 -36
- fiuai_sdk_python-0.8.1/README.md +0 -2
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/.gitignore +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/CHANGELOG.md +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/LICENSE +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/__init__.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/auth/__init__.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/bank.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/company.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/config.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/const.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/datatype.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/doctype.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/error.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/examples/fastapi_integration.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/http/__init__.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/item.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/perm.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/pkg/cache/__init__.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/pkg/cache/cache_client.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/pkg/cache/circuit_breaker.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/pkg/cache/decorator.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/pkg/cache/redis_manager.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/pkg/cache/types.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/profile.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/resp.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/setup.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/type.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/util.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/utils/__init__.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/utils/ids.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/utils/logger.py +0 -0
- {fiuai_sdk_python-0.8.1 → fiuai_sdk_python-0.8.3}/src/fiuai_sdk_python/utils/text.py +0 -0
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fiuai_sdk_python
|
|
3
|
+
Version: 0.8.3
|
|
4
|
+
Summary: FiuAI Python SDK - 企业级AI服务集成开发工具包
|
|
5
|
+
Project-URL: Homepage, https://github.com/fiuai/fiuai-sdk-python
|
|
6
|
+
Project-URL: Documentation, https://github.com/fiuai/fiuai-sdk-python#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/fiuai/fiuai-sdk-python.git
|
|
8
|
+
Project-URL: Issues, https://github.com/fiuai/fiuai-sdk-python/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/fiuai/fiuai-sdk-python/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: liming <lmlala@aliyun.com>
|
|
11
|
+
Maintainer-email: liming <lmlala@aliyun.com>
|
|
12
|
+
License: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: ai,enterprise,fiuai,python,sdk
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Requires-Dist: fastapi>=0.118.2
|
|
26
|
+
Requires-Dist: httpx>=0.28.1
|
|
27
|
+
Requires-Dist: langgraph-checkpoint-redis>=0.3.1
|
|
28
|
+
Requires-Dist: pydantic>=2.11.7
|
|
29
|
+
Requires-Dist: pytest>=8.4.1
|
|
30
|
+
Requires-Dist: redis>=7.1.0
|
|
31
|
+
Requires-Dist: snowflake-id>=1.0.2
|
|
32
|
+
Requires-Dist: tenacity>=9.1.2
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# fiuai-sdk-python
|
|
36
|
+
|
|
37
|
+
FiuAI Python SDK — 企业级 AI 服务集成开发工具包。
|
|
38
|
+
|
|
39
|
+
提供统一的 FiuAI 平台接入能力, 包括: 认证上下文管理、Frappe API 客户端、HTTP 客户端工厂、Redis 缓存 (含熔断降级)、雪花 ID 生成、日志初始化等。
|
|
40
|
+
|
|
41
|
+
## 安装
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# uv (推荐)
|
|
45
|
+
uv add fiuai-sdk-python
|
|
46
|
+
|
|
47
|
+
# pip
|
|
48
|
+
pip install fiuai-sdk-python
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> 要求 Python >= 3.12
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 目录
|
|
56
|
+
|
|
57
|
+
- [快速开始](#快速开始)
|
|
58
|
+
- [SDK 初始化](#sdk-初始化)
|
|
59
|
+
- [Frappe API 客户端](#frappe-api-客户端)
|
|
60
|
+
- [认证与上下文](#认证与上下文)
|
|
61
|
+
- [HTTP 客户端](#http-客户端)
|
|
62
|
+
- [Redis 连接池管理](#redis-连接池管理)
|
|
63
|
+
- [缓存客户端](#缓存客户端)
|
|
64
|
+
- [缓存装饰器](#缓存装饰器)
|
|
65
|
+
- [熔断器](#熔断器)
|
|
66
|
+
- [日志系统](#日志系统)
|
|
67
|
+
- [雪花 ID 生成](#雪花-id-生成)
|
|
68
|
+
- [工具函数](#工具函数)
|
|
69
|
+
- [FastAPI 集成](#fastapi-集成)
|
|
70
|
+
- [API 参考索引](#api-参考索引)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 快速开始
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from fiuai_sdk_python import init_fiuai, get_client
|
|
78
|
+
|
|
79
|
+
# 1. 初始化 SDK (全局一次)
|
|
80
|
+
init_fiuai(url="https://your-frappe-instance.com")
|
|
81
|
+
|
|
82
|
+
# 2. 获取客户端
|
|
83
|
+
client = get_client(
|
|
84
|
+
username="user@example.com",
|
|
85
|
+
auth_tenant_id="tenant-001",
|
|
86
|
+
current_company="company-001",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# 3. 调用 API
|
|
90
|
+
response = client.get_user_profile_info()
|
|
91
|
+
if response.is_success():
|
|
92
|
+
print(response.data)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## SDK 初始化
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from fiuai_sdk_python import init_fiuai
|
|
101
|
+
|
|
102
|
+
init_fiuai(
|
|
103
|
+
url="https://your-frappe-instance.com",
|
|
104
|
+
max_api_retry=3, # API 重试次数
|
|
105
|
+
timeout=5, # 请求超时 (秒)
|
|
106
|
+
verify=False, # SSL 证书校验
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`init_fiuai` 是全局单例, 只需在应用启动时调用一次。后续通过 `get_client()` 获取客户端时, 会自动读取此配置。
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Frappe API 客户端
|
|
115
|
+
|
|
116
|
+
### 获取客户端
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from fiuai_sdk_python import get_client, FiuaiSDK
|
|
120
|
+
|
|
121
|
+
# 方式 1: 工厂函数 (推荐, 自动读取 init_fiuai 配置)
|
|
122
|
+
client = get_client(
|
|
123
|
+
username="user@example.com",
|
|
124
|
+
auth_tenant_id="tenant-001",
|
|
125
|
+
current_company="company-001",
|
|
126
|
+
company_unique_no="UNQ-001",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# 方式 2: 直接实例化
|
|
130
|
+
client = FiuaiSDK(
|
|
131
|
+
url="https://your-frappe-instance.com",
|
|
132
|
+
username="user@example.com",
|
|
133
|
+
auth_tenant_id="tenant-001",
|
|
134
|
+
current_company="company-001",
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### CRUD 操作
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
# 创建文档
|
|
142
|
+
resp = client.internal_create(data={
|
|
143
|
+
"doctype": "Sales Invoice",
|
|
144
|
+
"customer": "CUST-001",
|
|
145
|
+
"items": [{"item_code": "ITEM-001", "qty": 10}],
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
# 查询单条
|
|
149
|
+
resp = client.internal_get("Sales Invoice", "SI-00001")
|
|
150
|
+
|
|
151
|
+
# 查询列表
|
|
152
|
+
resp = client.internal_get_list(
|
|
153
|
+
doctype="Sales Invoice",
|
|
154
|
+
filters={"customer": "CUST-001"},
|
|
155
|
+
fields=["name", "grand_total"],
|
|
156
|
+
limit_page_length=50,
|
|
157
|
+
order_by="creation desc",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# 更新
|
|
161
|
+
resp = client.internal_update(data={
|
|
162
|
+
"doctype": "Sales Invoice",
|
|
163
|
+
"name": "SI-00001",
|
|
164
|
+
"custom_field": "new_value",
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
# 删除
|
|
168
|
+
resp = client.internal_delete("Sales Invoice", "SI-00001")
|
|
169
|
+
|
|
170
|
+
# 提交 / 取消
|
|
171
|
+
resp = client.internal_submit("Sales Invoice", "SI-00001")
|
|
172
|
+
resp = client.internal_cancel("Sales Invoice", "SI-00001")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 通用请求
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
# POST
|
|
179
|
+
resp = client.internal_post_req("/api/method/custom_method", postdata={"key": "value"})
|
|
180
|
+
|
|
181
|
+
# GET
|
|
182
|
+
resp = client.internal_get_req("/api/method/custom_method", params={"key": "value"})
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 临时头覆盖
|
|
186
|
+
|
|
187
|
+
跨租户/跨公司调用时, 可临时切换身份:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
client.set_temp_header(
|
|
191
|
+
auth_tenant_id="other-tenant",
|
|
192
|
+
auth_company_id="other-company",
|
|
193
|
+
user_id="other-user@example.com",
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
resp = client.internal_get_list("Customer", limit_page_length=10)
|
|
197
|
+
client.clear_temp_headers()
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 响应处理
|
|
201
|
+
|
|
202
|
+
所有 API 调用返回 `ApiResponse`:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
resp = client.internal_get("Sales Invoice", "SI-00001")
|
|
206
|
+
|
|
207
|
+
resp.http_success # HTTP 状态码 2xx
|
|
208
|
+
resp.api_success # 业务层成功
|
|
209
|
+
resp.is_success() # http_success and api_success
|
|
210
|
+
resp.status_code # HTTP 状态码
|
|
211
|
+
resp.data # 响应数据
|
|
212
|
+
resp.error_code # 错误码
|
|
213
|
+
resp.error_message # 错误信息
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 认证与上下文
|
|
219
|
+
|
|
220
|
+
### 数据模型
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from fiuai_sdk_python.auth import AuthData, AuthHeader
|
|
224
|
+
|
|
225
|
+
# AuthData: 解析后的认证数据
|
|
226
|
+
# 字段: user_id, auth_tenant_id, current_company, impersonation,
|
|
227
|
+
# company_unique_no, trace_id, client, channel, lang, accept_language
|
|
228
|
+
|
|
229
|
+
# AuthHeader: HTTP 头映射
|
|
230
|
+
# 字段: x_fiuai_user, x_fiuai_auth_tenant_id, x_fiuai_current_company, ...
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### 从请求解析认证
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from fiuai_sdk_python.auth import parse_auth_headers, extract_auth_from_request
|
|
237
|
+
|
|
238
|
+
# 从字典解析
|
|
239
|
+
auth = parse_auth_headers({"X-Fiuai-User": "user@example.com", ...})
|
|
240
|
+
|
|
241
|
+
# 从 FastAPI Request 解析
|
|
242
|
+
auth = extract_auth_from_request(request, engine="fastapi")
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### 便捷函数
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from fiuai_sdk_python.auth import (
|
|
249
|
+
get_auth_data,
|
|
250
|
+
get_current_user_id,
|
|
251
|
+
get_current_tenant_id,
|
|
252
|
+
get_current_company,
|
|
253
|
+
get_company_unique_no,
|
|
254
|
+
is_impersonating,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
user_id = get_current_user_id(request)
|
|
258
|
+
tenant_id = get_current_tenant_id(request)
|
|
259
|
+
company_id = get_current_company(request)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 上下文管理
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from fiuai_sdk_python.auth import init_context, get_auth_data_from_context, ContextManager
|
|
266
|
+
|
|
267
|
+
# 初始化上下文 (通常在中间件中)
|
|
268
|
+
ctx = init_context(
|
|
269
|
+
auth_data=auth_data,
|
|
270
|
+
event_id="evt-001",
|
|
271
|
+
task_id="task-001",
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# 在业务代码中读取
|
|
275
|
+
auth = get_auth_data_from_context()
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### RequestContext (基于 contextvars)
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from fiuai_sdk_python import RequestContext, get_current_headers, get_trace_id
|
|
282
|
+
|
|
283
|
+
# 注入上下文
|
|
284
|
+
with RequestContext.from_fastapi_request(request):
|
|
285
|
+
headers = get_current_headers()
|
|
286
|
+
trace_id = get_trace_id()
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## HTTP 客户端
|
|
292
|
+
|
|
293
|
+
提供基于 `httpx` 的 HTTP 客户端工厂, 支持自动注入认证头和重试。
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from fiuai_sdk_python.http import (
|
|
297
|
+
create_http_client, # 创建异步客户端
|
|
298
|
+
create_sync_http_client, # 创建同步客户端
|
|
299
|
+
get_async_http_client, # 全局单例异步客户端
|
|
300
|
+
get_sync_http_client, # 全局单例同步客户端
|
|
301
|
+
close_global_clients, # 关闭全局客户端
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# 异步客户端 (自动注入 auth headers)
|
|
305
|
+
async_client = create_http_client(
|
|
306
|
+
base_url="https://api.example.com",
|
|
307
|
+
timeout=30,
|
|
308
|
+
retry_count=3,
|
|
309
|
+
retry_interval=1,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
async with async_client as client:
|
|
313
|
+
resp = await client.get("/api/resource")
|
|
314
|
+
|
|
315
|
+
# 同步客户端
|
|
316
|
+
sync_client = create_sync_http_client(base_url="https://api.example.com")
|
|
317
|
+
resp = sync_client.get("/api/resource")
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 认证头拦截器
|
|
321
|
+
|
|
322
|
+
HTTP 客户端会自动从当前 `RequestContext` 中提取认证头并注入请求:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from fiuai_sdk_python.http import extract_auth_headers, AUTH_HEADER_KEYS
|
|
326
|
+
|
|
327
|
+
headers = extract_auth_headers() # 从当前上下文提取
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Redis 连接池管理
|
|
333
|
+
|
|
334
|
+
`redis_manager` 是全局单例, 管理多个 Redis 连接池。
|
|
335
|
+
|
|
336
|
+
### 初始化
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
from fiuai_sdk_python.pkg.cache import redis_manager, RedisDBConfig
|
|
340
|
+
|
|
341
|
+
await redis_manager.initialize(
|
|
342
|
+
async_dbs=[
|
|
343
|
+
RedisDBConfig(
|
|
344
|
+
name="default",
|
|
345
|
+
host="127.0.0.1",
|
|
346
|
+
port=6379,
|
|
347
|
+
password="your-password",
|
|
348
|
+
db=0,
|
|
349
|
+
pool_size=20,
|
|
350
|
+
),
|
|
351
|
+
RedisDBConfig(
|
|
352
|
+
name="cache",
|
|
353
|
+
host="127.0.0.1",
|
|
354
|
+
port=6379,
|
|
355
|
+
password="your-password",
|
|
356
|
+
db=1,
|
|
357
|
+
pool_size=10,
|
|
358
|
+
),
|
|
359
|
+
],
|
|
360
|
+
sync_dbs=[
|
|
361
|
+
RedisDBConfig(
|
|
362
|
+
name="default",
|
|
363
|
+
host="127.0.0.1",
|
|
364
|
+
port=6379,
|
|
365
|
+
password="your-password",
|
|
366
|
+
db=0,
|
|
367
|
+
pool_size=5,
|
|
368
|
+
),
|
|
369
|
+
],
|
|
370
|
+
)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
> 支持增量注册: 多次调用 `initialize()` 不会重复创建已存在的连接池。
|
|
374
|
+
> async 和 sync 连接池独立追踪, 同名 db 可分别注册为 async 和 sync。
|
|
375
|
+
|
|
376
|
+
### 获取客户端
|
|
377
|
+
|
|
378
|
+
```python
|
|
379
|
+
# 异步
|
|
380
|
+
async_client = redis_manager.get_async_client("default")
|
|
381
|
+
value = await async_client.get("key")
|
|
382
|
+
|
|
383
|
+
# 同步
|
|
384
|
+
sync_client = redis_manager.get_sync_client("default")
|
|
385
|
+
value = sync_client.get("key")
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### LangGraph Checkpoint (Agent 场景)
|
|
389
|
+
|
|
390
|
+
```python
|
|
391
|
+
# 仅 Agent 项目需要, lazy import langgraph-checkpoint-redis
|
|
392
|
+
redis_manager.setup_langgraph_checkpoint("default")
|
|
393
|
+
|
|
394
|
+
# 异步版
|
|
395
|
+
await redis_manager.setup_langgraph_checkpoint_async("default")
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 关闭
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
await redis_manager.close_all()
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## 缓存客户端
|
|
407
|
+
|
|
408
|
+
`CacheClient` 在 Redis 客户端之上提供: key 前缀管理、TTL、Hash 操作、cache-aside 模式、熔断降级。
|
|
409
|
+
|
|
410
|
+
### 创建
|
|
411
|
+
|
|
412
|
+
```python
|
|
413
|
+
from fiuai_sdk_python.pkg.cache import CacheClient, CacheConfig, CircuitBreakerConfig
|
|
414
|
+
|
|
415
|
+
client = CacheClient(CacheConfig(
|
|
416
|
+
redis_db_name="cache", # redis_manager 中注册的连接池名
|
|
417
|
+
default_ttl=300, # 默认 TTL (秒), None 不过期
|
|
418
|
+
key_prefix="myapp", # key 前缀, 实际 key 为 "myapp:{key}"
|
|
419
|
+
circuit_breaker=CircuitBreakerConfig(
|
|
420
|
+
failure_threshold=5, # 连续失败 5 次后熔断
|
|
421
|
+
recovery_timeout=30.0, # 熔断 30 秒后进入半开
|
|
422
|
+
half_open_max_calls=1, # 半开状态允许 1 次探测
|
|
423
|
+
),
|
|
424
|
+
))
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### KV 操作
|
|
428
|
+
|
|
429
|
+
```python
|
|
430
|
+
# 异步
|
|
431
|
+
await client.set("user:123", '{"name": "test"}', ttl=60)
|
|
432
|
+
value = await client.get("user:123") # -> Optional[str]
|
|
433
|
+
exists = await client.exists("user:123") # -> bool
|
|
434
|
+
await client.delete("user:123")
|
|
435
|
+
|
|
436
|
+
# 同步
|
|
437
|
+
client.set_sync("user:123", '{"name": "test"}', ttl=60)
|
|
438
|
+
value = client.get_sync("user:123")
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Hash 操作
|
|
442
|
+
|
|
443
|
+
```python
|
|
444
|
+
await client.hset("config", "theme", "dark")
|
|
445
|
+
theme = await client.hget("config", "theme") # -> "dark"
|
|
446
|
+
all_config = await client.hgetall("config") # -> {"theme": "dark", ...}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Cache-Aside (核心)
|
|
450
|
+
|
|
451
|
+
`get_or_load` 是最常用的模式: 命中缓存直接返回, miss 时调用 loader 加载并写回缓存。
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
# 异步
|
|
455
|
+
async def load_user_config(user_id: str) -> dict:
|
|
456
|
+
return await db.query("SELECT * FROM config WHERE user_id = ?", user_id)
|
|
457
|
+
|
|
458
|
+
config = await client.get_or_load(
|
|
459
|
+
key=f"config:{user_id}",
|
|
460
|
+
loader=lambda: load_user_config(user_id),
|
|
461
|
+
ttl=120,
|
|
462
|
+
# serializer/deserializer 默认 json.dumps/json.loads
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
# 同步
|
|
466
|
+
config = client.get_or_load_sync(
|
|
467
|
+
key=f"config:{user_id}",
|
|
468
|
+
loader=lambda: db.query_sync(...),
|
|
469
|
+
ttl=120,
|
|
470
|
+
)
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### 熔断降级
|
|
474
|
+
|
|
475
|
+
Redis 不可用时, `CacheClient` 自动降级:
|
|
476
|
+
- `get` / `get_sync` → 返回 `None`
|
|
477
|
+
- `set` / `set_sync` → 返回 `False`
|
|
478
|
+
- `get_or_load` / `get_or_load_sync` → 直接调 loader, 业务不中断
|
|
479
|
+
|
|
480
|
+
无需业务代码感知 Redis 故障。
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## 缓存装饰器
|
|
485
|
+
|
|
486
|
+
`@cached` 装饰器将 cache-aside 模式封装为函数级注解。
|
|
487
|
+
|
|
488
|
+
### 初始化默认客户端
|
|
489
|
+
|
|
490
|
+
```python
|
|
491
|
+
from fiuai_sdk_python.pkg.cache import init_cache, CacheConfig
|
|
492
|
+
|
|
493
|
+
# 应用启动时调用一次 (在 redis_manager.initialize 之后)
|
|
494
|
+
init_cache(CacheConfig(
|
|
495
|
+
redis_db_name="cache",
|
|
496
|
+
default_ttl=120,
|
|
497
|
+
key_prefix="myapp",
|
|
498
|
+
))
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### 使用装饰器
|
|
502
|
+
|
|
503
|
+
```python
|
|
504
|
+
from fiuai_sdk_python.pkg.cache import cached
|
|
505
|
+
|
|
506
|
+
# 固定 key
|
|
507
|
+
@cached(key="global_settings", ttl=300)
|
|
508
|
+
async def get_settings() -> dict:
|
|
509
|
+
return await fetch_settings_from_db()
|
|
510
|
+
|
|
511
|
+
# 动态 key (根据函数参数生成)
|
|
512
|
+
@cached(
|
|
513
|
+
key=lambda company_id, side: f"config:{company_id}:{side}",
|
|
514
|
+
ttl=120,
|
|
515
|
+
)
|
|
516
|
+
def get_company_config(company_id: str, side: str) -> dict:
|
|
517
|
+
return query_config(company_id, side)
|
|
518
|
+
|
|
519
|
+
# 带 prefix
|
|
520
|
+
@cached(
|
|
521
|
+
key=lambda user_id: f"profile:{user_id}",
|
|
522
|
+
ttl=60,
|
|
523
|
+
prefix="finnexus", # 最终 key: "finnexus:profile:{user_id}"
|
|
524
|
+
)
|
|
525
|
+
async def get_user_profile(user_id: str) -> dict:
|
|
526
|
+
...
|
|
527
|
+
|
|
528
|
+
# 指定自定义 CacheClient
|
|
529
|
+
@cached(key="custom", client=my_cache_client)
|
|
530
|
+
def get_data() -> dict:
|
|
531
|
+
...
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
装饰器自动识别 sync/async 函数, 无需区分。
|
|
535
|
+
|
|
536
|
+
> 如果 `init_cache` 未调用 (即默认客户端为 None), 装饰器退化为直接调用原函数, 不会报错。
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## 熔断器
|
|
541
|
+
|
|
542
|
+
`CircuitBreaker` 实现三态熔断 (CLOSED → OPEN → HALF_OPEN → CLOSED), 通常由 `CacheClient` 内部使用, 也可独立使用。
|
|
543
|
+
|
|
544
|
+
```
|
|
545
|
+
正常 (CLOSED) ──连续失败达阈值──→ 熔断 (OPEN)
|
|
546
|
+
↑ │
|
|
547
|
+
│ 冷却期过后
|
|
548
|
+
│ ↓
|
|
549
|
+
└───── 探测成功 ───── 半开 (HALF_OPEN)
|
|
550
|
+
│
|
|
551
|
+
探测失败 ──→ 回到 OPEN
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### 独立使用
|
|
555
|
+
|
|
556
|
+
```python
|
|
557
|
+
from fiuai_sdk_python.pkg.cache import CircuitBreaker, CircuitBreakerConfig
|
|
558
|
+
|
|
559
|
+
breaker = CircuitBreaker(CircuitBreakerConfig(
|
|
560
|
+
failure_threshold=3,
|
|
561
|
+
recovery_timeout=10.0,
|
|
562
|
+
half_open_max_calls=1,
|
|
563
|
+
))
|
|
564
|
+
|
|
565
|
+
if breaker.allow_request:
|
|
566
|
+
try:
|
|
567
|
+
result = call_external_service()
|
|
568
|
+
breaker.record_success()
|
|
569
|
+
except Exception:
|
|
570
|
+
breaker.record_failure()
|
|
571
|
+
else:
|
|
572
|
+
result = fallback_value()
|
|
573
|
+
|
|
574
|
+
# 查看状态
|
|
575
|
+
print(breaker.state) # CircuitState.CLOSED / OPEN / HALF_OPEN
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## 日志系统
|
|
581
|
+
|
|
582
|
+
```python
|
|
583
|
+
from fiuai_sdk_python.utils.logger import init_logger, get_logger
|
|
584
|
+
|
|
585
|
+
# 初始化 (应用启动时调用一次)
|
|
586
|
+
init_logger(
|
|
587
|
+
log_path="logs/",
|
|
588
|
+
log_level="INFO",
|
|
589
|
+
context_injector=lambda: {"trace_id": get_current_trace_id()},
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
# 在各模块中使用
|
|
593
|
+
logger = get_logger(__name__)
|
|
594
|
+
logger.info("processing request")
|
|
595
|
+
logger.error("something went wrong", exc_info=True)
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
日志格式: `%(asctime)s - %(name)s - %(levelname)s - [trace_id:%(trace_id)s] - %(message)s`
|
|
599
|
+
|
|
600
|
+
`context_injector` 可注入自定义上下文字段 (如 trace_id) 到每条日志。
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## 雪花 ID 生成
|
|
605
|
+
|
|
606
|
+
```python
|
|
607
|
+
from fiuai_sdk_python.utils.ids import gen_id, get_instance_info
|
|
608
|
+
|
|
609
|
+
# 生成唯一 ID
|
|
610
|
+
unique_id = gen_id() # -> "7345892348923489234" (字符串)
|
|
611
|
+
|
|
612
|
+
# 查看实例信息
|
|
613
|
+
info = get_instance_info()
|
|
614
|
+
# -> {"instance_id": 42, "process_id": 12345, "start_time": ...}
|
|
615
|
+
|
|
616
|
+
# 自定义实例 ID (分布式部署时)
|
|
617
|
+
from fiuai_sdk_python.utils.ids import set_custom_instance_id
|
|
618
|
+
set_custom_instance_id(42)
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## 工具函数
|
|
624
|
+
|
|
625
|
+
### 文本处理
|
|
626
|
+
|
|
627
|
+
```python
|
|
628
|
+
from fiuai_sdk_python.utils.text import safe_string_name, safe_str, safe_json_str
|
|
629
|
+
|
|
630
|
+
# 全角符号转半角
|
|
631
|
+
safe_string_name("北京(朝阳)公司") # -> "北京(朝阳)公司"
|
|
632
|
+
|
|
633
|
+
# URL 安全编码 (用于 Redis 密码等含特殊字符的场景)
|
|
634
|
+
safe_str("p@ss#word!") # -> "p%40ss%23word%21"
|
|
635
|
+
|
|
636
|
+
# JSON 安全处理 (转义控制字符)
|
|
637
|
+
safe_json_str("line1\nline2") # -> "line1\\nline2"
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## FastAPI 集成
|
|
643
|
+
|
|
644
|
+
### 中间件注入上下文
|
|
645
|
+
|
|
646
|
+
```python
|
|
647
|
+
from fastapi import FastAPI
|
|
648
|
+
from fiuai_sdk_python import init_fiuai
|
|
649
|
+
from fiuai_sdk_python.examples.fastapi_integration import setup_fiuai_context
|
|
650
|
+
|
|
651
|
+
app = FastAPI()
|
|
652
|
+
|
|
653
|
+
# 初始化 SDK
|
|
654
|
+
init_fiuai(url="https://your-frappe-instance.com")
|
|
655
|
+
|
|
656
|
+
# 注册上下文中间件 (自动将请求头注入 RequestContext)
|
|
657
|
+
setup_fiuai_context(app)
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### 完整启动示例
|
|
661
|
+
|
|
662
|
+
```python
|
|
663
|
+
from contextlib import asynccontextmanager
|
|
664
|
+
from fastapi import FastAPI
|
|
665
|
+
from fiuai_sdk_python import init_fiuai
|
|
666
|
+
from fiuai_sdk_python.utils.logger import init_logger
|
|
667
|
+
from fiuai_sdk_python.pkg.cache import redis_manager, RedisDBConfig, init_cache, CacheConfig
|
|
668
|
+
from fiuai_sdk_python.examples.fastapi_integration import setup_fiuai_context
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
@asynccontextmanager
|
|
672
|
+
async def lifespan(app: FastAPI):
|
|
673
|
+
# 启动
|
|
674
|
+
init_logger(log_path="logs/", log_level="INFO")
|
|
675
|
+
init_fiuai(url="https://your-frappe-instance.com")
|
|
676
|
+
|
|
677
|
+
await redis_manager.initialize(async_dbs=[
|
|
678
|
+
RedisDBConfig(name="default", host="127.0.0.1", port=6379, password="", db=0),
|
|
679
|
+
])
|
|
680
|
+
init_cache(CacheConfig(redis_db_name="default", default_ttl=120, key_prefix="myapp"))
|
|
681
|
+
|
|
682
|
+
yield
|
|
683
|
+
|
|
684
|
+
# 关闭
|
|
685
|
+
await redis_manager.close_all()
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
app = FastAPI(lifespan=lifespan)
|
|
689
|
+
setup_fiuai_context(app)
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
---
|
|
693
|
+
|
|
694
|
+
## API 参考索引
|
|
695
|
+
|
|
696
|
+
### 顶层导出 (`fiuai_sdk_python`)
|
|
697
|
+
|
|
698
|
+
| 名称 | 类型 | 说明 |
|
|
699
|
+
|------|------|------|
|
|
700
|
+
| `init_fiuai` | function | 初始化 SDK 全局配置 |
|
|
701
|
+
| `FiuaiSDK` | class | Frappe API 客户端 |
|
|
702
|
+
| `get_client` | function | 客户端工厂 |
|
|
703
|
+
| `UserProfileInfo` | class | 用户信息模型 |
|
|
704
|
+
| `UserProfile` | class | 用户基本模型 |
|
|
705
|
+
| `RequestContext` | class | 请求上下文 (contextvars) |
|
|
706
|
+
| `get_current_headers` | function | 获取当前上下文 headers |
|
|
707
|
+
| `get_trace_id` | function | 获取当前 trace_id |
|
|
708
|
+
|
|
709
|
+
### 认证模块 (`fiuai_sdk_python.auth`)
|
|
710
|
+
|
|
711
|
+
| 名称 | 类型 | 说明 |
|
|
712
|
+
|------|------|------|
|
|
713
|
+
| `AuthData` | class | 解析后的认证数据 |
|
|
714
|
+
| `AuthHeader` | class | HTTP 认证头映射 |
|
|
715
|
+
| `parse_auth_headers` | function | 从 dict 解析认证 |
|
|
716
|
+
| `extract_auth_from_request` | function | 从 Request 解析认证 |
|
|
717
|
+
| `init_context` | function | 初始化上下文管理器 |
|
|
718
|
+
| `get_auth_data_from_context` | function | 从上下文获取 AuthData |
|
|
719
|
+
| `ContextManager` | class | 上下文管理器 |
|
|
720
|
+
| `WorldData` | class | World 事件/任务上下文 |
|
|
721
|
+
|
|
722
|
+
### HTTP 模块 (`fiuai_sdk_python.http`)
|
|
723
|
+
|
|
724
|
+
| 名称 | 类型 | 说明 |
|
|
725
|
+
|------|------|------|
|
|
726
|
+
| `create_http_client` | function | 创建异步 HTTP 客户端 |
|
|
727
|
+
| `create_sync_http_client` | function | 创建同步 HTTP 客户端 |
|
|
728
|
+
| `get_async_http_client` | function | 全局异步客户端单例 |
|
|
729
|
+
| `get_sync_http_client` | function | 全局同步客户端单例 |
|
|
730
|
+
| `close_global_clients` | function | 关闭全局客户端 |
|
|
731
|
+
| `extract_auth_headers` | function | 从上下文提取认证头 |
|
|
732
|
+
|
|
733
|
+
### 缓存模块 (`fiuai_sdk_python.pkg.cache`)
|
|
734
|
+
|
|
735
|
+
| 名称 | 类型 | 说明 |
|
|
736
|
+
|------|------|------|
|
|
737
|
+
| `redis_manager` | RedisManager | Redis 连接池管理器 (单例) |
|
|
738
|
+
| `RedisDBConfig` | class | Redis 连接配置 |
|
|
739
|
+
| `CacheClient` | class | 通用缓存客户端 |
|
|
740
|
+
| `CacheConfig` | class | 缓存客户端配置 |
|
|
741
|
+
| `init_cache` | function | 初始化默认 CacheClient |
|
|
742
|
+
| `get_default_client` | function | 获取默认 CacheClient |
|
|
743
|
+
| `cached` | decorator | 函数级缓存装饰器 |
|
|
744
|
+
| `CircuitBreaker` | class | 三态熔断器 |
|
|
745
|
+
| `CircuitBreakerConfig` | class | 熔断器配置 |
|
|
746
|
+
| `CircuitState` | enum | 熔断状态枚举 |
|
|
747
|
+
|
|
748
|
+
### 工具模块 (`fiuai_sdk_python.utils`)
|
|
749
|
+
|
|
750
|
+
| 名称 | 模块 | 说明 |
|
|
751
|
+
|------|------|------|
|
|
752
|
+
| `init_logger` | logger | 初始化日志系统 |
|
|
753
|
+
| `get_logger` | logger | 获取 logger 实例 |
|
|
754
|
+
| `safe_string_name` | text | 全角转半角 |
|
|
755
|
+
| `safe_str` | text | URL 安全编码 |
|
|
756
|
+
| `safe_json_str` | text | JSON 安全处理 |
|
|
757
|
+
| `gen_id` | ids | 生成雪花 ID |
|
|
758
|
+
| `set_custom_instance_id` | ids | 设置自定义实例 ID |
|
|
759
|
+
|
|
760
|
+
---
|
|
761
|
+
|
|
762
|
+
## 版本
|
|
763
|
+
|
|
764
|
+
当前版本: **0.8.1**
|
|
765
|
+
|
|
766
|
+
## License
|
|
767
|
+
|
|
768
|
+
MIT License - Copyright (c) 2025 FiuAI
|