sensoit 1.2.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.
sensoit-1.2.0/PKG-INFO ADDED
@@ -0,0 +1,510 @@
1
+ Metadata-Version: 2.1
2
+ Name: sensoit
3
+ Version: 1.2.0
4
+ Summary: Official Python SDK for Sensoit - AI Prompt Security & Management
5
+ Home-page: https://github.com/sensoit/sensoit-sdk
6
+ Author: Sensoit
7
+ Author-email: support@sensoit.io
8
+ Project-URL: Bug Tracker, https://github.com/sensoit/sensoit-sdk/issues
9
+ Project-URL: Documentation, https://docs.sensoit.io
10
+ Project-URL: Homepage, https://sensoit.io
11
+ Project-URL: Source, https://github.com/sensoit/sensoit-sdk/tree/main/python-sdk
12
+ Keywords: sensoit,ai,llm,prompt,security,guardrails,openai,anthropic,gpt,claude,sdk,pii,toxicity,moderation
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Classifier: Typing :: Typed
25
+ Classifier: Framework :: AsyncIO
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown
28
+ Requires-Dist: httpx>=0.26.0
29
+ Requires-Dist: pydantic>=2.0.0
30
+ Requires-Dist: tenacity>=8.0.0
31
+ Provides-Extra: fastapi
32
+ Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
33
+ Requires-Dist: starlette>=0.27.0; extra == "fastapi"
34
+ Provides-Extra: flask
35
+ Requires-Dist: flask>=2.0.0; extra == "flask"
36
+ Provides-Extra: django
37
+ Requires-Dist: django>=4.0.0; extra == "django"
38
+ Provides-Extra: all
39
+ Requires-Dist: fastapi>=0.100.0; extra == "all"
40
+ Requires-Dist: starlette>=0.27.0; extra == "all"
41
+ Requires-Dist: flask>=2.0.0; extra == "all"
42
+ Requires-Dist: django>=4.0.0; extra == "all"
43
+ Provides-Extra: dev
44
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
45
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
46
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
47
+ Requires-Dist: black>=23.0.0; extra == "dev"
48
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
49
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
50
+ Requires-Dist: respx>=0.20.0; extra == "dev"
51
+
52
+ # Sensoit Python SDK
53
+
54
+ Official Python SDK for [Sensoit](https://sensoit.io) - AI Prompt Security & Management Platform.
55
+
56
+ Sensoit provides enterprise-grade guardrails for AI applications, including PII detection, toxicity filtering, keyword blocking, and custom policy enforcement.
57
+
58
+ ## Installation
59
+
60
+ ```bash
61
+ pip install sensoit-sdk
62
+
63
+ # With framework integrations
64
+ pip install sensoit-sdk[fastapi]
65
+ pip install sensoit-sdk[flask]
66
+ pip install sensoit-sdk[django]
67
+ pip install sensoit-sdk[all] # All frameworks
68
+ ```
69
+
70
+ ## Quick Start
71
+
72
+ ### Async Usage
73
+
74
+ ```python
75
+ from sensoit import Sensoit
76
+
77
+ async def main():
78
+ async with Sensoit(api_key="gf_live_xxx") as gf:
79
+ result = await gf.run(
80
+ "customer-support",
81
+ input="I need help with my order #12345",
82
+ variables={"customer_name": "John"},
83
+ session_id="session_abc",
84
+ )
85
+
86
+ if result.blocked:
87
+ print(f"Blocked: {result.violations}")
88
+ else:
89
+ print(f"Response: {result.output}")
90
+
91
+ import asyncio
92
+ asyncio.run(main())
93
+ ```
94
+
95
+ ### Sync Usage
96
+
97
+ ```python
98
+ from sensoit import Sensoit
99
+
100
+ gf = Sensoit(api_key="gf_live_xxx")
101
+
102
+ result = gf.run_sync(
103
+ "customer-support",
104
+ input="I need help with my order #12345",
105
+ variables={"customer_name": "John"},
106
+ )
107
+
108
+ print(f"Response: {result.output}")
109
+ print(f"Tokens: {result.tokens_used}")
110
+ print(f"Cost: ${result.cost_usd}")
111
+ ```
112
+
113
+ ## Configuration
114
+
115
+ ```python
116
+ gf = Sensoit(
117
+ # Required: Your API key
118
+ api_key="gf_live_xxx",
119
+
120
+ # Optional: API base URL (default: https://api.sensoit.io)
121
+ base_url="https://api.sensoit.io",
122
+
123
+ # Optional: Request timeout in seconds (default: 30.0)
124
+ timeout=30.0,
125
+
126
+ # Optional: Max retry attempts (default: 3)
127
+ max_retries=3,
128
+
129
+ # Optional: Enable debug logging (default: False)
130
+ debug=True,
131
+
132
+ # Optional: Enable caching (default: True)
133
+ cache_enabled=True,
134
+
135
+ # Optional: Cache TTL in seconds (default: 60)
136
+ cache_ttl_seconds=60,
137
+ )
138
+ ```
139
+
140
+ | Option | Type | Default | Description |
141
+ |--------|------|---------|-------------|
142
+ | `api_key` | `str` | *required* | Your Sensoit API key |
143
+ | `base_url` | `str` | `https://api.sensoit.io` | API base URL |
144
+ | `timeout` | `float` | `30.0` | Request timeout in seconds |
145
+ | `max_retries` | `int` | `3` | Max retry attempts |
146
+ | `debug` | `bool` | `False` | Enable debug logging |
147
+ | `cache_enabled` | `bool` | `True` | Enable prompt caching |
148
+ | `cache_ttl_seconds` | `int` | `60` | Cache TTL in seconds |
149
+
150
+ ## Core Methods
151
+
152
+ ### `run()` / `run_sync()`
153
+
154
+ Run a prompt through Sensoit with guardrail protection.
155
+
156
+ ```python
157
+ # Async
158
+ result = await gf.run(
159
+ "support-assistant",
160
+ input="I need help with my order",
161
+ variables={"customer_name": "John"},
162
+ session_id="session_123",
163
+ force_refresh=False,
164
+ dry_run=False,
165
+ timeout=60.0,
166
+ metadata={"source": "web"},
167
+ )
168
+
169
+ # Sync
170
+ result = gf.run_sync("support-assistant", input="Hello")
171
+ ```
172
+
173
+ **Return Type: `RunResult`**
174
+
175
+ ```python
176
+ @dataclass
177
+ class RunResult:
178
+ output: str # Generated response
179
+ allowed: bool # Request passed guardrails
180
+ blocked: bool # Request was blocked
181
+ escalated: bool # Request needs human review
182
+ violations: list # Policy violations
183
+ tokens_used: int # Total tokens (in + out)
184
+ tokens_in: int # Input tokens
185
+ tokens_out: int # Output tokens
186
+ latency_ms: int # Total latency
187
+ cost_usd: float # Estimated cost
188
+ prompt_version: int # Prompt version used
189
+ model: str # LLM model used
190
+ provider: str # LLM provider
191
+ cached: bool # Served from cache
192
+ request_id: str # Unique request ID
193
+ ```
194
+
195
+ ### `run_batch()` / `run_batch_sync()`
196
+
197
+ Run multiple prompts in parallel.
198
+
199
+ ```python
200
+ # Async
201
+ batch_result = await gf.run_batch([
202
+ {"prompt": "translator", "options": {"input": "Hello", "variables": {"lang": "es"}}},
203
+ {"prompt": "translator", "options": {"input": "Goodbye", "variables": {"lang": "fr"}}},
204
+ ])
205
+
206
+ # Sync
207
+ batch_result = gf.run_batch_sync([...])
208
+
209
+ print(f"Total: {batch_result.summary.total}")
210
+ print(f"Allowed: {batch_result.summary.allowed}")
211
+ print(f"Blocked: {batch_result.summary.blocked}")
212
+
213
+ for result in batch_result.results:
214
+ print(f"{result.prompt}: {result.output}")
215
+ ```
216
+
217
+ ### `invalidate_cache()`
218
+
219
+ Clear cached prompt data.
220
+
221
+ ```python
222
+ # Clear cache for specific prompt
223
+ gf.invalidate_cache("support-assistant")
224
+
225
+ # Clear all cache
226
+ gf.invalidate_cache()
227
+ ```
228
+
229
+ ## FastAPI Integration
230
+
231
+ ```python
232
+ from fastapi import FastAPI, Request
233
+ from sensoit import Sensoit
234
+ from sensoit.middleware import SensoitMiddleware
235
+
236
+ app = FastAPI()
237
+ gf = Sensoit(api_key="gf_live_xxx")
238
+
239
+ # Add middleware to protect routes
240
+ app.add_middleware(
241
+ SensoitMiddleware,
242
+ client=gf,
243
+ prompt="support-bot",
244
+ protected_paths=["/api/chat", "/api/ask"],
245
+ blocked_response={"error": "Cannot help with that request"},
246
+ blocked_status_code=200,
247
+ )
248
+
249
+ @app.post("/api/chat")
250
+ async def chat(request: Request):
251
+ # Access Sensoit result
252
+ result = request.scope.get("sensoit")
253
+ return {"reply": result.output if result else "No response"}
254
+ ```
255
+
256
+ ## Flask Integration
257
+
258
+ ```python
259
+ from flask import Flask, request, jsonify
260
+ from sensoit import Sensoit
261
+ from sensoit.middleware import flask_protect
262
+
263
+ app = Flask(__name__)
264
+ gf = Sensoit(api_key="gf_live_xxx")
265
+
266
+ @app.route("/chat", methods=["POST"])
267
+ @flask_protect(gf, prompt="support-bot")
268
+ def chat():
269
+ # Access Sensoit result
270
+ result = request.sensoit
271
+ return jsonify({"reply": result.output})
272
+
273
+ # With custom options
274
+ @app.route("/ask", methods=["POST"])
275
+ @flask_protect(
276
+ gf,
277
+ prompt="qa-bot",
278
+ extract_input=lambda: request.json.get("question"),
279
+ extract_variables=lambda: {"user_id": request.json.get("user_id")},
280
+ blocked_response={"error": "Question not allowed"},
281
+ )
282
+ def ask():
283
+ return jsonify({"answer": request.sensoit.output})
284
+ ```
285
+
286
+ ## Django Integration
287
+
288
+ ```python
289
+ # settings.py
290
+ MIDDLEWARE = [
291
+ # ... other middleware
292
+ 'sensoit.middleware.DjangoSensoitMiddleware',
293
+ ]
294
+
295
+ SENSOIT = {
296
+ 'API_KEY': 'gf_live_xxx',
297
+ 'PROMPT': 'support-bot',
298
+ 'PROTECTED_PATHS': ['/api/chat/', '/api/ask/'],
299
+ 'BASE_URL': 'https://api.sensoit.io', # Optional
300
+ }
301
+
302
+ # views.py
303
+ from django.http import JsonResponse
304
+
305
+ def chat(request):
306
+ # Access Sensoit result
307
+ result = getattr(request, "sensoit", None)
308
+ if result:
309
+ return JsonResponse({"reply": result.output})
310
+ return JsonResponse({"error": "No response"})
311
+ ```
312
+
313
+ ## Error Handling
314
+
315
+ ```python
316
+ from sensoit import (
317
+ Sensoit,
318
+ BlockedError,
319
+ EscalatedError,
320
+ RateLimitError,
321
+ AuthError,
322
+ TimeoutError,
323
+ ValidationError,
324
+ is_retryable_error,
325
+ )
326
+
327
+ gf = Sensoit(api_key="gf_live_xxx")
328
+
329
+ try:
330
+ result = await gf.run("my-prompt", input="Hello")
331
+ print(result.output)
332
+
333
+ except BlockedError as e:
334
+ print(f"Blocked by: {[v['policyName'] for v in e.violations]}")
335
+ print(f"Has critical: {e.has_critical_violation()}")
336
+
337
+ except EscalatedError as e:
338
+ print(f"Escalated: {e.reason}")
339
+
340
+ except RateLimitError as e:
341
+ print(f"Rate limited, retry after: {e.retry_after_seconds}s")
342
+
343
+ except AuthError as e:
344
+ print(f"Auth error: {e.message}")
345
+
346
+ except TimeoutError as e:
347
+ print(f"Timed out after: {e.timeout_seconds}s")
348
+
349
+ except ValidationError as e:
350
+ print(f"Validation errors: {e.field_errors}")
351
+ ```
352
+
353
+ ### Error Classes
354
+
355
+ | Error | Code | Status | Description |
356
+ |-------|------|--------|-------------|
357
+ | `BlockedError` | `BLOCKED` | 200 | Response blocked by guardrails |
358
+ | `EscalatedError` | `ESCALATED` | 200 | Response needs human review |
359
+ | `RateLimitError` | `RATE_LIMIT` | 429 | Rate limit exceeded |
360
+ | `AuthError` | `AUTH_ERROR` | 401 | Invalid API key |
361
+ | `TimeoutError` | `TIMEOUT` | - | Request timed out |
362
+ | `ValidationError` | `VALIDATION_ERROR` | 400 | Invalid input |
363
+ | `PromptNotFoundError` | `PROMPT_NOT_FOUND` | 404 | Prompt doesn't exist |
364
+ | `NetworkError` | `NETWORK_ERROR` | - | Network failure |
365
+ | `SensoitAPIError` | `API_ERROR` | varies | General API error |
366
+
367
+ ## Event Callbacks
368
+
369
+ ```python
370
+ gf = Sensoit(api_key="gf_live_xxx")
371
+
372
+ # Register event handlers
373
+ @gf.on_blocked
374
+ def handle_blocked(prompt_name, violations, request_id):
375
+ print(f"Blocked: {prompt_name}")
376
+ for v in violations:
377
+ print(f" - {v['policyName']}: {v['detail']}")
378
+
379
+ @gf.on_escalated
380
+ def handle_escalated(prompt_name, request_id, reason):
381
+ print(f"Escalated: {prompt_name} - {reason}")
382
+
383
+ @gf.on_error
384
+ def handle_error(prompt_name, error, request_id):
385
+ print(f"Error in {prompt_name}: {error}")
386
+
387
+ @gf.on_complete
388
+ def handle_complete(prompt_name, result):
389
+ print(f"Completed: {prompt_name}")
390
+ print(f" Tokens: {result.tokens_used}")
391
+ print(f" Cost: ${result.cost_usd}")
392
+
393
+ @gf.on_retry
394
+ def handle_retry(prompt_name, attempt, max_attempts):
395
+ print(f"Retry {attempt}/{max_attempts} for {prompt_name}")
396
+ ```
397
+
398
+ ## Type Hints
399
+
400
+ All types are exported for type checking:
401
+
402
+ ```python
403
+ from sensoit import (
404
+ # Configuration
405
+ SensoitConfig,
406
+ CacheConfig,
407
+
408
+ # Run operations
409
+ RunOptions,
410
+ RunResult,
411
+
412
+ # Batch operations
413
+ BatchRunInput,
414
+ BatchRunResult,
415
+ BatchRunItemResult,
416
+ BatchRunSummary,
417
+
418
+ # Guardrails
419
+ GuardrailViolation,
420
+ GuardrailViolationType,
421
+ ViolationSeverity,
422
+ ViolationAction,
423
+ )
424
+ ```
425
+
426
+ ## Caching
427
+
428
+ The SDK includes built-in caching for prompt responses:
429
+
430
+ ```python
431
+ gf = Sensoit(
432
+ api_key="gf_live_xxx",
433
+ cache_enabled=True,
434
+ cache_ttl_seconds=60,
435
+ )
436
+
437
+ # First call - makes API request
438
+ result1 = await gf.run("prompt", input="Hello")
439
+ print(result1.cached) # False
440
+
441
+ # Second call - served from cache
442
+ result2 = await gf.run("prompt", input="Hello")
443
+ print(result2.cached) # True
444
+
445
+ # Force fresh request
446
+ result3 = await gf.run("prompt", input="Hello", force_refresh=True)
447
+ print(result3.cached) # False
448
+
449
+ # Invalidate cache
450
+ gf.invalidate_cache("prompt")
451
+ ```
452
+
453
+ ## Health Check
454
+
455
+ ```python
456
+ # Async
457
+ health = await gf.health()
458
+
459
+ # Sync
460
+ health = gf.health_sync()
461
+
462
+ print(health)
463
+ # {'status': 'ok', 'service': 'sensoit', 'timestamp': '...'}
464
+ ```
465
+
466
+ ## Context Manager
467
+
468
+ ```python
469
+ # Async
470
+ async with Sensoit(api_key="gf_live_xxx") as gf:
471
+ result = await gf.run("prompt", input="Hello")
472
+
473
+ # Sync
474
+ with Sensoit(api_key="gf_live_xxx") as gf:
475
+ result = gf.run_sync("prompt", input="Hello")
476
+ ```
477
+
478
+ ## Environment Variables
479
+
480
+ ```bash
481
+ export SENSOIT_API_KEY=gf_live_xxx
482
+ export SENSOIT_BASE_URL=https://api.sensoit.io
483
+ ```
484
+
485
+ ```python
486
+ import os
487
+ from sensoit import Sensoit
488
+
489
+ gf = Sensoit(
490
+ api_key=os.environ["SENSOIT_API_KEY"],
491
+ base_url=os.environ.get("SENSOIT_BASE_URL", "https://api.sensoit.io"),
492
+ )
493
+ ```
494
+
495
+ ## Requirements
496
+
497
+ - Python 3.9 or higher
498
+ - httpx >= 0.26.0
499
+ - pydantic >= 2.0.0
500
+ - tenacity >= 8.0.0
501
+
502
+ ## License
503
+
504
+ MIT
505
+
506
+ ## Support
507
+
508
+ - Documentation: [https://docs.sensoit.io](https://docs.sensoit.io)
509
+ - Issues: [https://github.com/sensoit/sensoit-sdk/issues](https://github.com/sensoit/sensoit-sdk/issues)
510
+ - Email: support@sensoit.io