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.
- axc_agent_engine/__init__.py +55 -0
- axc_agent_engine/agent.py +373 -0
- axc_agent_engine/api/__init__.py +1 -0
- axc_agent_engine/api/app.py +53 -0
- axc_agent_engine/api/deps.py +50 -0
- axc_agent_engine/api/routes/__init__.py +1 -0
- axc_agent_engine/api/routes/chat.py +356 -0
- axc_agent_engine/api/routes/health.py +14 -0
- axc_agent_engine/cli.py +126 -0
- axc_agent_engine/core/__init__.py +4 -0
- axc_agent_engine/core/constants.py +27 -0
- axc_agent_engine/core/context.py +174 -0
- axc_agent_engine/core/dispatcher.py +214 -0
- axc_agent_engine/core/errors.py +121 -0
- axc_agent_engine/core/events.py +130 -0
- axc_agent_engine/core/executor.py +370 -0
- axc_agent_engine/core/llm_caller.py +247 -0
- axc_agent_engine/core/message_store.py +104 -0
- axc_agent_engine/core/plugin_manager.py +222 -0
- axc_agent_engine/core/react_loop.py +149 -0
- axc_agent_engine/core/schema.py +167 -0
- axc_agent_engine/core/session.py +26 -0
- axc_agent_engine/core/session_manager.py +119 -0
- axc_agent_engine/core/stream_aggregator.py +159 -0
- axc_agent_engine/engine.py +296 -0
- axc_agent_engine/llm/__init__.py +4 -0
- axc_agent_engine/llm/client.py +198 -0
- axc_agent_engine/llm/config.py +21 -0
- axc_agent_engine/llm/provider.py +41 -0
- axc_agent_engine/llm/rate_limited.py +56 -0
- axc_agent_engine/llm/registry.py +56 -0
- axc_agent_engine/observability/__init__.py +1 -0
- axc_agent_engine/observability/audit.py +80 -0
- axc_agent_engine/observability/logging.py +53 -0
- axc_agent_engine/planning/__init__.py +24 -0
- axc_agent_engine/planning/checkpointing.py +196 -0
- axc_agent_engine/planning/observer.py +54 -0
- axc_agent_engine/planning/planner.py +83 -0
- axc_agent_engine/planning/planning_service.py +63 -0
- axc_agent_engine/planning/por_runner.py +424 -0
- axc_agent_engine/planning/prompts.py +21 -0
- axc_agent_engine/planning/replanner.py +52 -0
- axc_agent_engine/planning/router.py +43 -0
- axc_agent_engine/planning/runtime.py +22 -0
- axc_agent_engine/planning/scheduler.py +49 -0
- axc_agent_engine/planning/step_executor.py +23 -0
- axc_agent_engine/plugins/__init__.py +116 -0
- axc_agent_engine/plugins/base.py +145 -0
- axc_agent_engine/plugins/builtin/__init__.py +69 -0
- axc_agent_engine/plugins/builtin/builtin_tools/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/builtin_tools/command_tools.py +152 -0
- axc_agent_engine/plugins/builtin/builtin_tools/file_tools.py +241 -0
- axc_agent_engine/plugins/builtin/builtin_tools/http_tools.py +113 -0
- axc_agent_engine/plugins/builtin/builtin_tools/path_policy.py +56 -0
- axc_agent_engine/plugins/builtin/builtin_tools/plugin.py +148 -0
- axc_agent_engine/plugins/builtin/builtin_tools/presenter.py +47 -0
- axc_agent_engine/plugins/builtin/builtin_tools/registry.py +30 -0
- axc_agent_engine/plugins/builtin/builtin_tools/result_store.py +7 -0
- axc_agent_engine/plugins/builtin/builtin_tools/result_tools.py +55 -0
- axc_agent_engine/plugins/builtin/builtin_tools/support.py +17 -0
- axc_agent_engine/plugins/builtin/builtin_tools/tool_definitions.py +305 -0
- axc_agent_engine/plugins/builtin/collaboration/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/collaboration/plugin.py +312 -0
- axc_agent_engine/plugins/builtin/common.py +67 -0
- axc_agent_engine/plugins/builtin/compress/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/compress/context/__init__.py +2 -0
- axc_agent_engine/plugins/builtin/compress/context/boundary.py +103 -0
- axc_agent_engine/plugins/builtin/compress/context/file_cache.py +147 -0
- axc_agent_engine/plugins/builtin/compress/context/models.py +34 -0
- axc_agent_engine/plugins/builtin/compress/context/normalizer.py +76 -0
- axc_agent_engine/plugins/builtin/compress/context/packer.py +53 -0
- axc_agent_engine/plugins/builtin/compress/context/recall.py +91 -0
- axc_agent_engine/plugins/builtin/compress/context/recent_window.py +42 -0
- axc_agent_engine/plugins/builtin/compress/context/scoring.py +35 -0
- axc_agent_engine/plugins/builtin/compress/context/summarizer.py +33 -0
- axc_agent_engine/plugins/builtin/compress/context/tool_result.py +57 -0
- axc_agent_engine/plugins/builtin/compress/context/tool_summary.py +87 -0
- axc_agent_engine/plugins/builtin/compress/plugin.py +324 -0
- axc_agent_engine/plugins/builtin/compress/prompts.py +12 -0
- axc_agent_engine/plugins/builtin/cost_statistics/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/cost_statistics/plugin.py +131 -0
- axc_agent_engine/plugins/builtin/graph/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/graph/audit.py +35 -0
- axc_agent_engine/plugins/builtin/graph/config.py +52 -0
- axc_agent_engine/plugins/builtin/graph/plugin.py +112 -0
- axc_agent_engine/plugins/builtin/graph/policy.py +29 -0
- axc_agent_engine/plugins/builtin/graph/presenter.py +38 -0
- axc_agent_engine/plugins/builtin/graph/service.py +200 -0
- axc_agent_engine/plugins/builtin/graph/source_loader.py +99 -0
- axc_agent_engine/plugins/builtin/graph/support.py +205 -0
- axc_agent_engine/plugins/builtin/graph/tool_factory.py +54 -0
- axc_agent_engine/plugins/builtin/graph/tool_handlers.py +164 -0
- axc_agent_engine/plugins/builtin/graph/utils.py +52 -0
- axc_agent_engine/plugins/builtin/hooks/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/hooks/plugin.py +189 -0
- axc_agent_engine/plugins/builtin/human_in_the_loop/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/human_in_the_loop/plugin.py +137 -0
- axc_agent_engine/plugins/builtin/knowledge/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/knowledge/plugin.py +714 -0
- axc_agent_engine/plugins/builtin/knowledge/support/__init__.py +65 -0
- axc_agent_engine/plugins/builtin/knowledge/support/chunker.py +151 -0
- axc_agent_engine/plugins/builtin/knowledge/support/ingestion.py +170 -0
- axc_agent_engine/plugins/builtin/knowledge/support/retrieval.py +836 -0
- axc_agent_engine/plugins/builtin/mcp/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/mcp/plugin.py +239 -0
- axc_agent_engine/plugins/builtin/mcp/support/__init__.py +4 -0
- axc_agent_engine/plugins/builtin/mcp/support/client.py +17 -0
- axc_agent_engine/plugins/builtin/mcp/support/connection.py +85 -0
- axc_agent_engine/plugins/builtin/mcp/support/models.py +28 -0
- axc_agent_engine/plugins/builtin/mcp/support/normalization.py +47 -0
- axc_agent_engine/plugins/builtin/mcp/support/transports/__init__.py +38 -0
- axc_agent_engine/plugins/builtin/mcp/support/transports/base.py +42 -0
- axc_agent_engine/plugins/builtin/mcp/support/transports/http.py +42 -0
- axc_agent_engine/plugins/builtin/mcp/support/transports/sdk.py +113 -0
- axc_agent_engine/plugins/builtin/mcp/support/transports/stdio.py +101 -0
- axc_agent_engine/plugins/builtin/memory/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/memory/plugin.py +946 -0
- axc_agent_engine/plugins/builtin/memory/prompts.py +10 -0
- axc_agent_engine/plugins/builtin/memory/support/__init__.py +58 -0
- axc_agent_engine/plugins/builtin/memory/support/embedding.py +54 -0
- axc_agent_engine/plugins/builtin/memory/support/graph.py +147 -0
- axc_agent_engine/plugins/builtin/memory/support/retrieval.py +123 -0
- axc_agent_engine/plugins/builtin/memory/support/service.py +405 -0
- axc_agent_engine/plugins/builtin/output_format/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/output_format/plugin.py +208 -0
- axc_agent_engine/plugins/builtin/output_format/support/__init__.py +4 -0
- axc_agent_engine/plugins/builtin/output_format/support/service.py +395 -0
- axc_agent_engine/plugins/builtin/post_process/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/post_process/plugin.py +32 -0
- axc_agent_engine/plugins/builtin/reflexion/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/reflexion/plugin.py +59 -0
- axc_agent_engine/plugins/builtin/reflexion/prompts.py +9 -0
- axc_agent_engine/plugins/builtin/repetition_guard/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/repetition_guard/plugin.py +147 -0
- axc_agent_engine/plugins/builtin/risk_guard/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/risk_guard/plugin.py +42 -0
- axc_agent_engine/plugins/builtin/safety/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/safety/plugin.py +124 -0
- axc_agent_engine/plugins/builtin/skill/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/skill/plugin.py +756 -0
- axc_agent_engine/plugins/builtin/swarm/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/swarm/plugin.py +315 -0
- axc_agent_engine/plugins/builtin/tracing/__init__.py +1 -0
- axc_agent_engine/plugins/builtin/tracing/plugin.py +700 -0
- axc_agent_engine/plugins/loader.py +105 -0
- axc_agent_engine/plugins/registry.py +87 -0
- axc_agent_engine/py.typed +0 -0
- axc_agent_engine/runtime/__init__.py +1 -0
- axc_agent_engine/runtime/checkpoint.py +122 -0
- axc_agent_engine/runtime/concurrency.py +155 -0
- axc_agent_engine/runtime/input.py +28 -0
- axc_agent_engine/runtime/policy.py +68 -0
- axc_agent_engine/runtime/recovery.py +98 -0
- axc_agent_engine/runtime/resources.py +70 -0
- axc_agent_engine/runtime/risk.py +145 -0
- axc_agent_engine/runtime/sandbox_code.py +86 -0
- axc_agent_engine/runtime/sandbox_local.py +66 -0
- axc_agent_engine/runtime/sandbox_models.py +52 -0
- axc_agent_engine/runtime/sandbox_policy.py +43 -0
- axc_agent_engine/runtime/sandbox_provider.py +33 -0
- axc_agent_engine/runtime/sandbox_utils.py +50 -0
- axc_agent_engine/runtime/sandbox_workspace.py +64 -0
- axc_agent_engine/sidecar/README.md +49 -0
- axc_agent_engine/sidecar/__init__.py +51 -0
- axc_agent_engine/sidecar/agent_selector/__init__.py +127 -0
- axc_agent_engine/sidecar/cost_optimizer/__init__.py +168 -0
- axc_agent_engine/sidecar/distiller/__init__.py +144 -0
- axc_agent_engine/sidecar/eval/__init__.py +31 -0
- axc_agent_engine/sidecar/eval/judge.py +59 -0
- axc_agent_engine/sidecar/eval/matcher.py +145 -0
- axc_agent_engine/sidecar/eval/report.py +56 -0
- axc_agent_engine/sidecar/eval/runner.py +233 -0
- axc_agent_engine/sidecar/eval/store.py +126 -0
- axc_agent_engine/sidecar/failure_miner/__init__.py +130 -0
- axc_agent_engine/sidecar/multi_agent/__init__.py +16 -0
- axc_agent_engine/sidecar/multi_agent/events.py +14 -0
- axc_agent_engine/sidecar/multi_agent/modes.py +171 -0
- axc_agent_engine/sidecar/multi_agent/persona.py +65 -0
- axc_agent_engine/sidecar/multi_agent/scheduler/__init__.py +19 -0
- axc_agent_engine/sidecar/multi_agent/scheduler/all_parallel.py +24 -0
- axc_agent_engine/sidecar/multi_agent/scheduler/debate.py +42 -0
- axc_agent_engine/sidecar/multi_agent/scheduler/redblue.py +64 -0
- axc_agent_engine/sidecar/multi_agent/scheduler/round_robin.py +20 -0
- axc_agent_engine/sidecar/multi_agent/scheduler/supervisor.py +79 -0
- axc_agent_engine/sidecar/multi_agent/session.py +284 -0
- axc_agent_engine/sidecar/multi_agent/shared_context.py +69 -0
- axc_agent_engine/sidecar/multi_agent/simulation_session.py +90 -0
- axc_agent_engine/sidecar/multi_agent/stop_condition/__init__.py +20 -0
- axc_agent_engine/sidecar/multi_agent/stop_condition/causal_chain.py +17 -0
- axc_agent_engine/sidecar/multi_agent/stop_condition/consensus.py +16 -0
- axc_agent_engine/sidecar/multi_agent/stop_condition/goal_reached.py +17 -0
- axc_agent_engine/sidecar/multi_agent/stop_condition/llm_based.py +54 -0
- axc_agent_engine/sidecar/multi_agent/stop_condition/max_rounds.py +22 -0
- axc_agent_engine/sidecar/multi_agent/types.py +39 -0
- axc_agent_engine/sidecar/orchestration/__init__.py +232 -0
- axc_agent_engine/sidecar/simulation/__init__.py +74 -0
- axc_agent_engine/sidecar/simulation/action_parser.py +69 -0
- axc_agent_engine/sidecar/simulation/adapters.py +74 -0
- axc_agent_engine/sidecar/simulation/defaults.py +157 -0
- axc_agent_engine/sidecar/simulation/interfaces.py +70 -0
- axc_agent_engine/sidecar/simulation/models.py +235 -0
- axc_agent_engine/sidecar/simulation/modes.py +133 -0
- axc_agent_engine/sidecar/simulation/report.py +131 -0
- axc_agent_engine/sidecar/simulation/runner.py +248 -0
- axc_agent_engine/storage/__init__.py +12 -0
- axc_agent_engine/storage/in_memory.py +202 -0
- axc_agent_engine/storage/protocols.py +97 -0
- axc_agent_engine/storage/result_store.py +135 -0
- axc_agent_engine/tools/__init__.py +1 -0
- axc_agent_engine/tools/context.py +41 -0
- axc_agent_engine/tools/decorator.py +103 -0
- axc_agent_engine/tools/executor.py +234 -0
- axc_agent_engine/tools/name_mapping.py +71 -0
- axc_agent_engine/tools/orchestrator.py +334 -0
- axc_agent_engine/tools/registry.py +165 -0
- axc_agent_engine/tools/tool_output.py +113 -0
- axc_agent_engine/tools/utils.py +40 -0
- axc_agent_engine/utils/__init__.py +1 -0
- axc_agent_engine/utils/json_utils.py +73 -0
- axc_agent_engine/utils/math_utils.py +14 -0
- axc_agent_engine-0.2.0.dist-info/METADATA +454 -0
- axc_agent_engine-0.2.0.dist-info/RECORD +226 -0
- axc_agent_engine-0.2.0.dist-info/WHEEL +4 -0
- axc_agent_engine-0.2.0.dist-info/entry_points.txt +2 -0
- axc_agent_engine-0.2.0.dist-info/licenses/LICENSE +15 -0
- 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 路由"""
|