-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathpyproject.toml
More file actions
206 lines (189 loc) · 9.9 KB
/
Copy pathpyproject.toml
File metadata and controls
206 lines (189 loc) · 9.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
[build-system]
requires = ["setuptools>=77", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "dm-code-agent"
version = "2.0.0"
description = "A local-first, auditable Python Code Agent with ReAct, trace/replay, MCP tools, planning, benchmarks, memory compression, and skills."
readme = "README.md"
requires-python = ">=3.10"
license = "MIT"
license-files = ["LICENSE"]
authors = [
{ name = "hwfengcs" }
]
keywords = [
"ai-agent",
"code-agent",
"react-agent",
"mcp",
"trace",
"benchmark",
"code-maintenance",
"llm",
"python",
"agent-framework"
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Code Generators",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = [
"requests>=2.31.0,<3.0.0",
"python-dotenv>=1.0.0,<2.0.0",
"colorama>=0.4.6,<1.0.0",
"rich>=13.7.0,<15.0.0",
"openai>=1.0.0",
"anthropic>=0.18.0",
"google-genai>=0.1.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-cov>=5.0.0",
"ruff>=0.6.0",
"black>=24.0.0",
"mypy>=1.11.0",
"types-requests>=2.31.0",
"build>=1.2.0",
"pre-commit>=4.0.0,<5.0.0",
# starlette 的 TestClient 依赖 httpx;server 层的测试全部走 TestClient,无需真起端口。
"httpx>=0.27.0",
# 自引用 web extra:让 `--extra dev` 一条就把 server 层的依赖也装齐。
# 这样 CI 与 pre-commit 里现有的 `uv run --frozen --extra dev ...` 全部无需改动,
# 也不会出现「某一步漏传 --extra web 导致 uv 把 fastapi 卸掉」的连锁失败。
"dm-code-agent[web]",
]
# Web 控制台(dm-agent-web)。核心包保持零 web 依赖:不装这个 extra 时
# `import dm_agent` 与全部 CLI 功能不受影响,只有 dm-agent-web 会提示缺依赖。
# 不用 uvicorn[standard]:它带的 uvloop 不支持 Windows、websockets 本项目用不到
# (实时流走 SSE),httptools 只是性能优化。本地单用户场景纯 asyncio 实现足够。
web = [
"fastapi>=0.115.0",
"uvicorn>=0.30.0",
]
[project.urls]
Homepage = "https://github.com/hwfengcs/DM-Code-Agent"
Repository = "https://github.com/hwfengcs/DM-Code-Agent"
Issues = "https://github.com/hwfengcs/DM-Code-Agent/issues"
[project.scripts]
dm-agent = "dm_agent.cli:main"
dm-agent-eval = "dm_agent.evals.cli:main"
dm-agent-bench = "dm_agent.benchmarks.cli:main"
dm-agent-trace = "dm_agent.tracing.cli:main"
dm-agent-economics = "dm_agent.benchmarks.economics:main"
dm-agent-manifest-diff = "dm_agent.benchmarks.manifest_diff:main"
dm-agent-score-diff = "dm_agent.benchmarks.score_diff:main"
dm-agent-web = "dm_agent.server.cli:main"
[tool.setuptools.packages.find]
include = ["dm_agent*"]
[tool.setuptools.package-data]
# 前端构建产物。web/ 下的源码不进 wheel,只有 npm run build 拷进来的静态文件进。
"dm_agent.server" = ["static/**/*"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q"
[tool.black]
line-length = 100
target-version = ["py310"]
[tool.ruff]
line-length = 100
target-version = "py310"
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM", "TID", "RUF"]
ignore = [
# E501(行超长)与 black 存在两处结构性冲突,故由 black 的 line-length=100 单独负责行宽:
# 1. 关闭前全部 40 处 E501 都落在字符串字面量内部(纯代码行 0 处),
# 即 black 已拆完所有可拆的行,剩下的都是它判定不可拆的长字符串;
# 这些字符串是 system prompt、工具描述、benchmark 任务描述等直接进 LLM 上下文的文本,
# 手工折行会改变 prompt 字面内容(属于改行为),且会触发 benchmark manifest 漂移告警。
# 2. 本项目按规范使用中文注释与文档,black 按「字符数」计宽而 ruff E501 按「显示宽度」计宽
# (中文字符记 2 宽),含中文的行会永久处于「black 判定合规、ruff 判定超长」的死锁。
"E501",
# RUF001/002/003(歧义 Unicode 字符)用于防范同形字符伪装(如西里尔字母冒充拉丁字母),
# 但会把中文全角标点「,」「:」「!」全部误报。本项目 CLAUDE.md 强制中文注释与文档,
# 关闭前 551 处报告 100% 来自合法的中文全角标点,无一例真实的同形字符风险。
"RUF001",
"RUF002",
"RUF003",
]
[tool.ruff.lint.flake8-tidy-imports.banned-api]
# 分层契约(TID251)。依赖必须单向向下,CLI 是最外层的组装者,不是被复用的库:
# clients ← 不依赖任何其他 dm_agent 子包
# tools ← 只依赖 clients
# tracing ← 只依赖 tools / memory(是个下沉的记录汇,不回头依赖 core)
# core ← 依赖 clients / tools / memory / prompts / tracing,禁止依赖 cli
# cli ← 可以依赖任何层
# server ← 与 cli 同级的另一个最外层装配者。可以依赖任何下层,但**不得依赖 cli**:
# 它通过 spawn 子进程复用 CLI,而不是 import 它。这一条 ruff 拦不住
# (见下方 per-file-ignores 里 server 的豁免理由),由
# tests/test_server_layering.py 显式断言。
"dm_agent.cli".msg = "core / tools / clients / memory / evals / benchmarks / tracing 层不得依赖 CLI 层;CLI 是最外层组装者。需要共享的逻辑请下移到对应的层。"
"dm_agent.server".msg = "只有 server 层自身可以 import dm_agent.server;它是最外层装配者,任何下层(含 cli)都不得反向依赖它。"
# 防止顶级 main 模块回归:它曾经通过 py-modules = ["main"] 被装进 site-packages 顶层,
# 污染用户命名空间(见 d763fc8)。除根目录那个薄转发自身外,任何地方都不该 import 它。
"main".msg = "不要 import 顶级 main 模块(它只是 dm_agent.cli 的薄转发);直接用 dm_agent.cli。"
[tool.ruff.lint.per-file-ignores]
# BaseSkill.on_activate / on_deactivate 是 template method 模式下的「可选生命周期钩子」,
# 空实现即为有意的默认行为。B027 建议加 @abstractmethod,那会强制所有子类实现,属于改行为。
"dm_agent/skills/base.py" = ["B027"]
# 分层契约(上面的 banned-api)的三类合法例外:
# 1. cli 包内部互相依赖本就是允许的。ruff 会把相对导入 from .ui import UI 解析成绝对的
# dm_agent.cli.ui.UI 再做匹配,所以改写成相对导入并不能绕过,必须在这里显式豁免。
# server 包同理:它的 from .settings import ... 会被解析成 dm_agent.server.settings。
# 代价是 ruff 也不再拦 server → cli,那一条改由 tests/test_server_layering.py 断言。
# 2. 根目录 main.py 就是那个薄转发入口,它的职责正是 import dm_agent.cli。
# 3. 测试需要直接触达 CLI 内部符号,断言参数解析与 agent 装配行为。
"dm_agent/cli/**" = ["TID251"]
"dm_agent/server/**" = ["TID251"]
"main.py" = ["TID251"]
"tests/**" = ["TID251"]
[tool.mypy]
python_version = "3.10"
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true
# 全仓库先只做「基础检查」:不强制补全标注,只抓已标注代码里的真实类型错误。
# 严格档位(disallow_untyped_defs)当前只对最稳定、依赖最少的 tools 与 clients 开启,
# 后续再逐模块扩大,避免一次性产生几千个无人处理的错误。
[[tool.mypy.overrides]]
module = ["dm_agent.tools.*", "dm_agent.clients.*"]
disallow_untyped_defs = true
[[tool.mypy.overrides]]
# 已知技术债(唯一根因):ReactAgent / CriticAgent / Reflector 的 client 形参标注为具体基类
# BaseLLMClient,而 eval 与 benchmark 侧注入的是鸭子类型假客户端(ScriptedLLMClient、
# UsageTrackingClient)。这两个类不能改为继承 BaseLLMClient——后者是 ABC 且构造函数强制
# 要求非空 api_key,继承会摧毁「确定性 eval 无需 API key 即可回归」这一核心设计。
# 正解是把 client 形参改为结构化类型(Protocol),属于内核接口调整,留待分层重构那一步。
# 放宽范围收到最窄:仅这 4 个模块、仅 arg-type 一个错误码,其余检查全部保持生效。
module = [
"dm_agent.evals.runner",
"dm_agent.evals.real_runner",
"dm_agent.benchmarks.runner",
]
disable_error_code = ["arg-type"]
[[tool.mypy.overrides]]
# colorama 0.4.6 不随包提供 py.typed,本项目只用到 Fore / Style / init 三个符号。
# 改装 types-colorama 也消不掉下面那条 no-redef(降级分支必须重定义同名类),收益为零,
# 故不引入新依赖,直接跳过对 colorama 自身的类型解析。
module = ["colorama"]
ignore_missing_imports = true
[[tool.mypy.overrides]]
# dm_agent.cli.ui 是「rich / colorama 可选依赖降级」的集中地,两个错误码各有唯一根因,
# 且都源自 mypy 表达不了的运行时不变式(这些代码在拆分前住在包外的 main.py 里,从未被检查):
# 1. no-redef:rich / colorama 缺失时,except ImportError 分支必须重定义同名的
# Fore / Style 空常量类,否则全部 UI.paint 调用点都要按依赖是否存在分叉。
# 这是可选依赖的标准降级写法,无法用别名或 Protocol 消除。
# 2. assignment:RICH_CONSOLE 在 try 分支是 Console、except 分支是 None,运行时由
# UI.rich_enabled() 守卫,mypy 读不出这个跨函数不变式;print_menu 的 badge 在 rich
# 分支是 Text、降级分支是 str,而 Text 在降级路径上根本没被导入,写不出 Text | str。
# 放宽范围收到最窄:仅这一个模块、仅这两个错误码。arg-type 等高信号检查全部保持生效
# (ask() 里的 prompt_kwargs 就是宁可加显式标注、也不放宽 arg-type 的例子)。
module = ["dm_agent.cli.ui"]
disable_error_code = ["no-redef", "assignment"]