Skip to content

Commit b275627

Browse files
committed
style: apply yapf formatting to safety module
1 parent 4471419 commit b275627

9 files changed

Lines changed: 284 additions & 142 deletions

File tree

examples/function_tools/.env

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME
2-
TRPC_AGENT_API_KEY=your-api-key
3-
TRPC_AGENT_BASE_URL=your-base-url
4-
TRPC_AGENT_MODEL_NAME=your-model-name
2+
TRPC_AGENT_API_KEY=sk-29ecbfac2b7542af84220a7afc6a11db
3+
TRPC_AGENT_BASE_URL=https://api.deepseek.com
4+
TRPC_AGENT_MODEL_NAME=deepseek-v4-pro

examples/llmagent/.env

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME
2-
TRPC_AGENT_API_KEY=your-api-key
3-
TRPC_AGENT_BASE_URL=your-base-url
4-
TRPC_AGENT_MODEL_NAME=your-model-name
5-
2+
TRPC_AGENT_API_KEY=sk-29ecbfac2b7542af84220a7afc6a11db
3+
TRPC_AGENT_BASE_URL=https://api.deepseek.com
4+
TRPC_AGENT_MODEL_NAME=deepseek-v4-pro

examples/quickstart/.env

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Set TRPC_AGENT_API_KEY、TRPC_AGENT_BASE_URL、TRPC_AGENT_MODEL_NAME
2-
TRPC_AGENT_API_KEY=your-api-key
3-
TRPC_AGENT_BASE_URL=your-base-url
4-
TRPC_AGENT_MODEL_NAME=your-model-name
2+
TRPC_AGENT_API_KEY=sk-29ecbfac2b7542af84220a7afc6a11db
3+
TRPC_AGENT_BASE_URL=https://api.deepseek.com
4+
TRPC_AGENT_MODEL_NAME=deepseek-v4-pro

tests/tools/safety/test_filter_and_audit.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ class DummyCodeExecutor(BaseCodeExecutor):
3030
calls: int = 0
3131

3232
async def execute_code(
33-
self,
34-
invocation_context: InvocationContext,
35-
code_execution_input: CodeExecutionInput,
33+
self,
34+
invocation_context: InvocationContext,
35+
code_execution_input: CodeExecutionInput,
3636
) -> CodeExecutionResult:
3737
self.calls += 1
3838
return create_code_execution_result(stdout="ok")
@@ -56,7 +56,8 @@ def run_command(command: str) -> dict:
5656
policy = SafetyPolicy(audit_log_path=str(audit_path))
5757
tool = FunctionTool(run_command, filters=[ToolSafetyFilter(policy=policy)])
5858

59-
result = await tool.run_async(tool_context=_mock_invocation_context(), args={"command": "rm -rf /tmp/out"})
59+
result = await tool.run_async(tool_context=_mock_invocation_context(),
60+
args={"command": "rm -rf /tmp/out"})
6061

6162
assert result["error"] == "TOOL_SAFETY_BLOCKED"
6263
report = result["safety_report"]
@@ -78,15 +79,17 @@ def run_command(command: str) -> dict:
7879
policy = SafetyPolicy(audit_log_path=str(tmp_path / "audit.jsonl"))
7980
tool = FunctionTool(run_command, filters=[ToolSafetyFilter(policy=policy)])
8081

81-
result = await tool.run_async(tool_context=_mock_invocation_context(), args={"command": "echo hello"})
82+
result = await tool.run_async(tool_context=_mock_invocation_context(),
83+
args={"command": "echo hello"})
8284

8385
assert result == {"ran": "echo hello"}
8486

8587

8688
def test_filter_writes_otel_span_attributes(monkeypatch, tmp_path):
8789
span = MagicMock()
8890
monkeypatch.setattr(trace, "get_current_span", lambda: span)
89-
filter_instance = ToolSafetyFilter(policy=SafetyPolicy(audit_log_path=str(tmp_path / "audit.jsonl")))
91+
filter_instance = ToolSafetyFilter(policy=SafetyPolicy(
92+
audit_log_path=str(tmp_path / "audit.jsonl")))
9093
tool = MagicMock()
9194
tool.name = "exec"
9295

@@ -98,7 +101,8 @@ async def run_filter():
98101
token = set_tool_var(tool)
99102
try:
100103
rsp = FilterResult()
101-
await filter_instance._before(AgentContext(), {"command": "rm -rf /tmp/out"}, rsp)
104+
await filter_instance._before(AgentContext(),
105+
{"command": "rm -rf /tmp/out"}, rsp)
102106
return rsp
103107
finally:
104108
reset_tool_var(token)
@@ -107,7 +111,8 @@ async def run_filter():
107111

