zero-agent-core 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 (104) hide show
  1. agent_core/__init__.py +525 -0
  2. agent_core/access/__init__.py +6 -0
  3. agent_core/access/context.py +45 -0
  4. agent_core/access/guard.py +40 -0
  5. agent_core/acp/README.md +236 -0
  6. agent_core/acp/__init__.py +20 -0
  7. agent_core/acp/server.py +351 -0
  8. agent_core/acp/types.py +86 -0
  9. agent_core/callbacks/__init__.py +17 -0
  10. agent_core/callbacks/manager.py +105 -0
  11. agent_core/checkpoint/__init__.py +19 -0
  12. agent_core/checkpoint/naming.py +93 -0
  13. agent_core/checkpoint/store.py +186 -0
  14. agent_core/checkpoint/travel.py +448 -0
  15. agent_core/compaction/__init__.py +57 -0
  16. agent_core/compaction/middleware.py +428 -0
  17. agent_core/compaction/prompts.py +32 -0
  18. agent_core/compaction/pruner.py +121 -0
  19. agent_core/compaction/region.py +145 -0
  20. agent_core/compaction/spill.py +81 -0
  21. agent_core/compaction/spill_tool.py +42 -0
  22. agent_core/compaction/summarizer.py +95 -0
  23. agent_core/compaction/token_limit.py +74 -0
  24. agent_core/compaction/types.py +93 -0
  25. agent_core/docs.py +15 -0
  26. agent_core/documents/__init__.py +23 -0
  27. agent_core/documents/loaders.py +79 -0
  28. agent_core/documents/splitters.py +94 -0
  29. agent_core/documents/types.py +197 -0
  30. agent_core/errors.py +81 -0
  31. agent_core/guardrails/__init__.py +19 -0
  32. agent_core/guardrails/core.py +186 -0
  33. agent_core/hitl/__init__.py +15 -0
  34. agent_core/hitl/core.py +517 -0
  35. agent_core/mcp/__init__.py +14 -0
  36. agent_core/mcp/client.py +212 -0
  37. agent_core/mcp/errors.py +26 -0
  38. agent_core/middleware/__init__.py +26 -0
  39. agent_core/middleware/base.py +283 -0
  40. agent_core/model/__init__.py +73 -0
  41. agent_core/model/catalog.py +73 -0
  42. agent_core/model/core.py +179 -0
  43. agent_core/model/governance.py +317 -0
  44. agent_core/model/providers/__init__.py +43 -0
  45. agent_core/model/providers/anthropic.py +294 -0
  46. agent_core/model/providers/common.py +28 -0
  47. agent_core/model/providers/factory.py +107 -0
  48. agent_core/model/providers/gemini.py +273 -0
  49. agent_core/model/providers/ollama.py +80 -0
  50. agent_core/model/providers/openai_compatible.py +526 -0
  51. agent_core/model/structured.py +200 -0
  52. agent_core/observability/__init__.py +21 -0
  53. agent_core/observability/core.py +311 -0
  54. agent_core/ports/__init__.py +31 -0
  55. agent_core/ports/memory.py +130 -0
  56. agent_core/ports/model_selection.py +64 -0
  57. agent_core/ports/storage.py +114 -0
  58. agent_core/prompts/__init__.py +19 -0
  59. agent_core/prompts/registry.py +236 -0
  60. agent_core/prompts/templates.py +166 -0
  61. agent_core/protocol/__init__.py +31 -0
  62. agent_core/protocol/capabilities.py +49 -0
  63. agent_core/protocol/messages.py +100 -0
  64. agent_core/protocol/runtime.py +293 -0
  65. agent_core/py.typed +1 -0
  66. agent_core/retrieval/__init__.py +40 -0
  67. agent_core/retrieval/_common.py +121 -0
  68. agent_core/retrieval/chroma.py +269 -0
  69. agent_core/retrieval/core.py +63 -0
  70. agent_core/retrieval/factory.py +72 -0
  71. agent_core/retrieval/memory.py +232 -0
  72. agent_core/retrieval/pgvector.py +239 -0
  73. agent_core/retrieval/types.py +95 -0
  74. agent_core/runtime/__init__.py +28 -0
  75. agent_core/runtime/compat.py +98 -0
  76. agent_core/runtime/react.py +888 -0
  77. agent_core/runtime/service.py +557 -0
  78. agent_core/runtime/store.py +55 -0
  79. agent_core/serialization/__init__.py +21 -0
  80. agent_core/serialization/registry.py +318 -0
  81. agent_core/skills/__init__.py +24 -0
  82. agent_core/skills/errors.py +27 -0
  83. agent_core/skills/loader.py +258 -0
  84. agent_core/skills/registry.py +205 -0
  85. agent_core/skills/types.py +56 -0
  86. agent_core/storage/__init__.py +107 -0
  87. agent_core/storage/_sqlite_utils.py +92 -0
  88. agent_core/storage/context.py +382 -0
  89. agent_core/storage/lease.py +435 -0
  90. agent_core/storage/memory.py +604 -0
  91. agent_core/storage/model_selection.py +360 -0
  92. agent_core/storage/runtime.py +899 -0
  93. agent_core/storage/session.py +904 -0
  94. agent_core/storage/workflow.py +411 -0
  95. agent_core/tools/__init__.py +5 -0
  96. agent_core/tools/executor.py +306 -0
  97. agent_core/workflow/__init__.py +25 -0
  98. agent_core/workflow/durable.py +175 -0
  99. agent_core/workflow/state_machine.py +427 -0
  100. zero_agent_core-0.2.0.dist-info/METADATA +237 -0
  101. zero_agent_core-0.2.0.dist-info/RECORD +104 -0
  102. zero_agent_core-0.2.0.dist-info/WHEEL +4 -0
  103. zero_agent_core-0.2.0.dist-info/entry_points.txt +3 -0
  104. zero_agent_core-0.2.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,205 @@
