tamar-model-client 0.1.21__py3-none-any.whl → 0.1.23__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 +41 -10
- tamar_model_client/error_handler.py +81 -59
- tamar_model_client/exceptions.py +32 -1
- tamar_model_client/sync_client.py +103 -7
- {tamar_model_client-0.1.21.dist-info → tamar_model_client-0.1.23.dist-info}/METADATA +1 -1
- {tamar_model_client-0.1.21.dist-info → tamar_model_client-0.1.23.dist-info}/RECORD +10 -9
- tests/test_google_azure_final.py +325 -63
- tests/test_logging_issue.py +75 -0
- {tamar_model_client-0.1.21.dist-info → tamar_model_client-0.1.23.dist-info}/WHEEL +0 -0
- {tamar_model_client-0.1.21.dist-info → tamar_model_client-0.1.23.dist-info}/top_level.txt +0 -0
@@ -31,7 +31,7 @@ from .core import (
|
|
31
31
|
generate_request_id,
|
32
32
|
set_request_id,
|
33
33
|
get_protected_logger,
|
34
|
-
MAX_MESSAGE_LENGTH
|
34
|
+
MAX_MESSAGE_LENGTH, get_request_id
|
35
35
|
)
|
36
36
|
from .core.base_client import BaseClient
|
37
37
|
from .core.request_builder import RequestBuilder
|
@@ -201,10 +201,17 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
201
201
|
"""
|
202
202
|
使用增强的错误处理器进行重试(同步版本)
|
203
203
|
"""
|
204
|
+
# 记录方法开始时间
|
205
|
+
method_start_time = time.time()
|
206
|
+
|
207
|
+
# 从kwargs中提取request_id(如果有的话),然后移除它
|
208
|
+
request_id = kwargs.pop('request_id', None) or get_request_id()
|
209
|
+
|
204
210
|
# 构建请求上下文
|
205
211
|
context = {
|
206
212
|
'method': func.__name__ if hasattr(func, '__name__') else 'unknown',
|
207
213
|
'client_version': 'sync',
|
214
|
+
'request_id': request_id,
|
208
215
|
}
|
209
216
|
|
210
217
|
last_exception = None
|
@@ -222,9 +229,14 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
222
229
|
should_retry = self._should_retry(e, attempt)
|
223
230
|
if not should_retry or attempt >= self.max_retries:
|
224
231
|
# 不可重试或已达到最大重试次数
|
232
|
+
current_duration = time.time() - method_start_time
|
233
|
+
context['duration'] = current_duration
|
225
234
|
last_exception = self.error_handler.handle_error(e, context)
|
226
235
|
break
|
227
236
|
|
237
|
+
# 计算当前的耗时
|
238
|
+
current_duration = time.time() - method_start_time
|
239
|
+
|
228
240
|
# 记录重试日志
|
229
241
|
log_data = {
|
230
242
|
"log_type": "info",
|
@@ -234,7 +246,8 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
234
246
|
"retry_count": attempt,
|
235
247
|
"max_retries": self.max_retries,
|
236
248
|
"method": context.get('method', 'unknown')
|
237
|
-
}
|
249
|
+
},
|
250
|
+
"duration": current_duration
|
238
251
|
}
|
239
252
|
logger.warning(
|
240
253
|
f"Attempt {attempt + 1}/{self.max_retries + 1} failed: {e.code()}",
|
@@ -246,6 +259,7 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
246
259
|
delay = self._calculate_backoff(attempt, e.code())
|
247
260
|
time.sleep(delay)
|
248
261
|
|
262
|
+
context['duration'] = current_duration
|
249
263
|
last_exception = self.error_handler.handle_error(e, context)
|
250
264
|
|
251
265
|
except Exception as e:
|
@@ -260,6 +274,73 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
260
274
|
else:
|
261
275
|
raise TamarModelException("Unknown error occurred")
|
262
276
|
|
277
|
+
def _should_retry(self, error: grpc.RpcError, attempt: int) -> bool:
|
278
|
+
"""
|
279
|
+
判断是否应该重试
|
280
|
+
|
281
|
+
Args:
|
282
|
+
error: gRPC错误
|
283
|
+
attempt: 当前重试次数
|
284
|
+
|
285
|
+
Returns:
|
286
|
+
bool: 是否应该重试
|
287
|
+
"""
|
288
|
+
error_code = error.code()
|
289
|
+
from .exceptions import get_retry_policy, ErrorContext
|
290
|
+
policy = get_retry_policy(error_code)
|
291
|
+
|
292
|
+
# 先检查错误级别的 max_attempts 配置
|
293
|
+
# max_attempts 表示最大重试次数(不包括初始请求)
|
294
|
+
error_max_attempts = policy.get('max_attempts', self.max_retries)
|
295
|
+
if attempt >= error_max_attempts:
|
296
|
+
return False
|
297
|
+
|
298
|
+
# 再检查全局的 max_retries
|
299
|
+
if attempt >= self.max_retries:
|
300
|
+
return False
|
301
|
+
|
302
|
+
retryable = policy.get('retryable', False)
|
303
|
+
|
304
|
+
if retryable == False:
|
305
|
+
return False
|
306
|
+
elif retryable == True:
|
307
|
+
return True
|
308
|
+
elif retryable == 'conditional':
|
309
|
+
# 条件重试,特殊处理
|
310
|
+
if error_code == grpc.StatusCode.CANCELLED:
|
311
|
+
# 检查是否是网络中断导致的取消
|
312
|
+
context = {'method': 'unknown', 'client_version': 'sync'}
|
313
|
+
error_context = ErrorContext(error, context)
|
314
|
+
return error_context.is_network_cancelled()
|
315
|
+
else:
|
316
|
+
return self._check_error_details_for_retry(error)
|
317
|
+
|
318
|
+
return False
|
319
|
+
|
320
|
+
def _check_error_details_for_retry(self, error: grpc.RpcError) -> bool:
|
321
|
+
"""
|
322
|
+
检查错误详情决定是否重试
|
323
|
+
|
324
|
+
Args:
|
325
|
+
error: gRPC错误
|
326
|
+
|
327
|
+
Returns:
|
328
|
+
bool: 是否应该重试
|
329
|
+
"""
|
330
|
+
error_message = error.details().lower() if error.details() else ""
|
331
|
+
|
332
|
+
# 可重试的错误模式
|
333
|
+
retryable_patterns = [
|
334
|
+
'temporary', 'timeout', 'unavailable',
|
335
|
+
'connection', 'network', 'try again'
|
336
|
+
]
|
337
|
+
|
338
|
+
for pattern in retryable_patterns:
|
339
|
+
if pattern in error_message:
|
340
|
+
return True
|
341
|
+
|
342
|
+
return False
|
343
|
+
|
263
344
|
def _calculate_backoff(self, attempt: int, error_code: grpc.StatusCode = None) -> float:
|
264
345
|
"""
|
265
346
|
计算退避时间,支持不同的退避策略
|
@@ -316,10 +397,17 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
316
397
|
Yields:
|
317
398
|
流式响应的每个元素
|
318
399
|
"""
|
400
|
+
# 记录方法开始时间
|
401
|
+
method_start_time = time.time()
|
402
|
+
|
403
|
+
# 从kwargs中提取request_id(如果有的话),然后移除它
|
404
|
+
request_id = kwargs.pop('request_id', None) or get_request_id()
|
405
|
+
|
319
406
|
last_exception = None
|
320
407
|
context = {
|
321
408
|
'method': 'stream',
|
322
409
|
'client_version': 'sync',
|
410
|
+
'request_id': request_id,
|
323
411
|
}
|
324
412
|
|
325
413
|
for attempt in range(self.max_retries + 1):
|
@@ -334,6 +422,9 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
334
422
|
# 使用智能重试判断
|
335
423
|
context['retry_count'] = attempt
|
336
424
|
|
425
|
+
# 计算当前的耗时
|
426
|
+
current_duration = time.time() - method_start_time
|
427
|
+
|
337
428
|
# 判断是否应该重试
|
338
429
|
should_retry = self._should_retry(e, attempt)
|
339
430
|
if not should_retry or attempt >= self.max_retries:
|
@@ -347,12 +438,14 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
347
438
|
"max_retries": self.max_retries,
|
348
439
|
"method": "stream",
|
349
440
|
"will_retry": False
|
350
|
-
}
|
441
|
+
},
|
442
|
+
"duration": current_duration
|
351
443
|
}
|
352
444
|
logger.error(
|
353
445
|
f"Stream failed: {e.code()} (no retry)",
|
354
446
|
extra=log_data
|
355
447
|
)
|
448
|
+
context['duration'] = current_duration
|
356
449
|
last_exception = self.error_handler.handle_error(e, context)
|
357
450
|
break
|
358
451
|
|
@@ -365,7 +458,8 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
365
458
|
"retry_count": attempt,
|
366
459
|
"max_retries": self.max_retries,
|
367
460
|
"method": "stream"
|
368
|
-
}
|
461
|
+
},
|
462
|
+
"duration": current_duration
|
369
463
|
}
|
370
464
|
logger.warning(
|
371
465
|
f"Stream attempt {attempt + 1}/{self.max_retries + 1} failed: {e.code()} (will retry)",
|
@@ -609,10 +703,11 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
609
703
|
# 对于流式响应,使用重试包装器
|
610
704
|
return self._retry_request_stream(
|
611
705
|
self._stream_with_logging,
|
612
|
-
request, metadata, invoke_timeout, start_time, model_request
|
706
|
+
request, metadata, invoke_timeout, start_time, model_request,
|
707
|
+
request_id=request_id
|
613
708
|
)
|
614
709
|
else:
|
615
|
-
result = self._retry_request(self._invoke_request, request, metadata, invoke_timeout)
|
710
|
+
result = self._retry_request(self._invoke_request, request, metadata, invoke_timeout, request_id=request_id)
|
616
711
|
|
617
712
|
# 记录非流式响应的成功日志
|
618
713
|
duration = time.time() - start_time
|
@@ -742,7 +837,8 @@ class TamarModelClient(BaseClient, HttpFallbackMixin):
|
|
742
837
|
self.stub.BatchInvoke,
|
743
838
|
batch_request,
|
744
839
|
metadata=metadata,
|
745
|
-
timeout=invoke_timeout
|
840
|
+
timeout=invoke_timeout,
|
841
|
+
request_id=request_id
|
746
842
|
)
|
747
843
|
|
748
844
|
# 构建响应对象
|
@@ -1,12 +1,12 @@
|
|
1
1
|
tamar_model_client/__init__.py,sha256=4DEIUGlLTeiaECjJQbGYik7C0JO6hHwwfbLYpYpMdzg,444
|
2
|
-
tamar_model_client/async_client.py,sha256=
|
2
|
+
tamar_model_client/async_client.py,sha256=H6IhGI415DGXoeNAd4A0anw1oL4Ss3LYdcEVeG_Co68,34416
|
3
3
|
tamar_model_client/auth.py,sha256=gbwW5Aakeb49PMbmYvrYlVx1mfyn1LEDJ4qQVs-9DA4,438
|
4
4
|
tamar_model_client/circuit_breaker.py,sha256=0XHJXBYA4O8vwsDGwqNrae9zxNJphY5Rfucc9ytVFGA,5419
|
5
|
-
tamar_model_client/error_handler.py,sha256=
|
6
|
-
tamar_model_client/exceptions.py,sha256=
|
5
|
+
tamar_model_client/error_handler.py,sha256=oI_jUTjnq4OXu8fwJoGXNmQpddEgOFF9ZUhbytq7H6c,12384
|
6
|
+
tamar_model_client/exceptions.py,sha256=D6G8igA-YO4AroeCa-9CDDPt4hSqBFX5C_4w-NCIL1w,13063
|
7
7
|
tamar_model_client/json_formatter.py,sha256=IyBv_pEEzjF-KaMF-7rxRpNc_fxRYK2A-pu_2n4Liow,1990
|
8
8
|
tamar_model_client/logging_icons.py,sha256=MRTZ1Xvkep9ce_jdltj54_XZUXvIpQ95soRNmLdJ4qw,1837
|
9
|
-
tamar_model_client/sync_client.py,sha256=
|
9
|
+
tamar_model_client/sync_client.py,sha256=RDM-ptIL0cNIie-2qpkTEFh60XTks8p2Wdz0Q5YHA1Q,36241
|
10
10
|
tamar_model_client/utils.py,sha256=Kn6pFz9GEC96H4eejEax66AkzvsrXI3WCSDtgDjnVTI,5238
|
11
11
|
tamar_model_client/core/__init__.py,sha256=bJRJllrp4Xc0g_qu1pW9G-lsXNB7c1r0NBIfb2Ypxe0,832
|
12
12
|
tamar_model_client/core/base_client.py,sha256=sYvJZsDu_66akddAMowSnihFtgOoVKaQJxxnVruF9Ms,8995
|
@@ -27,9 +27,10 @@ tamar_model_client/schemas/inputs.py,sha256=dz1m8NbUIxA99JXZc8WlyzbKpDuz1lEzx3Vg
|
|
27
27
|
tamar_model_client/schemas/outputs.py,sha256=M_fcqUtXPJnfiLabHlyA8BorlC5pYkf5KLjXO1ysKIQ,1031
|
28
28
|
tests/__init__.py,sha256=kbmImddLDwdqlkkmkyKtl4bQy_ipe-R8eskpaBylU9w,38
|
29
29
|
tests/stream_hanging_analysis.py,sha256=W3W48IhQbNAR6-xvMpoWZvnWOnr56CTaH4-aORNBuD4,14807
|
30
|
-
tests/test_google_azure_final.py,sha256=
|
30
|
+
tests/test_google_azure_final.py,sha256=9UI8G62prmnflBm4XuOxvBBshYvwCLQ1H2pqbish_DQ,26390
|
31
|
+
tests/test_logging_issue.py,sha256=JTMbotfHpAEPMBj73pOwxPn-Zn4QVQJX6scMz48FRDQ,2427
|
31
32
|
tests/test_simple.py,sha256=Xf0U-J9_xn_LzUsmYu06suK0_7DrPeko8OHoHldsNxE,7169
|
32
|
-
tamar_model_client-0.1.
|
33
|
-
tamar_model_client-0.1.
|
34
|
-
tamar_model_client-0.1.
|
35
|
-
tamar_model_client-0.1.
|
33
|
+
tamar_model_client-0.1.23.dist-info/METADATA,sha256=aW4OAMkth31oY5n04wM1QTkfH7K1TFio9nrnQML5W6g,23453
|
34
|
+
tamar_model_client-0.1.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
35
|
+
tamar_model_client-0.1.23.dist-info/top_level.txt,sha256=f1I-S8iWN-cgv4gB8gxRg9jJOTJMumvm4oGKVPfGg6A,25
|
36
|
+
tamar_model_client-0.1.23.dist-info/RECORD,,
|