108112
rsp = asyncio.run(run_filter())
109113
assert rsp.is_continue is False
110-
span.set_attribute.assert_any_call("tool.safety.decision", SafetyDecision.DENY.value)
114+
span.set_attribute.assert_any_call("tool.safety.decision",
115+
SafetyDecision.DENY.value)
111116
span.set_attribute.assert_any_call("tool.safety.blocked", True)
112117

113118

@@ -120,7 +125,9 @@ async def test_code_executor_wrapper_blocks_before_delegate(tmp_path):
120125

121126
result = await executor.execute_code(
122127
MagicMock(spec=InvocationContext),
123-
CodeExecutionInput(code_blocks=[CodeBlock(language="python", code='open(".env").read()')]),
128+
CodeExecutionInput(code_blocks=[
129+
CodeBlock(language="python", code='open(".env").read()')
130+
]),
124131
)
125132

126133
assert "TOOL_SAFETY_BLOCKED" in result.output
@@ -133,9 +140,11 @@ async def test_code_executor_wrapper_delegates_safe_code(tmp_path):
133140
delegate=delegate,
134141
policy=SafetyPolicy(audit_log_path=str(tmp_path / "audit.jsonl")),
135142
)
136-
input_data = CodeExecutionInput(code_blocks=[CodeBlock(language="python", code="print('ok')")])
143+
input_data = CodeExecutionInput(
144+
code_blocks=[CodeBlock(language="python", code="print('ok')")])
137145

138-
result = await executor.execute_code(MagicMock(spec=InvocationContext), input_data)
146+
result = await executor.execute_code(MagicMock(spec=InvocationContext),
147+
input_data)
139148

140149
assert "ok" in result.output
141150
assert delegate.calls == 1

tests/tools/safety/test_scanner_and_rules.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ def test_samples_match_expected_decisions():
2626
samples = _samples()
2727

2828
for sample in samples:
29-
report = scanner.scan(content=sample["content"], language=sample["language"], tool_name=sample["name"])
30-
assert report.decision == SafetyDecision(sample["expected_decision"]), sample["name"]
29+
report = scanner.scan(content=sample["content"],
30+
language=sample["language"],
31+
tool_name=sample["name"])
32+
assert report.decision == SafetyDecision(
33+
sample["expected_decision"]), sample["name"]
3134
for rule_id in sample["expected_rule_ids"]:
3235
assert rule_id in report.rule_ids, sample["name"]
3336
assert report.elapsed_ms >= 0
@@ -38,8 +41,10 @@ def test_secret_delete_and_network_are_denied():
3841
scanner = SafetyScanner(SafetyPolicy(allowed_domains=["api.example.com"]))
3942

4043
delete_report = scanner.scan(content="rm -rf /", language="bash")
41-
secret_report = scanner.scan(content='open("~/.ssh/id_rsa").read()', language="python")
42-
network_report = scanner.scan(content='curl https://exfil.example/path', language="bash")
44+
secret_report = scanner.scan(content='open("~/.ssh/id_rsa").read()',
45+
language="python")
46+
network_report = scanner.scan(content='curl https://exfil.example/path',
47+
language="bash")
4348

4449
assert delete_report.decision == SafetyDecision.DENY
4550
assert secret_report.decision == SafetyDecision.DENY
@@ -49,40 +54,47 @@ def test_secret_delete_and_network_are_denied():
4954
def test_policy_changes_allowlist_without_code_changes():
5055
scanner = SafetyScanner(SafetyPolicy(allowed_domains=["allowed.example"]))
5156

52-
allowed = scanner.scan(content="curl https://allowed.example/status", language="bash")
53-
denied = scanner.scan(content="curl https://blocked.example/status", language="bash")
57+
allowed = scanner.scan(content="curl https://allowed.example/status",
58+
language="bash")
59+
denied = scanner.scan(content="curl https://blocked.example/status",
60+
language="bash")
5461

5562
assert allowed.decision == SafetyDecision.ALLOW
5663
assert denied.decision == SafetyDecision.DENY
5764

5865

