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 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,8 @@
1
+ httpx>=0.24.0
2
+ pydantic>=2.0.0
3
+
4
+ [dev]
5
+ pytest>=7.0
6
+ pytest-cov>=4.0
7
+ ruff>=0.8.0
8
+ pre-commit>=3.0.0
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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=[])