diff --git a/nix/hermes-agent.nix b/nix/hermes-agent.nix index 043d1e94202..71546c6140c 100644 --- a/nix/hermes-agent.nix +++ b/nix/hermes-agent.nix @@ -9,6 +9,7 @@ stdenv, makeWrapper, callPackage, + python311, python312, nodejs_22, electron, @@ -37,15 +38,28 @@ }: let nodejs = nodejs_22; + mkHermesVenv = - extraDependencyGroups: + { + extraDependencyGroups, + python ? null, + }: callPackage ./python.nix { - inherit uv2nix pyproject-nix pyproject-build-systems; + inherit + uv2nix + pyproject-nix + pyproject-build-systems + python + ; pythonSrc = hermesNpmLib.pythonSrc; dependency-groups = [ "all" ] ++ extraDependencyGroups; }; - hermesVenv = (mkHermesVenv extraDependencyGroups).venv; + hermesVenv = + (mkHermesVenv { + inherit extraDependencyGroups; + python = python312; + }).venv; hermesNpmLib = callPackage ./lib.nix { inherit npm-lockfile-fix nodejs; @@ -61,8 +75,7 @@ let bundledSkills = lib.cleanSourceWith { src = ../skills; - filter = - path: _type: !(lib.hasInfix "/index-cache/" path) && !(lib.hasInfix "/__pycache__/" path); + filter = path: _type: !(lib.hasInfix "/index-cache/" path) && !(lib.hasInfix "/__pycache__/" path); }; # Optional skills are NOT in the wheel (pythonSrc excludes them, see @@ -70,8 +83,7 @@ let # same mechanism Homebrew packaging uses. bundledOptionalSkills = lib.cleanSourceWith { src = ../optional-skills; - filter = - path: _type: !(lib.hasInfix "/index-cache/" path) && !(lib.hasInfix "/__pycache__/" path); + filter = path: _type: !(lib.hasInfix "/index-cache/" path) && !(lib.hasInfix "/__pycache__/" path); }; # Import bundled plugins (memory, context_engine, platforms/*). Keeping @@ -224,8 +236,13 @@ stdenv.mkDerivation (finalAttrs: { ''; passthru = + python: let - devPython = (mkHermesVenv (extraDependencyGroups ++ [ "dev" ])).editableVenv; + devPython = + (mkHermesVenv ({ + extraDependencyGroups = extraDependencyGroups ++ [ "dev" ]; + python = python311; + })).editableVenv; in { inherit diff --git a/nix/python.nix b/nix/python.nix index 602c08a3fd0..40f689736de 100644 --- a/nix/python.nix +++ b/nix/python.nix @@ -1,6 +1,6 @@ # nix/python.nix — uv2nix virtual environment builder { - python312, + python, lib, callPackage, uv2nix, @@ -65,30 +65,30 @@ let final: _prev: if isAarch64Darwin then { - numpy = mkPrebuiltOverride final python312.pkgs.numpy { }; + numpy = mkPrebuiltOverride final python.pkgs.numpy { }; - pyarrow = mkPrebuiltOverride final python312.pkgs.pyarrow { }; + pyarrow = mkPrebuiltOverride final python.pkgs.pyarrow { }; - av = mkPrebuiltOverride final python312.pkgs.av { }; + av = mkPrebuiltOverride final python.pkgs.av { }; - humanfriendly = mkPrebuiltOverride final python312.pkgs.humanfriendly { }; + humanfriendly = mkPrebuiltOverride final python.pkgs.humanfriendly { }; - coloredlogs = mkPrebuiltOverride final python312.pkgs.coloredlogs { + coloredlogs = mkPrebuiltOverride final python.pkgs.coloredlogs { humanfriendly = [ ]; }; - onnxruntime = mkPrebuiltOverride final python312.pkgs.onnxruntime { + onnxruntime = mkPrebuiltOverride final python.pkgs.onnxruntime { coloredlogs = [ ]; numpy = [ ]; packaging = [ ]; }; - ctranslate2 = mkPrebuiltOverride final python312.pkgs.ctranslate2 { + ctranslate2 = mkPrebuiltOverride final python.pkgs.ctranslate2 { numpy = [ ]; pyyaml = [ ]; }; - faster-whisper = mkPrebuiltOverride final python312.pkgs.faster-whisper { + faster-whisper = mkPrebuiltOverride final python.pkgs.faster-whisper { av = [ ]; ctranslate2 = [ ]; huggingface-hub = [ ]; @@ -102,7 +102,7 @@ let pythonSet = (callPackage pyproject-nix.build.packages { - python = python312; + python = python; }).overrideScope ( lib.composeManyExtensions [ diff --git a/notes.txt b/notes.txt index 11abc565f39..2b80a8f5fe7 100644 --- a/notes.txt +++ b/notes.txt @@ -261,3 +261,52 @@ duck-typed access. all 190 are ty being unable to track either cross-module attribute init (186) or duck-typed hasattr-guarded access (4). no logic bugs found. + +### remaining 6 unresolved-attribute (post class-level annotation fix) + +1. **line 2162** (`object.function` ×2): duck-typed access to OpenAI SDK + `ChatCompletionMessageToolCall` objects. `hasattr` guard makes it safe. + ty can't infer the list element type through `hasattr`. not a bug. + +2. **lines 3586-3587** (`iteration_budget.used` / `.max_total`): `iteration_budget` + is `Optional[IterationBudget]` but `init_agent` always sets it to a non-None + value (`iteration_budget or IterationBudget(max_iterations)`). the annotation + says `| None` but the code guarantees non-None at runtime. not a bug — but + the annotation could be tightened to just `IterationBudget` (without Optional). + +3. **lines 4677 + 4799** (`_anthropic_client.close()`): `_anthropic_client` is + `Any | None` — could be None if the anthropic path was never taken. but both + call sites are wrapped in `try/except Exception: pass`, so even if it IS None, + the AttributeError is caught. safe. + +--- + +## plugins/platforms/discord/adapter.py — unresolved-attribute analysis (269 errors) + +### root cause: ty resolving the wrong Python environment + +the `.venv` directory (created by `uv run` earlier) contained Python 3.13 with +NO dependencies installed. ty auto-discovers `.venv` and uses it for module +resolution — so it couldn't find `discord.py`, `aiohttp`, etc., producing 254 +`Module 'discord' has no member X` errors. + +the actual dependencies are installed in the nix develop environment +(Python 3.12, all deps in site-packages). removing the empty `.venv` made ty +fall back to the system/nix python, resolving all 254 import errors instantly. + +**fix:** `rm -rf .venv` (it was a stray artifact, not a real venv). +also set `python-version = "3.12"` in `[tool.ty.environment]` to match the +nix env, with a comment explaining why. + +**impact:** 4,422 → 3,385 diagnostics (−1,037) from this single fix. + +### remaining 122 discord errors (post-fix) + +after ty could resolve discord.py: +- 17 `not defined on None` — `self._client` (Optional[commands.Bot]) accessed + before ty can prove it's non-None. all are in event handlers called after + the bot is ready. not bugs, but could benefit from `assert self._client is not None` + guards for type narrowing. +- ~100 remaining are real type-checking errors from ty now being able to + see discord.py's actual types (mismatched args, wrong attribute access, etc.) + these need individual triage. diff --git a/pyproject.toml b/pyproject.toml index 93dee881279..652624f8b98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -366,7 +366,8 @@ markers = [ addopts = "-m 'not integration'" [tool.ty.environment] -python-version = "3.13" +# Match requires-python floor (>=3.11). +python-version = "3.11" [tool.ty.rules] unknown-argument = "warn"