axc-agent-engine 0.2.0__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.
Files changed (226) hide show
  1. axc_agent_engine/__init__.py +55 -0
  2. axc_agent_engine/agent.py +373 -0
  3. axc_agent_engine/api/__init__.py +1 -0
  4. axc_agent_engine/api/app.py +53 -0
  5. axc_agent_engine/api/deps.py +50 -0
  6. axc_agent_engine/api/routes/__init__.py +1 -0
  7. axc_agent_engine/api/routes/chat.py +356 -0
  8. axc_agent_engine/api/routes/health.py +14 -0
  9. axc_agent_engine/cli.py +126 -0
  10. axc_agent_engine/core/__init__.py +4 -0
  11. axc_agent_engine/core/constants.py +27 -0
  12. axc_agent_engine/core/context.py +174 -0
  13. axc_agent_engine/core/dispatcher.py +214 -0
  14. axc_agent_engine/core/errors.py +121 -0
  15. axc_agent_engine/core/events.py +130 -0
  16. axc_agent_engine/core/executor.py +370 -0
  17. axc_agent_engine/core/llm_caller.py +247 -0
  18. axc_agent_engine/core/message_store.py +104 -0
  19. axc_agent_engine/core/plugin_manager.py +222 -0
  20. axc_agent_engine/core/react_loop.py +149 -0
  21. axc_agent_engine/core/schema.py +167 -0
  22. axc_agent_engine/core/session.py +26 -0
  23. axc_agent_engine/core/session_manager.py +119 -0
  24. axc_agent_engine/core/stream_aggregator.py +159 -0
  25. axc_agent_engine/engine.py +296 -0
  26. axc_agent_engine/llm/__init__.py +4 -0
  27. axc_agent_engine/llm/client.py +198 -0
  28. axc_agent_engine/llm/config.py +21 -0
  29. axc_agent_engine/llm/provider.py +41 -0
  30. axc_agent_engine/llm/rate_limited.py +56 -0
  31. axc_agent_engine/llm/registry.py +56 -0
  32. axc_agent_engine/observability/__init__.py +1 -0
  33. axc_agent_engine/observability/audit.py +80 -0
  34. axc_agent_engine/observability/logging.py +53 -0
  35. axc_agent_engine/planning/__init__.py +24 -0
  36. axc_agent_engine/planning/checkpointing.py +196 -0
  37. axc_agent_engine/planning/observer.py +54 -0
  38. axc_agent_engine/planning/planner.py +83 -0
  39. axc_agent_engine/planning/planning_service.py +63 -0
  40. axc_agent_engine/planning/por_runner.py +424 -0
  41. axc_agent_engine/planning/prompts.py +21 -0
  42. axc_agent_engine/planning/replanner.py +52 -0
  43. axc_agent_engine/planning/router.py +43 -0
  44. axc_agent_engine/planning/runtime.py +22 -0
  45. axc_agent_engine/planning/scheduler.py +49 -0
  46. axc_agent_engine/planning/step_executor.py +23 -0
  47. axc_agent_engine/plugins/__init__.py +116 -0
  48. axc_agent_engine/plugins/base.py +145 -0
  49. axc_agent_engine/plugins/builtin/__init__.py +69 -0
  50. axc_agent_engine/plugins/builtin/builtin_tools/__init__.py +1 -0
  51. axc_agent_engine/plugins/builtin/builtin_tools/command_tools.py +152 -0
  52. axc_agent_engine/plugins/builtin/builtin_tools/file_tools.py +241 -0
  53. axc_agent_engine/plugins/builtin/builtin_tools/http_tools.py +113 -0
  54. axc_agent_engine/plugins/builtin/builtin_tools/path_policy.py +56 -0
  55. axc_agent_engine/plugins/builtin/builtin_tools/plugin.py +148 -0
  56. axc_agent_engine/plugins/builtin/builtin_tools/presenter.py +47 -0
  57. axc_agent_engine/plugins/builtin/builtin_tools/registry.py +30 -0
  58. axc_agent_engine/plugins/builtin/builtin_tools/result_store.py +7 -0
  59. axc_agent_engine/plugins/builtin/builtin_tools/result_tools.py +55 -0
  60. axc_agent_engine/plugins/builtin/builtin_tools/support.py +17 -0
  61. axc_agent_engine/plugins/builtin/builtin_tools/tool_definitions.py +305 -0
  62. axc_agent_engine/plugins/builtin/collaboration/__init__.py +1 -0
  63. axc_agent_engine/plugins/builtin/collaboration/plugin.py +312 -0
  64. axc_agent_engine/plugins/builtin/common.py +67 -0
  65. axc_agent_engine/plugins/builtin/compress/__init__.py +1 -0
  66. axc_agent_engine/plugins/builtin/compress/context/__init__.py +2 -0
  67. axc_agent_engine/plugins/builtin/compress/context/boundary.py +103 -0
  68. axc_agent_engine/plugins/builtin/compress/context/file_cache.py +147 -0
  69. axc_agent_engine/plugins/builtin/compress/context/models.py +34 -0
  70. axc_agent_engine/plugins/builtin/compress/context/normalizer.py +76 -0
  71. axc_agent_engine/plugins/builtin/compress/context/packer.py +53 -0
  72. axc_agent_engine/plugins/builtin/compress/context/recall.py +91 -0
  73. axc_agent_engine/plugins/builtin/compress/context/recent_window.py +42 -0
  74. axc_agent_engine/plugins/builtin/compress/context/scoring.py +35 -0
  75. axc_agent_engine/plugins/builtin/compress/context/summarizer.py +33 -0
  76. axc_agent_engine/plugins/builtin/compress/context/tool_result.py +57 -0
  77. axc_agent_engine/plugins/builtin/compress/context/tool_summary.py +87 -0
  78. axc_agent_engine/plugins/builtin/compress/plugin.py +324 -0
  79. axc_agent_engine/plugins/builtin/compress/prompts.py +12 -0
  80. axc_agent_engine/plugins/builtin/cost_statistics/__init__.py +1 -0
  81. axc_agent_engine/plugins/builtin/cost_statistics/plugin.py +131 -0
  82. axc_agent_engine/plugins/builtin/graph/__init__.py +1 -0
  83. axc_agent_engine/plugins/builtin/graph/audit.py +35 -0
  84. axc_agent_engine/plugins/builtin/graph/config.py +52 -0
  85. axc_agent_engine/plugins/builtin/graph/plugin.py +112 -0
  86. axc_agent_engine/plugins/builtin/graph/policy.py +29 -0
  87. axc_agent_engine/plugins/builtin/graph/presenter.py +38 -0
  88. axc_agent_engine/plugins/builtin/graph/service.py +200 -0
  89. axc_agent_engine/plugins/builtin/graph/source_loader.py +99 -0
  90. axc_agent_engine/plugins/builtin/graph/support.py +205 -0
  91. axc_agent_engine/plugins/builtin/graph/tool_factory.py +54 -0
  92. axc_agent_engine/plugins/builtin/graph/tool_handlers.py +164 -0
  93. axc_agent_engine/plugins/builtin/graph/utils.py +52 -0
  94. axc_agent_engine/plugins/builtin/hooks/__init__.py +1 -0
  95. axc_agent_engine/plugins/builtin/hooks/plugin.py +189 -0
  96. axc_agent_engine/plugins/builtin/human_in_the_loop/__init__.py +1 -0
  97. axc_agent_engine/plugins/builtin/human_in_the_loop/plugin.py +137 -0
  98. axc_agent_engine/plugins/builtin/knowledge/__init__.py +1 -0
  99. axc_agent_engine/plugins/builtin/knowledge/plugin.py +714 -0
  100. axc_agent_engine/plugins/builtin/knowledge/support/__init__.py +65 -0
  101. axc_agent_engine/plugins/builtin/knowledge/support/chunker.py +151 -0
  102. axc_agent_engine/plugins/builtin/knowledge/support/ingestion.py +170 -0
  103. axc_agent_engine/plugins/builtin/knowledge/support/retrieval.py +836 -0
  104. axc_agent_engine/plugins/builtin/mcp/__init__.py +1 -0
  105. axc_agent_engine/plugins/builtin/mcp/plugin.py +239 -0
  106. axc_agent_engine/plugins/builtin/mcp/support/__init__.py +4 -0
  107. axc_agent_engine/plugins/builtin/mcp/support/client.py +17 -0
  108. axc_agent_engine/plugins/builtin/mcp/support/connection.py +85 -0
  109. axc_agent_engine/plugins/builtin/mcp/support/models.py +28 -0
  110. axc_agent_engine/plugins/builtin/mcp/support/normalization.py +47 -0
  111. axc_agent_engine/plugins/builtin/mcp/support/transports/__init__.py +38 -0
  112. axc_agent_engine/plugins/builtin/mcp/support/transports/base.py +42 -0
  113. axc_agent_engine/plugins/builtin/mcp/support/transports/http.py +42 -0
  114. axc_agent_engine/plugins/builtin/mcp/support/transports/sdk.py +113 -0
  115. axc_agent_engine/plugins/builtin/mcp/support/transports/stdio.py +101 -0
  116. axc_agent_engine/plugins/builtin/memory/__init__.py +1 -0
  117. axc_agent_engine/plugins/builtin/memory/plugin.py +946 -0
  118. axc_agent_engine/plugins/builtin/memory/prompts.py +10 -0
  119. axc_agent_engine/plugins/builtin/memory/support/__init__.py +58 -0
  120. axc_agent_engine/plugins/builtin/memory/support/embedding.py +54 -0
  121. axc_agent_engine/plugins/builtin/memory/support/graph.py +147 -0
  122. axc_agent_engine/plugins/builtin/memory/support/retrieval.py +123 -0
  123. axc_agent_engine/plugins/builtin/memory/support/service.py +405 -0
  124. axc_agent_engine/plugins/builtin/output_format/__init__.py +1 -0
  125. axc_agent_engine/plugins/builtin/output_format/plugin.py +208 -0
  126. axc_agent_engine/plugins/builtin/output_format/support/__init__.py +4 -0
  127. axc_agent_engine/plugins/builtin/output_format/support/service.py +395 -0
  128. axc_agent_engine/plugins/builtin/post_process/__init__.py +1 -0
  129. axc_agent_engine/plugins/builtin/post_process/plugin.py +32 -0
  130. axc_agent_engine/plugins/builtin/reflexion/__init__.py +1 -0
  131. axc_agent_engine/plugins/builtin/reflexion/plugin.py +59 -0
  132. axc_agent_engine/plugins/builtin/reflexion/prompts.py +9 -0
  133. axc_agent_engine/plugins/builtin/repetition_guard/__init__.py +1 -0
  134. axc_agent_engine/plugins/builtin/repetition_guard/plugin.py +147 -0
  135. axc_agent_engine/plugins/builtin/risk_guard/__init__.py +1 -0
  136. axc_agent_engine/plugins/builtin/risk_guard/plugin.py +42 -0
  137. axc_agent_engine/plugins/builtin/safety/__init__.py +1 -0
  138. axc_agent_engine/plugins/builtin/safety/plugin.py +124 -0
  139. axc_agent_engine/plugins/builtin/skill/__init__.py +1 -0
  140. axc_agent_engine/plugins/builtin/skill/plugin.py +756 -0
  141. axc_agent_engine/plugins/builtin/swarm/__init__.py +1 -0
  142. axc_agent_engine/plugins/builtin/swarm/plugin.py +315 -0
  143. axc_agent_engine/plugins/builtin/tracing/__init__.py +1 -0
  144. axc_agent_engine/plugins/builtin/tracing/plugin.py +700 -0
  145. axc_agent_engine/plugins/loader.py +105 -0
  146. axc_agent_engine/plugins/registry.py +87 -0
  147. axc_agent_engine/py.typed +0 -0
  148. axc_agent_engine/runtime/__init__.py +1 -0
  149. axc_agent_engine/runtime/checkpoint.py +122 -0
  150. axc_agent_engine/runtime/concurrency.py +155 -0
  151. axc_agent_engine/runtime/input.py +28 -0
  152. axc_agent_engine/runtime/policy.py +68 -0
  153. axc_agent_engine/runtime/recovery.py +98 -0
  154. axc_agent_engine/runtime/resources.py +70 -0
  155. axc_agent_engine/runtime/risk.py +145 -0
  156. axc_agent_engine/runtime/sandbox_code.py +86 -0
  157. axc_agent_engine/runtime/sandbox_local.py +66 -0
  158. axc_agent_engine/runtime/sandbox_models.py +52 -0
  159. axc_agent_engine/runtime/sandbox_policy.py +43 -0
  160. axc_agent_engine/runtime/sandbox_provider.py +33 -0
  161. axc_agent_engine/runtime/sandbox_utils.py +50 -0
  162. axc_agent_engine/runtime/sandbox_workspace.py +64 -0
  163. axc_agent_engine/sidecar/README.md +49 -0
  164. axc_agent_engine/sidecar/__init__.py +51 -0
  165. axc_agent_engine/sidecar/agent_selector/__init__.py +127 -0
  166. axc_agent_engine/sidecar/cost_optimizer/__init__.py +168 -0
  167. axc_agent_engine/sidecar/distiller/__init__.py +144 -0
  168. axc_agent_engine/sidecar/eval/__init__.py +31 -0
  169. axc_agent_engine/sidecar/eval/judge.py +59 -0
  170. axc_agent_engine/sidecar/eval/matcher.py +145 -0
  171. axc_agent_engine/sidecar/eval/report.py +56 -0
  172. axc_agent_engine/sidecar/eval/runner.py +233 -0
  173. axc_agent_engine/sidecar/eval/store.py +126 -0
  174. axc_agent_engine/sidecar/failure_miner/__init__.py +130 -0
  175. axc_agent_engine/sidecar/multi_agent/__init__.py +16 -0
  176. axc_agent_engine/sidecar/multi_agent/events.py +14 -0
  177. axc_agent_engine/sidecar/multi_agent/modes.py +171 -0
  178. axc_agent_engine/sidecar/multi_agent/persona.py +65 -0
  179. axc_agent_engine/sidecar/multi_agent/scheduler/__init__.py +19 -0
  180. axc_agent_engine/sidecar/multi_agent/scheduler/all_parallel.py +24 -0
  181. axc_agent_engine/sidecar/multi_agent/scheduler/debate.py +42 -0
  182. axc_agent_engine/sidecar/multi_agent/scheduler/redblue.py +64 -0
  183. axc_agent_engine/sidecar/multi_agent/scheduler/round_robin.py +20 -0
  184. axc_agent_engine/sidecar/multi_agent/scheduler/supervisor.py +79 -0
  185. axc_agent_engine/sidecar/multi_agent/session.py +284 -0
  186. axc_agent_engine/sidecar/multi_agent/shared_context.py +69 -0
  187. axc_agent_engine/sidecar/multi_agent/simulation_session.py +90 -0
  188. axc_agent_engine/sidecar/multi_agent/stop_condition/__init__.py +20 -0
  189. axc_agent_engine/sidecar/multi_agent/stop_condition/causal_chain.py +17 -0
  190. axc_agent_engine/sidecar/multi_agent/stop_condition/consensus.py +16 -0
  191. axc_agent_engine/sidecar/multi_agent/stop_condition/goal_reached.py +17 -0
  192. axc_agent_engine/sidecar/multi_agent/stop_condition/llm_based.py +54 -0
  193. axc_agent_engine/sidecar/multi_agent/stop_condition/max_rounds.py +22 -0
  194. axc_agent_engine/sidecar/multi_agent/types.py +39 -0
  195. axc_agent_engine/sidecar/orchestration/__init__.py +232 -0
  196. axc_agent_engine/sidecar/simulation/__init__.py +74 -0
  197. axc_agent_engine/sidecar/simulation/action_parser.py +69 -0
  198. axc_agent_engine/sidecar/simulation/adapters.py +74 -0
  199. axc_agent_engine/sidecar/simulation/defaults.py +157 -0
  200. axc_agent_engine/sidecar/simulation/interfaces.py +70 -0
  201. axc_agent_engine/sidecar/simulation/models.py +235 -0
  202. axc_agent_engine/sidecar/simulation/modes.py +133 -0
  203. axc_agent_engine/sidecar/simulation/report.py +131 -0
  204. axc_agent_engine/sidecar/simulation/runner.py +248 -0
  205. axc_agent_engine/storage/__init__.py +12 -0
  206. axc_agent_engine/storage/in_memory.py +202 -0
  207. axc_agent_engine/storage/protocols.py +97 -0
  208. axc_agent_engine/storage/result_store.py +135 -0
  209. axc_agent_engine/tools/__init__.py +1 -0
  210. axc_agent_engine/tools/context.py +41 -0
  211. axc_agent_engine/tools/decorator.py +103 -0
  212. axc_agent_engine/tools/executor.py +234 -0
  213. axc_agent_engine/tools/name_mapping.py +71 -0
  214. axc_agent_engine/tools/orchestrator.py +334 -0
  215. axc_agent_engine/tools/registry.py +165 -0
  216. axc_agent_engine/tools/tool_output.py +113 -0
  217. axc_agent_engine/tools/utils.py +40 -0
  218. axc_agent_engine/utils/__init__.py +1 -0
  219. axc_agent_engine/utils/json_utils.py +73 -0
  220. axc_agent_engine/utils/math_utils.py +14 -0
  221. axc_agent_engine-0.2.0.dist-info/METADATA +454 -0
  222. axc_agent_engine-0.2.0.dist-info/RECORD +226 -0
  223. axc_agent_engine-0.2.0.dist-info/WHEEL +4 -0
  224. axc_agent_engine-0.2.0.dist-info/entry_points.txt +2 -0
  225. axc_agent_engine-0.2.0.dist-info/licenses/LICENSE +15 -0
  226. axc_agent_engine-0.2.0.dist-info/licenses/NOTICE +7 -0
