Files
TheRock/rocm-systems/experimental/python/perfxpert/tests/test_agents/test_runtime.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

237 lines
7.8 KiB
Python

"""Tests for perfxpert.agents.runtime — session factory + provider wiring."""
import os
import pytest
from perfxpert.agents import runtime as runtime_module
from perfxpert.agents import schemas
from perfxpert.providers._exceptions import (
AuthError,
ProviderChainExhausted,
RateLimitError,
TransientError,
)
from perfxpert.runtime import RecursionGuardViolation, recursion_guard
def test_session_builds_with_anthropic(monkeypatch):
# Stub so provider lookup succeeds without API key
monkeypatch.setenv("ANTHROPIC_API_KEY", "fake")
s = runtime_module.build_session(provider="anthropic")
assert s.provider == "anthropic"
assert s.providers == ("anthropic",)
assert s.session_id is not None
def test_session_airgap_has_no_provider():
s = runtime_module.build_session(airgap=True)
assert s.airgap is True
def test_session_rejects_opencode_recursion(monkeypatch):
monkeypatch.setenv("PERFXPERT_IN_OPENCODE_SESSION", "1")
with pytest.raises(RecursionGuardViolation):
runtime_module.build_session(provider="opencode")
def test_session_rejects_opencode_recursion_from_local_session_state():
with recursion_guard.opencode_session():
with pytest.raises(RecursionGuardViolation):
runtime_module.build_session(provider="opencode")
def test_session_honors_PERFXPERT_AIRGAP(monkeypatch):
monkeypatch.setenv("PERFXPERT_AIRGAP", "1")
s = runtime_module.build_session()
assert s.airgap is True
def test_session_unknown_provider_raises():
with pytest.raises(ValueError, match="unknown provider"):
runtime_module.build_session(provider="my-fake-llm")
def test_session_parses_fallback_chain_with_primary_first(monkeypatch):
monkeypatch.setenv("PERFXPERT_LLM_FALLBACK_CHAIN", "openai, anthropic, openai, private")
session = runtime_module.build_session(provider="anthropic")
assert session.providers == ("anthropic", "openai", "private")
def test_session_rejects_unknown_provider_in_fallback_chain(monkeypatch):
monkeypatch.setenv("PERFXPERT_LLM_FALLBACK_CHAIN", "openai,not-real")
with pytest.raises(ValueError, match="unknown provider 'not-real'"):
runtime_module.build_session(provider="anthropic")
def test_session_applies_recursion_guard_to_all_fallback_candidates(monkeypatch):
monkeypatch.setenv("PERFXPERT_LLM_FALLBACK_CHAIN", "opencode")
with recursion_guard.opencode_session():
with pytest.raises(RecursionGuardViolation):
runtime_module.build_session(provider="anthropic")
def test_session_generates_session_id_if_missing():
s = runtime_module.build_session(airgap=True)
assert s.session_id is not None
assert len(s.session_id) > 5
def test_session_preserves_explicit_session_id():
s = runtime_module.build_session(airgap=True, session_id="abc-123")
assert s.session_id == "abc-123"
def test_session_run_root_returns_root_output(monkeypatch):
"""The session.run_root() facade returns a RootOutput."""
s = runtime_module.build_session(airgap=True)
result = s.run_root(schemas.RootInput(user_query="why slow?", database_path=None))
assert isinstance(result, schemas.RootOutput)
@pytest.mark.parametrize(
("method_name", "module_name", "func_name", "payload"),
[
(
"run_compute_specialist",
"compute_specialist",
"run_compute_specialist",
schemas.ComputeSpecialistInput(gfx_id="gfx942", hot_kernels=[]),
),
(
"run_memory_specialist",
"memory_specialist",
"run_memory_specialist",
schemas.MemorySpecialistInput(gfx_id="gfx942", hot_kernels=[]),
),
(
"run_latency_specialist",
"latency_specialist",
"run_latency_specialist",
schemas.LatencySpecialistInput(gfx_id="gfx942", hot_kernels=[]),
),
(
"run_diff_specialist",
"diff_specialist",
"run_diff_specialist",
schemas.DiffSpecialistInput(
baseline_db="baseline.db",
new_db="new.db",
top_kernels=1,
),
),
],
)
def test_session_restored_specialist_facades_dispatch(
monkeypatch,
method_name,
module_name,
func_name,
payload,
):
session = runtime_module.build_session(airgap=True)
sentinel = object()
def _fake_runner(arg, *, airgap=None, provider="anthropic"):
assert arg == payload
assert airgap is True
return sentinel
monkeypatch.setattr(
getattr(runtime_module, module_name),
func_name,
_fake_runner,
)
assert getattr(session, method_name)(payload) is sentinel
def test_session_live_call_scopes_explicit_api_key(monkeypatch):
monkeypatch.setenv("OPENAI_API_KEY", "sk-old")
seen = {}
expected = schemas.RootOutput(
narrative="ok",
recommendations=[],
primary_bottleneck="mixed",
warnings=[],
metadata={},
)
def _fake_run_root(payload, *, provider="anthropic", airgap=None):
assert isinstance(payload, schemas.RootInput)
assert provider == "openai"
assert airgap is None
seen["during"] = os.environ.get("OPENAI_API_KEY")
return expected
monkeypatch.setattr(runtime_module.root, "run_root", _fake_run_root)
session = runtime_module.build_session(provider="openai", api_key="sk-new")
assert session.run_root(
schemas.RootInput(user_query="why slow?", database_path=None)
) == expected
assert seen["during"] == "sk-new"
assert os.environ.get("OPENAI_API_KEY") == "sk-old"
def test_session_live_call_cascades_on_retryable_provider_error(monkeypatch):
expected = schemas.RootOutput(
narrative="ok",
recommendations=[],
primary_bottleneck="mixed",
warnings=[],
metadata={},
)
seen = []
def _fake_run_root(payload, *, provider="anthropic", airgap=None):
seen.append(provider)
if provider == "anthropic":
raise RateLimitError("anthropic", retry_after=0.5, message="slow down")
assert provider == "openai"
return expected
monkeypatch.setenv("PERFXPERT_LLM_FALLBACK_CHAIN", "openai")
monkeypatch.setattr(runtime_module.root, "run_root", _fake_run_root)
session = runtime_module.build_session(provider="anthropic")
out = session.run_root(schemas.RootInput(user_query="why slow?", database_path=None))
assert out == expected
assert seen == ["anthropic", "openai"]
def test_session_live_call_raises_chain_exhausted_after_retryables(monkeypatch):
seen = []
def _fake_run_root(payload, *, provider="anthropic", airgap=None):
seen.append(provider)
raise TransientError(provider, kind="transport", message="temporary failure")
monkeypatch.setenv("PERFXPERT_LLM_FALLBACK_CHAIN", "openai")
monkeypatch.setattr(runtime_module.root, "run_root", _fake_run_root)
session = runtime_module.build_session(provider="anthropic")
with pytest.raises(ProviderChainExhausted) as exc_info:
session.run_root(schemas.RootInput(user_query="why slow?", database_path=None))
assert exc_info.value.providers == ("anthropic", "openai")
assert seen == ["anthropic", "openai"]
def test_session_live_call_surfaces_non_retryable_provider_error(monkeypatch):
seen = []
def _fake_run_root(payload, *, provider="anthropic", airgap=None):
seen.append(provider)
raise AuthError(provider, "bad key")
monkeypatch.setenv("PERFXPERT_LLM_FALLBACK_CHAIN", "openai")
monkeypatch.setattr(runtime_module.root, "run_root", _fake_run_root)
session = runtime_module.build_session(provider="anthropic")
with pytest.raises(AuthError):
session.run_root(schemas.RootInput(user_query="why slow?", database_path=None))
assert seen == ["anthropic"]