truefoundry 0.2.10__py3-none-any.whl → 0.3.0__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 truefoundry might be problematic. Click here for more details.
- truefoundry/__init__.py +1 -0
- truefoundry/autodeploy/cli.py +31 -18
- truefoundry/deploy/__init__.py +112 -1
- truefoundry/deploy/auto_gen/models.py +1714 -0
- truefoundry/deploy/builder/__init__.py +134 -0
- truefoundry/deploy/builder/builders/__init__.py +22 -0
- truefoundry/deploy/builder/builders/dockerfile.py +57 -0
- truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py +46 -0
- truefoundry/deploy/builder/builders/tfy_notebook_buildpack/dockerfile_template.py +66 -0
- truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py +44 -0
- truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py +158 -0
- truefoundry/deploy/builder/docker_service.py +168 -0
- truefoundry/deploy/cli/cli.py +21 -26
- truefoundry/deploy/cli/commands/__init__.py +18 -0
- truefoundry/deploy/cli/commands/apply_command.py +52 -0
- truefoundry/deploy/cli/commands/build_command.py +45 -0
- truefoundry/deploy/cli/commands/build_logs_command.py +89 -0
- truefoundry/deploy/cli/commands/create_command.py +75 -0
- truefoundry/deploy/cli/commands/delete_command.py +77 -0
- truefoundry/deploy/cli/commands/deploy_command.py +102 -0
- truefoundry/deploy/cli/commands/get_command.py +216 -0
- truefoundry/deploy/cli/commands/list_command.py +171 -0
- truefoundry/deploy/cli/commands/login_command.py +33 -0
- truefoundry/deploy/cli/commands/logout_command.py +20 -0
- truefoundry/deploy/cli/commands/logs_command.py +134 -0
- truefoundry/deploy/cli/commands/patch_application_command.py +81 -0
- truefoundry/deploy/cli/commands/patch_command.py +70 -0
- truefoundry/deploy/cli/commands/redeploy_command.py +41 -0
- truefoundry/deploy/cli/commands/terminate_comand.py +44 -0
- truefoundry/deploy/cli/commands/trigger_command.py +145 -0
- truefoundry/deploy/cli/config.py +10 -0
- truefoundry/deploy/cli/console.py +5 -0
- truefoundry/deploy/cli/const.py +12 -0
- truefoundry/deploy/cli/display_util.py +118 -0
- truefoundry/deploy/cli/util.py +129 -0
- truefoundry/deploy/core/__init__.py +7 -0
- truefoundry/deploy/core/login.py +9 -0
- truefoundry/deploy/core/logout.py +5 -0
- truefoundry/deploy/function_service/__init__.py +3 -0
- truefoundry/deploy/function_service/__main__.py +27 -0
- truefoundry/deploy/function_service/app.py +92 -0
- truefoundry/deploy/function_service/build.py +45 -0
- truefoundry/deploy/function_service/remote/__init__.py +6 -0
- truefoundry/deploy/function_service/remote/context.py +3 -0
- truefoundry/deploy/function_service/remote/method.py +67 -0
- truefoundry/deploy/function_service/remote/remote.py +144 -0
- truefoundry/deploy/function_service/route.py +137 -0
- truefoundry/deploy/function_service/service.py +113 -0
- truefoundry/deploy/function_service/utils.py +53 -0
- truefoundry/deploy/io/__init__.py +0 -0
- truefoundry/deploy/io/output_callback.py +23 -0
- truefoundry/deploy/io/rich_output_callback.py +27 -0
- truefoundry/deploy/json_util.py +7 -0
- truefoundry/deploy/lib/__init__.py +0 -0
- truefoundry/deploy/lib/auth/auth_service_client.py +181 -0
- truefoundry/deploy/lib/auth/credential_file_manager.py +115 -0
- truefoundry/deploy/lib/auth/credential_provider.py +131 -0
- truefoundry/deploy/lib/auth/servicefoundry_session.py +59 -0
- truefoundry/deploy/lib/clients/__init__.py +0 -0
- truefoundry/deploy/lib/clients/servicefoundry_client.py +746 -0
- truefoundry/deploy/lib/clients/shell_client.py +13 -0
- truefoundry/deploy/lib/clients/utils.py +41 -0
- truefoundry/deploy/lib/const.py +43 -0
- truefoundry/deploy/lib/dao/__init__.py +0 -0
- truefoundry/deploy/lib/dao/application.py +263 -0
- truefoundry/deploy/lib/dao/apply.py +80 -0
- truefoundry/deploy/lib/dao/version.py +33 -0
- truefoundry/deploy/lib/dao/workspace.py +71 -0
- truefoundry/deploy/lib/exceptions.py +26 -0
- truefoundry/deploy/lib/logs_utils.py +43 -0
- truefoundry/deploy/lib/messages.py +12 -0
- truefoundry/deploy/lib/model/__init__.py +0 -0
- truefoundry/deploy/lib/model/entity.py +400 -0
- truefoundry/deploy/lib/session.py +158 -0
- truefoundry/deploy/lib/util.py +90 -0
- truefoundry/deploy/lib/win32.py +129 -0
- truefoundry/deploy/v2/__init__.py +0 -0
- truefoundry/deploy/v2/lib/__init__.py +3 -0
- truefoundry/deploy/v2/lib/deploy.py +283 -0
- truefoundry/deploy/v2/lib/deploy_workflow.py +295 -0
- truefoundry/deploy/v2/lib/deployable_patched_models.py +86 -0
- truefoundry/deploy/v2/lib/models.py +53 -0
- truefoundry/deploy/v2/lib/patched_models.py +479 -0
- truefoundry/deploy/v2/lib/source.py +267 -0
- truefoundry/langchain/__init__.py +12 -1
- truefoundry/langchain/deprecated.py +302 -0
- truefoundry/langchain/truefoundry_chat.py +130 -0
- truefoundry/langchain/truefoundry_embeddings.py +171 -0
- truefoundry/langchain/truefoundry_llm.py +106 -0
- truefoundry/langchain/utils.py +85 -0
- truefoundry/logger.py +17 -0
- truefoundry/pydantic_v1.py +5 -0
- truefoundry/python_deploy_codegen.py +132 -0
- truefoundry/version.py +6 -0
- truefoundry/workflow/__init__.py +19 -0
- truefoundry/workflow/container_task.py +12 -0
- truefoundry/workflow/example/deploy.sh +1 -0
- truefoundry/workflow/example/hello_world_package/workflow.py +20 -0
- truefoundry/workflow/example/package/test_workflow.py +152 -0
- truefoundry/workflow/example/truefoundry.yaml +9 -0
- truefoundry/workflow/example/workflow.yaml +116 -0
- truefoundry/workflow/map_task.py +45 -0
- truefoundry/workflow/python_task.py +32 -0
- truefoundry/workflow/task.py +50 -0
- truefoundry/workflow/workflow.py +114 -0
- {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/METADATA +27 -7
- truefoundry-0.3.0.dist-info/RECORD +136 -0
- truefoundry/deploy/cli/deploy.py +0 -165
- truefoundry/deploy/cli/version.py +0 -6
- truefoundry-0.2.10.dist-info/RECORD +0 -38
- {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/WHEEL +0 -0
- {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tarfile
|
|
3
|
+
import tempfile
|
|
4
|
+
import time
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Callable, List, Optional
|
|
7
|
+
|
|
8
|
+
import gitignorefile
|
|
9
|
+
from tqdm import tqdm
|
|
10
|
+
|
|
11
|
+
from truefoundry.deploy import builder
|
|
12
|
+
from truefoundry.deploy.auto_gen import models
|
|
13
|
+
from truefoundry.deploy.builder.docker_service import (
|
|
14
|
+
pull_docker_image,
|
|
15
|
+
push_docker_image,
|
|
16
|
+
push_docker_image_with_latest_tag,
|
|
17
|
+
)
|
|
18
|
+
from truefoundry.deploy.lib.clients.servicefoundry_client import (
|
|
19
|
+
ServiceFoundryServiceClient,
|
|
20
|
+
)
|
|
21
|
+
from truefoundry.deploy.lib.dao import workspace as workspace_lib
|
|
22
|
+
from truefoundry.deploy.v2.lib.patched_models import Image, RemoteSource
|
|
23
|
+
from truefoundry.logger import logger
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _human_readable_size(num_bytes: float) -> str:
|
|
27
|
+
units = ["B", "KiB", "MiB", "GiB"]
|
|
28
|
+
i = 0
|
|
29
|
+
amount = num_bytes
|
|
30
|
+
while amount >= 1024 and i < len(units) - 1:
|
|
31
|
+
amount /= 1024.0
|
|
32
|
+
i += 1
|
|
33
|
+
amount = round(amount, 2)
|
|
34
|
+
return f"{amount} {units[i]}"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _make_tarfile(
|
|
38
|
+
output_filename: str,
|
|
39
|
+
source_dir: str,
|
|
40
|
+
additional_directories: List[str],
|
|
41
|
+
is_path_ignored: Optional[Callable[[str], bool]] = None,
|
|
42
|
+
) -> None:
|
|
43
|
+
if not is_path_ignored:
|
|
44
|
+
# if no callback handler present assume that every file needs to be added
|
|
45
|
+
is_path_ignored = lambda *_: False # noqa: E731
|
|
46
|
+
|
|
47
|
+
with tarfile.open(output_filename, "w:gz") as tar:
|
|
48
|
+
source_dirs = [source_dir]
|
|
49
|
+
source_dirs.extend(additional_directories)
|
|
50
|
+
for source_dir in source_dirs:
|
|
51
|
+
_add_files_in_tar(
|
|
52
|
+
is_path_ignored=is_path_ignored,
|
|
53
|
+
source_dir=source_dir,
|
|
54
|
+
tar=tar,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _add_files_in_tar(
|
|
59
|
+
is_path_ignored: Callable[[str], bool],
|
|
60
|
+
source_dir: str,
|
|
61
|
+
tar: tarfile.TarFile,
|
|
62
|
+
) -> None:
|
|
63
|
+
for root, dirs, files in tqdm(
|
|
64
|
+
os.walk(source_dir, topdown=True), desc="Packaging source code"
|
|
65
|
+
):
|
|
66
|
+
if is_path_ignored(root):
|
|
67
|
+
logger.debug("Ignoring directory %s", root)
|
|
68
|
+
|
|
69
|
+
# NOTE: we can safely ignore going through the sub-dir
|
|
70
|
+
# if root itself is excluded.
|
|
71
|
+
dirs.clear()
|
|
72
|
+
continue
|
|
73
|
+
logger.debug("Adding contents of the directory %s", root)
|
|
74
|
+
files_added_count = 0
|
|
75
|
+
for file in files:
|
|
76
|
+
file_path = os.path.join(root, file)
|
|
77
|
+
if not is_path_ignored(file_path):
|
|
78
|
+
arcname = os.path.relpath(file_path, source_dir)
|
|
79
|
+
tar.add(file_path, arcname=arcname)
|
|
80
|
+
logger.debug("Adding %s with arcname %r", file_path, arcname)
|
|
81
|
+
files_added_count += 1
|
|
82
|
+
|
|
83
|
+
if not files_added_count:
|
|
84
|
+
# If no files were added and the directory was not ignored too, then we
|
|
85
|
+
# want to add the directory as an empty dir
|
|
86
|
+
arcname = os.path.relpath(root, source_dir)
|
|
87
|
+
tar.add(root, arcname=arcname, recursive=False)
|
|
88
|
+
logger.debug("Adding empty directory %s with arcname %r", root, arcname)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _get_callback_handler_to_ignore_file_path(
|
|
92
|
+
source_dir: str,
|
|
93
|
+
) -> Optional[Callable[[str], bool]]:
|
|
94
|
+
ignorefile_path = os.path.join(source_dir, ".tfyignore")
|
|
95
|
+
if os.path.exists(ignorefile_path):
|
|
96
|
+
logger.info(".tfyignore file found in %s", source_dir)
|
|
97
|
+
return gitignorefile.parse(path=ignorefile_path, base_path=source_dir)
|
|
98
|
+
|
|
99
|
+
ignorefile_path = os.path.join(source_dir, ".sfyignore")
|
|
100
|
+
if os.path.exists(ignorefile_path):
|
|
101
|
+
logger.info(".sfyignore file found in %s", source_dir)
|
|
102
|
+
warnings.warn(
|
|
103
|
+
"`.sfyignore` is deprecated and will be ignored in future versions. "
|
|
104
|
+
"Please rename the file to `.tfyignore`",
|
|
105
|
+
category=DeprecationWarning,
|
|
106
|
+
stacklevel=2,
|
|
107
|
+
)
|
|
108
|
+
return gitignorefile.parse(path=ignorefile_path, base_path=source_dir)
|
|
109
|
+
|
|
110
|
+
# check for valid git repo
|
|
111
|
+
try:
|
|
112
|
+
import git
|
|
113
|
+
|
|
114
|
+
repo = git.Repo(source_dir, search_parent_directories=True)
|
|
115
|
+
logger.info(
|
|
116
|
+
"Git repository detected in source. Files added in .gitignore will be ignored.\n"
|
|
117
|
+
"If you don't want to ignore these files or otherwise, "
|
|
118
|
+
"please create .tfyignore file and add file patterns to ignore."
|
|
119
|
+
)
|
|
120
|
+
return lambda file_path: bool(repo.ignored(file_path)) # noqa: E731
|
|
121
|
+
except Exception as ex:
|
|
122
|
+
logger.debug(
|
|
123
|
+
"Could not treat source %r as a git repository due to %r", source_dir, ex
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
logger.info(
|
|
127
|
+
"Neither `.tfyignore` file found in %s nor a valid git repository found. "
|
|
128
|
+
"We recommend you to create .tfyignore file and add file patterns to ignore",
|
|
129
|
+
source_dir,
|
|
130
|
+
)
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def local_source_to_remote_source(
|
|
135
|
+
local_source: models.LocalSource,
|
|
136
|
+
workspace_fqn: str,
|
|
137
|
+
component_name: str,
|
|
138
|
+
) -> RemoteSource:
|
|
139
|
+
with tempfile.TemporaryDirectory() as local_dir:
|
|
140
|
+
package_local_path = os.path.join(local_dir, "build.tar.gz")
|
|
141
|
+
source_dir = os.path.abspath(local_source.project_root_path)
|
|
142
|
+
|
|
143
|
+
if not os.path.exists(source_dir):
|
|
144
|
+
raise ValueError(
|
|
145
|
+
f"project root path {source_dir!r} of component {component_name!r} does not exist"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
logger.info("Archiving contents of dir: %r", source_dir)
|
|
149
|
+
|
|
150
|
+
is_path_ignored = _get_callback_handler_to_ignore_file_path(source_dir)
|
|
151
|
+
_make_tarfile(
|
|
152
|
+
output_filename=package_local_path,
|
|
153
|
+
source_dir=source_dir,
|
|
154
|
+
additional_directories=[],
|
|
155
|
+
is_path_ignored=is_path_ignored,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
file_size = _human_readable_size(os.path.getsize(package_local_path))
|
|
160
|
+
logger.info("Code archive size: %r", file_size)
|
|
161
|
+
except Exception:
|
|
162
|
+
# Should not block code upload
|
|
163
|
+
logger.exception("Failed to calculate code archive size")
|
|
164
|
+
|
|
165
|
+
logger.debug("Uploading code archive.")
|
|
166
|
+
client = ServiceFoundryServiceClient()
|
|
167
|
+
remote_uri = client.upload_code_package(
|
|
168
|
+
workspace_fqn=workspace_fqn,
|
|
169
|
+
component_name=component_name,
|
|
170
|
+
package_local_path=package_local_path,
|
|
171
|
+
)
|
|
172
|
+
logger.debug("Uploaded code archive.")
|
|
173
|
+
return RemoteSource(remote_uri=remote_uri)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def local_source_to_image(
|
|
177
|
+
build: models.Build,
|
|
178
|
+
docker_registry_fqn: Optional[str],
|
|
179
|
+
workspace_fqn: str,
|
|
180
|
+
component_name: str,
|
|
181
|
+
) -> Image:
|
|
182
|
+
build = build.copy(deep=True)
|
|
183
|
+
source_dir = os.path.abspath(
|
|
184
|
+
os.path.expanduser(build.build_source.project_root_path)
|
|
185
|
+
)
|
|
186
|
+
if not os.path.exists(source_dir):
|
|
187
|
+
raise ValueError(
|
|
188
|
+
f"project root path {source_dir!r} of component {component_name!r} does not exist"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
build.build_spec.build_context_path = os.path.join(
|
|
192
|
+
source_dir, build.build_spec.build_context_path
|
|
193
|
+
)
|
|
194
|
+
if not os.path.exists(build.build_spec.build_context_path):
|
|
195
|
+
raise ValueError(
|
|
196
|
+
f"Build context path {build.build_spec.build_context_path!r} "
|
|
197
|
+
f"of component {component_name!r} does not exist"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if isinstance(build.build_spec, models.DockerFileBuild):
|
|
201
|
+
build.build_spec.dockerfile_path = os.path.join(
|
|
202
|
+
source_dir, build.build_spec.dockerfile_path
|
|
203
|
+
)
|
|
204
|
+
if not os.path.exists(build.build_spec.dockerfile_path):
|
|
205
|
+
raise ValueError(
|
|
206
|
+
f"Dockerfile path {build.build_spec.dockerfile_path!r} "
|
|
207
|
+
f"of component {component_name!r} does not exist"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
client = ServiceFoundryServiceClient()
|
|
211
|
+
|
|
212
|
+
workspace = workspace_lib.get_workspace_by_fqn(workspace_fqn=workspace_fqn)
|
|
213
|
+
docker_registry = client.get_docker_registry_creds(
|
|
214
|
+
docker_registry_fqn=docker_registry_fqn,
|
|
215
|
+
cluster_id=workspace.clusterId,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
docker_registry_fqn = docker_registry.fqn
|
|
219
|
+
registry_url = docker_registry.registryUrl
|
|
220
|
+
registry_username = docker_registry.username
|
|
221
|
+
registry_password = docker_registry.password
|
|
222
|
+
|
|
223
|
+
create_repo_response = client.create_repo_in_registry(
|
|
224
|
+
docker_registry_fqn=docker_registry_fqn,
|
|
225
|
+
workspace_fqn=workspace_fqn,
|
|
226
|
+
application_name=component_name,
|
|
227
|
+
)
|
|
228
|
+
repo_name = create_repo_response.repoName
|
|
229
|
+
|
|
230
|
+
image_uri = f"{registry_url}/{repo_name}:{int(time.time())}"
|
|
231
|
+
cache_from = f"{registry_url}/{repo_name}:latest"
|
|
232
|
+
|
|
233
|
+
# pull cache image if it exists
|
|
234
|
+
try:
|
|
235
|
+
pull_docker_image(
|
|
236
|
+
image_uri=cache_from,
|
|
237
|
+
docker_login_username=registry_username,
|
|
238
|
+
docker_login_password=registry_password,
|
|
239
|
+
)
|
|
240
|
+
except Exception:
|
|
241
|
+
logger.info(f"Failed to pull {cache_from}. Building without cache image.")
|
|
242
|
+
|
|
243
|
+
# build
|
|
244
|
+
builder.build(
|
|
245
|
+
build.build_spec,
|
|
246
|
+
tag=image_uri,
|
|
247
|
+
extra_opts=["--cache-from", cache_from],
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# push built image to registry
|
|
251
|
+
push_docker_image(
|
|
252
|
+
image_uri=image_uri,
|
|
253
|
+
docker_login_username=registry_username,
|
|
254
|
+
docker_login_password=registry_password,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# push 'latest' tag for image
|
|
258
|
+
push_docker_image_with_latest_tag(
|
|
259
|
+
image_uri=image_uri,
|
|
260
|
+
docker_login_username=registry_username,
|
|
261
|
+
docker_login_password=registry_password,
|
|
262
|
+
)
|
|
263
|
+
return Image(
|
|
264
|
+
image_uri=image_uri,
|
|
265
|
+
docker_registry=docker_registry_fqn,
|
|
266
|
+
command=build.build_spec.command,
|
|
267
|
+
)
|
|
@@ -1 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
try:
|
|
2
|
+
import langchain as _
|
|
3
|
+
except Exception as ex:
|
|
4
|
+
raise Exception(
|
|
5
|
+
"Failed to import langchain. "
|
|
6
|
+
"Please install langchain by using `pip install langchain` command"
|
|
7
|
+
) from ex
|
|
8
|
+
from truefoundry.langchain.deprecated import TruefoundryLLM, TruefoundryPlaygroundLLM
|
|
9
|
+
from truefoundry.langchain.truefoundry_chat import TrueFoundryChat
|
|
10
|
+
from truefoundry.langchain.truefoundry_embeddings import TrueFoundryEmbeddings
|
|
11
|
+
from truefoundry.langchain.truefoundry_llm import TrueFoundryLLM
|
|
12
|
+
from truefoundry.langchain.utils import ModelParameters
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Any, Dict, List, Mapping, Optional
|
|
4
|
+
from urllib.parse import urljoin
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
from requests.auth import HTTPBasicAuth
|
|
8
|
+
|
|
9
|
+
from truefoundry.deploy.lib.auth.servicefoundry_session import ServiceFoundrySession
|
|
10
|
+
from truefoundry.pydantic_v1 import root_validator
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from langchain.callbacks.manager import CallbackManagerForLLMRun
|
|
14
|
+
from langchain.llms.base import LLM
|
|
15
|
+
from langchain.llms.utils import enforce_stop_tokens
|
|
16
|
+
except Exception as ex:
|
|
17
|
+
raise Exception(
|
|
18
|
+
"Failed to import langchain."
|
|
19
|
+
" Please install langchain by using `pip install langchain` command"
|
|
20
|
+
) from ex
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class _ModelServerImpl(str, enum.Enum):
|
|
24
|
+
MLSERVER = "MLSERVER"
|
|
25
|
+
TGI = "TGI"
|
|
26
|
+
VLLM = "VLLM"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_model_server_and_validate_if_mlserver(endpoint_url, auth, model_name=None):
|
|
30
|
+
try:
|
|
31
|
+
response = requests.get(urljoin(endpoint_url, "info"), json={}, auth=auth)
|
|
32
|
+
if response.status_code == 200:
|
|
33
|
+
return _ModelServerImpl.TGI, None
|
|
34
|
+
elif response.status_code == 404:
|
|
35
|
+
# We are not using TGI, try for mlserver
|
|
36
|
+
response = requests.post(
|
|
37
|
+
urljoin(endpoint_url, "v2/repository/index"), json={}, auth=auth
|
|
38
|
+
)
|
|
39
|
+
if response.status_code == 200:
|
|
40
|
+
models = response.json()
|
|
41
|
+
if len(models) == 0:
|
|
42
|
+
raise ValueError("No model is deployed in the model server")
|
|
43
|
+
model_names = [m.get("name") for m in models]
|
|
44
|
+
if model_name and model_name not in model_names:
|
|
45
|
+
raise ValueError(
|
|
46
|
+
f"Model {model_name!r} is not available in the model server. "
|
|
47
|
+
f"Available models {model_names!r}"
|
|
48
|
+
)
|
|
49
|
+
if not model_name and len(model_names) > 1:
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"Please pass `model_name` while instantiating `TruefoundryLLM`. "
|
|
52
|
+
f"Available models are {model_names!r} "
|
|
53
|
+
)
|
|
54
|
+
if model_name:
|
|
55
|
+
return _ModelServerImpl.MLSERVER, model_name
|
|
56
|
+
return _ModelServerImpl.MLSERVER, model_names[0]
|
|
57
|
+
if response.status_code == 404:
|
|
58
|
+
return _ModelServerImpl.VLLM, None
|
|
59
|
+
response.raise_for_status()
|
|
60
|
+
except Exception as e:
|
|
61
|
+
raise Exception(f"Error raised by inference API: {e}") from e
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# TODO (chiragjn): Refactor this into separate implementations for each model server
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TruefoundryLLM(LLM):
|
|
68
|
+
"""Wrapper around TFY model deployment.
|
|
69
|
+
To use this class, you need to have the langchain library installed.
|
|
70
|
+
Example:
|
|
71
|
+
.. code-block:: python
|
|
72
|
+
from truefoundry.langchain import TruefoundryLLM
|
|
73
|
+
endpoint_url = (
|
|
74
|
+
"https://pythia-70m-model-model-catalogue.demo2.truefoundry.tech"
|
|
75
|
+
)
|
|
76
|
+
model = TruefoundryLLM(
|
|
77
|
+
endpoint_url=endpoint_url,
|
|
78
|
+
parameters={
|
|
79
|
+
"max_new_tokens": 100,
|
|
80
|
+
"temperature": 0.7,
|
|
81
|
+
"top_k": 5,
|
|
82
|
+
"top_p": 0.9
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
endpoint_url: str
|
|
88
|
+
model_name: Optional[str] = None
|
|
89
|
+
auth: Optional[HTTPBasicAuth] = None
|
|
90
|
+
parameters: Optional[Dict[str, Any]] = None
|
|
91
|
+
model_server_impl: Optional[_ModelServerImpl] = None
|
|
92
|
+
|
|
93
|
+
@root_validator(pre=False)
|
|
94
|
+
def validate_model_server_and_name(cls, values: Dict):
|
|
95
|
+
warnings.warn(
|
|
96
|
+
message=f"{cls.__name__} is deprecated and will be removed soon. Please use `TrueFoundryLLM` or `TrueFoundryChat` to invoke models using the new TrueFoundry LLM Gateway",
|
|
97
|
+
category=DeprecationWarning,
|
|
98
|
+
stacklevel=2,
|
|
99
|
+
)
|
|
100
|
+
endpoint_url = values["endpoint_url"]
|
|
101
|
+
model_name = values.get("model_name")
|
|
102
|
+
auth = values.get("auth")
|
|
103
|
+
model_server_impl, model_name = _get_model_server_and_validate_if_mlserver(
|
|
104
|
+
endpoint_url=endpoint_url, model_name=model_name, auth=auth
|
|
105
|
+
)
|
|
106
|
+
values["model_server_impl"] = model_server_impl
|
|
107
|
+
if model_server_impl == _ModelServerImpl.MLSERVER:
|
|
108
|
+
values["model_name"] = model_name
|
|
109
|
+
return values
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def _identifying_params(self) -> Mapping[str, Any]:
|
|
113
|
+
"""Get the identifying parameters."""
|
|
114
|
+
return {
|
|
115
|
+
"endpoint_url": self.endpoint_url,
|
|
116
|
+
"model_name": self.model_name,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def _llm_type(self) -> str:
|
|
121
|
+
"""Return type of llm."""
|
|
122
|
+
return "tfy_model_deployment"
|
|
123
|
+
|
|
124
|
+
def _call( # noqa: C901
|
|
125
|
+
self,
|
|
126
|
+
prompt: str,
|
|
127
|
+
stop: Optional[List[str]] = None,
|
|
128
|
+
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
129
|
+
**params: Any,
|
|
130
|
+
) -> str:
|
|
131
|
+
"""Call out to the deployed model
|
|
132
|
+
Args:
|
|
133
|
+
prompt: The prompt to pass into the model.
|
|
134
|
+
stop: Optional list of stop words to use when generating.
|
|
135
|
+
Returns:
|
|
136
|
+
The string generated by the model.
|
|
137
|
+
Example:
|
|
138
|
+
.. code-block:: python
|
|
139
|
+
response = model("Tell me a joke.")
|
|
140
|
+
"""
|
|
141
|
+
_params_already_set = self.parameters or {}
|
|
142
|
+
params = {**_params_already_set, **params, "return_full_text": False}
|
|
143
|
+
|
|
144
|
+
if self.model_server_impl == _ModelServerImpl.MLSERVER:
|
|
145
|
+
generate_path = f"v2/models/{self.model_name}/infer/simple"
|
|
146
|
+
payload = {"inputs": prompt, "parameters": params}
|
|
147
|
+
elif self.model_server_impl == _ModelServerImpl.TGI:
|
|
148
|
+
generate_path = "generate"
|
|
149
|
+
payload = {"inputs": prompt, "parameters": params}
|
|
150
|
+
elif self.model_server_impl == _ModelServerImpl.VLLM:
|
|
151
|
+
generate_path = "generate"
|
|
152
|
+
payload = {**params, "prompt": prompt}
|
|
153
|
+
else:
|
|
154
|
+
raise ValueError(f"No known generate path for {self.model_server_impl}")
|
|
155
|
+
url = urljoin(self.endpoint_url, generate_path)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
response = requests.post(url, json=payload, auth=self.auth)
|
|
159
|
+
response.raise_for_status()
|
|
160
|
+
except Exception as e:
|
|
161
|
+
raise Exception(f"Error raised by inference API: {e}") from e
|
|
162
|
+
response_dict = response.json()
|
|
163
|
+
if "error" in response_dict:
|
|
164
|
+
raise ValueError(
|
|
165
|
+
f"Error raised by inference API: {response_dict['error']!r}"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if self.model_server_impl == _ModelServerImpl.MLSERVER:
|
|
169
|
+
inference_result = response_dict[0]
|
|
170
|
+
elif self.model_server_impl == _ModelServerImpl.TGI:
|
|
171
|
+
inference_result = response_dict
|
|
172
|
+
elif self.model_server_impl == _ModelServerImpl.VLLM:
|
|
173
|
+
inference_result = response_dict
|
|
174
|
+
else:
|
|
175
|
+
raise ValueError(
|
|
176
|
+
f"Unknown model server {self.model_server_impl}, cannot parse response"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if "generated_text" in inference_result:
|
|
180
|
+
text = inference_result["generated_text"]
|
|
181
|
+
elif "summarization" in inference_result:
|
|
182
|
+
text = inference_result["summary_text"]
|
|
183
|
+
elif "text" in inference_result:
|
|
184
|
+
text = inference_result["text"]
|
|
185
|
+
else:
|
|
186
|
+
raise ValueError(f"Could not parse inference response: {response_dict!r}")
|
|
187
|
+
|
|
188
|
+
if isinstance(text, list):
|
|
189
|
+
text = text[0]
|
|
190
|
+
|
|
191
|
+
if stop:
|
|
192
|
+
text = enforce_stop_tokens(text, stop)
|
|
193
|
+
|
|
194
|
+
return text
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class TruefoundryPlaygroundLLM(LLM):
|
|
198
|
+
"""Wrapper around TFY Playground.
|
|
199
|
+
To use this class, you need to have the langchain library installed.
|
|
200
|
+
Example:
|
|
201
|
+
.. code-block:: python
|
|
202
|
+
from truefoundry.langchain import TruefoundryPlaygroundLLM
|
|
203
|
+
import os
|
|
204
|
+
# Note: Login using tfy login --host <https://example-domain.com>
|
|
205
|
+
model = TruefoundryPlaygroundLLM(
|
|
206
|
+
model_name="vicuna-13b",
|
|
207
|
+
parameters={
|
|
208
|
+
"maximumLength": 100,
|
|
209
|
+
"temperature": 0.7,
|
|
210
|
+
"topP": 0.9,
|
|
211
|
+
"repetitionPenalty": 1
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
response = model.predict("Enter the prompt here")
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
model_name: str
|
|
218
|
+
parameters: Optional[Dict[str, Any]] = None
|
|
219
|
+
provider: str = "truefoundry-public"
|
|
220
|
+
|
|
221
|
+
@root_validator(pre=False)
|
|
222
|
+
def validate_model_server_and_name(cls, values: Dict):
|
|
223
|
+
warnings.warn(
|
|
224
|
+
message=f"{cls.__name__} is deprecated and will be removed soon. Please use `TrueFoundryLLM` or `TrueFoundryChat` to invoke models using the new TrueFoundry LLM Gateway",
|
|
225
|
+
category=DeprecationWarning,
|
|
226
|
+
stacklevel=2,
|
|
227
|
+
)
|
|
228
|
+
return values
|
|
229
|
+
|
|
230
|
+
@property
|
|
231
|
+
def _get_model(self) -> str:
|
|
232
|
+
"""returns the model name"""
|
|
233
|
+
return self.model_name
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def _get_provider(self) -> str:
|
|
237
|
+
"""Returns the provider name"""
|
|
238
|
+
return self.provider
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def _llm_type(self) -> str:
|
|
242
|
+
"""Return type of llm."""
|
|
243
|
+
return "tfy_playground"
|
|
244
|
+
|
|
245
|
+
def _call(
|
|
246
|
+
self,
|
|
247
|
+
prompt: str,
|
|
248
|
+
stop: Optional[List[str]] = None,
|
|
249
|
+
**params: Any,
|
|
250
|
+
) -> str:
|
|
251
|
+
"""Call out to the deployed model
|
|
252
|
+
Args:
|
|
253
|
+
prompt: The prompt to pass into the model.
|
|
254
|
+
stop: Optional list of stop words to use when generating.
|
|
255
|
+
Returns:
|
|
256
|
+
The string generated by the model.
|
|
257
|
+
Example:
|
|
258
|
+
.. code-block:: python
|
|
259
|
+
response = model("I have a joke for you...")
|
|
260
|
+
"""
|
|
261
|
+
_params_already_set = self.parameters or {}
|
|
262
|
+
params = {**_params_already_set, **params}
|
|
263
|
+
if stop:
|
|
264
|
+
params["stopSequences"] = stop
|
|
265
|
+
session = ServiceFoundrySession()
|
|
266
|
+
|
|
267
|
+
if not session:
|
|
268
|
+
raise Exception(
|
|
269
|
+
"Unauthenticated: Please login using tfy login --host <https://example-domain.com>"
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
host = session.base_url
|
|
273
|
+
|
|
274
|
+
if host[-1] == "/":
|
|
275
|
+
host = host[: len(host) - 1]
|
|
276
|
+
|
|
277
|
+
url = f"{host}/llm-playground/api/inference/text"
|
|
278
|
+
headers = {"Authorization": f"Bearer {session.access_token}"}
|
|
279
|
+
|
|
280
|
+
json = {
|
|
281
|
+
"prompt": prompt,
|
|
282
|
+
"models": [
|
|
283
|
+
{
|
|
284
|
+
"name": self.model_name,
|
|
285
|
+
"provider": self.provider,
|
|
286
|
+
"tag": self.model_name,
|
|
287
|
+
"parameters": params,
|
|
288
|
+
}
|
|
289
|
+
],
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
response = requests.post(url=url, headers=headers, json=json)
|
|
294
|
+
response.raise_for_status()
|
|
295
|
+
except Exception as ex:
|
|
296
|
+
raise Exception(f"Error inferencing the model: {ex}") from ex
|
|
297
|
+
|
|
298
|
+
data = response.json()
|
|
299
|
+
text = data[0].get("text")
|
|
300
|
+
if stop:
|
|
301
|
+
text = enforce_stop_tokens(text, stop)
|
|
302
|
+
return text
|