inferencesh 0.1.24__py3-none-any.whl → 0.2.1__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.
Potentially problematic release.
This version of inferencesh might be problematic. Click here for more details.
- inferencesh/sdk.py +106 -35
- {inferencesh-0.1.24.dist-info → inferencesh-0.2.1.dist-info}/METADATA +5 -1
- inferencesh-0.2.1.dist-info/RECORD +8 -0
- inferencesh-0.1.24.dist-info/RECORD +0 -8
- {inferencesh-0.1.24.dist-info → inferencesh-0.2.1.dist-info}/WHEEL +0 -0
- {inferencesh-0.1.24.dist-info → inferencesh-0.2.1.dist-info}/entry_points.txt +0 -0
- {inferencesh-0.1.24.dist-info → inferencesh-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {inferencesh-0.1.24.dist-info → inferencesh-0.2.1.dist-info}/top_level.txt +0 -0
inferencesh/sdk.py
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
from typing import Optional, Union
|
|
2
|
-
from pydantic import BaseModel, ConfigDict, PrivateAttr, model_validator
|
|
2
|
+
from pydantic import BaseModel, ConfigDict, PrivateAttr, model_validator, Field, field_validator
|
|
3
3
|
import mimetypes
|
|
4
4
|
import os
|
|
5
5
|
import urllib.request
|
|
6
6
|
import urllib.parse
|
|
7
7
|
import tempfile
|
|
8
|
-
from pydantic import Field
|
|
9
8
|
from typing import Any, Dict, List
|
|
10
9
|
|
|
11
10
|
import inspect
|
|
12
11
|
import ast
|
|
13
12
|
import textwrap
|
|
14
13
|
from collections import OrderedDict
|
|
14
|
+
from enum import Enum
|
|
15
|
+
import shutil
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
import hashlib
|
|
18
|
+
from tqdm import tqdm
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
# inspired by https://github.com/pydantic/pydantic/issues/7580
|
|
@@ -102,39 +106,34 @@ class BaseApp(BaseModel):
|
|
|
102
106
|
|
|
103
107
|
class File(BaseModel):
|
|
104
108
|
"""A class representing a file in the inference.sh ecosystem."""
|
|
105
|
-
uri: Optional[str] = None # Original location (URL or file path)
|
|
109
|
+
uri: Optional[str] = Field(default=None) # Original location (URL or file path)
|
|
106
110
|
path: Optional[str] = None # Resolved local file path
|
|
107
111
|
content_type: Optional[str] = None # MIME type of the file
|
|
108
112
|
size: Optional[int] = None # File size in bytes
|
|
109
113
|
filename: Optional[str] = None # Original filename if available
|
|
110
114
|
_tmp_path: Optional[str] = PrivateAttr(default=None) # Internal storage for temporary file path
|
|
111
115
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
def __init__(self, initializer=None, **data):
|
|
117
|
+
if initializer is not None:
|
|
118
|
+
if isinstance(initializer, str):
|
|
119
|
+
data['uri'] = initializer
|
|
120
|
+
elif isinstance(initializer, File):
|
|
121
|
+
data = initializer.model_dump()
|
|
122
|
+
else:
|
|
123
|
+
raise ValueError(f'Invalid input for File: {initializer}')
|
|
124
|
+
super().__init__(**data)
|
|
125
|
+
|
|
126
|
+
@model_validator(mode='before')
|
|
122
127
|
@classmethod
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return value
|
|
131
|
-
elif isinstance(value, dict):
|
|
132
|
-
# If it's a dict, use normal validation
|
|
133
|
-
return cls(**value)
|
|
134
|
-
raise ValueError(f'Invalid input for File: {value}')
|
|
135
|
-
|
|
128
|
+
def convert_str_to_file(cls, values):
|
|
129
|
+
if isinstance(values, str): # Only accept strings
|
|
130
|
+
return {"uri": values}
|
|
131
|
+
elif isinstance(values, dict):
|
|
132
|
+
return values
|
|
133
|
+
raise ValueError(f'Invalid input for File: {values}')
|
|
134
|
+
|
|
136
135
|
@model_validator(mode='after')
|
|
137
|
-
def
|
|
136
|
+
def validate_required_fields(self) -> 'File':
|
|
138
137
|
"""Validate that either uri or path is provided."""
|
|
139
138
|
if not self.uri and not self.path:
|
|
140
139
|
raise ValueError("Either 'uri' or 'path' must be provided")
|
|
@@ -147,7 +146,10 @@ class File(BaseModel):
|
|
|
147
146
|
self.path = os.path.abspath(self.uri)
|
|
148
147
|
elif self.uri:
|
|
149
148
|
self.path = self.uri
|
|
150
|
-
self.
|
|
149
|
+
if self.path:
|
|
150
|
+
self._populate_metadata()
|
|
151
|
+
else:
|
|
152
|
+
raise ValueError("Either 'uri' or 'path' must be provided")
|
|
151
153
|
|
|
152
154
|
def _is_url(self, path: str) -> bool:
|
|
153
155
|
"""Check if the path is a URL."""
|
|
@@ -174,11 +176,22 @@ class File(BaseModel):
|
|
|
174
176
|
}
|
|
175
177
|
req = urllib.request.Request(original_url, headers=headers)
|
|
176
178
|
|
|
177
|
-
# Download the file
|
|
179
|
+
# Download the file with progress bar
|
|
178
180
|
print(f"Downloading URL: {original_url} to {self._tmp_path}")
|
|
179
181
|
try:
|
|
180
|
-
with urllib.request.urlopen(req) as response
|
|
181
|
-
|
|
182
|
+
with urllib.request.urlopen(req) as response:
|
|
183
|
+
total_size = int(response.headers.get('content-length', 0))
|
|
184
|
+
block_size = 1024 # 1 Kibibyte
|
|
185
|
+
|
|
186
|
+
with tqdm(total=total_size, unit='iB', unit_scale=True) as pbar:
|
|
187
|
+
with open(self._tmp_path, 'wb') as out_file:
|
|
188
|
+
while True:
|
|
189
|
+
buffer = response.read(block_size)
|
|
190
|
+
if not buffer:
|
|
191
|
+
break
|
|
192
|
+
out_file.write(buffer)
|
|
193
|
+
pbar.update(len(buffer))
|
|
194
|
+
|
|
182
195
|
self.path = self._tmp_path
|
|
183
196
|
except (urllib.error.URLError, urllib.error.HTTPError) as e:
|
|
184
197
|
raise RuntimeError(f"Failed to download URL {original_url}: {str(e)}")
|
|
@@ -234,13 +247,24 @@ class File(BaseModel):
|
|
|
234
247
|
|
|
235
248
|
def refresh_metadata(self) -> None:
|
|
236
249
|
"""Refresh all metadata from the file."""
|
|
237
|
-
self.
|
|
250
|
+
if os.path.exists(self.path):
|
|
251
|
+
self.content_type = self._guess_content_type()
|
|
252
|
+
self.size = self._get_file_size() # Always update size
|
|
253
|
+
self.filename = self._get_filename()
|
|
238
254
|
|
|
239
255
|
|
|
256
|
+
class ContextMessageRole(str, Enum):
|
|
257
|
+
USER = "user"
|
|
258
|
+
ASSISTANT = "assistant"
|
|
259
|
+
SYSTEM = "system"
|
|
260
|
+
|
|
261
|
+
class Message(BaseModel):
|
|
262
|
+
role: ContextMessageRole
|
|
263
|
+
content: str
|
|
264
|
+
|
|
240
265
|
class ContextMessage(BaseModel):
|
|
241
|
-
role:
|
|
266
|
+
role: ContextMessageRole = Field(
|
|
242
267
|
description="The role of the message",
|
|
243
|
-
enum=["user", "assistant", "system"]
|
|
244
268
|
)
|
|
245
269
|
text: str = Field(
|
|
246
270
|
description="The text content of the message"
|
|
@@ -300,4 +324,51 @@ class LLMInputWithImage(LLMInput):
|
|
|
300
324
|
image: Optional[File] = Field(
|
|
301
325
|
description="The image to use for the model",
|
|
302
326
|
default=None
|
|
303
|
-
)
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
class DownloadDir(str, Enum):
|
|
330
|
+
"""Standard download directories used by the SDK."""
|
|
331
|
+
DATA = "./data" # Persistent storage/cache directory
|
|
332
|
+
TEMP = "./tmp" # Temporary storage directory
|
|
333
|
+
CACHE = "./cache" # Cache directory
|
|
334
|
+
|
|
335
|
+
def download(url: str, directory: Union[str, Path, DownloadDir]) -> str:
|
|
336
|
+
"""Download a file to the specified directory and return its path.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
url: The URL to download from
|
|
340
|
+
directory: The directory to save the file to. Can be a string path,
|
|
341
|
+
Path object, or DownloadDir enum value.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
str: The path to the downloaded file
|
|
345
|
+
"""
|
|
346
|
+
# Convert directory to Path
|
|
347
|
+
dir_path = Path(directory)
|
|
348
|
+
dir_path.mkdir(exist_ok=True)
|
|
349
|
+
|
|
350
|
+
# Create hash directory from URL
|
|
351
|
+
url_hash = hashlib.sha256(url.encode()).hexdigest()[:12]
|
|
352
|
+
hash_dir = dir_path / url_hash
|
|
353
|
+
hash_dir.mkdir(exist_ok=True)
|
|
354
|
+
|
|
355
|
+
# Keep original filename
|
|
356
|
+
filename = os.path.basename(urllib.parse.urlparse(url).path)
|
|
357
|
+
if not filename:
|
|
358
|
+
filename = 'download'
|
|
359
|
+
|
|
360
|
+
output_path = hash_dir / filename
|
|
361
|
+
|
|
362
|
+
# If file exists in directory and it's not a temp directory, return it
|
|
363
|
+
if output_path.exists() and directory != DownloadDir.TEMP:
|
|
364
|
+
return str(output_path)
|
|
365
|
+
|
|
366
|
+
# Download the file
|
|
367
|
+
file = File(url)
|
|
368
|
+
if file.path:
|
|
369
|
+
shutil.copy2(file.path, output_path)
|
|
370
|
+
# Prevent the File instance from deleting its temporary file
|
|
371
|
+
file._tmp_path = None
|
|
372
|
+
return str(output_path)
|
|
373
|
+
|
|
374
|
+
raise RuntimeError(f"Failed to download {url}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: inferencesh
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: inference.sh Python SDK
|
|
5
5
|
Author: Inference Shell Inc.
|
|
6
6
|
Author-email: "Inference Shell Inc." <hello@inference.sh>
|
|
@@ -13,6 +13,10 @@ Requires-Python: >=3.7
|
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
Requires-Dist: pydantic>=2.0.0
|
|
16
|
+
Requires-Dist: tqdm>=4.67.0
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
19
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
16
20
|
Dynamic: author
|
|
17
21
|
Dynamic: license-file
|
|
18
22
|
Dynamic: requires-python
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
inferencesh/__init__.py,sha256=hbKkgHCh0lCdhWyHs3FHHRd8JfLeHkTd1bT4v79Fi8M,192
|
|
2
|
+
inferencesh/sdk.py,sha256=KiD11omxGg0sbCiLSjyJnD1UvfWkUGyk9wB-3wIUcJU,15094
|
|
3
|
+
inferencesh-0.2.1.dist-info/licenses/LICENSE,sha256=OsgqEWIh2el_QMj0y8O1A5Q5Dl-dxqqYbFE6fszuR4s,1086
|
|
4
|
+
inferencesh-0.2.1.dist-info/METADATA,sha256=xD36jCRjFL0guP2oNp6qe9WCsWjDG60VZe4aoN0T220,2756
|
|
5
|
+
inferencesh-0.2.1.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
|
6
|
+
inferencesh-0.2.1.dist-info/entry_points.txt,sha256=6IC-fyozAqW3ljsMLGCXxJ0_ui2Jb-2fLHtoH1RTnEE,45
|
|
7
|
+
inferencesh-0.2.1.dist-info/top_level.txt,sha256=TSMHg3T1ThMl1HGAWmzBClwOYH1ump5neof9BfHIwaA,12
|
|
8
|
+
inferencesh-0.2.1.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
inferencesh/__init__.py,sha256=hbKkgHCh0lCdhWyHs3FHHRd8JfLeHkTd1bT4v79Fi8M,192
|
|
2
|
-
inferencesh/sdk.py,sha256=u8GbIAn5clg_9zHUy99zu2AidgBION4f1KcMJnmjChg,12417
|
|
3
|
-
inferencesh-0.1.24.dist-info/licenses/LICENSE,sha256=OsgqEWIh2el_QMj0y8O1A5Q5Dl-dxqqYbFE6fszuR4s,1086
|
|
4
|
-
inferencesh-0.1.24.dist-info/METADATA,sha256=FM54ThOJtpP14Z1-DN2ZV0Mv0g73md2u8kW15mJfSE4,2612
|
|
5
|
-
inferencesh-0.1.24.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
|
6
|
-
inferencesh-0.1.24.dist-info/entry_points.txt,sha256=6IC-fyozAqW3ljsMLGCXxJ0_ui2Jb-2fLHtoH1RTnEE,45
|
|
7
|
-
inferencesh-0.1.24.dist-info/top_level.txt,sha256=TSMHg3T1ThMl1HGAWmzBClwOYH1ump5neof9BfHIwaA,12
|
|
8
|
-
inferencesh-0.1.24.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|