What you'll learn
- every name you can import from
foxy_;audit - the arguments and defaults of each class and function;
- every exception, and when you get it;
- which submodules are safe to use, and which are internal.
The top-level names
import foxy_audit
print(foxy_audit.__all__)
What you should see:
['AuditRequiredError', 'CheckResult', 'ExplainResult', 'FoxyClient', 'FoxyConfig', 'FoxyPolicyBlocked', 'FoxyResponseBlocked', 'InsecureEndpointError', 'SpoolFull', 'audit', 'check', 'explain', '__version__']
| Name | Kind | Defined in |
|---|---|---|
FoxyClient |
class | foxy_ |
audit |
function | foxy_ |
check |
function | foxy_ |
explain |
function | foxy_ |
CheckResult |
dataclass | foxy_ |
ExplainResult |
dataclass | foxy_ |
FoxyConfig |
dataclass | foxy_ |
FoxyPolicyBlocked |
exception | foxy_ |
FoxyResponseBlocked |
exception | foxy_ |
AuditRequiredError |
exception | foxy_ |
SpoolFull |
exception | foxy_ |
InsecureEndpointError |
exception | foxy_ |
__ |
text | "1.18.0" |
FoxyClient
class FoxyClient:
def __init__(self, api_key=None, endpoint=None, udp_host=None, udp_port=None,
desktop_ping=True, timeout=5.0, commitment_key=None, commitment_key_path=None,
salt_sidecar_path=None, spool_path=None, client_id=None, system_id=None,
audit_required=None, mode=None, response_scan=None, on_event=None,
injection_classifier=None): ...
Each argument left as None falls back to its environment variable, then to its default. udp_, udp_, desktop_, timeout and on_ have no environment variable. Configuration lists them all.
Raises:
ValueErrorwhensystem_is given but is not written exactly asid GET /returns it;v1/ systems ValueErrorwhenclient_is given with a character other than letters, digits andid _ . : -, or over 128 characters;ValueErrorwhenaudit_is given and no API key is set.required= True FOXY_with no key raises nothing: an error is logged, andAUDIT_ REQUIRED audit_is off;required TypeErrorwhenon_is not a function, or is anevent async deffunction;InsecureEndpointErrorwhenendpointis plainhttp://to another machine;- an error such as
PermissionErrorwhen an API key is set and the spool file's folder cannot be written to; OSErrorwhencommitment_is given and the key file there can be neither read nor made, or holds something that is not a key.key_ path FOXY_raises nothing: a warning is logged, and the API key is used;COMMITMENT_ KEY_ FILE OSErrorwhenaudit_is given and the key file can be neither read nor made, so commitments would be made with the API key.required= True FOXY_raises nothing: an error is logged, and the API key is used.AUDIT_ REQUIRED
When it is made, with an API key set, it:
- makes and keeps a
client_in the spool, if you did not give one;id - reads the commitment key file, or makes it, when no commitment key is set;
- wakes up delivery of records left in the spool by an earlier run;
- signs up for the background read of your workspace's setting. That read never happens on your call's path.
| Attribute | What it is |
|---|---|
cfg |
the resolved settings, a FoxyConfig. It is read-only. |
enabled |
True when an API key is set, so records are sent |
on_ |
the receipt function. Setting it checks it the same way the constructor does. |
FoxyClient.audit
def audit(self, policy="default", agent=None, mode=None, system_id=None): ...
Returns a decorator that checks and records calls to the function it wraps.
It finds the prompt among each call's arguments: the first of the keyword arguments prompt, user_, message, messages, contents, text, input and query, or else the first positional argument that is a string. With neither, it checks and hashes an empty prompt.
| Argument | What it does |
|---|---|
policy |
which checks run. Folded to lower case. An unknown but valid tag runs the basic checks and warns. A malformed one falls back to "default" and warns. |
agent |
the model's name, saved in the record and in the chain. Letters, digits, _ . - : / ( ) + and spaces, up to 128 characters. Any other value raises ValueError when you decorate, because the backend would refuse it. |
mode |
"observe", "block" or "redact", for this function only. An unknown value warns and falls back to "observe". |
system_ |
the AI system for these calls. Raises ValueError when you decorate, if it is malformed. |
The mode governs the prompt only. What happens to the answer is set by the client's response_, which has no per-function setting.
It works with plain functions, async def functions, async def functions that yield, and plain functions that return a generator. Errors your function raises reach you unchanged, after an exception record is made.
It can raise three errors into your code:
FoxyPolicyBlocked:mode="block", a workspace set toblock, or a redaction that could not remove a finding. Your function was not called.FoxyResponseBlocked:response_and the scan flagged the answer. Your function did run. In a stream, the pieces before the flagged one have already reached your code.scan="block" AuditRequiredError:audit_, and the record could not be confirmed. A blocked prompt can raise it in place ofrequired= True FoxyPolicyBlocked.
FoxyClient.check
def check(self, prompt, policy="default"): ...
The same as the module-level check. It uses no client state.
FoxyClient.log_interaction
def log_interaction(self, prompt, response, policy, agent=None, metadata=None,
event_type="interaction", decision=None, policy_rules=None,
signals=None, blocked_reason=None, system_id=None): ...
Records one call without a decorator. It does not check the prompt first.
| Argument | What it does |
|---|---|
prompt, response |
any value. Written out in a fixed way and hashed. The text is dropped right after. |
policy |
the policy tag, folded here. After folding it must be lower-case letters, digits and _, up to 32, or ValueError is raised. |
agent |
the model's name, optional. The same limits as on the decorator: any other value raises ValueError. |
metadata |
extra keys for event_. Keys the SDK sets itself are dropped, with a warning, and so is a key the backend does not accept or a value over its limits. The wire format lists both. |
event_ |
"interaction", "stream", "blocked", "redacted", "exception" or "response_. Anything that is not lower-case letters, digits and _, up to 32, raises ValueError. |
decision, policy_, signals, blocked_ |
the checks' own findings. signals is merged with the SDK's own personal-data scan of the prompt and answer, sorted, with no repeats, and sent as pii_. |
system_ |
the AI system for this call |
Returns, when an API key is set, a small dictionary with the keys status ("queued"), event_ and client_. With audit_ on, it returns the backend's receipt for the record instead. With no key, it returns None.
Raises ValueError for a malformed system_, agent, event_ or policy, and AuditRequiredError when audit_ is on and the record could not be confirmed. Any other problem is logged at debug level and swallowed, so recording can never break your program.
Keys the SDK sets itself in event_, so a copy passed in metadata is dropped with a warning: ruleset_, ruleset_, decision, policy_, blocked_, policy_ and system_. Renaming such a key does not keep it: the SDK drops a key the backend does not know as well.
audit
def audit(policy="default", agent=None, mode=None): ...
The module-level decorator. It uses one client, made from the environment the first time you use it, and kept for the life of the program. It has no system_ argument: set FOXY_, or make a FoxyClient.
check
def check(prompt, policy="default"): ...
Would this prompt trip anything, under today's rules? It needs no key, no network and no spool, and it returns only labels. It may warn about an unknown policy tag.
CheckResult
A read-only dataclass, with these fields: policy, triggered, rules, signals, reason, ruleset_ and ruleset_. as_ gives a plain dictionary with no prompt text in it.
explain
def explain(prompt, event_id, export, commitment_key, salt_sidecar_path=""): ...
Replays one exported record against the ruleset it names, and shows what matched. export is a parsed export, or a path to one. commitment_ is one key, or a list of keys tried in turn. It never raises for a bad export, a missing record, a wrong prompt or an unknown ruleset: each is a status. See Proving a row with foxy explain.
ExplainResult
A read-only dataclass, with these fields: status, message, event_, policy_, ruleset_, commitment_, ruleset_ and matches.
| Member | What it is |
|---|---|
ok |
True when the status is "explained" or "no_ |
as_ |
a plain dictionary. It leaves out the matched text unless you ask for it. |
ruleset_ |
True (checked and matched), False (checked and different) or None (not checked) |
matches |
a list of Match |
Match
Match(rule_, with as_. Import it from foxy_.
introspect.STATUSES
('explained', 'no_matches', 'hash_mismatch', 'row_not_found', 'export_unreadable',
'salt_unavailable', 'unknown_ruleset', 'ruleset_mismatch', 'ruleset_unrecorded',
'no_rules_fired', 'provenance_ambiguous')
Test whether a status is in this tuple, rather than typing the names into your code.
FoxyConfig
A read-only dataclass. FoxyClient makes one for you, in foxy..
from dataclasses import dataclass
@dataclass(frozen=True)
class FoxyConfig:
api_key: str = ""
endpoint: str = "http://127.0.0.1:8000"
udp_host: str = "127.0.0.1"
udp_port: int = 9999
desktop_ping: bool = True
timeout: float = 5.0
commitment_key: str = ""
commitment_key_path: str = ""
salt_sidecar_path: str = ""
spool_path: str = ""
client_id: str = ""
system_id: str = ""
audit_required: bool = False
mode: str = "observe"
mode_is_explicit: bool = False
response_scan: str = "observe"
org_policy_enabled: bool = True
org_policy_ttl: float = 300.0
spool_max_rows: int = 100000
spool_receipt_ttl: float = 604800.0
injection_classifier: bool = True
allow_insecure: bool = False
endpoint_refused: bool = False
| Member | What it is |
|---|---|
FoxyConfig. |
builds one from arguments, then environment variables, then defaults. A bad environment variable never makes it raise. It raises InsecureEndpointError when the endpoint argument is plain http:// to another machine. It also takes org_, org_, spool_ and spool_, which FoxyClient does not pass on. |
enabled |
True when an API key is set |
transport_ |
True when the SDK may send the key to endpoint |
mode_ |
True when a person chose the mode. Your workspace's setting fills in the mode where this is False. A workspace set to block blocks whatever this says. |
endpoint_ |
True when FOXY_ was refused, so nothing is sent |
Exceptions
from foxy_audit import (AuditRequiredError, FoxyPolicyBlocked, FoxyResponseBlocked,
InsecureEndpointError, SpoolFull)
FoxyPolicyBlocked
A RuntimeError. A prompt was blocked before your function ran, because of mode="block" or a redaction that could not remove a finding. A blocked record was made first. The message names the policy tag and a short reason, and never the prompt.
FoxyResponseBlocked
A RuntimeError. An answer was blocked after your function ran, because the scan flagged it and response_ is "block". The answer is not returned. A response_ record was made first.
For a stream, the pieces before the flagged one have already reached your code, and the error is raised inside your loop. The record is then a stream with the decision response_, unless the stream was cut at its first piece.
It has one attribute, audit_. It is True when audit_ is on and the record also could not be confirmed. The block still wins.
It is not a kind of FoxyPolicyBlocked. Code that catches FoxyPolicyBlocked may assume the model was never called. Here, it was.
AuditRequiredError
A RuntimeError, raised only when audit_ is on and the record could not be confirmed: the wait ran out, the spool was full, the backend address is refused, or saving the record failed. Its __ names the problem, when there is one.
When the wait ran out, the record was saved in the spool before the wait began, so it may still reach the ledger later. When the spool was full, the record was refused and never saved, and __ is a SpoolFull.
SpoolFull
A RuntimeError. A record was refused, not saved, because the spool is full. Nothing already in it was thrown away.
Your code never catches it from a call. Without audit_, the SDK logs a warning and your call goes on. With it, you get an AuditRequiredError whose __ is the SpoolFull, with these attributes:
| Attribute | What it is |
|---|---|
path |
the spool file |
rows |
how many records are waiting |
cap |
the limit that was reached |
scope |
"pair" when one key and address reached its limit, "file" when the whole file reached its ceiling |
SpoolFull means "refused, never saved": that call needs recording again.
InsecureEndpointError
A ValueError, raised when a backend address given in code may not carry your key. That is plain http:// to another machine, unless FOXY_ is set, or an address that two parts of Python read differently. The same address in FOXY_ does not raise: it is logged, and nothing is sent.
Warnings and log messages
| What | When |
|---|---|
UserWarning |
an unknown policy tag, once for each tag in each run |
| a warning in the log | a malformed tag or mode on a decorator, a reserved metadata key, a metadata key or value the backend would refuse, an unreadable FOXY_, a refused system, a commitment key that is your API key (once in each run), a new commitment key file (once, with its path), a key file that can be neither read nor made |
| an error in the log | a malformed FOXY_ or FOXY_, FOXY_ with no API key, or a refused FOXY_ |
All SDK logging goes to the "foxy_ logger. The workspace setting uses "foxy_.
Submodules
These are public and stable:
| Module | Use it for |
|---|---|
foxy_ |
evaluate, redact, redact_, surviving_, normalise_, resolve_, KNOWN_, set_ |
foxy_ |
evaluate_, StreamScanner, coverage_, CARRY_ |
foxy_ |
detect_, redact, REDACTION_, deep_ |
foxy_ |
CURRENT_, CURRENT_, load, known_, hash_, provenance, describe_, drift, PROVENANCE_ |
foxy_ |
sha256_, canonical_, commitment_, estimate_ |
foxy_ |
check, explain, replay, STATUSES, CheckResult, ExplainResult, Match, UnknownValidator |
foxy_ |
starts_, issuer_, length_, ISSUERS_2, Issuer |
These are internal. They are listed so you can read them, not so you can depend on them: foxy_, config, dispatch, spool, sidecar, org_, adapters, normalise, udp and cli.
policy., which evaluate returns, is internal too. It is shared by the prompt and answer checks, and it has changed often. Use check() and its CheckResult instead.
Foxy Audit