chunkr-ai 0.1.0a2__py3-none-any.whl → 0.1.0a4__py3-none-any.whl

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.
chunkr_ai/__init__.py CHANGED
@@ -72,6 +72,9 @@ __all__ = [
72
72
  ]
73
73
 
74
74
  if not _t.TYPE_CHECKING:
75
+ # Load custom helpers that monkey-patch generated types.
76
+ # This keeps custom code separate from generated files, per Stainless guidance.
77
+ from .lib import tasks_poll as _tasks_poll # noqa: F401
75
78
  from ._utils._resources_proxy import resources as resources
76
79
 
77
80
  _setup_logging()
chunkr_ai/_client.py CHANGED
@@ -21,7 +21,7 @@ from ._types import (
21
21
  )
22
22
  from ._utils import is_given, get_async_library
23
23
  from ._version import __version__
24
- from .resources import health
24
+ from .resources import files, health
25
25
  from ._streaming import Stream as Stream, AsyncStream as AsyncStream
26
26
  from ._exceptions import ChunkrError, APIStatusError
27
27
  from ._base_client import (
@@ -29,13 +29,14 @@ from ._base_client import (
29
29
  SyncAPIClient,
30
30
  AsyncAPIClient,
31
31
  )
32
- from .resources.task import task
32
+ from .resources.tasks import tasks
33
33
 
34
34
  __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Chunkr", "AsyncChunkr", "Client", "AsyncClient"]
35
35
 
36
36
 
37
37
  class Chunkr(SyncAPIClient):
38
- task: task.TaskResource
38
+ tasks: tasks.TasksResource
39
+ files: files.FilesResource
39
40
  health: health.HealthResource
40
41
  with_raw_response: ChunkrWithRawResponse
41
42
  with_streaming_response: ChunkrWithStreamedResponse
@@ -94,7 +95,8 @@ class Chunkr(SyncAPIClient):
94
95
  _strict_response_validation=_strict_response_validation,
95
96
  )
96
97
 
97
- self.task = task.TaskResource(self)
98
+ self.tasks = tasks.TasksResource(self)
99
+ self.files = files.FilesResource(self)
98
100
  self.health = health.HealthResource(self)
99
101
  self.with_raw_response = ChunkrWithRawResponse(self)
100
102
  self.with_streaming_response = ChunkrWithStreamedResponse(self)
@@ -205,7 +207,8 @@ class Chunkr(SyncAPIClient):
205
207
 
206
208
 
207
209
  class AsyncChunkr(AsyncAPIClient):
208
- task: task.AsyncTaskResource
210
+ tasks: tasks.AsyncTasksResource
211
+ files: files.AsyncFilesResource
209
212
  health: health.AsyncHealthResource
210
213
  with_raw_response: AsyncChunkrWithRawResponse
211
214
  with_streaming_response: AsyncChunkrWithStreamedResponse
@@ -264,7 +267,8 @@ class AsyncChunkr(AsyncAPIClient):
264
267
  _strict_response_validation=_strict_response_validation,
265
268
  )
266
269
 
267
- self.task = task.AsyncTaskResource(self)
270
+ self.tasks = tasks.AsyncTasksResource(self)
271
+ self.files = files.AsyncFilesResource(self)
268
272
  self.health = health.AsyncHealthResource(self)
269
273
  self.with_raw_response = AsyncChunkrWithRawResponse(self)
270
274
  self.with_streaming_response = AsyncChunkrWithStreamedResponse(self)
@@ -376,25 +380,29 @@ class AsyncChunkr(AsyncAPIClient):
376
380
 
377
381
  class ChunkrWithRawResponse:
378
382
  def __init__(self, client: Chunkr) -> None:
379
- self.task = task.TaskResourceWithRawResponse(client.task)
383
+ self.tasks = tasks.TasksResourceWithRawResponse(client.tasks)
384
+ self.files = files.FilesResourceWithRawResponse(client.files)
380
385
  self.health = health.HealthResourceWithRawResponse(client.health)
381
386
 
382
387
 
383
388
  class AsyncChunkrWithRawResponse:
384
389
  def __init__(self, client: AsyncChunkr) -> None:
385
- self.task = task.AsyncTaskResourceWithRawResponse(client.task)
390
+ self.tasks = tasks.AsyncTasksResourceWithRawResponse(client.tasks)
391
+ self.files = files.AsyncFilesResourceWithRawResponse(client.files)
386
392
  self.health = health.AsyncHealthResourceWithRawResponse(client.health)
387
393
 
388
394
 
389
395
  class ChunkrWithStreamedResponse:
390
396
  def __init__(self, client: Chunkr) -> None:
391
- self.task = task.TaskResourceWithStreamingResponse(client.task)
397
+ self.tasks = tasks.TasksResourceWithStreamingResponse(client.tasks)
398
+ self.files = files.FilesResourceWithStreamingResponse(client.files)
392
399
  self.health = health.HealthResourceWithStreamingResponse(client.health)
393
400
 
394
401
 
