-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathpyproject.toml
More file actions
290 lines (273 loc) · 12.4 KB
/
Copy pathpyproject.toml
File metadata and controls
290 lines (273 loc) · 12.4 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
[tool.poetry]
name = "reactome-chatbot"
version = "0.1.0"
description = "Chatbot for reactome's data portal"
authors = ["Helia Mohammadi <hmohammadi@oicr.on.ca>",
"Adam Wright <awright@oicr.on.ca>",
"Greg Hogue <ghogue@oicr.on.ca>"]
license = "Apache2"
readme = "README.md"
packages = [
{ include = "src", from = "." }
]
#lark is needed for langchain -> SelfQueryRetriever
[tool.poetry.dependencies]
python = ">=3.12, <4"
langchain = "^1.4.0"
# Declared, not inherited. The answer endpoint verifies a signed caller
# token, so this repository imports PyJWT directly. It arrives transitively today
# via chainlit and mcp, which is not a dependency -- it is a coincidence that a
# chainlit upgrade could end, and the symptom would be an endpoint that cannot
# verify anyone.
pyjwt = {version = "^2.10", extras = ["crypto"]}
# Imported directly by bin/make-caller-token-keypair.py and the token tests,
# not just pulled in by pyjwt's crypto extra. Declared so it does not vanish
# if that extra or the JWT library ever changes.
cryptography = ">=42,<51"
# Declared, not inherited. LangChain 1.0 moved the pre-LCEL chain builders here
# (retrievers/rag_chain.py, the metadata_info modules), so this repository
# imports langchain_classic directly and must depend on it directly. It arrived
# transitively while langchain-community 0.4 was briefly installed, which is
# exactly why a stale virtualenv made the code look fine locally and CI did not.
langchain-classic = "^1.0.8"
# Same: data_generation imports RecursiveCharacterTextSplitter from it.
langchain-text-splitters = "^1.1.2"
# 2.x, required by langchain-openai 1.x (openai>=2.45). Used directly only by
# bin/probe_model_temperature, for models.list().
openai = "^2.45.0"
# Held below 1.0, and langchain-chroma below 1.0 with it. chromadb 1.x opens a
# 0.5/0.6 bundle by migrating its sqlite sysdb in place (9 -> 10), which needs
# write access to the bundle directory. Installed bundles here are root-owned
# 755, so every developer running as themselves gets
#
# InternalError: error returned from database: (code: 8)
# attempt to write a readonly database
#
# at startup, before a single question. Migrating is a deliberate operational
# step -- chown every bundle on every host -- not something to slip into a
# dependency upgrade. (The migration itself is sound: verified that chromadb
# 1.5.9 reads a 0.6-written bundle and that 0.6.3 still reads it afterwards, so
# a rollback does not strand the data.)
chromadb = "<1.0"
# chromadb 0.6 calls posthog.capture() positionally; posthog 6 changed the
# signature, so every telemetry call fails and chromadb logs it at ERROR.
# Telemetry is off anyway; this is purely to keep the log readable.
posthog = "<6"
pandas = "^2.2.1"
pyasn1 = "^0.6.4"
langchain-openai = "^1.6.1"
neo4j = "4.3.6"
python-dotenv = "^1.0.1"
pyfiglet = "^1.0.2"
chainlit = "^2.0.3"
asyncpg = "^0.30.0"
sqlalchemy = "^2.0.30"
# Held at 0.3 deliberately. 0.4 requires langchain-classic and drops
# langchain_community.chat_models.vertexai -- which ragas 0.4.3 still imports
# unconditionally at ragas/llms/base.py:12, so langchain-community 0.4 makes
# `import ragas` a ModuleNotFoundError and takes ./bin/evaluate with it. ragas
# declares langchain-community with no version bound, so the resolver cannot
# see the conflict; only importing it does.
#
# 0.3.31 accepts langchain-core <2.0.0,>=0.3.78, so it runs against core 1.x.
# The app uses it for BM25Retriever, the CSV/directory loaders and
# OpenAICallbackHandler. Lift when ragas stops importing the vertexai module.
langchain-community = "^0.3.31"
langchain-core = "^1.6.2"
langchain-huggingface = "^1.2.2"
# Capped, and coupled to the torch pin below. transformers 5 requires torch>=2.5
# and, finding 2.4.1, disables PyTorch entirely rather than failing:
#
# [transformers] Disabling PyTorch because PyTorch >= 2.5 is required but
# found 2.4.1+cpu
#
# get_embedding("huggingfacelocal", ...) then raises `NameError: name 'nn' is
# not defined` deep inside sentence-transformers. Lifting this cap means moving
# the torch pin first -- which is a separate change, because torch carries
# platform markers and a custom CPU wheel source.
transformers = "<5"
einops = "^0.8.0"
boto3 = "^1.34.148"
torch = [
{ version = "2.4.1+cpu", markers = "sys_platform == 'linux' and platform_machine == 'x86_64'", source = "pytorch_cpu" },
{ version = "2.2.*", markers = "sys_platform != 'linux' or platform_machine != 'x86_64'", source = "PyPI" },
]
# 0.2.x, because 1.x requires chromadb>=1.3.5 -- see the chromadb note above.
# 0.2.3 declares langchain-core>=0.3.52 with no upper bound, so it runs against
# core 1.x.
langchain-chroma = "^0.2.3"
langchain-ollama = "^1.1.0"
lark = "^1.2.2"
langgraph = "^1.2.11"
langgraph-checkpoint-postgres = "^3.1.2"
rank-bm25 = "^0.2.2"
psycopg = {extras = ["binary"], version = "^3.2.3"}
pydantic = "^2.10.5"
pyyaml = "^6.0.2"
tavily-python = "^0.5.0"
openpyxl = "^3.1.5"
# Vault, for short-lived Postgres credentials. Only imported when a Vault token
# is actually mounted (see util/secrets.py), so a deployment without Vault
# carries the dependency but never loads it.
hvac = "^2.3.0"
requests-unixsocket2 = "^1.0.0"
nltk = "^3.9.1"
beautifulsoup4 = "^4.12.0"
lxml = "^6.1.0"
requests = "^2.32.0"
# Not imported here; it arrives via chromadb, and is constrained only so the
# Intel-macOS leg of CI can resolve. onnxruntime stopped publishing
# macosx x86_64 wheels after 1.23.2 -- 1.24 onwards are arm64 and Linux only --
# so poetry fails there with "Unable to find installation candidates for
# onnxruntime". Split by marker rather than capped outright, as torch is below,
# so the platform that actually ships keeps getting new versions.
onnxruntime = [
{ version = "<1.24", markers = "sys_platform == 'darwin' and platform_machine == 'x86_64'" },
{ version = "*", markers = "sys_platform != 'darwin' or platform_machine != 'x86_64'" },
]
[tool.poetry.group.dev.dependencies]
ruff = "^0.7.1"
pytest = "^8.3.3"
mypy = "^1.13.0"
pandas-stubs = "^2.2.3.241009"
types-requests = "^2.32.0.20241016"
types-pyyaml = "^6.0.12.20241230"
datasets = "^4.0.0"
ragas = "^0.4.3"
[[tool.poetry.source]]
name = "PyPI"
priority = "primary"
[[tool.poetry.source]]
name = "pytorch_cpu"
url = "https://download.pytorch.org/whl/cpu"
priority = "explicit"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.ruff]
line-length = 88
target-version = "py312"
src = ["src", "bin"]
extend-exclude = ["embeddings", "records", "data", ".venv"]
[tool.ruff.lint]
# One rule set for the whole repo. E501 is the formatter's job -- it cannot split
# long string literals anyway -- and RUF001 fires on intentional typography inside
# LLM prompt text.
select = [
"E", "F", "I", "UP", "B", "SIM", "RUF", # the core set
"S", # bandit: security
"C4", # comprehensions
"RET", # return-statement hygiene
"PIE", "PT", "ISC", "INT", "ICN", "TID", "A", "LOG", "G", "ERA", "N",
]
ignore = ["E501", "RUF001", "ISC001"]
# Not yet enforced. Each needs a decision rather than a mechanical fix, so they
# are listed here instead of being silently dropped:
# PTH ~41 os.path -> pathlib rewrites; mechanical but touches files the
# retriever work will rewrite. Worth its own commit afterwards.
# DTZ datetime.now() without tzinfo. The rate limiter and static-message
# throttle deliberately store naive local timestamps in user metadata;
# changing that is a storage-format decision, not a lint fix.
# ARG unused arguments -- several are required by LangGraph node signatures.
# BLE blind `except Exception` in top-level scripts.
# C901 two functions over the complexity threshold.
[tool.ruff.lint.isort]
known-first-party = [
"agent",
"data_generation",
"evaluation",
"retrievers",
"tools",
"util",
]
[tool.ruff.lint.per-file-ignores]
# Entry-point scripts intentionally configure the app at import time.
"bin/*" = ["E402"]
# assert is how pytest asserts.
"tests/*" = ["S101"]
[tool.ruff.format]
docstring-code-format = true
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src", "bin"]
addopts = "-q --strict-markers --strict-config"
markers = [
"requires_embeddings: needs an installed embeddings bundle (see ./bin/embeddings_manager)",
"requires_retrieval_stack: needs the full langchain/chromadb dependency set installed",
"requires_live_model: calls a real LLM and needs OPENAI_API_KEY plus embeddings",
]
[tool.mypy]
python_version = "3.12"
files = ["bin", "src", "tests", ".github", "export_csvs.py"]
exclude = "^(data|embeddings|records)/"
mypy_path = "src"
explicit_package_bases = true
ignore_missing_imports = true
# --- ratchet: currently-enforced floor (the code already largely satisfies this) ---
check_untyped_defs = true
no_implicit_optional = true
strict_equality = true
warn_redundant_casts = true
# Annotation coverage reached 100%, so these are enforced repo-wide rather than
# per-module. There is now one standard for every file.
disallow_untyped_defs = true
disallow_incomplete_defs = true
warn_no_return = true
warn_return_any = true
extra_checks = true
# Free: both already pass with zero errors, so they cost nothing to hold and
# stop the corresponding slippage from arriving unnoticed.
warn_unused_configs = true
disallow_subclassing_any = true
# A stale ignore is a claim about the code that is no longer true. There were
# five, all in one test file, all obsolete.
warn_unused_ignores = true
# Names now come from where they are defined rather than from whichever module
# happened to import them first: ProfileName from agent.profile_names,
# TriggerEvent from util.config_yml.messages, SharedSystemClient from
# chromadb.api.shared_system_client. The one deliberate re-export, `logging`
# from util.logging, is declared in that module's __all__ -- importing it is
# what runs dictConfig, so callers take the logger through it on purpose.
no_implicit_reexport = true
# Enabled repo-wide, with three modules exempted below. Every violation is a
# call into an untyped third-party API rather than anything we could annotate,
# so exempting those three keeps the rule meaningful everywhere else: new
# untyped calls cannot appear in ordinary code.
disallow_untyped_calls = true
# disallow_any_generics is the one strict setting still off. Every bare builtin
# it flagged is now parameterised; the 43 left are all framework generics --
# Runnable (29), StateGraph (9), CompiledStateGraph (3), RunnableLambda and
# BasePromptTemplate. Turning it on means writing the real input and output
# type of every chain and graph, and a guess there encodes a type that is
# wrong rather than absent, which is worse than the bare form. It needs doing
# per chain, with the chain in front of you.
# disallow_untyped_decorators stays off: chainlit's @cl.* decorators are
# untyped upstream, so it only ever fires on bin/chat-chainlit.py.
# Untyped third-party surfaces, not our debt. chainlit ships no annotations for
# `cl.user_session.get/set`, `cl.Message.send` or `get_data_layer`, and boto3's
# `Session` is untyped without boto3-stubs. Nothing here is fixable by
# annotating our own code; it needs stubs upstream or a typed wrapper of our
# own, which is a change with its own design rather than a lint fix.
# bin/chat-chainlit.py carries the same exemption as an inline directive: its
# hyphenated name cannot appear in an overrides section.
[[tool.mypy.overrides]]
module = ["util.chainlit_helpers", "util.secrets"]
disallow_untyped_calls = false
# --- mypy baseline -------------------------------------------------------------
# Modules that do not yet meet the floor above. Each entry is debt: fix the module,
# then delete its block. Do not add to this list without a TODO.
[[tool.mypy.overrides]]
# TODO(phase-2): every node returns a partial state; the TypedDict needs
# `total=False` or per-node Partial* types.
module = ["agent.profiles.cross_database"]
disable_error_code = ["typeddict-item", "no-any-return"]
[[tool.mypy.overrides]]
# A module may appear in only one override section, so both TODOs live here.
# TODO(phase-2): partial-state returns, as for cross_database above.
# TODO(phase-2): preprocess() narrows BaseState to ReactToMeState, which is an LSP
# violation -- parameters may widen, not narrow. The fix is to make BaseGraphBuilder
# generic in its state type (BaseGraphBuilder[StateT]) so each profile declares its
# own. Deferred because base.py is also modified by the safetycheck and analysis
# branches, and a generics refactor there would collide with both.
module = ["agent.profiles.react_to_me"]
disable_error_code = ["typeddict-item", "override"]