Metadata-Version: 2.4
Name: its-ai
Version: 0.2.0
Summary: Typed Python SDK client for ITS-AI API (sync + async).
Author-email: ITS AI <support@its-ai.org>
License-Expression: MIT
Project-URL: Homepage, https://api.its-ai.org
Project-URL: Repository, https://github.com/its-ai/its-ai-python-sdk
Keywords: its-ai,sdk,client,api,ml
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests<3,>=2.28
Requires-Dist: httpx<1,>=0.24
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-anyio>=0.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: anyio>=3.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Dynamic: license-file

# ITS-AI Python SDK

Typed, ergonomic Python client for the ITS-AI API with sync and async interfaces, robust error mapping, attempts, and helpful docs.

- Sync and async clients: `ItsAIClient` (requests) and `AsyncItsAIClient` (httpx)
- Strongly-typed results via dataclasses
- Attempts on 5xx and rate limits, with exponential backoff and `Retry-After` support
- Rich, typed error hierarchy mapped from API `type` and HTTP status
- Safe logging with masked API keys

## Installation
```bash
pip install its-ai
```
## Quick start

### Single text analysis

```python
from its_ai import ItsAIClient, AnalyzeTextResult

client = ItsAIClient(api_key="api_key")
try:
    result: AnalyzeTextResult = client.analyze_text("Your English text here.")
    print(result.answer)
finally:
    client.close()
```

With deep scan:

```python
with ItsAIClient(api_key="api_key") as client:
    result = client.analyze_text("Your English text here.", deep_scan=True)
    print(result.answer, result.segmentation_tokens)
```

### Batch analysis

```python
from its_ai import ItsAIClient, AnalyzeBatchItemResult

texts = [
    "Short text",  # might trigger LowWords
    "A sufficiently long English text ...",
]

with ItsAIClient(api_key="api_key") as client:
    items: list[AnalyzeBatchItemResult] = client.analyze_batch(texts, deep_scan=False)
    for item in items:
        print(item.text, item.answer)
```

Chunking large batches automatically:

```python
with ItsAIClient(api_key="api_key") as client:
    results = client.analyze_batch(texts, max_batch_size=50)
```

### Plagiarism

A plagiarism check costs twice the words of an AI scan of the same text and can take
minutes, so it uses its own generous timeout (`PLAGIARISM_TIMEOUT`) and is **not
retried** — a retry cannot resume the abandoned scan, it starts a second one that is
billed again. Pass `timeout` only to go *higher*.

```python
with ItsAIClient(api_key="api_key") as client:
    result = client.check_plagiarism("Your text here.")
    print(result.score)                 # 0.0 original – 1.0 fully copied
    for source in result.results:
        print(source.score, source.link, source.title)
        for match in source.matches:    # the fragments that matched this source
            print(match.match_score, match.text_sentence, match.link)
```

### Grammar & style

Unlike AI detection, the grammar endpoints are multilingual and accept short texts (from 20
characters). There is no score — the result is the list of issues.

```python
with ItsAIClient(api_key="api_key") as client:
    result = client.check_grammar("She go to school every day.")
    print(result.language, result.stats.errors)
    for match in result.matches:
        print(match.severity, match.message, match.replacements)

    # Several texts at once — a failing text carries `error` instead of matches
    for item in client.check_grammar_batch(["First text ...", "Second text ..."]):
        print(item.error or item.stats.total)
```

`report_id` opens the web report, but the PDF certificate covers AI and plagiarism only —
downloading it for a grammar-only scan returns 404.

### PDF certificate

Every AI and plagiarism scan returns a `report_id` you can exchange for the PDF certificate.
The endpoint takes no API key — the `report_id` is the secret — so the link is shareable.

```python
with ItsAIClient(api_key="api_key") as client:
    result = client.analyze_text_v2("Your text here ...")
    pdf = client.download_report(result.report_id, lang="fr", tz="Europe/Paris")
    open("certificate.pdf", "wb").write(pdf)
```

`lang` and `tz` are optional (English / UTC by default). An unknown `report_id`, or one from
a grammar-only check, raises `NotFound`.

### Async usage

