Saf3AI SDK for Python
Observability, security scanning, and data lineage for production AI agents
Overview
saf3ai-sdk is the official Python client for the Saf3AI platform. It combines:
- Observability — Saf3AI-native tracing with rich attributes (conversation IDs, models, usage, lineage).
- Security — Integration with the Saf3AI Scanner for prompt and optional response analysis, with policy callbacks.
- Governance — Automatic capture of data sources (HTTP, tools, DB access) onto traces for audit and compliance workflows.
Call init() once at process startup. The SDK detects your agent framework, configures the Saf3AI TraceProvider, connects to your Saf3AI platform account, and enables framework-aware instrumentation.
Table of contents
- Why use this SDK
- Platform capabilities
- Architecture
- Requirements
- Installation
- Quick start
- Configuration
- Supported frameworks
- Observability conventions
- Security scanning
- Production checklist
- API surface
- Troubleshooting
- Recent changes
- Support & license
Why use this SDK
| Need | How the SDK helps |
|---|---|
| Single pane for agents | One initialization path sends trace data to Saf3AI with consistent service and semantic attributes. |
| Safe defaults | Scanner and platform auth can share SAF3AI_API_KEY unless explicitly overridden. |
| Framework drift | Optional imports: install only ADK, LangChain, CrewAI, OpenAI Agents, AutoGen, or Bedrock; detection order is documented and predictable. |
| Operational clarity | Conversation stitching and cross-framework capture improve troubleshooting in production. |
Platform capabilities
Observability
- Saf3AI
TraceProviderowned by the SDK (set duringinit()). - Trace delivery to your Saf3AI platform workspace.
- Framework tracers:
saf3ai-adk,saf3ai-langchain,saf3ai-crewai,saf3ai-rest. - Optional console export for local development.
Security
- Prompt scanning and optional response scanning via callbacks or direct helpers.
- Policy hooks return allow/block; results can be attached to traces when enabled.
Data lineage
- HTTP — Outbound request sources can be attached to active traces.
- MCP — Tool and resource usage can be attached as lineage entries.
- Databases — Database operations can be captured for governance and compliance contexts.
Architecture
flowchart TB
classDef box fill:#f8fbff,stroke:#2b5876,stroke-width:1.5px,color:#0f172a;
classDef platform fill:#eefbf3,stroke:#1f7a4a,stroke-width:1.5px,color:#0f172a;
subgraph App["Application Layer"]
A[Your Agent Experience]
end
subgraph SDK["Saf3AI SDK Layer"]
I[Initialize SDK]
T[Saf3AI TraceProvider]
F[Framework Integrations]
G[Security and Governance Controls]
end
subgraph Platform["Saf3AI Platform Layer"]
P[Unified Saf3AI Platform]
end
A --> I
I --> T
I --> F
I --> G
T --> P
F --> P
G --> P
class A,I,T,F,G box;
class P platform;
Requirements
| Item | Detail |
|---|---|
| Python | 3.9, 3.10, 3.11, or 3.12 |
| Network | Outbound HTTPS access to Saf3AI platform endpoints |
| Credentials | Organization API key and Saf3AI account endpoint |
| Frameworks | Optional: Google ADK, LangChain / LangFlow, CrewAI, OpenAI Agents, Microsoft AutoGen, or Amazon Bedrock — install only what you run |
Installation
pip install saf3ai-sdk
This command installs the version currently published on PyPI. This checkout prepares 0.2.5; to verify it before publication, install it with pip install -e . and check python -m pip show saf3ai-sdk. Once published, deploy the tested version explicitly with pip install saf3ai-sdk==0.2.5.
Development dependencies (formatting, tests, types):
pip install saf3ai-sdk[dev]
Quick start
Initialize before you construct agents or LLM clients.
import os
from saf3ai_sdk import init, get_detected_framework
init(
agent_id=os.environ["SAF3AI_AGENT_ID"],
api_key=os.environ["SAF3AI_API_KEY"],
safeai_collector_agent=os.environ["SAF3AI_COLLECTOR_AGENT"],
service_name=os.getenv("SAF3AI_SERVICE_NAME"),
scanner_endpoint=os.getenv("SAF3AI_SCANNER_ENDPOINT"),
# Only needed when the policy's registered agent identifier differs from agent_id:
scanner_agent_identifier=os.getenv("SAF3AI_SCANNER_AGENT_IDENTIFIER"),
)
print(get_detected_framework()) # e.g. "adk", "langchain", "crewai", "openai-agents", "autogen", "bedrock", "custom"
For richer configuration, pass an SDKConfig object into init() (see saf3ai_sdk.config.sdk_config).
Configuration
Required parameters or environment
| Variable / parameter | Purpose |
|---|---|
agent_id |
Stable identifier for this agent instance in trace data |
scanner_agent_identifier |
Registered agent identifier used by agent-scoped scanner rules; defaults to agent_id |
api_key / SAF3AI_API_KEY |
Authenticates platform and scanner access |
safeai_collector_agent |
Saf3AI endpoint base URL (required if not injected by your runtime wrapper) |
Security: Never commit API keys. Use secret managers or CI-injected variables in production.
Frequently used optional variables
| Variable | Purpose |
|---|---|
SAF3AI_SERVICE_NAME |
Human-readable service.name when different from agent_id |
SAF3AI_SCANNER_ENDPOINT |
Scanner API base URL |
SAF3AI_SCANNER_AGENT_IDENTIFIER |
Example application variable passed explicitly to scanner_agent_identifier; the SDK does not read it on its own |
SAF3AI_SCAN_RESPONSES |
Scan model outputs (true / false) |
SAF3AI_CAPTURE_RESPONSES |
Attach response text to traces when permitted by policy |
SAF3AI_DEBUG_MODE |
Verbose SDK logging |
SAF3AI_CONSOLE_OUTPUT |
Emit trace data to stdout (development) |
SAF3AI_CLIENT_DATA_CAPTURE |
Client context enrichment where supported |
Example: shell
export SAF3AI_API_KEY="your-key"
export SAF3AI_COLLECTOR_AGENT="https://collector.example.com"
export SAF3AI_SCANNER_ENDPOINT="https://scanner.example.com"
export SAF3AI_AGENT_ID="claims-agent-prod"
Supported frameworks
Detection order: ADK -> CrewAI -> LangChain -> OpenAI Agents -> AutoGen -> Bedrock -> custom.
| Framework | Auto-detected | Built-in instrumentation after init() |
|---|---|---|
| Google ADK | Yes | Yes (saf3ai-adk) |
| LangChain / LangFlow | Yes | Yes (saf3ai-langchain) |
| CrewAI | Yes | Yes (saf3ai-crewai) |
| OpenAI Agents | Yes | Yes (saf3ai-openai-agents) |
| Microsoft AutoGen | Yes | Yes (saf3ai-autogen) |
| Amazon Bedrock | Yes | Yes (saf3ai-bedrock) |
| Custom / REST | Fallback | Yes (saf3ai-rest) |
Install only the stack your service imports; the SDK selects the first matching framework.
Observability conventions
- Traces include standardized Saf3AI metadata such as
service.name,saf3ai.sdk.version, and environment. - Conversation stitching uses
gen_ai.conversation.idandconversation_idhelpers (get_conversation_id_unified,set_conversation_id_unified,reset_conversation). - Third-party spans may omit conversation attributes; the SDK aligns them with the unified conversation where possible.
Security scanning
Agent-scoped rules in 0.2.5
An agent-scoped rule matches only when the /scan request includes the exact registered identifier as metadata.agent_identifier. Automatic @traceable prompt and response scans now send it. The value comes from scanner_agent_identifier in init() (default: agent_id); @traceable(agent_identifier="...") overrides it for one decorated agent. Use the registered identifier, not a display name or function name. This is especially important when one process hosts multiple agents.
from saf3ai_sdk import init, traceable
init(
agent_id="it-service-agent",
api_key="...", # Load from a secret manager or environment in production.
safeai_collector_agent="https://your-collector.example",
scanner_endpoint="https://your-scanner.example",
scanner_agent_identifier="registered-policy-id", # Must exactly match the policy identifier.
)
@traceable(name="it-service")
def answer(prompt: str) -> str:
return call_your_model(prompt)
Replace the placeholder with the identifier actually registered for your agent, including its exact spelling. In the reported test this was it service agetn; a corrected-looking name would not match that rule. For a multi-agent process, put agent_identifier="..." on each @traceable decorator instead of relying on one process-wide default. Direct scan_prompt() and scan_response() calls still accept metadata={"agent_identifier": "..."}.
Detection and blocking are different. Automatic @traceable scanning records scan results; it does not, by itself, interpret every match as a block. To prevent a prompt reaching the model, register a policy callback that returns False on a block decision. The prompt callback now runs before the decorated function; the response callback runs before its result is returned. A framework adapter callback can scan again, so choose one scan/enforcement path per interaction where possible.
For a custom agent that needs blocking, use create_framework_security_callbacks(framework="custom", agent_identifier=..., on_scan_complete=...) before invoking the decorated function. on_scan_complete(text, scan_results, text_type) should implement your organization's policy and return True to allow or False to block. ADK, LangChain, CrewAI, and REST adapters also support explicit agent identifiers in their callback configuration. Test both a matching rule and a benign prompt before production.
def security_policy(text: str, scan_results: dict, text_type: str) -> bool:
"""text_type is 'prompt' or 'response' where applicable."""
detections = scan_results.get("detection_results") or {}
for _, result in detections.items():
if isinstance(result, dict) and result.get("result") == "MATCH_FOUND":
return False
return True
Intentional policy blocks
Intentional policy blocks in custom agents should use the shared SDK exception:
from saf3ai_sdk import PolicyBlocked, traceable
@traceable(name="support-agent")
def answer(prompt: str) -> str:
if policy_denies(prompt): # Your application's policy decision.
raise PolicyBlocked("Request denied by policy", phase="prompt", blocked_by="support-policy")
return call_your_model(prompt)
Catch PolicyBlocked at the application boundary to return your blocked-request response. Replace any locally defined exception of that name with this import. Both synchronous and asynchronous @traceable functions propagate it while retaining block evidence, without recording a runtime failure. SDK-owned tracing contexts and call_llm_with_saf3ai() follow the same rule. Other exceptions still record errors; matching a threat in Monitor Only does not itself raise PolicyBlocked. When using a third-party tracing context around the SDK, its exception-recording policy remains under that library's control.
Production checklist
-
init()called once at cold start, before any tracer-backed client. - Secrets from environment or vault; never in source control.
- Platform endpoint reachable from runtime network (VPC / egress rules).
- Scanner endpoint and scan flags match organizational policy (prompt-only vs prompt+response).
- Agent identity: capture one
/scanrequest and verifymetadata.agent_identifierexactly matches the registered rule identifier for each agent. - Enforcement: verify that a blocked prompt never enters the model/tool call and a blocked response is never returned to the caller. Automatic scanning alone is observational.
- Failure policy: decide whether a scanner outage should allow or reject requests. Current callback error paths may fail open; test this against your security requirements.
-
agent_id/service_namestable per deployment unit for cost and ownership dashboards. - Log level appropriate (
INFOorWARNINGin production;DEBUGonly when diagnosing). - Conversation IDs for multi-tenant apps: use explicit session-scoped IDs (
reset_conversation()between unrelated chats).
API surface
| Export | Role |
|---|---|
init(...) |
Configure trace provider, auth, framework and instrumentation wiring |
get_detected_framework() |
Resolved framework string |
get_tracer(name) |
Tracer from the SDK TraceProvider |
traceable |
Trace decorator / wrapper for custom code paths |
PolicyBlocked |
Intentional policy rejection; propagates without marking SDK-owned spans as runtime failures |
call_llm_with_saf3ai(...) |
Model-call wrapper with prompt/response scan callbacks and enforcement evidence |
set_scanner_config, set_global_security_callbacks |
Scanner wiring for traceable flows |
scan_prompt, scan_response, scan_prompt_and_response |
Direct scanner HTTP APIs |
set_custom_attributes, get_custom_attributes, clear_custom_attributes, reset_conversation |
Context and attribute helpers |
create_security_callback |
ADK-oriented security callbacks (lazy import) |
SDKConfig |
Typed configuration object for init() |
Full parameter lists and behaviors live in docstrings in saf3ai_sdk/__init__.py and saf3ai_sdk.config.
Troubleshooting
| Symptom | What to verify |
|---|---|
| No traces | SAF3AI_API_KEY, platform endpoint, firewall, and logs immediately after init() |
| Wrong framework | Import order and installed packages (CrewAI is checked before LangChain) |
| Scanner not invoked | scanner_endpoint, SAF3AI_API_KEY, and callbacks attached to the LLM or ADK agent |
| Rule matches only with manual metadata | Install the fixed 0.2.5 release or this checkout, verify metadata.agent_identifier in the outgoing /scan payload, and check exact spelling against the registered policy identifier |
| Match appears but request was not blocked | Configure and test an enforcement callback; automatic @traceable scanning records results but does not automatically turn every match into a block |
| Duplicate or fragmented conversations | Ensure conversation ID helpers are used consistently per user session |
Recent changes
0.2.5 release candidate (publish and deploy separately)
- Agent identity is included in automatic prompt and response scans.
@traceableprompt-policy callbacks run before agent execution.- AutoGen and Bedrock automatic scan configuration paths are repaired.
- Public
PolicyBlockedpreserves intentional block evidence without classifying the rejection as a runtime failure in sync/async decorators, SDK-owned contexts, and the model-call wrapper.- REST/custom callbacks propagate explicitly raised policy blocks.
- AutoGen accepts generated callback signatures, records completed-scan markers, and enforces response blocks.
- Automatic response scans respect
SAF3AI_SCAN_RESPONSES; prompt text and attribute values are no longer printed at INFO level by@traceable.- Wheel and source archives use core metadata 2.4 for package-tool compatibility.
Local release verification
Run the local regression suite and build both distribution formats before publication:
python -m pytest -q tests
python -m build
python -m twine check dist/*
Tests, caches, and generated packages are intentionally Git-ignored and excluded from distribution archives. Keep the local test suite available in the release workspace; a fresh clone does not include those tests. Release notes remain version-controlled in this README and CHANGELOG.md. The sample-policy checks use mocked scanner/model calls; live framework and scanner validation must be performed against the deployment configuration.
The final local regression run passed 119 tests. Archive-content and installed-wheel smoke checks also verify that the packaged SDK exposes the expected API and reports version 0.2.5. Python 3.9 syntax was checked; the full regression run used Python 3.13 and does not replace runtime checks on your deployment's Python/framework versions.
Support & license
- Issues & features: GitHub Issues
- Email: team@saf3ai.com
- License: MIT
For security-sensitive reports, contact the maintainers directly rather than using public issues until a disclosure process is published.
Saf3AI and the Saf3AI logo are trademarks of their respective owners. This README describes the open-source saf3ai-sdk Python package.
Metadata
Release files for saf3ai-sdk 0.2.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| saf3ai_sdk-0.2.5.tar.gz | 181.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| saf3ai_sdk-0.2.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 390.1 kB
Release files / saf3ai_sdk-0.2.5.tar.gz
| Download URL | saf3ai_sdk-0.2.5.tar.gz |
|---|---|
| Size | 181.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cde8a4982404529c5f696b85d5aaba22875c8264fc06437a7658dd0b33a46d57
|
|
BLAKE2b-256 checksum How to use checksums |
2ba41846004ce39b237f5f21c80cf5e6ef3d31356114336a6f7f9d94177b77c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.13
|
Release files / saf3ai_sdk-0.2.5-py3-none-any.whl
| Download URL | saf3ai_sdk-0.2.5-py3-none-any.whl |
|---|---|
| Size | 208.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8f9ba6cf5e33df7dc33547ac7105c4bf453614aba22596f93b06b16d667410be
|
|
BLAKE2b-256 checksum How to use checksums |
97d2ee1dcfabed094a2963c938d63548def5a54c511a09fd9d0c683779e7c55a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.13
|