diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..a65ae2b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: Tuteliq +custom: + - "https://tuteliq.ai" diff --git a/.github/logo.png b/.github/logo.png new file mode 100644 index 0000000..34ac456 Binary files /dev/null and b/.github/logo.png differ diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..dd79120 --- /dev/null +++ b/CITATION.cff @@ -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 diff --git a/README.md b/README.md index d6e247f..c1b2efa 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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`: @@ -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+) | --- diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..8b91fe5 --- /dev/null +++ b/llms.txt @@ -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 diff --git a/tuteliq/models.py b/tuteliq/models.py index 39633ed..0c300a7 100644 --- a/tuteliq/models.py +++ b/tuteliq/models.py @@ -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 @@ -243,6 +244,9 @@ 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"], @@ -250,6 +254,7 @@ def from_dict(cls, data: dict[str, Any]) -> "GroomingResult": 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"), @@ -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.""" @@ -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 @@ -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"], @@ -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"),