mirror of
https://github.com/usestrix/strix.git
synced 2026-08-16 01:16:40 +02:00
Some OpenAI-compatible gateways don't support Server-Sent Events (or deliver them unreliably), but the SDK run loop Strix uses only issues streamed requests, so such a gateway fails every turn. Add an opt-in LLM_DISABLE_STREAMING setting that wraps the resolved model in _NonStreamingModel: each turn makes one non-streaming get_response and replays the completed result as a single terminal stream event, so tool calls, usage, and the rest of the agent loop are unchanged. Subscription (ChatGPT) models are always streamed and are not wrapped.
372 lines
12 KiB
TOML
372 lines
12 KiB
TOML
[project]
|
|
name = "strix-agent"
|
|
version = "1.4.1"
|
|
description = "Open-source AI Hackers for your apps"
|
|
readme = "README.md"
|
|
license = "Apache-2.0"
|
|
requires-python = ">=3.12"
|
|
authors = [
|
|
{ name = "Strix", email = "hi@usestrix.com" },
|
|
]
|
|
keywords = [
|
|
"cybersecurity",
|
|
"security",
|
|
"vulnerability",
|
|
"scanner",
|
|
"pentest",
|
|
"agent",
|
|
"ai",
|
|
"cli",
|
|
]
|
|
classifiers = [
|
|
"Development Status :: 3 - Alpha",
|
|
"Intended Audience :: Information Technology",
|
|
"Intended Audience :: System Administrators",
|
|
"Topic :: Security",
|
|
"License :: OSI Approved :: Apache Software License",
|
|
"Environment :: Console",
|
|
"Programming Language :: Python",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3 :: Only",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Programming Language :: Python :: 3.14",
|
|
]
|
|
dependencies = [
|
|
"openai-agents[litellm]==0.14.6",
|
|
"openai>=2.26.0,<2.45",
|
|
"litellm",
|
|
"pydantic>=2.11.3",
|
|
"pydantic-settings>=2.13.0",
|
|
"rich",
|
|
"docker>=7.1.0",
|
|
"textual>=6.0.0",
|
|
"requests>=2.32.0",
|
|
"cvss>=3.2",
|
|
"caido-sdk-client>=0.2.0",
|
|
"reportlab>=4.0",
|
|
"pypdf>=5.0",
|
|
# Cap <49: 49.x drops the universal2 macOS wheel (arm64-only), which breaks
|
|
# the Intel macOS (macos-x86_64) release build's `uv sync --frozen`.
|
|
"cryptography>=48.0.1,<49",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
vertex = ["google-auth>=2.0.0"]
|
|
bedrock = ["boto3>=1.28.0"]
|
|
|
|
[project.scripts]
|
|
strix = "strix.interface.main:main"
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"mypy>=1.16.0",
|
|
"ruff>=0.11.13",
|
|
"pyright>=1.1.401",
|
|
"bandit>=1.8.3",
|
|
"pre-commit>=4.2.0",
|
|
"pyinstaller>=6.17.0; python_version >= '3.12' and python_version < '3.15'",
|
|
"pytest>=8.3",
|
|
"pytest-asyncio>=0.24",
|
|
]
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["strix"]
|
|
# The prebuilt viewer bundle under strix/interface/viewer/static/ ships automatically
|
|
# (hatchling includes non-.py files under the package). The Vite SOURCE lives
|
|
# under the package dir too (strix/interface/viewer/frontend/) but must never ship in the wheel.
|
|
exclude = ["strix/interface/viewer/frontend", "strix/interface/viewer/frontend/**"]
|
|
|
|
# ============================================================================
|
|
# Type Checking Configuration
|
|
# ============================================================================
|
|
|
|
[tool.mypy]
|
|
python_version = "3.12"
|
|
strict = true
|
|
strict_optional = true
|
|
warn_redundant_casts = true
|
|
warn_unused_ignores = true
|
|
warn_return_any = true
|
|
warn_unreachable = true
|
|
disallow_untyped_defs = true
|
|
disallow_any_generics = true
|
|
disallow_subclassing_any = true
|
|
disallow_untyped_calls = true
|
|
disallow_incomplete_defs = true
|
|
check_untyped_defs = true
|
|
disallow_untyped_decorators = true
|
|
no_implicit_optional = true
|
|
warn_unused_configs = true
|
|
show_error_codes = true
|
|
show_column_numbers = true
|
|
pretty = true
|
|
|
|
# Allow some flexibility for third-party libraries
|
|
[[tool.mypy.overrides]]
|
|
module = [
|
|
"litellm.*",
|
|
"rich.*",
|
|
"jinja2.*",
|
|
"textual.*",
|
|
"cvss.*",
|
|
"docker.*",
|
|
"caido_sdk_client.*",
|
|
"pydantic_settings.*",
|
|
"reportlab.*",
|
|
"pypdf.*",
|
|
"pygments.*",
|
|
]
|
|
ignore_missing_imports = true
|
|
disable_error_code = ["import-untyped"]
|
|
|
|
[[tool.mypy.overrides]]
|
|
module = ["tests.*"]
|
|
disallow_untyped_decorators = false
|
|
|
|
# ============================================================================
|
|
# Ruff Configuration (Fast Python Linter & Formatter)
|
|
# ============================================================================
|
|
|
|
[tool.ruff]
|
|
target-version = "py312"
|
|
line-length = 100
|
|
extend-exclude = [
|
|
".git",
|
|
".mypy_cache",
|
|
".ruff_cache",
|
|
"__pycache__",
|
|
"build",
|
|
"dist",
|
|
"migrations",
|
|
]
|
|
|
|
[tool.ruff.lint]
|
|
# Enable comprehensive rule sets
|
|
select = [
|
|
"E", # pycodestyle errors
|
|
"W", # pycodestyle warnings
|
|
"F", # Pyflakes
|
|
"I", # isort
|
|
"N", # pep8-naming
|
|
"UP", # pyupgrade
|
|
"YTT", # flake8-2020
|
|
"S", # flake8-bandit
|
|
"BLE", # flake8-blind-except
|
|
"FBT", # flake8-boolean-trap
|
|
"B", # flake8-bugbear
|
|
"A", # flake8-builtins
|
|
"COM", # flake8-commas
|
|
"C4", # flake8-comprehensions
|
|
"DTZ", # flake8-datetimez
|
|
"T10", # flake8-debugger
|
|
"EM", # flake8-errmsg
|
|
"FA", # flake8-future-annotations
|
|
"ISC", # flake8-implicit-str-concat
|
|
"ICN", # flake8-import-conventions
|
|
"G", # flake8-logging-format
|
|
"INP", # flake8-no-pep420
|
|
"PIE", # flake8-pie
|
|
"T20", # flake8-print
|
|
"PYI", # flake8-pyi
|
|
"Q", # flake8-quotes
|
|
"RSE", # flake8-raise
|
|
"RET", # flake8-return
|
|
"SLF", # flake8-self
|
|
"SIM", # flake8-simplify
|
|
"TID", # flake8-tidy-imports
|
|
"TCH", # flake8-type-checking
|
|
"ARG", # flake8-unused-arguments
|
|
"PTH", # flake8-use-pathlib
|
|
"ERA", # eradicate
|
|
"PD", # pandas-vet
|
|
"PGH", # pygrep-hooks
|
|
"PL", # Pylint
|
|
"TRY", # tryceratops
|
|
"FLY", # flynt
|
|
"PERF", # Perflint
|
|
"RUF", # Ruff-specific rules
|
|
]
|
|
|
|
ignore = [
|
|
"S101", # Use of assert
|
|
"S104", # Possible binding to all interfaces
|
|
"S301", # Use of pickle
|
|
"COM812", # Missing trailing comma (handled by formatter)
|
|
"ISC001", # Single line implicit string concatenation (handled by formatter)
|
|
"PLR0913", # Too many arguments to function call
|
|
"TRY003", # Avoid specifying long messages outside the exception class
|
|
"EM101", # Exception must not use a string literal
|
|
"EM102", # Exception must not use an f-string literal
|
|
"FBT001", # Boolean positional arg in function definition
|
|
"FBT002", # Boolean default positional argument in function definition
|
|
"G004", # Logging statement uses f-string
|
|
"PLR2004", # Magic value used in comparison
|
|
"SLF001", # Private member accessed
|
|
]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
# Test doubles use fixture tokens/passwords and match a callee signature whose
|
|
# args they intentionally ignore.
|
|
"tests/test_viewer_auth.py" = ["S105", "S106", "ARG001"]
|
|
"tests/test_codex_auth.py" = ["S105", "S106", "SLF001"]
|
|
# Stdlib HTTP handler overrides (do_GET/do_POST).
|
|
"strix/interface/auth_cli.py" = ["N802"]
|
|
"tests/test_codex_streaming.py" = ["N802"]
|
|
"tests/test_disable_streaming.py" = ["N802"]
|
|
"tests/test_report_pdf.py" = ["S105", "S106"]
|
|
# Stdlib HTTP handler overrides (do_GET/do_POST) and lazy imports that avoid a
|
|
# circular dependency with strix.telemetry / strix.interface.viewer.report_pdf.
|
|
"strix/interface/viewer/server.py" = ["N802", "PLC0415"]
|
|
# Lazy telemetry import to avoid importing PostHog before the viewer starts.
|
|
"strix/interface/viewer/cli.py" = ["PLC0415"]
|
|
# Lazy imports inside functions to avoid circular dependency with
|
|
# strix.telemetry / strix.report.dedupe / cvss.
|
|
"strix/tools/notes/tools.py" = ["PLC0415", "TC002"]
|
|
"strix/tools/finish/tool.py" = ["PLC0415", "TC002"]
|
|
"strix/tools/reporting/tool.py" = ["PLC0415", "TC002"]
|
|
"strix/tools/**/*.py" = [
|
|
"ARG001", # Unused function argument (tools may have unused args for interface consistency)
|
|
]
|
|
# Custom Docker subclass duplicates parent body; some imports are for annotations.
|
|
# Backend factories import their backend's deps lazily so deployments
|
|
# that pick a different backend don't need every backend's libs installed.
|
|
"strix/runtime/backends.py" = ["PLC0415"]
|
|
"strix/runtime/docker_client.py" = [
|
|
"TC002", # Manifest, Container imported for annotations
|
|
"TC003", # uuid imported for annotation
|
|
]
|
|
# SDK function-tool wrappers: the SDK calls get_type_hints() at registration
|
|
# time to derive the JSON schema, which evaluates annotations at runtime —
|
|
# so RunContextWrapper / Tool / TResponseInputItem must be imported eagerly,
|
|
# not under TYPE_CHECKING.
|
|
"strix/tools/todo/tools.py" = ["TC002"]
|
|
"strix/tools/thinking/tool.py" = ["TC002"]
|
|
"strix/tools/web_search/tool.py" = ["TC002"]
|
|
"strix/tools/proxy/tools.py" = ["TC002", "PLR0911"]
|
|
"strix/tools/agents_graph/tools.py" = ["TC002"]
|
|
"strix/agents/factory.py" = ["TC002"]
|
|
# Entry point: ``Path`` is used at runtime by the typing of the
|
|
# session_manager call; importing under TYPE_CHECKING would defer
|
|
# resolution past where mypy needs it.
|
|
"strix/core/runner.py" = ["TC003", "PLR0912", "PLR0915", "PLC0415"]
|
|
# ReportState carries scan artifact/report fields and
|
|
# a runtime ``Callable`` annotation on ``vulnerability_found_callback``.
|
|
"strix/report/state.py" = ["TC003", "PLR0912", "PLR0915", "E501", "PERF401", "PLC0415"]
|
|
"strix/report/usage.py" = ["PLC0415"]
|
|
# Lazy import of strix.config.models avoids a circular dependency between the
|
|
# report pipeline and the config layer.
|
|
"strix/report/dedupe.py" = ["PLC0415"]
|
|
"strix/telemetry/logging.py" = ["PLC0415"]
|
|
"strix/config/models.py" = ["PLC0415"]
|
|
# Heavy inference deps (httpx, openai) imported lazily so auth-status checks
|
|
# don't pull them in.
|
|
"strix/config/codex.py" = ["PLC0415"]
|
|
# Interface utility branches per scope-mode / target-type combination;
|
|
# splitting would obscure the decision tree without simplifying it.
|
|
"strix/interface/utils.py" = ["PLR0912", "BLE001", "PLC0415"]
|
|
# CLI / TUI / main keep extensive lazy imports + broad exception
|
|
# swallows for resilience around terminal-rendering errors.
|
|
"strix/interface/cli.py" = ["BLE001", "PLC0415"]
|
|
"strix/interface/tui/app.py" = ["BLE001", "PLC0415", "PLR0912", "PLR0915", "SIM105"]
|
|
"strix/interface/main.py" = ["BLE001", "PLC0415", "PLR0912", "PLR0915"]
|
|
"strix/interface/tui/renderers/agent_message_renderer.py" = ["PLC0415"]
|
|
|
|
[tool.ruff.lint.isort]
|
|
force-single-line = false
|
|
lines-after-imports = 2
|
|
known-first-party = ["strix"]
|
|
known-third-party = ["pydantic"]
|
|
|
|
[tool.ruff.lint.pylint]
|
|
max-args = 8
|
|
|
|
[tool.ruff.format]
|
|
quote-style = "double"
|
|
indent-style = "space"
|
|
skip-magic-trailing-comma = false
|
|
line-ending = "auto"
|
|
|
|
# ============================================================================
|
|
# PyRight Configuration (Alternative type checker)
|
|
# ============================================================================
|
|
|
|
[tool.pyright]
|
|
include = ["strix"]
|
|
exclude = ["**/__pycache__", "build", "dist"]
|
|
pythonVersion = "3.12"
|
|
pythonPlatform = "Linux"
|
|
|
|
typeCheckingMode = "strict"
|
|
reportMissingImports = true
|
|
reportMissingTypeStubs = false
|
|
reportGeneralTypeIssues = true
|
|
reportPropertyTypeMismatch = true
|
|
reportFunctionMemberAccess = true
|
|
reportMissingParameterType = true
|
|
reportMissingTypeArgument = true
|
|
reportIncompatibleMethodOverride = true
|
|
reportIncompatibleVariableOverride = true
|
|
reportInconsistentConstructor = true
|
|
reportOverlappingOverload = true
|
|
reportConstantRedefinition = true
|
|
reportImportCycles = true
|
|
reportUnusedImport = true
|
|
reportUnusedClass = true
|
|
reportUnusedFunction = true
|
|
reportUnusedVariable = true
|
|
reportDuplicateImport = true
|
|
|
|
# ============================================================================
|
|
# Black Configuration (Code Formatter)
|
|
# ============================================================================
|
|
|
|
[tool.black]
|
|
line-length = 100
|
|
target-version = ['py312']
|
|
include = '\\.pyi?$'
|
|
extend-exclude = '''
|
|
/(
|
|
# directories
|
|
\.eggs
|
|
| \.git
|
|
| \.hg
|
|
| \.mypy_cache
|
|
| \.tox
|
|
| \.venv
|
|
| build
|
|
| dist
|
|
)/
|
|
'''
|
|
|
|
# ============================================================================
|
|
# isort Configuration (Import Sorting)
|
|
# ============================================================================
|
|
|
|
[tool.isort]
|
|
profile = "black"
|
|
line_length = 100
|
|
multi_line_output = 3
|
|
include_trailing_comma = true
|
|
force_grid_wrap = 0
|
|
use_parentheses = true
|
|
ensure_newline_before_comments = true
|
|
known_first_party = ["strix"]
|
|
known_third_party = ["pydantic", "litellm"]
|
|
|
|
# ============================================================================
|
|
# Bandit Configuration (Security Linting)
|
|
# ============================================================================
|
|
|
|
[tool.bandit]
|
|
exclude_dirs = ["docs", "build", "dist"]
|
|
skips = ["B101", "B601", "B404", "B603", "B607"] # Skip assert, shell injection, subprocess import and partial path checks
|
|
severity = "medium"
|