tamar-model-client 0.1.27__py3-none-any.whl → 0.1.30__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.
- tamar_model_client/async_client.py +83 -40
- tamar_model_client/circuit_breaker.py +6 -3
- tamar_model_client/core/__init__.py +5 -1
- tamar_model_client/core/base_client.py +136 -40
- tamar_model_client/core/http_fallback.py +313 -31
- tamar_model_client/core/logging_setup.py +15 -1
- tamar_model_client/core/utils.py +27 -1
- tamar_model_client/error_handler.py +112 -17
- tamar_model_client/json_formatter.py +9 -0
- tamar_model_client/sync_client.py +177 -38
- {tamar_model_client-0.1.27.dist-info → tamar_model_client-0.1.30.dist-info}/METADATA +588 -6
- {tamar_model_client-0.1.27.dist-info → tamar_model_client-0.1.30.dist-info}/RECORD +16 -15
- tests/test_circuit_breaker.py +269 -0
- tests/test_google_azure_final.py +605 -21
- {tamar_model_client-0.1.27.dist-info → tamar_model_client-0.1.30.dist-info}/WHEEL +0 -0
- {tamar_model_client-0.1.27.dist-info → tamar_model_client-0.1.30.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: tamar-model-client
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.30
|
4
4
|
Summary: A Python SDK for interacting with the Model Manager gRPC service
|
5
5
|
Home-page: http://gitlab.tamaredge.top/project-tap/AgentOS/model-manager-client
|
6
6
|
Author: Oscar Ou
|
@@ -18,6 +18,8 @@ Requires-Dist: PyJWT
|
|
18
18
|
Requires-Dist: nest_asyncio
|
19
19
|
Requires-Dist: openai
|
20
20
|
Requires-Dist: google-genai
|
21
|
+
Requires-Dist: requests>=2.25.0
|
22
|
+
Requires-Dist: aiohttp>=3.7.0
|
21
23
|
Dynamic: author
|
22
24
|
Dynamic: author-email
|
23
25
|
Dynamic: classifier
|
@@ -65,11 +67,15 @@ Dynamic: summary
|
|
65
67
|
- 🔄 **自动重试** 指数退避策略
|
66
68
|
|
67
69
|
### 🛡️ 生产级特性
|
70
|
+
- 🛡️ **熔断降级** 服务故障时自动切换到 HTTP
|
71
|
+
- 🚀 **快速降级** 失败立即降级,最大化成功率
|
68
72
|
- 🔐 **JWT 认证** 安全可靠
|
69
73
|
- 📊 **使用量追踪** Token 统计与成本计算
|
70
|
-
- 🆔 **请求追踪** 唯一 request_id
|
71
|
-
- ⚠️ **完善错误处理**
|
74
|
+
- 🆔 **请求追踪** 唯一 request_id 和 origin_request_id 全链路追踪
|
75
|
+
- ⚠️ **完善错误处理** 详细错误信息和异常堆栈追踪
|
72
76
|
- ✅ **类型安全** Pydantic v2 验证
|
77
|
+
- 📦 **批量降级** HTTP 降级支持批量请求
|
78
|
+
- 🔍 **结构化日志** JSON 格式日志便于监控分析
|
73
79
|
|
74
80
|
### 🚀 高性能设计
|
75
81
|
- 🔗 **gRPC 通信** HTTP/2 长连接
|
@@ -87,7 +93,14 @@ pip install tamar-model-client
|
|
87
93
|
|
88
94
|
- Python ≥ 3.8
|
89
95
|
- 支持 Windows / Linux / macOS
|
90
|
-
-
|
96
|
+
- 依赖项会自动安装(包括以下核心库):
|
97
|
+
- `grpcio>=1.67.1` - gRPC 通信协议
|
98
|
+
- `pydantic` - 数据验证和序列化
|
99
|
+
- `PyJWT` - JWT 认证
|
100
|
+
- `requests>=2.25.0` - HTTP 降级功能(同步)
|
101
|
+
- `aiohttp>=3.7.0` - HTTP 降级功能(异步)
|
102
|
+
- `openai` - OpenAI 服务商支持
|
103
|
+
- `google-genai` - Google AI 服务商支持
|
91
104
|
|
92
105
|
## 🏗️ 项目架构
|
93
106
|
|
@@ -103,10 +116,20 @@ tamar_model_client/
|
|
103
116
|
│ ├── providers.py # AI 服务商(OpenAI, Google, Azure...)
|
104
117
|
│ ├── invoke.py # 调用类型(generation, images...)
|
105
118
|
│ └── channel.py # 服务通道(openai, vertexai...)
|
119
|
+
├── 📁 core/ # 核心功能模块
|
120
|
+
│ ├── base_client.py # 客户端基类(熔断、降级、配置)
|
121
|
+
│ ├── http_fallback.py # HTTP 降级功能(支持批量请求)
|
122
|
+
│ ├── request_builder.py # 请求构建器
|
123
|
+
│ ├── response_handler.py # 响应处理器
|
124
|
+
│ ├── logging_setup.py # 结构化日志配置
|
125
|
+
│ └── utils.py # 请求ID管理和工具函数
|
106
126
|
├── 📄 sync_client.py # 同步客户端 TamarModelClient
|
107
127
|
├── 📄 async_client.py # 异步客户端 AsyncTamarModelClient
|
128
|
+
├── 📄 error_handler.py # 增强错误处理和重试策略
|
129
|
+
├── 📄 circuit_breaker.py # 熔断器实现
|
108
130
|
├── 📄 exceptions.py # 异常层级定义
|
109
131
|
├── 📄 auth.py # JWT 认证管理
|
132
|
+
├── 📄 json_formatter.py # JSON 日志格式化器
|
110
133
|
└── 📄 utils.py # 工具函数
|
111
134
|
```
|
112
135
|
|
@@ -339,7 +362,7 @@ async def main():
|
|
339
362
|
)
|
340
363
|
|
341
364
|
# 发送请求并获取响应
|
342
|
-
async for r in await client.invoke(
|
365
|
+
async for r in await client.invoke(request_data):
|
343
366
|
if r.error:
|
344
367
|
print(f"错误: {r.error}")
|
345
368
|
else:
|
@@ -496,6 +519,305 @@ response = client.invoke(
|
|
496
519
|
)
|
497
520
|
```
|
498
521
|
|
522
|
+
### 🔄 错误处理最佳实践
|
523
|
+
|
524
|
+
SDK 提供了完善的异常体系,便于精确处理不同类型的错误:
|
525
|
+
|
526
|
+
```python
|
527
|
+
from tamar_model_client import TamarModelClient
|
528
|
+
from tamar_model_client.exceptions import (
|
529
|
+
TamarModelException,
|
530
|
+
NetworkException,
|
531
|
+
AuthenticationException,
|
532
|
+
RateLimitException,
|
533
|
+
ProviderException,
|
534
|
+
TimeoutException
|
535
|
+
)
|
536
|
+
|
537
|
+
client = TamarModelClient()
|
538
|
+
|
539
|
+
try:
|
540
|
+
response = client.invoke(request)
|
541
|
+
except TimeoutException as e:
|
542
|
+
# 处理超时错误
|
543
|
+
logger.warning(f"请求超时: {e.message}, request_id: {e.request_id}")
|
544
|
+
# 可以重试或使用更快的模型
|
545
|
+
except RateLimitException as e:
|
546
|
+
# 处理限流错误
|
547
|
+
logger.error(f"触发限流: {e.message}")
|
548
|
+
# 等待一段时间后重试
|
549
|
+
time.sleep(60)
|
550
|
+
except AuthenticationException as e:
|
551
|
+
# 处理认证错误
|
552
|
+
logger.error(f"认证失败: {e.message}")
|
553
|
+
# 检查 JWT 配置
|
554
|
+
except NetworkException as e:
|
555
|
+
# 处理网络错误(已自动重试后仍失败)
|
556
|
+
logger.error(f"网络错误: {e.message}")
|
557
|
+
# 可能需要检查网络连接或服务状态
|
558
|
+
except ProviderException as e:
|
559
|
+
# 处理提供商特定错误
|
560
|
+
logger.error(f"提供商错误: {e.message}")
|
561
|
+
# 根据错误码进行特定处理
|
562
|
+
if "insufficient_quota" in str(e):
|
563
|
+
# 切换到其他提供商
|
564
|
+
pass
|
565
|
+
except TamarModelException as e:
|
566
|
+
# 处理其他所有模型相关错误
|
567
|
+
logger.error(f"模型错误: {e.message}")
|
568
|
+
logger.error(f"错误上下文: {e.error_context}")
|
569
|
+
```
|
570
|
+
|
571
|
+
### 🔀 多提供商无缝切换
|
572
|
+
|
573
|
+
轻松实现提供商之间的切换和降级:
|
574
|
+
|
575
|
+
```python
|
576
|
+
from tamar_model_client import TamarModelClient
|
577
|
+
from tamar_model_client.schemas import ModelRequest, UserContext
|
578
|
+
from tamar_model_client.enums import ProviderType
|
579
|
+
from tamar_model_client.exceptions import ProviderException, RateLimitException
|
580
|
+
|
581
|
+
client = TamarModelClient()
|
582
|
+
|
583
|
+
# 定义提供商优先级
|
584
|
+
providers = [
|
585
|
+
(ProviderType.OPENAI, "gpt-4"),
|
586
|
+
(ProviderType.GOOGLE, "gemini-pro"),
|
587
|
+
(ProviderType.AZURE, "gpt-4o-mini")
|
588
|
+
]
|
589
|
+
|
590
|
+
user_context = UserContext(
|
591
|
+
user_id="test_user",
|
592
|
+
org_id="test_org",
|
593
|
+
client_type="python-sdk"
|
594
|
+
)
|
595
|
+
|
596
|
+
# 尝试多个提供商直到成功
|
597
|
+
for provider, model in providers:
|
598
|
+
try:
|
599
|
+
request = ModelRequest(
|
600
|
+
provider=provider,
|
601
|
+
model=model,
|
602
|
+
messages=[{"role": "user", "content": "Hello"}] if provider != ProviderType.GOOGLE else None,
|
603
|
+
contents=[{"role": "user", "parts": [{"text": "Hello"}]}] if provider == ProviderType.GOOGLE else None,
|
604
|
+
user_context=user_context
|
605
|
+
)
|
606
|
+
|
607
|
+
response = client.invoke(request)
|
608
|
+
print(f"成功使用 {provider.value} - {model}")
|
609
|
+
print(f"响应: {response.content}")
|
610
|
+
break
|
611
|
+
|
612
|
+
except (ProviderException, RateLimitException) as e:
|
613
|
+
logger.warning(f"{provider.value} 失败: {e.message}")
|
614
|
+
continue
|
615
|
+
```
|
616
|
+
|
617
|
+
### 🎯 请求上下文管理
|
618
|
+
|
619
|
+
使用上下文管理器确保资源正确释放:
|
620
|
+
|
621
|
+
```python
|
622
|
+
from tamar_model_client import TamarModelClient, AsyncTamarModelClient
|
623
|
+
import asyncio
|
624
|
+
|
625
|
+
# 同步客户端上下文管理器
|
626
|
+
with TamarModelClient() as client:
|
627
|
+
response = client.invoke(request)
|
628
|
+
print(response.content)
|
629
|
+
# 自动调用 client.close()
|
630
|
+
|
631
|
+
# 异步客户端上下文管理器
|
632
|
+
async def async_example():
|
633
|
+
async with AsyncTamarModelClient() as client:
|
634
|
+
response = await client.invoke(request)
|
635
|
+
print(response.content)
|
636
|
+
# 自动调用 await client.close()
|
637
|
+
|
638
|
+
asyncio.run(async_example())
|
639
|
+
```
|
640
|
+
|
641
|
+
### ⏱️ 超时控制
|
642
|
+
|
643
|
+
通过环境变量或代码控制请求超时:
|
644
|
+
|
645
|
+
```python
|
646
|
+
import os
|
647
|
+
from tamar_model_client import TamarModelClient
|
648
|
+
|
649
|
+
# 方式一:环境变量设置全局超时
|
650
|
+
os.environ['MODEL_MANAGER_SERVER_GRPC_TIMEOUT'] = '30' # 30秒超时
|
651
|
+
|
652
|
+
# 方式二:创建客户端时设置
|
653
|
+
client = TamarModelClient(
|
654
|
+
server_address="localhost:50051",
|
655
|
+
timeout=30.0 # 30秒超时
|
656
|
+
)
|
657
|
+
|
658
|
+
# 处理超时
|
659
|
+
try:
|
660
|
+
response = client.invoke(request)
|
661
|
+
except TimeoutException as e:
|
662
|
+
logger.error(f"请求超时: {e.message}")
|
663
|
+
# 可以尝试更小的模型或减少 max_tokens
|
664
|
+
```
|
665
|
+
|
666
|
+
### 📊 性能监控与指标
|
667
|
+
|
668
|
+
获取详细的性能指标和使用统计:
|
669
|
+
|
670
|
+
```python
|
671
|
+
from tamar_model_client import TamarModelClient
|
672
|
+
import time
|
673
|
+
|
674
|
+
client = TamarModelClient()
|
675
|
+
|
676
|
+
# 监控单次请求性能
|
677
|
+
start_time = time.time()
|
678
|
+
response = client.invoke(request)
|
679
|
+
latency = time.time() - start_time
|
680
|
+
|
681
|
+
print(f"请求延迟: {latency:.2f}秒")
|
682
|
+
print(f"Request ID: {response.request_id}")
|
683
|
+
if response.usage:
|
684
|
+
print(f"输入 Tokens: {response.usage.prompt_tokens}")
|
685
|
+
print(f"输出 Tokens: {response.usage.completion_tokens}")
|
686
|
+
print(f"总 Tokens: {response.usage.total_tokens}")
|
687
|
+
print(f"预估成本: ${response.usage.total_cost:.4f}")
|
688
|
+
|
689
|
+
# 获取熔断器指标
|
690
|
+
metrics = client.get_resilient_metrics()
|
691
|
+
if metrics:
|
692
|
+
print(f"\n熔断器状态:")
|
693
|
+
print(f"- 状态: {metrics['circuit_state']}")
|
694
|
+
print(f"- 失败次数: {metrics['failure_count']}")
|
695
|
+
print(f"- 上次失败: {metrics['last_failure_time']}")
|
696
|
+
print(f"- HTTP降级地址: {metrics['http_fallback_url']}")
|
697
|
+
```
|
698
|
+
|
699
|
+
### 🔧 自定义配置示例
|
700
|
+
|
701
|
+
灵活的配置选项满足不同场景需求:
|
702
|
+
|
703
|
+
```python
|
704
|
+
from tamar_model_client import TamarModelClient
|
705
|
+
|
706
|
+
# 完整配置示例
|
707
|
+
client = TamarModelClient(
|
708
|
+
# 服务器配置
|
709
|
+
server_address="grpc.example.com:443",
|
710
|
+
use_tls=True,
|
711
|
+
default_authority="grpc.example.com",
|
712
|
+
|
713
|
+
# 认证配置
|
714
|
+
jwt_secret_key="your-secret-key",
|
715
|
+
jwt_expiration=3600, # 1小时过期
|
716
|
+
|
717
|
+
# 重试配置
|
718
|
+
max_retries=5,
|
719
|
+
retry_delay=1.0,
|
720
|
+
|
721
|
+
# 超时配置
|
722
|
+
timeout=60.0,
|
723
|
+
|
724
|
+
# 熔断降级配置
|
725
|
+
resilient_enabled=True,
|
726
|
+
http_fallback_url="https://backup.example.com",
|
727
|
+
circuit_breaker_threshold=3,
|
728
|
+
circuit_breaker_timeout=30
|
729
|
+
)
|
730
|
+
```
|
731
|
+
|
732
|
+
### 🔐 安全最佳实践
|
733
|
+
|
734
|
+
确保 SDK 使用的安全性:
|
735
|
+
|
736
|
+
```python
|
737
|
+
import os
|
738
|
+
from tamar_model_client import TamarModelClient
|
739
|
+
|
740
|
+
# 1. 使用环境变量存储敏感信息
|
741
|
+
os.environ['MODEL_MANAGER_SERVER_JWT_SECRET_KEY'] = os.getenv('JWT_SECRET')
|
742
|
+
|
743
|
+
# 2. 启用 TLS 加密
|
744
|
+
client = TamarModelClient(
|
745
|
+
server_address="grpc.example.com:443",
|
746
|
+
use_tls=True
|
747
|
+
)
|
748
|
+
|
749
|
+
# 3. 最小权限原则 - 只请求需要的数据
|
750
|
+
request = ModelRequest(
|
751
|
+
provider=ProviderType.OPENAI,
|
752
|
+
model="gpt-3.5-turbo",
|
753
|
+
messages=[{"role": "user", "content": "分析这段文本"}],
|
754
|
+
user_context=UserContext(
|
755
|
+
user_id="limited_user",
|
756
|
+
org_id="restricted_org",
|
757
|
+
client_type="analysis-service"
|
758
|
+
),
|
759
|
+
max_tokens=100 # 限制输出长度
|
760
|
+
)
|
761
|
+
|
762
|
+
# 4. 审计日志
|
763
|
+
response = client.invoke(request)
|
764
|
+
logger.info(f"AI请求审计: user={request.user_context.user_id}, model={request.model}, request_id={response.request_id}")
|
765
|
+
```
|
766
|
+
|
767
|
+
### 🚀 并发请求优化
|
768
|
+
|
769
|
+
高效处理大量并发请求:
|
770
|
+
|
771
|
+
```python
|
772
|
+
import asyncio
|
773
|
+
from tamar_model_client import AsyncTamarModelClient
|
774
|
+
from tamar_model_client.schemas import ModelRequest, UserContext
|
775
|
+
|
776
|
+
async def process_batch_async(questions: list[str]):
|
777
|
+
"""异步并发处理多个问题"""
|
778
|
+
async with AsyncTamarModelClient() as client:
|
779
|
+
tasks = []
|
780
|
+
|
781
|
+
for i, question in enumerate(questions):
|
782
|
+
request = ModelRequest(
|
783
|
+
provider=ProviderType.OPENAI,
|
784
|
+
model="gpt-3.5-turbo",
|
785
|
+
messages=[{"role": "user", "content": question}],
|
786
|
+
user_context=UserContext(
|
787
|
+
user_id="batch_user",
|
788
|
+
org_id="test_org",
|
789
|
+
client_type="batch-processor"
|
790
|
+
)
|
791
|
+
)
|
792
|
+
|
793
|
+
# 创建异步任务
|
794
|
+
task = asyncio.create_task(client.invoke(request))
|
795
|
+
tasks.append((i, task))
|
796
|
+
|
797
|
+
# 并发执行所有请求
|
798
|
+
results = []
|
799
|
+
for i, task in tasks:
|
800
|
+
try:
|
801
|
+
response = await task
|
802
|
+
results.append((i, response.content))
|
803
|
+
except Exception as e:
|
804
|
+
results.append((i, f"Error: {str(e)}"))
|
805
|
+
|
806
|
+
return results
|
807
|
+
|
808
|
+
# 使用示例
|
809
|
+
questions = [
|
810
|
+
"什么是人工智能?",
|
811
|
+
"解释机器学习的原理",
|
812
|
+
"深度学习和机器学习的区别",
|
813
|
+
"什么是神经网络?"
|
814
|
+
]
|
815
|
+
|
816
|
+
results = asyncio.run(process_batch_async(questions))
|
817
|
+
for i, content in results:
|
818
|
+
print(f"问题 {i+1} 的回答: {content[:100]}...")
|
819
|
+
```
|
820
|
+
|
499
821
|
## 🛠️ 高级功能
|
500
822
|
|
501
823
|
### 🔥 使用场景和最佳实践
|
@@ -587,6 +909,156 @@ metrics = client.get_resilient_metrics()
|
|
587
909
|
# }
|
588
910
|
```
|
589
911
|
|
912
|
+
### 🚀 快速降级功能(用户体验优化)
|
913
|
+
|
914
|
+
在传统的熔断降级基础上,SDK 新增了快速降级功能,进一步提升用户体验:
|
915
|
+
|
916
|
+
#### 传统降级 vs 快速降级
|
917
|
+
|
918
|
+
**传统模式**:
|
919
|
+
```
|
920
|
+
gRPC请求 → 失败 → 重试1 → 失败 → 重试2 → 失败 → ... → 重试N → 失败 → HTTP降级
|
921
|
+
耗时:(重试次数 × 退避时间) + 降级时间 // 可能需要十几秒
|
922
|
+
```
|
923
|
+
|
924
|
+
**快速降级模式**:
|
925
|
+
```
|
926
|
+
gRPC请求 → 失败 → 立即HTTP降级 (或重试1次后降级)
|
927
|
+
耗时:降级时间 // 通常1-2秒内完成
|
928
|
+
```
|
929
|
+
|
930
|
+
#### 降级策略配置
|
931
|
+
|
932
|
+
- **立即降级错误**:`UNAVAILABLE`, `DEADLINE_EXCEEDED`, `CANCELLED` (网络问题)
|
933
|
+
- **延迟降级错误**:其他错误重试指定次数后降级
|
934
|
+
- **永不降级错误**:`UNAUTHENTICATED`, `PERMISSION_DENIED`, `INVALID_ARGUMENT` (客户端问题)
|
935
|
+
|
936
|
+
#### 批量请求降级支持
|
937
|
+
|
938
|
+
快速降级同时支持单个请求和批量请求:
|
939
|
+
|
940
|
+
```python
|
941
|
+
# 单个请求降级
|
942
|
+
response = client.invoke(request) # 自动降级到 /v1/invoke
|
943
|
+
|
944
|
+
# 批量请求降级
|
945
|
+
batch_response = client.invoke_batch(batch_request) # 自动降级到 /v1/batch-invoke
|
946
|
+
```
|
947
|
+
|
948
|
+
#### 使用示例
|
949
|
+
|
950
|
+
```python
|
951
|
+
from tamar_model_client import TamarModelClient
|
952
|
+
|
953
|
+
# 启用快速降级(通过环境变量)
|
954
|
+
# MODEL_CLIENT_FAST_FALLBACK_ENABLED=true
|
955
|
+
# MODEL_CLIENT_FALLBACK_AFTER_RETRIES=1
|
956
|
+
|
957
|
+
client = TamarModelClient()
|
958
|
+
|
959
|
+
# 正常使用,快速降级对用户透明
|
960
|
+
response = client.invoke(request)
|
961
|
+
# 如果gRPC不可用,会在1-2秒内自动切换到HTTP并返回结果
|
962
|
+
```
|
963
|
+
|
964
|
+
#### 配置选项详解
|
965
|
+
|
966
|
+
```bash
|
967
|
+
# 启用快速降级(默认false,建议开启)
|
968
|
+
MODEL_CLIENT_FAST_FALLBACK_ENABLED=true
|
969
|
+
|
970
|
+
# 非立即降级的错误,重试多少次后降级(默认1次)
|
971
|
+
MODEL_CLIENT_FALLBACK_AFTER_RETRIES=1
|
972
|
+
|
973
|
+
# 网络错误立即降级(默认配置)
|
974
|
+
MODEL_CLIENT_IMMEDIATE_FALLBACK_ERRORS=UNAVAILABLE,DEADLINE_EXCEEDED,CANCELLED
|
975
|
+
|
976
|
+
# 认证错误永不降级(避免无效降级)
|
977
|
+
MODEL_CLIENT_NEVER_FALLBACK_ERRORS=UNAUTHENTICATED,PERMISSION_DENIED,INVALID_ARGUMENT
|
978
|
+
```
|
979
|
+
|
980
|
+
### 🔍 请求追踪与监控
|
981
|
+
|
982
|
+
SDK 提供了完善的请求追踪功能,便于问题排查和性能监控:
|
983
|
+
|
984
|
+
#### 请求 ID 追踪
|
985
|
+
|
986
|
+
每个请求都会自动生成唯一的 `request_id`,用于追踪单次请求:
|
987
|
+
|
988
|
+
```python
|
989
|
+
from tamar_model_client import TamarModelClient
|
990
|
+
from tamar_model_client.core import generate_request_id, set_request_id
|
991
|
+
|
992
|
+
# 自动生成 request_id
|
993
|
+
response = client.invoke(request)
|
994
|
+
print(f"Request ID: {response.request_id}")
|
995
|
+
|
996
|
+
# 手动设置 request_id
|
997
|
+
custom_request_id = generate_request_id()
|
998
|
+
set_request_id(custom_request_id)
|
999
|
+
response = client.invoke(request)
|
1000
|
+
```
|
1001
|
+
|
1002
|
+
#### 原始请求 ID 追踪
|
1003
|
+
|
1004
|
+
对于需要跨多个服务调用的场景,可以使用 `origin_request_id` 进行全链路追踪:
|
1005
|
+
|
1006
|
+
```python
|
1007
|
+
from tamar_model_client.core import set_origin_request_id
|
1008
|
+
|
1009
|
+
# 设置原始请求 ID(通常来自上游服务)
|
1010
|
+
set_origin_request_id("user-provided-id-123")
|
1011
|
+
|
1012
|
+
# 所有后续请求都会携带这个 origin_request_id
|
1013
|
+
response = client.invoke(request)
|
1014
|
+
```
|
1015
|
+
|
1016
|
+
#### 结构化日志
|
1017
|
+
|
1018
|
+
启用 JSON 日志格式后,每条日志都包含完整的追踪信息:
|
1019
|
+
|
1020
|
+
```json
|
1021
|
+
{
|
1022
|
+
"timestamp": "2025-07-03T14:40:32.729313",
|
1023
|
+
"level": "INFO",
|
1024
|
+
"type": "request",
|
1025
|
+
"uri": "/invoke/openai/chat",
|
1026
|
+
"request_id": "448a64f4-3bb0-467c-af15-d4181d0ac499",
|
1027
|
+
"data": {
|
1028
|
+
"origin_request_id": "user-provided-id-123",
|
1029
|
+
"provider": "openai",
|
1030
|
+
"model": "gpt-4",
|
1031
|
+
"stream": false
|
1032
|
+
},
|
1033
|
+
"message": "🚀 Invoke request started"
|
1034
|
+
}
|
1035
|
+
```
|
1036
|
+
|
1037
|
+
#### 错误追踪
|
1038
|
+
|
1039
|
+
错误日志包含异常堆栈和完整上下文:
|
1040
|
+
|
1041
|
+
```json
|
1042
|
+
{
|
1043
|
+
"timestamp": "2025-07-03T14:40:35.123456",
|
1044
|
+
"level": "ERROR",
|
1045
|
+
"type": "response",
|
1046
|
+
"request_id": "448a64f4-3bb0-467c-af15-d4181d0ac499",
|
1047
|
+
"data": {
|
1048
|
+
"origin_request_id": "user-provided-id-123",
|
1049
|
+
"error_code": "DEADLINE_EXCEEDED",
|
1050
|
+
"error_message": "Request timeout after 30 seconds",
|
1051
|
+
"retry_count": 2,
|
1052
|
+
"fallback_attempted": true
|
1053
|
+
},
|
1054
|
+
"exception": {
|
1055
|
+
"type": "TimeoutException",
|
1056
|
+
"message": "Request timeout after 30 seconds",
|
1057
|
+
"traceback": ["Traceback (most recent call last):", "..."]
|
1058
|
+
}
|
1059
|
+
}
|
1060
|
+
```
|
1061
|
+
|
590
1062
|
### ⚠️ 注意事项
|
591
1063
|
|
592
1064
|
1. **参数说明**
|
@@ -599,9 +1071,11 @@ metrics = client.get_resilient_metrics()
|
|
599
1071
|
- 如需多实例,务必调用 `client.close()` 释放资源
|
600
1072
|
|
601
1073
|
3. **错误处理**
|
602
|
-
- 所有错误包含 `request_id`
|
1074
|
+
- 所有错误包含 `request_id` 和 `origin_request_id` 用于全链路问题追踪
|
603
1075
|
- 网络错误会自动重试(指数退避)
|
604
1076
|
- 提供商错误保留原始错误信息
|
1077
|
+
- 支持异常堆栈追踪,便于问题排查
|
1078
|
+
- 结构化 JSON 日志格式,便于监控系统集成
|
605
1079
|
|
606
1080
|
## ⚙️ 环境变量配置(推荐)
|
607
1081
|
|
@@ -614,6 +1088,11 @@ export MODEL_MANAGER_SERVER_GRPC_USE_TLS="false"
|
|
614
1088
|
export MODEL_MANAGER_SERVER_GRPC_DEFAULT_AUTHORITY="localhost"
|
615
1089
|
export MODEL_MANAGER_SERVER_GRPC_MAX_RETRIES="5"
|
616
1090
|
export MODEL_MANAGER_SERVER_GRPC_RETRY_DELAY="1.5"
|
1091
|
+
|
1092
|
+
# 快速降级配置(可选,优化用户体验)
|
1093
|
+
export MODEL_CLIENT_FAST_FALLBACK_ENABLED="true"
|
1094
|
+
export MODEL_CLIENT_HTTP_FALLBACK_URL="http://localhost:8080"
|
1095
|
+
export MODEL_CLIENT_FALLBACK_AFTER_RETRIES="1"
|
617
1096
|
```
|
618
1097
|
|
619
1098
|
或者本地 `.env` 文件
|
@@ -667,6 +1146,39 @@ MODEL_CLIENT_CIRCUIT_BREAKER_THRESHOLD=5
|
|
667
1146
|
|
668
1147
|
# 熔断器恢复超时(秒,熔断后多久尝试恢复,默认 60)
|
669
1148
|
MODEL_CLIENT_CIRCUIT_BREAKER_TIMEOUT=60
|
1149
|
+
|
1150
|
+
|
1151
|
+
# ========================
|
1152
|
+
# 🚀 快速降级配置(可选,优化体验)
|
1153
|
+
# ========================
|
1154
|
+
|
1155
|
+
# 是否启用快速降级功能(默认 false,建议开启)
|
1156
|
+
# 启用后,gRPC 请求失败时会立即尝试 HTTP 降级,而不是等待所有重试完成
|
1157
|
+
MODEL_CLIENT_FAST_FALLBACK_ENABLED=true
|
1158
|
+
|
1159
|
+
# 降级前的最大 gRPC 重试次数(默认 1)
|
1160
|
+
# 对于非立即降级的错误,重试指定次数后才尝试降级
|
1161
|
+
MODEL_CLIENT_FALLBACK_AFTER_RETRIES=1
|
1162
|
+
|
1163
|
+
# 立即降级的错误类型(逗号分隔,默认网络相关错误)
|
1164
|
+
# 这些错误类型会在第一次失败后立即尝试降级
|
1165
|
+
MODEL_CLIENT_IMMEDIATE_FALLBACK_ERRORS=UNAVAILABLE,DEADLINE_EXCEEDED,CANCELLED
|
1166
|
+
|
1167
|
+
# 永不降级的错误类型(逗号分隔,默认认证相关错误)
|
1168
|
+
# 这些错误类型不会触发降级,通常是客户端问题而非服务不可用
|
1169
|
+
MODEL_CLIENT_NEVER_FALLBACK_ERRORS=UNAUTHENTICATED,PERMISSION_DENIED,INVALID_ARGUMENT
|
1170
|
+
|
1171
|
+
|
1172
|
+
# ========================
|
1173
|
+
# 🔍 日志与监控配置(可选)
|
1174
|
+
# ========================
|
1175
|
+
|
1176
|
+
# 启用结构化 JSON 日志格式(默认 false,建议开启)
|
1177
|
+
# 启用后日志将以 JSON 格式输出,便于监控系统集成
|
1178
|
+
MODEL_CLIENT_ENABLE_JSON_LOGGING=true
|
1179
|
+
|
1180
|
+
# 日志级别设置(DEBUG, INFO, WARNING, ERROR,默认 INFO)
|
1181
|
+
MODEL_CLIENT_LOG_LEVEL=INFO
|
670
1182
|
```
|
671
1183
|
|
672
1184
|
加载后,初始化时无需传参:
|
@@ -740,6 +1252,76 @@ twine upload dist/*
|
|
740
1252
|
- **并发支持**: 1000+ 并发请求
|
741
1253
|
- **连接复用**: HTTP/2 多路复用
|
742
1254
|
- **自动重试**: 指数退避,最多 5 次
|
1255
|
+
- **降级时间**: 快速降级 < 2 秒内完成
|
1256
|
+
- **熔断恢复**: 自动恢复检测,60 秒周期
|
1257
|
+
|
1258
|
+
## 🔧 故障排除
|
1259
|
+
|
1260
|
+
### 常见问题
|
1261
|
+
|
1262
|
+
#### 1. gRPC 连接失败
|
1263
|
+
```bash
|
1264
|
+
# 错误: failed to connect to all addresses
|
1265
|
+
# 解决方案: 检查服务地址和网络连接
|
1266
|
+
export MODEL_MANAGER_SERVER_ADDRESS="correct-host:port"
|
1267
|
+
```
|
1268
|
+
|
1269
|
+
#### 2. JWT 认证失败
|
1270
|
+
```bash
|
1271
|
+
# 错误: UNAUTHENTICATED
|
1272
|
+
# 解决方案: 检查 JWT 密钥或令牌
|
1273
|
+
export MODEL_MANAGER_SERVER_JWT_SECRET_KEY="your-secret-key"
|
1274
|
+
```
|
1275
|
+
|
1276
|
+
#### 3. HTTP 降级失败
|
1277
|
+
```bash
|
1278
|
+
# 错误: HTTP fallback URL not configured
|
1279
|
+
# 解决方案: 配置 HTTP 降级地址
|
1280
|
+
export MODEL_CLIENT_HTTP_FALLBACK_URL="http://backup-server:8080"
|
1281
|
+
```
|
1282
|
+
|
1283
|
+
#### 4. 依赖包缺失
|
1284
|
+
```bash
|
1285
|
+
# 错误: aiohttp library is not installed
|
1286
|
+
# 解决方案: 安装 HTTP 客户端依赖
|
1287
|
+
pip install aiohttp requests
|
1288
|
+
```
|
1289
|
+
|
1290
|
+
### 调试技巧
|
1291
|
+
|
1292
|
+
#### 启用详细日志
|
1293
|
+
```python
|
1294
|
+
import logging
|
1295
|
+
logging.basicConfig(level=logging.DEBUG)
|
1296
|
+
|
1297
|
+
# 或使用环境变量
|
1298
|
+
# MODEL_CLIENT_LOG_LEVEL=DEBUG
|
1299
|
+
```
|
1300
|
+
|
1301
|
+
#### 检查熔断器状态
|
1302
|
+
```python
|
1303
|
+
client = TamarModelClient()
|
1304
|
+
metrics = client.get_resilient_metrics()
|
1305
|
+
print(f"Circuit state: {metrics.get('circuit_state')}")
|
1306
|
+
print(f"Failure count: {metrics.get('failure_count')}")
|
1307
|
+
```
|
1308
|
+
|
1309
|
+
#### 追踪请求流程
|
1310
|
+
```python
|
1311
|
+
from tamar_model_client.core import set_origin_request_id
|
1312
|
+
set_origin_request_id("debug-trace-001")
|
1313
|
+
|
1314
|
+
# 在日志中搜索这个 ID 可以看到完整请求流程
|
1315
|
+
response = client.invoke(request)
|
1316
|
+
```
|
1317
|
+
|
1318
|
+
### 性能优化建议
|
1319
|
+
|
1320
|
+
1. **使用单例客户端**:避免频繁创建客户端实例
|
1321
|
+
2. **启用快速降级**:减少用户感知的错误延迟
|
1322
|
+
3. **合理设置超时**:根据业务需求调整超时时间
|
1323
|
+
4. **监控熔断状态**:及时发现服务问题
|
1324
|
+
5. **使用批量 API**:提高批量处理效率
|
743
1325
|
|
744
1326
|
## 🤝 支持与贡献
|
745
1327
|
|