You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e48caf6
Browse filesBrowse the repository at this point in the historyBrowse files
feat(otel): client-side redaction of PII and/or secrets (#584)
* feat: first shot
* fix: tweaks
* fix: resolve redaction and policy changes
* feat: add secret regexes to default redaction patterns
Fold AWS, Google, JWT, PEM and Stripe key patterns into
DEFAULT_TOKEN_PATTERNS and compose DEFAULT_PII_SECRET_PATTERNS from it,
with tests covering each secret and the composition invariant.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* refactor: type RedactingSpanExporter against SpanExporter base
Use a conditional base (SpanExporter under TYPE_CHECKING, object at
runtime) so linters verify the export/shutdown/force_flush overrides
while keeping the OpenTelemetry SDK an optional import.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* feat: redact string elements of homogeneous sequences
Add a shared _redact_value helper so both policies scan the string
elements of list/tuple attribute values instead of passing them through
verbatim, preserving the container type and leaving numeric/bool
sequences untouched.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* test: convert redaction tests to pytest style
Rewrite test_redaction.py as pytest classes with fixtures and
parametrization, and convert the TestTelemetryRedaction class in
test_telemetry.py to a plain pytest class (using caplog for log
assertions). Optional span attributes/context are narrowed with
asserts instead of file-level pyright suppressions. Older telemetry
tests are left as-is.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* fix: tweaks
* fix: additional sensitive keys
* docs: add observability docs
* feat: more secret patterns
* feat: make RegexRedactionPolicy the default redaction policy
Switch default_redaction_policy() from the key-oriented
AttributeRedactionPolicy to the content-oriented RegexRedactionPolicy,
which preserves keys/structure and redacts only matched secret/PII
substrings. Update docstrings, README policy table, the example, and
adapt the behavioural tests accordingly.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* chore: split in 2 files and adjust public interface
* docs: more detailed redaction docs
* test: reconfiguring dedicated telemetry should apply new redaction
Regression test for the case where a second configure_telemetry call in
dedicated mode is ignored because of the early return on an existing
auto provider. Currently fails: the provider is not rebuilt so the new
redaction setting never takes effect.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* fix: apply new settings when reconfiguring dedicated telemetry
The early return on an existing auto telemetry provider fired before the
replace_existing check, so an explicit configure_telemetry call (which
always requests replacement) was a no-op: the provider was never rebuilt
and a changed redaction setting was silently ignored. Only short-circuit
when replace_existing is False, letting the rebuild path shut down the
old provider and recreate it with the new settings.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* feat: optional metadata to attribute redaction policy
* fix: typing in test
---------
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
Spans capture the operation, model, token usage, and — unless redacted — the input/output messages and tool calls. Telemetry is **opt-in** and lives in the `mistralai.extra.observability` module.
1287
+
1288
+
### Installation
1289
+
1290
+
Install the `telemetry` extra:
1291
+
1292
+
```bash
1293
+
pip install "mistralai[telemetry]"
1294
+
# or: uv add "mistralai[telemetry]"
1295
+
```
1296
+
1297
+
### Enabling telemetry
1298
+
1299
+
Either set an environment variable before creating the client:
1300
+
1301
+
```bash
1302
+
export MISTRAL_SDK_TELEMETRY=dedicated # dedicated | global | false
1303
+
```
1304
+
1305
+
or configure it in code:
1306
+
1307
+
```python
1308
+
import os
1309
+
from mistralai.client import Mistral
1310
+
from mistralai.extra.observability import configure_telemetry
1311
+
1312
+
with Mistral(api_key=os.environ["MISTRAL_API_KEY"]) as client:
1313
+
# Dedicated mode (default): the SDK creates and owns an OTLP exporter that
1314
+
# ships spans to the Mistral telemetry endpoint. Spans are redacted before
1315
+
# export.
1316
+
configure_telemetry(client)
1317
+
1318
+
client.chat.complete(
1319
+
model="mistral-small-latest",
1320
+
messages=[{"role": "user", "content": "Hello!"}],
1321
+
)
1322
+
```
1323
+
1324
+
### Provider modes
1325
+
1326
+
`configure_telemetry(client, provider=...)` selects where spans go and who owns the export pipeline:
1327
+
1328
+
|`provider`| Who owns the exporter | Where spans go | Redaction |
|`"global"`| Your application | Your global OpenTelemetry provider |**Not** applied — you need to wrap your own exporter |
1332
+
| a `TracerProvider`| Your application | The provider you pass |**Not** applied — you need to wrap your own exporter |
1333
+
1334
+
In `global`/custom modes your application owns the pipeline, so the `redaction` argument is ignored (a warning is logged). Wrap your own exporter with `RedactingSpanExporter` to redact spans there:
1335
+
1336
+
```python
1337
+
from opentelemetry import trace
1338
+
from opentelemetry.sdk.trace import TracerProvider
1339
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
1340
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
1341
+
from mistralai.extra.observability import RedactingSpanExporter, configure_telemetry
# SDK spans now flow through your global provider (already redacted above).
1350
+
configure_telemetry(client, provider="global")
1351
+
```
1352
+
1353
+
### Redaction
1354
+
1355
+
In dedicated mode, redaction is on by default. Control it with the `redaction` argument, which also accepts any of the reusable policies from `mistralai.extra.observability`:
1356
+
1357
+
```python
1358
+
from mistralai.extra.observability import AttributeRedactionPolicy
|`RegexRedactionPolicy` (default, `redaction=True`) | Content-oriented: keeps keys and structure, redacts matched substrings (secret tokens plus PII — emails, card-like sequences, IPv4). | Redacts most sensitive data while preserving observability value; may miss free-form PII or secrets not in the pattern set. |
1372
+
|`AttributeRedactionPolicy`| Key-oriented: redacts whole values for sensitive keys (explicit set, fragment match, or non-primitive value), then scans kept values for secret token patterns. | Very conservative, but erases most prompt/response content. |
1373
+
|`CallbackRedactionPolicy` (`redaction=<callable>`) | Your `(key, value) -> value \| None` masker per attribute; return `None` to drop the attribute. | Full control; you own the logic. |
1374
+
1375
+
The built-in defaults are exported as constants, so you can extend them instead of replacing them wholesale:
1376
+
1377
+
```python
1378
+
import re
1379
+
1380
+
from mistralai.extra.observability import (
1381
+
DEFAULT_PII_SECRET_PATTERNS,
1382
+
DEFAULT_SENSITIVE_ATTRIBUTE_KEYS,
1383
+
AttributeRedactionPolicy,
1384
+
RegexRedactionPolicy,
1385
+
)
1386
+
1387
+
# Content-oriented: add a custom secret pattern to the default set.
|`MISTRAL_OTLP_TRACES_ENDPOINT`| Override the OTLP traces endpoint used in dedicated mode. |`https://api.mistral.ai/telemetry/v1/traces`|
1412
+
|`MISTRAL_SDK_DEBUG_TRACING`| Set to `true` for verbose tracing logs. |`false`|
1413
+
|`MISTRAL_API_KEY`| Used as the bearer token for the dedicated-mode exporter. | — |
1414
+
1415
+
Runnable examples live in [`examples/mistral/observability`](/examples/mistral/observability).
1416
+
1417
+
1280
1418
# Development
1281
1419
1282
1420
## Contributions
1283
1421
1284
-
While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation.
1285
-
We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release.
1422
+
While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation.
1423
+
We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release.
0 commit comments