wagent-framework 1.4.4__tar.gz → 1.5.2__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.
Files changed (74) hide show
  1. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/PKG-INFO +16 -3
  2. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/README.md +15 -2
  3. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/setup.py +1 -1
  4. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/__init__.py +10 -3
  5. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/__main__.py +1 -1
  6. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/aop/aspects.py +161 -161
  7. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/cli.py +108 -108
  8. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/container/bean_factory.py +7 -2
  9. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/core/agent.py +7 -7
  10. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/core/decorators.py +138 -138
  11. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/core/doctor.py +3 -1
  12. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/core/event_bus.py +6 -3
  13. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/exceptions/framework_errors.py +8 -0
  14. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/lifecycle/manager.py +8 -8
  15. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/observability/logging.py +74 -74
  16. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/observability/metrics.py +98 -98
  17. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/observability/tracing.py +58 -58
  18. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/scanner/parallel_scanner.py +21 -18
  19. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/skills/skill.py +188 -187
  20. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/tools/langchain_adapter.py +12 -2
  21. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/wagent_framework.egg-info/PKG-INFO +16 -3
  22. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/LICENSE +0 -0
  23. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/setup.cfg +0 -0
  24. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test.py +0 -0
  25. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test_aop.py +0 -0
  26. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test_config.py +0 -0
  27. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test_container.py +0 -0
  28. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test_distributed_lock.py +0 -0
  29. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test_event_bus.py +0 -0
  30. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test_lifecycle.py +0 -0
  31. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test_sandbox.py +0 -0
  32. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/tests/test_scanner.py +0 -0
  33. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/aop/__init__.py +0 -0
  34. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/aop/joinpoint.py +0 -0
  35. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/aop/pointcut.py +0 -0
  36. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/aop/proxy_factory.py +0 -0
  37. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/config/__init__.py +0 -0
  38. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/config/dynamic_config.py +0 -0
  39. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/container/__init__.py +0 -0
  40. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/container/reflection_cache.py +0 -0
  41. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/core/__init__.py +0 -0
  42. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/deployment/__init__.py +0 -0
  43. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/deployment/fastapi_depends.py +0 -0
  44. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/distributed/__init__.py +0 -0
  45. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/distributed/lock.py +0 -0
  46. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/distributed/lock_pool.py +0 -0
  47. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/exceptions/__init__.py +0 -0
  48. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/lifecycle/__init__.py +0 -0
  49. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/lifecycle/graceful_shutdown.py +0 -0
  50. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/lifecycle/order.py +0 -0
  51. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/migration/__init__.py +0 -0
  52. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/migration/migrate_previous.py +0 -0
  53. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/observability/__init__.py +0 -0
  54. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/observability/health.py +0 -0
  55. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/resilience/__init__.py +0 -0
  56. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/resilience/bulkhead.py +0 -0
  57. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/resilience/timeout.py +0 -0
  58. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/scanner/__init__.py +0 -0
  59. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/scanner/cache.py +0 -0
  60. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/security/__init__.py +0 -0
  61. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/security/mcp_auth.py +0 -0
  62. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/skills/__init__.py +0 -0
  63. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/skills/sandbox/__init__.py +0 -0
  64. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/skills/sandbox/nsjail_sandbox.py +0 -0
  65. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/skills/sandbox/wasm_sandbox.py +0 -0
  66. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/skills/signature.py +0 -0
  67. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/testing/__init__.py +0 -0
  68. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/testing/mock_utils.py +0 -0
  69. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/w_agent/tools/__init__.py +0 -0
  70. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/wagent_framework.egg-info/SOURCES.txt +0 -0
  71. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/wagent_framework.egg-info/dependency_links.txt +0 -0
  72. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/wagent_framework.egg-info/entry_points.txt +0 -0
  73. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/wagent_framework.egg-info/requires.txt +0 -0
  74. {wagent_framework-1.4.4 → wagent_framework-1.5.2}/wagent_framework.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wagent-framework
3
- Version: 1.4.4
3
+ Version: 1.5.2
4
4
  Summary: Python enterprise agent framework with AOP, IOC, and sandbox security
5
5
  Home-page: https://github.com/LuckyStar2456/W-Agent-FrameWork
