chunkr-ai 0.0.12__py3-none-any.whl → 0.0.15__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/api/schema.py CHANGED
@@ -2,17 +2,22 @@ from pydantic import BaseModel
2
2
  from typing import Optional, List, Union, Type
3
3
  import json
4
4
 
5
+
5
6
  class Property(BaseModel):
6
7
  name: str
7
8
  prop_type: str
8
9
  description: Optional[str] = None
9
10
  default: Optional[str] = None
10
11
 
12
+
11
13
  class JsonSchema(BaseModel):
12
14
  title: str
13
15
  properties: List[Property]
14
16
 
15
- def from_pydantic(pydantic: Union[BaseModel, Type[BaseModel]], current_depth: int = 0) -> dict:
17
+
18
+ def from_pydantic(
19
+ pydantic: Union[BaseModel, Type[BaseModel]], current_depth: int = 0
20
+ ) -> dict:
16
21
  """Convert a Pydantic model to a Chunk json schema."""
17
22
  MAX_DEPTH = 5
18
23
  model = pydantic if isinstance(pydantic, type) else pydantic.__class__
@@ -21,108 +26,111 @@ def from_pydantic(pydantic: Union[BaseModel, Type[BaseModel]], current_depth: in
21
26
 
22
27
  def get_enum_description(details: dict) -> str:
23
28
  """Get description including enum values if they exist"""
24
- description = details.get('description', '')
25
-
29
+ description = details.get("description", "")
30
+
26
31
  # First check if this is a direct enum
27
- if 'enum' in details:
28
- enum_values = details['enum']
29
- enum_str = '\nAllowed values:\n' + '\n'.join(f'- {val}' for val in enum_values)
32
+ if "enum" in details:
33
+ enum_values = details["enum"]
34
+ enum_str = "\nAllowed values:\n" + "\n".join(
35
+ f"- {val}" for val in enum_values
36
+ )
30
37
  return f"{description}{enum_str}"
31
-
38
+
32
39
  # Then check if it's a reference to an enum
33
- if '$ref' in details:
34
- ref_schema = resolve_ref(details['$ref'], schema.get('$defs', {}))
35
- if 'enum' in ref_schema:
36
- enum_values = ref_schema['enum']
37
- enum_str = '\nAllowed values:\n' + '\n'.join(f'- {val}' for val in enum_values)
40
+ if "$ref" in details:
41
+ ref_schema = resolve_ref(details["$ref"], schema.get("$defs", {}))
42
+ if "enum" in ref_schema:
43
+ enum_values = ref_schema["enum"]
44
+ enum_str = "\nAllowed values:\n" + "\n".join(
45
+ f"- {val}" for val in enum_values
46
+ )
38
47
  return f"{description}{enum_str}"
39
-
48
+
40
49
  return description
41
50
 
42
51
  def resolve_ref(ref: str, definitions: dict) -> dict:
43
52
  """Resolve a $ref reference to its actual schema"""
44
- if not ref.startswith('#/$defs/'):
53
+ if not ref.startswith("#/$defs/"):
45
54
  return {}
46
- ref_name = ref[len('#/$defs/'):]
55
+ ref_name = ref[len("#/$defs/") :]
47
56
  return definitions.get(ref_name, {})
48
57
 
49
58
  def get_nested_schema(field_schema: dict, depth: int) -> dict:
50
59
  if depth >= MAX_DEPTH:
51
60
  return {}
52
-
61
+
53
62
  # If there's a $ref, resolve it first
54
- if '$ref' in field_schema:
55
- field_schema = resolve_ref(field_schema['$ref'], schema.get('$defs', {}))
56
-
63
+ if "$ref" in field_schema:
64
+ field_schema = resolve_ref(field_schema["$ref"], schema.get("$defs", {}))
65
+
57
66
  nested_props = {}
58
- if field_schema.get('type') == 'object':
59
- for name, details in field_schema.get('properties', {}).items():
60
- if details.get('type') == 'object' or '$ref' in details:
67
+ if field_schema.get("type") == "object":
68
+ for name, details in field_schema.get("properties", {}).items():
69
+ if details.get("type") == "object" or "$ref" in details:
61
70
  ref_schema = details
62
- if '$ref' in details:
63
- ref_schema = resolve_ref(details['$ref'], schema.get('$defs', {}))
71
+ if "$ref" in details:
72
+ ref_schema = resolve_ref(
73
+ details["$ref"], schema.get("$defs", {})
74
+ )
64
75
  nested_schema = get_nested_schema(ref_schema, depth + 1)
65
76
  nested_props[name] = {
66
- 'type': 'object',
67
- 'description': get_enum_description(details),
68
- 'properties': nested_schema
77
+ "type": "object",
78
+ "description": get_enum_description(details),
79
+ "properties": nested_schema,
69
80
  }
70
81
  else:
71
82
  nested_props[name] = {
72
- 'type': details.get('type', 'string'),
73
- 'description': get_enum_description(details)
83
+ "type": details.get("type", "string"),
84
+ "description": get_enum_description(details),
74
85
  }
75
86
  return nested_props
76
87
 
77
- for name, details in schema.get('properties', {}).items():
88
+ for name, details in schema.get("properties", {}).items():
78
89
  # Handle arrays
79
- if details.get('type') == 'array':
80
- items = details.get('items', {})
81
- if '$ref' in items:
82
- items = resolve_ref(items['$ref'], schema.get('$defs', {}))
83
-
90
+ if details.get("type") == "array":
91
+ items = details.get("items", {})
92
+ if "$ref" in items:
93
+ items = resolve_ref(items["$ref"], schema.get("$defs", {}))
94
+
84
95
  # Get nested schema for array items
85
96
  item_schema = get_nested_schema(items, current_depth)
86
97
  description = get_enum_description(details)
87
-
98
+
88
99
  if item_schema:
89
100
  description = f"{description}\nList items schema:\n{json.dumps(item_schema, indent=2)}"
90
-
91
- prop = Property(
92
- name=name,
93
- prop_type='list',
94
- description=description
95
- )
101
+
102
+ prop = Property(name=name, prop_type="list", description=description)
96
103
  # Handle objects and references
97
- elif details.get('type') == 'object' or '$ref' in details:
98
- prop_type = 'object'
104
+ elif details.get("type") == "object" or "$ref" in details:
105
+ prop_type = "object"
99
106
  ref_schema = details
100
- if '$ref' in details:
101
- ref_schema = resolve_ref(details['$ref'], schema.get('$defs', {}))
102
-
107
+ if "$ref" in details:
108
+ ref_schema = resolve_ref(details["$ref"], schema.get("$defs", {}))
109
+
103
110
  nested_schema = get_nested_schema(ref_schema, current_depth)
104
-
111
+
105
112
  prop = Property(
106
113
  name=name,
107
114
  prop_type=prop_type,
108
115
  description=get_enum_description(details),
109
- properties=nested_schema
116
+ properties=nested_schema,
110
117
  )
111
-
118
+
112
119
  # Handle primitive types
113
120
  else:
114
121
  prop = Property(
115
122
  name=name,
116
- prop_type=details.get('type', 'string'),
123
+ prop_type=details.get("type", "string"),
117
124
  description=get_enum_description(details),
118
- default=str(details.get('default')) if details.get('default') is not None else None
125
+ default=str(details.get("default"))
126
+ if details.get("default") is not None
127
+ else None,
119
128
  )
120
-
129
+
121
130
  properties.append(prop)
122
-
131
+
123
132
  json_schema = JsonSchema(
124
- title=schema.get('title', model.__name__),
125
- properties=properties
133
+ title=schema.get("title", model.__name__), properties=properties
126
134
  )
127
-
128
- return json_schema.model_dump(mode="json", exclude_none=True)
135
+
136
+ return json_schema.model_dump(mode="json", exclude_none=True)
chunkr_ai/api/task.py CHANGED
@@ -3,50 +3,56 @@ from .misc import prepare_upload_data
3
3
  from .task_base import TaskBase
4
4
  import time
5
5
 
6
+
6
7
  class TaskResponse(TaskBase):
7
8
  def _poll_request(self) -> dict:
8
9
  while True:
9
10
  try:
10
- r = self._client._session.get(self.task_url, headers=self._client._headers())
11
+ if not self.task_url:
12
+ raise ValueError("Task URL not found in response")
13
+ if not self._client._session:
14
+ raise ValueError("Client session not found")
15
+ r = self._client._session.get(
16
+ self.task_url, headers=self._client._headers()
17
+ )
11
18
  r.raise_for_status()
12
19
  return r.json()
13
20
  except (ConnectionError, TimeoutError) as _:
14
21
  print("Connection error while polling the task, retrying...")
15
22
  time.sleep(0.5)
16
- except Exception as e:
23
+ except Exception:
17
24
  raise
18
25
 
19
- def poll(self) -> 'TaskResponse':
20
- if not self.task_url:
21
- raise ValueError("Task URL not found in response")
26
+ def poll(self) -> "TaskResponse":
22
27
  while True:
23
- response = self._poll_request_sync()
28
+ response = self._poll_request()
24
29
  updated_task = TaskResponse(**response).with_client(self._client)
25
30
  self.__dict__.update(updated_task.__dict__)
26
31
  if result := self._check_status():
27
32
  return result
28
33
  time.sleep(0.5)
29
-
30
- def update(self, config: Configuration) -> 'TaskResponse':
34
+
35
+ def update(self, config: Configuration) -> "TaskResponse":
31
36
  if not self.task_url:
32
37
  raise ValueError("Task URL not found")
38
+ if not self._client._session:
39
+ raise ValueError("Client session not found")
33
40
  files = prepare_upload_data(None, config)
34
41
  r = self._client._session.patch(
35
- f"{self.task_url}",
36
- files=files,
37
- headers=self._client._headers()
42
+ self.task_url, files=files, headers=self._client._headers()
38
43
  )
39
44
  r.raise_for_status()
40
45
  updated = TaskResponse(**r.json()).with_client(self._client)
41
46
  self.__dict__.update(updated.__dict__)
42
47
  return self.poll()
43
-
48
+
44
49
  def cancel(self):
45
50
  if not self.task_url:
46
51
  raise ValueError("Task URL not found")
52
+ if not self._client._session:
53
+ raise ValueError("Client session not found")
47
54
  r = self._client._session.get(
48
- f"{self.task_url}/cancel",
49
- headers=self._client._headers()
55
+ f"{self.task_url}/cancel", headers=self._client._headers()
50
56
  )
51
57
  r.raise_for_status()
52
58
  self.poll()
@@ -54,8 +60,7 @@ class TaskResponse(TaskBase):
54
60
  def delete(self):
55
61
  if not self.task_url:
56
62
  raise ValueError("Task URL not found")
57
- r = self._client._session.delete(
58
- self.task_url,
59
- headers=self._client._headers()
60
- )
63
+ if not self._client._session:
64
+ raise ValueError("Client session not found")
65
+ r = self._client._session.delete(self.task_url, headers=self._client._headers())
61
66
  r.raise_for_status()
@@ -3,21 +3,28 @@ from .misc import prepare_upload_data
3
3
  from .task_base import TaskBase
4
4
  import asyncio
5
5
 
6
+
6
7
  class TaskResponseAsync(TaskBase):
7
8
  async def _poll_request(self) -> dict:
8
9
  try:
9
- r = await self._client._client.get(self.task_url, headers=self._client._headers())
10
+ if not self._client._client:
11
+ raise ValueError("Client not found")
12
+ r = await self._client._client.get(
13
+ self.task_url, headers=self._client._headers()
14
+ )
10
15
  r.raise_for_status()
11
16
  return r.json()
12
17
  except (ConnectionError, TimeoutError) as _:
13
18
  print("Connection error while polling the task, retrying...")
14
19
  await asyncio.sleep(0.5)
15
- except Exception as e:
20
+ except Exception:
16
21
  raise
17
22
 
18
- async def poll(self) -> 'TaskResponseAsync':
23
+ async def poll(self) -> "TaskResponseAsync":
19
24
  if not self.task_url:
20
25
  raise ValueError("Task URL not found")
26
+ if not self._client._client:
27
+ raise ValueError("Client not found")
21
28
  while True:
22
29
  j = await self._poll_request()
23
30
  updated = TaskResponseAsync(**j).with_client(self._client)
@@ -26,11 +33,15 @@ class TaskResponseAsync(TaskBase):
26
33
  return res
27
34
  await asyncio.sleep(0.5)
28
35
 
29
- async def update(self, config: Configuration) -> 'TaskResponseAsync':
36
+ async def update(self, config: Configuration) -> "TaskResponseAsync":
30
37
  if not self.task_url:
31
38
  raise ValueError("Task URL not found")
39
+ if not self._client._client:
40
+ raise ValueError("Client not found")
32
41
  f = prepare_upload_data(None, config)
33
- r = await self._client._client.patch(self.task_url, files=f, headers=self._client._headers())
42
+ r = await self._client._client.patch(
43
+ self.task_url, files=f, headers=self._client._headers()
44
+ )
34
45
  r.raise_for_status()
35
46
  updated = TaskResponseAsync(**r.json()).with_client(self._client)
36
47
  self.__dict__.update(updated.__dict__)
@@ -39,12 +50,20 @@ class TaskResponseAsync(TaskBase):
39
50
  async def cancel(self):
40
51
  if not self.task_url:
41
52
  raise ValueError("Task URL not found")
42
- r = await self._client._client.get(f"{self.task_url}/cancel", headers=self._client._headers())
53
+ if not self._client._client:
54
+ raise ValueError("Client not found")
55
+ r = await self._client._client.get(
56
+ f"{self.task_url}/cancel", headers=self._client._headers()
57
+ )
43
58
  r.raise_for_status()
44
59
  return await self.poll()
45
60
 
46
61
  async def delete(self):
47
62
  if not self.task_url:
48
63
  raise ValueError("Task URL not found")
49
- r = await self._client._client.delete(self.task_url, headers=self._client._headers())
50
- r.raise_for_status()
64
+ if not self._client._client:
65
+ raise ValueError("Client not found")
66
+ r = await self._client._client.delete(
67
+ self.task_url, headers=self._client._headers()
68
+ )
69
+ r.raise_for_status()
@@ -1,12 +1,12 @@
1
- from .config import Configuration
1
+ from .config import Configuration, Status, OutputResponse
2
2
  from .protocol import ChunkrClientProtocol
3
- from ..models import Status, OutputResponse
4
3
  from abc import ABC, abstractmethod
5
- from typing import TypeVar, Optional, Generic, Union
4
+ from typing import TypeVar, Optional, Generic
6
5
  from pydantic import BaseModel, PrivateAttr
7
6
  from datetime import datetime
8
7
 
9
- T = TypeVar('T', bound='TaskBase')
8
+ T = TypeVar("T", bound="TaskBase")
9
+
10
10
 
11
11
  class TaskBase(BaseModel, ABC, Generic[T]):
12
12
  configuration: Configuration
@@ -23,7 +23,7 @@ class TaskBase(BaseModel, ABC, Generic[T]):
23
23
  status: Status
24
24
  task_id: str
25
25
  task_url: Optional[str]
26
- _client: Optional[Union[ChunkrClientProtocol]] = PrivateAttr(default=None)
26
+ _client: Optional[ChunkrClientProtocol] = PrivateAttr(default=None)
27
27
 
28
28
  @abstractmethod
29
29
  def _poll_request(self) -> dict:
@@ -50,7 +50,7 @@ class TaskBase(BaseModel, ABC, Generic[T]):
50
50
  """Delete the task."""
51
51
  pass
52
52
 
53
- def with_client(self, client: Union[ChunkrClientProtocol]) -> T:
53
+ def with_client(self, client: ChunkrClientProtocol) -> T:
54
54
  self._client = client
55
55
  return self
56
56
 
chunkr_ai/models.py CHANGED
@@ -24,26 +24,25 @@ from .api.task import TaskResponse
24
24
  from .api.task_async import TaskResponseAsync
25
25
 
26
26
  __all__ = [
27
- 'BoundingBox',
28
- 'Chunk',
29
- 'ChunkProcessing',
30
- 'Configuration',
31
- 'CroppingStrategy',
32
- 'ExtractedJson',
33
- 'GenerationConfig',
34
- 'GenerationStrategy',
35
- 'JsonSchema',
36
- 'LlmConfig',
37
- 'Model',
38
- 'OCRResult',
39
- 'OcrStrategy',
40
- 'OutputResponse',
41
- 'Property',
42
- 'Segment',
43
- 'SegmentProcessing',
44
- 'SegmentType',
45
- 'SegmentationStrategy',
46
- 'Status',
47
- 'TaskResponse',
48
- 'TaskResponseAsync',
27
+ "BoundingBox",
28
+ "Chunk",
29
+ "ChunkProcessing",
30
+ "Configuration",
31
+ "CroppingStrategy",
32
+ "ExtractedJson",
33
+ "GenerationConfig",
34
+ "GenerationStrategy",
35
+ "JsonSchema",
36
+ "Model",
37
+ "OCRResult",
38
+ "OcrStrategy",
39
+ "OutputResponse",
40
+ "Property",
41
+ "Segment",
42
+ "SegmentProcessing",
43
+ "SegmentType",
44
+ "SegmentationStrategy",
45
+ "Status",
46
+ "TaskResponse",
47
+ "TaskResponseAsync",
49
48
  ]
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: chunkr-ai
3
- Version: 0.0.12
3
+ Version: 0.0.15
4
4
  Summary: Python client for Chunkr: open source document intelligence
5
5
  Author-email: Ishaan Kapoor <ishaan@lumina.sh>
6
6
  Project-URL: Homepage, https://chunkr.ai
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: httpx>=0.25.0
10
- Requires-Dist: httpx>=0.25.0
11
10
  Requires-Dist: pillow>=10.0.0
12
11
  Requires-Dist: pydantic>=2.0.0
13
12
  Requires-Dist: pytest-asyncio>=0.21.0
@@ -81,7 +80,7 @@ async def process_document():
81
80
  # If you want to upload without waiting for processing
82
81
  task = await chunkr.start_upload("document.pdf")
83
82
  # ... do other things ...
84
- await task.poll_async() # Check status when needed
83
+ await task.poll() # Check status when needed
85
84
  ```
86
85
 
87
86
  ### Additional Features
@@ -0,0 +1,21 @@
1
+ chunkr_ai/__init__.py,sha256=q5YosvCNXPNGjV10pZY1gcvdosqUh38nVQTQA9g8EuM,110
2
+ chunkr_ai/models.py,sha256=KPcZDkRAziyke33ciEZymvCA28_QuSozoR11fXtjats,886
3
+ chunkr_ai/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ chunkr_ai/api/api.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ chunkr_ai/api/auth.py,sha256=hlv0GiUmlsbFO1wLL9sslqOnsBSoBqkL_6Mk2SDvxgE,413
6
+ chunkr_ai/api/base.py,sha256=QvHl8FInKHYKPLWDeEPpCchB1uktzOwTW7iPnyXccUc,6449
7
+ chunkr_ai/api/chunkr.py,sha256=0extAWVeZtI7B-g14smTfFZD_csdJNCcVNXx2_L69OQ,2617
8
+ chunkr_ai/api/chunkr_async.py,sha256=aa0s_tnYoujHBsfe8uLiPpVEnb2l9A3CXwPP34w9Mk8,4127
9
+ chunkr_ai/api/chunkr_base.py,sha256=k34Dyt1f21NBWZvZJ3w6Svvpg4SKnzr2ldGQ4ib96Wc,4951
10
+ chunkr_ai/api/config.py,sha256=0vbPMAYvs-tb7zIDIoSFQekz8bK0-iw3iz7BqMnHbDI,4930
11
+ chunkr_ai/api/misc.py,sha256=Dk8lWrgX-pZYcvcP2SzRhCdyBwMSSTrE8y7MzVQnScw,4878
12
+ chunkr_ai/api/protocol.py,sha256=lxIR_qoCA2a1OXjpq3LrWMdS0jRHct1bEmBlUzV8gvE,526
13
+ chunkr_ai/api/schema.py,sha256=yYesvueGgtmRa7Fi_Tpdv8A2bzHlx-B-5DxRAPlaDHo,4926
14
+ chunkr_ai/api/task.py,sha256=28J4dR8BDjvtkh3CQjW_YUEkgPXhCHBGu0wH6AQKKuE,2474
15
+ chunkr_ai/api/task_async.py,sha256=K5hTEOnmD42snPZg_JtJsVWg6QBUFZ1aBz1Abwv58-A,2529
16
+ chunkr_ai/api/task_base.py,sha256=esEawvntjzfaPnV78KW2IbIFqI74-eJV_g70BVIubbY,2329
17
+ chunkr_ai-0.0.15.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ chunkr_ai-0.0.15.dist-info/METADATA,sha256=097bhYg2V6cuxLkiVmiSTNuGzlBvGFV1Hw2bFWxBYKc,4839
19
+ chunkr_ai-0.0.15.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
20
+ chunkr_ai-0.0.15.dist-info/top_level.txt,sha256=0IZY7PZIiS8bw5r4NUQRUQ-ATi-L_3vLQVq3ZLouOW8,10
21
+ chunkr_ai-0.0.15.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- chunkr_ai/__init__.py,sha256=eXygrEhGxxIHXNYIlHF2eied8rGsx2RphgR8Wo4lRyo,110
2
- chunkr_ai/models.py,sha256=-dbwtTHTcGhH3LXUdVUPkobbPoeFNXRizeAW8BCGSkE,903
3
- chunkr_ai/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- chunkr_ai/api/auth.py,sha256=iSd5Jek2BFaHGw9HY-RrqgwP56BHFU0xbSuJS4fU6AA,425
5
- chunkr_ai/api/chunkr.py,sha256=0qpV9b1hOpDhA9EuKkXW9X_laUmw5NY3ZYq0cUOTbww,5190
6
- chunkr_ai/api/chunkr_async.py,sha256=ZkLBrn4cqzu3sqMfS8cfZZgSvpdyQuWZP95lfGxuHx0,4900
7
- chunkr_ai/api/chunkr_base.py,sha256=IYO0pmoL02GchIggj6_Q5nvtAUoOvYAAvT7VLFU6scY,2506
8
- chunkr_ai/api/config.py,sha256=joTn7jiOlJXTwwza-jHauLV-39CMzaxZVGB9JBm8Cok,4862
9
- chunkr_ai/api/misc.py,sha256=9vnfrbJ7sFlZqwEIQ4NTMb5rhPOmETT7e1jR-b42PXM,4977
10
- chunkr_ai/api/protocol.py,sha256=XKS9RmtvBpJItYhPg18qlOCKpaSHdOuQTRSUxAdUz2g,479
11
- chunkr_ai/api/schema.py,sha256=OeLOhBRXeRBgEImg0Q6O9Z10ojT6aSEVvwnDR8UeENo,4971
12
- chunkr_ai/api/task.py,sha256=4insrdGEVzBHs1ejZvde8bbEetVzgJELa47UjhfBqCA,2116
13
- chunkr_ai/api/task_async.py,sha256=LqS-LL-mCOgfGsgvuSXhKkSEUM6MMro-EZHl_ZedQQk,1998
14
- chunkr_ai/api/task_base.py,sha256=iS5UVIDEPIiDoWrn21Oh_dQurkd_hvKQ8ng32j6sGoA,2369
15
- chunkr_ai-0.0.12.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- chunkr_ai-0.0.12.dist-info/METADATA,sha256=dfo9myRizW2A5W0H6FpIoBzHa4QxmEe3lsedPYhwjXM,4874
17
- chunkr_ai-0.0.12.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
18
- chunkr_ai-0.0.12.dist-info/top_level.txt,sha256=0IZY7PZIiS8bw5r4NUQRUQ-ATi-L_3vLQVq3ZLouOW8,10
19
- chunkr_ai-0.0.12.dist-info/RECORD,,