-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpyproject.toml
More file actions
328 lines (288 loc) · 12.8 KB
/
Copy pathpyproject.toml
File metadata and controls
328 lines (288 loc) · 12.8 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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "trw-memory"
version = "0.15.0"
description = "Persistent memory engine with hybrid retrieval, tiered storage, and semantic dedup for AI agents"
readme = "README.md"
license = "BUSL-1.1"
requires-python = ">=3.10"
authors = [
{ name = "TRW Framework", email = "noreply@trwframework.com" },
]
keywords = ["memory", "ai-agents", "retrieval", "embeddings", "mcp", "knowledge-management", "semantic-search"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
"Typing :: Typed",
]
dependencies = [
"cryptography>=48.0.1",
"httpx>=0.27.0",
# pysqlite3-binary ships a recent SQLite (>=3.51 wheels) and is preferred
# over stdlib sqlite3 to mitigate the WAL-reset bug present in older
# bundled SQLite versions. The storage._dbapi shim swaps it in at import
# time; absence is a silent no-op (stdlib sqlite3 + the code-level WAL
# single-connection mitigation) so the package still works without it.
# Linux-only marker: pysqlite3-binary publishes manylinux wheels but NO
# macOS-arm64 wheel (removed upstream — `pip install` fails with "No
# matching distribution" on macos-latest, which blocked the release smoke
# matrix at v0.8.5), and none for Windows. macOS/Windows fall back to
# stdlib sqlite3.
"pysqlite3-binary>=0.5.4; platform_system == 'Linux'",
"pydantic>=2.0.0",
"pydantic-settings>=2.14.2",
"ruamel.yaml>=0.18.0",
"structlog>=24.0.0",
"typing-extensions>=4.6.0",
]
[project.optional-dependencies]
mcp = ["fastmcp>=3.2.0,<4.0.0"]
encryption = ["sqlcipher3>=0.5.0", "keyring>=24.0.0", "cryptography>=48.0.1"]
embeddings = [
"sentence-transformers>=2.0.0",
# numpy>=2.5 drops Python 3.10 support (and ships PEP-695 `type X = ...`
# stubs that mypy rejects under python_version="3.10"), which trw-memory
# still supports. Pin below 2.5 wherever a transitive dep (here
# sentence-transformers) pulls numpy in unconstrained.
"numpy<2.5",
]
vectors = ["sqlite-vec>=0.1.0"]
bm25 = [
"rank-bm25>=0.2.0",
# numpy>=2.5 drops Python 3.10 support (and ships PEP-695 `type X = ...`
# stubs that mypy rejects under python_version="3.10"), which trw-memory
# still supports. rank-bm25 declares an unconstrained numpy dep, so floor
# the ceiling here to keep the CI `.[dev,vectors,bm25,mcp]` install on a
# 3.10-compatible numpy.
"numpy<2.5",
]
llm = ["anthropic>=0.40.0"]
langchain = ["langchain-core>=0.3.0"]
llamaindex = ["llama-index-core>=0.11.0"]
crewai = [
"crewai>=0.74.0",
# ChromaDB 1.x currently has an unfixed pre-auth code-injection advisory
# (CVE-2026-45829). CrewAI's compatible 0.x line works with ChromaDB 0.6.x;
# keep public integration installs off the vulnerable server line until an
# upstream fixed release is available.
"chromadb<1.0",
# CrewAI versions compatible with ChromaDB 0.x otherwise resolve to a
# LiteLLM release with multiple fixed 2026 advisories.
"litellm>=1.84.0",
]
all-integrations = [
"trw-memory[langchain,llamaindex,crewai]",
]
all = ["trw-memory[mcp,embeddings,vectors,bm25,llm]"]
dev = [
"pytest>=8.0.0",
"pytest-cov>=5.0.0",
"pytest-asyncio>=0.24.0",
"mypy>=1.11.0,<2.0", # pin to the 1.x line: mypy 2.x is a major release with behavior changes that a raw `pip install .[dev]` would resolve (2.3.0), diverging from the verified uv.lock (1.20.2). Migrate to 2.x deliberately, not via CI drift.
"ruff>=0.15.0",
"tomli>=2.0.1; python_version < '3.11'",
"httpx>=0.27.0",
"pip-audit>=2.7.0",
"vulture>=2.11",
"deptry>=0.21.0",
]
[project.urls]
Homepage = "https://trwframework.com"
Documentation = "https://trwframework.com/docs"
Repository = "https://github.com/wallter/trw-memory"
Issues = "https://github.com/wallter/trw-memory/issues"
Changelog = "https://github.com/wallter/trw-memory/blob/main/CHANGELOG.md"
[project.scripts]
trw-memory = "trw_memory.cli:main"
trw-memory-server = "trw_memory.server:main"
[tool.hatch.build.targets.wheel]
packages = ["src/trw_memory"]
[tool.hatch.build.targets.sdist]
# Defense in depth alongside .gitignore: hatchling honors the package-local
# .gitignore but NOT a global git excludesfile, so local runtime/test state
# (the .memory/ store dirs, coverage artifacts, stray SQLite files) must be
# excluded explicitly here too — they must never ship in the published sdist.
#
# The agent-facing dev docs (CLAUDE.md / AGENTS.md / FRAMEWORK.md) and the uv
# lockfile are monorepo development artifacts, not part of the published
# library — they are excluded so the public PyPI sdist carries only library
# sources, packaging metadata, and the changelog/license/readme.
exclude = [
".memory",
"coverage.json",
"*.db",
"*.db-wal",
"*.db-shm",
"uv.lock",
"CLAUDE.md",
"AGENTS.md",
"FRAMEWORK.md",
]
[tool.deptry]
known_first_party = ["trw_memory"]
optional_dependencies_dev_groups = ["dev"]
package_module_name_map = { "llama-index-core" = "llama_index", "langchain-core" = "langchain_core", "sqlcipher3" = "sqlcipher3", "crewai" = "crewai" }
[tool.deptry.per_rule_ignores]
# Optional probe: text embeddings can hide a broken torchcodec install during
# sentence-transformers import, but torchcodec itself is not required.
DEP001 = ["torchcodec"]
# Optional extras / self-referential aggregate extras are intentionally not
# imported in the core package scan.
DEP002 = ["sqlcipher3", "anthropic", "crewai", "trw-memory"]
# PyNaCl-backed signing is optional; row provenance can still use cryptography
# and the chain helpers degrade explicitly when nacl is unavailable.
DEP003 = ["nacl"]
[tool.ruff]
target-version = "py310"
line-length = 120
[tool.ruff.lint]
select = [
# --- Foundations ---
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes: unused imports (F401), undefined names (F821), unused vars (F841)
# --- Import hygiene ---
"I", # isort: consistent import ordering
# --- Bug patterns (catches redundant except, mutable defaults, unused loop vars) ---
"B", # flake8-bugbear: B014 duplicate exception handler, B006 mutable defaults, B018 useless expr
# --- Modern Python syntax ---
"UP", # pyupgrade: deprecated aliases, union types (X | Y)
# --- Code simplification ---
"SIM", # flake8-simplify: SIM102 nested-if, SIM105 contextlib.suppress
"RET", # flake8-return: RET504 assign-then-return, RET505-508 superfluous else
"PIE", # flake8-pie: PIE790 unnecessary pass, PIE794 duplicate class field
# --- Exception handling quality ---
"TRY", # tryceratops: TRY201 verbose raise, TRY301 raise-in-try
# --- Async safety ---
"ASYNC", # flake8-async: blocking I/O in async functions
# --- pytest hygiene ---
"PT", # flake8-pytest-style: fixture/assert/exception style
# --- Type checking optimization ---
"TC", # flake8-type-checking: move type-only imports to TYPE_CHECKING
# --- Ruff-native ---
"RUF", # RUF012 mutable ClassVar, RUF100 unused noqa
# --- Expanded rule sets (PRD-QUAL-046 FR07) ---
"C4", # flake8-comprehensions: unnecessary list/dict comprehension
"PERF", # performance anti-patterns
"G", # flake8-logging-format: logging format strings
"S", # bandit: security checks
"DTZ", # flake8-datetimez: timezone-aware datetime
"FURB", # refurb: modern Python idioms
"C901", # McCabe complexity
"ANN", # flake8-annotations: type annotations
]
ignore = [
"E501", # line too long — handled by line-length setting
"TRY003", # long exception messages inline are fine in application code
"TRY002", # raising ValueError/TypeError directly is idiomatic in libraries
"TRY300", # return-in-try is standard CLI handler pattern (try/except/finally)
"TRY301", # raise-within-try is sometimes the cleanest error-wrap pattern
"SIM108", # ternary operator is not always more readable than if/else
"SIM105", # contextlib.suppress vs try/except/pass is a style choice; codebase chooses try/except
"RET504", # assign-then-return is often clearer than inline return
"TC001", # typing imports used at runtime via Pydantic models — not type-checking-only
"TC002", # third-party imports used at runtime — not type-checking-only
"TC003", # stdlib imports used at runtime in constructors — not type-checking-only
"B008", # function calls in defaults — FastMCP/Pydantic patterns use this idiomatically
"ISC001", # conflicts with formatter
"ANN401", # Any annotations are intentional at JSON/config/MCP-boundary surfaces
"PERF203", # try-except inside loops is required for per-item error handling
"C901", # McCabe complexity is a code-review concern; max-complexity already capped at 15
"RUF002", # ambiguous unicode in docstrings (math glyphs intentional)
]
fixable = ["F401", "B007", "B013", "B014", "UP", "I", "PIE790", "PIE810", "TC", "RUF100", "RUF022", "RUF023", "SIM110", "SIM102", "SIM114", "SIM300", "PERF401", "TRY400", "C401", "C420", "B905"]
unfixable = ["F841"] # never auto-delete "unused" variables (could be side-effect assignments)
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["PT", "TRY", "SIM", "B018", "RUF012", "F841", "E741", "B017", "RUF059", "B905", "ANN", "S101", "S", "G"]
"benchmarks/**/*.py" = ["TRY", "PT", "B905"]
"src/trw_memory/cli.py" = ["ASYNC230", "ASYNC240", "G201", "TRY400"] # CLI: blocking I/O fine, structured error logging at boundary
"**/__init__.py" = ["F401", "E402"] # re-exports after NullHandler init are intentional
"**/conftest.py" = ["PT"]
"src/trw_memory/_logging.py" = ["ANN401", "S110"] # structlog processor convention; S110: best-effort version binding
[tool.ruff.lint.isort]
known-first-party = ["trw_memory"]
split-on-trailing-comma = true
[tool.ruff.lint.mccabe]
max-complexity = 15
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]
addopts = "-m 'not slow'"
asyncio_mode = "auto"
markers = [
"unit: pure unit tests — no filesystem I/O beyond tmp_path, mocks only, fast",
"integration: uses real filesystem / sqlite / network — slower, still isolated",
"slow: individual-test runtime > 5 seconds — excluded from default runs",
"network: requires external network access (embedding models, API calls)",
]
[tool.mypy]
strict = true
python_version = "3.10"
plugins = ["pydantic.mypy"]
warn_return_any = true
warn_unused_configs = true
# Optional dependencies that lack type stubs — ignore on a per-module basis
# so `mypy --strict` passes even when a minimal `.[dev]` install is used.
# Each override uses a separate table entry because mypy requires the module
# field to be a glob string (not a list) in older versions.
[[tool.mypy.overrides]]
module = "sentence_transformers"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "sentence_transformers.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "rank_bm25"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "sqlite_vec"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "keyring"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "fastmcp"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "fastmcp.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "cryptography"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "cryptography.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "nacl"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "nacl.*"
ignore_missing_imports = true
# PyNaCl is an optional dep. The signing-key fallbacks in these two modules
# carry `# type: ignore[misc,assignment]` that is USED when PyNaCl is present
# (real nacl types -> rebinding to Any is a genuine error these ignores
# suppress) but UNUSED when PyNaCl is absent (e.g. the public CI dep set,
# where nacl resolves to Any via the override above). `warn_unused_ignores`
# would therefore fire in CI only. Disabling it for just these modules keeps
# both environments green without env-dependent source edits.
[[tool.mypy.overrides]]
module = ["trw_memory.security.provenance", "trw_memory.security.keys"]
warn_unused_ignores = false
[tool.coverage.run]
source = ["trw_memory"]
omit = ["*/server.py"]
[tool.coverage.report]
fail_under = 85 # PRD-INFRA-042: normalized across packages (mcp=80, memory=85, eval=85, backend=75)
show_missing = true