Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ agent_control.init(
agent_name="my-agent", # Required: human-readable name
agent_name="550e8400-e29b-41d4-a716-446655440000", # Required: UUID
server_url="http://localhost:8000", # Optional: defaults to env var
policy_refresh_interval_seconds=60, # Optional: set 0 to disable background refresh
steps=[ # Optional: register available steps
{
"type": "tool",
Expand All @@ -600,6 +601,9 @@ agent_control.init(
)
```

When enabled, background refresh fetches controls via `GET /agents/{agent_id}/controls`.
Refresh failures are fail-open: the SDK keeps the last successful local cache snapshot.

### The @control Decorator

The `@control()` decorator applies server-side policies to any function.
Expand Down
9 changes: 6 additions & 3 deletions sdks/python/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.PHONY: help test lint lint-fix typecheck build publish

TEST_DB ?= agent_control_test

help:
@echo "Agent Control SDK - Makefile commands"
@echo ""
Expand All @@ -17,14 +19,15 @@ help:

test:
@echo "Starting server for tests..."
$(MAKE) -C ../../ server-alembic-upgrade
DB_DATABASE=$(TEST_DB) uv run --package agent-control-server python scripts/reset_test_db.py
DB_DATABASE=$(TEST_DB) $(MAKE) -C ../../ server-alembic-upgrade
@# Start server in background and save PID
@uv run --package agent-control-server uvicorn agent_control_server.main:app --port 8000 --host 0.0.0.0 > server.log 2>&1 & echo $$! > server.pid
@DB_DATABASE=$(TEST_DB) uv run --package agent-control-server uvicorn agent_control_server.main:app --port 8000 --host 0.0.0.0 > server.log 2>&1 & echo $$! > server.pid
@echo "Waiting for server..."
@bash -c 'for i in {1..30}; do if curl -s http://localhost:8000/health >/dev/null; then echo "Server up!"; exit 0; fi; sleep 1; done; echo "Server failed"; cat server.log; exit 1'
@# Run tests, capture exit code, and ensure cleanup
@set -e; \
uv run pytest --cov=src --cov-report=xml:../../coverage-sdk.xml -q; \
DB_DATABASE=$(TEST_DB) uv run pytest --cov=src --cov-report=xml:../../coverage-sdk.xml -q; \
TEST_EXIT_CODE=$$?; \
echo "Stopping server..."; \
if [ -f server.pid ]; then kill `cat server.pid` && rm server.pid; fi; \
Expand Down
11 changes: 9 additions & 2 deletions sdks/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ def init(
agent_description: Optional[str] = None,
agent_version: Optional[str] = None,
server_url: Optional[str] = None,
rules_file: Optional[str] = None,
controls_file: Optional[str] = None,
policy_refresh_interval_seconds: int = 60,
**kwargs
) -> Agent:
```
Expand All @@ -175,11 +176,17 @@ Initialize Agent Control with your agent's information.
- `agent_description`: Optional description
- `agent_version`: Optional version string
- `server_url`: Optional server URL (defaults to `AGENT_CONTROL_URL` env var)
- `rules_file`: Optional rules file path (auto-discovered if not provided)
- `controls_file`: Optional controls file path (auto-discovered if not provided)
- `policy_refresh_interval_seconds`: Background cache refresh interval in seconds.
Default `60`; set to `0` to disable background refresh.
- `**kwargs`: Additional metadata

**Returns:** `Agent` instance

When background refresh is enabled, the SDK refreshes cache snapshots via
`GET /agents/{agent_id}/controls`. On refresh failures, it keeps the previous
snapshot (fail-open behavior).

### Decorator

#### `@control()`
Expand Down
41 changes: 41 additions & 0 deletions sdks/python/scripts/reset_test_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Reset the configured SDK test database to an empty schema."""

from __future__ import annotations

from sqlalchemy import MetaData, create_engine, text

from agent_control_server.config import db_config


def _ensure_postgres_database_exists() -> None:
admin_url = (
f"postgresql+{db_config.driver}://{db_config.user}:{db_config.password}@"
f"{db_config.host}:{db_config.port}/postgres"
)
admin_engine = create_engine(admin_url, isolation_level="AUTOCOMMIT")

with admin_engine.connect() as conn:
exists = conn.execute(
text("SELECT 1 FROM pg_database WHERE datname = :name"),
{"name": db_config.database},
).scalar()
if not exists:
database_name = db_config.database.replace('"', '""')
conn.execute(text(f'CREATE DATABASE "{database_name}"'))

admin_engine.dispose()


def reset_database() -> None:
if db_config.url is None:
_ensure_postgres_database_exists()

engine = create_engine(db_config.get_url(), future=True)
reflected_metadata = MetaData()
reflected_metadata.reflect(bind=engine)
reflected_metadata.drop_all(bind=engine)
engine.dispose()


if __name__ == "__main__":
reset_database()
Loading
Loading