```python
import asyncio
from its_ai import AsyncItsAIClient

async def main():
    async with AsyncItsAIClient(api_key="api_key") as client:
        res = await client.analyze_text("hello world", deep_scan=True)
        print(res)

asyncio.run(main())
```

## Errors and attempts

The client raises typed exceptions derived from `ItsAIError` based on the API error `type` and HTTP status. Common ones include:

- `ValidationError`, `AuthenticationFailed`, `PermissionDenied`, `NotFound`, `NotAcceptable`
- Domain errors: `LowWords`, `ManyWords`, `OnlyEnglish`, `RateLimitExceeded`, etc.
- Transport failures: `RequestTimeout`, `NetworkError`

API errors arrive as `<type>:<code>` (e.g. `validation:low_words`, `server:server`) and are
matched on both halves, so an unfamiliar code still lands on its category's class rather than
on the bare `ItsAIError`.

Idempotent POSTs are retried up to 3 times on 5xx and on a rate limit, with exponential backoff and `Retry-After` respected. A rate limit arrives as HTTP 400 with code `validation:rate_limit` (not 429) and is raised as `RateLimitExceeded`; every other 4xx is final. Plagiarism checks are never retried — see below.

```python
from its_ai import ItsAIClient, LowWords, ManyWords, OnlyEnglish, AuthenticationFailed

try:
    with ItsAIClient() as client:  # reads ITS_AI_API_KEY from env by default
        client.analyze_text("too short")
except LowWords as e:
    print("Text too short:", e.message)
except ManyWords:
    print("Text too long")
except OnlyEnglish:
    print("Only English is supported")
except AuthenticationFailed:
    print("Invalid/absent API key")
```

## Configuration

- `api_key`: string, required (defaults from `ITS_AI_API_KEY`)
- `base_url`: defaults to `https://api.its-ai.org` (trailing slashes trimmed)
- `timeout`: default 10s (override per-call via `timeout=`)
- `max_attempts`: default 3 (5xx and rate limits)
- `max_batch_size` (batch-only): optional chunking of input texts

Headers are set automatically: `User-Agent: its-ai-python-sdk/<version>`, `Accept: application/json`, `Content-Type: application/json`.

## Logging

The package uses Python's `logging` under the logger name `its_ai`. Enable DEBUG to see request URLs, status codes, and trimmed payloads. The `api_key` is masked.

```python
import logging
logging.basicConfig(level=logging.DEBUG)
```

## Environment

- `ITS_AI_API_KEY` – used by default if `api_key` is not passed.
- `ITS_AI_E2E=1` – enable smoke tests to hit the real API in CI (optional).

## Testing

Run unit tests:

```bash
python -m pytest -q
```

Run smoke (real API) tests when you have a valid key:

```bash
export ITS_AI_API_KEY="api_key"
export ITS_AI_E2E=1
python -m pytest -q
```

## API Reference (brief)

Every method has an `await`-able twin with the same signature on `AsyncItsAIClient`.

**AI detection**

- `analyze_text(text, deep_scan=False, *, timeout=None) -> AnalyzeTextResult` — v1
- `analyze_batch(texts, deep_scan=False, *, timeout=None, max_batch_size=None) -> list[AnalyzeBatchItemResult]` — v1
- `analyze_text_v2(text, *, timeout=None) -> AnalyzeTextV2Result` — richer result (`score`, `ai_percentage`, `probabilities`, `segments`), always a deep scan
- `analyze_batch_v2(texts, *, timeout=None, max_batch_size=None) -> list[AnalyzeBatchV2ItemResult]` — per-text `error` instead of one error for the whole batch

**Plagiarism**

- `check_plagiarism(text, *, timeout=None) -> PlagiarismResult`

**Grammar & style**

- `check_grammar(text, *, timeout=None) -> GrammarResult`
- `check_grammar_batch(texts, *, timeout=None, max_batch_size=None) -> list[GrammarBatchItemResult]`

**Reports**

- `download_report(report_id, *, lang=None, tz=None, timeout=None) -> bytes` — the PDF certificate

Note that API access is an **Enterprise-plan** feature, enforced per request: a key issued on
Enterprise stops working after a downgrade (`PermissionDenied`).

## License

MIT

For API details and the hosted endpoint see `https://api.its-ai.org`.
