truefoundry 0.5.0rc6__py3-none-any.whl → 0.5.1rc1__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/common/utils.py +73 -1
- truefoundry/deploy/__init__.py +5 -0
- truefoundry/deploy/cli/cli.py +2 -0
- truefoundry/deploy/cli/commands/__init__.py +1 -0
- truefoundry/deploy/cli/commands/deploy_init_command.py +22 -0
- truefoundry/deploy/lib/dao/application.py +2 -1
- truefoundry/deploy/v2/lib/patched_models.py +8 -0
- truefoundry/ml/__init__.py +15 -12
- truefoundry/ml/artifact/truefoundry_artifact_repo.py +8 -3
- truefoundry/ml/autogen/client/__init__.py +11 -0
- truefoundry/ml/autogen/client/api/mlfoundry_artifacts_api.py +161 -0
- truefoundry/ml/autogen/client/models/__init__.py +11 -0
- truefoundry/ml/autogen/client/models/artifact_version_manifest.py +2 -2
- truefoundry/ml/autogen/client/models/export_deployment_files_request_dto.py +82 -0
- truefoundry/ml/autogen/client/models/infer_method_name.py +34 -0
- truefoundry/ml/autogen/client/models/model_server.py +34 -0
- truefoundry/ml/autogen/client/models/model_version_environment.py +97 -0
- truefoundry/ml/autogen/client/models/model_version_manifest.py +14 -3
- truefoundry/ml/autogen/client/models/serialization_format.py +35 -0
- truefoundry/ml/autogen/client/models/sklearn_framework.py +31 -2
- truefoundry/ml/autogen/client/models/transformers_framework.py +2 -2
- truefoundry/ml/autogen/client/models/xg_boost_framework.py +20 -2
- truefoundry/ml/autogen/client_README.md +6 -0
- truefoundry/ml/autogen/entities/artifacts.py +65 -6
- truefoundry/ml/cli/commands/model_init.py +97 -0
- truefoundry/ml/cli/utils.py +34 -0
- truefoundry/ml/log_types/artifacts/model.py +48 -24
- truefoundry/ml/log_types/artifacts/utils.py +37 -1
- truefoundry/ml/mlfoundry_api.py +77 -79
- truefoundry/ml/mlfoundry_run.py +3 -31
- truefoundry/ml/model_framework.py +257 -3
- truefoundry/ml/validation_utils.py +2 -0
- {truefoundry-0.5.0rc6.dist-info → truefoundry-0.5.1rc1.dist-info}/METADATA +2 -6
- {truefoundry-0.5.0rc6.dist-info → truefoundry-0.5.1rc1.dist-info}/RECORD +36 -45
- truefoundry/deploy/function_service/__init__.py +0 -3
- truefoundry/deploy/function_service/__main__.py +0 -27
- truefoundry/deploy/function_service/app.py +0 -92
- truefoundry/deploy/function_service/build.py +0 -45
- truefoundry/deploy/function_service/remote/__init__.py +0 -6
- truefoundry/deploy/function_service/remote/context.py +0 -3
- truefoundry/deploy/function_service/remote/method.py +0 -67
- truefoundry/deploy/function_service/remote/remote.py +0 -144
- truefoundry/deploy/function_service/route.py +0 -137
- truefoundry/deploy/function_service/service.py +0 -113
- truefoundry/deploy/function_service/utils.py +0 -53
- truefoundry/langchain/__init__.py +0 -12
- truefoundry/langchain/deprecated.py +0 -302
- truefoundry/langchain/truefoundry_chat.py +0 -130
- truefoundry/langchain/truefoundry_embeddings.py +0 -171
- truefoundry/langchain/truefoundry_llm.py +0 -106
- truefoundry/langchain/utils.py +0 -44
- {truefoundry-0.5.0rc6.dist-info → truefoundry-0.5.1rc1.dist-info}/WHEEL +0 -0
- {truefoundry-0.5.0rc6.dist-info → truefoundry-0.5.1rc1.dist-info}/entry_points.txt +0 -0
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import importlib
|
|
2
|
-
import os
|
|
3
|
-
from threading import Thread
|
|
4
|
-
|
|
5
|
-
import uvicorn
|
|
6
|
-
from fastapi import FastAPI
|
|
7
|
-
|
|
8
|
-
from truefoundry.deploy.function_service.remote import QUAL_NAME_TO_URL_MAP
|
|
9
|
-
from truefoundry.deploy.function_service.route import (
|
|
10
|
-
ClassRoute,
|
|
11
|
-
FunctionRoute,
|
|
12
|
-
RouteGroups,
|
|
13
|
-
)
|
|
14
|
-
from truefoundry.deploy.function_service.utils import (
|
|
15
|
-
create_pydantic_model_from_function_signature,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _encapsulate_user_function(function, input_model_name: str):
|
|
20
|
-
model = create_pydantic_model_from_function_signature(
|
|
21
|
-
func=function, model_name=input_model_name
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
def func(x: model):
|
|
25
|
-
return function(**dict(x._iter(to_dict=False)))
|
|
26
|
-
|
|
27
|
-
return func
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _add_function_route(app: FastAPI, function_route: FunctionRoute, port: int):
|
|
31
|
-
module = importlib.import_module(function_route.module)
|
|
32
|
-
function = getattr(module, function_route.function_name)
|
|
33
|
-
|
|
34
|
-
app.add_api_route(
|
|
35
|
-
path=function_route.path,
|
|
36
|
-
endpoint=_encapsulate_user_function(
|
|
37
|
-
function, input_model_name=function_route.qual_name
|
|
38
|
-
),
|
|
39
|
-
methods=[function_route.http_method],
|
|
40
|
-
)
|
|
41
|
-
QUAL_NAME_TO_URL_MAP[function_route.qual_name] = (
|
|
42
|
-
f"http://localhost:{port}{function_route.path}"
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _add_class_route(app: FastAPI, class_route: ClassRoute, port: int):
|
|
47
|
-
module = importlib.import_module(class_route.module)
|
|
48
|
-
class_factory = getattr(module, class_route.class_name)
|
|
49
|
-
instance = class_factory(**class_route.init_kwargs)
|
|
50
|
-
|
|
51
|
-
for route in class_route.routes:
|
|
52
|
-
function = getattr(instance, route.function_name)
|
|
53
|
-
app.add_api_route(
|
|
54
|
-
path=route.path,
|
|
55
|
-
endpoint=_encapsulate_user_function(function, route.qual_name),
|
|
56
|
-
methods=[route.http_method],
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
QUAL_NAME_TO_URL_MAP[route.qual_name] = f"http://localhost:{port}{route.path}"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def build_app(route_groups: RouteGroups, port: int) -> FastAPI:
|
|
63
|
-
_root_path = os.getenv("TFY_SERVICE_ROOT_PATH")
|
|
64
|
-
app_kwargs = {"root_path": _root_path} if _root_path else {}
|
|
65
|
-
app = FastAPI(docs_url="/", **app_kwargs)
|
|
66
|
-
app.add_api_route(path="/ping", endpoint=lambda: "pong")
|
|
67
|
-
|
|
68
|
-
for route in route_groups.functions:
|
|
69
|
-
_add_function_route(app=app, function_route=route, port=port)
|
|
70
|
-
|
|
71
|
-
for class_route in route_groups.classes.values():
|
|
72
|
-
_add_class_route(app=app, class_route=class_route, port=port)
|
|
73
|
-
|
|
74
|
-
# print(json.dumps(QUAL_NAME_TO_URL_MAP, indent=2))
|
|
75
|
-
return app
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def build_and_run_app(route_groups: RouteGroups, port: int):
|
|
79
|
-
app = build_app(route_groups=route_groups, port=port)
|
|
80
|
-
uvicorn.run(app, port=port, host="0.0.0.0")
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def build_and_run_app_in_background_thread(
|
|
84
|
-
route_groups: RouteGroups, port: int
|
|
85
|
-
) -> Thread:
|
|
86
|
-
thread = Thread(
|
|
87
|
-
target=build_and_run_app,
|
|
88
|
-
kwargs={"route_groups": route_groups, "port": port},
|
|
89
|
-
daemon=True,
|
|
90
|
-
)
|
|
91
|
-
thread.start()
|
|
92
|
-
return thread
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import sys
|
|
3
|
-
from typing import List, Optional
|
|
4
|
-
|
|
5
|
-
from truefoundry.deploy.function_service.route import RouteGroups
|
|
6
|
-
from truefoundry.deploy.v2.lib.patched_models import PythonBuild
|
|
7
|
-
from truefoundry.logger import logger
|
|
8
|
-
from truefoundry.pydantic_v1 import BaseModel, constr
|
|
9
|
-
from truefoundry.version import __version__
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class BuildConfig(BaseModel):
|
|
13
|
-
python_version: constr(regex=r"^\d+(\.\d+){1,2}$") = (
|
|
14
|
-
f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
15
|
-
)
|
|
16
|
-
pip_packages: Optional[List[str]]
|
|
17
|
-
requirements_path: Optional[str] = None
|
|
18
|
-
|
|
19
|
-
def __init__(self, **data):
|
|
20
|
-
pip_packages = data.get("pip_packages", [])
|
|
21
|
-
# locally version == 0.0.0
|
|
22
|
-
# pip_packages.append(f"truefoundry=={__version__}")
|
|
23
|
-
|
|
24
|
-
if __version__ in ("NA", "0.0.0"):
|
|
25
|
-
# TODO (chiragjn): Any change to truefoundry.deploy.function_service.__main__ is untestable!
|
|
26
|
-
# We need to vendor `truefoundry.deploy.function_service` parts in the local source to be able to test.
|
|
27
|
-
tfy_version = "<=1.0.0"
|
|
28
|
-
logger.info("Could not detect truefoundry version. Using %r", tfy_version)
|
|
29
|
-
else:
|
|
30
|
-
tfy_version = f"=={__version__}"
|
|
31
|
-
|
|
32
|
-
pip_packages.append(f"truefoundry{tfy_version}")
|
|
33
|
-
data["pip_packages"] = pip_packages
|
|
34
|
-
super().__init__(**data)
|
|
35
|
-
|
|
36
|
-
def to_tfy_python_build_config(
|
|
37
|
-
self, port: int, route_groups: RouteGroups
|
|
38
|
-
) -> PythonBuild:
|
|
39
|
-
escaped_route_groups_json = json.dumps(route_groups.json())
|
|
40
|
-
return PythonBuild(
|
|
41
|
-
python_version=self.python_version,
|
|
42
|
-
pip_packages=self.pip_packages,
|
|
43
|
-
requirements_path=self.requirements_path,
|
|
44
|
-
command=f"python -m truefoundry.deploy.function_service run --port {port} --route-groups-json {escaped_route_groups_json}",
|
|
45
|
-
)
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import abc
|
|
4
|
-
import json
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
6
|
-
|
|
7
|
-
import requests
|
|
8
|
-
|
|
9
|
-
from truefoundry.deploy.function_service.remote.context import QUAL_NAME_TO_URL_MAP
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from truefoundry.deploy.function_service.remote.remote import Remote
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Method(abc.ABC):
|
|
16
|
-
def __call__(self, *args, **kwargs):
|
|
17
|
-
if len(args) > 0:
|
|
18
|
-
raise Exception(
|
|
19
|
-
"Positional arguments are not supported now.\n"
|
|
20
|
-
"Please use keyword arguments"
|
|
21
|
-
)
|
|
22
|
-
return self.run(**kwargs)
|
|
23
|
-
|
|
24
|
-
@abc.abstractmethod
|
|
25
|
-
def run(self, **kwargs): ...
|
|
26
|
-
|
|
27
|
-
@abc.abstractmethod
|
|
28
|
-
async def run_async(self, **kwargs): ...
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class LocalMethod(Method):
|
|
32
|
-
def __init__(self, remote_object: Remote, method):
|
|
33
|
-
self._remote_object = remote_object
|
|
34
|
-
self._method_name = method.__name__
|
|
35
|
-
|
|
36
|
-
def run(self, **kwargs):
|
|
37
|
-
return getattr(self._remote_object.instance, self._method_name)(**kwargs)
|
|
38
|
-
|
|
39
|
-
async def run_async(self, **kwargs):
|
|
40
|
-
raise NotImplementedError()
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class RemoteMethod(Method):
|
|
44
|
-
def __init__(self, remote_object: Remote, method):
|
|
45
|
-
self._remote_object = remote_object
|
|
46
|
-
self._method_name = method.__name__
|
|
47
|
-
self._qual_name = self._remote_object.get_qual_name(method)
|
|
48
|
-
|
|
49
|
-
def check(self) -> bool:
|
|
50
|
-
return self._qual_name in QUAL_NAME_TO_URL_MAP
|
|
51
|
-
|
|
52
|
-
def run(self, **kwargs):
|
|
53
|
-
url = QUAL_NAME_TO_URL_MAP.get(self._qual_name)
|
|
54
|
-
r = requests.post(url, json=kwargs)
|
|
55
|
-
assert r.status_code == 200, r.text
|
|
56
|
-
return json.loads(r.text)
|
|
57
|
-
|
|
58
|
-
async def run_async(self, **kwargs):
|
|
59
|
-
raise NotImplementedError()
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def method_factory(*args, **kwargs) -> Method:
|
|
63
|
-
remote_method = RemoteMethod(*args, **kwargs)
|
|
64
|
-
if remote_method.check():
|
|
65
|
-
return remote_method
|
|
66
|
-
|
|
67
|
-
return LocalMethod(*args, **kwargs)
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import abc
|
|
2
|
-
import inspect
|
|
3
|
-
from typing import Dict, Optional
|
|
4
|
-
|
|
5
|
-
from truefoundry.deploy.function_service.remote.method import method_factory
|
|
6
|
-
from truefoundry.deploy.function_service.utils import get_qual_name
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Remote(abc.ABC):
|
|
10
|
-
@property
|
|
11
|
-
@abc.abstractmethod
|
|
12
|
-
def instance(self): ...
|
|
13
|
-
|
|
14
|
-
@abc.abstractmethod
|
|
15
|
-
def get_qual_name(self, method) -> str: ...
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class RemoteFunction(Remote):
|
|
19
|
-
def __init__(self, func, **kwargs):
|
|
20
|
-
self._func = func
|
|
21
|
-
self._instance = inspect.getmodule(self._func)
|
|
22
|
-
self.method = method_factory(remote_object=self, method=self._func)
|
|
23
|
-
|
|
24
|
-
def __call__(self, *args, **kwargs):
|
|
25
|
-
if len(args) > 0:
|
|
26
|
-
raise Exception(
|
|
27
|
-
"Positional arguments are not supported now.\n"
|
|
28
|
-
"Please use keyword arguments"
|
|
29
|
-
)
|
|
30
|
-
return self.method.__call__(**kwargs)
|
|
31
|
-
|
|
32
|
-
def run(self, **kwargs):
|
|
33
|
-
return self.method.run(**kwargs)
|
|
34
|
-
|
|
35
|
-
async def run_async(self, **kwargs):
|
|
36
|
-
return await self.method.run_async(**kwargs)
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def instance(self):
|
|
40
|
-
return self._instance
|
|
41
|
-
|
|
42
|
-
def get_qual_name(self, method) -> str:
|
|
43
|
-
return get_qual_name(method)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class RemoteClass(Remote):
|
|
47
|
-
def __init__(
|
|
48
|
-
self,
|
|
49
|
-
class_,
|
|
50
|
-
*,
|
|
51
|
-
init_kwargs: Optional[Dict] = None,
|
|
52
|
-
name: Optional[str] = None,
|
|
53
|
-
**kwargs,
|
|
54
|
-
):
|
|
55
|
-
self._class = class_
|
|
56
|
-
self._name = name or class_.__name__
|
|
57
|
-
self._init_kwargs = init_kwargs or {}
|
|
58
|
-
|
|
59
|
-
self._instance = None
|
|
60
|
-
|
|
61
|
-
###############
|
|
62
|
-
# Maybe this is not the right way to model it.
|
|
63
|
-
# Another approach can be, identify whether the environment is local
|
|
64
|
-
# or our service. If local, then we can just return the instance of
|
|
65
|
-
# the user defined class. Else, we return a class which reflects the
|
|
66
|
-
# methods in the class, but internally doing network call.
|
|
67
|
-
# But I am sticking with this now because not all the concepts of a python
|
|
68
|
-
# class right now, will work in both remote and local. Examples, you cannot
|
|
69
|
-
# access arbitrary attributes, properties etc. I want to keep the behaviour
|
|
70
|
-
# identical for now.
|
|
71
|
-
absent = object()
|
|
72
|
-
methods = inspect.getmembers(class_, predicate=inspect.isfunction)
|
|
73
|
-
for method_name, method in methods:
|
|
74
|
-
if method_name.startswith("_"):
|
|
75
|
-
continue
|
|
76
|
-
existing_attribute = getattr(self, method_name, absent)
|
|
77
|
-
if existing_attribute is not absent:
|
|
78
|
-
raise Exception(
|
|
79
|
-
f"Cannot reflect {method_name!r} of class {class_.__name__!r}.\n"
|
|
80
|
-
f"{method_name!r} is reserved. Use a different method name"
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
setattr(
|
|
84
|
-
self,
|
|
85
|
-
method_name,
|
|
86
|
-
method_factory(remote_object=self, method=method),
|
|
87
|
-
)
|
|
88
|
-
###############
|
|
89
|
-
|
|
90
|
-
def get_qual_name(self, method) -> str:
|
|
91
|
-
return f"{self.name}:{get_qual_name(method)}"
|
|
92
|
-
|
|
93
|
-
@property
|
|
94
|
-
def instance(self):
|
|
95
|
-
if self._instance:
|
|
96
|
-
return self._instance
|
|
97
|
-
instance = self._class(**self._init_kwargs)
|
|
98
|
-
self._instance = instance
|
|
99
|
-
return self._instance
|
|
100
|
-
|
|
101
|
-
@property
|
|
102
|
-
def class_(self):
|
|
103
|
-
return self._class
|
|
104
|
-
|
|
105
|
-
@property
|
|
106
|
-
def name(self) -> str:
|
|
107
|
-
return self._name
|
|
108
|
-
|
|
109
|
-
@property
|
|
110
|
-
def init_kwargs(self) -> Dict:
|
|
111
|
-
return self._init_kwargs
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def remote(func_or_class, **kwargs):
|
|
115
|
-
if inspect.isfunction(func_or_class):
|
|
116
|
-
return RemoteFunction(func_or_class, **kwargs)
|
|
117
|
-
if inspect.isclass(func_or_class):
|
|
118
|
-
return RemoteClass(func_or_class, **kwargs)
|
|
119
|
-
|
|
120
|
-
raise Exception()
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if __name__ == "__main__":
|
|
124
|
-
|
|
125
|
-
def foo(a, b):
|
|
126
|
-
print(a, b)
|
|
127
|
-
|
|
128
|
-
remote(foo)(a=1, b=2)
|
|
129
|
-
|
|
130
|
-
class Foo:
|
|
131
|
-
def __init__(self, a, b):
|
|
132
|
-
self.a = a
|
|
133
|
-
self.b = b
|
|
134
|
-
|
|
135
|
-
def foo(self):
|
|
136
|
-
print(self.a, self.b)
|
|
137
|
-
|
|
138
|
-
foo_instance = remote(Foo, name="foo", init_kwargs={"a": 1, "b": 2})
|
|
139
|
-
foo_instance_2 = remote(Foo, name="foo_2", init_kwargs={"a": 3, "b": 2})
|
|
140
|
-
|
|
141
|
-
foo_instance.foo()
|
|
142
|
-
foo_instance_2.foo()
|
|
143
|
-
foo_instance.foo.run()
|
|
144
|
-
# foo_instance.foo.async_run()
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import inspect
|
|
2
|
-
import json
|
|
3
|
-
from typing import Any, Callable, Dict, List
|
|
4
|
-
|
|
5
|
-
from truefoundry.deploy.function_service.remote import RemoteClass
|
|
6
|
-
from truefoundry.deploy.function_service.utils import (
|
|
7
|
-
create_pydantic_model_from_function_signature,
|
|
8
|
-
get_qual_name,
|
|
9
|
-
)
|
|
10
|
-
from truefoundry.logger import logger
|
|
11
|
-
from truefoundry.pydantic_v1 import BaseModel, Field, constr, validator
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def validate_we_can_create_pydantic_model_of_func_args(func: Callable):
|
|
15
|
-
qual_name = get_qual_name(func)
|
|
16
|
-
try:
|
|
17
|
-
create_pydantic_model_from_function_signature(func, get_qual_name(func))
|
|
18
|
-
except Exception as ex:
|
|
19
|
-
raise Exception(
|
|
20
|
-
f"Unable to create a route for {qual_name!r}.\n"
|
|
21
|
-
"Please ensure that in the function type signature, you have only used in-built\n"
|
|
22
|
-
"types like `int`, `float`, `str`, `bool`, `typing.Dict`, `typing.List`, typing.Optional`.\n"
|
|
23
|
-
"To temporarily resolve this error, you can remove the unsupported type signatures.\n"
|
|
24
|
-
) from ex
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def path_pre_processor(path: str, prefix: str = "") -> str:
|
|
28
|
-
path = path.strip("/")
|
|
29
|
-
|
|
30
|
-
if not path:
|
|
31
|
-
raise ValueError("path cannot be empty")
|
|
32
|
-
|
|
33
|
-
prefix = prefix.strip("/")
|
|
34
|
-
if not prefix:
|
|
35
|
-
return f"/{path}"
|
|
36
|
-
|
|
37
|
-
return f"/{prefix}/{path}"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class Route(BaseModel):
|
|
41
|
-
function_name: str
|
|
42
|
-
http_method: str
|
|
43
|
-
path: constr(regex=r"^[A-Za-z0-9\-_/]+$")
|
|
44
|
-
|
|
45
|
-
qual_name: str
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class FunctionRoute(Route):
|
|
49
|
-
module: str
|
|
50
|
-
|
|
51
|
-
@classmethod
|
|
52
|
-
def from_func(cls, func: Callable, path: str):
|
|
53
|
-
validate_we_can_create_pydantic_model_of_func_args(func)
|
|
54
|
-
return cls(
|
|
55
|
-
function_name=func.__name__,
|
|
56
|
-
http_method="POST",
|
|
57
|
-
path=path_pre_processor(path or func.__name__),
|
|
58
|
-
qual_name=get_qual_name(func),
|
|
59
|
-
module=func.__module__,
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class ClassRoute(BaseModel):
|
|
64
|
-
class_name: str
|
|
65
|
-
init_kwargs: Dict[str, Any] = Field(default_factory=dict)
|
|
66
|
-
module: str
|
|
67
|
-
|
|
68
|
-
routes: List[Route] = Field(default_factory=list)
|
|
69
|
-
|
|
70
|
-
@validator("init_kwargs")
|
|
71
|
-
def init_kwargs_is_json_serializable(cls, v, values):
|
|
72
|
-
try:
|
|
73
|
-
json.dumps(v)
|
|
74
|
-
except Exception as ex:
|
|
75
|
-
class_name = values.get("class_name")
|
|
76
|
-
raise ValueError(
|
|
77
|
-
f"init_kwargs {v!r} of class {class_name!r} is not JSON serializable"
|
|
78
|
-
) from ex
|
|
79
|
-
|
|
80
|
-
return v
|
|
81
|
-
|
|
82
|
-
@classmethod
|
|
83
|
-
def from_class(cls, remote_class: RemoteClass):
|
|
84
|
-
routes = []
|
|
85
|
-
methods = inspect.getmembers(remote_class.class_, predicate=inspect.isfunction)
|
|
86
|
-
|
|
87
|
-
for method_name, method in methods:
|
|
88
|
-
if method_name.startswith("_"):
|
|
89
|
-
continue
|
|
90
|
-
validate_we_can_create_pydantic_model_of_func_args(method)
|
|
91
|
-
route = Route(
|
|
92
|
-
function_name=method_name,
|
|
93
|
-
http_method="POST",
|
|
94
|
-
path=path_pre_processor(prefix=remote_class.name, path=method_name),
|
|
95
|
-
qual_name=remote_class.get_qual_name(method),
|
|
96
|
-
)
|
|
97
|
-
routes.append(route)
|
|
98
|
-
|
|
99
|
-
return cls(
|
|
100
|
-
class_name=remote_class.class_.__name__,
|
|
101
|
-
init_kwargs=remote_class.init_kwargs,
|
|
102
|
-
routes=routes,
|
|
103
|
-
module=remote_class.class_.__module__,
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
class RouteGroups(BaseModel):
|
|
108
|
-
functions: List[FunctionRoute] = Field(default_factory=list)
|
|
109
|
-
classes: Dict[str, ClassRoute] = Field(default_factory=dict)
|
|
110
|
-
|
|
111
|
-
def register_function(self, func, path):
|
|
112
|
-
function_route = FunctionRoute.from_func(func=func, path=path)
|
|
113
|
-
logger.info(
|
|
114
|
-
"Function %r from module %r will be deployed on path '%s %s'.",
|
|
115
|
-
function_route.function_name,
|
|
116
|
-
function_route.module,
|
|
117
|
-
function_route.http_method,
|
|
118
|
-
function_route.path,
|
|
119
|
-
)
|
|
120
|
-
self.functions.append(function_route)
|
|
121
|
-
|
|
122
|
-
def register_class(self, remote_class: RemoteClass):
|
|
123
|
-
if remote_class.name in self.classes:
|
|
124
|
-
raise ValueError(
|
|
125
|
-
f"name {remote_class.name!r} is already used to register a class"
|
|
126
|
-
)
|
|
127
|
-
class_route = ClassRoute.from_class(remote_class)
|
|
128
|
-
for route in class_route.routes:
|
|
129
|
-
logger.info(
|
|
130
|
-
"Method %r from `%s:%s` will be deployed on path '%s %s'.",
|
|
131
|
-
route.function_name,
|
|
132
|
-
class_route.class_name,
|
|
133
|
-
remote_class.name,
|
|
134
|
-
route.http_method,
|
|
135
|
-
route.path,
|
|
136
|
-
)
|
|
137
|
-
self.classes[remote_class.name] = class_route
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
from threading import Thread
|
|
2
|
-
from typing import Any, Callable, Dict, Optional, Union
|
|
3
|
-
|
|
4
|
-
import yaml
|
|
5
|
-
|
|
6
|
-
from truefoundry.deploy.auto_gen.models import Port, Resources
|
|
7
|
-
from truefoundry.deploy.function_service.app import (
|
|
8
|
-
build_and_run_app_in_background_thread,
|
|
9
|
-
)
|
|
10
|
-
from truefoundry.deploy.function_service.build import BuildConfig
|
|
11
|
-
from truefoundry.deploy.function_service.remote import RemoteClass
|
|
12
|
-
from truefoundry.deploy.function_service.route import RouteGroups
|
|
13
|
-
from truefoundry.deploy.v2.lib.deployable_patched_models import Service
|
|
14
|
-
from truefoundry.deploy.v2.lib.patched_models import Build, LocalSource
|
|
15
|
-
from truefoundry.logger import logger
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class FunctionService:
|
|
19
|
-
def __init__(
|
|
20
|
-
self,
|
|
21
|
-
name: str,
|
|
22
|
-
build_config: Optional[BuildConfig] = None,
|
|
23
|
-
resources: Optional[Resources] = None,
|
|
24
|
-
replicas: int = 1,
|
|
25
|
-
port: Union[int, Port] = 8000,
|
|
26
|
-
env: Optional[Dict[str, str]] = None,
|
|
27
|
-
):
|
|
28
|
-
self._name = name
|
|
29
|
-
self._build_config = build_config or BuildConfig()
|
|
30
|
-
self._resources = resources or Resources()
|
|
31
|
-
self._replicas = replicas
|
|
32
|
-
if isinstance(port, int):
|
|
33
|
-
port = Port(port=port, expose=False)
|
|
34
|
-
if not port.host:
|
|
35
|
-
logger.warning(
|
|
36
|
-
"No host is set for the port. This is not an issue if you don't "
|
|
37
|
-
"want an exposed endpoint or are just testing locally.\n"
|
|
38
|
-
"However, for actual deployment it is required to pass an "
|
|
39
|
-
"instance of `truefoundry.deploy.Port` with "
|
|
40
|
-
"`host` argument defined.\n"
|
|
41
|
-
"E.g. `FunctionService(name='...', port=Port(port=8000, host='...', path='...'), ...)`"
|
|
42
|
-
)
|
|
43
|
-
self._port = port
|
|
44
|
-
self._env = env or {}
|
|
45
|
-
|
|
46
|
-
self._route_groups: RouteGroups = RouteGroups()
|
|
47
|
-
|
|
48
|
-
@property
|
|
49
|
-
def route_groups(self) -> RouteGroups:
|
|
50
|
-
return self._route_groups
|
|
51
|
-
|
|
52
|
-
def __repr__(self):
|
|
53
|
-
return yaml.dump(
|
|
54
|
-
{
|
|
55
|
-
"name": self._name,
|
|
56
|
-
"build_config": self._build_config.dict(),
|
|
57
|
-
"resources": self._resources.dict(),
|
|
58
|
-
"routes": self._route_groups.dict(),
|
|
59
|
-
"replicas": self._replicas,
|
|
60
|
-
"port": self._port.dict(),
|
|
61
|
-
"env": self._env,
|
|
62
|
-
},
|
|
63
|
-
indent=2,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
def register_function(
|
|
67
|
-
self,
|
|
68
|
-
func: Callable,
|
|
69
|
-
*,
|
|
70
|
-
path: Optional[str] = None,
|
|
71
|
-
):
|
|
72
|
-
self._route_groups.register_function(func=func, path=path)
|
|
73
|
-
|
|
74
|
-
def register_class(
|
|
75
|
-
self,
|
|
76
|
-
class_,
|
|
77
|
-
*,
|
|
78
|
-
init_kwargs: Optional[Dict[str, Any]] = None,
|
|
79
|
-
name: Optional[str] = None,
|
|
80
|
-
):
|
|
81
|
-
# TODO: I need to rethink this `RemoteClass`.
|
|
82
|
-
# I am mixing up multiple responsibilities here.
|
|
83
|
-
# For now, I am removing the burden of using `remote` from the user when deploying
|
|
84
|
-
# an instance of a class.
|
|
85
|
-
remote_class = RemoteClass(class_, init_kwargs=init_kwargs, name=name)
|
|
86
|
-
self._route_groups.register_class(remote_class=remote_class)
|
|
87
|
-
|
|
88
|
-
def run(self) -> Thread:
|
|
89
|
-
return build_and_run_app_in_background_thread(
|
|
90
|
-
route_groups=self._route_groups, port=self._port.port
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
def get_deployment_definition(self) -> Service:
|
|
94
|
-
# Keeping this function right now so that later,
|
|
95
|
-
# the constructor of the application call this function
|
|
96
|
-
# to get the component spec, if an object of this class
|
|
97
|
-
# is directly passed as a component
|
|
98
|
-
tfy_python_build_config = self._build_config.to_tfy_python_build_config(
|
|
99
|
-
port=self._port.port, route_groups=self._route_groups
|
|
100
|
-
)
|
|
101
|
-
service = Service(
|
|
102
|
-
name=self._name,
|
|
103
|
-
image=Build(build_source=LocalSource(), build_spec=tfy_python_build_config),
|
|
104
|
-
resources=self._resources,
|
|
105
|
-
replicas=self._replicas,
|
|
106
|
-
ports=[self._port],
|
|
107
|
-
env=self._env,
|
|
108
|
-
)
|
|
109
|
-
return service
|
|
110
|
-
|
|
111
|
-
def deploy(self, workspace_fqn: str, wait: bool = True):
|
|
112
|
-
service = self.get_deployment_definition()
|
|
113
|
-
service.deploy(workspace_fqn=workspace_fqn, wait=wait)
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import inspect
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
from truefoundry.pydantic_v1 import BaseModel
|
|
5
|
-
from truefoundry.pydantic_v1 import create_model as pydantic_create_model
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def get_qual_name(obj):
|
|
9
|
-
return f"{obj.__module__}.{obj.__qualname__}"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def create_pydantic_model_from_function_signature(func, model_name: str):
|
|
13
|
-
# https://github.com/pydantic/pydantic/issues/1391
|
|
14
|
-
(
|
|
15
|
-
args,
|
|
16
|
-
_,
|
|
17
|
-
varkw,
|
|
18
|
-
defaults,
|
|
19
|
-
kwonlyargs,
|
|
20
|
-
kwonlydefaults,
|
|
21
|
-
annotations,
|
|
22
|
-
) = inspect.getfullargspec(func)
|
|
23
|
-
defaults = defaults or []
|
|
24
|
-
args = args or []
|
|
25
|
-
if len(args) > 0 and args[0] == "self":
|
|
26
|
-
del args[0]
|
|
27
|
-
|
|
28
|
-
non_default_args = len(args) - len(defaults)
|
|
29
|
-
defaults = [
|
|
30
|
-
...,
|
|
31
|
-
] * non_default_args + list(defaults)
|
|
32
|
-
|
|
33
|
-
keyword_only_params = {
|
|
34
|
-
param: kwonlydefaults.get(param, Any) for param in kwonlyargs
|
|
35
|
-
}
|
|
36
|
-
params = {
|
|
37
|
-
param: (annotations.get(param, Any), default)
|
|
38
|
-
for param, default in zip(args, defaults)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
class Config:
|
|
42
|
-
extra = "allow"
|
|
43
|
-
|
|
44
|
-
# Allow extra params if there is a **kwargs parameter in the function signature
|
|
45
|
-
config = Config if varkw else None
|
|
46
|
-
|
|
47
|
-
return pydantic_create_model(
|
|
48
|
-
model_name,
|
|
49
|
-
**params,
|
|
50
|
-
**keyword_only_params,
|
|
51
|
-
__base__=BaseModel,
|
|
52
|
-
__config__=config,
|
|
53
|
-
)
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|