Skip to content

Commit b491d98

Browse files
committed
docs: add docstrings to Python adapter modules
1 parent 71b9a94 commit b491d98

File tree

5 files changed

+279
-0
lines changed

5 files changed

+279
-0
lines changed

packages/agent-os/src/agent_os/integrations/crewai_adapter.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,22 @@ def _wrap_tool(self, tool, agent_name: str):
150150
crew_name = self._crew_name
151151

152152
def governed_run(*args, **kwargs):
153+
"""Governed wrapper around a CrewAI tool's run method.
154+
155+
Intercepts the tool call, runs pre-execution policy checks,
156+
records the invocation in the audit log, and delegates
157+
to the original _run implementation.
158+
159+
Args:
160+
*args: Positional arguments forwarded to the original tool.
161+
**kwargs: Keyword arguments forwarded to the original tool.
162+
163+
Returns:
164+
The result from the original tool's run method.
165+
166+
Raises:
167+
PolicyViolationError: If the tool call violates the active policy.
168+
"""
153169
request = ToolCallRequest(
154170
tool_name=tool_name,
155171
arguments=kwargs if kwargs else {"args": args},
@@ -194,6 +210,22 @@ def _wrap_agent(self, agent):
194210
crew_name = self._crew_name
195211

196212
def governed_execute(task, *args, **kwargs):
213+
"""Governed wrapper around a CrewAI agent's task execution.
214+
215+
Intercepts each task execution call, applies pre-execution
216+
policy checks, and delegates to the original execute method.
217+
218+
Args:
219+
task: The CrewAI Task object to execute.
220+
*args: Additional positional arguments.
221+
**kwargs: Additional keyword arguments.
222+
223+
Returns:
224+
The task execution result from the underlying agent.
225+
226+
Raises:
227+
PolicyViolationError: If the execution violates the active policy.
228+
"""
197229
task_id = getattr(task, 'id', None) or str(id(task))
198230
logger.info("Agent task execution started: crew_name=%s, task_id=%s", crew_name, task_id)
199231
if self._kernel.policy.require_human_approval:
@@ -253,6 +285,22 @@ def _intercept_task_steps(
253285

254286
@functools.wraps(original_step)
255287
def governed_step(*args: Any, _orig=original_step, _attr=step_attr, **kwargs: Any) -> Any:
288+
"""Governed wrapper around a CrewAI task step.
289+
290+
Intercepts individual step calls within a task, validates
291+
inputs against the active policy, and records each step
292+
in the audit trail before delegating to the original method.
293+
294+
Args:
295+
*args: Positional arguments forwarded to the original step.
296+
**kwargs: Keyword arguments forwarded to the original step.
297+
298+
Returns:
299+
The result from the original step method.
300+
301+
Raises:
302+
PolicyViolationError: If the step input violates the active policy.
303+
"""
256304
step_record = {
257305
"crew": crew_name,
258306
"agent": agent_name,
@@ -306,6 +354,22 @@ def _intercept_crew_memory(
306354

307355
@functools.wraps(save_fn)
308356
def governed_save(*args: Any, _orig=save_fn, _mname=save_method_name, **kwargs: Any) -> Any:
357+
"""Governed wrapper around CrewAI memory save operations.
358+
359+
Validates content before it is written to crew memory,
360+
checking for PII patterns and policy-blocked content.
361+
Records every save attempt in the memory audit log.
362+
363+
Args:
364+
*args: Positional arguments forwarded to the original save.
365+
**kwargs: Keyword arguments forwarded to the original save.
366+
367+
Returns:
368+
The result from the original memory save method.
369+
370+
Raises:
371+
PolicyViolationError: If the content contains PII or blocked patterns.
372+
"""
309373
combined = str(args) + str(kwargs)
310374

311375
# PII / secrets check
@@ -357,6 +421,22 @@ def _detect_crew_delegation(
357421

358422
@functools.wraps(delegate_fn)
359423
def governed_delegate(*args: Any, **kwargs: Any) -> Any:
424+
"""Governed wrapper around CrewAI agent delegation.
425+
426+
Intercepts delegation calls between agents, tracks delegation
427+
depth, and enforces the maximum delegation limit defined in
428+
the active policy.
429+
430+
Args:
431+
*args: Positional arguments forwarded to the original delegate.
432+
**kwargs: Keyword arguments forwarded to the original delegate.
433+
434+
Returns:
435+
The result from the delegated agent.
436+
437+
Raises:
438+
PolicyViolationError: If the delegation depth exceeds the policy limit.
439+
"""
360440
depth = len(kernel._delegation_log) + 1
361441
if depth > max_depth:
362442
raise PolicyViolationError(

packages/agent-os/src/agent_os/integrations/google_adk_adapter.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,26 @@ def unwrap(self, governed_agent: Any) -> Any:
233233
# ------------------------------------------------------------------
234234

235235
def _default_violation_handler(self, error: PolicyViolationError) -> None:
236+
"""Default handler called when a policy violation occurs.
237+
238+
Logs the violation at ERROR level. Override by passing a custom
239+
on_violation callable to the kernel constructor.
240+
241+
Args:
242+
error: The PolicyViolationError that was raised.
243+
"""
236244
logger.error(f"Policy violation: {error}")
237245

238246
def _record(self, event_type: str, agent_name: str, details: dict[str, Any]) -> None:
247+
"""Append an audit event to the internal audit log.
248+
249+
Records the event only when log_all_calls is enabled.
250+
251+
Args:
252+
event_type: Short string label for the event.
253+
agent_name: Name of the ADK agent generating the event.
254+
details: Arbitrary dict of additional context.
255+
"""
239256
if self._adk_config.log_all_calls:
240257
self._audit_log.append(
241258
AuditEvent(
@@ -247,26 +264,55 @@ def _record(self, event_type: str, agent_name: str, details: dict[str, Any]) ->
247264
)
248265

249266
def _check_tool_allowed(self, tool_name: str) -> tuple[bool, str]:
267+
"""Check whether a tool is permitted by the active ADK policy.
268+
269+
Args:
270+
tool_name: Name of the ADK tool to check.
271+
272+
Returns:
273+
Tuple of (allowed: bool, reason: str).
274+
"""
250275
if tool_name in self._adk_config.blocked_tools:
251276
return False, f"Tool '{tool_name}' is blocked by policy"
252277
if self._adk_config.allowed_tools and tool_name not in self._adk_config.allowed_tools:
253278
return False, f"Tool '{tool_name}' not in allowed list"
254279
return True, ""
255280

256281
def _check_content(self, content: str) -> tuple[bool, str]:
282+
"""Scan a string for policy-blocked patterns.
283+
284+
Args:
285+
content: The text to scan.
286+
287+
Returns:
288+
Tuple of (allowed: bool, reason: str).
289+
"""
257290
content_lower = content.lower()
258291
for pattern in self._adk_config.blocked_patterns:
259292
if pattern.lower() in content_lower:
260293
return False, f"Content matches blocked pattern: '{pattern}'"
261294
return True, ""
262295

263296
def _check_timeout(self) -> tuple[bool, str]:
297+
"""Check whether the kernel has exceeded its configured timeout.
298+
299+
Returns:
300+
Tuple of (within_limit: bool, reason: str).
301+
"""
264302
elapsed = time.time() - self._start_time
265303
if elapsed > self._adk_config.timeout_seconds:
266304
return False, f"Execution timeout ({elapsed:.0f}s > {self._adk_config.timeout_seconds}s)"
267305
return True, ""
268306

269307
def _check_budget(self, cost: float = 1.0) -> tuple[bool, str]:
308+
"""Check whether a tool call would exceed the configured cost budget.
309+
310+
Args:
311+
cost: Cost units to add for this call (default 1.0).
312+
313+
Returns:
314+
Tuple of (within_budget: bool, reason: str).
315+
"""
270316
if self._adk_config.max_budget is not None:
271317
if self._budget_spent + cost > self._adk_config.max_budget:
272318
return False, (
@@ -286,6 +332,17 @@ def _needs_approval(self, tool_name: str) -> bool:
286332
return True
287333

288334
def _raise_violation(self, policy_name: str, description: str) -> PolicyViolationError:
335+
"""Create, record, and surface a PolicyViolationError.
336+
337+
Appends the error to the violations list and calls on_violation.
338+
339+
Args:
340+
policy_name: Short identifier for the violated policy rule.
341+
description: Human-readable description of the violation.
342+
343+
Returns:
344+
The constructed PolicyViolationError (caller may raise it).
345+
"""
289346
error = PolicyViolationError(policy_name, description)
290347
self._violations.append(error)
291348
self.on_violation(error)

packages/agent-os/src/agent_os/integrations/guardrails_adapter.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ class ValidationOutcome:
7777
metadata: dict[str, Any] = field(default_factory=dict)
7878

7979
def to_dict(self) -> dict[str, Any]:
80+
"""Serialise this outcome to a plain dictionary.
81+
82+
Returns:
83+
A dict with validator, passed, and optionally error
84+
and fixed_value keys.
85+
"""
8086
d: dict[str, Any] = {
8187
"validator": self.validator_name,
8288
"passed": self.passed,
@@ -101,9 +107,19 @@ class ValidationResult:
101107

102108
@property
103109
def failed_validators(self) -> list[str]:
110+
"""Return the names of all validators that did not pass.
111+
112+
Returns:
113+
List of validator name strings where passed is False.
114+
"""
104115
return [o.validator_name for o in self.outcomes if not o.passed]
105116

106117
def to_dict(self) -> dict[str, Any]:
118+
"""Serialise this aggregated result to a plain dictionary.
119+
120+
Returns:
121+
A dict with passed, action, outcomes, and failed_validators keys.
122+
"""
107123
return {
108124
"passed": self.passed,
109125
"action": self.action_taken.value,
@@ -128,9 +144,23 @@ def __init__(self, patterns: list[str], validator_name: str = "regex"):
128144

129145
@property
130146
def name(self) -> str:
147+
"""Return the human-readable name of this regex validator.
148+
149+
Returns:
150+
The validator name string used in audit logs and outcomes.
151+
"""
131152
return self._name
132153

133154
def validate(self, value: str, metadata: dict[str, Any] | None = None) -> ValidationOutcome:
155+
"""Validate a string by checking it against blocked regex patterns.
156+
157+
Args:
158+
value: The text to scan.
159+
metadata: Optional dict of additional context (unused).
160+
161+
Returns:
162+
ValidationOutcome indicating pass or fail.
163+
"""
134164

135165
for pattern in self._patterns:
136166
match = pattern.search(value)
@@ -152,9 +182,23 @@ def __init__(self, max_length: int = 10000, validator_name: str = "length"):
152182

153183
@property
154184
def name(self) -> str:
185+
"""Return the human-readable name of this length validator.
186+
187+
Returns:
188+
The validator name string used in audit logs and outcomes.
189+
"""
155190
return self._name
156191

157192
def validate(self, value: str, metadata: dict[str, Any] | None = None) -> ValidationOutcome:
193+
"""Validate that a string does not exceed the configured max length.
194+
195+
Args:
196+
value: The text to check.
197+
metadata: Optional dict of additional context (unused).
198+
199+
Returns:
200+
ValidationOutcome with a fixed_value truncated to max_length on fail.
201+
"""
158202
if len(value) > self._max_length:
159203
return ValidationOutcome(
160204
validator_name=self._name,
@@ -174,9 +218,23 @@ def __init__(self, blocked_keywords: list[str], validator_name: str = "keywords"
174218

175219
@property
176220
def name(self) -> str:
221+
"""Return the human-readable name of this keyword validator.
222+
223+
Returns:
224+
The validator name string used in audit logs and outcomes.
225+
"""
177226
return self._name
178227

179228
def validate(self, value: str, metadata: dict[str, Any] | None = None) -> ValidationOutcome:
229+
"""Validate that a string contains none of the blocked keywords.
230+
231+
Args:
232+
value: The text to scan (case-insensitive).
233+
metadata: Optional dict of additional context (unused).
234+
235+
Returns:
236+
ValidationOutcome indicating pass or fail.
237+
"""
180238
value_lower = value.lower()
181239
for kw in self._keywords:
182240
if kw in value_lower:
@@ -213,6 +271,14 @@ def __init__(
213271
self._history: list[ValidationResult] = []
214272

215273
def _default_violation_handler(self, result: ValidationResult) -> None:
274+
"""Default handler called when one or more validators fail.
275+
276+
Logs a warning for each failed validator name. Override by
277+
passing a custom on_violation callable to GuardrailsKernel.
278+
279+
Args:
280+
result: The aggregated ValidationResult.
281+
"""
216282
for name in result.failed_validators:
217283
logger.warning(f"Guardrail violation: {name}")
218284

packages/agent-os/src/agent_os/integrations/pydantic_ai_adapter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,12 @@ def run_sync(self_inner, prompt: str, **kwargs) -> Any:
237237

238238
@property
239239
def original(self_inner) -> Any:
240+
"""Return the original unwrapped agent before governance wrapping."""
240241
return self_inner._original
241242

242243
@property
243244
def context(self_inner) -> ExecutionContext:
245+
"""Return the ExecutionContext tracking call counts and session state."""
244246
return self_inner._ctx
245247

246248
def __getattr__(self_inner, name: str) -> Any:
@@ -364,6 +366,7 @@ def _wrap_single_tool(
364366

365367
@wraps(original_fn)
366368
def governed_fn(*args: Any, **kwargs: Any) -> Any:
369+
"""Governed wrapper that validates and delegates PydanticAI tool calls."""
367370
# Build arguments dict for policy check
368371
call_args: dict[str, Any] = kwargs.copy()
369372
if args:

0 commit comments

Comments
 (0)