[build-system]
requires = ["setuptools>=77.0.1"]
build-backend = "setuptools.build_meta"

[project]
# Do not add spaces around the '=' sign for any of the fields
# preceded by a marker comment as it affects the publish workflow.
#replace_package_name_marker
name="dspy"
#replace_package_version_marker
version="3.3.0"
description = "DSPy"
readme = "README.md"
authors = [{ name = "Omar Khattab", email = "okhattab@stanford.edu" }]
license = {file = "LICENSE"}
requires-python = ">=3.10, <3.15"
classifiers = [
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Science/Research",
    "Operating System :: POSIX :: Linux",
    "Programming Language :: Python :: 3"
]
dependencies = [
    "openai>=1.66.2",
    "regex>=2023.10.3",
    "orjson>=3.9.0",
    "tqdm>=4.66.1",
    "requests>=2.31.0",
    "pydantic>=2.0",
    "litellm>=1.65.8",
    "diskcache>=5.6.0",
    "json-repair>=0.54.2",
    "tenacity>=8.2.3",  # undeclared runtime dep of litellm, needed for exponential_backoff_retry
    "anyio",
    "cachetools>=5.5.0",
    "cloudpickle>=3.1.2",
    "gepa[dspy]==0.1.1",
]

[project.optional-dependencies]
anthropic = ["anthropic>=0.18.0,<1.0.0"]
weaviate = ["weaviate-client>=4.5.4,<4.22.0"]
mcp = ["mcp; python_version >= '3.10'"]
langchain = ["langchain_core>=0.3.0"]
optuna = ["optuna>=3.4.0"]
numpy = ["numpy>=1.26.0"]
litellm = ["litellm>=1.65.8"]
dev = [
    "pytest>=6.2.5",
    "pytest-mock>=3.12.0",
    "pytest-asyncio>=0.26.0",
    "pytest-xdist>=3.5.0",
    "ruff>=0.3.0",
    "pre-commit>=3.7.0",
    "pillow>=10.1.0",
    "datamodel_code_generator>=0.26.3",
    "build>=1.0.3",
    "numpy>=1.26.0",
    # LiteLLM proxy imports an internal FastAPI helper removed in 0.140.7.
    "fastapi<0.140.7; sys_platform != 'win32' and python_version < '3.14'",
    "litellm>=1.65.8; sys_platform == 'win32' or python_version == '3.14'",
    "litellm[proxy]>=1.65.8; sys_platform != 'win32' and python_version < '3.14'",  # Remove 3.14 condition once uvloop supports
]
test_extras = [
    "mcp; python_version >= '3.10'",
    "datasets>=2.14.6",
    "pandas>=2.1.1",
    "optuna>=3.4.0",
    "langchain_core>=0.3.0",
    "numpy>=1.26.0",
]

[tool.setuptools.packages.find]
where = ["."]
include = ["dspy", "dspy.*"]
exclude = ["tests", "tests.*"]

[tool.setuptools.package-data]
dspy = ["primitives/*.js"]

[project.urls]
homepage = "https://github.com/stanfordnlp/dspy"

[tool.coverage.run]
branch = true
omit = [
    "*/__init__.py",
    "*/test_*.py",
    "*/tests/*.py",
    "*/conftest.py",
    "*/venv/*",
    "*/virtualenv/*",
    "*/.venv/*",
    "*/.virtualenv/*",
    "*/env/*",
    "*/.env/*",
    "*/setup.py",
]

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "if self.debug:",
    "raise AssertionError",
    "raise NotImplementedError",
    "if __name__ == '__main__':",
    "logger",
    "try",
    "except",
    "^\\s*self\\.\\w+(:\\s*[^=]+)?\\s*=.*$",
    "continue",
]

[tool.pytest.ini_options]
filterwarnings = [
    # litellm uses deprecated pydantic config classes sometimes.
    # The issue has been fixed repeatedly, but still keeps showing up.
    # For examples, see litellm PRs #6903, #7300, #8096, #9372, and #12528.
    "ignore:.+class-based `config` is deprecated, use ConfigDict:DeprecationWarning",
]

[tool.ruff]
include = ["dspy/**/*.py", "tests/**/*.py"]
exclude = [
  "dspy/__metadata__.py",
  "tests/reliability/*.py",
]


line-length = 120
indent-width = 4
target-version = "py310"

[tool.ruff.lint]
select = [
    "E",   # pycodestyle errors
    "W",   # pycodestyle warnings
    "F",   # pyflakes
    "I",   # isort
    "C",   # flake8-comprehensions
    "B",   # flake8-bugbear
    "UP",  # pyupgrade
    "N",   # pep8-naming
    "RUF", # ruff-specific rules
    "Q",   # flake8-quotes
]

ignore = [
    "B027",  # Allow non-abstract empty methods in abstract base classes
    "FBT003",# Allow boolean positional values in function calls
    "C901",  # Ignore complexity checking
    "E501",  # Ignore line length errors (handled by formatter)
    "UP035", # Allow python typing modules
    "RUF005", # Allow using + operator to concatenate collections
    "B904", # Allow raise custom exceptions in except blocks
    "F403", # Allow wildcard imports
    "E721", # Allow using == to compare with type
    "UP031", # Allow percent format
    "RUF022", # Allow unsorted __all__ value
]

# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []

[tool.ruff.format]
docstring-code-format = false
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"

[tool.ruff.lint.isort]
known-first-party = ["dspy"]

[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = [
    "S101",    # Allow assert statements in tests
    "TID252",  # Allow relative imports in tests
    "ARG001",  # Allow unused arguments in tests (like pytest fixtures)
]
"__init__.py" = ["F401"]  # Init files can be empty
"dspy/__init__.py" = [
    "I001",  # Allow unsorted or unformatted imports (isort)
    "E402",  # Allow imports not at the top of the file (needed for certain __init__ patterns)
    "F405",  # Allow undefined names from wildcard imports (common in __init__ files)
]