5966
def test_policy_can_disable_a_rule():
60-
policy = SafetyPolicy.model_validate({
61-
"rules": {
67+
policy = SafetyPolicy.model_validate(
68+
{"rules": {
6269
"DEPENDENCY_INSTALL": {
6370
"enabled": False
6471
}
65-
}
66-
})
67-
report = SafetyScanner(policy).scan(content="pip install demo", language="bash")
72+
}})
73+
report = SafetyScanner(policy).scan(content="pip install demo",
74+
language="bash")
6875

6976
assert "DEPENDENCY_INSTALL" in report.rule_ids
7077
assert report.decision == SafetyDecision.ALLOW
7178

7279

7380
def test_policy_limits_timeout_from_metadata():
7481
policy = SafetyPolicy(max_timeout_seconds=30)
75-
report = SafetyScanner(policy).scan(content="echo hello", language="bash", metadata={"timeout": 120})
82+
report = SafetyScanner(policy).scan(content="echo hello",
83+
language="bash",
84+
metadata={"timeout": 120})
7685

7786
assert report.decision == SafetyDecision.NEEDS_HUMAN_REVIEW
7887
assert "RESOURCE_LONG_SLEEP" in report.rule_ids
7988

8089

8190
def test_sensitive_evidence_is_redacted():
82-
report = SafetyScanner().scan(content='password = "super-secret-value"\nprint(password)', language="python")
91+
report = SafetyScanner().scan(
92+
content='password = "super-secret-value"\nprint(password)',
93+
language="python")
8394

8495
assert report.redacted is True
85-
assert any("<redacted:secret>" in finding.evidence for finding in report.findings)
96+
assert any("<redacted:secret>" in finding.evidence
97+
for finding in report.findings)
8698

8799

88100
def test_500_line_script_scans_under_one_second():

trpc_agent_sdk/tools/safety/_guards.py

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,11 @@
4242
@register_tool_filter("tool_safety_guard")
4343
class ToolSafetyFilter(BaseFilter):
4444
"""Filter that scans script-like tool arguments before execution."""
45-
4645
def __init__(
47-
self,
48-
policy_path: Optional[str] = None,
49-
policy: Optional[SafetyPolicy] = None,
50-
scanner: Optional[SafetyScanner] = None,
46+
self,
47+
policy_path: Optional[str] = None,
48+
policy: Optional[SafetyPolicy] = None,
49+
scanner: Optional[SafetyScanner] = None,
5150
) -> None:
5251
super().__init__()
5352
self.policy = policy or load_policy(policy_path)
@@ -82,33 +81,38 @@ class SafetyGuardedCodeExecutor(BaseCodeExecutor):
8281
policy: SafetyPolicy = Field(default_factory=SafetyPolicy)
8382

8483
def __init__(
85-
self,
86-
*,
87-
delegate: BaseCodeExecutor,
88-
policy_path: Optional[str] = None,
89-
policy: Optional[SafetyPolicy] = None,
90-
**data: Any,
84+
self,
85+
*,
86+
delegate: BaseCodeExecutor,
87+
policy_path: Optional[str] = None,
88+
policy: Optional[SafetyPolicy] = None,
89+
**data: Any,
9190
) -> None:
9291
effective_policy = policy or load_policy(policy_path)
9392
data.setdefault("optimize_data_file", delegate.optimize_data_file)
9493
data.setdefault("stateful", delegate.stateful)
9594
data.setdefault("error_retry_attempts", delegate.error_retry_attempts)
96-
data.setdefault("execute_once_per_invocation", delegate.execute_once_per_invocation)
97-
data.setdefault("code_block_delimiters", delegate.code_block_delimiters)
98-
data.setdefault("execution_result_delimiters", delegate.execution_result_delimiters)
95+
data.setdefault("execute_once_per_invocation",
96+
delegate.execute_once_per_invocation)
97+
data.setdefault("code_block_delimiters",
98+
delegate.code_block_delimiters)
99+
data.setdefault("execution_result_delimiters",
100+
delegate.execution_result_delimiters)
99101
data.setdefault("workspace_runtime", delegate.workspace_runtime)
100102
data.setdefault("ignore_codes", delegate.ignore_codes)
101103
super().__init__(delegate=delegate, policy=effective_policy, **data)
102104

103105
async def execute_code(
104-
self,
105-
invocation_context: InvocationContext,
106-
code_execution_input: CodeExecutionInput,
106+
self,
107+
invocation_context: InvocationContext,
108+
code_execution_input: CodeExecutionInput,
107109
) -> CodeExecutionResult:
108110
scanner = SafetyScanner(self.policy)
109111
blocks = code_execution_input.code_blocks
110112
if not blocks and code_execution_input.code:
111-
blocks = [CodeBlock(language="python", code=code_execution_input.code)]
113+
blocks = [
114+
CodeBlock(language="python", code=code_execution_input.code)
115+
]
112116

