Metadata-Version: 2.4
Name: voxsheild
Version: 1.0.0
Summary: Client for the VoxVerify voice-authenticity API: batch analysis, the risk gate, and live calls.
License-Expression: MIT
Keywords: voice,deepfake,speech,authenticity,websocket,api-client
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27
Requires-Dist: websockets>=14.0
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: anyio>=4; extra == "test"

<!-- Generated by tools/gen_sdk.py from the VoxVerify OpenAPI document.
     Do not edit: `python tools/gen_sdk.py` overwrites it, and tests/test_sdk.py fails
     first. An HTML comment because the registries render this file. -->
# voxsheild

Client for the **VoxVerify** voice-authenticity API — batch file analysis, the
pre-action risk gate, and live-call scoring over a WebSocket.

The classes keep the service's name, because that is what they are a client of: the API
paths, the environment variables and the OpenAPI document all say VoxVerify. `voxsheild`
is what you install and import.

```bash
pip install voxsheild
```

Python 3.11 or newer, `httpx` and `websockets>=14.0`. The floor on `websockets` is real: the stream client passes `additional_headers=`, which 14.0 renamed from `extra_headers`.

## Analyse a recording

```python
from voxsheild import VoxVerify

with VoxVerify("http://127.0.0.1:8000", api_key=KEY) as vx:
    verdict = vx.analyze("call.wav", segments=True)
    print(verdict["verdict"], verdict["ai_score"])   # LIKELY_AI 0.91
```

Three verdicts, never two: `LIKELY_HUMAN`, `LIKELY_AI`, `INCONCLUSIVE`. The service
abstains rather than guessing when the fused score does not clear a validation-selected
threshold, or when the fusion weight behind it is split. Treat the estimate as evidence,
not proof — the absence of a synthesis cue is not evidence of human origin.

## Score a live call

```python
import asyncio
from voxsheild import LiveCall

async def main():
    async with LiveCall("http://127.0.0.1:8000", api_key=KEY,
                        sample_rate=8000, codec="pcm_mulaw") as call:
        asyncio.create_task(pump_audio(call))   # your RTP / device loop
        async for event in call.events():
            if event["type"] == "risk":
                print(event["state"], event["confidence"])
        print(await call.stop())
```

`send()` and `events()` are meant to run in different tasks: audio arrives continuously while events come back on their own schedule. Do not iterate `events()` from two tasks at once.

Frames are capped at 1,048,576 bytes, and both clients split at the cap rather than
let an oversized frame close the socket and cost the call every window it had
accumulated. The caps are generated from the document, so a deployment that tunes them
does not need this package patched.

## Auth

Keys are per-tenant and scoped, and the tenant comes off the key — no request body may
name one. 10 of the 18 documented operations require a scope; the batch
forensic routes are open by design. A 403 carrying `scope` means the key is real but
provisioned for a different surface, which is a different fix from a bad key.

## Version

`1.0.0`, which is the API document's version rather than a number this package
maintains separately. The request methods are generated from that document; the
live-call client is hand-written, because OpenAPI 3.1 has no vocabulary for a socket.

Prototype for research and evaluation. Do not present any output of the service as proof
of origin, and do not use it as the sole basis for an accusation.
