[build-system] requires = ["hatchling>=1.27,<1.33"] build-backend = "hatchling.build" [project] name = "entroly" version = "1.0.84" description = "Drop-in Context Assurance for AI agents: reduce avoidable token usage and AI cost with evidence selection, receipts, exact recovery, and verification." readme = "PYPI_README.md" license = "Apache-2.0" license-files = ["LICENSE", "NOTICE"] logo = { file = "docs/assets/logo.png", content-type = "image/png" } requires-python = ">=3.10" authors = [ { name = "entroly", email = "fastrunner10090@gmail.com" }, ] keywords = [ "ai-agents", "ai-coding-agents", "ai-cost-optimization", "llm-cost-optimization", "reduce-ai-costs", "context-assurance", "context-engineering", "context-management", "context-optimization", "context-compression", "prompt-compression", "token-optimization", "token-reduction", "context-window", "context-receipts", "content-addressed-recovery", "context-control-plane", "llm-proxy", "local-models", "mcp", "mcp-server", "model-context-protocol", "github-copilot", "cursor", "claude-code", "codex", "openclaw", "hermes-agent", "opencode", "hallucination-detection", "agent-memory", "llm" ] 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", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Scientific/Engineering :: Artificial Intelligence", ] dependencies = [ "mcp>=1.28.1,<2", "httpx>=0.28.1", "starlette>=1.3.1", "uvicorn>=0.51.0", # The native engine is a REQUIRED dependency, not an extra. # # Query-conditioned selection (QCCR) is gated on the native module: the # candidate set comes from `self._rust.export_fragments()`, which has no # pure-Python equivalent, so `optimize_context` skips relevance ranking # entirely without it (server.py). The measured effect of shipping this as # an extra: three unrelated queries -- including a nonsense control -- # returned a byte-identical selection (23 fragments / 7,588 tokens) and the # same "76.29% saved", because the reported figure is baseline-minus-budget # arithmetic and nothing depended on the query. Every accuracy benchmark in # this repo (needle/longbench/squad/bfcl/mmlu/gsm8k/truthfulqa) measures the # QCCR path, so an install without it does not run the code that was # benchmarked. # # `pip install entroly` is the command in the README, the quickstart, and # every integration doc. It must produce the engine those docs describe. # abi3 wheels are published for macOS universal2, glibc+musl Linux # x86_64/aarch64, and Windows x64; the pure-Python fallback remains for any # platform without a wheel. "entroly-core>=1.0.84,<2", # Required, not optional: it is the only reader for the governance policy # file. # # `governance/policy.py` is the sole yaml importer, and it degrades to the # built-in deny-by-default policy when the import fails -- logged, never # raised. PyYAML was declared in no dependency group and is not pulled in # transitively, so on a default `pip install entroly` an operator's # policies.yaml was read by nothing: every agent ran read-only regardless # of what the file said, and `load_policies` returned as if it had loaded. # # Failing to the restrictive side is right; doing it silently is not. # `policy_source_status` now names the reason, and this pin means the # answer is no longer "the parser is missing". # # Longer term this parsing belongs in entroly-engine with the rest of the # governance computation, so Python, Node, and WASM cannot disagree about # what a policy says -- the same reason identity tokens are computed there. "pyyaml>=6.0,<7", ] [project.urls] Homepage = "https://juyterman1000.github.io/entroly/" Documentation = "https://juyterman1000.github.io/entroly/" Source = "https://github.com/juyterman1000/entroly" Issues = "https://github.com/juyterman1000/entroly/issues" "Release Notes" = "https://github.com/juyterman1000/entroly/releases" [project.optional-dependencies] # Retained as a compatibility alias for existing install instructions. The # public proxy/go commands now work from a plain base install. proxy = [ "httpx>=0.28.1", "starlette>=1.3.1", "uvicorn>=0.51.0", ] # Retained as a compatibility alias for existing install instructions. The # native engine now ships with the base install, so `entroly[native]` and a # plain `entroly` resolve to the same thing. native = [ "entroly-core>=1.0.84,<2", ] receipt-proof = [ "cryptography>=42", ] code-intelligence = [ "tree-sitter-language-pack>=1.14.3,<2", ] images = [ "Pillow>=10,<13", ] browser = [ "playwright>=1.49,<2", ] full = [ "cryptography>=42", "entroly-core>=1.0.84,<2", "httpx>=0.28.1", "starlette>=1.3.1", "uvicorn>=0.51.0", "tree-sitter-language-pack>=1.14.3,<2", "playwright>=1.49,<2", ] test = [ "pytest>=9.0.3,<10", "pytest-timeout>=2.3,<3", "tiktoken>=0.9,<0.15", ] benchmark = [ "tiktoken>=0.9,<0.15", ] neural = [ "sentence-transformers>=5.6.1,<7", ] neural-benchmark = [ "datasets>=4,<6", "sentence-transformers>=5.6.1,<7", ] [project.scripts] entroly = "entroly.docker_launcher_safe:launch" entroly-memory = "entroly.memory_cli:main" entroly-compression-mcp = "entroly.compression_mcp:main" entroly-repository-mcp = "entroly.repository_intelligence.mcp:main" entroly-work = "entroly.work_graph_cli:main" entroly-work-graph-mcp = "entroly.work_graph_mcp_server:main" [tool.pytest.ini_options] testpaths = ["tests"] [tool.hatch.build.targets.wheel] core-metadata-version = "2.4" [tool.hatch.build.targets.sdist] core-metadata-version = "2.4" [tool.ruff] target-version = "py310" [tool.ruff.lint] select = ["E", "F", "W"] ignore = [ "UP006", # Use `dict` instead of `Dict` — we keep typing.Dict for 3.9 compat "UP007", # Use `X | Y` instead of `Union[X, Y]` "UP035", # Import from `collections.abc` instead of `typing` "UP045", # Use `X | None` instead of `Optional[X]` "E501", # Line too long — we use descriptive print strings "F401", # Unused import — some are used conditionally "E722", # Bare except — used intentionally for resilient CLI "W605", # Invalid escape sequence — some are in regex patterns ] [tool.ruff.lint.per-file-ignores] "tests/*" = ["F811"] # Redefined names in test fixtures "entroly/context_check.py" = ["E731"] # Local formatter lambda is intentionally compact