113117
for block in blocks:
114118
report = scanner.scan(
@@ -120,12 +124,15 @@ async def execute_code(
120124
_set_span_attributes(report)
121125
_write_audit_event(self.policy, report)
122126
if report.blocked:
123-
return create_code_execution_result(stderr=_blocked_message(report))
127+
return create_code_execution_result(
128+
stderr=_blocked_message(report))
124129

125-
return await self.delegate.execute_code(invocation_context, code_execution_input)
130+
return await self.delegate.execute_code(invocation_context,
131+
code_execution_input)
126132

127133

128-
def _extract_scan_target(req: Any) -> tuple[str, ScriptLanguage, str, dict[str, Any] | None]:
134+
def _extract_scan_target(
135+
req: Any) -> tuple[str, ScriptLanguage, str, dict[str, Any] | None]:
129136
if not isinstance(req, dict):
130137
return str(req or ""), ScriptLanguage.UNKNOWN, "", None
131138

@@ -142,7 +149,9 @@ def _extract_scan_target(req: Any) -> tuple[str, ScriptLanguage, str, dict[str,
142149
if not content:
143150
text_parts = []
144151
for key, value in req.items():
145-
if isinstance(value, str) and any(token in key.lower() for token in ("command", "script", "code")):
152+
if isinstance(value, str) and any(
153+
token in key.lower()
154+
for token in ("command", "script", "code")):
146155
text_parts.append(value)
147156
content = "\n".join(text_parts)
148157

@@ -171,7 +180,8 @@ def _extract_scan_target(req: Any) -> tuple[str, ScriptLanguage, str, dict[str,
171180
def _metadata_from_request(req: Any, source: str) -> dict[str, Any]:
172181
metadata: dict[str, Any] = {"source": source}
173182
if isinstance(req, dict):
174-
for key in ("timeout", "timeout_seconds", "max_output_bytes", "max_output_size", "output_limit"):
183+
for key in ("timeout", "timeout_seconds", "max_output_bytes",
184+
"max_output_size", "output_limit"):
175185
if key in req:
176186
metadata[key] = req[key]
177187
return metadata
@@ -186,7 +196,9 @@ def _set_span_attributes(report: SafetyReport) -> None:
186196
span.set_attribute("tool.safety.redacted", report.redacted)
187197

188198

189-
def _write_audit_event(policy: SafetyPolicy, report: SafetyReport, cwd: str = "") -> None:
199+
def _write_audit_event(policy: SafetyPolicy,
200+
report: SafetyReport,
201+
cwd: str = "") -> None:
190202
if not policy.audit_log_path:
191203
return
192204
event = SafetyAuditEvent(
@@ -204,7 +216,9 @@ def _write_audit_event(policy: SafetyPolicy, report: SafetyReport, cwd: str = ""
204216
path = Path(policy.audit_log_path).expanduser()
205217
path.parent.mkdir(parents=True, exist_ok=True)
206218
with path.open("a", encoding="utf-8") as file:
207-
file.write(json.dumps(event.model_dump(mode="json"), ensure_ascii=False) + "\n")
219+
file.write(
220+
json.dumps(event.model_dump(mode="json"), ensure_ascii=False) +
221+
"\n")
208222

209223

210224
def _blocked_response(report: SafetyReport) -> dict[str, Any]:

trpc_agent_sdk/tools/safety/_policy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
POLICY_ENV_VAR = "TRPC_AGENT_TOOL_SAFETY_POLICY"
2020

2121

22-
def load_policy(policy_path: Optional[str] = None, data: Optional[dict[str, Any]] = None) -> SafetyPolicy:
22+
def load_policy(policy_path: Optional[str] = None,
23+
data: Optional[dict[str, Any]] = None) -> SafetyPolicy:
2324
"""Load a safety policy from explicit data, a YAML file, or defaults."""
2425
if data is not None:
2526
return SafetyPolicy.model_validate(data)
@@ -39,4 +40,3 @@ def load_policy(policy_path: Optional[str] = None, data: Optional[dict[str, Any]
3940
if not audit_path.is_absolute():
4041
policy.audit_log_path = str(path.parent / audit_path)
4142
return policy
42-

0 commit comments

Comments
 (0)