idside 0.1.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- idside/__about__.py +5 -0
- idside/__init__.py +158 -0
- idside/_destination.py +165 -0
- idside/_guard.py +573 -0
- idside/_jcs.py +130 -0
- idside/_log.py +163 -0
- idside/_routing.py +280 -0
- idside/_transport.py +831 -0
- idside/adapters/__init__.py +110 -0
- idside/adapters/_call_options.py +859 -0
- idside/adapters/base.py +462 -0
- idside/adapters/gemini.py +89 -0
- idside/consent-options.json +207 -0
- idside/consent.py +1580 -0
- idside/exceptions.py +481 -0
- idside/options.py +297 -0
- idside/providers/__init__.py +252 -0
- idside/providers/anthropic.json +108 -0
- idside/providers/deepseek.json +108 -0
- idside/providers/gemini.json +130 -0
- idside/providers/mistral.json +195 -0
- idside/providers/openai.json +217 -0
- idside/py.typed +0 -0
- idside/verify.py +776 -0
- idside-0.1.0.dev0.dist-info/METADATA +473 -0
- idside-0.1.0.dev0.dist-info/RECORD +29 -0
- idside-0.1.0.dev0.dist-info/WHEEL +4 -0
- idside-0.1.0.dev0.dist-info/licenses/LICENSE +350 -0
- idside-0.1.0.dev0.dist-info/licenses/NOTICE +29 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
{
|
|
2
|
+
"catalogue_version": "2",
|
|
3
|
+
"title": "ID Side consent option catalogue",
|
|
4
|
+
"notes": "The single source of truth for what an end user can be asked. Every SDK, the widget, the provider registry and the ACT token spec read their vocabulary from here. Options are grouped by PURPOSE, not by provider: a person decides what may be done with their data, not which vendor may do it. The provider descriptors then declare, option by option, whether they can enforce that choice technically, only contractually, or not at all — see `enforcement` in each descriptor. Defaults are conservative everywhere: an option nobody has answered is treated as refused.",
|
|
5
|
+
"why_purposes_not_a_single_flag": "Providers bundle different processing purposes under one switch and label them differently ('improve the model', 'improve our services', 'personalised experience'). DeepSeek merges training with general service improvement so indistinguishably that a user cannot consent to one without the other. A single boolean cannot express a refusal whose scope differs per provider, and GDPR Article 5(1)(b) purpose limitation is exactly about not doing that.",
|
|
6
|
+
"options": [
|
|
7
|
+
{
|
|
8
|
+
"name": "training.foundation",
|
|
9
|
+
"group": "training",
|
|
10
|
+
"label_en": "Train general AI models on my conversations",
|
|
11
|
+
"label_fr": "Entraîner les modèles d'IA généraux sur mes conversations",
|
|
12
|
+
"description_en": "Your prompts and the answers you receive may be used to train the provider's general-purpose models, which serve every other user.",
|
|
13
|
+
"description_fr": "Vos requêtes et les réponses reçues peuvent servir à entraîner les modèles généraux du fournisseur, qui servent à tous les autres utilisateurs.",
|
|
14
|
+
"default": false,
|
|
15
|
+
"legal_basis": "consent",
|
|
16
|
+
"gdpr": "Art. 6(1)(a); purpose limitation Art. 5(1)(b)",
|
|
17
|
+
"rationale": "The highest-stakes choice across every provider, and the one handled most inconsistently."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "training.personalisation",
|
|
21
|
+
"group": "training",
|
|
22
|
+
"label_en": "Personalise my experience from my history",
|
|
23
|
+
"label_fr": "Personnaliser mon expérience à partir de mon historique",
|
|
24
|
+
"description_en": "Your data may be used to tailor answers to you specifically. This does not feed the general models.",
|
|
25
|
+
"description_fr": "Vos données peuvent servir à adapter les réponses à vous spécifiquement. Cela n'alimente pas les modèles généraux.",
|
|
26
|
+
"default": false,
|
|
27
|
+
"legal_basis": "consent",
|
|
28
|
+
"gdpr": "Art. 6(1)(a); profiling Art. 22 where decisions follow"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "training.analytics",
|
|
32
|
+
"group": "training",
|
|
33
|
+
"label_en": "Aggregated statistics to improve the service",
|
|
34
|
+
"label_fr": "Statistiques agrégées pour améliorer le service",
|
|
35
|
+
"description_en": "Anonymous, aggregated measurements. No individual content is retained.",
|
|
36
|
+
"description_fr": "Mesures agrégées et anonymes. Aucun contenu individuel n'est conservé.",
|
|
37
|
+
"default": false,
|
|
38
|
+
"legal_basis": "legitimate_interest",
|
|
39
|
+
"gdpr": "Art. 6(1)(f), balancing test required",
|
|
40
|
+
"rationale": "Separated out precisely because providers bundle it with foundation training; a user who refuses model training has not necessarily refused this."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "memory",
|
|
44
|
+
"group": "profiling",
|
|
45
|
+
"label_en": "Remember me between conversations",
|
|
46
|
+
"label_fr": "Se souvenir de moi d'une conversation à l'autre",
|
|
47
|
+
"description_en": "The assistant keeps a persistent memory of you across sessions.",
|
|
48
|
+
"description_fr": "L'assistant conserve une mémoire persistante de vous d'une session à l'autre.",
|
|
49
|
+
"default": false,
|
|
50
|
+
"legal_basis": "consent",
|
|
51
|
+
"gdpr": "Art. 6(1)(a); Art. 9 if special categories are inferred",
|
|
52
|
+
"rationale": "Structurally the highest re-identification risk in the AI context: enabling memory creates a behavioural profile anchored to an identified account."
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "agentic_tools",
|
|
56
|
+
"group": "agentic",
|
|
57
|
+
"label_en": "Let the AI use tools that store my data elsewhere",
|
|
58
|
+
"label_fr": "Autoriser l'IA à utiliser des outils qui stockent mes données ailleurs",
|
|
59
|
+
"description_en": "Web search, code execution, file stores, connectors and autonomous agents. These bring retention back in even when everything else is switched off.",
|
|
60
|
+
"description_fr": "Recherche web, exécution de code, stockage de fichiers, connecteurs et agents autonomes. Ils réintroduisent de la conservation même quand tout le reste est désactivé.",
|
|
61
|
+
"default": false,
|
|
62
|
+
"legal_basis": "consent",
|
|
63
|
+
"gdpr": "Art. 6(1)(a); Art. 28 where the tool is a processor",
|
|
64
|
+
"rationale": "THE per-request lever. Providers publish per-feature retention eligibility (code execution, Files, Batch, MCP servers are all excluded from zero-retention arrangements) and do not block their use — using them is the choice. The SDK sees `tools` on the call, so this is the one axis it can genuinely enforce everywhere."
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "data_residency",
|
|
68
|
+
"group": "location",
|
|
69
|
+
"label_en": "Keep processing inside my region",
|
|
70
|
+
"label_fr": "Garder le traitement dans ma région",
|
|
71
|
+
"description_en": "Where the AI actually runs. Refusing regional processing may mean your data is handled outside your jurisdiction.",
|
|
72
|
+
"description_fr": "Où l'IA s'exécute réellement. Renoncer au traitement régional peut signifier que vos données sont traitées hors de votre juridiction.",
|
|
73
|
+
"default": false,
|
|
74
|
+
"legal_basis": "consent",
|
|
75
|
+
"gdpr": "Chapter V — international transfers",
|
|
76
|
+
"rationale": "No provider forces it per request. Anthropic's `inference_geo` is a real body parameter, but its two settings (`global`, `us`) answer 'inside my region' for nobody whose region we know, and we never learn one — `location` is unsupported everywhere on purpose. OpenAI chooses residency by domain (eu./gb./ca.…), which is a property of the credential, not of the call. It stays its own option because it is `routable`: a developer holding one credential per region can have the SDK send each user's calls to the matching one — and because the SDK can read the base URL and record which region a call actually ran in."
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "retention.conversation",
|
|
80
|
+
"group": "retention",
|
|
81
|
+
"label_en": "Store my conversations on the provider's servers",
|
|
82
|
+
"label_fr": "Conserver mes conversations sur les serveurs du fournisseur",
|
|
83
|
+
"description_en": "Server-side history so the assistant can resume where you left off. Note: no provider offers absolute zero — content flagged by trust & safety is retained regardless (up to 2 years at one provider).",
|
|
84
|
+
"description_fr": "Historique côté serveur, pour reprendre une conversation là où elle s'est arrêtée. À noter : aucun fournisseur n'offre le zéro absolu — un contenu signalé par leurs systèmes de sécurité est conservé quoi qu'il arrive (jusqu'à 2 ans chez l'un d'eux).",
|
|
85
|
+
"default": false,
|
|
86
|
+
"legal_basis": "consent",
|
|
87
|
+
"gdpr": "Art. 5(1)(e) storage limitation",
|
|
88
|
+
"rationale": "The most widely available request-level control: OpenAI `store`, Mistral `store` on /v1/conversations, Gemini `store` on the Interactions API, xAI `store_messages`."
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "retention.cache",
|
|
92
|
+
"group": "retention",
|
|
93
|
+
"label_en": "Keep my prompts in a fast cache",
|
|
94
|
+
"label_fr": "Garder mes requêtes dans un cache rapide",
|
|
95
|
+
"description_en": "Prompts are cached to speed up and cheapen repeated calls, typically for up to 24 hours. Note: no provider offers absolute zero — content flagged by trust & safety is retained regardless (up to 2 years at one provider).",
|
|
96
|
+
"description_fr": "Les requêtes sont mises en cache pour accélérer et réduire le coût des appels répétés, généralement jusqu'à 24 heures. À noter : aucun fournisseur n'offre le zéro absolu — un contenu signalé par leurs systèmes de sécurité est conservé quoi qu'il arrive (jusqu'à 2 ans chez l'un d'eux).",
|
|
97
|
+
"default": false,
|
|
98
|
+
"legal_basis": "consent",
|
|
99
|
+
"gdpr": "Art. 5(1)(e)",
|
|
100
|
+
"rationale": "Separated from conversation storage because the levers differ (OpenAI prompt_cache_options.ttl, Vertex project cacheConfig) and a user may reasonably accept one and not the other."
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "retention.files",
|
|
104
|
+
"group": "retention",
|
|
105
|
+
"label_en": "Keep files I upload on the provider's servers",
|
|
106
|
+
"label_fr": "Conserver sur les serveurs du fournisseur les fichiers que j'envoie",
|
|
107
|
+
"description_en": "Documents and images sent to the AI are stored until deleted. Note: no provider offers absolute zero — content flagged by trust & safety is retained regardless.",
|
|
108
|
+
"description_fr": "Les documents et images envoyés à l'IA sont conservés jusqu'à suppression. À noter : aucun fournisseur n'offre le zéro absolu — un contenu signalé par leurs systèmes de sécurité est conservé quoi qu'il arrive.",
|
|
109
|
+
"default": false,
|
|
110
|
+
"legal_basis": "consent",
|
|
111
|
+
"gdpr": "Art. 5(1)(e) storage limitation",
|
|
112
|
+
"rationale": "Uploaded files outlive the conversation that carried them, and are excluded from zero-retention arrangements at every provider that offers one. OpenAI exposes `expires_after` per upload and on vector stores — where the file's CONTENT lives, chunked and indexed — and Mistral exposes `expiry` and `visibility`; all of them on endpoints the SDK does not yet wrap."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "location",
|
|
116
|
+
"group": "collection",
|
|
117
|
+
"label_en": "Use my approximate location",
|
|
118
|
+
"label_fr": "Utiliser ma localisation approximative",
|
|
119
|
+
"description_en": "City or region level, inferred or declared, used to adapt answers.",
|
|
120
|
+
"description_fr": "Au niveau de la ville ou de la région, déduite ou déclarée, pour adapter les réponses.",
|
|
121
|
+
"default": false,
|
|
122
|
+
"legal_basis": "consent",
|
|
123
|
+
"gdpr": "Art. 6(1)(a)",
|
|
124
|
+
"rationale": "No provider lets a request refuse it. The endpoint-by-endpoint check of 2026-07-26 found no location parameter a request could refuse at any of the five providers with a descriptor here. The pinned OpenAI client does carry one an application declares — `user_location`, in `web_search_options` on chat completions and on the `web_search` tool of Responses — and a refusal of `location` alone reaches nothing there: a user who refuses `agentic_tools` loses the web search, and the declared location with it. So there is no parameter for an SDK to force or route. It stays because it remains a real question at the APPLICATION layer — an app that collects a user's city and puts it in the prompt, or in a web-search tool, is doing exactly what this asks about — but the honest reading is that only the developer's own code can honour it, and every descriptor says `unsupported` for that reason rather than for want of looking."
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "human_review",
|
|
128
|
+
"group": "collection",
|
|
129
|
+
"label_en": "Allow humans to review my conversations",
|
|
130
|
+
"label_fr": "Autoriser la relecture humaine de mes conversations",
|
|
131
|
+
"description_en": "Provider staff or contractors may read your exchanges, typically for quality or safety.",
|
|
132
|
+
"description_fr": "Des employés ou prestataires du fournisseur peuvent lire vos échanges, généralement pour la qualité ou la sécurité.",
|
|
133
|
+
"default": false,
|
|
134
|
+
"legal_basis": "consent",
|
|
135
|
+
"gdpr": "Art. 6(1)(a); Art. 9 for special categories",
|
|
136
|
+
"rationale": "Consistently the control users are most surprised to learn exists."
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"groups": [
|
|
140
|
+
{
|
|
141
|
+
"name": "training",
|
|
142
|
+
"label_en": "How my data trains AI",
|
|
143
|
+
"label_fr": "Comment mes données entraînent l'IA"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "profiling",
|
|
147
|
+
"label_en": "What is remembered about me",
|
|
148
|
+
"label_fr": "Ce qui est mémorisé sur moi"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"name": "agentic",
|
|
152
|
+
"label_en": "What connected apps and agents may do",
|
|
153
|
+
"label_fr": "Ce que les applications et agents connectés peuvent faire"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "retention",
|
|
157
|
+
"label_en": "How long it is kept",
|
|
158
|
+
"label_fr": "Combien de temps c'est conservé"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"name": "collection",
|
|
162
|
+
"label_en": "What is collected about me",
|
|
163
|
+
"label_fr": "Ce qui est collecté sur moi"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"name": "location",
|
|
167
|
+
"label_en": "Where my data is processed",
|
|
168
|
+
"label_fr": "Où mes données sont traitées"
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
"enforcement_levels": {
|
|
172
|
+
"enforced": "A real request parameter the SDK sets. The provider's API accepts it and it changes behaviour. The only level that is technically guaranteed.",
|
|
173
|
+
"contractual": "No request parameter exists; the provider's written terms govern it. Which way they decide is the entry's `protective`: `true` where they rule the processing out for every call, `false` where they allow it, impose it or leave it to the developer's organization — and only the first is a guarantee to the user. The ACT token is the proof that the choice was collected and transmitted, not the mechanism that applies it.",
|
|
174
|
+
"account": "Settable, but only in the provider's account or console — outside the API, so the SDK cannot apply it per request. Must be surfaced honestly to the developer. Marked `routable` when the developer can provision one credential per posture and let the SDK choose between them per request — which turns it back into a genuine per-user control.",
|
|
175
|
+
"absent": "The provider does not perform this processing on the API this descriptor wraps, so there is nothing to refuse: the answer changes nothing because nothing would have happened. Said only where the descriptor's note states why the wrapped API performs no such processing, and `unsupported` otherwise. What an application builds on its side with the same data is its own processing, which no descriptor describes.",
|
|
176
|
+
"unsupported": "This descriptor knows no way to honour this choice on the endpoints it wraps — no request parameter, written commitment or account setting — and nothing establishes that the processing is absent. Saying so plainly is worth more to a DPO than pretending otherwise."
|
|
177
|
+
},
|
|
178
|
+
"legacy_mapping": {
|
|
179
|
+
"training": {
|
|
180
|
+
"maps_to": [
|
|
181
|
+
"training.foundation",
|
|
182
|
+
"training.personalisation",
|
|
183
|
+
"training.analytics"
|
|
184
|
+
],
|
|
185
|
+
"note": "The original single boolean. `training: false` migrates to false on all three — the most protective reading, never a widening of what was consented to."
|
|
186
|
+
},
|
|
187
|
+
"connectors": {
|
|
188
|
+
"maps_to": [
|
|
189
|
+
"agentic_tools"
|
|
190
|
+
],
|
|
191
|
+
"note": "Renamed and widened: the risk is not the connector, it is any tool that persists data outside the conversation."
|
|
192
|
+
},
|
|
193
|
+
"retention.extended": {
|
|
194
|
+
"maps_to": [
|
|
195
|
+
"retention.conversation",
|
|
196
|
+
"retention.cache"
|
|
197
|
+
],
|
|
198
|
+
"note": "Split, because the levers differ per provider and a user may accept one and refuse the other. Migrates to refusal on both."
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"hard_truths": {
|
|
202
|
+
"no_absolute_zero": "Content flagged by trust & safety survives every zero-retention arrangement (up to 2 years at one provider; CSAM detections and abuse logs elsewhere). No option here may be worded as an absolute.",
|
|
203
|
+
"tools_reintroduce_retention": "Agentic tools are excluded from zero-retention arrangements at every provider that offers one, and the APIs do not block them — using one is the choice. This is why `agentic_tools` is a per-request option and not an account setting.",
|
|
204
|
+
"contracts_are_the_real_guardrail": "A DPA plus a retention amendment is what binds a provider. Request parameters only stop the application from contradicting the contract it signed. Say this plainly rather than implying the parameters are the protection."
|
|
205
|
+
},
|
|
206
|
+
"credential_routing": "An account-level setting applies to every end user of an app at once, so it cannot express one person's choice. Unless the developer provisions SEVERAL credentials with different privacy postures: the SDK then selects the credential matching each user's answer, and the account-level control becomes a per-user one. This is the only way training opt-out can ever be honoured per user, because no provider exposes it as a request parameter."
|
|
207
|
+
}
|