mocra 0.1.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.
- mocra-0.1.0/PKG-INFO +134 -0
- mocra-0.1.0/README.md +108 -0
- mocra-0.1.0/mocra/__init__.py +35 -0
- mocra-0.1.0/mocra/client.py +150 -0
- mocra-0.1.0/mocra/models.py +66 -0
- mocra-0.1.0/mocra.egg-info/PKG-INFO +134 -0
- mocra-0.1.0/mocra.egg-info/SOURCES.txt +12 -0
- mocra-0.1.0/mocra.egg-info/dependency_links.txt +1 -0
- mocra-0.1.0/mocra.egg-info/requires.txt +8 -0
- mocra-0.1.0/mocra.egg-info/top_level.txt +1 -0
- mocra-0.1.0/pyproject.toml +68 -0
- mocra-0.1.0/setup.cfg +4 -0
- mocra-0.1.0/tests/test_client.py +94 -0
- mocra-0.1.0/tests/test_models.py +44 -0
mocra-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mocra
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Mocra's Observe API - observability for video generation workflows
|
|
5
|
+
Author: Mocra
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://docs.mocra.io
|
|
8
|
+
Project-URL: Homepage, https://mocra.io
|
|
9
|
+
Keywords: mocra,video,observability,quality,ai
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: httpx>=0.24.0
|
|
20
|
+
Requires-Dist: pydantic>=2.0.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: ruff>=0.8.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
26
|
+
|
|
27
|
+
# Mocra Python SDK
|
|
28
|
+
|
|
29
|
+
Python SDK for [Mocra's Observe API](https://docs.mocra.io/) — observability for video generation workflows. The API mirrors the [TypeScript SDK](https://github.com/Mocra-AI/Mocra) so you can switch between languages easily.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install -e .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- Python 3.10+
|
|
40
|
+
- [httpx](https://www.python-httpx.org/)
|
|
41
|
+
- [pydantic](https://docs.pydantic.dev/) v2
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from mocra import VideoObservabilityApi, ExtraCriterion
|
|
47
|
+
|
|
48
|
+
api = VideoObservabilityApi("YOUR_API_KEY")
|
|
49
|
+
result = api.score_video(
|
|
50
|
+
"https://example.org/video.mp4",
|
|
51
|
+
extra_criteria=[
|
|
52
|
+
ExtraCriterion(
|
|
53
|
+
criterion_name="Blur",
|
|
54
|
+
criterion_description="The video should not be blurry",
|
|
55
|
+
),
|
|
56
|
+
],
|
|
57
|
+
ignore_criteria=["UNNATURAL PHYSICS"],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
print(f"Overall score: {result.severity}")
|
|
61
|
+
for criterion in result.criteria:
|
|
62
|
+
print(f" {criterion.name}: {criterion.score}")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API Mapping (TypeScript ↔ Python)
|
|
66
|
+
|
|
67
|
+
| TypeScript | Python |
|
|
68
|
+
|------------|--------|
|
|
69
|
+
| `VideoObservabilityApi` | `VideoObservabilityApi` |
|
|
70
|
+
| `scoreVideo(videoUrl, extraCriteria?, ignoreCriteria?)` | `score_video(video_url, extra_criteria?, ignore_criteria?)` |
|
|
71
|
+
| `ExtraCriterion { criterionName, criterionDescription }` | `ExtraCriterion(criterion_name=..., criterion_description=...)` |
|
|
72
|
+
| `ignoreCriteria: ["UNNATURAL PHYSICS", ...]` | `ignore_criteria=["UNNATURAL PHYSICS"]` or `[DefaultCriterion.UNNATURAL_PHYSICS]` |
|
|
73
|
+
| `ScoreMap { severity, criteria }` | `ObserveResponse` |
|
|
74
|
+
|
|
75
|
+
**Default criteria** (for `ignore_criteria`): `UNNATURAL PHYSICS`, `MORPHING`, `FLICKERING`, `ARTIFACTING`, `TEXT ISSUES`
|
|
76
|
+
|
|
77
|
+
## API Reference
|
|
78
|
+
|
|
79
|
+
### VideoObservabilityApi
|
|
80
|
+
|
|
81
|
+
| Parameter | Type | Description |
|
|
82
|
+
|-----------|------|-------------|
|
|
83
|
+
| `api_key` | `str` | Your Mocra API key ([get one](https://docs.mocra.io/quickstart)) |
|
|
84
|
+
| `base_url` | `str` | Override API URL (default: `https://api.mocra.io`) |
|
|
85
|
+
| `timeout` | `float` | Request timeout in seconds |
|
|
86
|
+
| `http_client` | `httpx.Client` | Optional pre-configured client |
|
|
87
|
+
|
|
88
|
+
### score_video()
|
|
89
|
+
|
|
90
|
+
| Parameter | Type | Description |
|
|
91
|
+
|-----------|------|-------------|
|
|
92
|
+
| `video_url` | `str` | URL of the video to analyze |
|
|
93
|
+
| `extra_criteria` | `list[ExtraCriterion]` | Extra criteria (default: `[]`) |
|
|
94
|
+
| `ignore_criteria` | `list[str \| DefaultCriterion]` | Default criteria to exclude (default: `[]`) |
|
|
95
|
+
|
|
96
|
+
**Returns:** `{ severity, criteria: [{ name, score }] }` (1–100 scale)
|
|
97
|
+
|
|
98
|
+
## Context Manager
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
with VideoObservabilityApi("YOUR_API_KEY") as api:
|
|
102
|
+
result = api.score_video("https://example.org/video.mp4")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
### Setup
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install -e ".[dev]"
|
|
111
|
+
pre-commit install # Run lint on git commit
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Lint
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
ruff check mocra tests
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Test
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pytest tests/ -v
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Publishing to PyPI
|
|
127
|
+
|
|
128
|
+
1. Create an API token at [pypi.org/manage/account/token](https://pypi.org/manage/account/token)
|
|
129
|
+
2. Add `PYPI_API_TOKEN` as a repository secret in GitHub
|
|
130
|
+
3. Create a release or run the workflow manually — the publish workflow runs on release publish and `workflow_dispatch`
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT
|
mocra-0.1.0/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Mocra Python SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for [Mocra's Observe API](https://docs.mocra.io/) — observability for video generation workflows. The API mirrors the [TypeScript SDK](https://github.com/Mocra-AI/Mocra) so you can switch between languages easily.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install -e .
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Python 3.10+
|
|
14
|
+
- [httpx](https://www.python-httpx.org/)
|
|
15
|
+
- [pydantic](https://docs.pydantic.dev/) v2
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from mocra import VideoObservabilityApi, ExtraCriterion
|
|
21
|
+
|
|
22
|
+
api = VideoObservabilityApi("YOUR_API_KEY")
|
|
23
|
+
result = api.score_video(
|
|
24
|
+
"https://example.org/video.mp4",
|
|
25
|
+
extra_criteria=[
|
|
26
|
+
ExtraCriterion(
|
|
27
|
+
criterion_name="Blur",
|
|
28
|
+
criterion_description="The video should not be blurry",
|
|
29
|
+
),
|
|
30
|
+
],
|
|
31
|
+
ignore_criteria=["UNNATURAL PHYSICS"],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
print(f"Overall score: {result.severity}")
|
|
35
|
+
for criterion in result.criteria:
|
|
36
|
+
print(f" {criterion.name}: {criterion.score}")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API Mapping (TypeScript ↔ Python)
|
|
40
|
+
|
|
41
|
+
| TypeScript | Python |
|
|
42
|
+
|------------|--------|
|
|
43
|
+
| `VideoObservabilityApi` | `VideoObservabilityApi` |
|
|
44
|
+
| `scoreVideo(videoUrl, extraCriteria?, ignoreCriteria?)` | `score_video(video_url, extra_criteria?, ignore_criteria?)` |
|
|
45
|
+
| `ExtraCriterion { criterionName, criterionDescription }` | `ExtraCriterion(criterion_name=..., criterion_description=...)` |
|
|
46
|
+
| `ignoreCriteria: ["UNNATURAL PHYSICS", ...]` | `ignore_criteria=["UNNATURAL PHYSICS"]` or `[DefaultCriterion.UNNATURAL_PHYSICS]` |
|
|
47
|
+
| `ScoreMap { severity, criteria }` | `ObserveResponse` |
|
|
48
|
+
|
|
49
|
+
**Default criteria** (for `ignore_criteria`): `UNNATURAL PHYSICS`, `MORPHING`, `FLICKERING`, `ARTIFACTING`, `TEXT ISSUES`
|
|
50
|
+
|
|
51
|
+
## API Reference
|
|
52
|
+
|
|
53
|
+
### VideoObservabilityApi
|
|
54
|
+
|
|
55
|
+
| Parameter | Type | Description |
|
|
56
|
+
|-----------|------|-------------|
|
|
57
|
+
| `api_key` | `str` | Your Mocra API key ([get one](https://docs.mocra.io/quickstart)) |
|
|
58
|
+
| `base_url` | `str` | Override API URL (default: `https://api.mocra.io`) |
|
|
59
|
+
| `timeout` | `float` | Request timeout in seconds |
|
|
60
|
+
| `http_client` | `httpx.Client` | Optional pre-configured client |
|
|
61
|
+
|
|
62
|
+
### score_video()
|
|
63
|
+
|
|
64
|
+
| Parameter | Type | Description |
|
|
65
|
+
|-----------|------|-------------|
|
|
66
|
+
| `video_url` | `str` | URL of the video to analyze |
|
|
67
|
+
| `extra_criteria` | `list[ExtraCriterion]` | Extra criteria (default: `[]`) |
|
|
68
|
+
| `ignore_criteria` | `list[str \| DefaultCriterion]` | Default criteria to exclude (default: `[]`) |
|
|
69
|
+
|
|
70
|
+
**Returns:** `{ severity, criteria: [{ name, score }] }` (1–100 scale)
|
|
71
|
+
|
|
72
|
+
## Context Manager
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
with VideoObservabilityApi("YOUR_API_KEY") as api:
|
|
76
|
+
result = api.score_video("https://example.org/video.mp4")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
### Setup
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install -e ".[dev]"
|
|
85
|
+
pre-commit install # Run lint on git commit
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Lint
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
ruff check mocra tests
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Test
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pytest tests/ -v
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Publishing to PyPI
|
|
101
|
+
|
|
102
|
+
1. Create an API token at [pypi.org/manage/account/token](https://pypi.org/manage/account/token)
|
|
103
|
+
2. Add `PYPI_API_TOKEN` as a repository secret in GitHub
|
|
104
|
+
3. Create a release or run the workflow manually — the publish workflow runs on release publish and `workflow_dispatch`
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mocra Python SDK - Observability for video generation workflows.
|
|
3
|
+
|
|
4
|
+
API mirrors the TypeScript SDK: https://github.com/Mocra-AI/Mocra
|
|
5
|
+
|
|
6
|
+
from mocra import VideoObservabilityApi, ExtraCriterion
|
|
7
|
+
|
|
8
|
+
api = VideoObservabilityApi("your-api-key")
|
|
9
|
+
result = api.score_video(
|
|
10
|
+
"https://example.org/video.mp4",
|
|
11
|
+
extra_criteria=[
|
|
12
|
+
ExtraCriterion(criterion_name="Blur", criterion_description="..."),
|
|
13
|
+
],
|
|
14
|
+
ignore_criteria=["UNNATURAL PHYSICS"],
|
|
15
|
+
)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from mocra.client import MocraError, VideoObservabilityApi
|
|
19
|
+
from mocra.models import (
|
|
20
|
+
CriterionScore,
|
|
21
|
+
DefaultCriterion,
|
|
22
|
+
ExtraCriterion,
|
|
23
|
+
ObserveResponse,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"VideoObservabilityApi",
|
|
28
|
+
"MocraError",
|
|
29
|
+
"ExtraCriterion",
|
|
30
|
+
"DefaultCriterion",
|
|
31
|
+
"ObserveResponse",
|
|
32
|
+
"CriterionScore",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTTP client for the Mocra Observe API.
|
|
3
|
+
|
|
4
|
+
API mirrors the TypeScript SDK: VideoObservabilityApi(api_key).score_video(...)
|
|
5
|
+
See https://github.com/Mocra-AI/Mocra
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
from mocra.models import (
|
|
11
|
+
DefaultCriterion,
|
|
12
|
+
ExtraCriterion,
|
|
13
|
+
ObserveRequest,
|
|
14
|
+
ObserveResponse,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
MOCRA_API_DOMAIN = "https://api.mocra.io"
|
|
18
|
+
OBSERVE_PATH = "/observe"
|
|
19
|
+
|
|
20
|
+
DEFAULT_HEADERS = {
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
"Accept": "application/json",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _normalize_ignore_criteria(
|
|
27
|
+
criteria: list[str | DefaultCriterion],
|
|
28
|
+
) -> list[str]:
|
|
29
|
+
"""Convert ignore_criteria to list of strings for API payload."""
|
|
30
|
+
return [
|
|
31
|
+
c.value if isinstance(c, DefaultCriterion) else str(c)
|
|
32
|
+
for c in criteria
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _parse_error_message(response: httpx.Response) -> str:
|
|
37
|
+
"""Extract error message from failed API response."""
|
|
38
|
+
try:
|
|
39
|
+
body = response.json()
|
|
40
|
+
return body.get("message", response.text)
|
|
41
|
+
except Exception:
|
|
42
|
+
return response.text
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MocraError(Exception):
|
|
46
|
+
"""Raised when the Mocra API returns an error."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, message: str, status_code: int | None = None):
|
|
49
|
+
self.message = message
|
|
50
|
+
self.status_code = status_code
|
|
51
|
+
super().__init__(f"[{status_code}] {message}" if status_code else message)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class VideoObservabilityApi:
|
|
55
|
+
"""
|
|
56
|
+
Video observability API. Matches TypeScript VideoObservabilityApi.
|
|
57
|
+
|
|
58
|
+
Example (mirrors TS SDK):
|
|
59
|
+
>>> api = VideoObservabilityApi("your-api-key")
|
|
60
|
+
>>> result = api.score_video(
|
|
61
|
+
... "http://example.org/video.mp4",
|
|
62
|
+
... extra_criteria=[
|
|
63
|
+
... ExtraCriterion(
|
|
64
|
+
... criterion_name="Blur",
|
|
65
|
+
... criterion_description="The video should not be blurry",
|
|
66
|
+
... ),
|
|
67
|
+
... ],
|
|
68
|
+
... ignore_criteria=["UNNATURAL PHYSICS"],
|
|
69
|
+
... )
|
|
70
|
+
>>> print(result.severity, result.criteria)
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
api_key: str,
|
|
76
|
+
base_url: str | None = None,
|
|
77
|
+
timeout: float = 60.0,
|
|
78
|
+
http_client: httpx.Client | None = None,
|
|
79
|
+
):
|
|
80
|
+
"""
|
|
81
|
+
Initialize the API client. Matches TS: VideoObservabilityApi(apiKey).
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
api_key: Your Mocra API key.
|
|
85
|
+
base_url: Override API base URL (default: https://api.mocra.io).
|
|
86
|
+
timeout: Request timeout in seconds.
|
|
87
|
+
http_client: Optional pre-configured httpx client.
|
|
88
|
+
"""
|
|
89
|
+
self._api_key = api_key
|
|
90
|
+
base = (base_url or MOCRA_API_DOMAIN).rstrip("/") + "/"
|
|
91
|
+
self._timeout = timeout
|
|
92
|
+
headers = {"Authorization": f"Bearer {api_key}", **DEFAULT_HEADERS}
|
|
93
|
+
self._client = http_client or httpx.Client(
|
|
94
|
+
base_url=base,
|
|
95
|
+
headers=headers,
|
|
96
|
+
timeout=timeout,
|
|
97
|
+
)
|
|
98
|
+
self._owns_client = http_client is None
|
|
99
|
+
|
|
100
|
+
def score_video(
|
|
101
|
+
self,
|
|
102
|
+
video_url: str,
|
|
103
|
+
extra_criteria: list[ExtraCriterion] | None = None,
|
|
104
|
+
ignore_criteria: list[str | DefaultCriterion] | None = None,
|
|
105
|
+
) -> ObserveResponse:
|
|
106
|
+
"""
|
|
107
|
+
Score a video (TS: scoreVideo(videoUrl, extraCriteria?, ignoreCriteria?)).
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
video_url: URL of the video to analyze.
|
|
111
|
+
extra_criteria: Extra criteria to analyze (default: []).
|
|
112
|
+
ignore_criteria: Default criteria to exclude, e.g. ["UNNATURAL PHYSICS"]
|
|
113
|
+
or [DefaultCriterion.UNNATURAL_PHYSICS] (default: []).
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
{ severity, criteria: [{ name, score }] }
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
MocraError: API returned an error.
|
|
120
|
+
httpx.HTTPError: Network error.
|
|
121
|
+
"""
|
|
122
|
+
extra = extra_criteria or []
|
|
123
|
+
ignore_strs = _normalize_ignore_criteria(ignore_criteria or [])
|
|
124
|
+
request = ObserveRequest(
|
|
125
|
+
video_url=video_url,
|
|
126
|
+
custom_criteria=extra,
|
|
127
|
+
remove_criteria=ignore_strs,
|
|
128
|
+
)
|
|
129
|
+
return self._request(request)
|
|
130
|
+
|
|
131
|
+
def _request(self, request: ObserveRequest) -> ObserveResponse:
|
|
132
|
+
payload = request.model_dump(by_alias=True, mode="json")
|
|
133
|
+
response = self._client.post(OBSERVE_PATH, json=payload)
|
|
134
|
+
|
|
135
|
+
if response.status_code == 200:
|
|
136
|
+
return ObserveResponse.model_validate(response.json())
|
|
137
|
+
|
|
138
|
+
message = _parse_error_message(response)
|
|
139
|
+
raise MocraError(message=message, status_code=response.status_code)
|
|
140
|
+
|
|
141
|
+
def close(self) -> None:
|
|
142
|
+
"""Close the HTTP client and release resources."""
|
|
143
|
+
if self._owns_client:
|
|
144
|
+
self._client.close()
|
|
145
|
+
|
|
146
|
+
def __enter__(self) -> "VideoObservabilityApi":
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
def __exit__(self, *args: object) -> None:
|
|
150
|
+
self.close()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models for the Mocra Observe API.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DefaultCriterion(str, Enum):
|
|
11
|
+
"""Built-in criteria that can be removed from analysis."""
|
|
12
|
+
|
|
13
|
+
UNNATURAL_PHYSICS = "UNNATURAL PHYSICS"
|
|
14
|
+
MORPHING = "MORPHING"
|
|
15
|
+
FLICKERING = "FLICKERING"
|
|
16
|
+
ARTIFACTING = "ARTIFACTING"
|
|
17
|
+
TEXT_ISSUES = "TEXT ISSUES"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ExtraCriterion(BaseModel):
|
|
21
|
+
"""
|
|
22
|
+
Extra criterion to add to the analysis.
|
|
23
|
+
Matches TypeScript ExtraCriterion: { criterionName, criterionDescription }
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
criterion_name: str = Field(..., alias="criterionName")
|
|
27
|
+
criterion_description: str = Field(..., alias="criterionDescription")
|
|
28
|
+
|
|
29
|
+
model_config = {"populate_by_name": True}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ObserveRequest(BaseModel):
|
|
33
|
+
"""Request body for the /observe endpoint."""
|
|
34
|
+
|
|
35
|
+
video_url: str = Field(..., alias="videoUrl")
|
|
36
|
+
custom_criteria: list[ExtraCriterion] = Field(
|
|
37
|
+
default_factory=list, alias="customCriteria"
|
|
38
|
+
)
|
|
39
|
+
remove_criteria: list[str] = Field(
|
|
40
|
+
default_factory=list, alias="removeCriteria"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
model_config = {"populate_by_name": True}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CriterionScore(BaseModel):
|
|
47
|
+
"""Per-criterion score in the response."""
|
|
48
|
+
|
|
49
|
+
name: str
|
|
50
|
+
score: float = Field(..., ge=1, le=100)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ObserveResponse(BaseModel):
|
|
54
|
+
"""
|
|
55
|
+
Response from the /observe endpoint.
|
|
56
|
+
Matches TypeScript ScoreMap: { severity, criteria: [{ name, score }] }
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
severity: float = Field(..., ge=1, le=100)
|
|
60
|
+
criteria: list[CriterionScore]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ErrorResponse(BaseModel):
|
|
64
|
+
"""Error response from the API."""
|
|
65
|
+
|
|
66
|
+
message: str
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mocra
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Mocra's Observe API - observability for video generation workflows
|
|
5
|
+
Author: Mocra
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://docs.mocra.io
|
|
8
|
+
Project-URL: Homepage, https://mocra.io
|
|
9
|
+
Keywords: mocra,video,observability,quality,ai
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: httpx>=0.24.0
|
|
20
|
+
Requires-Dist: pydantic>=2.0.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: ruff>=0.8.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
26
|
+
|
|
27
|
+
# Mocra Python SDK
|
|
28
|
+
|
|
29
|
+
Python SDK for [Mocra's Observe API](https://docs.mocra.io/) — observability for video generation workflows. The API mirrors the [TypeScript SDK](https://github.com/Mocra-AI/Mocra) so you can switch between languages easily.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install -e .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- Python 3.10+
|
|
40
|
+
- [httpx](https://www.python-httpx.org/)
|
|
41
|
+
- [pydantic](https://docs.pydantic.dev/) v2
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from mocra import VideoObservabilityApi, ExtraCriterion
|
|
47
|
+
|
|
48
|
+
api = VideoObservabilityApi("YOUR_API_KEY")
|
|
49
|
+
result = api.score_video(
|
|
50
|
+
"https://example.org/video.mp4",
|
|
51
|
+
extra_criteria=[
|
|
52
|
+
ExtraCriterion(
|
|
53
|
+
criterion_name="Blur",
|
|
54
|
+
criterion_description="The video should not be blurry",
|
|
55
|
+
),
|
|
56
|
+
],
|
|
57
|
+
ignore_criteria=["UNNATURAL PHYSICS"],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
print(f"Overall score: {result.severity}")
|
|
61
|
+
for criterion in result.criteria:
|
|
62
|
+
print(f" {criterion.name}: {criterion.score}")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API Mapping (TypeScript ↔ Python)
|
|
66
|
+
|
|
67
|
+
| TypeScript | Python |
|
|
68
|
+
|------------|--------|
|
|
69
|
+
| `VideoObservabilityApi` | `VideoObservabilityApi` |
|
|
70
|
+
| `scoreVideo(videoUrl, extraCriteria?, ignoreCriteria?)` | `score_video(video_url, extra_criteria?, ignore_criteria?)` |
|
|
71
|
+
| `ExtraCriterion { criterionName, criterionDescription }` | `ExtraCriterion(criterion_name=..., criterion_description=...)` |
|
|
72
|
+
| `ignoreCriteria: ["UNNATURAL PHYSICS", ...]` | `ignore_criteria=["UNNATURAL PHYSICS"]` or `[DefaultCriterion.UNNATURAL_PHYSICS]` |
|
|
73
|
+
| `ScoreMap { severity, criteria }` | `ObserveResponse` |
|
|
74
|
+
|
|
75
|
+
**Default criteria** (for `ignore_criteria`): `UNNATURAL PHYSICS`, `MORPHING`, `FLICKERING`, `ARTIFACTING`, `TEXT ISSUES`
|
|
76
|
+
|
|
77
|
+
## API Reference
|
|
78
|
+
|
|
79
|
+
### VideoObservabilityApi
|
|
80
|
+
|
|
81
|
+
| Parameter | Type | Description |
|
|
82
|
+
|-----------|------|-------------|
|
|
83
|
+
| `api_key` | `str` | Your Mocra API key ([get one](https://docs.mocra.io/quickstart)) |
|
|
84
|
+
| `base_url` | `str` | Override API URL (default: `https://api.mocra.io`) |
|
|
85
|
+
| `timeout` | `float` | Request timeout in seconds |
|
|
86
|
+
| `http_client` | `httpx.Client` | Optional pre-configured client |
|
|
87
|
+
|
|
88
|
+
### score_video()
|
|
89
|
+
|
|
90
|
+
| Parameter | Type | Description |
|
|
91
|
+
|-----------|------|-------------|
|
|
92
|
+
| `video_url` | `str` | URL of the video to analyze |
|
|
93
|
+
| `extra_criteria` | `list[ExtraCriterion]` | Extra criteria (default: `[]`) |
|
|
94
|
+
| `ignore_criteria` | `list[str \| DefaultCriterion]` | Default criteria to exclude (default: `[]`) |
|
|
95
|
+
|
|
96
|
+
**Returns:** `{ severity, criteria: [{ name, score }] }` (1–100 scale)
|
|
97
|
+
|
|
98
|
+
## Context Manager
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
with VideoObservabilityApi("YOUR_API_KEY") as api:
|
|
102
|
+
result = api.score_video("https://example.org/video.mp4")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
### Setup
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install -e ".[dev]"
|
|
111
|
+
pre-commit install # Run lint on git commit
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Lint
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
ruff check mocra tests
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Test
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pytest tests/ -v
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Publishing to PyPI
|
|
127
|
+
|
|
128
|
+
1. Create an API token at [pypi.org/manage/account/token](https://pypi.org/manage/account/token)
|
|
129
|
+
2. Add `PYPI_API_TOKEN` as a repository secret in GitHub
|
|
130
|
+
3. Create a release or run the workflow manually — the publish workflow runs on release publish and `workflow_dispatch`
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
mocra/__init__.py
|
|
4
|
+
mocra/client.py
|
|
5
|
+
mocra/models.py
|
|
6
|
+
mocra.egg-info/PKG-INFO
|
|
7
|
+
mocra.egg-info/SOURCES.txt
|
|
8
|
+
mocra.egg-info/dependency_links.txt
|
|
9
|
+
mocra.egg-info/requires.txt
|
|
10
|
+
mocra.egg-info/top_level.txt
|
|
11
|
+
tests/test_client.py
|
|
12
|
+
tests/test_models.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mocra
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mocra"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for Mocra's Observe API - observability for video generation workflows"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Mocra" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["mocra", "video", "observability", "quality", "ai"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"httpx>=0.24.0",
|
|
27
|
+
"pydantic>=2.0.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=7.0",
|
|
33
|
+
"pytest-cov>=4.0",
|
|
34
|
+
"ruff>=0.8.0",
|
|
35
|
+
"pre-commit>=3.0.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Documentation = "https://docs.mocra.io"
|
|
40
|
+
Homepage = "https://mocra.io"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
include = ["mocra*"]
|
|
44
|
+
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
target-version = "py310"
|
|
47
|
+
line-length = 88
|
|
48
|
+
src = ["mocra", "tests"]
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = [
|
|
52
|
+
"E", # pycodestyle errors
|
|
53
|
+
"W", # pycodestyle warnings
|
|
54
|
+
"F", # Pyflakes
|
|
55
|
+
"I", # isort
|
|
56
|
+
"B", # flake8-bugbear
|
|
57
|
+
"C4", # flake8-comprehensions
|
|
58
|
+
"UP", # pyupgrade
|
|
59
|
+
]
|
|
60
|
+
ignore = []
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint.isort]
|
|
63
|
+
known-first-party = ["mocra"]
|
|
64
|
+
|
|
65
|
+
[tool.pytest.ini_options]
|
|
66
|
+
testpaths = ["tests"]
|
|
67
|
+
pythonpath = ["."]
|
|
68
|
+
addopts = "-v --tb=short"
|
mocra-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Tests for VideoObservabilityApi client."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from mocra import ExtraCriterion, MocraError, VideoObservabilityApi
|
|
7
|
+
from tests.conftest import SAMPLE_VIDEO_URL, create_mock_api
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_video_observability_api_constructor_accepts_api_key(api_key: str) -> None:
|
|
11
|
+
"""Should accept API key without checking validity (matches TS SDK)."""
|
|
12
|
+
api = VideoObservabilityApi(api_key)
|
|
13
|
+
assert api._api_key == api_key
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_score_video_success(mock_httpx_client: VideoObservabilityApi) -> None:
|
|
17
|
+
"""Should return parsed response on 200."""
|
|
18
|
+
result = mock_httpx_client.score_video(SAMPLE_VIDEO_URL)
|
|
19
|
+
assert result.severity == 85.5
|
|
20
|
+
assert len(result.criteria) == 2
|
|
21
|
+
assert result.criteria[0].name == "UNNATURAL PHYSICS"
|
|
22
|
+
assert result.criteria[0].score == 90.0
|
|
23
|
+
assert result.criteria[1].name == "Blur"
|
|
24
|
+
assert result.criteria[1].score == 81.0
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_score_video_with_extra_criteria(
|
|
28
|
+
mock_httpx_client: VideoObservabilityApi,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Should accept extra_criteria and pass to API."""
|
|
31
|
+
result = mock_httpx_client.score_video(
|
|
32
|
+
SAMPLE_VIDEO_URL,
|
|
33
|
+
extra_criteria=[
|
|
34
|
+
ExtraCriterion(
|
|
35
|
+
criterion_name="Blur",
|
|
36
|
+
criterion_description="Video should not be blurry",
|
|
37
|
+
),
|
|
38
|
+
],
|
|
39
|
+
)
|
|
40
|
+
assert result.severity == 85.5
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_score_video_with_ignore_criteria(
|
|
44
|
+
mock_httpx_client: VideoObservabilityApi,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Should accept ignore_criteria as strings."""
|
|
47
|
+
result = mock_httpx_client.score_video(
|
|
48
|
+
SAMPLE_VIDEO_URL,
|
|
49
|
+
ignore_criteria=["UNNATURAL PHYSICS", "MORPHING"],
|
|
50
|
+
)
|
|
51
|
+
assert result.severity == 85.5
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_score_video_with_default_criterion_enum(
|
|
55
|
+
mock_httpx_client: VideoObservabilityApi,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Should accept DefaultCriterion enum in ignore_criteria."""
|
|
58
|
+
from mocra.models import DefaultCriterion
|
|
59
|
+
|
|
60
|
+
result = mock_httpx_client.score_video(
|
|
61
|
+
SAMPLE_VIDEO_URL,
|
|
62
|
+
ignore_criteria=[DefaultCriterion.UNNATURAL_PHYSICS],
|
|
63
|
+
)
|
|
64
|
+
assert result.severity == 85.5
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_score_video_raises_on_error() -> None:
|
|
68
|
+
"""Should raise MocraError on non-200 response."""
|
|
69
|
+
response = httpx.Response(401, json={"message": "Invalid API key"})
|
|
70
|
+
api = create_mock_api(response, api_key="bad-key")
|
|
71
|
+
|
|
72
|
+
with pytest.raises(MocraError) as exc_info:
|
|
73
|
+
api.score_video(SAMPLE_VIDEO_URL)
|
|
74
|
+
|
|
75
|
+
assert exc_info.value.status_code == 401
|
|
76
|
+
assert "Invalid API key" in str(exc_info.value)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_score_video_raises_on_400() -> None:
|
|
80
|
+
"""Should raise MocraError with message on 400."""
|
|
81
|
+
response = httpx.Response(400, json={"message": "Invalid video URL"})
|
|
82
|
+
api = create_mock_api(response)
|
|
83
|
+
|
|
84
|
+
with pytest.raises(MocraError) as exc_info:
|
|
85
|
+
api.score_video("not-a-valid-url")
|
|
86
|
+
assert exc_info.value.message == "Invalid video URL"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_context_manager_closes_client(api_key: str) -> None:
|
|
90
|
+
"""Should close client when used as context manager."""
|
|
91
|
+
with VideoObservabilityApi(api_key) as api:
|
|
92
|
+
assert api._client is not None
|
|
93
|
+
# Client should be closed (we can't easily assert without internal access,
|
|
94
|
+
# but at least it shouldn't raise)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Tests for mocra models."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from pydantic import ValidationError
|
|
5
|
+
|
|
6
|
+
from mocra.models import (
|
|
7
|
+
CriterionScore,
|
|
8
|
+
DefaultCriterion,
|
|
9
|
+
ExtraCriterion,
|
|
10
|
+
ObserveResponse,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_extra_criterion_serialization() -> None:
|
|
15
|
+
"""ExtraCriterion should serialize with camelCase aliases."""
|
|
16
|
+
c = ExtraCriterion(
|
|
17
|
+
criterion_name="Blur",
|
|
18
|
+
criterion_description="No blur allowed",
|
|
19
|
+
)
|
|
20
|
+
d = c.model_dump(by_alias=True)
|
|
21
|
+
assert d["criterionName"] == "Blur"
|
|
22
|
+
assert d["criterionDescription"] == "No blur allowed"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_default_criterion_values() -> None:
|
|
26
|
+
"""DefaultCriterion should have correct string values."""
|
|
27
|
+
assert DefaultCriterion.UNNATURAL_PHYSICS.value == "UNNATURAL PHYSICS"
|
|
28
|
+
assert DefaultCriterion.TEXT_ISSUES.value == "TEXT ISSUES"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_observe_response_validation() -> None:
|
|
32
|
+
"""ObserveResponse should validate severity and criteria."""
|
|
33
|
+
r = ObserveResponse(severity=75.5, criteria=[CriterionScore(name="A", score=80)])
|
|
34
|
+
assert r.severity == 75.5
|
|
35
|
+
assert r.criteria[0].name == "A"
|
|
36
|
+
assert r.criteria[0].score == 80
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_observe_response_rejects_invalid_severity() -> None:
|
|
40
|
+
"""ObserveResponse should reject severity outside 1-100."""
|
|
41
|
+
with pytest.raises(ValidationError):
|
|
42
|
+
ObserveResponse(severity=0, criteria=[])
|
|
43
|
+
with pytest.raises(ValidationError):
|
|
44
|
+
ObserveResponse(severity=101, criteria=[])
|