dragoneye-python 0.4.0__tar.gz → 0.5.1__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.
Files changed (21) hide show
  1. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/PKG-INFO +1 -1
  2. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/dragoneye_python.egg-info/PKG-INFO +1 -1
  3. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/pyproject.toml +2 -2
  4. dragoneye_python-0.5.1/src/dragoneye/__init__.py +32 -0
  5. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/src/dragoneye/classification.py +15 -1
  6. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/src/dragoneye/models.py +26 -11
  7. dragoneye_python-0.5.1/src/dragoneye/types/common.py +12 -0
  8. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/src/dragoneye/types/media.py +35 -10
  9. dragoneye_python-0.4.0/src/dragoneye/__init__.py +0 -25
  10. dragoneye_python-0.4.0/src/dragoneye/types/common.py +0 -31
  11. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/README.md +0 -0
  12. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/dragoneye_python.egg-info/SOURCES.txt +0 -0
  13. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/dragoneye_python.egg-info/dependency_links.txt +0 -0
  14. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/dragoneye_python.egg-info/requires.txt +0 -0
  15. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/dragoneye_python.egg-info/top_level.txt +0 -0
  16. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/requirements.txt +0 -0
  17. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/setup.cfg +0 -0
  18. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/src/dragoneye/client.py +0 -0
  19. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/src/dragoneye/constants.py +0 -0
  20. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/src/dragoneye/types/__init__.py +0 -0
  21. {dragoneye_python-0.4.0 → dragoneye_python-0.5.1}/src/dragoneye/types/exception.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dragoneye-python
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  License-Expression: MIT
5
5
  Requires-Python: >=3.8
6
6
  Requires-Dist: requests
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dragoneye-python
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  License-Expression: MIT
5
5
  Requires-Python: >=3.8
6
6
  Requires-Dist: requests
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dragoneye-python"
7
- version = "0.4.0"
7
+ version = "0.5.1"
8
8
  requires-python = ">=3.8"
9
9
  dynamic = ["dependencies"]
10
10
  license = "MIT"
@@ -13,4 +13,4 @@ license = "MIT"
13
13
  dependencies = {file = ["requirements.txt"]}
14
14
 
15
15
  [tool.setuptools.package-dir]
16
- dragoneye = "src/dragoneye"
16
+ dragoneye = "src/dragoneye"
@@ -0,0 +1,32 @@
1
+ from .classification import (
2
+ Classification,
3
+ )
4
+ from .client import Dragoneye
5
+ from .models import (
6
+ ClassificationAttributeOption,
7
+ ClassificationAttributeResponse,
8
+ ClassificationCategory,
9
+ ClassificationCategoryPrediction,
10
+ ClassificationObjectPrediction,
11
+ ClassificationPredictImageResponse,
12
+ ClassificationPredictVideoResponse,
13
+ ClassificationVideoObjectPrediction,
14
+ )
15
+ from .types.common import NormalizedBbox
16
+ from .types.media import Image, Video
17
+
18
+ __all__ = [
19
+ "Classification",
20
+ "ClassificationAttributeOption",
21
+ "ClassificationAttributeResponse",
22
+ "ClassificationCategory",
23
+ "ClassificationCategoryPrediction",
24
+ "ClassificationObjectPrediction",
25
+ "ClassificationPredictImageResponse",
26
+ "ClassificationPredictVideoResponse",
27
+ "ClassificationVideoObjectPrediction",
28
+ "Dragoneye",
29
+ "Image",
30
+ "NormalizedBbox",
31
+ "Video",
32
+ ]
@@ -97,12 +97,14 @@ class Classification:
97
97
  media: Image,
98
98
  model_name: str,
99
99
  timeout_seconds: Optional[int] = None,
100
+ **kwargs: Any,
100
101
  ) -> ClassificationPredictImageResponse:
101
102
  return await self._predict_unified(
102
103
  media=media,
103
104
  model_name=model_name,
104
105
  frames_per_second=None,
105
106
  timeout_seconds=timeout_seconds,
107
+ **kwargs,
106
108
  )
107
109
 
108
110
  async def predict_video(
@@ -111,12 +113,14 @@ class Classification:
111
113
  model_name: str,
112
114
  frames_per_second: int = 1,
113
115
  timeout_seconds: Optional[int] = None,
116
+ **kwargs: Any,
114
117
  ) -> ClassificationPredictVideoResponse:
