A memory system that only answers from its own store is a filing cabinet, not a partner. The transactive rule the project is named for — not knowing is fine when you know who does — needed a holder registry, a directory query, and a referral that never leaks the text it may not read. - schema v5 adds `holders` (person/agent/service, declared topics, contact). A holder with memories exists whether or not anybody registered them; the registry adds reachability, it does not grant existence. - `who_knows` ranks holders by their own memories and is deliberately NOT filtered by visibility: "she knows" is the answer. The answer is filtered. - `ask` searches the asker's visible scope first (hop 0), then walks the directory with a visited set and a hop budget. Three terminal states: answered, referred, no_holder_knows. Nobody knowing is a result, not an error. - Confidence is ordinal, not calibrated: relative strength, coverage, and declared topics. Every hit carries the raw numbers and the basis lines that produced the score, so a ranking can be argued with. - Faces: HTTP (/api/holders, /api/who_knows, /api/ask), CLI (holders, holder-set, holder-rm, who-knows, ask), MCP (who_knows, ask, list_holders). Four bugs found by running the thing, not by reading it: - FTS5 refuses bm25() inside MAX(); the aggregate uses FTS5's own `rank` column. - A CJK question is longer than the sentence that answers it, so the whole-run phrase matched nothing. The query now ORs adjacent bigram phrases; adjacency is kept, equal length is not. Latin path untouched, baseline unaffected. - The hop budget counted the asker as a hop, so max_hops=1 asked nobody. - A second, stricter readability test in _answered() disagreed with the SQL visibility clause and hid a memory the asker was allowed to read. The SQL clause is the only authority now. A method named `list` shadowed the builtin inside its own class and made mypy reject the return annotation; renamed rather than cast around it. Proof: 155 pytest, 12 Go tests, ruff and mypy clean on touched files, pyright ledger unchanged at 62, and scripts/mcp_write_smoke.sh drives two principals on one SQLite file — the husband asks through MCP, gets `referred` with his wife's contact channel, and her private sentence appears nowhere in the reply.
58 lines
1.3 KiB
TOML
58 lines
1.3 KiB
TOML
[build-system]
|
|
requires = ["setuptools>=80", "wheel"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "ely-eye"
|
|
version = "0.1.0"
|
|
description = "Local multimodal context operating system for Qwen3.5-9B."
|
|
readme = "README.md"
|
|
requires-python = ">=3.12"
|
|
dependencies = [
|
|
"accelerate>=1.12.0",
|
|
"bitsandbytes>=0.49.2",
|
|
"datasets>=4.8.5",
|
|
"fastapi>=0.124.0",
|
|
"huggingface-hub>=1.2.0",
|
|
"httpx>=0.28.0",
|
|
"networkx>=3.6.0",
|
|
"ninja>=1.13.0",
|
|
"numpy>=2.3.0",
|
|
"opencv-python-headless>=4.12.0.0",
|
|
"orjson>=3.11.0",
|
|
"peft>=0.19.0",
|
|
"pillow>=12.0.0",
|
|
"platformdirs>=4.5.0",
|
|
"pyarrow>=22.0.0",
|
|
"pydantic>=2.12.0",
|
|
"pydantic-settings>=2.12.0",
|
|
"pymupdf>=1.26.0",
|
|
"python-multipart>=0.0.20",
|
|
"qwen-vl-utils>=0.0.14",
|
|
"rank-bm25>=0.2.2",
|
|
"rapidocr-onnxruntime>=1.2.3,<2",
|
|
"safetensors>=0.7.0",
|
|
"sentence-transformers>=5.2.0",
|
|
"torch>=2.12.0",
|
|
"torchvision>=0.27.0",
|
|
"transformers>=4.57.0",
|
|
"typer>=0.20.0",
|
|
"uvicorn[standard]>=0.38.0",
|
|
"zstandard>=0.25.0"
|
|
]
|
|
|
|
[project.scripts]
|
|
ely-eye = "ely_eye.cli:app"
|
|
|
|
[tool.setuptools.packages.find]
|
|
where = ["src"]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py312"
|
|
|
|
[tool.ruff.lint]
|
|
# Typer requires an option default to be a typer.Option call, which is what B008 forbids.
|
|
ignore = ["B008"]
|
|
|