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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,6 @@ __marimo__/
# Streamlit
.streamlit/secrets.toml
internal/

# macOS
.DS_Store
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# WildEdge Python SDK
<p align="center">
<img src="https://raw.githubusercontent.com/wild-edge/wildedge-python/devel/docs/wildedge-logo-text.svg" alt="WildEdge" height="72"/>
</p>

<br/>

[![CI](https://github.com/wild-edge/wildedge-python/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/wild-edge/wildedge-python/actions/workflows/ci.yml)
[![Python Versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://pypi.org/project/wildedge-sdk/)
Expand Down Expand Up @@ -40,6 +44,7 @@ Useful flags:
| `--hubs` | Hub trackers to activate: `huggingface`, `torchhub` |
| `--print-startup-report` | Print per-integration status at startup |
| `--strict-integrations` | Fail if a requested integration can't be loaded |
| `--attachments` | Enable opt-in raw input/output attachment upload |
| `--no-propagate` | Don't pass WildEdge env vars to child processes |

## SDK
Expand Down Expand Up @@ -102,8 +107,9 @@ For unsupported frameworks, see [Manual tracking](https://github.com/wild-edge/w
| `app_identity` | `<project_key>` | Namespace for offline persistence. Set per-app in multi-process workloads (or `WILDEDGE_APP_IDENTITY`) |
| `enable_offline_persistence` | `true` | Persist unsent events to disk and replay on restart |
| `sampling_interval_s` | `30.0` | Seconds between background hardware snapshots. Set to `0` or `None` to disable (or `WILDEDGE_SAMPLING_INTERVAL_S`) |
| `attachments_enabled` | `false` | Opt-in upload of raw inference inputs/outputs (or `WILDEDGE_ATTACHMENTS_ENABLED`). See [Attachments](https://github.com/wild-edge/wildedge-python/blob/main/docs/configuration.md#attachments) |

For advanced options (batching, queue tuning, dead-letter storage), see [Configuration](https://github.com/wild-edge/wildedge-python/blob/main/docs/configuration.md).
For advanced options (batching, queue tuning, dead-letter storage, attachments), see [Configuration](https://github.com/wild-edge/wildedge-python/blob/main/docs/configuration.md).

## Projects using this SDK

Expand All @@ -115,9 +121,13 @@ For advanced options (batching, queue tuning, dead-letter storage), see [Configu

Using WildEdge in your project? Open a PR to add it to the list.

## Privacy
## Security & Privacy

By default the SDK transmits only anonymized telemetry, never raw model inputs
or outputs. The one exception is opt-in [attachments](https://github.com/wild-edge/wildedge-python/blob/main/docs/configuration.md#attachments)
(`attachments_enabled`), which upload raw bytes you explicitly pass in.

Report security & priact issues to: wildedge@googlegroups.com.
Report security and privacy issues to: support@wildedge.dev

## Links

Expand Down
18 changes: 18 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,21 @@ Full reference for all `WildEdge` client parameters.
| `max_event_age_sec` | `900` | - | Max age in seconds before an event is dropped |
| `enable_dead_letter_persistence` | `false` | - | Persist dropped batches to disk for later inspection |
| `debug` | `false` | `WILDEDGE_DEBUG` | Log SDK internals to console |

## Attachments

Opt-in upload of raw inference inputs and outputs alongside telemetry. Off by
default, and requires the attachments feature to be enabled on your project.
Bytes are buffered to disk and uploaded in the background via presigned URLs;
event flushes never block on an upload.

| Parameter | Default | Env var | Description |
|---|---|---|---|
| `attachments_enabled` | `false` | `WILDEDGE_ATTACHMENTS_ENABLED` | Enable raw input/output upload |
| `max_attachments_per_inference` | `10` | - | Cap on attachments buffered per inference event |
| `max_attachment_size_bytes` | `10485760` | - | Per-attachment size limit (10 MB); larger ones are dropped |
| `attachment_storage_strategy` | `"file"` | - | `"file"` (bytes on disk) or `"inline"` (small blobs embedded in the record) |
| `attachment_filter` | `None` | - | `Callable[[list[Attachment]], list[Attachment]]` to redact/drop before buffering |
| `attachment_dir` | per `app_identity` | - | Override the local buffer directory |

See [Track attachments](manual-tracking.md#track-attachments) for usage.
36 changes: 36 additions & 0 deletions docs/manual-tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,42 @@ handle.feedback(FeedbackType.THUMBS_DOWN)

`FeedbackType` values: `THUMBS_UP`, `THUMBS_DOWN`.

## Track attachments

Opt-in upload of the raw bytes behind an inference, such as the source image,
audio clip, or generated text, so you can inspect or curate them later. This is
off by default and requires the attachment feature enabled on your project.
Enable it at init, then pass `attachments=` to `track_inference`:

```python
import wildedge
from wildedge import Attachment

client = wildedge.init(attachments_enabled=True)
handle = client.register_model(model, model_id="doc-classifier-v1")

handle.track_inference(
duration_ms=120,
input_modality="image",
output_modality="text",
attachments=[
Attachment(content_type="image/jpeg", role="input", data=image_bytes),
Attachment(content_type="text/plain", role="output", data=answer.encode()),
],
)
```

An `Attachment` carries either in-memory `data` or a file `path`, plus a
`role` (`"input"` or `"output"`) and `content_type`. The SDK writes a reference
into the inference event immediately and uploads the bytes in the background via
a presigned URL. A failed or disabled upload never blocks telemetry.

Bytes are buffered to disk and survive restarts. Capture is gated by
`max_attachments_per_inference`, `max_attachment_size_bytes`, and an optional
`attachment_filter` hook. See [Configuration](configuration.md#attachments) and
[`examples/attachments_example.py`](../examples/attachments_example.py) for the
full reference.

## Track spans for agentic workflows

Use span events to track non-inference steps like planning, tool calls, retrieval, or memory updates.
Expand Down
99 changes: 99 additions & 0 deletions docs/wildedge-logo-text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions examples/attachments_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# /// script
# requires-python = ">=3.10"
# dependencies = ["wildedge-sdk"]
#
# [tool.uv.sources]
# wildedge-sdk = { path = "..", editable = true }
# ///
"""
Attachment upload example. Run with: uv run attachments_example.py

Opt-in raw input/output capture. When `attachments_enabled=True` (and the
project has the paid feature turned on), the SDK buffers the raw bytes locally,
writes a reference into the inference event, and uploads the bytes independently
via a presigned URL, so the batch flush never waits on the upload.

Attachments are off by default and must be explicitly enabled. Set WILDEDGE_DSN
to see real uploads; otherwise the client runs in no-op mode.
"""

import wildedge
from wildedge import Attachment


# Optional: redact / drop attachments before they are buffered.
def redact(attachments: list[Attachment]) -> list[Attachment]:
return [a for a in attachments if a.content_type != "application/secret"]


client = wildedge.init(
app_version="1.0.0",
attachments_enabled=True,
max_attachments_per_inference=5,
max_attachment_size_bytes=5 * 1024 * 1024,
attachment_storage_strategy="file", # or "inline" for small blobs
attachment_filter=redact,
)

handle = client.register_model(
object(),
model_id="doc-classifier-v1",
source="local",
family="custom",
)

# Pretend these came from a real inference call.
image_bytes = b"\xff\xd8\xff\xe0fake-jpeg-bytes"
answer = "This document is an invoice."

inference_id = handle.track_inference(
duration_ms=120,
input_modality="image",
output_modality="text",
attachments=[
Attachment(content_type="image/jpeg", role="input", data=image_bytes),
Attachment(content_type="text/plain", role="output", data=answer.encode()),
],
)

print(f"tracked inference {inference_id[:8]}… with 2 attachments")

# Bytes upload in the background; flush/close lets buffered events drain.
client.close()
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "wildedge-sdk"
version = "0.1.4"
description = "On-device ML inference monitoring for Python"
version = "0.1.5"
description = "ML inference monitoring for Python: on-device and remote models"
readme = "README.md"
requires-python = ">=3.10"
dependencies = []
Expand Down
Loading
Loading