115
118
  return await self._predict_unified(
116
119
  media=media,
117
120
  model_name=model_name,
118
121
  frames_per_second=frames_per_second,
119
122
  timeout_seconds=timeout_seconds,
123
+ **kwargs,
120
124
  )
121
125
 
122
126
  async def status(
@@ -219,6 +223,7 @@ class Classification:
219
223
  model_name: str,
220
224
  frames_per_second: Optional[int],
221
225
  timeout_seconds: Optional[int] = None,
226
+ **kwargs: Any,
222
227
  ) -> ClassificationPredictImageResponse: ...
223
228
 
224
229
  @overload
@@ -228,6 +233,7 @@ class Classification:
228
233
  model_name: str,
229
234
  frames_per_second: Optional[int],
230
235
  timeout_seconds: Optional[int] = None,
236
+ **kwargs: Any,
231
237
  ) -> ClassificationPredictVideoResponse: ...
232
238
 
233
239
  async def _predict_unified(
@@ -236,10 +242,12 @@ class Classification:
236
242
  model_name: str,
237
243
  frames_per_second: Optional[int],
238
244
  timeout_seconds: Optional[int] = None,
245
+ **kwargs: Any,
239
246
  ) -> Union[ClassificationPredictImageResponse, ClassificationPredictVideoResponse]:
240
247
  prediction_task_begin_response = await self._begin_prediction_task(
241
248
  mime_type=media.mime_type,
242
249
  frames_per_second=frames_per_second,
250
+ file_name=media.name,
243
251
  )
244
252
 
245
253
  await self._upload_media_to_prediction_task(
@@ -247,9 +255,10 @@ class Classification:
247
255
  )
248
256
 
249
257
  predict_url = f"{BASE_API_URL}/predict"
