modelfuzz 0.1.0__tar.gz → 0.1.2__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.
- modelfuzz-0.1.2/.claude/settings.local.json +27 -0
- modelfuzz-0.1.2/CONTRIBUTING.md +46 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/PKG-INFO +1 -1
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/pyproject.toml +1 -1
- modelfuzz-0.1.2/src/modelfuzz/decorator.py +64 -0
- modelfuzz-0.1.0/.claude/settings.local.json +0 -12
- modelfuzz-0.1.0/src/modelfuzz/decorator.py +0 -45
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/.github/workflows/ci.yml +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/.gitignore +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/.pre-commit-config.yaml +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/LICENSE +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/README.md +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/demo.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/src/modelfuzz/__init__.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/src/modelfuzz/cli.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/src/modelfuzz/engine.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/src/modelfuzz/exceptions.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/src/modelfuzz/rules.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/tests/test_decorator.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/tests/test_engine.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/tests/test_rules.py +0 -0
- {modelfuzz-0.1.0 → modelfuzz-0.1.2}/uv.lock +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(git -C /Users/gagandeep/agentshield status --short --branch)",
|
|
5
|
+
"Bash(uv --version)",
|
|
6
|
+
"Bash(uv sync *)",
|
|
7
|
+
"Bash(uv run python -c ' *)",
|
|
8
|
+
"Bash(uv run *)",
|
|
9
|
+
"Bash(git add *)",
|
|
10
|
+
"Bash(python3 -c \"import modelfuzz; print\\(modelfuzz.__file__\\)\")",
|
|
11
|
+
"Bash(pip show *)",
|
|
12
|
+
"Bash(python3 test_agent.py)",
|
|
13
|
+
"Bash(python3 -c \"import sys; print\\(sys.path\\)\")",
|
|
14
|
+
"Bash(pip list *)",
|
|
15
|
+
"Bash(python3 -c \"import sys,site; print\\(site.getsitepackages\\(\\)\\)\")",
|
|
16
|
+
"Bash(find / -maxdepth 6 -iname \"modelfuzz*\" -not -path \"*/modelfuzz/*\")",
|
|
17
|
+
"Bash(./.venv/bin/pip show *)",
|
|
18
|
+
"Bash(./.venv/bin/python3 test_agent.py)",
|
|
19
|
+
"Bash(python3 -c ' *)",
|
|
20
|
+
"Bash(python3 -m pytest -q)",
|
|
21
|
+
"Bash(./.venv/bin/pip install *)",
|
|
22
|
+
"Bash(./.venv/bin/python test_agent.py)",
|
|
23
|
+
"Bash(python3 -m pip show build twine)",
|
|
24
|
+
"Bash(env)"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Contributing to ModelFuzz
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing. This document covers how to set up a development environment, run tests, and submit changes.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
ModelFuzz uses [uv](https://github.com/astral-sh/uv) for dependency management.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/higagan/modelfuzz.git
|
|
11
|
+
cd modelfuzz
|
|
12
|
+
uv sync
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This installs the package and its dependencies, including dev tools (`pytest`, `ruff`), into a local virtual environment.
|
|
16
|
+
|
|
17
|
+
## Running Tests
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv run pytest
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Tests live under [tests/](tests/) and are organized by module (`test_decorator.py`, `test_engine.py`, `test_rules.py`). Add tests alongside the code they cover, and make sure new policies or behaviors are exercised by at least one test.
|
|
24
|
+
|
|
25
|
+
## Linting
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv run ruff check .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
CI runs lint and tests against Python 3.10, 3.11, and 3.12 on every push and pull request (see [.github/workflows/ci.yml](.github/workflows/ci.yml)). Please make sure both pass locally before opening a PR.
|
|
32
|
+
|
|
33
|
+
## Submitting Changes
|
|
34
|
+
|
|
35
|
+
1. Fork the repository and create a branch from `main`.
|
|
36
|
+
2. Make your changes, with tests covering new behavior.
|
|
37
|
+
3. Run `uv run ruff check .` and `uv run pytest` locally.
|
|
38
|
+
4. Open a pull request describing the change and its motivation.
|
|
39
|
+
|
|
40
|
+
## Reporting Issues
|
|
41
|
+
|
|
42
|
+
Use [GitHub Issues](https://github.com/higagan/modelfuzz/issues) to report bugs or propose features. Include a minimal reproduction where possible.
|
|
43
|
+
|
|
44
|
+
## License
|
|
45
|
+
|
|
46
|
+
By contributing, you agree that your contributions will be licensed under the project's [MIT License](LICENSE).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Decorators for shielding agent tool functions."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import ParamSpec, TypeVar, overload
|
|
6
|
+
|
|
7
|
+
from modelfuzz.engine import PolicyEngine
|
|
8
|
+
from modelfuzz.exceptions import ModelFuzzBlockError
|
|
9
|
+
from modelfuzz.rules import SensitiveDataFilter
|
|
10
|
+
|
|
11
|
+
P = ParamSpec("P")
|
|
12
|
+
R = TypeVar("R")
|
|
13
|
+
|
|
14
|
+
# Default policy engine for the decorator
|
|
15
|
+
default_policies = [SensitiveDataFilter()]
|
|
16
|
+
_default_engine = PolicyEngine(default_policies)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@overload
|
|
20
|
+
def shield_tool(engine: Callable[P, R]) -> Callable[P, R]: ...
|
|
21
|
+
@overload
|
|
22
|
+
def shield_tool(
|
|
23
|
+
engine: PolicyEngine | None = None,
|
|
24
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]]: ...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def shield_tool(engine=None):
|
|
28
|
+
"""Wrap a tool function so every call is intercepted before execution.
|
|
29
|
+
|
|
30
|
+
Usable bare (``@shield_tool``) or called (``@shield_tool()`` /
|
|
31
|
+
``@shield_tool(engine)``).
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
engine: The policy engine to use for validation. If None, a default
|
|
35
|
+
engine with a SensitiveDataFilter is used. When applied bare,
|
|
36
|
+
this receives the function being decorated instead.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
The wrapped function, or a decorator that produces it.
|
|
40
|
+
"""
|
|
41
|
+
if callable(engine) and not isinstance(engine, PolicyEngine):
|
|
42
|
+
return _wrap(engine, _default_engine)
|
|
43
|
+
|
|
44
|
+
actual_engine = engine if engine is not None else _default_engine
|
|
45
|
+
|
|
46
|
+
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
|
47
|
+
return _wrap(func, actual_engine)
|
|
48
|
+
|
|
49
|
+
return decorator
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _wrap(func: Callable[P, R], actual_engine: PolicyEngine) -> Callable[P, R]:
|
|
53
|
+
@functools.wraps(func)
|
|
54
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
55
|
+
# Check all arguments against the policy engine
|
|
56
|
+
for arg in list(args) + list(kwargs.values()):
|
|
57
|
+
result = actual_engine.run(arg)
|
|
58
|
+
if not result.allowed:
|
|
59
|
+
raise ModelFuzzBlockError(result.reason or "Call blocked by policy")
|
|
60
|
+
|
|
61
|
+
print(f"ModelFuzz Intercepted: {func.__name__}")
|
|
62
|
+
return func(*args, **kwargs)
|
|
63
|
+
|
|
64
|
+
return wrapper
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"""Decorators for shielding agent tool functions."""
|
|
2
|
-
|
|
3
|
-
import functools
|
|
4
|
-
from collections.abc import Callable
|
|
5
|
-
from typing import ParamSpec, TypeVar
|
|
6
|
-
|
|
7
|
-
from modelfuzz.engine import PolicyEngine
|
|
8
|
-
from modelfuzz.exceptions import ModelFuzzBlockError
|
|
9
|
-
from modelfuzz.rules import SensitiveDataFilter
|
|
10
|
-
|
|
11
|
-
P = ParamSpec("P")
|
|
12
|
-
R = TypeVar("R")
|
|
13
|
-
|
|
14
|
-
# Default policy engine for the decorator
|
|
15
|
-
default_policies = [SensitiveDataFilter()]
|
|
16
|
-
_default_engine = PolicyEngine(default_policies)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def shield_tool(engine: PolicyEngine | None = None) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
20
|
-
"""Wrap a tool function so every call is intercepted before execution.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
engine: The policy engine to use for validation. If None, a default
|
|
24
|
-
engine with a SensitiveDataFilter is used.
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
A decorator that wraps the function.
|
|
28
|
-
"""
|
|
29
|
-
actual_engine = engine if engine is not None else _default_engine
|
|
30
|
-
|
|
31
|
-
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
|
32
|
-
@functools.wraps(func)
|
|
33
|
-
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
34
|
-
# Check all arguments against the policy engine
|
|
35
|
-
for arg in list(args) + list(kwargs.values()):
|
|
36
|
-
result = actual_engine.run(arg)
|
|
37
|
-
if not result.allowed:
|
|
38
|
-
raise ModelFuzzBlockError(result.reason or "Call blocked by policy")
|
|
39
|
-
|
|
40
|
-
print(f"ModelFuzz Intercepted: {func.__name__}")
|
|
41
|
-
return func(*args, **kwargs)
|
|
42
|
-
|
|
43
|
-
return wrapper
|
|
44
|
-
|
|
45
|
-
return decorator
|
|
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
|