henchman-ai 0.1.14__tar.gz → 0.1.15__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/140365231305696/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/PKG-INFO +1 -1
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/pyproject.toml +1 -1
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/app.py +8 -4
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/builtins.py +3 -1
- henchman_ai-0.1.15/src/henchman/cli/commands/model.py +285 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/providers/anthropic.py +20 -0
- henchman_ai-0.1.15/src/henchman/utils/ratelimit.py +71 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/utils/tokens.py +1 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/version.py +1 -1
- henchman_ai-0.1.15/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.15/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.15/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.15/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.15/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.15/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.15/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.15/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.15/tests/__init__.py +0 -0
- henchman_ai-0.1.15/tests/cli/__init__.py +0 -0
- henchman_ai-0.1.15/tests/cli/commands/test_model.py +323 -0
- henchman_ai-0.1.15/tests/cli/commands/test_model_integration.py +189 -0
- henchman_ai-0.1.15/tests/cli/commands/test_plan_branch.py +138 -0
- henchman_ai-0.1.15/tests/cli/test_console_confirm.py +77 -0
- henchman_ai-0.1.15/tests/config/__init__.py +0 -0
- henchman_ai-0.1.15/tests/core/__init__.py +0 -0
- henchman_ai-0.1.15/tests/integration/test_anthropic_functional.py +29 -0
- henchman_ai-0.1.15/tests/mcp/__init__.py +0 -0
- henchman_ai-0.1.15/tests/providers/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/providers/test_anthropic.py +1 -0
- henchman_ai-0.1.15/tests/providers/test_anthropic_ratelimit.py +76 -0
- henchman_ai-0.1.15/tests/providers/test_anthropic_validation.py +123 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/test_version.py +1 -1
- henchman_ai-0.1.15/tests/tools/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/.github/copilot-instructions.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/.github/workflows/ci.yml +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/.github/workflows/docs.yml +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/.github/workflows/publish.yml +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/.gitignore +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/ALPHA_TEST_LOG.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/BETA_TESTING_ISSUES.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/BETA_TESTING_ISSUES2.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/BETA_TESTING_ISSUES3.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/CHANGELOG.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/CONTRIBUTING.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/LICENSE +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125050554038512/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125050555035824/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125050555125200/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125051893219152/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125080879298512/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125080879878832/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125080880913088/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125081401798976/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125246043105824/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125246043924496/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125246044001472/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/MagicMock/mock.rag.cache_dir/125247627445840/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/131782863223120 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/126367803948160}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/131782952758032 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/126368206535376}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/131782953186608 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/126368218825504}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/131783467925184 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/126368326698240}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/131953655609296 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/129118288064016}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/131953656855104 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/129118289735760}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/131954146008512 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/129118290807072}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/131954155550192 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/129118485247328}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/133226809006208 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/131782863223120}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/133226810375248 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/131782952758032}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/133226812410672 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/131782953186608}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/133227143444000 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/131783467925184}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/133999527639808 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/131953655609296}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/133999929687216 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/131953656855104}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/133999955332368 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/131954146008512}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134000027622176 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/131954155550192}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134508512171968 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/133226809006208}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134508512231200 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/133226810375248}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134508512485616 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/133226812410672}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134508855764000 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/133227143444000}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134604944477792 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/133999527639808}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134604945877536 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/133999929687216}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134604946059776 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/133999955332368}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134605456585744 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134000027622176}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134618534097648 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134508512171968}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134618535070656 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134508512231200}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134619942883056 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134508512485616}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/134620110571280 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134508855764000}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/138555538485184 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134604944477792}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/138555538874752 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134604945877536}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/138555539451408 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134604946059776}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/138556062045760 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134605456585744}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/139088593563008 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134618534097648}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/139088594086144 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134618535070656}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/139088594201216 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134619942883056}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/139088659820192 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134620110571280}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/139391159286624 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134745693453872}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/139391160298352 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134745694538112}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/139391160361968 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134746088611312}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/139391778658240 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/134746194079344}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/140323476876816 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/138555538485184}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/140323493157376 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/138555538874752}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/140323629475472 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/138555539451408}/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14/MagicMock/mock.rag.cache_dir/140324020609984 → henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/138556062045760}/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/139088593563008/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/139088594086144/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/139088594201216/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/139088659820192/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/139391159286624/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/139391160298352/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/139391160361968/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/139391778658240/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/__init__.py → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/140323476876816/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/cli/__init__.py → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/140323493157376/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/config/__init__.py → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/140323629475472/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/core/__init__.py → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/140324020609984/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/mcp/__init__.py → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/140365102460192/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/providers/__init__.py → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/140365132965952/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.14/tests/tools/__init__.py → /henchman_ai-0.1.15/MagicMock/mock.rag.cache_dir/140365156415680/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/PROJECT_PLAN.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/README.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/README_enhanced.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/RELEASE_CHECKLIST.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/TASK_COMPLETION_SUMMARY.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/debug_compaction.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/docs/api.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/docs/configuration.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/docs/extensions.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/docs/getting-started.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/docs/index.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/docs/mcp.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/docs/providers.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/docs/tools.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/README.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/conftest.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/helpers.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/test_answer_vs_action.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/test_coding_tasks.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/test_edit_precision.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/test_skills_memory.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/evals/test_tool_selection.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/fix_repl.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/fix_repl_simple.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/mkdocs.yml +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/replace_method.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/reproduce_400_error.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/run_interactive_tests.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/scripts/build_docs.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/scripts/ci.sh +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/scripts/run_evals.sh +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/__main__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/chat.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/extensions.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/mcp.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/plan.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/rag.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/skill.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/commands/unlimited.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/console.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/input.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/json_output.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/prompts.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/repl.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/repl.py.backup +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/cli/repl.py.backup2 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/config/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/config/context.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/config/schema.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/config/settings.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/core/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/core/agent.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/core/agent.py.backup +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/core/events.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/core/session.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/core/turn.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/extensions/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/extensions/base.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/extensions/manager.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/mcp/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/mcp/client.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/mcp/config.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/mcp/manager.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/mcp/tool.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/providers/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/providers/base.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/providers/deepseek.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/providers/ollama.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/providers/openai_compat.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/providers/openai_compat.py.backup +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/providers/registry.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/rag/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/rag/chunker.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/rag/concurrency.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/rag/embedder.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/rag/indexer.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/rag/repo_id.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/rag/store.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/rag/system.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/skills/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/skills/executor.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/skills/learner.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/skills/models.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/skills/store.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/base.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/ask_user.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/file_edit.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/file_read.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/file_write.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/glob_tool.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/grep.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/ls.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/rag_search.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/shell.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/web_fetch.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/builtins/web_search.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/tools/registry.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/utils/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/utils/compaction.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/utils/retry.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/src/henchman/utils/validation.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/test_compaction.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/test_compaction_fix.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/test_fixes.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/test_output.txt +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/test_run.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/commands/test_plan.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/commands/test_session_resume_id.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/commands/test_skill.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/commands/test_skill_extended.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/commands/test_unlimited.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_app.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_app_extended.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_builtins.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_chat_command.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_cli_smoke.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_commands.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_commands_repro.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_console.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_enhanced_tool_display.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_input.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_input_bindings.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_interrupt.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_json_output.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_keyboard_fixes.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_keyboard_integration.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_keyboard_interrupt.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_keyboard_verification.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_loop_protection.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_mcp_command.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_repl.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_repl_attribute_fix.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_repl_startup_message.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/cli/test_repl_toolbar.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/config/test_context.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/config/test_schema.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/config/test_settings.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/conftest.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/core/test_automatic_compaction.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/core/test_events.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/core/test_session.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/core/test_session_manager.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/core/test_streaming_tool_calls.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/core/test_turn_state.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/e2e/test_context_safety.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/e2e/test_plan_mode_workflow.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/e2e/test_tool_fix.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/empty_message_validation/test_empty_messages.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/extensions/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/extensions/test_base.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/extensions/test_command.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/extensions/test_manager.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/integration/test_context_limits.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/integration/test_tool_integration.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/mcp/test_client.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/mcp/test_config.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/mcp/test_manager.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/mcp/test_tool.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/providers/test_413_error_handling.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/providers/test_base.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/providers/test_deepseek.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/providers/test_ollama.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/providers/test_openai_compat.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/providers/test_registry.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_chunker.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_concurrency.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_concurrency_smoke.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_embedder.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_indexer.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_rag_command.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_rag_concurrency_integration.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_rag_search_tool.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_repo_id.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_store.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/rag/test_system.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/skills/test_executor.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/skills/test_learner.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/skills/test_markdown_skills.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/skills/test_models.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/skills/test_store.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/skills/test_store_extended.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/smoke/test_escape_key_behavior.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/smoke/test_large_file_handling.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/test_coverage_suite.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/test_main.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_ask_user_tool.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_base.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_directory_tools.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_file_tools.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_grep_tool.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_plan_mode_enforcement.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_registry.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_shell_advanced.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_shell_tool.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/tools/test_web_fetch_tool.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/INTERACTIVE_SESSION_TESTS.md +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/__init__.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/conftest.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_agent.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_compaction_llm.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_events.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_llm.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_mcp.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_plan_mode.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_repl_e2e.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_repl_integration.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_session.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_skills.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_slash_commands.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_tokens_llm.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_tool_calls.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/ui_integration/test_tool_integration.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_compaction.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_compaction_edge_cases.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_compaction_validation.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_multi_turn_tool_calls.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_protected_zone.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_retry.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_summarization.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_tiktoken_integration.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_token_counter_extended.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_tool_sequence_compaction.py +0 -0
- {henchman_ai-0.1.14 → henchman_ai-0.1.15}/tests/utils/test_validation.py +0 -0
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: henchman-ai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.15
|
|
4
4
|
Summary: A model-agnostic AI agent CLI - your AI henchman for the terminal
|
|
5
5
|
Project-URL: Homepage, https://github.com/MGPowerlytics/henchman-ai
|
|
6
6
|
Project-URL: Repository, https://github.com/MGPowerlytics/henchman-ai
|
|
@@ -43,13 +43,17 @@ def _get_provider() -> ModelProvider:
|
|
|
43
43
|
registry = get_default_registry()
|
|
44
44
|
|
|
45
45
|
provider_name = settings.providers.default or "deepseek"
|
|
46
|
-
provider_settings = getattr(settings.providers, provider_name,
|
|
46
|
+
provider_settings = getattr(settings.providers, provider_name, {})
|
|
47
47
|
|
|
48
|
-
if provider_settings:
|
|
48
|
+
if isinstance(provider_settings, dict):
|
|
49
|
+
# Ensure api_key is handled correctly (backward compatibility or env var)
|
|
50
|
+
kwargs = provider_settings.copy()
|
|
51
|
+
if not kwargs.get("api_key"):
|
|
52
|
+
kwargs["api_key"] = os.environ.get("ANTHROPIC_API_KEY") if provider_name == "anthropic" else os.environ.get("HENCHMAN_API_KEY")
|
|
53
|
+
|
|
49
54
|
return registry.create(
|
|
50
55
|
provider_name,
|
|
51
|
-
|
|
52
|
-
model=getattr(provider_settings, "model", None),
|
|
56
|
+
**kwargs
|
|
53
57
|
)
|
|
54
58
|
except Exception: # pragma: no cover
|
|
55
59
|
pass
|
|
@@ -8,6 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
from henchman.cli.commands import Command, CommandContext
|
|
9
9
|
from henchman.cli.commands.chat import ChatCommand
|
|
10
10
|
from henchman.cli.commands.mcp import McpCommand
|
|
11
|
+
from henchman.cli.commands.model import ModelCommand
|
|
11
12
|
from henchman.cli.commands.plan import PlanCommand
|
|
12
13
|
from henchman.cli.commands.rag import RagCommand
|
|
13
14
|
from henchman.cli.commands.skill import SkillCommand
|
|
@@ -57,10 +58,10 @@ class HelpCommand(Command):
|
|
|
57
58
|
ctx.console.print(" /skill - Manage and execute learned skills")
|
|
58
59
|
ctx.console.print(" /chat - Manage chat sessions (save, list, resume)")
|
|
59
60
|
ctx.console.print(" /mcp - Manage MCP server connections")
|
|
61
|
+
ctx.console.print(" /model - Show or change model/provider")
|
|
60
62
|
ctx.console.print(" /quit - Exit the CLI")
|
|
61
63
|
ctx.console.print(" /clear - Clear the screen")
|
|
62
64
|
ctx.console.print(" /tools - List available tools")
|
|
63
|
-
ctx.console.print(" /model - Show or change the model")
|
|
64
65
|
ctx.console.print("")
|
|
65
66
|
|
|
66
67
|
|
|
@@ -212,6 +213,7 @@ def get_builtin_commands() -> list[Command]:
|
|
|
212
213
|
ToolsCommand(),
|
|
213
214
|
ChatCommand(),
|
|
214
215
|
McpCommand(),
|
|
216
|
+
ModelCommand(),
|
|
215
217
|
PlanCommand(),
|
|
216
218
|
RagCommand(),
|
|
217
219
|
SkillCommand(),
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""Model and provider management commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from henchman.cli.commands import Command, CommandContext
|
|
9
|
+
from henchman.config import load_settings
|
|
10
|
+
from henchman.providers import get_default_registry
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from henchman.providers.base import ModelProvider
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ModelCommand(Command):
|
|
17
|
+
"""Show or change the model and provider."""
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def name(self) -> str:
|
|
21
|
+
"""Command name.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Command name string.
|
|
25
|
+
"""
|
|
26
|
+
return "model"
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def description(self) -> str:
|
|
30
|
+
"""Command description.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Description string.
|
|
34
|
+
"""
|
|
35
|
+
return "Show or change the model and provider"
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def usage(self) -> str:
|
|
39
|
+
"""Command usage.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Usage string.
|
|
43
|
+
"""
|
|
44
|
+
return "/model [list|set <provider> [<model>]]"
|
|
45
|
+
|
|
46
|
+
async def execute(self, ctx: CommandContext) -> None:
|
|
47
|
+
"""Execute the model command.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
ctx: Command context.
|
|
51
|
+
"""
|
|
52
|
+
args = ctx.args
|
|
53
|
+
if not args:
|
|
54
|
+
await self._show_current(ctx)
|
|
55
|
+
elif args[0] == "list":
|
|
56
|
+
await self._list_providers(ctx)
|
|
57
|
+
elif args[0] == "set" and len(args) >= 2:
|
|
58
|
+
await self._set_provider(ctx, args[1], args[2] if len(args) > 2 else None)
|
|
59
|
+
else:
|
|
60
|
+
ctx.console.print(f"[yellow]Usage: {self.usage}[/]")
|
|
61
|
+
|
|
62
|
+
async def _show_current(self, ctx: CommandContext) -> None:
|
|
63
|
+
"""Show current provider and model.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
ctx: Command context.
|
|
67
|
+
"""
|
|
68
|
+
if not ctx.agent:
|
|
69
|
+
ctx.console.print("[yellow]No active agent. Cannot show current model.[/]")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
provider = ctx.agent.provider
|
|
73
|
+
settings = load_settings()
|
|
74
|
+
registry = get_default_registry()
|
|
75
|
+
|
|
76
|
+
ctx.console.print("\n[bold blue]Current Configuration[/]\n")
|
|
77
|
+
ctx.console.print(f" Provider: [cyan]{provider.name}[/]")
|
|
78
|
+
|
|
79
|
+
# Show model if available
|
|
80
|
+
if hasattr(provider, "default_model"):
|
|
81
|
+
ctx.console.print(f" Model: [cyan]{provider.default_model}[/]")
|
|
82
|
+
|
|
83
|
+
# Show available providers
|
|
84
|
+
available = registry.list_providers()
|
|
85
|
+
ctx.console.print(f"\n Available providers: [dim]{', '.join(available)}[/]")
|
|
86
|
+
ctx.console.print(f"\n Use [cyan]/model list[/] to see all providers")
|
|
87
|
+
ctx.console.print(f" Use [cyan]/model set <provider> [model][/] to switch")
|
|
88
|
+
ctx.console.print("")
|
|
89
|
+
|
|
90
|
+
async def _list_providers(self, ctx: CommandContext) -> None:
|
|
91
|
+
"""List all available providers and models.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
ctx: Command context.
|
|
95
|
+
"""
|
|
96
|
+
registry = get_default_registry()
|
|
97
|
+
providers = registry.list_providers()
|
|
98
|
+
|
|
99
|
+
ctx.console.print("\n[bold blue]Available Providers[/]\n")
|
|
100
|
+
|
|
101
|
+
for provider_name in sorted(providers):
|
|
102
|
+
try:
|
|
103
|
+
provider_class = registry.get(provider_name)
|
|
104
|
+
|
|
105
|
+
# Get example configuration
|
|
106
|
+
example_config = self._get_example_config(provider_name)
|
|
107
|
+
|
|
108
|
+
ctx.console.print(f" [cyan]{provider_name}[/]")
|
|
109
|
+
if hasattr(provider_class, "__doc__") and provider_class.__doc__:
|
|
110
|
+
doc_lines = provider_class.__doc__.strip().split('\n')
|
|
111
|
+
first_line = doc_lines[0].strip()
|
|
112
|
+
ctx.console.print(f" [dim]{first_line}[/]")
|
|
113
|
+
|
|
114
|
+
if example_config:
|
|
115
|
+
ctx.console.print(f" [yellow]Config:[/] {example_config}")
|
|
116
|
+
|
|
117
|
+
# Show environment variables needed
|
|
118
|
+
env_vars = self._get_env_vars(provider_name)
|
|
119
|
+
if env_vars:
|
|
120
|
+
ctx.console.print(f" [yellow]Env vars:[/] {env_vars}")
|
|
121
|
+
|
|
122
|
+
ctx.console.print("")
|
|
123
|
+
except Exception as e:
|
|
124
|
+
ctx.console.print(f" [red]{provider_name}[/] - Error: {e}")
|
|
125
|
+
|
|
126
|
+
async def _set_provider(
|
|
127
|
+
self,
|
|
128
|
+
ctx: CommandContext,
|
|
129
|
+
provider_name: str,
|
|
130
|
+
model_name: str | None = None
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Switch to a different provider.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
ctx: Command context.
|
|
136
|
+
provider_name: Name of the provider to switch to.
|
|
137
|
+
model_name: Optional model name to use.
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
ValueError: If provider cannot be created.
|
|
141
|
+
"""
|
|
142
|
+
if not ctx.repl:
|
|
143
|
+
ctx.console.print("[yellow]Cannot switch providers without REPL context.[/]")
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# Get registry and create new provider
|
|
148
|
+
registry = get_default_registry()
|
|
149
|
+
|
|
150
|
+
if provider_name not in registry.list_providers():
|
|
151
|
+
ctx.console.print(f"[red]Provider '{provider_name}' not found.[/]")
|
|
152
|
+
ctx.console.print(f"Available providers: {', '.join(registry.list_providers())}")
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
# Try to get API key from environment or settings
|
|
156
|
+
api_key = self._get_api_key_for_provider(provider_name)
|
|
157
|
+
|
|
158
|
+
# Create provider instance
|
|
159
|
+
provider_kwargs = {"api_key": api_key or ""}
|
|
160
|
+
if model_name:
|
|
161
|
+
provider_kwargs["model"] = model_name
|
|
162
|
+
|
|
163
|
+
new_provider = registry.create(provider_name, **provider_kwargs)
|
|
164
|
+
|
|
165
|
+
# Test the provider with a simple call
|
|
166
|
+
ctx.console.print(f"[dim]Testing {provider_name} connection...[/]")
|
|
167
|
+
try:
|
|
168
|
+
# Simple test to verify provider works
|
|
169
|
+
if hasattr(new_provider, "default_model"):
|
|
170
|
+
ctx.console.print(f"[green]✓ Connected to {provider_name}[/]")
|
|
171
|
+
if model_name:
|
|
172
|
+
ctx.console.print(f"[green]✓ Using model: {model_name}[/]")
|
|
173
|
+
else:
|
|
174
|
+
ctx.console.print(f"[green]✓ Using default model: {new_provider.default_model}[/]")
|
|
175
|
+
else:
|
|
176
|
+
ctx.console.print(f"[green]✓ Connected to {provider_name}[/]")
|
|
177
|
+
except Exception as e:
|
|
178
|
+
ctx.console.print(f"[yellow]⚠ Connection test failed: {e}[/]")
|
|
179
|
+
ctx.console.print("[yellow]Provider created but may not work correctly.[/]")
|
|
180
|
+
|
|
181
|
+
# Update the agent with new provider
|
|
182
|
+
old_provider = ctx.agent.provider
|
|
183
|
+
ctx.agent.provider = new_provider
|
|
184
|
+
|
|
185
|
+
# Update REPL's provider reference
|
|
186
|
+
ctx.repl.provider = new_provider
|
|
187
|
+
|
|
188
|
+
ctx.console.print(f"\n[bold green]✓ Switched from {old_provider.name} to {new_provider.name}[/]")
|
|
189
|
+
|
|
190
|
+
# Show any configuration needed
|
|
191
|
+
if not api_key:
|
|
192
|
+
env_var = self._get_env_var_name(provider_name)
|
|
193
|
+
ctx.console.print(f"\n[yellow]⚠ No API key found for {provider_name}[/]")
|
|
194
|
+
ctx.console.print(f" Set environment variable: [cyan]{env_var}=your-api-key[/]")
|
|
195
|
+
ctx.console.print(f" Or configure in [cyan]~/.henchman/settings.yaml[/]:")
|
|
196
|
+
ctx.console.print(f" providers:")
|
|
197
|
+
ctx.console.print(f" {provider_name}:")
|
|
198
|
+
ctx.console.print(f" api_key: your-api-key")
|
|
199
|
+
|
|
200
|
+
except Exception as e:
|
|
201
|
+
ctx.console.print(f"[red]Failed to switch provider: {e}[/]")
|
|
202
|
+
ctx.console.print("[dim]Check that the provider is properly configured.[/]")
|
|
203
|
+
|
|
204
|
+
def _get_example_config(self, provider_name: str) -> str:
|
|
205
|
+
"""Get example configuration for a provider.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
provider_name: Name of the provider.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Example configuration string.
|
|
212
|
+
"""
|
|
213
|
+
examples = {
|
|
214
|
+
"deepseek": "deepseek-chat (default), deepseek-coder",
|
|
215
|
+
"openai": "gpt-4-turbo, gpt-3.5-turbo",
|
|
216
|
+
"anthropic": "claude-3-opus, claude-3-sonnet",
|
|
217
|
+
"ollama": "llama2, mistral, codellama",
|
|
218
|
+
}
|
|
219
|
+
return examples.get(provider_name, "Check provider documentation")
|
|
220
|
+
|
|
221
|
+
def _get_env_vars(self, provider_name: str) -> str:
|
|
222
|
+
"""Get environment variables needed for a provider.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
provider_name: Name of the provider.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Environment variable names.
|
|
229
|
+
"""
|
|
230
|
+
env_vars = {
|
|
231
|
+
"deepseek": "DEEPSEEK_API_KEY",
|
|
232
|
+
"openai": "OPENAI_API_KEY",
|
|
233
|
+
"anthropic": "ANTHROPIC_API_KEY",
|
|
234
|
+
"ollama": "OLLAMA_HOST (optional, defaults to http://localhost:11434)",
|
|
235
|
+
}
|
|
236
|
+
return env_vars.get(provider_name, "Check provider documentation")
|
|
237
|
+
|
|
238
|
+
def _get_env_var_name(self, provider_name: str) -> str:
|
|
239
|
+
"""Get the environment variable name for a provider's API key.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
provider_name: Name of the provider.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Environment variable name.
|
|
246
|
+
"""
|
|
247
|
+
mapping = {
|
|
248
|
+
"deepseek": "DEEPSEEK_API_KEY",
|
|
249
|
+
"openai": "OPENAI_API_KEY",
|
|
250
|
+
"anthropic": "ANTHROPIC_API_KEY",
|
|
251
|
+
"ollama": "OLLAMA_API_KEY", # Ollama doesn't usually need API key
|
|
252
|
+
}
|
|
253
|
+
return mapping.get(provider_name, f"{provider_name.upper()}_API_KEY")
|
|
254
|
+
|
|
255
|
+
def _get_api_key_for_provider(self, provider_name: str) -> str | None:
|
|
256
|
+
"""Get API key for a provider from environment or settings.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
provider_name: Name of the provider.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
API key if found, None otherwise.
|
|
263
|
+
"""
|
|
264
|
+
# Try environment variables first
|
|
265
|
+
env_var = self._get_env_var_name(provider_name)
|
|
266
|
+
api_key = os.environ.get(env_var)
|
|
267
|
+
|
|
268
|
+
if api_key:
|
|
269
|
+
return api_key
|
|
270
|
+
|
|
271
|
+
# Try generic HENCHMAN_API_KEY
|
|
272
|
+
api_key = os.environ.get("HENCHMAN_API_KEY")
|
|
273
|
+
if api_key:
|
|
274
|
+
return api_key
|
|
275
|
+
|
|
276
|
+
# Try settings
|
|
277
|
+
try:
|
|
278
|
+
settings = load_settings()
|
|
279
|
+
provider_settings = getattr(settings.providers, provider_name, None)
|
|
280
|
+
if provider_settings and hasattr(provider_settings, "api_key"):
|
|
281
|
+
return provider_settings.api_key
|
|
282
|
+
except Exception:
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
return None
|
|
@@ -19,11 +19,14 @@ from henchman.providers.base import (
|
|
|
19
19
|
ToolCall,
|
|
20
20
|
ToolDeclaration,
|
|
21
21
|
)
|
|
22
|
+
from henchman.utils.ratelimit import AsyncRateLimiter
|
|
23
|
+
from henchman.utils.tokens import TokenCounter
|
|
22
24
|
|
|
23
25
|
__all__ = ["AnthropicProvider"]
|
|
24
26
|
|
|
25
27
|
# Available Claude models
|
|
26
28
|
ANTHROPIC_MODELS = [
|
|
29
|
+
"claude-opus-4-6",
|
|
27
30
|
"claude-sonnet-4-20250514",
|
|
28
31
|
"claude-3-7-sonnet-20250219",
|
|
29
32
|
"claude-3-5-sonnet-20241022",
|
|
@@ -50,6 +53,7 @@ class AnthropicProvider(ModelProvider):
|
|
|
50
53
|
api_key: str | None = None,
|
|
51
54
|
model: str = "claude-sonnet-4-20250514",
|
|
52
55
|
max_tokens: int = 8192,
|
|
56
|
+
tokens_per_minute: int = 30000,
|
|
53
57
|
) -> None:
|
|
54
58
|
"""Initialize the Anthropic provider.
|
|
55
59
|
|
|
@@ -57,11 +61,13 @@ class AnthropicProvider(ModelProvider):
|
|
|
57
61
|
api_key: API key for authentication. Defaults to ANTHROPIC_API_KEY env var.
|
|
58
62
|
model: Default model to use.
|
|
59
63
|
max_tokens: Maximum tokens in response.
|
|
64
|
+
tokens_per_minute: Maximum tokens per minute (rate limit).
|
|
60
65
|
"""
|
|
61
66
|
self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY", "")
|
|
62
67
|
self.default_model = model
|
|
63
68
|
self.max_tokens = max_tokens
|
|
64
69
|
self._client = AsyncAnthropic(api_key=self.api_key or "placeholder")
|
|
70
|
+
self._rate_limiter = AsyncRateLimiter(tokens_per_minute)
|
|
65
71
|
|
|
66
72
|
@property
|
|
67
73
|
def name(self) -> str:
|
|
@@ -187,6 +193,11 @@ class AnthropicProvider(ModelProvider):
|
|
|
187
193
|
# All other messages must have non-empty content
|
|
188
194
|
if not (message.content or '').strip():
|
|
189
195
|
raise ValueError(f"Message with role '{message.role}' cannot have empty content")
|
|
196
|
+
|
|
197
|
+
# Rate limiting: wait for capacity based on input tokens
|
|
198
|
+
input_tokens = TokenCounter.count_messages(messages, model=self.default_model)
|
|
199
|
+
await self._rate_limiter.wait_for_capacity(input_tokens)
|
|
200
|
+
|
|
190
201
|
system_prompt, formatted_messages = self._format_messages(messages)
|
|
191
202
|
|
|
192
203
|
params: dict[str, Any] = {
|
|
@@ -202,6 +213,7 @@ class AnthropicProvider(ModelProvider):
|
|
|
202
213
|
if tools:
|
|
203
214
|
params["tools"] = [self._format_tool(t) for t in tools]
|
|
204
215
|
|
|
216
|
+
total_output_tokens = 0
|
|
205
217
|
async with self._client.messages.stream(**params) as stream:
|
|
206
218
|
pending_tool_calls: dict[str, dict[str, Any]] = {}
|
|
207
219
|
current_tool_id: str | None = None
|
|
@@ -226,10 +238,15 @@ class AnthropicProvider(ModelProvider):
|
|
|
226
238
|
delta = event.delta
|
|
227
239
|
if delta.type == "text_delta":
|
|
228
240
|
content = delta.text
|
|
241
|
+
total_output_tokens += TokenCounter.count_text(content, model=self.default_model)
|
|
229
242
|
elif delta.type == "thinking_delta":
|
|
230
243
|
thinking = delta.thinking
|
|
244
|
+
total_output_tokens += TokenCounter.count_text(thinking, model=self.default_model)
|
|
231
245
|
elif delta.type == "input_json_delta" and current_tool_id:
|
|
232
246
|
pending_tool_calls[current_tool_id]["arguments"] += delta.partial_json
|
|
247
|
+
# Note: we don't count JSON tokens precisely here as they come in,
|
|
248
|
+
# but we could count the delta text.
|
|
249
|
+
total_output_tokens += TokenCounter.count_text(delta.partial_json, model=self.default_model)
|
|
233
250
|
|
|
234
251
|
elif event.type == "content_block_stop":
|
|
235
252
|
current_tool_id = None
|
|
@@ -261,3 +278,6 @@ class AnthropicProvider(ModelProvider):
|
|
|
261
278
|
finish_reason=finish_reason,
|
|
262
279
|
thinking=thinking,
|
|
263
280
|
)
|
|
281
|
+
|
|
282
|
+
# Record final usage
|
|
283
|
+
await self._rate_limiter.add_usage(input_tokens + total_output_tokens)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Rate limiting utilities for API providers."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
from collections import deque
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AsyncRateLimiter:
|
|
9
|
+
"""An asynchronous rate limiter using a sliding window.
|
|
10
|
+
|
|
11
|
+
Tracks token usage and provides a way to wait until enough capacity
|
|
12
|
+
is available.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, tokens_per_minute: int) -> None:
|
|
16
|
+
"""Initialize the rate limiter.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
tokens_per_minute: Maximum tokens allowed per 60-second window.
|
|
20
|
+
"""
|
|
21
|
+
self.tokens_per_minute = tokens_per_minute
|
|
22
|
+
# Each entry is (timestamp, token_count)
|
|
23
|
+
self.usage: deque[tuple[float, int]] = deque()
|
|
24
|
+
self._lock = asyncio.Lock()
|
|
25
|
+
|
|
26
|
+
async def _clean_old_usage(self) -> None:
|
|
27
|
+
"""Remove usage entries older than 60 seconds."""
|
|
28
|
+
now = time.time()
|
|
29
|
+
while self.usage and self.usage[0][0] < now - 60:
|
|
30
|
+
self.usage.popleft()
|
|
31
|
+
|
|
32
|
+
def _get_current_usage(self) -> int:
|
|
33
|
+
"""Calculate the total tokens used in the last 60 seconds."""
|
|
34
|
+
return sum(tokens for _, tokens in self.usage)
|
|
35
|
+
|
|
36
|
+
async def wait_for_capacity(self, tokens: int) -> None:
|
|
37
|
+
"""Wait until the specified number of tokens can be used.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
tokens: The number of tokens to be used.
|
|
41
|
+
"""
|
|
42
|
+
if tokens > self.tokens_per_minute:
|
|
43
|
+
# If a single request is larger than the limit, we can't really
|
|
44
|
+
# satisfy it, but we'll just wait for the window to be completely clear.
|
|
45
|
+
tokens = self.tokens_per_minute
|
|
46
|
+
|
|
47
|
+
async with self._lock:
|
|
48
|
+
while True:
|
|
49
|
+
await self._clean_old_usage()
|
|
50
|
+
current_usage = self._get_current_usage()
|
|
51
|
+
|
|
52
|
+
if current_usage + tokens <= self.tokens_per_minute:
|
|
53
|
+
break
|
|
54
|
+
|
|
55
|
+
# Wait until the oldest entry expires
|
|
56
|
+
if self.usage:
|
|
57
|
+
sleep_time = self.usage[0][0] + 60.1 - time.time()
|
|
58
|
+
if sleep_time > 0:
|
|
59
|
+
await asyncio.sleep(sleep_time)
|
|
60
|
+
else:
|
|
61
|
+
# Should not happen if current_usage > 0, but safety first
|
|
62
|
+
await asyncio.sleep(1)
|
|
63
|
+
|
|
64
|
+
async def add_usage(self, tokens: int) -> None:
|
|
65
|
+
"""Record token usage.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
tokens: The number of tokens used.
|
|
69
|
+
"""
|
|
70
|
+
async with self._lock:
|
|
71
|
+
self.usage.append((time.time(), tokens))
|
|
@@ -27,6 +27,7 @@ MODEL_LIMITS: dict[str, int] = {
|
|
|
27
27
|
"gpt-4": 8192,
|
|
28
28
|
"gpt-3.5-turbo": 16385,
|
|
29
29
|
# Anthropic models (these use different tokenization but we estimate)
|
|
30
|
+
"claude-opus-4-6": 200000,
|
|
30
31
|
"claude-sonnet-4-20250514": 200000,
|
|
31
32
|
"claude-3-7-sonnet-20250219": 200000,
|
|
32
33
|
"claude-3-5-sonnet-20241022": 200000,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|