395
402
  class AsyncChunkrWithStreamedResponse:
396
403
  def __init__(self, client: AsyncChunkr) -> None:
397
- self.task = task.AsyncTaskResourceWithStreamingResponse(client.task)
404
+ self.tasks = tasks.AsyncTasksResourceWithStreamingResponse(client.tasks)
405
+ self.files = files.AsyncFilesResourceWithStreamingResponse(client.files)
398
406
  self.health = health.AsyncHealthResourceWithStreamingResponse(client.health)
399
407
 
400
408
 
chunkr_ai/_files.py CHANGED
@@ -34,7 +34,7 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None:
34
34
  if not is_file_content(obj):
35
35
  prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`"
36
36
  raise RuntimeError(
37
- f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead."
37
+ f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/lumina-ai-inc/chunkr-python/tree/main#file-uploads"
38
38
  ) from None
39
39
 
40
40
 
chunkr_ai/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "chunkr_ai"
4
- __version__ = "0.1.0-alpha.2" # x-release-please-version
4
+ __version__ = "0.1.0-alpha.4" # x-release-please-version
@@ -0,0 +1,122 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Custom helpers for task polling.
5
+
6
+ This module adds `Task.poll()` and `Task.apoll()` methods at runtime to the
7
+ generated `Task` model, without modifying generated code directly.
8
+
9
+ Usage:
10
+ task = client.tasks.get(task_id)
11
+ task = task.poll(client) # blocks until terminal state
12
+
13
+ # async
14
+ task = await async_client.tasks.get(task_id)
15
+ task = await task.apoll(async_client)
16
+ """
17
+
18
+ import time
19
+ import asyncio
20
+ from typing import Protocol, cast
21
+
22
+ from .._types import NOT_GIVEN, NotGiven
23
+ from .._client import Chunkr, AsyncChunkr
24
+ from ..types.task import Task as _Task
25
+ from .._exceptions import ChunkrError
26
+
27
+ TERMINAL_STATUSES = {"Succeeded", "Failed", "Cancelled"}
28
+
29
+
30
+ def _task_poll(
31
+ self: _Task,
32
+ client: Chunkr,
33
+ *,
34
+ interval: float = 0.5,
35
+ timeout: float = 600.0,
36
+ include_chunks: bool | NotGiven = NOT_GIVEN,
37
+ base64_urls: bool | NotGiven = NOT_GIVEN,
38
+ ) -> _Task:
39
+ """Poll the task until it reaches a terminal status.
40
+
41
+ Args:
42
+ client: Synchronous Chunkr client instance.
43
+ interval: Seconds to sleep between polls.
44
+ timeout: Maximum total seconds to wait before raising an error.
45
+ include_chunks: Whether to include chunks in the output response for each poll.
46
+ base64_urls: Whether to return base64 encoded URLs.
47
+ """
48
+ start_time = time.monotonic()
49
+ current: _Task = self
50
+
51
+ class _TasksGetProtocol(Protocol):
52
+ def get(
53
+ self,
54
+ task_id: str,
55
+ *,
56
+ base64_urls: bool | NotGiven = NOT_GIVEN,
57
+ include_chunks: bool | NotGiven = NOT_GIVEN,
58
+ ) -> _Task: ...
59
+
60
+ resource = cast(_TasksGetProtocol, client.tasks)
61
+
62
+ while current.status not in TERMINAL_STATUSES:
63
+ if time.monotonic() - start_time > timeout:
64
+ raise ChunkrError("Task polling timed out.")
65
+
66
+ if interval > 0:
67
+ time.sleep(interval)
68
+
69
+ current = resource.get(
70
+ current.task_id,
71
+ include_chunks=include_chunks,
72
+ base64_urls=base64_urls,
73
+ )
74
+
75
+ return current
76
+
77
+
78
+ async def _task_apoll(
79
+ self: _Task,
80
+ client: AsyncChunkr,
81
+ *,
82
+ interval: float = 0.5,
83
+ timeout: float = 600.0,
84
+ include_chunks: bool | NotGiven = NOT_GIVEN,
85
+ base64_urls: bool | NotGiven = NOT_GIVEN,
86
+ ) -> _Task:
87
+ """Async poll the task until it reaches a terminal status."""
88
+ start_time = time.monotonic()
89
+ current: _Task = self
90
+
91
+ class _AsyncTasksGetProtocol(Protocol):
92
+ async def get(
93
+ self,
94
+ task_id: str,
95
+ *,
96
+ base64_urls: bool | NotGiven = NOT_GIVEN,
97
+ include_chunks: bool | NotGiven = NOT_GIVEN,
98
+ ) -> _Task: ...
99
+
100
+ aresource = cast(_AsyncTasksGetProtocol, client.tasks)
101
+
102
+ while current.status not in TERMINAL_STATUSES:
103
+ if time.monotonic() - start_time > timeout:
104
+ raise ChunkrError("Task polling timed out.")
105
+
106
+ if interval > 0:
107
+ await asyncio.sleep(interval)
108
+
109
+ current = await aresource.get(
110
+ current.task_id,
111
+ include_chunks=include_chunks,
112
+ base64_urls=base64_urls,
113
+ )
114
+
115
+ return current
116
+
117
+
118
+ # Attach methods to the generated Task model
119
+ _Task.poll = _task_poll # type: ignore[attr-defined]
120
+ _Task.apoll = _task_apoll # type: ignore[attr-defined]
121
+
122
+
chunkr_ai/pagination.py CHANGED
@@ -6,7 +6,7 @@ from typing_extensions import override
6
6
 
7
7
  from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage
8
8
 
9
- __all__ = ["SyncTasksPage", "AsyncTasksPage"]
9
+ __all__ = ["SyncTasksPage", "AsyncTasksPage", "SyncFilesPage", "AsyncFilesPage"]
10
10
 
11
11
  _T = TypeVar("_T")
12
12
 
@@ -69,3 +69,63 @@ class AsyncTasksPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
69
69
  return None
70
70
 
71
71
  return PageInfo(params={"cursor": next_cursor})
72
+
73
+
74
+ class SyncFilesPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
75
+ files: List[_T]
76
+ next_cursor: Optional[datetime] = None
77
+ has_more: Optional[bool] = None
78
+ """Whether there are more pages available"""
79
+
80
+ @override
81
+ def _get_page_items(self) -> List[_T]:
82
+ files = self.files
83
+ if not files:
84
+ return []
85
+ return files
86
+
87
+ @override
88
+ def has_next_page(self) -> bool:
89
+ has_more = self.has_more
90
+ if has_more is not None and has_more is False:
91
+ return False
92
+
93
+ return super().has_next_page()
94
+
95
+ @override
96
+ def next_page_info(self) -> Optional[PageInfo]:
97
+ next_cursor = self.next_cursor
98
+ if not next_cursor:
99
+ return None
100
+
101
+ return PageInfo(params={"cursor": next_cursor})
102
+
103
+
104
+ class AsyncFilesPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
105
+ files: List[_T]
106
+ next_cursor: Optional[datetime] = None
107
+ has_more: Optional[bool] = None
108
+ """Whether there are more pages available"""
109
+
110
+ @override
111
+ def _get_page_items(self) -> List[_T]:
112
+ files = self.files
113
+ if not files:
114
+ return []
115
+ return files
116
+
117
+ @override
118
+ def has_next_page(self) -> bool:
119
+ has_more = self.has_more
120
+ if has_more is not None and has_more is False:
121
+ return False
122
+
123
+ return super().has_next_page()
124
+
125
+ @override
126
+ def next_page_info(self) -> Optional[PageInfo]:
127
+ next_cursor = self.next_cursor
128
+ if not next_cursor:
129
+ return None
130
+
131
+ return PageInfo(params={"cursor": next_cursor})
@@ -1,12 +1,20 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
- from .task import (
4
- TaskResource,
5
- AsyncTaskResource,
6
- TaskResourceWithRawResponse,
7
- AsyncTaskResourceWithRawResponse,
8
- TaskResourceWithStreamingResponse,
9
- AsyncTaskResourceWithStreamingResponse,
3
+ from .files import (
4
+ FilesResource,
5
+ AsyncFilesResource,
6
+ FilesResourceWithRawResponse,
7
+ AsyncFilesResourceWithRawResponse,
8
+ FilesResourceWithStreamingResponse,
9
+ AsyncFilesResourceWithStreamingResponse,
10
+ )
11
+ from .tasks import (
12
+ TasksResource,
13
+ AsyncTasksResource,
14
+ TasksResourceWithRawResponse,
15
+ AsyncTasksResourceWithRawResponse,
16
+ TasksResourceWithStreamingResponse,
17
+ AsyncTasksResourceWithStreamingResponse,
10
18
  )
11
19
  from .health import (
12
20
  HealthResource,
@@ -18,12 +26,18 @@ from .health import (
18
26
  )
19
27
 
20
28
  __all__ = [
21
- "TaskResource",
22
- "AsyncTaskResource",
23
- "TaskResourceWithRawResponse",
24
- "AsyncTaskResourceWithRawResponse",
25
- "TaskResourceWithStreamingResponse",
26
- "AsyncTaskResourceWithStreamingResponse",
29
+ "TasksResource",
30
+ "AsyncTasksResource",
31
+ "TasksResourceWithRawResponse",
32
+ "AsyncTasksResourceWithRawResponse",
33
+ "TasksResourceWithStreamingResponse",
34
+ "AsyncTasksResourceWithStreamingResponse",
35
+ "FilesResource",
36
+ "AsyncFilesResource",
37
+ "FilesResourceWithRawResponse",
38
+ "AsyncFilesResourceWithRawResponse",
39
+ "FilesResourceWithStreamingResponse",
40
+ "AsyncFilesResourceWithStreamingResponse",
27
41
  "HealthResource",
28
42
  "AsyncHealthResource",
29
43
  "HealthResourceWithRawResponse",