agentprobe-testing 0.8.2__tar.gz → 0.8.4__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-0.8.2 → agentprobe_testing-0.8.4}/PKG-INFO +1 -1
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/__init__.py +1 -1
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/llm.py +54 -2
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/PKG-INFO +1 -1
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/pyproject.toml +1 -1
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_access_control_agent.py +7 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_classifier.py +5 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_complex_access_control_agent.py +5 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_injector.py +31 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_llm.py +20 -3
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_target_agent.py +8 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_validate_scenarios.py +6 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/LICENSE +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/README.md +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/agents/__init__.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/agents/base.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/agents/rule_based.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/agents/scripted.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/agents/target_agent.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/agreement.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/classifier.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/cli.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/diff.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domain.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/__init__.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/__init__.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/agent.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/clean.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/complex_agent.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/decoy.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/domain.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/entities.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/injector_prompt.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/rule_based_agent.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/scenarios.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/split.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/tools.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/trap.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/feedback.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/generic_world.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/injection.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/injector.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/playbook.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/quickstart.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/reachability.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/registry.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/report.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/runner.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/scenario.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/scenarios/__init__.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/scenarios/clean.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/scenarios/decoy.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/scenarios/registry.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/scenarios/split.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/scenarios/trap.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/termui.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/tools.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/trajectory.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/triage.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/validate_scenarios.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/world.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/SOURCES.txt +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/dependency_links.txt +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/entry_points.txt +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/requires.txt +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/top_level.txt +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/setup.cfg +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_access_control_domain.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_access_control_rule_based_agent.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_access_control_scenarios.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_access_control_tools.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_agreement.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_cli.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_diff.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_domain.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_feedback.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_generic_world.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_injection.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_package_api.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_playbook.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_quickstart.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_reachability.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_registry.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_report.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_rule_based_agent.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_runner.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_scenarios.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_termui.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_tools.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_trajectory.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_triage.py +0 -0
- {agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_world.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentprobe-testing
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
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
|
|
|
@@ -60,7 +60,7 @@ from agentprobe.scenario import CommitPattern, FactPattern, GoalSpec, Scenario
|
|
|
60
60
|
from agentprobe.scenarios.registry import ALL_SCENARIOS as TICKET_SCENARIOS
|
|
61
61
|
from agentprobe.scenarios.registry import BY_ID as TICKET_SCENARIOS_BY_ID
|
|
62
62
|
|
|
63
|
-
__version__ = "0.8.
|
|
63
|
+
__version__ = "0.8.4"
|
|
64
64
|
|
|
65
65
|
__all__ = [
|
|
66
66
|
"Agent",
|
|
@@ -15,20 +15,72 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
from typing import Any
|
|
17
17
|
|
|
18
|
+
import anthropic._streaming as _anthropic_streaming
|
|
19
|
+
|
|
18
20
|
_TEMPERATURE_SUPPORTED: dict[str, bool] = {}
|
|
19
21
|
|
|
20
22
|
|
|
23
|
+
def _patch_sse_decoder_for_memoryview_chunks() -> None:
|
|
24
|
+
"""anthropic._streaming.SSEDecoder calls `chunk.splitlines()` directly on
|
|
25
|
+
whatever the underlying HTTP iterator yields, assuming it's always
|
|
26
|
+
`bytes`. Under Cloudflare's Python Worker runtime (Pyodide/emscripten),
|
|
27
|
+
the httpx2-jsfetch transport that bridges JS's fetch API yields
|
|
28
|
+
`memoryview` chunks instead -- confirmed live: every single streamed
|
|
29
|
+
Injector call failed with AttributeError('memoryview' object has no
|
|
30
|
+
attribute 'splitlines') the moment create_deterministic() switched from
|
|
31
|
+
create() to stream() to dodge a separate idle-timeout bug. `bytes(chunk)`
|
|
32
|
+
is a no-op for already-bytes chunks and a cheap copy for memoryview, so
|
|
33
|
+
coercing at the one boundary where raw chunks enter the SSE decoder
|
|
34
|
+
fixes this without reimplementing SSE parsing ourselves. Wrapped in
|
|
35
|
+
try/except so an anthropic SDK upgrade that moves this internal method
|
|
36
|
+
fails quiet (falls back to the original, unpatched bug) rather than
|
|
37
|
+
breaking import entirely.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
decoder_cls = _anthropic_streaming.SSEDecoder
|
|
41
|
+
original_iter_chunks = decoder_cls._iter_chunks
|
|
42
|
+
original_aiter_chunks = decoder_cls._aiter_chunks
|
|
43
|
+
|
|
44
|
+
def _iter_chunks(self, iterator):
|
|
45
|
+
return original_iter_chunks(self, (bytes(chunk) for chunk in iterator))
|
|
46
|
+
|
|
47
|
+
async def _aiter_chunks(self, iterator):
|
|
48
|
+
async def _coerced():
|
|
49
|
+
async for chunk in iterator:
|
|
50
|
+
yield bytes(chunk)
|
|
51
|
+
|
|
52
|
+
async for sse_chunk in original_aiter_chunks(self, _coerced()):
|
|
53
|
+
yield sse_chunk
|
|
54
|
+
|
|
55
|
+
decoder_cls._iter_chunks = _iter_chunks
|
|
56
|
+
decoder_cls._aiter_chunks = _aiter_chunks
|
|
57
|
+
except AttributeError:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
_patch_sse_decoder_for_memoryview_chunks()
|
|
62
|
+
|
|
63
|
+
|
|
21
64
|
def create_deterministic(client: Any, **kwargs: Any) -> Any:
|
|
65
|
+
# Streamed, not create(): a non-streaming call blocks with zero bytes on
|
|
66
|
+
# the wire until the whole completion is ready, which under Cloudflare's
|
|
67
|
+
# Python Worker runtime (where the Injector actually runs) gets read as
|
|
68
|
+
# an idle connection and killed as a timeout -- confirmed live, this
|
|
69
|
+
# exact symptom (APITimeoutError on every single decide() call, even a
|
|
70
|
+
# trivial first step) is what the same non-streaming pattern already
|
|
71
|
+
# caused for domain generation, fixed there the same way.
|
|
22
72
|
model = kwargs["model"]
|
|
23
73
|
if _TEMPERATURE_SUPPORTED.get(model, True):
|
|
24
74
|
try:
|
|
25
|
-
|
|
75
|
+
with client.messages.stream(**kwargs, extra_body={"temperature": 0}) as stream:
|
|
76
|
+
return stream.get_final_message()
|
|
26
77
|
except Exception as e:
|
|
27
78
|
if "temperature" in str(e).lower():
|
|
28
79
|
_TEMPERATURE_SUPPORTED[model] = False
|
|
29
80
|
else:
|
|
30
81
|
raise
|
|
31
|
-
|
|
82
|
+
with client.messages.stream(**kwargs) as stream:
|
|
83
|
+
return stream.get_final_message()
|
|
32
84
|
|
|
33
85
|
|
|
34
86
|
_PRICE_PER_TOKEN: dict[str, tuple[float, float]] = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentprobe-testing
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
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
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agentprobe-testing"
|
|
3
|
-
version = "0.8.
|
|
3
|
+
version = "0.8.4"
|
|
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"]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from types import SimpleNamespace
|
|
2
2
|
|
|
3
|
+
from conftest import stream_from_create
|
|
4
|
+
|
|
3
5
|
from agentprobe.domains.access_control.agent import AccessControlAgent
|
|
4
6
|
|
|
5
7
|
|
|
@@ -15,6 +17,7 @@ def test_next_action_returns_tool_call_and_uses_cached_system(monkeypatch):
|
|
|
15
17
|
captured.update(kwargs)
|
|
16
18
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_request", input={"id": "AR-1"})
|
|
17
19
|
return SimpleNamespace(content=[tool_use], usage=_usage())
|
|
20
|
+
stream = stream_from_create(create)
|
|
18
21
|
|
|
19
22
|
class FakeClient:
|
|
20
23
|
def __init__(self):
|
|
@@ -41,6 +44,7 @@ def test_next_action_returns_final_answer_when_no_tool_use(monkeypatch):
|
|
|
41
44
|
def create(self, **kwargs):
|
|
42
45
|
text_block = SimpleNamespace(type="text", text="All done, access granted.")
|
|
43
46
|
return SimpleNamespace(content=[text_block], usage=_usage())
|
|
47
|
+
stream = stream_from_create(create)
|
|
44
48
|
|
|
45
49
|
class FakeClient:
|
|
46
50
|
def __init__(self):
|
|
@@ -63,6 +67,7 @@ def test_next_action_prices_cost_by_the_agents_own_model_not_a_fixed_rate(monkey
|
|
|
63
67
|
def create(self, **kwargs):
|
|
64
68
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_request", input={"id": "AR-1"})
|
|
65
69
|
return SimpleNamespace(content=[tool_use], usage=_usage(input_tokens=1000, output_tokens=1000))
|
|
70
|
+
stream = stream_from_create(create)
|
|
66
71
|
|
|
67
72
|
class FakeClient:
|
|
68
73
|
def __init__(self):
|
|
@@ -98,6 +103,7 @@ def test_next_action_includes_cache_write_cost_from_a_real_shaped_response(monke
|
|
|
98
103
|
input_tokens=100, output_tokens=50, cache_creation_input_tokens=5000, cache_read_input_tokens=0
|
|
99
104
|
),
|
|
100
105
|
)
|
|
106
|
+
stream = stream_from_create(create)
|
|
101
107
|
|
|
102
108
|
class FakeClient:
|
|
103
109
|
def __init__(self):
|
|
@@ -120,6 +126,7 @@ def test_observe_appends_tool_result_and_marks_errors(monkeypatch):
|
|
|
120
126
|
def create(self, **kwargs):
|
|
121
127
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_request", input={"id": "AR-1"})
|
|
122
128
|
return SimpleNamespace(content=[tool_use], usage=_usage())
|
|
129
|
+
stream = stream_from_create(create)
|
|
123
130
|
|
|
124
131
|
class FakeClient:
|
|
125
132
|
def __init__(self):
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from types import SimpleNamespace
|
|
2
2
|
|
|
3
|
+
from conftest import stream_from_create
|
|
4
|
+
|
|
3
5
|
from agentprobe.classifier import ResponseClassification, _render_classification_prompt, classify_response
|
|
4
6
|
from agentprobe.injection import AppliedInjection, Injection, InjectionKind, ResponseSignature
|
|
5
7
|
from agentprobe.trajectory import Reachability, ReachabilityStatus, Step
|
|
@@ -68,6 +70,7 @@ def test_classify_response_parses_forced_tool_call(monkeypatch):
|
|
|
68
70
|
input={"classification": "HANDLED", "rationale": "retried and succeeded"},
|
|
69
71
|
)
|
|
70
72
|
return SimpleNamespace(content=[tool_use], usage=SimpleNamespace(input_tokens=100, output_tokens=20))
|
|
73
|
+
stream = stream_from_create(create)
|
|
71
74
|
|
|
72
75
|
class FakeClient:
|
|
73
76
|
def __init__(self):
|
|
@@ -91,6 +94,7 @@ def test_classify_response_raises_clear_error_when_no_tool_use_block(monkeypatch
|
|
|
91
94
|
def create(self, **kwargs):
|
|
92
95
|
text_block = SimpleNamespace(type="text", text="I refuse to classify this.")
|
|
93
96
|
return SimpleNamespace(content=[text_block], stop_reason="end_turn")
|
|
97
|
+
stream = stream_from_create(create)
|
|
94
98
|
|
|
95
99
|
class FakeClient:
|
|
96
100
|
def __init__(self):
|
|
@@ -116,6 +120,7 @@ def test_classify_response_raises_clear_error_when_tool_call_missing_fields(monk
|
|
|
116
120
|
input={"classification": "HANDLED"}, # missing "rationale"
|
|
117
121
|
)
|
|
118
122
|
return SimpleNamespace(content=[tool_use], stop_reason="max_tokens")
|
|
123
|
+
stream = stream_from_create(create)
|
|
119
124
|
|
|
120
125
|
class FakeClient:
|
|
121
126
|
def __init__(self):
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_complex_access_control_agent.py
RENAMED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from types import SimpleNamespace
|
|
2
2
|
|
|
3
|
+
from conftest import stream_from_create
|
|
4
|
+
|
|
3
5
|
from agentprobe.domains.access_control.complex_agent import ComplexAccessControlAgent
|
|
4
6
|
|
|
5
7
|
|
|
@@ -15,6 +17,7 @@ def test_next_action_returns_tool_call_and_uses_cached_system(monkeypatch):
|
|
|
15
17
|
captured.update(kwargs)
|
|
16
18
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_request", input={"id": "AR-1"})
|
|
17
19
|
return SimpleNamespace(content=[tool_use], usage=_usage())
|
|
20
|
+
stream = stream_from_create(create)
|
|
18
21
|
|
|
19
22
|
class FakeClient:
|
|
20
23
|
def __init__(self):
|
|
@@ -40,6 +43,7 @@ def test_next_action_returns_final_answer_when_no_tool_use(monkeypatch):
|
|
|
40
43
|
def create(self, **kwargs):
|
|
41
44
|
text_block = SimpleNamespace(type="text", text="Escalated per policy, did not grant directly.")
|
|
42
45
|
return SimpleNamespace(content=[text_block], usage=_usage())
|
|
46
|
+
stream = stream_from_create(create)
|
|
43
47
|
|
|
44
48
|
class FakeClient:
|
|
45
49
|
def __init__(self):
|
|
@@ -62,6 +66,7 @@ def test_observe_appends_tool_result_and_marks_errors(monkeypatch):
|
|
|
62
66
|
def create(self, **kwargs):
|
|
63
67
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_request", input={"id": "AR-1"})
|
|
64
68
|
return SimpleNamespace(content=[tool_use], usage=_usage())
|
|
69
|
+
stream = stream_from_create(create)
|
|
65
70
|
|
|
66
71
|
class FakeClient:
|
|
67
72
|
def __init__(self):
|
|
@@ -2,6 +2,8 @@ from types import SimpleNamespace
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
+
from conftest import stream_from_create
|
|
6
|
+
|
|
5
7
|
from agentprobe.injection import ArmedInjection, CommitPattern, Injection, InjectionKind, ResponseSignature, Trigger
|
|
6
8
|
from agentprobe.injector import (
|
|
7
9
|
HardcodedToolErrorInjector,
|
|
@@ -113,6 +115,7 @@ def test_model_injector_declined_for_budget_stays_zero_when_floor_never_bites(mo
|
|
|
113
115
|
class FakeMessages:
|
|
114
116
|
def create(self, **kwargs):
|
|
115
117
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
118
|
+
stream = stream_from_create(create)
|
|
116
119
|
|
|
117
120
|
class FakeClient:
|
|
118
121
|
def __init__(self):
|
|
@@ -133,6 +136,7 @@ def test_model_injector_accumulates_cost_across_calls_but_not_when_declined_for_
|
|
|
133
136
|
content=[SimpleNamespace(type="tool_use", name="wait", input={})],
|
|
134
137
|
usage=SimpleNamespace(input_tokens=100, output_tokens=50),
|
|
135
138
|
)
|
|
139
|
+
stream = stream_from_create(create)
|
|
136
140
|
|
|
137
141
|
class FakeClient:
|
|
138
142
|
def __init__(self):
|
|
@@ -172,6 +176,7 @@ def test_model_injector_includes_cache_write_cost_from_a_real_shaped_response(mo
|
|
|
172
176
|
input_tokens=100, output_tokens=50, cache_creation_input_tokens=5000, cache_read_input_tokens=0
|
|
173
177
|
),
|
|
174
178
|
)
|
|
179
|
+
stream = stream_from_create(create)
|
|
175
180
|
|
|
176
181
|
class FakeClient:
|
|
177
182
|
def __init__(self):
|
|
@@ -200,6 +205,7 @@ def test_free_policy_offers_all_kinds_and_never_forces(monkeypatch):
|
|
|
200
205
|
def create(self, **kwargs):
|
|
201
206
|
captured.update(kwargs)
|
|
202
207
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
208
|
+
stream = stream_from_create(create)
|
|
203
209
|
|
|
204
210
|
class FakeClient:
|
|
205
211
|
def __init__(self):
|
|
@@ -230,6 +236,7 @@ def test_free_policy_ignores_remaining_step_budget_forcing():
|
|
|
230
236
|
def create(self, **kwargs):
|
|
231
237
|
captured.update(kwargs)
|
|
232
238
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
239
|
+
stream = stream_from_create(create)
|
|
233
240
|
|
|
234
241
|
class FakeClient:
|
|
235
242
|
def __init__(self):
|
|
@@ -262,6 +269,7 @@ def test_forced_coverage_policy_never_offers_wait_while_a_kind_is_pending(monkey
|
|
|
262
269
|
},
|
|
263
270
|
)
|
|
264
271
|
return SimpleNamespace(content=[tool_use], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
272
|
+
stream = stream_from_create(create)
|
|
265
273
|
|
|
266
274
|
class FakeClient:
|
|
267
275
|
def __init__(self):
|
|
@@ -285,6 +293,7 @@ def test_decision_log_records_every_call_including_waits_and_declines():
|
|
|
285
293
|
class FakeMessages:
|
|
286
294
|
def create(self, **kwargs):
|
|
287
295
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
296
|
+
stream = stream_from_create(create)
|
|
288
297
|
|
|
289
298
|
class FakeClient:
|
|
290
299
|
def __init__(self):
|
|
@@ -311,6 +320,7 @@ def test_decision_log_records_declined_for_budget_without_calling_the_model():
|
|
|
311
320
|
def create(self, **kwargs):
|
|
312
321
|
call_count["n"] += 1
|
|
313
322
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
323
|
+
stream = stream_from_create(create)
|
|
314
324
|
|
|
315
325
|
class FakeClient:
|
|
316
326
|
def __init__(self):
|
|
@@ -338,6 +348,7 @@ def test_model_injector_wait_returns_none(monkeypatch):
|
|
|
338
348
|
def create(self, **kwargs):
|
|
339
349
|
wait_call = SimpleNamespace(type="tool_use", name="wait", input={})
|
|
340
350
|
return SimpleNamespace(content=[wait_call], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
351
|
+
stream = stream_from_create(create)
|
|
341
352
|
|
|
342
353
|
class FakeClient:
|
|
343
354
|
def __init__(self):
|
|
@@ -358,6 +369,7 @@ def test_model_injector_restricts_kind_enum_to_first_uncovered_kind(monkeypatch)
|
|
|
358
369
|
def create(self, **kwargs):
|
|
359
370
|
captured.update(kwargs)
|
|
360
371
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
372
|
+
stream = stream_from_create(create)
|
|
361
373
|
|
|
362
374
|
class FakeClient:
|
|
363
375
|
def __init__(self):
|
|
@@ -387,6 +399,7 @@ def test_model_injector_offers_next_uncovered_kind_once_others_are_used_fired_or
|
|
|
387
399
|
def create(self, **kwargs):
|
|
388
400
|
captured.update(kwargs)
|
|
389
401
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
402
|
+
stream = stream_from_create(create)
|
|
390
403
|
|
|
391
404
|
class FakeClient:
|
|
392
405
|
def __init__(self):
|
|
@@ -412,6 +425,7 @@ def test_model_injector_skips_without_calling_the_model_when_only_followup_sensi
|
|
|
412
425
|
def create(self, **kwargs):
|
|
413
426
|
call_count["n"] += 1
|
|
414
427
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
428
|
+
stream = stream_from_create(create)
|
|
415
429
|
|
|
416
430
|
class FakeClient:
|
|
417
431
|
def __init__(self):
|
|
@@ -440,6 +454,7 @@ def test_model_injector_excludes_followup_sensitive_kinds_from_free_choice_when_
|
|
|
440
454
|
def create(self, **kwargs):
|
|
441
455
|
captured.update(kwargs)
|
|
442
456
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
457
|
+
stream = stream_from_create(create)
|
|
443
458
|
|
|
444
459
|
class FakeClient:
|
|
445
460
|
def __init__(self):
|
|
@@ -491,6 +506,7 @@ def test_model_injector_forces_injection_when_budget_cant_cover_remaining_kinds(
|
|
|
491
506
|
},
|
|
492
507
|
)
|
|
493
508
|
return SimpleNamespace(content=[tool_use], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
509
|
+
stream = stream_from_create(create)
|
|
494
510
|
|
|
495
511
|
class FakeClient:
|
|
496
512
|
def __init__(self):
|
|
@@ -535,6 +551,7 @@ def test_model_injector_forces_after_one_wait_on_same_pending_kind(monkeypatch):
|
|
|
535
551
|
},
|
|
536
552
|
)
|
|
537
553
|
return SimpleNamespace(content=[tool_use], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
554
|
+
stream = stream_from_create(create)
|
|
538
555
|
|
|
539
556
|
class FakeClient:
|
|
540
557
|
def __init__(self):
|
|
@@ -565,6 +582,7 @@ def test_model_injector_allows_free_choice_once_all_kinds_covered(monkeypatch):
|
|
|
565
582
|
def create(self, **kwargs):
|
|
566
583
|
captured.update(kwargs)
|
|
567
584
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
585
|
+
stream = stream_from_create(create)
|
|
568
586
|
|
|
569
587
|
class FakeClient:
|
|
570
588
|
def __init__(self):
|
|
@@ -607,6 +625,7 @@ def test_model_injector_inject_returns_armed_injection_with_parsed_trigger(monke
|
|
|
607
625
|
},
|
|
608
626
|
)
|
|
609
627
|
return SimpleNamespace(content=[tool_use], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
628
|
+
stream = stream_from_create(create)
|
|
610
629
|
|
|
611
630
|
class FakeClient:
|
|
612
631
|
def __init__(self):
|
|
@@ -654,6 +673,7 @@ def test_model_injector_parses_after_commit_trigger(monkeypatch):
|
|
|
654
673
|
},
|
|
655
674
|
)
|
|
656
675
|
return SimpleNamespace(content=[tool_use], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
676
|
+
stream = stream_from_create(create)
|
|
657
677
|
|
|
658
678
|
class FakeClient:
|
|
659
679
|
def __init__(self):
|
|
@@ -731,6 +751,7 @@ def test_model_injector_parses_on_any_commit_trigger(monkeypatch):
|
|
|
731
751
|
},
|
|
732
752
|
)
|
|
733
753
|
return SimpleNamespace(content=[tool_use], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
754
|
+
stream = stream_from_create(create)
|
|
734
755
|
|
|
735
756
|
class FakeClient:
|
|
736
757
|
def __init__(self):
|
|
@@ -763,6 +784,7 @@ def test_model_injector_raises_on_missing_trigger_field(monkeypatch):
|
|
|
763
784
|
},
|
|
764
785
|
)
|
|
765
786
|
return SimpleNamespace(content=[tool_use], usage=SimpleNamespace(input_tokens=100, output_tokens=50), stop_reason="end_turn")
|
|
787
|
+
stream = stream_from_create(create)
|
|
766
788
|
|
|
767
789
|
class FakeClient:
|
|
768
790
|
def __init__(self):
|
|
@@ -863,6 +885,7 @@ def test_model_injector_includes_playbook_hint_when_data_exists(tmp_path, monkey
|
|
|
863
885
|
content=[SimpleNamespace(type="tool_use", name="wait", input={})],
|
|
864
886
|
usage=SimpleNamespace(input_tokens=100, output_tokens=50),
|
|
865
887
|
)
|
|
888
|
+
stream = stream_from_create(create)
|
|
866
889
|
|
|
867
890
|
class FakeClient:
|
|
868
891
|
def __init__(self):
|
|
@@ -890,6 +913,7 @@ def test_model_injector_omits_playbook_hint_when_no_playbook_configured(monkeypa
|
|
|
890
913
|
content=[SimpleNamespace(type="tool_use", name="wait", input={})],
|
|
891
914
|
usage=SimpleNamespace(input_tokens=100, output_tokens=50),
|
|
892
915
|
)
|
|
916
|
+
stream = stream_from_create(create)
|
|
893
917
|
|
|
894
918
|
class FakeClient:
|
|
895
919
|
def __init__(self):
|
|
@@ -922,6 +946,7 @@ def test_model_injector_omits_playbook_hint_when_insufficient_data(tmp_path, mon
|
|
|
922
946
|
content=[SimpleNamespace(type="tool_use", name="wait", input={})],
|
|
923
947
|
usage=SimpleNamespace(input_tokens=100, output_tokens=50),
|
|
924
948
|
)
|
|
949
|
+
stream = stream_from_create(create)
|
|
925
950
|
|
|
926
951
|
class FakeClient:
|
|
927
952
|
def __init__(self):
|
|
@@ -960,6 +985,7 @@ def test_model_injector_playbook_hint_includes_customer_feedback_notes(tmp_path,
|
|
|
960
985
|
content=[SimpleNamespace(type="tool_use", name="wait", input={})],
|
|
961
986
|
usage=SimpleNamespace(input_tokens=100, output_tokens=50),
|
|
962
987
|
)
|
|
988
|
+
stream = stream_from_create(create)
|
|
963
989
|
|
|
964
990
|
class FakeClient:
|
|
965
991
|
def __init__(self):
|
|
@@ -998,6 +1024,7 @@ def test_model_injector_playbook_hint_surfaces_feedback_even_below_fire_rate_min
|
|
|
998
1024
|
content=[SimpleNamespace(type="tool_use", name="wait", input={})],
|
|
999
1025
|
usage=SimpleNamespace(input_tokens=100, output_tokens=50),
|
|
1000
1026
|
)
|
|
1027
|
+
stream = stream_from_create(create)
|
|
1001
1028
|
|
|
1002
1029
|
class FakeClient:
|
|
1003
1030
|
def __init__(self):
|
|
@@ -1041,6 +1068,7 @@ def test_model_injector_uses_a_custom_system_prompt_when_supplied(monkeypatch):
|
|
|
1041
1068
|
def create(self, **kwargs):
|
|
1042
1069
|
captured.update(kwargs)
|
|
1043
1070
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
1071
|
+
stream = stream_from_create(create)
|
|
1044
1072
|
|
|
1045
1073
|
class FakeClient:
|
|
1046
1074
|
def __init__(self):
|
|
@@ -1068,6 +1096,7 @@ def test_model_injector_default_system_prompt_is_the_ticket_domain_one(monkeypat
|
|
|
1068
1096
|
def create(self, **kwargs):
|
|
1069
1097
|
captured.update(kwargs)
|
|
1070
1098
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
1099
|
+
stream = stream_from_create(create)
|
|
1071
1100
|
|
|
1072
1101
|
class FakeClient:
|
|
1073
1102
|
def __init__(self):
|
|
@@ -1089,6 +1118,7 @@ def test_model_injector_uses_custom_all_tools_and_commit_tools_in_the_schema(mon
|
|
|
1089
1118
|
def create(self, **kwargs):
|
|
1090
1119
|
captured.update(kwargs)
|
|
1091
1120
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
1121
|
+
stream = stream_from_create(create)
|
|
1092
1122
|
|
|
1093
1123
|
class FakeClient:
|
|
1094
1124
|
def __init__(self):
|
|
@@ -1116,6 +1146,7 @@ def test_model_injector_uses_custom_entity_types_in_the_world_snapshot(monkeypat
|
|
|
1116
1146
|
def create(self, **kwargs):
|
|
1117
1147
|
captured.update(kwargs)
|
|
1118
1148
|
return SimpleNamespace(content=[SimpleNamespace(type="tool_use", name="wait", input={})], usage=SimpleNamespace(input_tokens=100, output_tokens=50))
|
|
1149
|
+
stream = stream_from_create(create)
|
|
1119
1150
|
|
|
1120
1151
|
class FakeClient:
|
|
1121
1152
|
def __init__(self):
|
|
@@ -4,16 +4,33 @@ import agentprobe.llm as llm_module
|
|
|
4
4
|
from agentprobe.llm import cacheable_system, compute_cost_usd, cost_per_token, create_deterministic, response_cost_usd
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
class FakeMessageStream:
|
|
8
|
+
"""Mimics anthropic's MessageStreamManager just enough for
|
|
9
|
+
create_deterministic's `with ... as stream: stream.get_final_message()`."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, result):
|
|
12
|
+
self._result = result
|
|
13
|
+
|
|
14
|
+
def __enter__(self):
|
|
15
|
+
return self
|
|
16
|
+
|
|
17
|
+
def __exit__(self, *exc_info):
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
def get_final_message(self):
|
|
21
|
+
return self._result
|
|
22
|
+
|
|
23
|
+
|
|
7
24
|
class FakeMessages:
|
|
8
25
|
def __init__(self, reject_temperature=False):
|
|
9
26
|
self.reject_temperature = reject_temperature
|
|
10
27
|
self.calls = []
|
|
11
28
|
|
|
12
|
-
def
|
|
29
|
+
def stream(self, **kwargs):
|
|
13
30
|
self.calls.append(kwargs)
|
|
14
31
|
if self.reject_temperature and "extra_body" in kwargs:
|
|
15
32
|
raise ValueError("`temperature` is deprecated for this model.")
|
|
16
|
-
return "ok"
|
|
33
|
+
return FakeMessageStream("ok")
|
|
17
34
|
|
|
18
35
|
|
|
19
36
|
class FakeClient:
|
|
@@ -50,7 +67,7 @@ def test_falls_back_and_caches_when_temperature_rejected():
|
|
|
50
67
|
|
|
51
68
|
def test_unrelated_errors_are_not_swallowed():
|
|
52
69
|
class FailingMessages:
|
|
53
|
-
def
|
|
70
|
+
def stream(self, **kwargs):
|
|
54
71
|
raise RuntimeError("network blip")
|
|
55
72
|
|
|
56
73
|
class FailingClient:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from types import SimpleNamespace
|
|
2
2
|
|
|
3
|
+
from conftest import stream_from_create
|
|
4
|
+
|
|
3
5
|
from agentprobe.agents.target_agent import TargetAgent
|
|
4
6
|
|
|
5
7
|
|
|
@@ -15,6 +17,7 @@ def test_next_action_returns_tool_call_and_uses_cached_system(monkeypatch):
|
|
|
15
17
|
captured.update(kwargs)
|
|
16
18
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_ticket", input={"id": "T-1"})
|
|
17
19
|
return SimpleNamespace(content=[tool_use], usage=_usage())
|
|
20
|
+
stream = stream_from_create(create)
|
|
18
21
|
|
|
19
22
|
class FakeClient:
|
|
20
23
|
def __init__(self):
|
|
@@ -44,6 +47,7 @@ def test_custom_system_prompt_overrides_the_default(monkeypatch):
|
|
|
44
47
|
captured.update(kwargs)
|
|
45
48
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_ticket", input={"id": "T-1"})
|
|
46
49
|
return SimpleNamespace(content=[tool_use], usage=_usage())
|
|
50
|
+
stream = stream_from_create(create)
|
|
47
51
|
|
|
48
52
|
class FakeClient:
|
|
49
53
|
def __init__(self):
|
|
@@ -67,6 +71,7 @@ def test_next_action_returns_final_answer_when_no_tool_use(monkeypatch):
|
|
|
67
71
|
def create(self, **kwargs):
|
|
68
72
|
text_block = SimpleNamespace(type="text", text="All done, refunded you.")
|
|
69
73
|
return SimpleNamespace(content=[text_block], usage=_usage())
|
|
74
|
+
stream = stream_from_create(create)
|
|
70
75
|
|
|
71
76
|
class FakeClient:
|
|
72
77
|
def __init__(self):
|
|
@@ -89,6 +94,7 @@ def test_next_action_prices_cost_by_the_agents_own_model_not_a_fixed_rate(monkey
|
|
|
89
94
|
def create(self, **kwargs):
|
|
90
95
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_ticket", input={"id": "T-1"})
|
|
91
96
|
return SimpleNamespace(content=[tool_use], usage=_usage(input_tokens=1000, output_tokens=1000))
|
|
97
|
+
stream = stream_from_create(create)
|
|
92
98
|
|
|
93
99
|
class FakeClient:
|
|
94
100
|
def __init__(self):
|
|
@@ -114,6 +120,7 @@ def test_observe_appends_tool_result_and_marks_errors(monkeypatch):
|
|
|
114
120
|
def create(self, **kwargs):
|
|
115
121
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_ticket", input={"id": "T-1"})
|
|
116
122
|
return SimpleNamespace(content=[tool_use], usage=_usage())
|
|
123
|
+
stream = stream_from_create(create)
|
|
117
124
|
|
|
118
125
|
class FakeClient:
|
|
119
126
|
def __init__(self):
|
|
@@ -141,6 +148,7 @@ def test_receive_message_appends_a_plain_user_turn(monkeypatch):
|
|
|
141
148
|
def create(self, **kwargs):
|
|
142
149
|
tool_use = SimpleNamespace(type="tool_use", id="tu_1", name="get_ticket", input={"id": "T-1"})
|
|
143
150
|
return SimpleNamespace(content=[tool_use], usage=_usage())
|
|
151
|
+
stream = stream_from_create(create)
|
|
144
152
|
|
|
145
153
|
class FakeClient:
|
|
146
154
|
def __init__(self):
|
|
@@ -5,6 +5,8 @@ these tests exist to catch that.
|
|
|
5
5
|
|
|
6
6
|
from types import SimpleNamespace
|
|
7
7
|
|
|
8
|
+
from conftest import stream_from_create
|
|
9
|
+
|
|
8
10
|
from agentprobe.scenario import CommitPattern, FactPattern, GoalSpec, Scenario
|
|
9
11
|
from agentprobe.scenarios.registry import ALL_SCENARIOS
|
|
10
12
|
from agentprobe.validate_scenarios import (
|
|
@@ -239,6 +241,7 @@ def test_verify_warnings_adds_model_note_without_changing_severity(monkeypatch):
|
|
|
239
241
|
input={"consistent": True, "explanation": "200.0 * 0.5 = 100.0, matches."},
|
|
240
242
|
)
|
|
241
243
|
return SimpleNamespace(content=[tool_use])
|
|
244
|
+
stream = stream_from_create(create)
|
|
242
245
|
|
|
243
246
|
class FakeClient:
|
|
244
247
|
def __init__(self):
|
|
@@ -274,6 +277,7 @@ def test_verify_warnings_reports_inconsistent_without_promoting_to_broken(monkey
|
|
|
274
277
|
input={"consistent": False, "explanation": "200.0 * 0.5 = 100.0, not 60.0."},
|
|
275
278
|
)
|
|
276
279
|
return SimpleNamespace(content=[tool_use])
|
|
280
|
+
stream = stream_from_create(create)
|
|
277
281
|
|
|
278
282
|
class FakeClient:
|
|
279
283
|
def __init__(self):
|
|
@@ -307,6 +311,7 @@ def test_verify_warnings_raises_clear_error_when_no_tool_use_block(monkeypatch):
|
|
|
307
311
|
def create(self, **kwargs):
|
|
308
312
|
text_block = SimpleNamespace(type="text", text="I refuse.")
|
|
309
313
|
return SimpleNamespace(content=[text_block], stop_reason="end_turn")
|
|
314
|
+
stream = stream_from_create(create)
|
|
310
315
|
|
|
311
316
|
class FakeClient:
|
|
312
317
|
def __init__(self):
|
|
@@ -328,6 +333,7 @@ def test_verify_warnings_raises_clear_error_on_missing_fields(monkeypatch):
|
|
|
328
333
|
def create(self, **kwargs):
|
|
329
334
|
tool_use = SimpleNamespace(type="tool_use", input={"consistent": True}) # missing "explanation"
|
|
330
335
|
return SimpleNamespace(content=[tool_use], stop_reason="max_tokens")
|
|
336
|
+
stream = stream_from_create(create)
|
|
331
337
|
|
|
332
338
|
class FakeClient:
|
|
333
339
|
def __init__(self):
|
|
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-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/__init__.py
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/agent.py
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/clean.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/decoy.py
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/domain.py
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/entities.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/scenarios.py
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/split.py
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/tools.py
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe/domains/access_control/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
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/requires.txt
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/agentprobe_testing.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/tests/test_access_control_rule_based_agent.py
RENAMED
|
File without changes
|
{agentprobe_testing-0.8.2 → agentprobe_testing-0.8.4}/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
|
|
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
|