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.
- mankinds_sdk-1.0.0/PKG-INFO +413 -0
- mankinds_sdk-1.0.0/README.md +385 -0
- mankinds_sdk-1.0.0/mankinds_sdk/__init__.py +40 -0
- mankinds_sdk-1.0.0/mankinds_sdk/_http_client.py +151 -0
- mankinds_sdk-1.0.0/mankinds_sdk/client.py +400 -0
- mankinds_sdk-1.0.0/mankinds_sdk/connectors/__init__.py +32 -0
- mankinds_sdk-1.0.0/mankinds_sdk/connectors/base.py +112 -0
- mankinds_sdk-1.0.0/mankinds_sdk/connectors/databases/__init__.py +13 -0
- mankinds_sdk-1.0.0/mankinds_sdk/connectors/databases/postgresql.py +59 -0
- mankinds_sdk-1.0.0/mankinds_sdk/connectors/databases/sqlite.py +69 -0
- mankinds_sdk-1.0.0/mankinds_sdk/connectors/logs/__init__.py +13 -0
- mankinds_sdk-1.0.0/mankinds_sdk/connectors/logs/datadog.py +68 -0
- mankinds_sdk-1.0.0/mankinds_sdk/connectors/logs/file.py +56 -0
- mankinds_sdk-1.0.0/mankinds_sdk/exceptions.py +102 -0
- mankinds_sdk-1.0.0/mankinds_sdk.egg-info/PKG-INFO +413 -0
- mankinds_sdk-1.0.0/mankinds_sdk.egg-info/SOURCES.txt +19 -0
- mankinds_sdk-1.0.0/mankinds_sdk.egg-info/dependency_links.txt +1 -0
- mankinds_sdk-1.0.0/mankinds_sdk.egg-info/requires.txt +9 -0
- mankinds_sdk-1.0.0/mankinds_sdk.egg-info/top_level.txt +1 -0
- mankinds_sdk-1.0.0/pyproject.toml +59 -0
- mankinds_sdk-1.0.0/setup.cfg +4 -0
|
@@ -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
|
+
[](https://pypi.org/project/mankinds-sdk/)
|
|
38
|
+
[](https://gitlab.com/mankinds1/mankinds-sdk/-/pipelines)
|
|
39
|
+
[](../../LICENSE)
|
|
40
|
+
[](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>
|