项目文件夹

文件
Gelei Deng b9869307d0 Legacy multi llm base (#470)
* fix: 🐛 minor typo and build process

* feat: 🎸 [WIP] Pentest mode

* feat: 🎸 code abstraction

* feat: modernize legacy PentestGPT with native multi-LLM support (#469)

Rebuild the classic USENIX-2024 interactive PentestGPT (reasoning / generation /
parsing sessions + Pentesting Task Tree + REPL) as a standalone
`pentestgpt_legacy` package on a native per-provider LLM layer that supports the
latest 2026 models.

- llm/: BaseProvider + OpenAI-compatible / Anthropic / Gemini connectors, a
  web-verified model registry (OpenAI, Anthropic, Gemini, DeepSeek, xAI, Qwen,
  Moonshot, local Ollama), a factory, and an LLMClient bridging async providers
  to the core's synchronous send_new_message/send_message session API.
- CLI `pentestgpt-legacy`: --list-models and --smoke-test (live per-model
  round-trip matrix), plus --reasoning-model / --parsing-model / --base-url.
- Tests: 25 unit tests (mocked, no network). Live smoke test verified 22/22
  models with a configured key respond.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(backend): address review on ClaudeCodeBackend subprocess handling

- _build_env: pop ANTHROPIC_API_KEY instead of setting it to "", so an empty
  value can't shadow the CLI's own auth fallback (e.g. subscription login).
- _kill_process: reap the force-killed process with os.waitpid(.., WNOHANG)
  instead of calling the proc.wait() coroutine without awaiting it (removes the
  "coroutine was never awaited" warning).
- query/_drain_stderr: drain subprocess stderr in a background task so its pipe
  buffer can't fill and deadlock the child.

Also reformats backend.py, fixing the failing Lint (ruff format) check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(docker-test): assert uv instead of Poetry in container health check

The project migrated from Poetry to uv (the Dockerfile installs uv to
/home/pentester/.local/bin, which is on PATH), so test_poetry_installed failed
with exit 127. Replace it with test_uv_installed checking `uv --version`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:25:45 +08:00

67 行
2.3 KiB
Python

"""Tests for the provider/client factory."""
import pytest
from pentestgpt_legacy.llm import factory
from pentestgpt_legacy.llm.config import LLMSettings
from pentestgpt_legacy.llm.factory import (
MissingCredentialsError,
UnknownModelError,
get_client,
list_models,
)
from pentestgpt_legacy.llm.providers import (
AnthropicProvider,
OpenAICompatibleProvider,
)
pytestmark = pytest.mark.unit
def _settings(**kwargs: str) -> LLMSettings:
return LLMSettings(_env_file=None, **kwargs)
def test_get_client_openai(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(factory, "get_settings", lambda: _settings(openai_api_key="sk-x"))
client = get_client("gpt-5.5")
assert isinstance(client.provider, OpenAICompatibleProvider)
assert client.spec.id == "gpt-5.5"
assert client.provider.base_url is None # OpenAI uses SDK default
def test_get_client_deepseek_base_url(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(factory, "get_settings", lambda: _settings(deepseek_api_key="sk-d"))
client = get_client("deepseek-v4-flash")
assert isinstance(client.provider, OpenAICompatibleProvider)
assert client.provider.base_url == "https://api.deepseek.com"
def test_get_client_anthropic(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(factory, "get_settings", lambda: _settings(anthropic_api_key="sk-a"))
client = get_client("claude-opus-4-8")
assert isinstance(client.provider, AnthropicProvider)
def test_get_client_unknown_raises(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(factory, "get_settings", lambda: _settings(openai_api_key="x"))
with pytest.raises(UnknownModelError):
get_client("no-such-model")
def test_get_client_missing_key_raises(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(factory, "get_settings", lambda: _settings()) # no keys
with pytest.raises(MissingCredentialsError):
get_client("gemini-3.1-pro")
def test_get_client_ollama_no_key(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(factory, "get_settings", lambda: _settings())
client = get_client("ollama:qwen3")
assert client.spec.api_id == "qwen3"
assert client.provider.base_url == "http://localhost:11434/v1"
def test_list_models_nonempty() -> None:
assert list_models()