Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
github: Tuteliq
custom:
- "https://tuteliq.ai"
Binary file added .github/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cff-version: 1.2.0
title: Tuteliq Python SDK
message: "If you use this software, please cite it using this metadata."
type: software
authors:
- name: Tuteliq
website: https://tuteliq.ai
repository-code: https://github.com/Tuteliq/python
url: https://tuteliq.ai
abstract: >-
Official Python SDK for Tuteliq — AI-powered child safety API for detecting bullying, grooming, and unsafe content
keywords:
- child safety
- Python
- content moderation
- KOSA
- SDK
- PyPI
- online safety
- grooming detection
- async
- age verification
- identity verification
license: MIT
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ result = await client.detect_grooming(

if result.grooming_risk == GroomingRisk.HIGH:
print(f"Flags: {result.flags}") # ["secrecy", "isolation"]

# Per-message breakdown (optional, returned on conversation-aware endpoints)
if result.message_analysis:
for m in result.message_analysis:
print(f"Message {m.message_index}: risk={m.risk_score}, flags={m.flags}, summary={m.summary}")
```

### Unsafe Content Detection
Expand Down Expand Up @@ -188,6 +193,41 @@ print(f"Risk: {report.risk_level}")
print(f"Next Steps: {report.recommended_next_steps}")
```

### Age Verification (Beta)

> **Pro tier ($99/mo)+ required** · 5 credits per request · `POST /v1/verification/age`

```python
result = await client.verify_age(
document=open("id-front.jpg", "rb"),
selfie=open("selfie.jpg", "rb"),
method="combined", # "document" | "biometric" | "combined"
)

print(result.verified) # True
print(result.estimated_age) # 15
print(result.age_range) # "13-15"
print(result.is_minor) # True
print(result.confidence) # 0.97
```

### Identity Verification (Beta)

> **Business tier ($349/mo)+ required** · 10 credits per request · `POST /v1/verification/identity`

```python
result = await client.verify_identity(
document=open("id-front.jpg", "rb"),
selfie=open("selfie.jpg", "rb"),
)

print(result.verified) # True
print(result.match_score) # 0.98
print(result.liveness_passed) # True
print(result.document_authenticated) # True
print(result.is_minor) # False
```

### Voice Streaming

Real-time voice streaming with live safety analysis over WebSocket. Requires `websockets`:
Expand Down Expand Up @@ -243,6 +283,8 @@ print(f"Credits used: {result.credits_used}") # 1
| `generate_report()` | 3 | Structured output |
| `analyze_voice()` | 5 | Transcription + analysis |
| `analyze_image()` | 3 | Vision + OCR + analysis |
| `verify_age()` | 5 | Age verification (Beta, Pro+) |
| `verify_identity()` | 10 | Identity verification (Beta, Business+) |

---

Expand Down
36 changes: 36 additions & 0 deletions llms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Tuteliq Python SDK

> Official Python SDK for Tuteliq — AI-powered child safety API for detecting bullying, grooming, and unsafe content.

## What is this?
A Python library for the Tuteliq child safety API. Detect bullying, grooming, self-harm, substance promotion, sexual exploitation, and other harmful content in text, conversations, audio, images, and video. Supports both synchronous and async clients.

## Install
pip install tuteliq

## Quick Start
import os
from tuteliq import Tuteliq
client = Tuteliq(api_key=os.environ["TUTELIQ_API_KEY"])
result = client.detect_unsafe(text="content to check", age_group="13-15")

## Async
from tuteliq import AsyncTuteliq
client = AsyncTuteliq(api_key=os.environ["TUTELIQ_API_KEY"])
result = await client.detect_unsafe(text="content to check", age_group="13-15")

## Key Features
- Sync and async clients
- All 20+ Tuteliq API endpoints
- 16+ detection categories (KOSA compliant)
- Age group calibration (under-10, 10-12, 13-15, 14-17)
- Age verification (Beta) — POST /v1/verification/age (Pro tier+, 5 credits)
- Identity verification (Beta) — POST /v1/verification/identity (Business tier+, 10 credits)
- Automatic retries and error handling
- Python 3.9+

## Links
- Website: https://tuteliq.ai
- Documentation: https://docs.tuteliq.ai
- PyPI: https://pypi.org/project/tuteliq/
- GitHub: https://github.com/Tuteliq/python
30 changes: 30 additions & 0 deletions tuteliq/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ class GroomingResult:
rationale: str
risk_score: float
recommended_action: str
message_analysis: Optional[list[MessageAnalysis]] = None
language: Optional[str] = None
language_status: Optional[str] = None
credits_used: Optional[int] = None
Expand All @@ -243,13 +244,17 @@ class GroomingResult:
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "GroomingResult":
"""Create from API response dictionary."""
msg_analysis = None
if "message_analysis" in data and data["message_analysis"]:
msg_analysis = [MessageAnalysis.from_dict(m) for m in data["message_analysis"]]
return cls(
grooming_risk=GroomingRisk(data["grooming_risk"]),
confidence=data["confidence"],
flags=data["flags"],
rationale=data["rationale"],
risk_score=data["risk_score"],
recommended_action=data["recommended_action"],
message_analysis=msg_analysis,
language=data.get("language"),
language_status=data.get("language_status"),
credits_used=data.get("credits_used"),
Expand Down Expand Up @@ -1199,6 +1204,26 @@ class DetectionInput:
metadata: Optional[dict[str, Any]] = None


@dataclass
class MessageAnalysis:
"""Per-message analysis from conversation-aware detection."""

message_index: int
risk_score: float
flags: list[str]
summary: str

@classmethod
def from_dict(cls, data: dict[str, Any]) -> "MessageAnalysis":
"""Create from API response dictionary."""
return cls(
message_index=data["message_index"],
risk_score=data["risk_score"],
flags=data["flags"],
summary=data["summary"],
)


@dataclass
class DetectionCategory:
"""A detected category with tag and confidence."""
Expand Down Expand Up @@ -1243,6 +1268,7 @@ class DetectionResult:
language_status: str
evidence: Optional[list[DetectionEvidence]] = None
age_calibration: Optional[AgeCalibration] = None
message_analysis: Optional[list[MessageAnalysis]] = None
credits_used: Optional[int] = None
processing_time_ms: Optional[float] = None
external_id: Optional[str] = None
Expand Down Expand Up @@ -1270,6 +1296,9 @@ def from_dict(cls, data: dict[str, Any]) -> "DetectionResult":
age_group=ac.get("age_group"),
multiplier=ac.get("multiplier"),
)
msg_analysis = None
if "message_analysis" in data and data["message_analysis"]:
msg_analysis = [MessageAnalysis.from_dict(m) for m in data["message_analysis"]]
return cls(
endpoint=data["endpoint"],
detected=data["detected"],
Expand All @@ -1284,6 +1313,7 @@ def from_dict(cls, data: dict[str, Any]) -> "DetectionResult":
language_status=data["language_status"],
evidence=evidence,
age_calibration=age_cal,
message_analysis=msg_analysis,
credits_used=data.get("credits_used"),
processing_time_ms=data.get("processing_time_ms"),
external_id=data.get("external_id"),
Expand Down