mankinds-sdk 1.0.0__tar.gz

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.
@@ -0,0 +1,413 @@
1
+ Metadata-Version: 2.4
2
+ Name: mankinds-sdk
3
+ Version: 1.0.0
4
+ Summary: Python SDK for Mankinds API
5
+ Author-email: Mankinds <info@mankinds.com>
6
+ License: MIT
7
+ Keywords: mankinds,api,sdk
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: requests>=2.28.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0; extra == "dev"
23
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
24
+ Requires-Dist: black>=23.0; extra == "dev"
25
+ Requires-Dist: isort>=5.11; extra == "dev"
26
+ Requires-Dist: flake8>=5.0; extra == "dev"
27
+ Requires-Dist: mypy>=0.990; extra == "dev"
28
+
29
+ <div align="center">
30
+
31
+
32
+ <h1>
33
+ <img src="https://gitlab.com/mankinds1/mankinds-sdk/-/raw/main/assets/logo.png" alt="Mankinds" width="32" height="27" />
34
+ Mankinds SDK
35
+ </h1>
36
+
37
+ [![PyPI version](https://img.shields.io/pypi/v/mankinds-sdk?logo=pypi&logoColor=white)](https://pypi.org/project/mankinds-sdk/)
38
+ [![Build Status](https://img.shields.io/gitlab/pipeline/mankinds1/mankinds-sdk?branch=main&logo=gitlab&logoColor=white)](https://gitlab.com/mankinds1/mankinds-sdk/-/pipelines)
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?logo=opensourceinitiative&logoColor=white)](../../LICENSE)
40
+ [![Documentation Status](https://img.shields.io/badge/docs-latest-brightgreen.svg?logo=readthedocs&logoColor=white)](https://docs.mankinds.io)
41
+
42
+ *Evaluate AI system with automated tests.*
43
+
44
+ Register an AI system, optionally attach connectors (logs, databases), import or generate your golden dataset, and run automated evaluations covering privacy, security, performance, fairness, explainability, transparency and accountability.
45
+
46
+ </div>
47
+
48
+ ---
49
+
50
+ ## Features
51
+
52
+ - **System Management** — Create, update, and configure AI systems with custom API endpoints
53
+ - **Endpoint Configuration** — Support for REST, SSE streaming, and multi-turn conversations
54
+ - **Dataset Generation** — Auto-generate or provide custom test scenarios
55
+ - **Evaluation** — Run evaluations with real-time polling and configurable profiles
56
+ - **Connectors** — Attach data sources (log files, Datadog, SQLite, PostgreSQL)
57
+ - **Error Handling** — Typed exceptions for all error cases
58
+
59
+ ## Documentation
60
+
61
+ - [Mankinds Documentation](https://docs.mankinds.io)
62
+
63
+ ## Requirements
64
+
65
+ - Python ≥ 3.8
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ pip install mankinds-sdk
71
+ ```
72
+
73
+ ## Usage
74
+
75
+ The SDK follows a simple 3-step workflow: **create a system, generate test data, run an evaluation**.
76
+
77
+ ### Initialize the Client
78
+
79
+ ```python
80
+ from mankinds_sdk import MankindsClient
81
+
82
+ client = MankindsClient(api_key="mk_...")
83
+ ```
84
+
85
+ | Parameter | Type | Required | Default | Description |
86
+ |-----------|------|----------|---------|-------------|
87
+ | `api_key` | `str` | Yes | — | Your API key |
88
+ | `base_url` | `str` | No | `https://app.mankinds.io` | Custom API base URL |
89
+ | `timeout` | `int` | No | `120` | Request timeout in seconds |
90
+
91
+ ### Create an AI System
92
+
93
+ Register your AI system by providing its name, description, and API endpoint. The endpoint defines how your AI is called during evaluation.
94
+
95
+ ```python
96
+ system = client.create_system(
97
+ name="Customer Support Bot",
98
+ description="A chatbot that handles order inquiries and returns for an e-commerce platform.",
99
+ endpoint={
100
+ "url": "https://api.example.com/chat",
101
+ "method": "POST",
102
+ "headers": {"Authorization": "Bearer your-token"},
103
+ "body": {"message": "{{input}}"},
104
+ "response": {"answer": "{{output}}"}
105
+ }
106
+ )
107
+
108
+ system_id = system["id"]
109
+ ```
110
+
111
+ Use `{{input}}` in the request body and `{{output}}` in the response mapping so test inputs and expected outputs are bound during evaluation.
112
+
113
+ ### Endpoint Configuration
114
+
115
+ The **endpoint** defines how the API calls your AI system during evaluation. It's a JSON object that describes your API's request/response format.
116
+
117
+ | Field | Type | Required | Description |
118
+ |-------|------|----------|-------------|
119
+ | `url` | string | Yes | API endpoint URL |
120
+ | `method` | string | Yes | HTTP method (`POST`, `GET`, etc.) |
121
+ | `body` | object | Yes | Request body with `{{input}}` placeholder |
122
+ | `response` | object | Yes | Response mapping with `{{output}}` placeholder |
123
+ | `headers` | object | No | HTTP headers |
124
+ | `streaming` | object | No | SSE streaming configuration |
125
+ | `multiturn` | object | No | Multi-turn conversation configuration |
126
+
127
+ **Placeholders:**
128
+
129
+ - `{{input}}` in `body`: replaced with test inputs during evaluation
130
+ - `{{output}}` in `response`: indicates which field contains the AI response
131
+
132
+ ```python
133
+ "body": {"message": "{{input}}"},
134
+ "response": {"answer": "{{output}}"}
135
+ ```
136
+
137
+ **Streaming (SSE):**
138
+
139
+ ```python
140
+ endpoint = {
141
+ "url": "https://api.example.com/chat",
142
+ "method": "POST",
143
+ "body": {"message": "{{input}}"},
144
+ "response": {"answer": "{{output}}"},
145
+ "streaming": {
146
+ "enabled": True,
147
+ "format": "openai", # "openai" | "anthropic" | "custom"
148
+ "content_path": "choices[0].delta.content"
149
+ }
150
+ }
151
+ ```
152
+
153
+ **Multi-turn conversations:**
154
+
155
+ ```python
156
+ endpoint = {
157
+ "url": "https://api.example.com/chat",
158
+ "method": "POST",
159
+ "body": {"message": "{{input}}", "session_id": "{{session}}"},
160
+ "response": {"answer": "{{output}}"},
161
+ "multiturn": {
162
+ "type": "session_id", # "none" | "session_id" | "history"
163
+ "field": "conversation_id",
164
+ "location": "body"
165
+ }
166
+ }
167
+ ```
168
+
169
+ ### Generate Evaluation Dataset
170
+
171
+ Test scenarios can be auto-generated based on your system description, or you can provide custom scenarios.
172
+
173
+ **Auto-generate scenarios:**
174
+
175
+ ```python
176
+ dataset = client.generate_dataset(system_id, num_scenarios=20)
177
+ ```
178
+
179
+ **Provide custom scenarios:**
180
+
181
+ ```python
182
+ dataset = client.generate_dataset(
183
+ system_id,
184
+ scenarios=[
185
+ {"input": "Where is my order?", "outputs": ["I can help you track your order."]},
186
+ {"input": "I want a refund", "outputs": ["I'll process your refund request."]}
187
+ ]
188
+ )
189
+ ```
190
+
191
+ **Refine an existing dataset:**
192
+
193
+ ```python
194
+ dataset = client.update_dataset(
195
+ system_id,
196
+ orientation="Add more edge cases about payment failures"
197
+ )
198
+ ```
199
+
200
+ > **Note:** `generate_dataset` requires a validated system description. If validation fails, a `DescriptionNotValidatedError` is raised with recommendations.
201
+
202
+ ### Run Evaluation
203
+
204
+ Start an evaluation to test your AI system. By default, the call blocks until the evaluation completes.
205
+
206
+ **Block until complete (default):**
207
+
208
+ ```python
209
+ result = client.evaluate(system_id)
210
+ print(f"Score: {result['summary']}")
211
+ ```
212
+
213
+ **Start without waiting:**
214
+
215
+ ```python
216
+ run_info = client.evaluate(system_id, wait=False)
217
+ run_id = run_info["run_id"]
218
+
219
+ # Check status later
220
+ result = client.get_evaluation(run_id)
221
+ print(f"Status: {result['status']}")
222
+ ```
223
+
224
+ **With specific thematics:**
225
+
226
+ ```python
227
+ result = client.evaluate(
228
+ system_id,
229
+ thematics_config={
230
+ "explainability": {"justification": {"nb_tests": 5}},
231
+ "robustness": {"prompt_injection": {"nb_tests": 10}}
232
+ }
233
+ )
234
+ ```
235
+
236
+ **With evaluation profile:**
237
+
238
+ ```python
239
+ result = client.evaluate(system_id, profile="extended")
240
+ ```
241
+
242
+ **With progress callback:**
243
+
244
+ ```python
245
+ result = client.evaluate(
246
+ system_id,
247
+ poll_interval=10,
248
+ on_poll=lambda status, elapsed: print(f" {status} ({elapsed}s)")
249
+ )
250
+ ```
251
+
252
+ ### Connectors
253
+
254
+ **Connectors** attach external data sources (logs, databases) to your system for richer evaluation context.
255
+
256
+ **File logs:**
257
+
258
+ ```python
259
+ from mankinds_sdk.connectors import FileConnector
260
+
261
+ connector = FileConnector(file_path="/path/to/logs.json")
262
+ client.add_connector(system_id, connector)
263
+ ```
264
+
265
+ **Datadog logs:**
266
+
267
+ ```python
268
+ from mankinds_sdk.connectors import DatadogConnector
269
+
270
+ connector = DatadogConnector(
271
+ api_key="dd-api-key",
272
+ app_key="dd-app-key",
273
+ site="datadoghq.eu", # default
274
+ )
275
+ client.add_connector(system_id, connector)
276
+ ```
277
+
278
+ **SQLite database:**
279
+
280
+ ```python
281
+ from mankinds_sdk.connectors import SqliteConnector
282
+
283
+ connector = SqliteConnector(file_path="/path/to/database.db")
284
+ client.add_connector(system_id, connector)
285
+ ```
286
+
287
+ **PostgreSQL database:**
288
+
289
+ ```python
290
+ from mankinds_sdk.connectors import PostgresqlConnector
291
+
292
+ connector = PostgresqlConnector(
293
+ host="localhost",
294
+ database="mydb",
295
+ user="admin",
296
+ password="secret",
297
+ port=5432,
298
+ )
299
+ client.add_connector(system_id, connector)
300
+ ```
301
+
302
+ **Manage connectors:**
303
+
304
+ ```python
305
+ # List all connectors
306
+ connectors = client.get_connectors(system_id)
307
+
308
+ # Update a connector
309
+ connector = FileConnector(file_path="/path/to/new-logs.json")
310
+ client.update_connector(system_id, connector)
311
+
312
+ # Remove a connector
313
+ client.delete_connector(system_id, connector)
314
+ ```
315
+
316
+ > Only one connector per category (logs, database) is allowed per system. Adding a duplicate raises `ConnectorAlreadyExistsError`.
317
+
318
+ ## Complete Example
319
+
320
+ ```python
321
+ from mankinds_sdk import MankindsClient
322
+ from mankinds_sdk.connectors import FileConnector
323
+
324
+ client = MankindsClient(api_key="mk_...")
325
+
326
+ # Create system
327
+ system = client.create_system(
328
+ name="Support Bot",
329
+ description="A customer support chatbot for order tracking and returns.",
330
+ endpoint={
331
+ "url": "https://api.example.com/chat",
332
+ "method": "POST",
333
+ "body": {"message": "{{input}}"},
334
+ "response": {"answer": "{{output}}"}
335
+ }
336
+ )
337
+ system_id = system["id"]
338
+
339
+ # Attach production logs
340
+ connector = FileConnector(file_path="./logs/production.json")
341
+ client.add_connector(system_id, connector)
342
+
343
+ # Generate dataset and evaluate
344
+ dataset = client.generate_dataset(system_id, num_scenarios=15)
345
+ result = client.evaluate(system_id, profile="extended")
346
+
347
+ print(f"Status: {result['status']}")
348
+ print(f"Score: {result['summary']}")
349
+ ```
350
+
351
+ ## API Reference
352
+
353
+ ### MankindsClient
354
+
355
+ | Method | Description |
356
+ |--------|-------------|
357
+ | `get_system(system_id)` | Get system details and configuration |
358
+ | `create_system(name, description, endpoint)` | Create a new AI system |
359
+ | `update_system(system_id, name?, description?, endpoint?)` | Update an existing system |
360
+ | `generate_dataset(system_id, num_scenarios?, scenarios?)` | Generate and validate evaluation scenarios |
361
+ | `update_dataset(system_id, orientation?, scenarios?)` | Refine or replace dataset scenarios |
362
+ | `evaluate(system_id, ...)` | Run an evaluation |
363
+ | `get_evaluation(run_id)` | Get evaluation status and results |
364
+ | `add_connector(system_id, connector)` | Add a data source connector |
365
+ | `get_connectors(system_id)` | List all connectors for a system |
366
+ | `update_connector(system_id, connector)` | Update a connector |
367
+ | `delete_connector(system_id, connector)` | Remove a connector |
368
+
369
+ ### Exceptions
370
+
371
+ | Exception | When Raised |
372
+ |-----------|-------------|
373
+ | `CredentialsError` | Missing API key |
374
+ | `AuthenticationError` | Invalid or expired API key (401) |
375
+ | `NotFoundError` | Resource not found (404) |
376
+ | `ValidationError` | Request validation failed (422) |
377
+ | `RateLimitError` | Too many requests (429) |
378
+ | `ServerError` | Server error (5xx) |
379
+ | `InvalidEndpointError` | Endpoint missing required fields |
380
+ | `EndpointNotConfiguredError` | Evaluation without endpoint |
381
+ | `DescriptionNotValidatedError` | Dataset generation before validation |
382
+ | `ConnectorAlreadyExistsError` | Duplicate connector category |
383
+
384
+ ```python
385
+ from mankinds_sdk.exceptions import (
386
+ CredentialsError,
387
+ AuthenticationError,
388
+ InvalidEndpointError,
389
+ DescriptionNotValidatedError,
390
+ ConnectorAlreadyExistsError,
391
+ )
392
+
393
+ try:
394
+ result = client.evaluate(system_id)
395
+ except AuthenticationError:
396
+ print("Invalid API key")
397
+ except InvalidEndpointError as e:
398
+ print(f"Missing fields: {e.missing_fields}")
399
+ except DescriptionNotValidatedError as e:
400
+ print(f"Fix description: {e.recommendations}")
401
+ ```
402
+
403
+ ---
404
+
405
+ <div align="center">
406
+
407
+ ## License
408
+
409
+ [MIT](../../LICENSE)
410
+
411
+ © 2026 [Mankinds](https://mankinds.io). All rights reserved.
412
+
413
+ </div>