6
6
  Author: LuckyStar2456
@@ -58,7 +58,7 @@ Dynamic: requires-dist
58
58
  Dynamic: requires-python
59
59
  Dynamic: summary
60
60
 
61
- # W-Agent v1.4.4
61
+ # W-Agent v1.5.0
62
62
 
63
63
  [English](./README_EN.md) | 简体中文
64
64
 
@@ -670,12 +670,25 @@ export W_AGENT_RESILIENCE_RETRY_MAX_ATTEMPTS=5
670
670
  ## 📦 PyPI 包
671
671
 
672
672
  - **包名**: `wagent-framework`
673
- - **版本**: 1.4.4
673
+ - **版本**: 1.5.1
674
674
  - **安装**: `pip install wagent-framework`
675
675
  - **PyPI 地址**: [https://pypi.org/project/wagent-framework/](https://pypi.org/project/wagent-framework/)
676
676
 
677
677
  ## 📝 更新日志
678
678
 
679
+ ### v1.5.1 (2026-04-27)
680
+ - **版本更新**: 版本号更新至 1.5.1
681
+ - **文档更新**: 完善了更新日志内容
682
+ - **Bug 修复**: 修复了 LangChain 适配器在未安装 LangChain 时的导入错误
683
+ - **Bug 修复**: 修复了生命周期方法绑定问题
684
+
685
+ ### v1.5.0 (2026-04-27)
686
+ - **版本更新**: 版本号更新至 1.5.0
687
+ - **Bug 修复**: 修复了 LangChain 适配器在未安装 LangChain 时的导入错误
688
+ - **Bug 修复**: 修复了生命周期方法绑定问题
689
+ - **文档更新**: 统一更新所有文档的导入路径
690
+ - **文档修复**: 修复了 chat-agent 文档中的 Python 版本要求
691
+
679
692
  ### v1.4.4 (2026-04-26)
680
693
  - **框架优化**: 完善了核心功能的稳定性和性能
681
694
  - **代码质量**: 提升了代码的可维护性和可读性
@@ -1,4 +1,4 @@
1
- # W-Agent v1.4.4
1
+ # W-Agent v1.5.0
2
2
 
3
3
  [English](./README_EN.md) | 简体中文
4
4
 
@@ -610,12 +610,25 @@ export W_AGENT_RESILIENCE_RETRY_MAX_ATTEMPTS=5
610
610
  ## 📦 PyPI 包
611
611
 
612
612
  - **包名**: `wagent-framework`
613
- - **版本**: 1.4.4
613
+ - **版本**: 1.5.1
614
614
  - **安装**: `pip install wagent-framework`
615
615
  - **PyPI 地址**: [https://pypi.org/project/wagent-framework/](https://pypi.org/project/wagent-framework/)
616
616
 
617
617
  ## 📝 更新日志
618
618
 
619
+ ### v1.5.1 (2026-04-27)
620
+ - **版本更新**: 版本号更新至 1.5.1
621
+ - **文档更新**: 完善了更新日志内容
622
+ - **Bug 修复**: 修复了 LangChain 适配器在未安装 LangChain 时的导入错误
623
+ - **Bug 修复**: 修复了生命周期方法绑定问题
624
+
625
+ ### v1.5.0 (2026-04-27)
626
+ - **版本更新**: 版本号更新至 1.5.0
627
+ - **Bug 修复**: 修复了 LangChain 适配器在未安装 LangChain 时的导入错误
628
+ - **Bug 修复**: 修复了生命周期方法绑定问题
629
+ - **文档更新**: 统一更新所有文档的导入路径
630
+ - **文档修复**: 修复了 chat-agent 文档中的 Python 版本要求
631
+
619
632
  ### v1.4.4 (2026-04-26)
620
633
  - **框架优化**: 完善了核心功能的稳定性和性能
621
634
  - **代码质量**: 提升了代码的可维护性和可读性
@@ -8,7 +8,7 @@ with open(os.path.join(here, 'README.md'), 'r', encoding='utf-8') as f:
8
8
 
9
9
  setup(
10
10
  name="wagent-framework",
11
- version="1.4.4",
11
+ version="1.5.2",
12
12
  description="Python enterprise agent framework with AOP, IOC, and sandbox security",
13
13
  long_description=long_description,
14
14
  long_description_content_type="text/markdown",
@@ -1,6 +1,6 @@
1
1
  """W-Agent 框架包"""
2
2
 
3
- __version__ = "1.4.4"
3
+ __version__ = "1.5.1"
4
4
  __author__ = "LuckyStar2456"
5
5
  __license__ = "MIT"
6
6
 
@@ -10,7 +10,7 @@ from w_agent.container.bean_factory import BeanFactory, BeanDefinition, Scope
10
10
  from w_agent.config.dynamic_config import DynamicConfigManager
11
11
  from w_agent.core.decorators import (
12
12
  AgentComponent, ServiceComponent, ToolComponent,
13
- RepositoryComponent, ControllerComponent,
13
+ RepositoryComponent, ControllerComponent, Component,
14
14
  PostConstruct, PreDestroy, Autowired, Qualifier,
15
15
  Retry, CircuitBreaker
16
16
  )
@@ -31,6 +31,9 @@ from w_agent.skills.sandbox.wasm_sandbox import WasmSkillSandbox
31
31
  from w_agent.skills.sandbox.nsjail_sandbox import NsJailSkillSandbox
32
32
  from w_agent.skills.skill import Skill
33
33
  from w_agent.exceptions.framework_errors import BeanNotFoundError, InjectionError
34
+ from w_agent.scanner.parallel_scanner import ParallelASTScanner
35
+ from w_agent.lifecycle.graceful_shutdown import GracefulShutdownManager
36
+ from w_agent.tools.langchain_adapter import LangChainToolAdapter
34
37
 
35
38
  __all__ = [
36
39
  "BaseAgent",
@@ -43,6 +46,7 @@ __all__ = [
43
46
  "ToolComponent",
44
47
  "RepositoryComponent",
45
48
  "ControllerComponent",
49
+ "Component",
46
50
  "PostConstruct",
47
51
  "PreDestroy",
48
52
  "Autowired",
@@ -55,6 +59,7 @@ __all__ = [
55
59
  "Doctor",
56
60
  "LifecycleManager",
57
61
  "LifecycleOrder",
62
+ "GracefulShutdownManager",
58
63
  "ResilienceManager",
59
64
  "AspectJPointcut",
60
65
  "BeforeAdvice",
@@ -74,5 +79,7 @@ __all__ = [
74
79
  "NsJailSkillSandbox",
75
80
  "Skill",
76
81
  "BeanNotFoundError",
77
- "InjectionError"
82
+ "InjectionError",
83
+ "ParallelASTScanner",
84
+ "LangChainToolAdapter"
78
85
  ]
@@ -34,7 +34,7 @@ async def main():
34
34
  result = await agent.arun(prompt)
35
35
  print(result)
36
36
  else:
37
- print("W-Agent v1.4.4")
37
+ print("W-Agent v1.5.0")
38
38
  print("Usage: python -m w_agent <prompt>")
39
39
 
40
40
  if __name__ == "__main__":
@@ -1,162 +1,162 @@
1
- """AOP切面实现"""
2
-
3
- from typing import Callable, Any
4
- import asyncio
5
- import time
6
- from w_agent.aop.pointcut import AroundAdvice
7
- from w_agent.core.event_bus import EventBus, Event
8
- from w_agent.observability.tracing import global_tracer
9
-
10
- class RetryAspect:
11
- """重试切面"""
12
- def __init__(self, event_bus: EventBus = None):
13
- self.event_bus = event_bus
14
-
15
- def create_advice(self, func: Callable) -> AroundAdvice:
16
- """创建重试通知"""
17
- max_attempts = getattr(func, "__retry_max_attempts__", 3)
18
- delay = getattr(func, "__retry_delay__", 0.1)
19
- backoff = getattr(func, "__retry_backoff__", 2.0)
20
- retry_exceptions = getattr(func, "__retry_exceptions__", (Exception,))
21
-
22
- async def advice(joinpoint, proceed):
23
- span = global_tracer.start_span("aop.retry", attributes={"function": f"{func.__module__}.{func.__name__}", "max_attempts": max_attempts})
24
- attempts = 0
25
- current_delay = delay
26
- fallback_method = getattr(func, "__retry_fallback__", None)
27
-
28
- try:
29
- while attempts < max_attempts:
30
- try:
31
- if self.event_bus:
32
- await self.event_bus.emit(Event(
33
- name="retry.attempt",
34
- payload={"attempt": attempts + 1, "max_attempts": max_attempts}
35
- ))
36
-
37
- sub_span = global_tracer.start_span("aop.retry.attempt", attributes={"attempt": attempts + 1})
38
- result = await proceed()
39
- global_tracer.end_span(sub_span)
40
- return result
41
- except retry_exceptions as e:
42
- attempts += 1
43
- if attempts >= max_attempts:
44
- if self.event_bus:
45
- await self.event_bus.emit(Event(
46
- name="retry.failed",
47
- payload={"attempts": attempts, "error": str(e)}
48
- ))
49
- # 尝试调用fallback方法
50
- if fallback_method and hasattr(joinpoint.target, fallback_method):
51
- try:
52
- fallback = getattr(joinpoint.target, fallback_method)
53
- return fallback(*joinpoint.args, **joinpoint.kwargs)
54
- except Exception as fallback_error:
55
- raise Exception(f"Retry failed after {max_attempts} attempts, and fallback method {fallback_method} also failed: {str(fallback_error)}") from e
56
- raise Exception(f"Retry failed after {max_attempts} attempts: {str(e)}") from e
57
-
58
- if self.event_bus:
59
- await self.event_bus.emit(Event(
60
- name="retry.retrying",
61
- payload={"attempt": attempts, "delay": current_delay, "error": str(e)}
62
- ))
63
-
64
- await asyncio.sleep(current_delay)
65
- current_delay *= backoff
66
- finally:
67
- global_tracer.end_span(span)
68
-
69
- return AroundAdvice(advice)
70
-
71
- class CircuitBreakerAspect:
72
- """断路器切面"""
73
- def __init__(self, event_bus: EventBus = None):
74
- self.event_bus = event_bus
75
- self._state = {}
76
-
77
- def create_advice(self, func: Callable) -> AroundAdvice:
78
- """创建断路器通知"""
79
- failure_threshold = getattr(func, "__circuit_breaker_failure_threshold__", 5)
80
- recovery_timeout = getattr(func, "__circuit_breaker_recovery_timeout__", 30.0)
81
- fallback_method = getattr(func, "__circuit_breaker_fallback_method__", None)
82
-
83
- func_key = f"{func.__module__}.{func.__name__}"
84
-
85
- # 初始化状态
86
- if func_key not in self._state:
87
- self._state[func_key] = {
88
- "state": "CLOSED", # CLOSED, OPEN, HALF_OPEN
89
- "failure_count": 0,
90
- "last_failure_time": 0,
91
- "consecutive_successes": 0
92
- }
93
-
94
- async def advice(joinpoint, proceed):
95
- span = global_tracer.start_span("aop.circuit_breaker", attributes={"function": func_key, "state": self._state[func_key]["state"]})
96
- state = self._state[func_key]
97
-
98
- try:
99
- # 检查是否应该从OPEN状态切换到HALF_OPEN
100
- if state["state"] == "OPEN":
101
- if time.time() - state["last_failure_time"] > recovery_timeout:
102
- state["state"] = "HALF_OPEN"
103
- if self.event_bus:
104
- await self.event_bus.emit(Event(
105
- name="circuit_breaker.half_open",
106
- payload={"function": func_key}
107
- ))
108
- else:
109
- # 快速失败
110
- if self.event_bus:
111
- await self.event_bus.emit(Event(
112
- name="circuit_breaker.open",
113
- payload={"function": func_key}
114
- ))
115
- if fallback_method and hasattr(joinpoint.target, fallback_method):
116
- try:
117
- fallback = getattr(joinpoint.target, fallback_method)
118
- return fallback(*joinpoint.args, **joinpoint.kwargs)
119
- except Exception as fallback_error:
120
- raise Exception(f"Circuit breaker is open, and fallback method {fallback_method} also failed: {str(fallback_error)}")
121
- raise Exception(f"Circuit breaker is open for {func_key}")
122
-
123
- try:
124
- sub_span = global_tracer.start_span("aop.circuit_breaker.execute")
125
- result = await proceed()
126
- global_tracer.end_span(sub_span)
127
-
128
- # 处理成功
129
- if state["state"] == "HALF_OPEN":
130
- state["consecutive_successes"] += 1
131
- if state["consecutive_successes"] >= 3: # 连续成功3次后关闭
132
- state["state"] = "CLOSED"
133
- state["failure_count"] = 0
134
- state["consecutive_successes"] = 0
135
- if self.event_bus:
136
- await self.event_bus.emit(Event(
137
- name="circuit_breaker.closed",
138
- payload={"function": func_key}
139
- ))
140
- else:
141
- state["failure_count"] = 0
142
-
143
- return result
144
- except Exception as e:
145
- # 处理失败
146
- state["failure_count"] += 1
147
- state["last_failure_time"] = time.time()
148
- state["consecutive_successes"] = 0
149
-
150
- if state["state"] == "CLOSED" and state["failure_count"] >= failure_threshold:
151
- state["state"] = "OPEN"
152
- if self.event_bus:
153
- await self.event_bus.emit(Event(
154
- name="circuit_breaker.opened",
155
- payload={"function": func_key, "failure_count": state["failure_count"]}
156
- ))
157
-
158
- raise
159
- finally:
160
- global_tracer.end_span(span)
161
-
1
+ """AOP切面实现"""
2
+
3
+ from typing import Callable, Any
4
+ import asyncio
5
+ import time
6
+ from w_agent.aop.pointcut import AroundAdvice
7
+ from w_agent.core.event_bus import EventBus, Event
8
+ from w_agent.observability.tracing import global_tracer
9
+
10
+ class RetryAspect:
11
+ """重试切面"""
12
+ def __init__(self, event_bus: EventBus = None):
13
+ self.event_bus = event_bus
14
+
15
+ def create_advice(self, func: Callable) -> AroundAdvice:
16
+ """创建重试通知"""
17
+ max_attempts = getattr(func, "__retry_max_attempts__", 3)
18
+ delay = getattr(func, "__retry_delay__", 0.1)
19
+ backoff = getattr(func, "__retry_backoff__", 2.0)
20
+ retry_exceptions = getattr(func, "__retry_exceptions__", (Exception,))
21
+
22
+ async def advice(joinpoint, proceed):
23
+ span = global_tracer.start_span("aop.retry", attributes={"function": f"{func.__module__}.{func.__name__}", "max_attempts": max_attempts})
24
+ attempts = 0
25
+ current_delay = delay
26
+ fallback_method = getattr(func, "__retry_fallback__", None)
27
+
28
+ try:
29
+ while attempts < max_attempts:
30
+ try:
31
+ if self.event_bus:
32
+ await self.event_bus.emit(Event(
33
+ name="retry.attempt",
34
+ payload={"attempt": attempts + 1, "max_attempts": max_attempts}
35
+ ))
36
+
37
+ sub_span = global_tracer.start_span("aop.retry.attempt", attributes={"attempt": attempts + 1})
38
+ result = await proceed()
39
+ global_tracer.end_span(sub_span)
40
+ return result
41
+ except retry_exceptions as e:
42
+ attempts += 1
43
+ if attempts >= max_attempts:
44
+ if self.event_bus:
45
+ await self.event_bus.emit(Event(
46
+ name="retry.failed",
47
+ payload={"attempts": attempts, "error": str(e)}
48
+ ))
49
+ # 尝试调用fallback方法
50
+ if fallback_method and hasattr(joinpoint.target, fallback_method):
51
+ try:
52
+ fallback = getattr(joinpoint.target, fallback_method)
53
+ return fallback(*joinpoint.args, **joinpoint.kwargs)
54
+ except Exception as fallback_error:
55
+ raise Exception(f"Retry failed after {max_attempts} attempts, and fallback method {fallback_method} also failed: {str(fallback_error)}") from e
56
+ raise Exception(f"Retry failed after {max_attempts} attempts: {str(e)}") from e
57
+
58
+ if self.event_bus:
59
+ await self.event_bus.emit(Event(
60
+ name="retry.retrying",
61
+ payload={"attempt": attempts, "delay": current_delay, "error": str(e)}
62
+ ))
63
+
64
+ await asyncio.sleep(current_delay)
65
+ current_delay *= backoff
66
+ finally:
67
+ global_tracer.end_span(span)
68
+
69
+ return AroundAdvice(advice)
70
+
71
+ class CircuitBreakerAspect:
72
+ """断路器切面"""
73
+ def __init__(self, event_bus: EventBus = None):
74
+ self.event_bus = event_bus
75
+ self._state = {}
76
+
77
+ def create_advice(self, func: Callable) -> AroundAdvice:
78
+ """创建断路器通知"""
79
+ failure_threshold = getattr(func, "__circuit_breaker_failure_threshold__", 5)
80
+ recovery_timeout = getattr(func, "__circuit_breaker_recovery_timeout__", 30.0)
81
+ fallback_method = getattr(func, "__circuit_breaker_fallback_method__", None)
82
+
83
+ func_key = f"{func.__module__}.{func.__name__}"
84
+
85
+ # 初始化状态
86
+ if func_key not in self._state:
87
+ self._state[func_key] = {
88
+ "state": "CLOSED", # CLOSED, OPEN, HALF_OPEN
89
+ "failure_count": 0,
90
+ "last_failure_time": 0,
91
+ "consecutive_successes": 0
92
+ }
93
+
94
+ async def advice(joinpoint, proceed):
95
+ span = global_tracer.start_span("aop.circuit_breaker", attributes={"function": func_key, "state": self._state[func_key]["state"]})
96
+ state = self._state[func_key]
97
+
98
+ try:
99
+ # 检查是否应该从OPEN状态切换到HALF_OPEN
100
+ if state["state"] == "OPEN":
101
+ if time.time() - state["last_failure_time"] > recovery_timeout:
102
+ state["state"] = "HALF_OPEN"
103
+ if self.event_bus:
104
+ await self.event_bus.emit(Event(
105
+ name="circuit_breaker.half_open",
106
+ payload={"function": func_key}
107
+ ))
108
+ else:
109
+ # 快速失败
110
+ if self.event_bus:
111
+ await self.event_bus.emit(Event(
112
+ name="circuit_breaker.open",
113
+ payload={"function": func_key}
114
+ ))
115
+ if fallback_method and hasattr(joinpoint.target, fallback_method):
116
+ try:
117
+ fallback = getattr(joinpoint.target, fallback_method)
118
+ return fallback(*joinpoint.args, **joinpoint.kwargs)
119
+ except Exception as fallback_error:
120
+ raise Exception(f"Circuit breaker is open, and fallback method {fallback_method} also failed: {str(fallback_error)}")
121
+ raise Exception(f"Circuit breaker is open for {func_key}")
122
+
123
+ try:
124
+ sub_span = global_tracer.start_span("aop.circuit_breaker.execute")
125
+ result = await proceed()
126
+ global_tracer.end_span(sub_span)
127
+
128
+ # 处理成功
129
+ if state["state"] == "HALF_OPEN":
130
+ state["consecutive_successes"] += 1
131
+ if state["consecutive_successes"] >= 3: # 连续成功3次后关闭
132
+ state["state"] = "CLOSED"
133
+ state["failure_count"] = 0
134
+ state["consecutive_successes"] = 0
135
+ if self.event_bus:
136
+ await self.event_bus.emit(Event(
137
+ name="circuit_breaker.closed",
138
+ payload={"function": func_key}
139
+ ))
140
+ else:
141
+ state["failure_count"] = 0
142
+
143
+ return result
144
+ except Exception as e:
145
+ # 处理失败
146
+ state["failure_count"] += 1
147
+ state["last_failure_time"] = time.time()
148
+ state["consecutive_successes"] = 0
149
+
150
+ if state["state"] == "CLOSED" and state["failure_count"] >= failure_threshold:
151
+ state["state"] = "OPEN"
152
+ if self.event_bus:
153
+ await self.event_bus.emit(Event(
154
+ name="circuit_breaker.opened",
155
+ payload={"function": func_key, "failure_count": state["failure_count"]}
156
+ ))
157
+
158
+ raise
159
+ finally:
160
+ global_tracer.end_span(span)
161
+
162
162
  return AroundAdvice(advice)