Files
cleveragents-core/pyproject.toml
T
CoreRasurae 4d3499dcfb
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 16s
CI / build (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 18s
CI / security (pull_request) Successful in 35s
CI / typecheck (pull_request) Successful in 38s
CI / unit_tests (pull_request) Successful in 2m43s
CI / integration_tests (pull_request) Successful in 3m20s
CI / docker (pull_request) Successful in 51s
CI / coverage (pull_request) Successful in 6m18s
CI / build (push) Successful in 14s
CI / lint (push) Successful in 21s
CI / quality (push) Successful in 29s
CI / typecheck (push) Successful in 35s
CI / security (push) Successful in 36s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 3m17s
CI / docker (push) Successful in 9s
CI / integration_tests (push) Successful in 3m29s
CI / coverage (push) Successful in 7m3s
CI / benchmark-publish (push) Successful in 19m50s
CI / benchmark-regression (pull_request) Successful in 40m6s
feat(async): wire retry policies into services
Wire per-service retry policies and circuit breakers into the service
layer via ServiceRetryWiring, backed by ServiceRetryPolicyRegistry and
configurable through Settings environment variables.

Production hardening from code review:
- Fix TOCTOU race in CircuitBreaker._on_success (half-open state)
- Add half-open probe limit to prevent unbounded concurrent requests
- Track all exception types for circuit breaker failure counting
- Detect async callables wrapped in functools.partial and callable objects
- Enforce spec-compliant 2s minimum for linear backoff strategy
- Enforce 0.1s floor for fixed backoff strategy
- Add retry amplification guard via contextvars nesting depth tracking
- Cap total retry wall-clock time at 300s (MAX_RETRY_TOTAL_TIMEOUT)
- Sanitize exception messages in retry logs to prevent secret leakage
- Fix wrap_service_method TOCTOU by holding cache lock for full operation
- Deep-copy default policies to prevent cross-policy mutation
- Warn on unknown override keys in apply_overrides
- Guard apply_overrides against non-dict and deeply nested JSON values
- Read circuit breaker state under lock in is_circuit_open
- Catch RecursionError in JSON config parsing
- Add total_timeout + nesting guard to retry_service_operation decorator
- Extend secret sanitization to Authorization headers, private_key,
  connection_string, and access_key patterns
- Enforce 0.1s floor on jitter backoff strategy
- Cache wait strategies per service in ServiceRetryWiring (M3)
- Reset failure_count to 0 when entering half-open from open (M6)
- Use cached _get_wait_strategy() in execute()/async_execute()
- Move circuit-open logging out of _on_failure lock scope to prevent
  holding the lock during potentially slow I/O (F1)
- Pass total_timeout=MAX_RETRY_TOTAL_TIMEOUT to wrap_service_method
  retry_service_operation call for consistency with execute() (F4)
- Capture failure_count into local variable inside lock scope before
  logging outside the lock, preventing stale reads from concurrent
  threads in CircuitBreaker.call() and async_call() (F1)
- Deep-copy module-level DEFAULT_DATABASE_RETRY and DEFAULT_CIRCUIT_BREAKER
  in ServiceRetryPolicyRegistry.get() for auto-generated unknown service
  policies, preventing shared mutable state corruption (F1)
- Unify CircuitBreaker to a single threading.Lock for sync and async (P1-1)
- Restore BaseException permit in half-open path to prevent permit leak (P1-6)
- Prevent CircuitBreakerOpen cascading into failure_count (S2)
- Protect all logger calls with contextlib.suppress (S3, S4)
- Replace time.time() with time.monotonic() for monotonic timing (S5)
- Add distinct log events for half-open and closed transitions (S11, S12)
- Track pre-existing services so second apply_settings_defaults only
  targets newly registered services (P1-2)
- Lazy circuit breaker creation via _get_or_create_cb() (P1-3)
- Reject async callables in sync execute() with TypeError (P1-5)
- Strengthen retry predicate to retry_if_exception_type(Exception) &
  retry_if_not_exception_type(CircuitBreakerOpen) (S1)
- Add lock on _get_wait_strategy cache access (P2-16)
- Truncate raw JSON to 80 chars in override warning (P2-17)
- Warn on non-dict JSON overrides (P2-29)
- Debug log for nesting guard bypass (S13)
- Deep-copy from get() and all_policies() in registry (P1-4)
- Thread-safe registry with threading.Lock (P2-15)
- Robust exception handling in apply_overrides get() (P2-18)
- Log ValidationError details on override failure (P2-19)
- Sanitize service_name via _safe_service_name() (P2-28)
- Warn on non-dict sub-key values in overrides (P2-30)
- Allowlist for is_read_only_plan_operation phases (P2-10)
- Cap retry_auto_debug sleep at 60s (P2-11)
- Use is-not-None instead of falsy checks for error values (P2-12)
- Extend secret regex with bearer, session_id, auth_token,
  refresh_token, client_secret patterns (P2-25)
- Pre-truncate error messages to 2000 chars before regex (P2-26)
- Add upper bounds on retry Settings fields (P2-7)
- Add cross-field validator max_delay >= base_delay (P2-8)
- Case-insensitive backoff strategy validation (P2-21)
- Add half_open_max_successes setting (S10)
- Remove phantom ContextFragment from services __all__ (ImportError fix)
- Export ServiceRetryWiring from application.services package
- Include sanitised error context in TypeError logging fallback
- Initialise RetryContext.attempt_count to 1 for bare context-manager usage
- Introduce CircuitBreakerState StrEnum replacing raw string literals
- Fix vacuous CircuitBreakerOpen propagation assertions in BDD steps
- Replace tautological logging test with structlog capture verification
- Assert circuit breaker existence instead of silently skipping on None
- Add Unicode control-char rejection validator to ServiceRetryPolicy.service_name
- Add name parameter with service= in all log calls
- Add extra="forbid" to all 3 Pydantic models
- Deep-copy _SERVICE_DEFAULTS construction
- Key normalisation (.strip()) in get() and apply_overrides()
- Add cooldown <= recovery_timeout validator
- Async guard on RetryContext.execute()
- Nesting guard on RetryContext.execute()/async_execute()
- stop_after_delay(300.0) on RetryContext
- retry_auto_debug async-only guard, dict result fix, sleep guard
- Retry-attempt logging in RetryContext
- Module-level docs for contextlib.suppress(TypeError) rationale
- Exhaustion log on retry failure
- Startup log in __init__; name=service_name to CircuitBreaker
- log_after_retry guarded to not fire on first-attempt success
- get_retry_decorator now includes logging callbacks
- Changed retry_backoff_strategy from str to RetryStrategy StrEnum

Closes #313
2026-03-11 17:42:13 +00:00

229 lines
6.0 KiB
TOML

[build-system]
requires = ["hatchling>=1.21.0"]
build-backend = "hatchling.build"
[project]
name = "cleveragents"
version = "1.0.0"
description = "CleverAgents CLI and runtime toolkit"
readme = "README.md"
requires-python = ">=3.13"
license = {text = "MIT"}
authors = [
{name = "CleverThis Engineering", email = "engineering@cleverthis.com"},
]
keywords = ["cleveragents", "ai", "cli", "automation"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.13",
]
dependencies = [
# CLI Framework (ADR-009)
"typer>=0.9.0",
"uvicorn>=0.30.1",
"watchdog>=4.0.0",
"faiss-cpu>=1.7.4", # Vector store backend
"rx>=3.2.0", # Reactive streams for routing
"dependency-injector>=4.41.0", # DI container
"pydantic>=2.7.0",
"pydantic-settings>=2.11.0",
"structlog>=24.4.0",
"langchain>=0.2.14",
"langchain-anthropic>=0.2.0",
"langchain-community>=0.2.14",
"langchain-anthropic>=0.2.0",
"langchain-openai>=0.2.0",
"langchain-google-genai>=0.2.0",
"jinja2>=3.1.0",
"alembic>=1.13.1",
"numpy>=2.1.0",
"python-ulid>=2.7.0", # ULID generation for plan/action IDs
"RestrictedPython>=7.0", # Secure sandbox for user-supplied code
"jsonschema>=4.20.0", # JSON Schema validation for tool inputs/outputs
"tomlkit>=0.13.0", # TOML writing with comment preservation for config CLI
"tenacity>=8.2.0", # Retry framework for service layer resilience
]
[project.optional-dependencies]
dev = [
# Code formatting and linting
"ruff>=0.15.0,<0.16.0",
# Type checking
"pyright>=1.1.350",
"types-pyyaml>=6.0.0",
"types-aiofiles>=23.0.0",
# Testing
"behave==1.3.3",
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"pytest-cov>=4.1.0",
# Pre-commit hooks
"pre-commit>=3.6.0",
# Security scanning
"bandit[toml]>=1.7.5",
"semgrep>=1.60.0",
# Dead code detection
"vulture>=2.10",
# Complexity metrics
"radon>=6.0.1",
]
tests = [
"behave==1.3.3",
"slipcover>=1.0.17",
"asv>=0.6.5",
"robotframework>=7.3.2",
"robotframework-pabot>=4.0.0",
]
docs = [
"mkdocs>=1.6.1",
"mkdocs-material>=9.6.0",
"mkdocstrings[python]>=0.24.0",
"mkdocs-kroki-plugin>=1.2.0",
"mkdocs-gen-files>=0.5.0",
"mkdocs-literate-nav>=0.6.0",
"griffe-pydantic>=1.0.0",
"mkdocs-click>=0.8.0",
"ruff>=0.4.0",
]
[project.urls]
Homepage = "https://cleverthis.com/cleveragents"
Documentation = "https://docs.cleverthis.com/cleveragents"
Repository = "https://git.cleverthis.com/cleveragents/core"
Issues = "https://git.cleverthis.com/cleveragents/core/issues"
[project.scripts]
cleveragents = "cleveragents.cli:main"
agents = "cleveragents.cli:main"
[tool.hatch.build.targets.wheel]
packages = ["src/cleveragents"]
include = ["src/cleveragents/py.typed"]
[tool.ruff]
line-length = 88
target-version = "py313" # Target Python 3.13
src = ["src", "tests", "benchmarks"]
extend-exclude = ["docs/reference/contracts/stubs/*.py", "scripts/*.sh"]
[tool.ruff.lint]
select = ["E", "F", "W", "B", "UP", "I", "SIM", "RUF"]
ignore = []
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
# Behave step files: F811 = redefined step_impl (Behave pattern), E501 = long step decorator strings
"features/steps/*.py" = ["F811", "E501"]
"features/mocks/*.py" = ["E501"]
"features/environment.py" = ["E501"]
# retry_patterns.py re-exports symbols from retry_service_patterns at module bottom
"src/cleveragents/core/retry_patterns.py" = ["E402"]
[tool.ruff.format]
# Use double quotes for strings
quote-style = "double"
# Indent with 4 spaces
indent-style = "space"
# Unix line endings
line-ending = "auto"
[tool.pyright]
include = ["src"]
exclude = ["**/.nox", "**/__pycache__", "**/site-packages", "src/cleveragents/discovery"]
pythonVersion = "3.13" # Target Python 3.13
typeCheckingMode = "strict"
strictListInference = true
strictDictionaryInference = true
strictSetInference = true
reportMissingImports = true
reportMissingTypeStubs = false
reportMissingTypeArgument = true
reportIncompatibleMethodOverride = true
reportIncompatibleVariableOverride = true
reportUnusedImport = true
reportUnusedClass = true
reportUnusedFunction = true
reportUnusedVariable = true
reportDuplicateImport = true
reportOptionalMemberAccess = true
reportOptionalCall = true
reportOptionalIterable = true
reportOptionalContextManager = true
reportOptionalOperand = true
reportTypedDictNotRequiredAccess = true
reportUnnecessaryIsInstance = true
reportUnnecessaryCast = true
reportUnnecessaryComparison = true
reportImplicitStringConcatenation = false
reportUnusedCallResult = false
reportUnknownMemberType = false
reportUnknownVariableType = false
reportUnknownParameterType = false
[tool.coverage]
# Coverage configuration
[tool.coverage.run]
source = ["src", "scripts"]
branch = true
parallel = false
omit = [
"*/tests/*",
"*/test_*",
"features/*",
"*/features/*",
"*/__pycache__/*",
"*/site-packages/*",
"*/dependency_injector/*",
"*/venv/*",
"*/.venv/*",
"*/.nox/*",
"src/cleveragents/discovery/*",
]
data_file = "build/.coverage"
[tool.coverage.html]
directory = "build/htmlcov"
[tool.coverage.xml]
output = "build/coverage.xml"
[tool.bandit]
targets = ["src/cleveragents"]
exclude_dirs = [
"tests",
"features",
".nox",
"build",
"dist",
"docs",
"src/cleveragents/discovery",
]
# Severity level: LOW, MEDIUM, HIGH
severity = "MEDIUM"
# Confidence level: LOW, MEDIUM, HIGH
confidence = "MEDIUM"
# Skip specific tests if needed
skips = []
[tool.vulture]
min_confidence = 80
paths = ["src/cleveragents"]
exclude = ["src/cleveragents/discovery"]
[tool.hatch]
# Hatch build configuration
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "$version"
version_scheme = "pep440"
version_provider = "pep621"
update_changelog_on_bump = true
major_version_zero = true