agent-builder-gateway-sdk 0.7.1__py3-none-any.whl

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.
@@ -0,0 +1,540 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-builder-gateway-sdk
3
+ Version: 0.7.1
4
+ Summary: Python SDK for Agent Builder Gateway - 用于 AI 构建的程序调用预制件
5
+ Author: Agent Builder Team
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: httpx>=0.27.0
10
+ Requires-Dist: pydantic>=2.0.0
11
+ Requires-Dist: python-dotenv>=1.0.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: black>=24.0.0; extra == 'dev'
14
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
15
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
16
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
17
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
18
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
19
+ Description-Content-Type: text/markdown
20
+
21
+ # Gateway SDK
22
+
23
+ Python SDK for Gateway - 用于调用预制件
24
+
25
+ ## 概述
26
+
27
+ Gateway SDK 是一个用于调用预制件的 Python SDK。
28
+
29
+ ### 核心特性
30
+
31
+ - ✅ 简洁的 API
32
+ - ✅ 支持 JWT Token 和 API Key 认证
33
+ - ✅ 流式响应支持(SSE)
34
+ - ✅ 完整的类型提示
35
+ - ✅ 完善的错误处理
36
+
37
+ ## 安装
38
+
39
+ ```bash
40
+ pip install agent-builder-gateway-sdk
41
+ ```
42
+
43
+ ## 快速开始
44
+
45
+ ### 初始化客户端
46
+
47
+ ```python
48
+ from gateway_sdk import GatewayClient
49
+
50
+ # 方式1: 使用 Internal Token(Agent/Prefab 内部调用)
51
+ client = GatewayClient(internal_token="your-internal-token")
52
+
53
+ # 方式2: 使用 API Key(第三方集成)
54
+ client = GatewayClient.from_api_key("sk-xxx")
55
+
56
+ # 方式3: 白名单模式(适用于 OpenHands 等白名单环境)
57
+ client = GatewayClient() # 无需提供任何认证信息
58
+
59
+ # 可选:指定 base_url
60
+ client = GatewayClient(base_url="http://your-gateway-url")
61
+ ```
62
+
63
+ **三种使用模式对比**:
64
+
65
+ | 模式 | 初始化方式 | 适用场景 |
66
+ |------|-----------|---------|
67
+ | Internal Token | `GatewayClient(internal_token="...")` | Agent/Prefab 内部调用 |
68
+ | API Key | `GatewayClient.from_api_key("sk-xxx")` | 第三方应用集成 |
69
+ | 白名单模式 | `GatewayClient()` | OpenHands、内部开发环境 |
70
+
71
+ **白名单模式说明**:
72
+ - 如果你的IP已配置白名单(如 OpenHands、内部开发环境),可以直接创建客户端,无需提供任何认证信息
73
+ - SDK会以无鉴权模式发送请求,由Gateway基于IP白名单进行验证
74
+ - 白名单模式下会自动使用默认用户身份,无需手动管理用户ID
75
+ - 这种模式极大简化了开发流程,适合可信任的环境
76
+
77
+ ### 调用预制件
78
+
79
+ ```python
80
+ result = client.run(
81
+ prefab_id="llm-client",
82
+ version="1.0.0",
83
+ function_name="chat",
84
+ parameters={"messages": [{"role": "user", "content": "Hello"}]}
85
+ )
86
+
87
+ if result.is_success():
88
+ print(result.get_result())
89
+ else:
90
+ print(f"Error: {result.error}")
91
+ ```
92
+
93
+ ### 链式调用
94
+
95
+ ```python
96
+ llm = client.prefab("llm-client", "1.0.0")
97
+ result = llm.call("chat", messages=[...], model="gpt-4")
98
+ ```
99
+
100
+ ### 流式响应
101
+
102
+ ```python
103
+ for event in client.run(..., stream=True):
104
+ if event.type == "content":
105
+ print(event.data, end="", flush=True)
106
+ elif event.type == "done":
107
+ print("\n完成")
108
+ ```
109
+
110
+ ### 批量调用
111
+
112
+ ```python
113
+ from gateway_sdk import PrefabCall
114
+
115
+ calls = [
116
+ PrefabCall(
117
+ prefab_id="translator",
118
+ version="1.0.0",
119
+ function_name="translate",
120
+ parameters={"text": "Hello", "target": "zh"}
121
+ ),
122
+ PrefabCall(
123
+ prefab_id="translator",
124
+ version="1.0.0",
125
+ function_name="translate",
126
+ parameters={"text": "World", "target": "zh"}
127
+ )
128
+ ]
129
+
130
+ result = client.run_batch(calls)
131
+ for r in result.results:
132
+ if r.is_success():
133
+ print(r.get_result())
134
+ ```
135
+
136
+ ### 文件处理
137
+
138
+ **重要**: SDK 只接收 S3 URL,不负责文件上传/下载。
139
+
140
+ ```python
141
+ # 传递 S3 URL 作为文件输入
142
+ result = client.run(
143
+ prefab_id="video-processor",
144
+ version="1.0.0",
145
+ function_name="extract_audio",
146
+ parameters={"format": "mp3"},
147
+ files={"video": ["s3://bucket/input.mp4"]}
148
+ )
149
+
150
+ # 输出文件也是 S3 URL
151
+ output_files = result.get_files()
152
+ # {"audio": ["s3://bucket/output.mp3"]}
153
+ ```
154
+
155
+ **文件处理流程**:
156
+ 1. 📤 使用 S3 客户端上传文件,获取 S3 URL
157
+ 2. 📝 将 S3 URL 传递给 SDK
158
+ 3. 📥 从返回的 S3 URL 下载结果文件
159
+
160
+ ---
161
+
162
+ ## 🆕 Agent 文件操作(新特性)
163
+
164
+ **适用场景**: Agent 内部需要上传、下载、管理文件。
165
+
166
+ **核心特性**:
167
+ - ✅ 永久文件存储(agent-outputs)
168
+ - ✅ 临时文件支持(agent-workspace,自动删除)
169
+ - ✅ Session 管理(批量清理中间文件)
170
+ - ✅ 预签名 URL(直接下载)
171
+
172
+ ### 初始化(Agent 专用)
173
+
174
+ ```python
175
+ from gateway_sdk import GatewayClient
176
+ import os
177
+
178
+ # Agent 从请求头获取 internal_token
179
+ internal_token = os.environ.get("X_INTERNAL_TOKEN")
180
+
181
+ # 初始化客户端(internal_token 已包含 user_id 和 agent_id)
182
+ client = GatewayClient(internal_token=internal_token)
183
+ ```
184
+
185
+ ### 上传永久文件
186
+
187
+ ```python
188
+ # 上传最终输出文件到 agent-outputs
189
+ result = client.upload_file("/tmp/result.pdf")
190
+
191
+ print(result["s3_url"]) # s3://bucket/agent-outputs/{user_id}/{agent_id}/...
192
+ print(result["filename"]) # result.pdf
193
+ print(result["size"]) # 文件大小(字节)
194
+ ```
195
+
196
+ ### 上传临时文件
197
+
198
+ ```python
199
+ import uuid
200
+
201
+ # 创建 session ID(用于批量管理)
202
+ session_id = str(uuid.uuid4())
203
+
204
+ # 上传中间文件(默认 24 小时后自动删除)
205
+ result = client.upload_temp_file(
206
+ "/tmp/intermediate.jpg",
207
+ ttl=3600, # 1 小时后删除
208
+ session_id=session_id # 关联到 session
209
+ )
210
+
211
+ print(result["s3_url"]) # s3://bucket/agent-workspace/{user_id}/{agent_id}/{session_id}/...
212
+ ```
213
+
214
+ ### 下载文件
215
+
216
+ ```python
217
+ # 下载文件(支持所有 S3 URL)
218
+ client.download_file(
219
+ "s3://bucket/agent-outputs/user123/agent456/result.pdf",
220
+ "/tmp/downloaded_result.pdf"
221
+ )
222
+
223
+ # 或获取预签名 URL(推荐,适合大文件)
224
+ presigned_url = client.get_presigned_url(
225
+ "s3://bucket/agent-outputs/...",
226
+ expires_in=3600 # 1 小时有效期
227
+ )
228
+ # 可以直接用 presigned_url 下载
229
+ ```
230
+
231
+ ### 列出文件
232
+
233
+ ```python
234
+ # 列出永久文件
235
+ result = client.list_files(limit=100)
236
+ for file in result["files"]:
237
+ print(file["s3_url"], file["size"], file["last_modified"])
238
+
239
+ # 翻页
240
+ if "next_token" in result:
241
+ next_page = client.list_files(limit=100, continuation_token=result["next_token"])
242
+ ```
243
+
244
+ ### 列出临时文件
245
+
246
+ ```python
247
+ # 列出指定 session 的临时文件
248
+ result = client.list_temp_files(session_id=session_id)
249
+ for file in result["files"]:
250
+ print(file["s3_url"])
251
+ ```
252
+
253
+ ### 清理临时文件
254
+
255
+ ```python
256
+ # 任务完成后立即清理 session 的所有临时文件
257
+ deleted_count = client.cleanup_temp_files(session_id=session_id)
258
+ print(f"清理了 {deleted_count} 个临时文件")
259
+ ```
260
+
261
+ ### 完整工作流示例
262
+
263
+ ```python
264
+ import uuid
265
+ from gateway_sdk import GatewayClient
266
+
267
+ # 1. 初始化
268
+ client = GatewayClient(internal_token=os.environ["X_INTERNAL_TOKEN"])
269
+
270
+ # 2. 创建 session
271
+ session_id = str(uuid.uuid4())
272
+
273
+ try:
274
+ # 3. 下载输入文件(前端上传或其他 Agent 传入)
275
+ client.download_file("s3://bucket/agent-inputs/.../input.mp4", "/tmp/input.mp4")
276
+
277
+ # 4. 处理并上传中间文件(临时)
278
+ # 假设提取音频
279
+ extract_audio("/tmp/input.mp4", "/tmp/audio.wav")
280
+ audio_result = client.upload_temp_file("/tmp/audio.wav", session_id=session_id)
281
+
282
+ # 假设提取帧
283
+ extract_frame("/tmp/input.mp4", "/tmp/frame.jpg")
284
+ frame_result = client.upload_temp_file("/tmp/frame.jpg", session_id=session_id)
285
+
286
+ # 5. 处理完成,上传最终结果(永久)
287
+ process_video("/tmp/input.mp4", "/tmp/output.mp4")
288
+ output_result = client.upload_file("/tmp/output.mp4")
289
+
290
+ # 6. 清理临时文件
291
+ client.cleanup_temp_files(session_id=session_id)
292
+
293
+ # 7. 返回结果 S3 URL
294
+ return {"output_url": output_result["s3_url"]}
295
+
296
+ except Exception as e:
297
+ # 出错时也要清理临时文件
298
+ client.cleanup_temp_files(session_id=session_id)
299
+ raise
300
+ ```
301
+
302
+ **最佳实践**:
303
+ 1. 🗑️ 使用 `session_id` 管理临时文件,任务完成后立即清理
304
+ 2. ⏰ 根据文件大小合理设置 `ttl`(避免浪费存储)
305
+ 3. 📦 最终输出文件使用 `upload_file()`(永久存储)
306
+ 4. 🔒 中间文件使用 `upload_temp_file()`(自动清理)
307
+
308
+ ---
309
+
310
+ ## 🌐 第三方集成(使用 API Key)
311
+
312
+ **适用场景**: 外部应用集成预制件生态(网站、脚本、CI/CD 等)
313
+
314
+ ### 快速开始
315
+
316
+ ```python
317
+ from gateway_sdk import GatewayClient
318
+
319
+ # 使用 API Key 初始化(自动转换为 internal_token)
320
+ client = GatewayClient.from_api_key("sk-xxx")
321
+
322
+ # 上传输入文件
323
+ s3_url = client.upload_input_file("/tmp/video.mp4", content_type="video/mp4")
324
+
325
+ # 调用 Prefab
326
+ result = client.run(
327
+ prefab_id="video-processor",
328
+ version="1.0.0",
329
+ function_name="extract_audio",
330
+ parameters={"format": "mp3"},
331
+ files={"video": [s3_url]}
332
+ )
333
+
334
+ print(result.get_result())
335
+ ```
336
+
337
+ ### 完整示例
338
+
339
+ ```python
340
+ from gateway_sdk import GatewayClient, AgentContextRequiredError
341
+
342
+ # 初始化
343
+ client = GatewayClient.from_api_key("sk-xxx")
344
+
345
+ try:
346
+ # 1. 上传输入文件
347
+ video_url = client.upload_input_file("/tmp/input.mp4", content_type="video/mp4")
348
+ print(f"Uploaded: {video_url}")
349
+
350
+ # 2. 调用 Prefab 处理
351
+ result = client.run(
352
+ prefab_id="video-processor",
353
+ version="1.0.0",
354
+ function_name="extract_audio",
355
+ parameters={"format": "mp3"},
356
+ files={"video": [video_url]}
357
+ )
358
+
359
+ # 3. 获取输出文件
360
+ if result.is_success():
361
+ output_files = result.get_files()
362
+ print(f"Output: {output_files}")
363
+ else:
364
+ print(f"Error: {result.error}")
365
+
366
+ except AgentContextRequiredError as e:
367
+ # 文件操作需要 Agent context(第三方集成不支持)
368
+ print(f"不支持的操作: {e}")
369
+ print("请使用 upload_input_file() 上传输入文件")
370
+ ```
371
+
372
+ ### 注意事项
373
+
374
+ **第三方集成的限制**:
375
+ - ✅ 可以调用任何 Prefab
376
+ - ✅ 可以上传输入文件(`upload_input_file()`)
377
+ - ❌ 不能使用 Agent 文件操作(`upload_file()`, `upload_temp_file()` 等)
378
+ - 这些操作需要 Agent context,仅在生产环境(Agent invoke)中可用
379
+
380
+ **与 Agent 开发的区别**:
381
+
382
+ | 特性 | 第三方集成 | Agent 开发 |
383
+ |------|-----------|-----------|
384
+ | 初始化 | `from_api_key()` | `GatewayClient(internal_token)` |
385
+ | 调用 Prefab | ✅ 支持 | ✅ 支持 |
386
+ | 上传输入 | `upload_input_file()` | ✅ 任意上传 |
387
+ | Agent 文件操作 | ❌ 不支持 | ✅ 支持 |
388
+
389
+ ---
390
+
391
+ ## API 参考
392
+
393
+ ### GatewayClient
394
+
395
+ #### 初始化
396
+
397
+ ```python
398
+ GatewayClient(
399
+ base_url: str = "http://nodeport.sensedeal.vip:30566",
400
+ api_key: Optional[str] = None,
401
+ jwt_token: Optional[str] = None,
402
+ timeout: int = 60
403
+ )
404
+ ```
405
+
406
+ **参数**:
407
+ - `api_key`: API Key
408
+ - `jwt_token`: JWT Token
409
+ - `timeout`: 请求超时时间(秒)
410
+
411
+ **注意**:必须提供 `api_key` 或 `jwt_token` 之一。
412
+
413
+ #### 方法
414
+
415
+ **run()** - 执行单个预制件
416
+
417
+ ```python
418
+ run(
419
+ prefab_id: str,
420
+ version: str,
421
+ function_name: str,
422
+ parameters: Dict[str, Any],
423
+ files: Optional[Dict[str, List[str]]] = None, # 仅接受 S3 URL
424
+ stream: bool = False
425
+ ) -> Union[PrefabResult, Iterator[StreamEvent]]
426
+ ```
427
+
428
+ 参数:
429
+ - `files`: 文件输入,格式为 `{"参数名": ["s3://url1", "s3://url2"]}`,**仅接受 S3 URL**
430
+
431
+ **run_batch()** - 批量执行
432
+
433
+ ```python
434
+ run_batch(calls: List[PrefabCall]) -> BatchResult
435
+ ```
436
+
437
+ **prefab()** - 获取预制件对象
438
+
439
+ ```python
440
+ prefab(prefab_id: str, version: str) -> Prefab
441
+ ```
442
+
443
+ **list_prefabs()** - 列出预制件
444
+
445
+ ```python
446
+ list_prefabs(status: Optional[str] = None) -> List[PrefabInfo]
447
+ ```
448
+
449
+ **get_prefab_spec()** - 获取预制件规格
450
+
451
+ ```python
452
+ get_prefab_spec(prefab_id: str, version: Optional[str] = None) -> Dict[str, Any]
453
+ ```
454
+
455
+ ### PrefabResult
456
+
457
+ 预制件执行结果。
458
+
459
+ **属性**:
460
+ - `status`: 调用状态(SUCCESS / FAILED)
461
+ - `output`: 输出数据
462
+ - `error`: 错误信息
463
+ - `job_id`: 任务 ID
464
+
465
+ **方法**:
466
+ - `is_success()`: 判断是否成功
467
+ - `get(key, default)`: 获取输出字段
468
+ - `get_result()`: 获取业务结果
469
+ - `get_files()`: 获取输出文件
470
+
471
+ ### StreamEvent
472
+
473
+ 流式事件。
474
+
475
+ **属性**:
476
+ - `type`: 事件类型(start / content / progress / done / error)
477
+ - `data`: 事件数据
478
+
479
+ ## 错误处理
480
+
481
+ ```python
482
+ from gateway_sdk.exceptions import (
483
+ GatewayError,
484
+ AuthenticationError,
485
+ PrefabNotFoundError,
486
+ ValidationError,
487
+ QuotaExceededError,
488
+ ServiceUnavailableError,
489
+ MissingSecretError,
490
+ )
491
+
492
+ try:
493
+ result = client.run(...)
494
+ except AuthenticationError as e:
495
+ print(f"认证失败: {e}")
496
+ except PrefabNotFoundError as e:
497
+ print(f"预制件不存在: {e}")
498
+ except MissingSecretError as e:
499
+ print(f"缺少密钥: {e.secret_name}")
500
+ except QuotaExceededError as e:
501
+ print(f"配额超限: {e.used}/{e.limit}")
502
+ except GatewayError as e:
503
+ print(f"错误: {e}")
504
+ ```
505
+
506
+ ## 示例代码
507
+
508
+ - `examples/basic_usage.py` - 基础用法
509
+ - `examples/streaming.py` - 流式响应
510
+ - `examples/file_operations.py` - 文件操作(Agent 专用)
511
+
512
+ ## 常见问题
513
+
514
+ **Q: 如何处理超时?**
515
+
516
+ A: 设置 `timeout` 参数:
517
+ ```python
518
+ client = GatewayClient(jwt_token="...", timeout=120)
519
+ ```
520
+
521
+ **Q: 如何调试?**
522
+
523
+ A: 启用日志:
524
+ ```python
525
+ import logging
526
+ logging.basicConfig(level=logging.DEBUG)
527
+ ```
528
+
529
+ **Q: 如何停止流式响应?**
530
+
531
+ A: 使用 `break` 跳出循环:
532
+ ```python
533
+ for event in client.run(..., stream=True):
534
+ if some_condition:
535
+ break
536
+ ```
537
+
538
+ ## 许可证
539
+
540
+ MIT License
@@ -0,0 +1,10 @@
1
+ gateway_sdk/__init__.py,sha256=kbGV6rwTCeambM88Eoh8JXz9-JoO2qx9SiI2Z6IkijA,719
2
+ gateway_sdk/auth.py,sha256=Nx0p9FYZHBoQeN8SO2nn7MKH2y02v3UR3M8bYqhQG0w,1165
3
+ gateway_sdk/client.py,sha256=4FKbJ1o5mEoHER4_phhdmlMb-cDuq1herAjcDi3UUYk,24095
4
+ gateway_sdk/exceptions.py,sha256=ZpJDqCa4-uOfXOYEQ6XMdSeNhYeT4AQOPKRiSHVrGO0,2513
5
+ gateway_sdk/models.py,sha256=ge96k0Qcii7ESEwJQoO5fZwUZBcVbe2Ccgna3kPOAB8,6072
6
+ gateway_sdk/streaming.py,sha256=nwRWxKP5bU8uIMIv_82DyFws9djnEKdAxW6S4L9KtPI,1092
7
+ agent_builder_gateway_sdk-0.7.1.dist-info/METADATA,sha256=Yd9BKcLZ9_IDQEUNEvWqLxN3XXk-Z4XFGlpaXNr-gvk,13380
8
+ agent_builder_gateway_sdk-0.7.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ agent_builder_gateway_sdk-0.7.1.dist-info/licenses/LICENSE,sha256=C3JXUJe4vPtoVe1lp7O4WeGtH71JWYrRaCufEoJ84K0,1076
10
+ agent_builder_gateway_sdk-0.7.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Agent Builder Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,34 @@
1
+ """
2
+ Agent Builder Gateway SDK
3
+ 用于 AI 构建的程序调用预制件的 Python SDK
4
+ """
5
+
6
+ from .client import GatewayClient
7
+ from .models import PrefabCall, PrefabResult, BatchResult, StreamEvent
8
+ from .exceptions import (
9
+ GatewayError,
10
+ AuthenticationError,
11
+ PrefabNotFoundError,
12
+ ValidationError,
13
+ QuotaExceededError,
14
+ ServiceUnavailableError,
15
+ AgentContextRequiredError,
16
+ )
17
+
18
+ __version__ = "0.6.0"
19
+
20
+ __all__ = [
21
+ "GatewayClient",
22
+ "PrefabCall",
23
+ "PrefabResult",
24
+ "BatchResult",
25
+ "StreamEvent",
26
+ "GatewayError",
27
+ "AuthenticationError",
28
+ "PrefabNotFoundError",
29
+ "ValidationError",
30
+ "QuotaExceededError",
31
+ "ServiceUnavailableError",
32
+ "AgentContextRequiredError",
33
+ ]
34
+
gateway_sdk/auth.py ADDED
@@ -0,0 +1,49 @@
1
+ """认证管理"""
2
+
3
+ from typing import Optional
4
+
5
+
6
+ class AuthManager:
7
+ """认证管理器"""
8
+
9
+ def __init__(self, api_key: Optional[str] = None, jwt_token: Optional[str] = None):
10
+ """
11
+ 初始化认证管理器
12
+
13
+ Args:
14
+ api_key: API Key(优先级高)
15
+ jwt_token: JWT Token
16
+ """
17
+ self.api_key = api_key
18
+ self.jwt_token = jwt_token
19
+
20
+ def get_headers(self) -> dict[str, str]:
21
+ """
22
+ 获取认证请求头
23
+
24
+ Returns:
25
+ 认证请求头字典
26
+ """
27
+ headers: dict[str, str] = {}
28
+
29
+ # API Key 优先
30
+ if self.api_key:
31
+ headers["X-API-Key"] = self.api_key
32
+ elif self.jwt_token:
33
+ # 确保 Token 格式正确
34
+ token = self.jwt_token
35
+ if not token.startswith("Bearer "):
36
+ token = f"Bearer {token}"
37
+ headers["Authorization"] = token
38
+
39
+ return headers
40
+
41
+ def is_authenticated(self) -> bool:
42
+ """
43
+ 判断是否已配置认证信息
44
+
45
+ Returns:
46
+ 是否已配置认证
47
+ """
48
+ return bool(self.api_key or self.jwt_token)
49
+