[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "loosecanvas" version = "0.1.0" description = "Co-created understanding map: local LLM × Cytoscape.js" license = "MIT" authors = [{ name = "Joshua Sundance Bailey" }] requires-python = ">=3.13" dependencies = [ "fastapi>=0.115", "uvicorn[standard]>=0.41", "gradio>=5.0", "pydantic>=2.13", "networkx>=3.6", "httpx>=0.28", "PyYAML>=6.0", "markdown-it-py>=4.0", "aiofiles>=25.1", "python-multipart>=0.0.22", "sse-starlette>=3.3", "jinja2>=3.1", # M16: LangGraph turn state machine (routing + checkpointed conversation memory). "langgraph>=1.2", "langchain-core>=1.4", # Agent-forward turn loop: create_agent harness + middleware + ChatOpenAI driving # the local llama.cpp OpenAI-compatible endpoint (tool calling + token streaming). "langchain>=1.3", "langchain-openai>=1.3", "openai>=2.0", ] [project.optional-dependencies] dev = [ "pytest>=9.0", "pytest-asyncio>=1.3", "pytest-cov>=7.0", "mypy>=1.18", "types-PyYAML", "types-aiofiles", "pre-commit>=4.0", ] # M02 builds the `response_format` payload from the ScenePlan schema; the llama.cpp # server does JSON-Schema->grammar conversion internally, so no local grammar tooling # (llama-cpp-python / jsonref) is required. No `llm` optional group is needed. [tool.hatch.build.targets.wheel] packages = ["src/loosecanvas"] # ── Ruff ───────────────────────────────────────────────────────────────────── [tool.ruff] line-length = 120 target-version = "py313" src = ["src/loosecanvas", "tests"] [tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "I", # isort "N", # pep8-naming "UP", # pyupgrade "S", # bandit security "B", # bugbear "A", # flake8-builtins "C4", # flake8-comprehensions "DTZ", # flake8-datetimez (naive datetime usage) "T20", # flake8-print "SIM", # flake8-simplify "ERA", # flake8-eradicate (commented-out code) ] ignore = [ "S101", # assert in tests is fine ] [tool.ruff.lint.per-file-ignores] "scripts/**" = ["T201", "S310"] "archive/**" = ["T201", "S310"] # frozen PoC utility scripts (e.g. split_plan.py) "src/loosecanvas/llm_probe.py" = ["S310"] # Module holds a long natural-language SYSTEM_PROMPT literal; prose lines exceed 120. "src/loosecanvas/agent_harness.py" = ["E501"] "tests/**" = ["S101", "T20"] # ── Mypy ────────────────────────────────────────────────────────────────────── [tool.mypy] python_version = "3.13" strict = true warn_return_any = true warn_unused_configs = true warn_unused_ignores = true mypy_path = "src" explicit_package_bases = true # The M19 custom component is an external, untyped Gradio package — installed at # runtime (Dockerfile) and in local dev, but NOT in CI's `uv sync` env. Treat its # import as Any so `mypy src` passes regardless of whether it is installed. [[tool.mypy.overrides]] module = ["gradio_cytoscapecanvas.*"] ignore_missing_imports = true # ── Pytest ──────────────────────────────────────────────────────────────────── [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] pythonpath = ["src"] addopts = "--tb=short -q --cov=src --cov-report=term-missing --cov-fail-under=70" # Imports: from loosecanvas.contracts import Node (not: from contracts import Node) # ── Coverage ────────────────────────────────────────────────────────────────── [tool.coverage.run] source = ["src"] omit = ["tests/*"] branch = true [tool.coverage.report] show_missing = true skip_covered = false [tool.pyright] # Point pyright (incl. the standalone pyright-lsp plugin Claude Code spawns) at the # project venv so venv-only imports resolve instead of emitting false reportMissingImports. # VS Code's python.defaultInterpreterPath only configures Pylance, not this pyright. venvPath = "." venv = ".venv" extraPaths = ["src"]