@@ -0,0 +1,55 @@
1
+ """AxcAgentEngine — 纯 Agent 执行引擎框架。"""
2
+ from axc_agent_engine.llm.config import LLMConfig
3
+ from axc_agent_engine.runtime.concurrency import ConcurrencyConfig, ExecutionLimiter, RateLimiter, SessionExecutionGate
4
+ from axc_agent_engine.engine import Engine
5
+ from axc_agent_engine.agent import Agent
6
+ from axc_agent_engine.core.events import Event, EventType
7
+ from axc_agent_engine.core.errors import ErrorEnvelope, ErrorCategory
8
+ from axc_agent_engine.core.schema import ToolDefinition, LLMMessage, LLMUsage, LLMResponse, LLMStreamChunk, Capability
9
+ from axc_agent_engine.plugins.base import BasePlugin
10
+ from axc_agent_engine.plugins.registry import PluginRegistry
11
+ from axc_agent_engine.tools.decorator import tool
12
+ from axc_agent_engine.tools.tool_output import ToolOutput, ArtifactRef, ResultStore
13
+ from axc_agent_engine.observability.audit import AuditEvent, AuditEventType, InMemoryAuditSink
14
+ from axc_agent_engine.runtime.checkpoint import Checkpoint, CheckpointStatus, CheckpointStore, InMemoryCheckpointStore
15
+ from axc_agent_engine.runtime.recovery import ExecutionRecoveryService, RecoverableRun
16
+ from axc_agent_engine.runtime.policy import CapabilityPolicyEvaluator, PolicyDecision, PolicyEvaluator, PolicyRequest
17
+ from axc_agent_engine.runtime.input import InputProviderResult, InputProvider, PassthroughInputProvider
18
+ from axc_agent_engine.runtime.resources import (
19
+ ResourceRegistry,
20
+ ResourceError,
21
+ ResourceNotFoundError,
22
+ ResourceTypeError,
23
+ DuplicateResourceError,
24
+ )
25
+
26
+ __all__ = [
27
+ # 核心公开 API
28
+ "Engine", "LLMConfig", "Agent", "Event", "EventType",
29
+ "ConcurrencyConfig", "ExecutionLimiter", "RateLimiter", "SessionExecutionGate",
30
+ # 审计与错误
31
+ "AuditEvent", "AuditEventType", "InMemoryAuditSink", "ErrorEnvelope", "ErrorCategory",
32
+ # durable execution
33
+ "Checkpoint", "CheckpointStatus", "CheckpointStore", "InMemoryCheckpointStore",
34
+ "ExecutionRecoveryService", "RecoverableRun",
35
+ # policy
36
+ "CapabilityPolicyEvaluator", "PolicyDecision", "PolicyEvaluator", "PolicyRequest",
37
+ # LLM 响应模型
38
+ "LLMMessage", "LLMUsage", "LLMResponse", "LLMStreamChunk",
39
+ # 能力模型
40
+ "Capability",
41
+ # 工具输出
42
+ "ToolOutput", "ArtifactRef", "ResultStore",
43
+ # 输入和共享资源边界
44
+ "InputProviderResult", "InputProvider", "PassthroughInputProvider",
45
+ "ResourceRegistry", "ResourceError", "ResourceNotFoundError",
46
+ "ResourceTypeError", "DuplicateResourceError",
47
+ # 插件开发
48
+ "BasePlugin", "PluginRegistry", "ToolDefinition", "tool",
49
+ ]
50
+
51
+ try:
52
+ from importlib.metadata import version as _get_version
53
+ __version__ = _get_version("axc-agent-engine")
54
+ except Exception:
55
+ __version__ = "0.2.0"
@@ -0,0 +1,373 @@
1
+ """Agent — 对话入口。
2
+
3
+ English: User-facing chat entry point that wraps execution, sessions, tools, and plugins.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import asyncio
8
+ import logging
9
+ from dataclasses import replace
10
+ from contextlib import AsyncExitStack
11
+ from typing import Any, AsyncIterator
12
+
13
+ from axc_agent_engine.runtime.concurrency import ExecutionLimiter, SessionExecutionGate
14
+ from axc_agent_engine.core.context import ExecutionConfig, ExecutionContext, ExecutionServices
15
+ from axc_agent_engine.core.executor import Executor
16
+ from axc_agent_engine.core.llm_caller import LLMCaller
17
+ from axc_agent_engine.core.plugin_manager import PluginManager
18
+ from axc_agent_engine.core.session import Session
19
+ from axc_agent_engine.core.session_manager import SessionManager
20
+ from axc_agent_engine.core.events import Event, EventType
21
+ from axc_agent_engine.runtime.input import InputProvider, InputProviderResult, PassthroughInputProvider
22
+ from axc_agent_engine.llm.provider import LLMProvider
23
+ from axc_agent_engine.plugins.base import BasePlugin
24
+ from axc_agent_engine.plugins import agent_info_from_runtime, model_info_from_providers
25
+ from axc_agent_engine.core.schema import RuntimeConfig
26
+ from axc_agent_engine.tools.registry import ToolRegistry
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ class ExecutionContextFactory:
32
+ def __init__(self, agent: "Agent") -> None:
33
+ self.agent = agent
34
+
35
+ def create(
36
+ self,
37
+ session_id: str = "",
38
+ stream: bool = True,
39
+ llm_options: dict | None = None,
40
+ metadata: dict | None = None,
41
+ ) -> ExecutionContext:
42
+ agent = self.agent
43
+ config = ExecutionConfig(
44
+ system_prompt=agent._system_prompt,
45
+ max_rounds=agent._runtime.max_rounds,
46
+ stream=stream,
47
+ thinking=agent._runtime.thinking,
48
+ parallel_tool_calls=agent._runtime.parallel_tool_calls,
49
+ human_in_the_loop=agent._runtime.human_in_the_loop,
50
+ workspace=agent._runtime.workspace,
51
+ step_timeout=agent._runtime.step_timeout,
52
+ total_timeout=agent._runtime.total_timeout,
53
+ allowed_capabilities=frozenset(agent._runtime.allowed_capabilities),
54
+ )
55
+ services = replace(agent._services)
56
+ ctx = ExecutionContext(config=config, services=services, utility_llm=agent._utility_llm)
57
+ model_info = model_info_from_providers(agent._default_client, agent._fallback_client, agent._utility_llm)
58
+ routing_mode = agent._runtime.routing.mode if agent._runtime.routing else "auto"
59
+ agent_info = agent_info_from_runtime(
60
+ name=agent.name,
61
+ description=agent.description,
62
+ workspace=agent._runtime.workspace,
63
+ session_id=session_id,
64
+ routing_mode=routing_mode,
65
+ )
66
+ ctx.runtime.model_info = model_info
67
+ ctx.runtime.agent_info = agent_info
68
+ ctx.state.metadata["model"] = model_info.to_dict()
69
+ ctx.state.metadata["agent"] = agent_info.to_dict()
70
+ if metadata:
71
+ ctx.state.metadata.update(metadata)
72
+ try:
73
+ ctx.runtime.agent_call_depth = int(metadata.get("agent_call_depth", 0))
74
+ except (TypeError, ValueError):
75
+ ctx.runtime.agent_call_depth = 0
76
+ ctx.state.metadata["agent_name"] = agent.name
77
+ if session_id:
78
+ ctx.state.metadata["session_id"] = session_id
79
+ if llm_options:
80
+ ctx.runtime.llm_options = llm_options
81
+ return ctx
82
+
83
+
84
+ class ExecutorFactory:
85
+ def __init__(self, agent: "Agent") -> None:
86
+ self.agent = agent
87
+ self.context_factory = ExecutionContextFactory(agent)
88
+
89
+ def create(
90
+ self,
91
+ session_id: str = "",
92
+ stream: bool = True,
93
+ llm_options: dict | None = None,
94
+ metadata: dict | None = None,
95
+ ) -> Executor:
96
+ agent = self.agent
97
+ ctx = self.context_factory.create(session_id, stream, llm_options, metadata)
98
+ pm = PluginManager(agent._plugins)
99
+ llm_caller = LLMCaller(primary=agent._default_client, fallback=agent._fallback_client, plugin_manager=pm)
100
+ routing_mode = agent._runtime.routing.mode if agent._runtime.routing else "auto"
101
+ return Executor(
102
+ llm_caller=llm_caller,
103
+ registry=agent._registry,
104
+ plugin_manager=pm,
105
+ ctx=ctx,
106
+ routing_mode=routing_mode,
107
+ )
108
+
109
+
110
+ class RunCoordinator:
111
+ def __init__(self, agent: "Agent") -> None:
112
+ self.agent = agent
113
+
114
+ async def slots(self, session_id: str):
115
+ stack = AsyncExitStack()
116
+ await stack.enter_async_context(self.agent._engine_limiter.slot())
117
+ await stack.enter_async_context(self.agent._agent_limiter.slot())
118
+ await stack.enter_async_context(self.agent._session_gate.slot(session_id))
119
+ return stack
120
+
121
+
122
+ class Agent:
123
+ """Agent 实例 — 由 Engine.load_agent() 创建。
124
+
125
+ English: Runtime Agent instance created by Engine.load_agent().
126
+ """
127
+
128
+ def __init__(
129
+ self,
130
+ name: str,
131
+ description: str,
132
+ system_prompt: str,
133
+ runtime: RuntimeConfig,
134
+ plugins: list[BasePlugin],
135
+ default_client: LLMProvider,
136
+ fallback_client: LLMProvider | None,
137
+ utility_llm: LLMProvider | None = None,
138
+ session_manager: SessionManager | None = None,
139
+ registry: ToolRegistry | None = None,
140
+ result_store: "object | None" = None,
141
+ services: ExecutionServices | None = None,
142
+ input_provider: InputProvider | None = None,
143
+ engine_limiter: ExecutionLimiter | None = None,
144
+ ) -> None:
145
+ self.name = name
146
+ self.description = description
147
+ self._system_prompt = system_prompt
148
+ self._runtime = runtime
149
+ self._plugins = plugins
150
+ self._default_client = default_client
151
+ self._fallback_client = fallback_client
152
+ self._utility_llm = utility_llm
153
+ self._session_manager = session_manager or SessionManager()
154
+ self._registry = registry or ToolRegistry()
155
+ self._services = services or ExecutionServices(result_store=result_store)
156
+ self._input_provider = input_provider or PassthroughInputProvider()
157
+ queue_timeout = self._runtime.concurrency.queue_timeout
158
+ self._engine_limiter = engine_limiter or ExecutionLimiter()
159
+ self._agent_limiter = ExecutionLimiter(
160
+ self._runtime.concurrency.max_agent_concurrent_runs,
161
+ queue_timeout,
162
+ name=f"agent:{name}",
163
+ )
164
+ self._session_gate = SessionExecutionGate(
165
+ self._runtime.concurrency.max_session_concurrent_runs,
166
+ queue_timeout,
167
+ )
168
+ self._executor_factory = ExecutorFactory(self)
169
+ self._run_coordinator = RunCoordinator(self)
170
+ for plugin in plugins:
171
+ try:
172
+ tools = plugin.get_tools()
173
+ self._registry.register_many(tools)
174
+ except Exception as e:
175
+ logger.warning(f"Plugin {plugin.name} get_tools error: {e}")
176
+ self._registry.freeze()
177
+ logger.info(f"Agent '{name}' loaded: {len(plugins)} plugins, {self._registry.count} tools")
178
+
179
+ @property
180
+ def registry(self) -> ToolRegistry:
181
+ return self._registry
182
+
183
+ async def chat(
184
+ self,
185
+ message: str,
186
+ session_id: str = "",
187
+ llm_options: dict | None = None,
188
+ metadata: dict | None = None,
189
+ ) -> str:
190
+ """非流式对话。
191
+
192
+ English: Run a non-streaming chat turn and return the final assistant text.
193
+ """
194
+ return await self._execute(
195
+ user_message=message,
196
+ session_id=session_id,
197
+ stream=False,
198
+ llm_options=llm_options,
199
+ metadata=metadata,
200
+ )
201
+
202
+ async def chat_with_messages(self, messages: list[dict], session_id: str = "", llm_options: dict | None = None) -> str:
203
+ """接受结构化消息列表的非流式对话。
204
+
205
+ English: Run a non-streaming chat turn from a structured message list.
206
+ """
207
+ user_msg = self._extract_last_user_message(messages)
208
+ return await self._execute(user_message=user_msg, inject_messages=messages, session_id=session_id, stream=False, llm_options=llm_options)
209
+
210
+ async def stream(self, message: str, session_id: str = "", llm_options: dict | None = None) -> AsyncIterator[Event]:
211
+ """流式对话。
212
+
213
+ English: Stream execution events for one chat turn.
214
+ """
215
+ async for event in self._execute_stream(user_message=message, session_id=session_id, llm_options=llm_options):
216
+ yield event
217
+
218
+ async def stream_with_messages(self, messages: list[dict], session_id: str = "", llm_options: dict | None = None) -> AsyncIterator[Event]:
219
+ """接受结构化消息列表的流式对话。
220
+
221
+ English: Stream execution events from a structured message list.
222
+ """
223
+ user_msg = self._extract_last_user_message(messages)
224
+ async for event in self._execute_stream(user_message=user_msg, inject_messages=messages, session_id=session_id, llm_options=llm_options):
225
+ yield event
226
+
227
+ async def resume(self, run_id: str, message: str = "", llm_options: dict | None = None) -> str:
228
+ """非流式恢复执行级 checkpoint。"""
229
+ from axc_agent_engine.core.errors import ProviderError
230
+ result = ""
231
+ async for event in self.resume_stream(run_id, message=message, llm_options=llm_options):
232
+ if event.type == EventType.DONE:
233
+ result = event.content
234
+ elif event.type == EventType.ERROR:
235
+ raise ProviderError(event.content)
236
+ return result
237
+
238
+ async def resume_stream(self, run_id: str, message: str = "", llm_options: dict | None = None) -> AsyncIterator[Event]:
239
+ """从 CheckpointStore 中恢复 execution/round 或 POR checkpoint。"""
240
+ if not self._services.checkpoint_store:
241
+ yield Event.error("CheckpointStore is required for resume")
242
+ return
243
+ checkpoint = await self._services.checkpoint_store.latest(run_id)
244
+ if not checkpoint:
245
+ yield Event.error(f"No checkpoint found for run_id={run_id}")
246
+ return
247
+ session_id = _session_id_from_checkpoint(checkpoint)
248
+ stream = bool((llm_options or {}).get("stream", True))
249
+ async with await self._run_coordinator.slots(session_id):
250
+ executor = self._create_executor(session_id=session_id, stream=stream, llm_options=llm_options)
251
+ if checkpoint.kind == "por":
252
+ executor._ctx.state.metadata["run_id"] = run_id
253
+ async for event in executor.resume_por(run_id, message):
254
+ yield event
255
+ return
256
+ executor.restore_checkpoint(checkpoint)
257
+ async for event in executor.run_stream(message):
258
+ yield event
259
+ if session_id:
260
+ session = await self._session_manager.get_or_create(session_id)
261
+ session.messages = executor.message_store.get_all()
262
+ await self._session_manager.save(session_id)
263
+
264
+ async def get_session(self, session_id: str) -> Session | None:
265
+ return await self._session_manager.get(session_id)
266
+
267
+ async def reset_session(self, session_id: str = "") -> None:
268
+ """重置指定会话,空字符串清除所有"""
269
+ if session_id:
270
+ await self._session_manager.remove(session_id)
271
+ else:
272
+ await self._session_manager.clear()
273
+
274
+ async def close(self) -> None:
275
+ """并行释放所有插件资源,带超时。"""
276
+ async def _close_plugin(plugin: BasePlugin) -> None:
277
+ try:
278
+ await asyncio.wait_for(plugin.close(), timeout=5.0)
279
+ except asyncio.TimeoutError:
280
+ logger.warning(f"Plugin {plugin.name} close timed out")
281
+ except Exception as e:
282
+ logger.warning(f"Plugin {plugin.name} close error: {e}")
283
+ await asyncio.gather(*[_close_plugin(p) for p in self._plugins])
284
+
285
+ async def _execute(
286
+ self, user_message: str, session_id: str = "",
287
+ inject_messages: list[dict] | None = None, stream: bool = False,
288
+ llm_options: dict | None = None,
289
+ metadata: dict | None = None,
290
+ ) -> str:
291
+ """统一非流式执行"""
292
+ from axc_agent_engine.core.errors import ProviderError
293
+ result = ""
294
+ async for event in self._execute_stream(
295
+ user_message=user_message,
296
+ session_id=session_id,
297
+ inject_messages=inject_messages,
298
+ stream=stream,
299
+ llm_options=llm_options,
300
+ metadata=metadata,
301
+ ):
302
+ if event.type == EventType.DONE:
303
+ result = event.content
304
+ elif event.type == EventType.ERROR:
305
+ raise ProviderError(event.content)
306
+ return result
307
+
308
+ async def _execute_stream(
309
+ self, user_message: str, session_id: str = "",
310
+ inject_messages: list[dict] | None = None,
311
+ stream: bool = True,
312
+ llm_options: dict | None = None,
313
+ metadata: dict | None = None,
314
+ ) -> AsyncIterator[Event]:
315
+ """统一流式执行。"""
316
+ async with await self._run_coordinator.slots(session_id):
317
+ executor = self._create_executor(session_id, stream=stream, llm_options=llm_options, metadata=metadata)
318
+ raw_messages = inject_messages if inject_messages is not None else [{"role": "user", "content": user_message}]
319
+ processed = await self._process_input(raw_messages, session_id)
320
+ effective_user_message = self._extract_last_user_message(processed.messages) or user_message
321
+ if processed.artifacts:
322
+ executor._ctx.state.metadata["input_artifacts"] = processed.artifacts
323
+ if processed.metadata:
324
+ executor._ctx.state.metadata["input_metadata"] = processed.metadata
325
+ # 恢复会话上下文
326
+ if session_id:
327
+ session = await self._session_manager.get_or_create(session_id)
328
+ self._session_manager.restore_context(session, executor.message_store)
329
+ # 所有入口都走 InputProvider,因此统一注入标准 messages。
330
+ if processed.messages:
331
+ executor.message_store.extend(processed.messages)
332
+ executor.skip_user_init = True
333
+ executor._ctx.add_image_tokens(processed.messages)
334
+ async for event in executor.run_stream(effective_user_message):
335
+ yield event
336
+ # 写回会话并持久化
337
+ if session_id:
338
+ session = await self._session_manager.get_or_create(session_id)
339
+ session.messages = executor.message_store.get_all()
340
+ await self._session_manager.save(session_id)
341
+
342
+ def _create_executor(
343
+ self,
344
+ session_id: str = "",
345
+ stream: bool = True,
346
+ llm_options: dict | None = None,
347
+ metadata: dict | None = None,
348
+ ) -> "Executor":
349
+ return self._executor_factory.create(session_id, stream, llm_options, metadata)
350
+
351
+ async def _process_input(self, messages: list[dict[str, Any]], session_id: str) -> InputProviderResult:
352
+ context = {
353
+ "agent_name": self.name,
354
+ "session_id": session_id,
355
+ "workspace": self._runtime.workspace,
356
+ }
357
+ return await self._input_provider.process(messages, context)
358
+
359
+ @staticmethod
360
+ def _extract_last_user_message(messages: list[dict]) -> str:
361
+ for msg in reversed(messages):
362
+ if msg.get("role") == "user":
363
+ return msg.get("content", "")
364
+ return ""
365
+
366
+
367
+ def _session_id_from_checkpoint(checkpoint: Any) -> str:
368
+ metadata = checkpoint.state.get("metadata", {}) if isinstance(checkpoint.state, dict) else {}
369
+ if isinstance(metadata, dict) and metadata.get("session_id"):
370
+ return str(metadata["session_id"])
371
+ if checkpoint.metadata.get("session_id"):
372
+ return str(checkpoint.metadata["session_id"])
373
+ return ""
@@ -0,0 +1 @@
1
+ """REST API 模块(可选依赖:pip install axc-agent-engine[api])"""
@@ -0,0 +1,53 @@
1
+ """FastAPI 应用工厂。"""
2
+ from __future__ import annotations
3
+
4
+ import logging
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from fastapi import FastAPI
9
+ from axc_agent_engine.engine import Engine
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def create_app(engine: Engine, agents_dir: str = "") -> FastAPI:
15
+ """创建 FastAPI 应用"""
16
+ from fastapi import FastAPI
17
+ from fastapi.responses import JSONResponse
18
+ from starlette.requests import Request
19
+ from axc_agent_engine.api.routes.chat import create_chat_router
20
+ from axc_agent_engine.api.routes.health import router as health_router
21
+ from axc_agent_engine.api.deps import EngineState
22
+ from axc_agent_engine.core.errors import (
23
+ AxcError, ProviderError, TimeoutError, ConfigError,
24
+ CancelledError, SchemaError, ToolError,
25
+ )
26
+
27
+ app = FastAPI(title="AxcAgentEngine", version="0.1.0")
28
+ state = EngineState(engine=engine, agents_dir=agents_dir)
29
+ app.state.engine_state = state
30
+
31
+ _ERROR_STATUS_MAP: dict[type, int] = {
32
+ ProviderError: 502,
33
+ TimeoutError: 504,
34
+ ConfigError: 400,
35
+ CancelledError: 499,
36
+ SchemaError: 400,
37
+ ToolError: 500,
38
+ }
39
+
40
+ @app.exception_handler(AxcError)
41
+ async def axc_error_handler(request: Request, exc: AxcError) -> JSONResponse:
42
+ status_code = 500
43
+ for err_cls, code in _ERROR_STATUS_MAP.items():
44
+ if isinstance(exc, err_cls):
45
+ status_code = code
46
+ break
47
+ return JSONResponse(status_code=status_code, content={"error": type(exc).__name__, "detail": str(exc)})
48
+
49
+ app.include_router(health_router)
50
+ app.include_router(create_chat_router(state))
51
+
52
+ logger.info(f"API started, agents_dir={agents_dir}")
53
+ return app
@@ -0,0 +1,50 @@
1
+ """API 依赖:Engine 状态管理"""
2
+ import logging
3
+ import os
4
+ from dataclasses import dataclass, field
5
+ from typing import Any
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ @dataclass
11
+ class EngineState:
12
+ """API 层持有的引擎状态"""
13
+ engine: Any # Engine 实例
14
+ agents_dir: str = ""
15
+ _agents: dict[str, Any] = field(default_factory=dict) # name -> Agent 缓存
16
+
17
+ def get_agent(self, agent_name: str) -> Any:
18
+ """获取或加载 Agent(按名称缓存)"""
19
+ if agent_name in self._agents:
20
+ return self._agents[agent_name]
21
+ yaml_path = self._resolve_yaml(agent_name)
22
+ if not yaml_path:
23
+ return None
24
+ agent = self.engine.load_agent(yaml_path)
25
+ self._agents[agent_name] = agent
26
+ return agent
27
+
28
+ def list_agents(self) -> list[dict[str, str]]:
29
+ """列出可用 Agent"""
30
+ result = []
31
+ for name, agent in self._agents.items():
32
+ result.append({"name": name, "description": getattr(agent, "description", "")})
33
+ if self.agents_dir and os.path.isdir(self.agents_dir):
34
+ for f in os.listdir(self.agents_dir):
35
+ if f.endswith((".yaml", ".yml")):
36
+ name = f.rsplit(".", 1)[0]
37
+ if name not in self._agents:
38
+ result.append({"name": name, "description": ""})
39
+ return result
40
+
41
+ def _resolve_yaml(self, agent_name: str) -> str | None:
42
+ """解析 Agent 名称为 YAML 路径"""
43
+ if os.path.exists(agent_name):
44
+ return agent_name
45
+ if self.agents_dir:
46
+ for ext in (".yaml", ".yml"):
47
+ path = os.path.join(self.agents_dir, agent_name + ext)
48
+ if os.path.exists(path):
49
+ return path
50
+ return None
@@ -0,0 +1 @@
1
+ """API 路由"""