1
+ """Skill 注册、选择和工具绑定。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable, Iterable, Mapping
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from agent_core.mcp import McpToolCaller
10
+ from agent_core.skills.errors import SkillBindingError, SkillRegistrationError
11
+ from agent_core.skills.loader import SkillLoader
12
+ from agent_core.skills.types import SkillActivation, SkillSpec, SkillToolSpec
13
+ from agent_core.tools import ToolRegistry
14
+
15
+
16
+ class SkillRegistry:
17
+ """管理 Skill,并把激活 Skill 的工具安装到统一工具注册表。"""
18
+
19
+ def __init__(self) -> None:
20
+ self._skills: dict[str, SkillSpec] = {}
21
+
22
+ def register(self, skill: SkillSpec, *, replace: bool = False) -> None:
23
+ """注册一个 Skill;默认拒绝无意覆盖同名版本。"""
24
+ if skill.name in self._skills and not replace:
25
+ raise SkillRegistrationError(f"Skill 已注册:{skill.name}")
26
+ self._skills[skill.name] = skill
27
+
28
+ def unregister(self, name: str) -> None:
29
+ """移除一个 Skill。"""
30
+ self._skills.pop(name, None)
31
+
32
+ def get(self, name: str) -> SkillSpec | None:
33
+ """按名称查询 Skill。"""
34
+ return self._skills.get(name)
35
+
36
+ def names(self) -> tuple[str, ...]:
37
+ """按名称排序返回已注册 Skill。"""
38
+ return tuple(sorted(self._skills))
39
+
40
+ def all(self) -> tuple[SkillSpec, ...]:
41
+ """返回全部 Skill 规格。"""
42
+ return tuple(self._skills[name] for name in self.names())
43
+
44
+ def load(
45
+ self,
46
+ path: str | Path,
47
+ *,
48
+ loader: SkillLoader | None = None,
49
+ replace: bool = False,
50
+ ) -> SkillSpec:
51
+ """加载并注册一个 Skill。"""
52
+ skill = (loader or SkillLoader()).load(path)
53
+ self.register(skill, replace=replace)
54
+ return skill
55
+
56
+ def load_directory(
57
+ self,
58
+ directory: str | Path,
59
+ *,
60
+ loader: SkillLoader | None = None,
61
+ replace: bool = False,
62
+ ) -> tuple[SkillSpec, ...]:
63
+ """批量加载 Skill,并在写入注册表前完成全部重名检查。"""
64
+ skills = (loader or SkillLoader()).load_directory(directory)
65
+ batch_duplicates = self._duplicates(skill.name for skill in skills)
66
+ if batch_duplicates:
67
+ raise SkillRegistrationError(f"目录中包含重复 Skill:{', '.join(batch_duplicates)}")
68
+ conflicts = sorted(skill.name for skill in skills if skill.name in self._skills and not replace)
69
+ if conflicts:
70
+ raise SkillRegistrationError(f"Skill 已注册:{', '.join(conflicts)}")
71
+ for skill in skills:
72
+ self.register(skill, replace=replace)
73
+ return skills
74
+
75
+ def activate(
76
+ self,
77
+ names: str | Iterable[str] | None = None,
78
+ *,
79
+ tool_registry: ToolRegistry,
80
+ functions: Mapping[str, Callable[..., Any]] | None = None,
81
+ mcp_clients: Mapping[str, McpToolCaller] | None = None,
82
+ replace_tools: bool = False,
83
+ ) -> SkillActivation:
84
+ """激活 Skill,并绑定其 Function 与 MCP 工具。
85
+
86
+ ``functions`` 的键对应工具 ``target``;``mcp_clients`` 的键对应 MCP
87
+ 工具 ``server``。清单只声明绑定关系,不会动态导入任意 Python 代码。
88
+ """
89
+ selected = self._select(names)
90
+ functions = functions or {}
91
+ mcp_clients = mcp_clients or {}
92
+
93
+ declared: dict[str, tuple[str, SkillToolSpec]] = {}
94
+ bindings: list[tuple[SkillToolSpec, Callable[..., Any]]] = []
95
+ for skill in selected:
96
+ for tool in skill.tools:
97
+ previous = declared.get(tool.name)
98
+ if previous is not None:
99
+ raise SkillRegistrationError(
100
+ f"Skill {skill.name} 与 {previous[0]} 重复声明工具:{tool.name}"
101
+ )
102
+ declared[tool.name] = (skill.name, tool)
103
+ if tool_registry.get_tool(tool.name) is not None and not replace_tools:
104
+ raise SkillRegistrationError(f"工具已注册:{tool.name}")
105
+ bindings.append(
106
+ (
107
+ tool,
108
+ self._resolve_binding(
109
+ tool,
110
+ skill_name=skill.name,
111
+ functions=functions,
112
+ mcp_clients=mcp_clients,
113
+ ),
114
+ )
115
+ )
116
+
117
+ # 所有依赖和冲突都通过后再写注册表,保证失败不会产生部分注册。
118
+ for tool, function in bindings:
119
+ tool_registry.register(
120
+ tool.name,
121
+ function,
122
+ tool.description,
123
+ parameters=tool.parameters,
124
+ risk_level=tool.risk_level,
125
+ requires_approval=tool.requires_approval,
126
+ timeout_seconds=tool.timeout_seconds,
127
+ idempotent=tool.idempotent,
128
+ )
129
+
130
+ instructions = "\n\n".join(
131
+ skill.instructions.strip() for skill in selected if skill.instructions.strip()
132
+ )
133
+ definitions = [
134
+ {
135
+ "type": "function",
136
+ "function": {
137
+ "name": tool.name,
138
+ "description": tool.description,
139
+ "parameters": tool.parameters,
140
+ },
141
+ }
142
+ for _, tool in declared.values()
143
+ ]
144
+ return SkillActivation(
145
+ skills=selected,
146
+ instructions=instructions,
147
+ tool_names=tuple(declared),
148
+ tool_definitions=definitions,
149
+ )
150
+
151
+ def _select(self, names: str | Iterable[str] | None) -> tuple[SkillSpec, ...]:
152
+ if names is None:
153
+ selected_names = list(self.names())
154
+ elif isinstance(names, str):
155
+ selected_names = [names]
156
+ else:
157
+ selected_names = list(names)
158
+ duplicates = self._duplicates(selected_names)
159
+ if duplicates:
160
+ raise SkillRegistrationError(f"重复激活 Skill:{', '.join(duplicates)}")
161
+ missing = [name for name in selected_names if name not in self._skills]
162
+ if missing:
163
+ raise SkillRegistrationError(f"Skill 未注册:{', '.join(missing)}")
164
+ return tuple(self._skills[name] for name in selected_names)
165
+
166
+ @staticmethod
167
+ def _resolve_binding(
168
+ tool: SkillToolSpec,
169
+ *,
170
+ skill_name: str,
171
+ functions: Mapping[str, Callable[..., Any]],
172
+ mcp_clients: Mapping[str, McpToolCaller],
173
+ ) -> Callable[..., Any]:
174
+ if tool.kind == "function":
175
+ function = functions.get(tool.target)
176
+ if function is None:
177
+ raise SkillBindingError(f"Skill {skill_name} 缺少 Function 绑定:{tool.target}")
178
+ if not callable(function):
179
+ raise SkillBindingError(f"Skill {skill_name} 的 Function 绑定不可调用:{tool.target}")
180
+ return function
181
+
182
+ server = tool.server
183
+ if server is None:
184
+ raise SkillBindingError(f"Skill {skill_name} 的 MCP 工具未声明 server")
185
+ client = mcp_clients.get(server)
186
+ if client is None:
187
+ raise SkillBindingError(f"Skill {skill_name} 缺少 MCP 客户端:{server}")
188
+
189
+ async def call_mcp(**arguments: Any) -> dict[str, object]:
190
+ return await client.call_tool(tool.target, arguments)
191
+
192
+ return call_mcp
193
+
194
+ @staticmethod
195
+ def _duplicates(values: Iterable[str]) -> list[str]:
196
+ seen: set[str] = set()
197
+ duplicates: set[str] = set()
198
+ for value in values:
199
+ if value in seen:
200
+ duplicates.add(value)
201
+ seen.add(value)
202
+ return sorted(duplicates)
203
+
204
+
205
+ __all__ = ["SkillRegistry"]
@@ -0,0 +1,56 @@
1
+ """Skill 公共数据结构。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from pathlib import Path
7
+ from typing import Any, Literal
8
+
9
+ SkillToolKind = Literal["function", "mcp"]
10
+
11
+
12
+ @dataclass(frozen=True, slots=True)
13
+ class SkillToolSpec:
14
+ """Skill 声明的一个可调用工具。"""
15
+
16
+ name: str
17
+ kind: SkillToolKind
18
+ target: str
19
+ description: str
20
+ parameters: dict[str, Any] = field(default_factory=dict)
21
+ server: str | None = None
22
+ risk_level: str = "read"
23
+ requires_approval: bool = False
24
+ timeout_seconds: float | None = None
25
+ idempotent: bool = True
26
+
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class SkillSpec:
30
+ """一个可复用 Skill 的完整声明。"""
31
+
32
+ name: str
33
+ description: str
34
+ version: str
35
+ instructions: str = ""
36
+ tools: tuple[SkillToolSpec, ...] = ()
37
+ metadata: dict[str, Any] = field(default_factory=dict)
38
+ source: Path | None = None
39
+
40
+
41
+ @dataclass(slots=True)
42
+ class SkillActivation:
43
+ """一组 Skill 激活后交给 Agent 的提示词和工具定义。"""
44
+
45
+ skills: tuple[SkillSpec, ...]
46
+ instructions: str
47
+ tool_names: tuple[str, ...]
48
+ tool_definitions: list[dict[str, Any]]
49
+
50
+ def compose_system_prompt(self, base_prompt: str) -> str:
51
+ """把 Skill 指令追加到应用的基础系统提示词。"""
52
+ parts = [part.strip() for part in (base_prompt, self.instructions) if part.strip()]
53
+ return "\n\n".join(parts)
54
+
55
+
56
+ __all__ = ["SkillActivation", "SkillSpec", "SkillToolKind", "SkillToolSpec"]
@@ -0,0 +1,107 @@
1
+ """Agent Core 的通用存储实现。
2
+
3
+ SQLite 使用 Python 标准库,开箱即用;PostgreSQL 使用延迟导入的 ``asyncpg``,
4
+ 只有实际创建 PostgreSQL 存储时才要求安装对应驱动。
5
+ """
6
+
7
+ from agent_core.ports.model_selection import (
8
+ ModelSelection,
9
+ ModelSelectionScope,
10
+ ModelSelectionStore,
11
+ )
12
+ from agent_core.storage.context import (
13
+ ContextStore,
14
+ MemoryContextStore,
15
+ PostgresContextStore,
16
+ SqliteContextStore,
17
+ create_context_store,
18
+ )
19
+ from agent_core.storage.lease import (
20
+ MemoryRunLeaseStore,
21
+ PostgresRunLeaseStore,
22
+ RunLease,
23
+ RunLeaseStore,
24
+ SqliteRunLeaseStore,
25
+ create_run_lease_store,
26
+ )
27
+ from agent_core.storage.memory import (
28
+ InMemoryMemoryStore,
29
+ PostgresMemoryStore,
30
+ SqliteMemoryStore,
31
+ create_memory_store,
32
+ )
33
+ from agent_core.storage.model_selection import (
34
+ MemoryModelSelectionStore,
35
+ PostgresModelSelectionStore,
36
+ SqliteModelSelectionStore,
37
+ create_model_selection_store,
38
+ )
39
+ from agent_core.storage.runtime import (
40
+ MemoryRuntimeStore,
41
+ PostgresRuntimeStore,
42
+ RuntimeConcurrencyError,
43
+ RuntimeStore,
44
+ SqliteRuntimeStore,
45
+ create_runtime_store,
46
+ )
47
+ from agent_core.storage.session import (
48
+ BaseSessionStore,
49
+ MemorySessionStore,
50
+ PostgresSessionStore,
51
+ SessionManager,
52
+ SqliteSessionStore,
53
+ create_session_store,
54
+ )
55
+ from agent_core.storage.workflow import (
56
+ MemoryWorkflowExecutionStore,
57
+ PostgresWorkflowExecutionStore,
58
+ SqliteWorkflowExecutionStore,
59
+ WorkflowConcurrencyError,
60
+ WorkflowExecution,
61
+ WorkflowExecutionStore,
62
+ create_workflow_execution_store,
63
+ )
64
+
65
+ __all__ = [
66
+ "BaseSessionStore",
67
+ "ContextStore",
68
+ "InMemoryMemoryStore",
69
+ "MemoryContextStore",
70
+ "MemoryModelSelectionStore",
71
+ "MemoryRunLeaseStore",
72
+ "MemoryRuntimeStore",
73
+ "MemorySessionStore",
74
+ "MemoryWorkflowExecutionStore",
75
+ "ModelSelection",
76
+ "ModelSelectionScope",
77
+ "ModelSelectionStore",
78
+ "PostgresContextStore",
79
+ "PostgresMemoryStore",
80
+ "PostgresModelSelectionStore",
81
+ "PostgresRunLeaseStore",
82
+ "PostgresRuntimeStore",
83
+ "PostgresSessionStore",
84
+ "PostgresWorkflowExecutionStore",
85
+ "RunLease",
86
+ "RunLeaseStore",
87
+ "RuntimeConcurrencyError",
88
+ "RuntimeStore",
89
+ "SessionManager",
90
+ "SqliteContextStore",
91
+ "SqliteMemoryStore",
92
+ "SqliteModelSelectionStore",
93
+ "SqliteRunLeaseStore",
94
+ "SqliteRuntimeStore",
95
+ "SqliteSessionStore",
96
+ "SqliteWorkflowExecutionStore",
97
+ "WorkflowConcurrencyError",
98
+ "WorkflowExecution",
99
+ "WorkflowExecutionStore",
100
+ "create_context_store",
101
+ "create_memory_store",
102
+ "create_model_selection_store",
103
+ "create_run_lease_store",
104
+ "create_runtime_store",
105
+ "create_session_store",
106
+ "create_workflow_execution_store",
107
+ ]
@@ -0,0 +1,92 @@
1
+ """SQLite 存储共享工具:统一连接参数规范与线程池执行包装。
2
+
3
+ sqlite3 驱动是同步实现;写锁等待(busy timeout)会冻结所在线程。
4
+ 存储层保持"每操作开新连接"的简单模型,但所有 async 方法体统一经
5
+ `_run_in_thread` 放入工作线程执行,把可能的锁等待隔离在事件循环之外。
6
+
7
+ 统一连接参数规范(各存储的 `_connect` 必须经 `sqlite_connection` 复用):
8
+
9
+ - ``timeout=30.0``:写锁等待上限 30 秒,超时抛 ``sqlite3.OperationalError``;
10
+ - `ensure_wal`:WAL 是库级持久属性(保存在数据库文件中),在存储初始化
11
+ 建连接时开启一次即可,之后的连接自动继承。`sqlite_connection` 每次建连
12
+ 都幂等调用 `ensure_wal`,因此初始化与后续每操作连接使用同一规范。
13
+
14
+ 构造期 DDL / PRAGMA 初始化同样入线程:构造函数是同步入口、无法 ``await``,
15
+ 统一经 `run_sync_in_thread` 在一次性工作线程中执行后返回。
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import asyncio
21
+ import sqlite3
22
+ from collections.abc import Callable, Generator
23
+ from concurrent.futures import ThreadPoolExecutor
24
+ from contextlib import contextmanager
25
+ from pathlib import Path
26
+ from typing import Any
27
+
28
+ __all__ = [
29
+ "SQLITE_CONNECT_TIMEOUT",
30
+ "_run_in_thread",
31
+ "ensure_wal",
32
+ "run_sync_in_thread",
33
+ "sqlite_connection",
34
+ ]
35
+
36
+ # 所有 SQLite 存储统一使用的写锁等待上限(秒)。
37
+ SQLITE_CONNECT_TIMEOUT = 30.0
38
+
39
+ # 连接级回调签名:与 sqlite3.Connection.row_factory 的类型契约一致。
40
+ RowFactory = Callable[[sqlite3.Cursor, tuple[Any, ...]], Any]
41
+
42
+
43
+ def ensure_wal(conn: sqlite3.Connection) -> None:
44
+ """开启 WAL 日志模式与 NORMAL 同步级别(幂等,可重复调用)。
45
+
46
+ WAL 是库级属性:一经设置即持久保存在数据库文件中,后续连接自动继承;
47
+ ``synchronous`` 是连接级属性,因此每次建连都需要重新设置。
48
+ """
49
+ conn.execute("PRAGMA journal_mode=WAL")
50
+ conn.execute("PRAGMA synchronous=NORMAL")
51
+
52
+
53
+ @contextmanager
54
+ def sqlite_connection(
55
+ db_path: str | Path,
56
+ *,
57
+ row_factory: RowFactory | None = None,
58
+ ) -> Generator[sqlite3.Connection]:
59
+ """按统一参数规范打开一个自动提交/回滚的 SQLite 连接(每操作一个)。
60
+
61
+ - ``timeout=30.0``:统一写锁等待上限;
62
+ - `ensure_wal`:统一开启 WAL 与 ``synchronous=NORMAL``。
63
+
64
+ 退出时提交;异常时回滚并原样抛出;连接在 finally 中关闭。
65
+ """
66
+ conn = sqlite3.connect(str(db_path), timeout=SQLITE_CONNECT_TIMEOUT)
67
+ try:
68
+ if row_factory is not None:
69
+ conn.row_factory = row_factory
70
+ ensure_wal(conn)
71
+ yield conn
72
+ conn.commit()
73
+ except Exception:
74
+ conn.rollback()
75
+ raise
76
+ finally:
77
+ conn.close()
78
+
79
+
80
+ async def _run_in_thread[T](func: Callable[[], T]) -> T:
81
+ """把同步 SQLite IO 放到默认线程池执行,避免锁等待冻结事件循环。"""
82
+ return await asyncio.to_thread(func)
83
+
84
+
85
+ def run_sync_in_thread[T](func: Callable[[], T]) -> T:
86
+ """在一次性工作线程中执行同步初始化(DDL/PRAGMA)并等待完成。
87
+
88
+ 存储构造函数是同步入口,无法 ``await``;初始化放入独立线程执行,
89
+ 与 async 方法共用同一连接参数规范。
90
+ """
91
+ with ThreadPoolExecutor(max_workers=1, thread_name_prefix="agent-core-sqlite-init") as pool:
92
+ return pool.submit(func).result()