250
- predict_data = {
258
+ predict_data: dict[str, Any] = {
251
259
  "model_name": model_name,
252
260
  "prediction_task_uuid": prediction_task_begin_response.prediction_task_uuid,
261
+ **kwargs,
253
262
  }
254
263
  predict_headers = {
255
264
  "Authorization": f"Bearer {self._client.api_key}",
@@ -339,11 +348,16 @@ class Classification:
339
348
  self,
340
349
  mime_type: str,
341
350
  frames_per_second: Optional[int],
351
+ file_name: Optional[str],
342
352
  ) -> _PredictionTaskBeginResponse:
343
353
  url = f"{BASE_API_URL}/prediction-task/begin"
344
354
 
345
355
  form_data = aiohttp.FormData()
346
356
  form_data.add_field("mimetype", mime_type)
357
+ form_data.add_field("response_version", "object")
358
+ if file_name is not None:
359
+ form_data.add_field("file_name", file_name)
360
+
347
361
  if frames_per_second is not None:
348
362
  form_data.add_field("frames_per_second", str(frames_per_second))
349
363
 
@@ -1,4 +1,4 @@
1
- from typing import Dict, Sequence
1
+ from typing import Dict, List, Optional
2
2
 
3
3
  from pydantic import BaseModel
4
4
 
@@ -7,8 +7,6 @@ from dragoneye.types.common import (
7
7
  PredictionTaskState,
8
8
  PredictionTaskUUID,
9
9
  PredictionType,
10
- TaxonID,
11
- TaxonPrediction,
12
10
  )
13
11
 
14
12
 
@@ -18,22 +16,38 @@ class PredictionTaskStatusResponse(BaseModel):
18
16
  status: PredictionTaskState
19
17
 
20
18
 
21
- class ClassificationTraitRootPrediction(BaseModel):
22
- id: TaxonID
19
+ class ClassificationAttributeOption(BaseModel):
20
+ option_id: int
23
21
  name: str
24
- displayName: str
25
- taxons: Sequence[TaxonPrediction]
22
+ score: float
23
+
24
+
25
+ class ClassificationAttributeResponse(BaseModel):
26
+ attribute_id: int
27
+ name: str
28
+ options: List[ClassificationAttributeOption]
29
+
30
+
31
+ class ClassificationCategory(BaseModel):
32
+ id: int
33
+ name: str
34
+ score: float
35
+
36
+
37
+ class ClassificationCategoryPrediction(BaseModel):
38
+ category: ClassificationCategory
39
+ attributes: List[ClassificationAttributeResponse]
26
40
 
27
41
 
28
42
  class ClassificationObjectPrediction(BaseModel):
29
43
  normalizedBbox: NormalizedBbox
30
- category: TaxonPrediction
31
- traits: Sequence[ClassificationTraitRootPrediction]
44
+ predictions: List[ClassificationCategoryPrediction]
32
45
 
33
46
 
34
47
  class ClassificationPredictImageResponse(BaseModel):
35
- predictions: Sequence[ClassificationObjectPrediction]
48
+ object_predictions: List[ClassificationObjectPrediction]
36
49
  prediction_task_uuid: PredictionTaskUUID
50
+ original_file_name: Optional[str]
37
51
 
38
52
 
39
53
  class ClassificationVideoObjectPrediction(ClassificationObjectPrediction):
@@ -43,7 +57,8 @@ class ClassificationVideoObjectPrediction(ClassificationObjectPrediction):
43
57
 
44
58
  class ClassificationPredictVideoResponse(BaseModel):
45
59
  timestamp_us_to_predictions: Dict[
46
- int, Sequence[ClassificationVideoObjectPrediction]
60
+ int, List[ClassificationVideoObjectPrediction]
47
61
  ]
48
62
  frames_per_second: int
49
63
  prediction_task_uuid: PredictionTaskUUID
64
+ original_file_name: Optional[str]
@@ -0,0 +1,12 @@
1
+ from typing import Literal, NewType, Tuple
2
+
3
+ PredictionType = Literal["image", "video"]
4
+ PredictionTaskState = NewType("PredictionTaskState", str)
5
+
6
+ NormalizedBbox = NewType("NormalizedBbox", Tuple[float, float, float, float])
7
+
8
+ PredictionTaskUUID = NewType("PredictionTaskUUID", str)
9
+
10
+ TimestampUs = NewType("TimestampUs", int)
11
+
12
+ BASE_API_URL = "https://api.dragoneye.ai"
@@ -15,6 +15,7 @@ class Media:
15
15
  """Generic binary media + mime_type with conservative, non-destructive access."""
16
16
 
17
17
  file_or_bytes: Union[bytes, BytesIO, BinaryIO, BufferedReader]
18
+ name: Optional[str]
18
19
  mime_type: str
19
20
 
20
21
  # Subclasses set this to enforce a family of mimetypes, e.g. ("image/",)
@@ -33,16 +34,33 @@ class Media:
33
34
  # ---------- Convenience constructors ----------
34
35
 
35
36
  @classmethod
36
- def from_bytes(cls, data: bytes, mime_type: str) -> Self:
37
- return cls(file_or_bytes=data, mime_type=mime_type)
37
+ def from_bytes(
38
+ cls, data: bytes, mime_type: str, name: Optional[str] = None
39
+ ) -> Self:
40
+ """
41
+ Create a Media (or subclass) from raw bytes.
42
+
43
+ - `data`: Raw bytes of the media content.
44
+ - `mime_type`: The MIME type of the media (e.g., 'image/jpeg').
45
+ - `name`: Optional non-unique descriptive identifier provided by the user
46
+ for identifying or tracking responses to inputs.
47
+ """
48
+ return cls(file_or_bytes=data, mime_type=mime_type, name=name)
38
49
 
39
50
  @classmethod
40
- def from_stream(cls, stream: BinaryIO, *, mime_type: str) -> Self:
51
+ def from_stream(
52
+ cls, stream: BinaryIO, *, mime_type: str, name: Optional[str] = None
53
+ ) -> Self:
41
54
  """
42
55
  Accepts any readable binary stream (e.g., open('file', 'rb')).
43
56
  Keeps the stream as-is; reading is deferred to bytes_io().
57
+
58
+ - `stream`: A readable binary stream.
59
+ - `mime_type`: The MIME type of the media (e.g., 'image/jpeg').
60
+ - `name`: Optional non-unique descriptive identifier provided by the user
61
+ for identifying or tracking responses to inputs.
44
62
  """
45
- return cls(file_or_bytes=stream, mime_type=mime_type)
63
+ return cls(file_or_bytes=stream, mime_type=mime_type, name=name)
46
64
 
47
65
  @classmethod
48
66
  def from_path(
@@ -50,6 +68,7 @@ class Media:
50
68
  path: Union[str, os.PathLike[str]],
51
69
  *,
52
70
  mime_type: Optional[str] = None,
71
+ name: Optional[str] = None,
53
72
  guess_from_extension: bool = True,
54
73
  read_into_memory: bool = False,
55
74
  ) -> Self:
@@ -59,6 +78,9 @@ class Media:
59
78
  - `path`: Path to the file on disk.
60
79
  - `mime_type`: Explicit mime type. If omitted and `guess_from_extension=True`,
61
80
  we'll try to guess from the file extension.
81
+ - `name`: Optional non-unique descriptive identifier provided by the user
82
+ for identifying or tracking responses to inputs. If not provided, will
83
+ default to the filename from the path.
62
84
  - `read_into_memory=True`: load file bytes into memory (closes file immediately).
63
85
  Otherwise, keep an open file stream.
64
86
  """
@@ -74,12 +96,15 @@ class Media:
74
96
  f"mime_type is required for {path} (no extension-based guess available)."
75
97
  )
76
98
 
99
+ # Use provided name or extract from path
100
+ media_name = name if name is not None else path.name
101
+
77
102
  if read_into_memory:
78
103
  data = path.read_bytes()
79
- return cls(file_or_bytes=data, mime_type=mt)
104
+ return cls(file_or_bytes=data, mime_type=mt, name=media_name)
80
105
  else:
81
106
  f = path.open("rb")
82
- return cls(file_or_bytes=f, mime_type=mt)
107
+ return cls(file_or_bytes=f, mime_type=mt, name=media_name)
83
108
 
84
109
  # ---------- Utilities ----------
85
110
 
@@ -111,10 +136,10 @@ class Media:
111
136
 
112
137
  # For any readable object with .read()
113
138
  if hasattr(src, "read"):
114
- pos = _tell_safe(src)
115
- data = src.read()
116
- _seek_safe(src, pos)
117
- return BytesIO(data)
139
+ pos = _tell_safe(src) # pyright: ignore [reportArgumentType]
140
+ data = src.read() # pyright: ignore [reportUnknownMemberType, reportAttributeAccessIssue, reportUnknownVariableType]
141
+ _seek_safe(src, pos) # pyright: ignore [reportArgumentType]
142
+ return BytesIO(data) # pyright: ignore [reportUnknownArgumentType]
118
143
 
119
144
  raise TypeError(
120
145
  "Invalid media source: expected bytes, BytesIO, or a readable binary stream."
@@ -1,25 +0,0 @@
1
- from .classification import (
2
- Classification,
3
- )
4
- from .client import Dragoneye
5
- from .models import (
6
- ClassificationObjectPrediction,
7
- ClassificationPredictImageResponse,
8
- ClassificationTraitRootPrediction,
9
- )
10
- from .types.common import NormalizedBbox, TaxonID, TaxonPrediction, TaxonType
11
- from .types.media import Image, Video
12
-
13
- __all__ = [
14
- "Classification",
15
- "ClassificationObjectPrediction",
16
- "ClassificationPredictImageResponse",
17
- "ClassificationTraitRootPrediction",
18
- "Dragoneye",
19
- "Image",
20
- "Video",
21
- "NormalizedBbox",
22
- "TaxonID",
23
- "TaxonPrediction",
24
- "TaxonType",
25
- ]
@@ -1,31 +0,0 @@
1
- from enum import Enum
2
- from typing import Literal, NewType, Optional, Sequence, Tuple
3
-
4
- from pydantic import BaseModel
5
-
6
- PredictionType = Literal["image", "video"]
7
- PredictionTaskState = NewType("PredictionTaskState", str)
8
-
9
- NormalizedBbox = NewType("NormalizedBbox", Tuple[float, float, float, float])
10
-
11
-
12
- class TaxonType(str, Enum):
13
- CATEGORY = ("category",)
14
- TRAIT = ("trait",)
15
-
16
-
17
- TaxonID = NewType("TaxonID", int)
18
-
19
- PredictionTaskUUID = NewType("PredictionTaskUUID", str)
20
-
21
-
22
- class TaxonPrediction(BaseModel):
23
- id: TaxonID
24
- type: TaxonType
25
- name: str
26
- displayName: str
27
- score: Optional[float]
28
- children: Sequence["TaxonPrediction"]
29
-
30
-
31
- BASE_API_URL = "https://api.dragoneye.ai"