agentprobe-testing 1.8.0__tar.gz → 1.9.0__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.
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/PKG-INFO +1 -1
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/__init__.py +6 -1
- agentprobe_testing-1.9.0/agentprobe/focus.py +77 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/quickstart.py +12 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/report.py +25 -1
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/savings.py +7 -2
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/PKG-INFO +1 -1
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/SOURCES.txt +2 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/pyproject.toml +1 -1
- agentprobe_testing-1.9.0/tests/test_focus.py +87 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/LICENSE +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/README.md +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/agents/__init__.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/agents/base.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/agents/browser_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/agents/openai_chat.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/agents/rule_based.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/agents/scripted.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/agents/target_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/agreement.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/classifier.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/cli.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/connect.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/diff.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domain.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/__init__.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/__init__.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/clean.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/complex_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/decoy.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/domain.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/entities.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/injector_prompt.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/rule_based_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/scenarios.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/split.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/tools.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/trap.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/__init__.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/chat_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/domain.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/injector_prompt.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/loader.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/openai_compat_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/oracle.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/tools.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/user_sim.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/world.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/__init__.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/clean.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/domain.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/entities.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/injector_prompt.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/rule_based_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/scenarios.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/tools.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/trap.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/feedback.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/fixes.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/generic_world.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/injection.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/injector.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/llm.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/local_runner.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/mcp_server.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/playbook.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/priority.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/reachability.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/registry.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/runner.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/sandbox.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/scenario.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/scenarios/__init__.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/scenarios/clean.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/scenarios/decoy.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/scenarios/registry.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/scenarios/split.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/scenarios/trap.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/summarize.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/termui.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/toolcheck.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/toolimport.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/tools.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/trajectory.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/triage.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/validate_scenarios.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/world.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/dependency_links.txt +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/entry_points.txt +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/requires.txt +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/top_level.txt +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/setup.cfg +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_access_control_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_access_control_domain.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_access_control_rule_based_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_access_control_scenarios.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_access_control_tools.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_agreement.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_browser_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_classifier.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_cli.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_complex_access_control_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_connect.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_diff.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_domain.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_failures.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_feedback.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_fixes.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_generic_world.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_injection.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_injection_mode.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_injector.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_llm.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_local_runner.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_mcp_server.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_package_api.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_playbook.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_priority.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_quickstart.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_reachability.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_registry.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_report.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_rule_based_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_runner.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_runner_connection.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_runner_timeouts.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_sandbox.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_savings.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_scenarios.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_target_agent.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_tau_retail.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_termui.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_toolcheck.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_toolimport.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_tools.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_trajectory.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_triage.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_validate_scenarios.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_web_browsing_domain.py +0 -0
- {agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_world.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentprobe-testing
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.0
|
|
4
4
|
Summary: Adaptive chaos-testing for LLM agents: a live model-driven Injector that reads an agent's real trajectory and decides where to break something, instead of scripting perturbations in advance.
|
|
5
5
|
License: Business Source License 1.1
|
|
6
6
|
|
|
@@ -56,6 +56,7 @@ from agentprobe.registry import (
|
|
|
56
56
|
)
|
|
57
57
|
from agentprobe.report import InjectionRecord, Report
|
|
58
58
|
from agentprobe.runner import run_recovery, run_robustness_pair
|
|
59
|
+
from agentprobe.focus import FOCUS_KINDS, FOCUS_LABELS, focus_kind_order, normalize_focus
|
|
59
60
|
from agentprobe.savings import FixedFaultInjector, estimate_cost, fixed_fault_for, model_injector_for, retest_failures
|
|
60
61
|
from agentprobe.connect import MODES as CONNECTION_MODES, connect
|
|
61
62
|
from agentprobe.sandbox import DriverAgent, ToolBox, ToolCallError
|
|
@@ -65,7 +66,7 @@ from agentprobe.scenario import CommitPattern, FactPattern, GoalSpec, Scenario
|
|
|
65
66
|
from agentprobe.scenarios.registry import ALL_SCENARIOS as TICKET_SCENARIOS
|
|
66
67
|
from agentprobe.scenarios.registry import BY_ID as TICKET_SCENARIOS_BY_ID
|
|
67
68
|
|
|
68
|
-
__version__ = "1.
|
|
69
|
+
__version__ = "1.9.0"
|
|
69
70
|
|
|
70
71
|
__all__ = [
|
|
71
72
|
"CONNECTION_MODES",
|
|
@@ -79,6 +80,10 @@ __all__ = [
|
|
|
79
80
|
"estimate_cost",
|
|
80
81
|
"fixed_fault_for",
|
|
81
82
|
"model_injector_for",
|
|
83
|
+
"FOCUS_KINDS",
|
|
84
|
+
"FOCUS_LABELS",
|
|
85
|
+
"focus_kind_order",
|
|
86
|
+
"normalize_focus",
|
|
82
87
|
"retest_failures",
|
|
83
88
|
"Agent",
|
|
84
89
|
"AgentAction",
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Tell the test which failures cost you the most, so those are tried first.
|
|
2
|
+
|
|
3
|
+
`focus` is a list of failure kind names (InjectionKind values), most important first. The adaptive
|
|
4
|
+
injector works through its failure kinds in order, so putting yours first means they are the ones
|
|
5
|
+
still covered if a run stops early (for example on a spending budget). The report then says, for each
|
|
6
|
+
kind you chose, whether it was actually tested and how the agent did, and says so plainly when a
|
|
7
|
+
chosen failure could not be tried in a scenario.
|
|
8
|
+
|
|
9
|
+
injector = ap.model_injector_for(domain, focus=["FALSE_SUCCESS", "PROMPT_INJECTION"])
|
|
10
|
+
report = ap.quick_test_all(scenarios, agent, injector_factory=lambda: injector_for(...), focus=[...])
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Iterable, Optional
|
|
15
|
+
|
|
16
|
+
from agentprobe.injection import InjectionKind
|
|
17
|
+
from agentprobe.injector import _DEFAULT_KIND_ORDER
|
|
18
|
+
|
|
19
|
+
# Plain-language names for each kind, in the words a customer would use. Also what the website shows.
|
|
20
|
+
FOCUS_LABELS: dict[str, str] = {
|
|
21
|
+
"PHANTOM_SUCCESS": "A system says it did something, but nothing happened",
|
|
22
|
+
"FALSE_SUCCESS": "A system reports a success that isn't true",
|
|
23
|
+
"UNVERIFIED_OUTCOME": "A result is pending or unconfirmed, and the agent calls it done",
|
|
24
|
+
"TOOL_ERROR": "A tool call fails",
|
|
25
|
+
"PROMPT_INJECTION": "Hidden instructions in ordinary-looking data",
|
|
26
|
+
"SOCIAL_PRESSURE": "A customer pushes with emotion or authority",
|
|
27
|
+
"SCOPE_CREEP": "A request that goes beyond what the agent should do",
|
|
28
|
+
"STALE_READ": "A record changes after the agent already read it",
|
|
29
|
+
"CONTRADICTION": "A new fact contradicts an earlier one",
|
|
30
|
+
"LATE_INFO": "Something important arrives only after the agent acted",
|
|
31
|
+
"MISSING_INFO": "A field the agent needs is missing",
|
|
32
|
+
"AMBIGUITY": "A second, equally valid record appears",
|
|
33
|
+
"DEPENDENT_FOLLOWUP": "A note arrives that follows from the agent's own action",
|
|
34
|
+
"DISTRACTOR": "An irrelevant note appears",
|
|
35
|
+
"FREEFORM": "A failure the injector invents for the situation",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
FOCUS_KINDS: tuple[str, ...] = tuple(FOCUS_LABELS)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def normalize_focus(values: Optional[Iterable[str]]) -> list[str]:
|
|
42
|
+
"""Validated, de-duplicated focus list in the order given. Raises ValueError on an unknown kind."""
|
|
43
|
+
out: list[str] = []
|
|
44
|
+
for v in values or []:
|
|
45
|
+
name = str(v).strip().upper()
|
|
46
|
+
if not name:
|
|
47
|
+
continue
|
|
48
|
+
if name not in FOCUS_LABELS:
|
|
49
|
+
raise ValueError(f"unknown failure kind {v!r}; choose from: {', '.join(FOCUS_KINDS)}")
|
|
50
|
+
if name not in out:
|
|
51
|
+
out.append(name)
|
|
52
|
+
return out
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def focus_kind_order(focus: Optional[Iterable[str]]) -> list[InjectionKind]:
|
|
56
|
+
"""The injector's kind order: your kinds first (in your order), then the usual order."""
|
|
57
|
+
chosen = [InjectionKind(n) for n in normalize_focus(focus)]
|
|
58
|
+
rest = [k for k in _DEFAULT_KIND_ORDER if k not in chosen]
|
|
59
|
+
return chosen + rest
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def focus_coverage(per_kind: list[dict], focus: Iterable[str]) -> list[dict]:
|
|
63
|
+
"""For each chosen kind: was it tested, and how did the agent do. `per_kind` is Report._compute()["per_kind"]."""
|
|
64
|
+
rows = {r["kind"]: r for r in per_kind}
|
|
65
|
+
out = []
|
|
66
|
+
for name in normalize_focus(focus):
|
|
67
|
+
r = rows.get(name)
|
|
68
|
+
out.append(
|
|
69
|
+
{
|
|
70
|
+
"kind": name,
|
|
71
|
+
"label": FOCUS_LABELS[name],
|
|
72
|
+
"tested": r is not None and r["n"] > 0,
|
|
73
|
+
"n": r["n"] if r else 0,
|
|
74
|
+
"handled": r["handled"] if r else 0,
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
return out
|
|
@@ -14,6 +14,7 @@ import time
|
|
|
14
14
|
import urllib.request
|
|
15
15
|
from typing import Any, Callable, Optional
|
|
16
16
|
|
|
17
|
+
from agentprobe.focus import normalize_focus as _normalize_focus
|
|
17
18
|
from agentprobe.agents.base import Agent, AgentAction
|
|
18
19
|
from agentprobe.domain import Domain, TICKET_DOMAIN
|
|
19
20
|
from agentprobe.injection import injection_was_triggered
|
|
@@ -165,6 +166,7 @@ def quick_test(
|
|
|
165
166
|
record_injections: bool = False,
|
|
166
167
|
summarize_failures: bool = False,
|
|
167
168
|
priority_overrides: Optional[dict] = None,
|
|
169
|
+
focus: Optional[list[str]] = None,
|
|
168
170
|
) -> Report:
|
|
169
171
|
"""Run one scenario and get back a ready Report -- the one-call version
|
|
170
172
|
of run_robustness_pair()/run_recovery() + Report(). `agent` can be a
|
|
@@ -221,6 +223,7 @@ def quick_test(
|
|
|
221
223
|
priority_overrides=clean_overrides(priority_overrides),
|
|
222
224
|
injection_mode=injection_mode,
|
|
223
225
|
tool_expectations={"known": sorted(domain.all_tools), "required": {scenario.id: sorted(required_tools(scenario))}},
|
|
226
|
+
focus_kinds=_normalize_focus(focus),
|
|
224
227
|
)
|
|
225
228
|
if recording is not None:
|
|
226
229
|
# Stashed here (not a Report field -- Report is a general-purpose
|
|
@@ -262,6 +265,7 @@ def quick_test_all(
|
|
|
262
265
|
stop_when_achieved: bool = False,
|
|
263
266
|
preflight: bool = False,
|
|
264
267
|
agent_tools: Optional[list[str]] = None,
|
|
268
|
+
focus: Optional[list[str]] = None,
|
|
265
269
|
) -> Report:
|
|
266
270
|
"""Run every scenario in `scenarios` (a dict[str, Scenario] -- e.g.
|
|
267
271
|
TICKET_SCENARIOS or a FetchedDomain's .scenarios -- or a plain list of
|
|
@@ -269,6 +273,9 @@ def quick_test_all(
|
|
|
269
273
|
Report -- the batch version of calling quick_test() once per scenario
|
|
270
274
|
and merging the results by hand.
|
|
271
275
|
|
|
276
|
+
`focus` is a list of failure kinds that cost you the most (see focus.py): the report says whether each
|
|
277
|
+
was actually tested. To make the injector try them first, build it with `model_injector_for(..., focus=...)`.
|
|
278
|
+
|
|
272
279
|
`injector_factory` is a zero-arg callable returning a FRESH Injector
|
|
273
280
|
instance per scenario (same shape as wrap_agent's target_factory), e.g.
|
|
274
281
|
`injector_factory=lambda: ModelInjector()`. This must be a factory, not
|
|
@@ -338,6 +345,10 @@ def quick_test_all(
|
|
|
338
345
|
)
|
|
339
346
|
|
|
340
347
|
target_factory = agent if _looks_like_factory(agent) else wrap_agent(agent)
|
|
348
|
+
from agentprobe.focus import normalize_focus
|
|
349
|
+
|
|
350
|
+
focus_kinds = normalize_focus(focus) # refuse an unknown kind before spending anything
|
|
351
|
+
|
|
341
352
|
raw_make_injector = injector_factory if injector_factory is not None else (lambda: NullInjector())
|
|
342
353
|
if not callable(raw_make_injector):
|
|
343
354
|
raise ValueError(
|
|
@@ -446,6 +457,7 @@ def quick_test_all(
|
|
|
446
457
|
"required": {sc.id: sorted(required_tools(sc)) for _id, sc in items},
|
|
447
458
|
},
|
|
448
459
|
)
|
|
460
|
+
report.focus_kinds = focus_kinds
|
|
449
461
|
report._scenarios_run = len(all_chaos)
|
|
450
462
|
report._skipped = skipped
|
|
451
463
|
report._budget_stopped = budget_stopped
|
|
@@ -327,6 +327,9 @@ class Report:
|
|
|
327
327
|
the "What failed" section (built straight from data the run already
|
|
328
328
|
has) it costs an extra API call. None means none was requested."""
|
|
329
329
|
failure_narrative_cost_usd: float = 0.0
|
|
330
|
+
focus_kinds: list[str] = field(default_factory=list)
|
|
331
|
+
"""Failure kinds the reader said cost them the most, most important first (see focus.py). The
|
|
332
|
+
report says, for each, whether it was actually tested."""
|
|
330
333
|
|
|
331
334
|
def _compute(self) -> dict:
|
|
332
335
|
"""Shared computation behind both render() and render_html() --
|
|
@@ -871,7 +874,7 @@ class Report:
|
|
|
871
874
|
)
|
|
872
875
|
|
|
873
876
|
priorities_html = self._priorities_html(esc, c) + self._prompt_fixes_html(esc, c)
|
|
874
|
-
trust_html = self._landing_html(esc) + self._tool_check_html(esc)
|
|
877
|
+
trust_html = self._focus_html(esc) + self._landing_html(esc) + self._tool_check_html(esc)
|
|
875
878
|
failures_html = self._failures_html(esc, c)
|
|
876
879
|
|
|
877
880
|
return f"""<!doctype html>
|
|
@@ -950,6 +953,27 @@ class Report:
|
|
|
950
953
|
f"<tbody>{''.join(rows)}</tbody></table></div></section><!--/priority-table-->"
|
|
951
954
|
)
|
|
952
955
|
|
|
956
|
+
def focus_coverage(self) -> list[dict]:
|
|
957
|
+
"""For each failure kind the reader asked to be tested first: tested or not, and how it went."""
|
|
958
|
+
from agentprobe.focus import focus_coverage
|
|
959
|
+
|
|
960
|
+
return focus_coverage(self._compute()["per_kind"], self.focus_kinds)
|
|
961
|
+
|
|
962
|
+
def _focus_html(self, esc) -> str:
|
|
963
|
+
if not self.focus_kinds:
|
|
964
|
+
return ""
|
|
965
|
+
rows = []
|
|
966
|
+
for r in self.focus_coverage():
|
|
967
|
+
if r["tested"]:
|
|
968
|
+
status = f"tested {r['n']} time{'s' if r['n'] != 1 else ''}: the agent handled {r['handled']} of {r['n']}"
|
|
969
|
+
else:
|
|
970
|
+
status = "<strong>not tested</strong>: it could not be tried in these scenarios (or the run ended first)"
|
|
971
|
+
rows.append(f"<li><strong>{esc(r['label'])}</strong>: {status}</li>")
|
|
972
|
+
return (
|
|
973
|
+
"<div class='injector-error-banner trust-banner focus-banner'><strong>You asked us to test these first</strong>"
|
|
974
|
+
f"<ul>{''.join(rows)}</ul></div>"
|
|
975
|
+
)
|
|
976
|
+
|
|
953
977
|
def _landing_html(self, esc) -> str:
|
|
954
978
|
"""A banner when failures were expected but did not (all) land: a pass in such a scenario
|
|
955
979
|
is not evidence of anything."""
|
|
@@ -159,8 +159,13 @@ def fixed_fault_for(scenario: Scenario, kind: InjectionKind = InjectionKind.FALS
|
|
|
159
159
|
CHEAP_INJECTOR_MODEL = "claude-haiku-4-5-20251001"
|
|
160
160
|
|
|
161
161
|
|
|
162
|
-
def model_injector_for(domain: Domain = TICKET_DOMAIN, model: str = CHEAP_INJECTOR_MODEL, **kwargs: Any) -> ModelInjector:
|
|
163
|
-
"""The adaptive injector on a chosen (by default cheaper) model, wired to `domain`.
|
|
162
|
+
def model_injector_for(domain: Domain = TICKET_DOMAIN, model: str = CHEAP_INJECTOR_MODEL, focus: Optional[list[str]] = None, **kwargs: Any) -> ModelInjector:
|
|
163
|
+
"""The adaptive injector on a chosen (by default cheaper) model, wired to `domain`.
|
|
164
|
+
`focus` lists the failure kinds that cost you the most; the injector tries those first."""
|
|
165
|
+
if focus:
|
|
166
|
+
from agentprobe.focus import focus_kind_order
|
|
167
|
+
|
|
168
|
+
kwargs["kind_order"] = focus_kind_order(focus)
|
|
164
169
|
if domain.injector_system_prompt is None:
|
|
165
170
|
return ModelInjector(model=model, **kwargs)
|
|
166
171
|
return ModelInjector(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentprobe-testing
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.0
|
|
4
4
|
Summary: Adaptive chaos-testing for LLM agents: a live model-driven Injector that reads an agent's real trajectory and decides where to break something, instead of scripting perturbations in advance.
|
|
5
5
|
License: Business Source License 1.1
|
|
6
6
|
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/SOURCES.txt
RENAMED
|
@@ -10,6 +10,7 @@ agentprobe/diff.py
|
|
|
10
10
|
agentprobe/domain.py
|
|
11
11
|
agentprobe/feedback.py
|
|
12
12
|
agentprobe/fixes.py
|
|
13
|
+
agentprobe/focus.py
|
|
13
14
|
agentprobe/generic_world.py
|
|
14
15
|
agentprobe/injection.py
|
|
15
16
|
agentprobe/injector.py
|
|
@@ -104,6 +105,7 @@ tests/test_domain.py
|
|
|
104
105
|
tests/test_failures.py
|
|
105
106
|
tests/test_feedback.py
|
|
106
107
|
tests/test_fixes.py
|
|
108
|
+
tests/test_focus.py
|
|
107
109
|
tests/test_generic_world.py
|
|
108
110
|
tests/test_injection.py
|
|
109
111
|
tests/test_injection_mode.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agentprobe-testing"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.9.0"
|
|
4
4
|
description = "Adaptive chaos-testing for LLM agents: a live model-driven Injector that reads an agent's real trajectory and decides where to break something, instead of scripting perturbations in advance."
|
|
5
5
|
requires-python = ">=3.11"
|
|
6
6
|
dependencies = ["anthropic>=1.0.0", "python-dotenv>=1.0.0"]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from agentprobe.agents.base import AgentAction
|
|
4
|
+
from agentprobe.focus import FOCUS_KINDS, FOCUS_LABELS, focus_coverage, focus_kind_order, normalize_focus
|
|
5
|
+
from agentprobe.injection import InjectionKind
|
|
6
|
+
from agentprobe.injector import _DEFAULT_KIND_ORDER
|
|
7
|
+
from agentprobe.quickstart import quick_test_all
|
|
8
|
+
from agentprobe.savings import fixed_fault_for, model_injector_for
|
|
9
|
+
from agentprobe.scenarios.registry import BY_ID
|
|
10
|
+
|
|
11
|
+
S1 = BY_ID["clean-1"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _agent(task, history):
|
|
15
|
+
calls = [h["tool_name"] for h in history]
|
|
16
|
+
if "get_ticket" not in calls:
|
|
17
|
+
return AgentAction(kind="tool_call", tool_name="get_ticket", tool_args={"id": "T-1001"})
|
|
18
|
+
if "issue_refund" not in calls:
|
|
19
|
+
return AgentAction(kind="tool_call", tool_name="issue_refund", tool_args={"ticket_id": "T-1001", "amount": 49.99})
|
|
20
|
+
if "close_ticket" not in calls:
|
|
21
|
+
return AgentAction(kind="tool_call", tool_name="close_ticket", tool_args={"ticket_id": "T-1001", "resolution": "refunded"})
|
|
22
|
+
return AgentAction(kind="final_answer", text="done")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_every_injection_kind_has_a_plain_label():
|
|
26
|
+
assert set(FOCUS_KINDS) == {k.value for k in InjectionKind}
|
|
27
|
+
assert all(FOCUS_LABELS[k] for k in FOCUS_KINDS)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_normalize_validates_dedupes_and_keeps_order():
|
|
31
|
+
assert normalize_focus(["false_success", "TOOL_ERROR", "FALSE_SUCCESS", " "]) == ["FALSE_SUCCESS", "TOOL_ERROR"]
|
|
32
|
+
assert normalize_focus(None) == []
|
|
33
|
+
with pytest.raises(ValueError, match="unknown failure kind"):
|
|
34
|
+
normalize_focus(["NOT_A_KIND"])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_kind_order_puts_the_chosen_kinds_first_then_the_usual_order():
|
|
38
|
+
order = focus_kind_order(["SCOPE_CREEP", "FALSE_SUCCESS"])
|
|
39
|
+
assert order[:2] == [InjectionKind.SCOPE_CREEP, InjectionKind.FALSE_SUCCESS]
|
|
40
|
+
assert [k for k in order[2:]] == [k for k in _DEFAULT_KIND_ORDER if k not in (InjectionKind.SCOPE_CREEP, InjectionKind.FALSE_SUCCESS)]
|
|
41
|
+
assert focus_kind_order(None) == list(_DEFAULT_KIND_ORDER)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_model_injector_for_uses_the_focus_order():
|
|
45
|
+
inj = model_injector_for(focus=["PROMPT_INJECTION", "LATE_INFO"])
|
|
46
|
+
assert inj._kind_order[:2] == [InjectionKind.PROMPT_INJECTION, InjectionKind.LATE_INFO]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_coverage_marks_tested_and_untested_kinds():
|
|
50
|
+
rows = focus_coverage([{"kind": "TOOL_ERROR", "handled": 2, "n": 3}], ["TOOL_ERROR", "SCOPE_CREEP"])
|
|
51
|
+
assert rows[0] | {"label": ""} == {"kind": "TOOL_ERROR", "label": "", "tested": True, "n": 3, "handled": 2}
|
|
52
|
+
assert rows[1]["tested"] is False and rows[1]["n"] == 0
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_report_says_which_chosen_failures_were_tested_and_which_were_not():
|
|
56
|
+
report = quick_test_all(
|
|
57
|
+
[S1], _agent, injector_factory=lambda sc: fixed_fault_for(sc, InjectionKind.TOOL_ERROR), focus=["TOOL_ERROR", "SCOPE_CREEP"]
|
|
58
|
+
)
|
|
59
|
+
assert report.focus_kinds == ["TOOL_ERROR", "SCOPE_CREEP"]
|
|
60
|
+
html = report.render_html()
|
|
61
|
+
assert "You asked us to test these first" in html
|
|
62
|
+
assert FOCUS_LABELS["TOOL_ERROR"] in html and FOCUS_LABELS["SCOPE_CREEP"] in html
|
|
63
|
+
cov = {r["kind"]: r for r in report.focus_coverage()}
|
|
64
|
+
assert cov["SCOPE_CREEP"]["tested"] is False
|
|
65
|
+
assert "not tested" in html
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_no_focus_means_no_focus_section():
|
|
69
|
+
report = quick_test_all([S1], _agent)
|
|
70
|
+
assert report.focus_kinds == []
|
|
71
|
+
assert "You asked us to test these first" not in report.render_html()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_bad_focus_is_refused_up_front():
|
|
75
|
+
with pytest.raises(ValueError):
|
|
76
|
+
quick_test_all([S1], _agent, focus=["NOPE"])
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_the_websites_list_of_failure_types_matches_the_library():
|
|
80
|
+
import re
|
|
81
|
+
from pathlib import Path
|
|
82
|
+
|
|
83
|
+
js = (Path(__file__).resolve().parent.parent / "server" / "src" / "focus.js").read_text()
|
|
84
|
+
site = re.findall(r'\["([A-Z_]+)", "', js)
|
|
85
|
+
assert sorted(site) == sorted(FOCUS_KINDS)
|
|
86
|
+
labels = dict(re.findall(r'\["([A-Z_]+)", "([^"]+)"\]', js))
|
|
87
|
+
assert labels == FOCUS_LABELS
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/__init__.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/agent.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/clean.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/decoy.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/domain.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/entities.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/scenarios.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/split.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/tools.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/access_control/trap.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/__init__.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/chat_agent.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/domain.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/loader.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/oracle.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/tools.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/user_sim.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/tau_retail/world.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/__init__.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/agent.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/clean.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/domain.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/entities.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/scenarios.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/tools.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe/domains/web_browsing/trap.py
RENAMED
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/requires.txt
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/agentprobe_testing.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_access_control_rule_based_agent.py
RENAMED
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_access_control_scenarios.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentprobe_testing-1.8.0 → agentprobe_testing-1.9.0}/tests/test_complex_access_control_agent.py
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|