roboml 0.2.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.
- roboml/__init__.py +0 -0
- roboml/databases/__init__.py +5 -0
- roboml/databases/_base.py +166 -0
- roboml/databases/chroma.py +189 -0
- roboml/interfaces.py +137 -0
- roboml/main.py +62 -0
- roboml/models/__init__.py +13 -0
- roboml/models/_base.py +86 -0
- roboml/models/_encoding.py +70 -0
- roboml/models/llm.py +93 -0
- roboml/models/mllm.py +220 -0
- roboml/models/speech_to_text.py +72 -0
- roboml/models/text_to_speech.py +132 -0
- roboml/models/vision.py +165 -0
- roboml/ray/__init__.py +6 -0
- roboml/ray/app_factory.py +118 -0
- roboml/resp_server/server.py +278 -0
- roboml/resp_server/stream.py +63 -0
- roboml/tools/__init__.py +0 -0
- roboml/tools/download.py +103 -0
- roboml/utils.py +331 -0
- roboml-0.2.0.dist-info/LICENSE +21 -0
- roboml-0.2.0.dist-info/METADATA +125 -0
- roboml-0.2.0.dist-info/RECORD +27 -0
- roboml-0.2.0.dist-info/WHEEL +5 -0
- roboml-0.2.0.dist-info/entry_points.txt +3 -0
- roboml-0.2.0.dist-info/top_level.txt +1 -0
roboml/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from logging import Logger
|
|
3
|
+
import inspect
|
|
4
|
+
|
|
5
|
+
from pydantic import validate_call, ValidationError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from roboml.models._encoding import EncodingModel
|
|
9
|
+
from roboml.utils import Status, logging
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class VectorDBTemplate:
|
|
13
|
+
"""
|
|
14
|
+
This class describes a VectorDB template.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, *, name: str, **_):
|
|
18
|
+
"""__init__.
|
|
19
|
+
:param name:
|
|
20
|
+
:type name: str
|
|
21
|
+
:param _:
|
|
22
|
+
"""
|
|
23
|
+
self.name: str = name
|
|
24
|
+
self.encoding_model: EncodingModel
|
|
25
|
+
self.logger: Logger = logging.getLogger(self.name)
|
|
26
|
+
self.status: Status = Status.LOADED
|
|
27
|
+
|
|
28
|
+
def initialize(self, **kwargs) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Calls the db initialization function and sets status accordingly
|
|
31
|
+
"""
|
|
32
|
+
if self.status == Status.READY:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
self.status = Status.INITIALIZING
|
|
36
|
+
try:
|
|
37
|
+
validated_init = validate_call(self._initialize)
|
|
38
|
+
validated_init(**kwargs)
|
|
39
|
+
except Exception as e:
|
|
40
|
+
self.logger.error(f"Initialization Error: {e}")
|
|
41
|
+
self.status = Status.INITIALIZATION_ERROR
|
|
42
|
+
raise e
|
|
43
|
+
self.logger.info(f"{self.__class__.__name__} VectorDB initialized")
|
|
44
|
+
self.status = Status.READY
|
|
45
|
+
|
|
46
|
+
def get_status(self):
|
|
47
|
+
"""Returns status of the model node"""
|
|
48
|
+
return self.status.name
|
|
49
|
+
|
|
50
|
+
def add(self, **kwargs) -> dict:
|
|
51
|
+
"""VectorDB specific add function.
|
|
52
|
+
:param kwargs:
|
|
53
|
+
:rtype: None
|
|
54
|
+
"""
|
|
55
|
+
if self.status is not Status.READY:
|
|
56
|
+
self.logger.error("Error: DB method called before initialization")
|
|
57
|
+
raise Exception("DB Method Called called before initialization")
|
|
58
|
+
try:
|
|
59
|
+
data_model = inspect.signature(self._add).parameters["data"].annotation
|
|
60
|
+
data = data_model(**kwargs)
|
|
61
|
+
except ValidationError:
|
|
62
|
+
self.logger.error("Validation Error occured for inference input")
|
|
63
|
+
raise
|
|
64
|
+
return self._add(data)
|
|
65
|
+
|
|
66
|
+
def conditional_add(self, **kwargs) -> dict:
|
|
67
|
+
"""VectorDB specific conditional_add function.
|
|
68
|
+
:param kwargs:
|
|
69
|
+
:rtype: None
|
|
70
|
+
"""
|
|
71
|
+
if self.status is not Status.READY:
|
|
72
|
+
self.logger.error("Error: DB method called before initialization")
|
|
73
|
+
raise Exception("DB Method Called called before initialization")
|
|
74
|
+
try:
|
|
75
|
+
data_model = (
|
|
76
|
+
inspect.signature(self._conditional_add).parameters["data"].annotation
|
|
77
|
+
)
|
|
78
|
+
data = data_model(**kwargs)
|
|
79
|
+
except ValidationError:
|
|
80
|
+
self.logger.error("Validation Error occured for inference input")
|
|
81
|
+
raise
|
|
82
|
+
return self._conditional_add(data)
|
|
83
|
+
|
|
84
|
+
def metadata_query(self, **kwargs) -> dict:
|
|
85
|
+
"""VectorDB specific metadata_query function.
|
|
86
|
+
:param kwargs:
|
|
87
|
+
:rtype: None
|
|
88
|
+
"""
|
|
89
|
+
if self.status is not Status.READY:
|
|
90
|
+
self.logger.error("Error: DB method called before initialization")
|
|
91
|
+
raise Exception("DB Method Called called before initialization")
|
|
92
|
+
try:
|
|
93
|
+
data_model = (
|
|
94
|
+
inspect.signature(self._metadata_query).parameters["data"].annotation
|
|
95
|
+
)
|
|
96
|
+
data = data_model(**kwargs)
|
|
97
|
+
except ValidationError:
|
|
98
|
+
self.logger.error("Validation Error occured for inference input")
|
|
99
|
+
raise
|
|
100
|
+
return self._metadata_query(data)
|
|
101
|
+
|
|
102
|
+
def query(self, **kwargs) -> dict:
|
|
103
|
+
"""VectorDB specific query function.
|
|
104
|
+
:param kwargs:
|
|
105
|
+
:rtype: None
|
|
106
|
+
"""
|
|
107
|
+
if self.status is not Status.READY:
|
|
108
|
+
self.logger.error("Error: DB method called before initialization")
|
|
109
|
+
raise Exception("DB Method Called called before initialization")
|
|
110
|
+
try:
|
|
111
|
+
data_model = inspect.signature(self._query).parameters["data"].annotation
|
|
112
|
+
data = data_model(**kwargs)
|
|
113
|
+
except ValidationError:
|
|
114
|
+
self.logger.error("Validation Error occured for inference input")
|
|
115
|
+
raise
|
|
116
|
+
return self._query(data)
|
|
117
|
+
|
|
118
|
+
@abstractmethod
|
|
119
|
+
def _initialize(self, *_, **__) -> None:
|
|
120
|
+
"""VectorDB specific initialize function, to be implemented in derived classes.
|
|
121
|
+
:param kwargs:
|
|
122
|
+
:rtype: None
|
|
123
|
+
"""
|
|
124
|
+
raise NotImplementedError(
|
|
125
|
+
"VectorDB specific initialize method needs to be implemented by derived classes"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def _add(self, *_, **__) -> dict:
|
|
130
|
+
"""VectorDB specific add function, to be implemented in derived classes.
|
|
131
|
+
:param kwargs:
|
|
132
|
+
:rtype: None
|
|
133
|
+
"""
|
|
134
|
+
raise NotImplementedError(
|
|
135
|
+
"VectorDB specific add method needs to be implemented by derived classes"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@abstractmethod
|
|
139
|
+
def _conditional_add(self, *_, **__) -> dict:
|
|
140
|
+
"""VectorDB specific conditional_add function, to be implemented in derived classes.
|
|
141
|
+
:param kwargs:
|
|
142
|
+
:rtype: None
|
|
143
|
+
"""
|
|
144
|
+
raise NotImplementedError(
|
|
145
|
+
"VectorDB specific conditional_add method needs to be implemented by derived classes"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
@abstractmethod
|
|
149
|
+
def _metadata_query(self, *_, **__) -> dict:
|
|
150
|
+
"""VectorDB specific metadata_query function, to be implemented in derived classes.
|
|
151
|
+
:param kwargs:
|
|
152
|
+
:rtype: None
|
|
153
|
+
"""
|
|
154
|
+
raise NotImplementedError(
|
|
155
|
+
"VectorDB specific metadata_query method needs to be implemented by derived classes"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
@abstractmethod
|
|
159
|
+
def _query(self, *_, **__) -> dict:
|
|
160
|
+
"""VectorDB specific query function, to be implemented in derived classes.
|
|
161
|
+
:param kwargs:
|
|
162
|
+
:rtype: None
|
|
163
|
+
"""
|
|
164
|
+
raise NotImplementedError(
|
|
165
|
+
"VectorDB specific query method needs to be implemented by derived classes"
|
|
166
|
+
)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from chromadb import PersistentClient
|
|
3
|
+
from chromadb.api import ClientAPI
|
|
4
|
+
from chromadb.config import Settings
|
|
5
|
+
|
|
6
|
+
from roboml.interfaces import DBAdd, DBMetadataQuery, DBQuery
|
|
7
|
+
from roboml.models._encoding import EncodingModel
|
|
8
|
+
from roboml.ray import app, ingress_decorator
|
|
9
|
+
|
|
10
|
+
from ._base import VectorDBTemplate
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@ingress_decorator
|
|
14
|
+
class ChromaDB(VectorDBTemplate):
|
|
15
|
+
"""
|
|
16
|
+
ChromaDB Wrapper.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, **kwargs):
|
|
20
|
+
"""__init__.
|
|
21
|
+
:param kwargs:
|
|
22
|
+
"""
|
|
23
|
+
super().__init__(**kwargs)
|
|
24
|
+
self.vectordb: ClientAPI
|
|
25
|
+
|
|
26
|
+
@app.post("/initialize")
|
|
27
|
+
def _initialize(
|
|
28
|
+
self,
|
|
29
|
+
db_location: str = "./data",
|
|
30
|
+
username: Optional[str] = None,
|
|
31
|
+
password: Optional[str] = None,
|
|
32
|
+
encoder: Optional[dict] = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Initializes the db.
|
|
36
|
+
"""
|
|
37
|
+
if username and password:
|
|
38
|
+
self.logger.warning(
|
|
39
|
+
"Username/password authentication can only be used with local ChromaDB client. Cannot use username/password authentication with roboml ChromaDB instance."
|
|
40
|
+
)
|
|
41
|
+
# initialize the encoding model
|
|
42
|
+
self.encoding_model = EncodingModel(name="encoding_model")
|
|
43
|
+
encoder = encoder or {}
|
|
44
|
+
self.encoding_model._initialize(**encoder)
|
|
45
|
+
|
|
46
|
+
# create a vectordb client
|
|
47
|
+
self.vectordb = PersistentClient(
|
|
48
|
+
settings=Settings(anonymized_telemetry=False),
|
|
49
|
+
path=db_location,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
@app.post("/add")
|
|
53
|
+
def _add(self, data: DBAdd) -> dict:
|
|
54
|
+
"""Add data to the given collection.
|
|
55
|
+
:param data:
|
|
56
|
+
:param type: DBAdd
|
|
57
|
+
:rtype: dict
|
|
58
|
+
"""
|
|
59
|
+
# If specified reset existing collection
|
|
60
|
+
if data.reset_collection:
|
|
61
|
+
try:
|
|
62
|
+
self.vectordb.delete_collection(name=data.collection_name)
|
|
63
|
+
except ValueError:
|
|
64
|
+
self.logger.warning(
|
|
65
|
+
f"Cannot delete collection with name {data.collection_name} as it does not exist."
|
|
66
|
+
)
|
|
67
|
+
try:
|
|
68
|
+
# create a collection if one doesnt exist
|
|
69
|
+
collection = self.vectordb.get_or_create_collection(
|
|
70
|
+
name=data.collection_name, metadata={"hnsw:space": data.distance_func}
|
|
71
|
+
)
|
|
72
|
+
# create embeddings
|
|
73
|
+
embeddings = self.encoding_model.embed_documents(data.documents)
|
|
74
|
+
# add to collection
|
|
75
|
+
collection.add(
|
|
76
|
+
documents=data.documents,
|
|
77
|
+
embeddings=embeddings,
|
|
78
|
+
metadatas=data.metadatas,
|
|
79
|
+
ids=data.ids,
|
|
80
|
+
)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
self.logger.error(f"Exception occured: {e}")
|
|
83
|
+
raise
|
|
84
|
+
|
|
85
|
+
return {"output": "Success"}
|
|
86
|
+
|
|
87
|
+
@app.post("/conditional_add")
|
|
88
|
+
def _conditional_add(self, data: DBAdd) -> dict:
|
|
89
|
+
"""First check if id exists if not then add data to the collection provided
|
|
90
|
+
Update metadatas of the ids that exist
|
|
91
|
+
:param data:
|
|
92
|
+
:param type: DBAdd
|
|
93
|
+
:rtype: dict
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
# create a collection if one doesnt exist
|
|
97
|
+
collection = self.vectordb.get_or_create_collection(
|
|
98
|
+
name=data.collection_name, metadata={"hnsw:space": data.distance_func}
|
|
99
|
+
)
|
|
100
|
+
# check for ids that already exist in DB
|
|
101
|
+
already_existing = collection.get(ids=data.ids)
|
|
102
|
+
|
|
103
|
+
# if ids found in database, remove them from main input lists
|
|
104
|
+
# update metadatas of already existing IDs
|
|
105
|
+
metadatas_to_update = []
|
|
106
|
+
to_be_deleted = []
|
|
107
|
+
if already_existing_ids := already_existing["ids"]:
|
|
108
|
+
for idx in range(len(data.ids)):
|
|
109
|
+
if data.ids[idx] in already_existing_ids:
|
|
110
|
+
to_be_deleted.append(idx)
|
|
111
|
+
metadatas_to_update.append(data.metadatas[idx].copy())
|
|
112
|
+
# do the metadata update
|
|
113
|
+
collection.update(
|
|
114
|
+
ids=already_existing_ids, metadatas=metadatas_to_update
|
|
115
|
+
)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
self.logger.error(f"Exception occured: {e}")
|
|
118
|
+
raise
|
|
119
|
+
|
|
120
|
+
# delete from indices that were updated
|
|
121
|
+
for idx in sorted(to_be_deleted, reverse=True):
|
|
122
|
+
del data.ids[idx]
|
|
123
|
+
del data.metadatas[idx]
|
|
124
|
+
del data.documents[idx]
|
|
125
|
+
# add the remaining data
|
|
126
|
+
return self._add(data) if data.ids else {"output": "Success"}
|
|
127
|
+
|
|
128
|
+
@app.post("/metadata_query")
|
|
129
|
+
def _metadata_query(self, data: DBMetadataQuery) -> dict:
|
|
130
|
+
"""Retreive data by metadata query.
|
|
131
|
+
:param data:
|
|
132
|
+
:param type: DBMetadataQuery
|
|
133
|
+
:rtype: dict
|
|
134
|
+
"""
|
|
135
|
+
# create filters for all metadata values
|
|
136
|
+
all_filters = []
|
|
137
|
+
for metadata in data.metadatas:
|
|
138
|
+
# create filter for each metadata hashmap
|
|
139
|
+
if len(metadata) > 1:
|
|
140
|
+
filter = {"$and": [{i: {"$eq": metadata[i]}} for i in metadata]}
|
|
141
|
+
# if there is only one metadata entry, $and is not needed
|
|
142
|
+
elif len(metadata) == 1:
|
|
143
|
+
filter = {list(metadata.keys())[0]: {"$eq": list(metadata.values())[0]}}
|
|
144
|
+
else:
|
|
145
|
+
continue
|
|
146
|
+
all_filters.append(filter)
|
|
147
|
+
|
|
148
|
+
# if no filters, return output as None
|
|
149
|
+
if len(all_filters) == 0:
|
|
150
|
+
self.logger.warning(
|
|
151
|
+
"The metadata filters received were empty, please call query method for retreiving data without metadata filtering."
|
|
152
|
+
)
|
|
153
|
+
return {"output": []}
|
|
154
|
+
|
|
155
|
+
# if there are multiple filters, add an $or
|
|
156
|
+
filters = {"$or": all_filters} if len(all_filters) > 1 else all_filters[0]
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
# get collection
|
|
160
|
+
collection = self.vectordb.get_collection(name=data.collection_name)
|
|
161
|
+
# get filtered data
|
|
162
|
+
output = collection.get(where=filters)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
self.logger.error(f"Exception occured: {e}")
|
|
165
|
+
raise
|
|
166
|
+
return {"output": output}
|
|
167
|
+
|
|
168
|
+
@app.post("/query")
|
|
169
|
+
def _query(self, data: DBQuery) -> dict:
|
|
170
|
+
"""
|
|
171
|
+
Retreives results for a given DB query
|
|
172
|
+
:param data:
|
|
173
|
+
:param type: DBQuery
|
|
174
|
+
:rtype: dict
|
|
175
|
+
"""
|
|
176
|
+
query_vec = self.encoding_model.embed_query(data.query)
|
|
177
|
+
query_vec = query_vec.tolist()
|
|
178
|
+
try:
|
|
179
|
+
# create a collection for the map data
|
|
180
|
+
collection = self.vectordb.get_collection(name=data.collection_name)
|
|
181
|
+
output = (
|
|
182
|
+
collection.query(query_embeddings=query_vec, n_results=data.n_results)
|
|
183
|
+
or []
|
|
184
|
+
)
|
|
185
|
+
except Exception as e:
|
|
186
|
+
self.logger.error(f"Exception occured: {e}")
|
|
187
|
+
raise
|
|
188
|
+
|
|
189
|
+
return {"output": output}
|
roboml/interfaces.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NodeInit(BaseModel):
|
|
8
|
+
"""NodeInit."""
|
|
9
|
+
|
|
10
|
+
node_name: str
|
|
11
|
+
node_type: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NodeDeinit(BaseModel):
|
|
15
|
+
"""NodeDeinit."""
|
|
16
|
+
|
|
17
|
+
node_name: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# IO Interfaces
|
|
21
|
+
class AudioInput(BaseModel):
|
|
22
|
+
"""
|
|
23
|
+
Input values for audio inference
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
query: Union[str, bytes] = Field(title="Audio input raw bytes", min_length=1)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ImageInput(BaseModel):
|
|
30
|
+
"""
|
|
31
|
+
Input values for image inference
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
35
|
+
|
|
36
|
+
images: Union[list[str], list[np.ndarray]] = Field(
|
|
37
|
+
title="List of images as base64 strings or numpy arrays", min_length=1
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TextInput(BaseModel):
|
|
42
|
+
"""
|
|
43
|
+
Input values for text inference
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
query: Union[str, list[dict]] = Field(title="Input to the model", min_length=1)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TextToSpeechInput(TextInput):
|
|
50
|
+
"""
|
|
51
|
+
Input values for text to speech inference
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
voice: Optional[str] = Field(title="Voice to use", default=None)
|
|
55
|
+
get_bytes: bool = Field(title="Get raw audio bytes", default=False)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class SpeechToTextInput(AudioInput):
|
|
59
|
+
"""
|
|
60
|
+
Input values for speech to text inference
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
max_new_tokens: int = Field(
|
|
64
|
+
title="Maximum number of new tokens to be generated", default=128
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class LLMInput(TextInput):
|
|
69
|
+
"""
|
|
70
|
+
Input values for LLM inference
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
max_new_tokens: int = Field(
|
|
74
|
+
title="Maximum number of new tokens to be generated", default=100
|
|
75
|
+
)
|
|
76
|
+
temperature: float = Field(
|
|
77
|
+
title="Temperature with which inference is to be generated", default=0.7
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class VLLMInput(ImageInput, LLMInput):
|
|
82
|
+
"""
|
|
83
|
+
Input values for multi modal LLM inference
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class DetectionInput(ImageInput):
|
|
90
|
+
"""Input for Detection models."""
|
|
91
|
+
|
|
92
|
+
threshold: float = Field(title="Detection confidence threshold", default=0.5)
|
|
93
|
+
get_dataset_labels: bool = Field(
|
|
94
|
+
title="Get dataset label string names", default=True
|
|
95
|
+
)
|
|
96
|
+
labels_to_track: Optional[list[str]] = Field(
|
|
97
|
+
title="List of labels to track. Only used if tracking is enabled during initialization",
|
|
98
|
+
default=None,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# DB Interfaces
|
|
103
|
+
class DBAdd(BaseModel):
|
|
104
|
+
"""
|
|
105
|
+
Documents to be added to DB
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
collection_name: str = Field(title="DB Collection Name")
|
|
109
|
+
ids: list[str] = Field(title="Document IDs", min_length=1)
|
|
110
|
+
metadatas: list[dict] = Field(title="Document metadatas", min_length=1)
|
|
111
|
+
documents: list[str] = Field(title="Documents", min_length=1)
|
|
112
|
+
distance_func: str = Field(
|
|
113
|
+
title="Distance function for the collection", default="l2"
|
|
114
|
+
)
|
|
115
|
+
reset_collection: bool = Field(
|
|
116
|
+
title="Delete existing collection with the name defined in collection_name and create it again with the data provided",
|
|
117
|
+
default=False,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class DBMetadataQuery(BaseModel):
|
|
122
|
+
"""
|
|
123
|
+
For retreiving documents based on metadata
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
collection_name: str = Field(title="DB Collection Name")
|
|
127
|
+
metadatas: list[dict] = Field(title="Document metadatas", min_length=1)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class DBQuery(BaseModel):
|
|
131
|
+
"""
|
|
132
|
+
For retreiving documents based on query
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
collection_name: str = Field(title="DB Collection Name")
|
|
136
|
+
query: str = Field(title="Query string")
|
|
137
|
+
n_results: int = Field(title="Number of results", default=1)
|
roboml/main.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from ray import serve
|
|
4
|
+
|
|
5
|
+
from roboml.ray.app_factory import AppFactory
|
|
6
|
+
from roboml.resp_server.server import Server
|
|
7
|
+
from roboml.utils import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_args_for_ray() -> argparse.Namespace:
|
|
11
|
+
"""Parse arguments."""
|
|
12
|
+
parser = argparse.ArgumentParser(description="Run models and event handlers")
|
|
13
|
+
parser.add_argument(
|
|
14
|
+
"--host", type=str, help="Specify server host address. Default '127.0.0.1'"
|
|
15
|
+
)
|
|
16
|
+
parser.add_argument("--port", type=int, help="Specify server port. Default 8000")
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"--nodes_per_cpu",
|
|
19
|
+
type=int,
|
|
20
|
+
help="Specify number of nodes to run per CPU. Default None",
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"--nodes_per_gpu",
|
|
24
|
+
type=int,
|
|
25
|
+
help="Specify number of nodes to run per GPU. Default None",
|
|
26
|
+
)
|
|
27
|
+
return parser.parse_args()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def ray() -> None:
|
|
31
|
+
"""Main entry function for ray"""
|
|
32
|
+
|
|
33
|
+
args = parse_args_for_ray()
|
|
34
|
+
host = args.host or "0.0.0.0"
|
|
35
|
+
port = args.port or 8000
|
|
36
|
+
nodes_per_cpu = args.nodes_per_cpu or None
|
|
37
|
+
nodes_per_gpu = args.nodes_per_gpu or None
|
|
38
|
+
app_factory = AppFactory.bind(
|
|
39
|
+
nodes_per_cpu=nodes_per_cpu, nodes_per_gpu=nodes_per_gpu
|
|
40
|
+
)
|
|
41
|
+
serve.start(http_options=serve.HTTPOptions(host=host, port=port))
|
|
42
|
+
serve.run(app_factory, name="app_factory", blocking=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def parse_args_for_resp() -> argparse.Namespace:
|
|
46
|
+
"""Parse arguments."""
|
|
47
|
+
parser = argparse.ArgumentParser(description="Run models and event handlers")
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--host", type=str, help="Specify server host address. Default '0.0.0.0'"
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument("--port", type=int, help="Specify server port. Default 6379")
|
|
52
|
+
return parser.parse_args()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def resp() -> None:
|
|
56
|
+
"""Main entry function for resp"""
|
|
57
|
+
|
|
58
|
+
args = parse_args_for_resp()
|
|
59
|
+
host = args.host or "0.0.0.0"
|
|
60
|
+
port = args.port or 6379
|
|
61
|
+
server = Server(logger)
|
|
62
|
+
server._start_server(host=host, port=port)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .mllm import Idefics, TransformersMLLM
|
|
2
|
+
from .llm import TransformersLLM
|
|
3
|
+
from .speech_to_text import Whisper
|
|
4
|
+
from .text_to_speech import Bark, SpeechT5
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"Whisper",
|
|
8
|
+
"TransformersLLM",
|
|
9
|
+
"TransformersMLLM",
|
|
10
|
+
"SpeechT5",
|
|
11
|
+
"Idefics",
|
|
12
|
+
"Bark",
|
|
13
|
+
]
|
roboml/models/_base.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from logging import Logger
|
|
3
|
+
from threading import Lock
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import ValidationError, validate_call
|
|
7
|
+
import torch
|
|
8
|
+
import inspect
|
|
9
|
+
|
|
10
|
+
from roboml.utils import Status, logging
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ModelTemplate:
|
|
14
|
+
"""
|
|
15
|
+
This class describes a model template.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, *, name: str, init_timeout: int = 600, **_):
|
|
19
|
+
self.name: str = name
|
|
20
|
+
self.init_timeout: Optional[int] = init_timeout # 10 minutes
|
|
21
|
+
self.device: str = "cuda" if torch.cuda.is_available() else "cpu"
|
|
22
|
+
self.model: Any = None
|
|
23
|
+
self.pre_processor: Any = None
|
|
24
|
+
self.status: Status = Status.LOADED
|
|
25
|
+
self.logger: Logger = logging.getLogger(self.name)
|
|
26
|
+
self.lock = Lock()
|
|
27
|
+
|
|
28
|
+
def initialize(self, **kwargs) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Calls the models initialization function and sets status accordingly
|
|
31
|
+
"""
|
|
32
|
+
if self.status == Status.READY:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
self.status = Status.INITIALIZING
|
|
36
|
+
try:
|
|
37
|
+
validated_init = validate_call(self._initialize)
|
|
38
|
+
validated_init(**kwargs)
|
|
39
|
+
except Exception as e:
|
|
40
|
+
self.logger.error(f"Initialization Error: {e}")
|
|
41
|
+
self.status = Status.INITIALIZATION_ERROR
|
|
42
|
+
raise e
|
|
43
|
+
self.logger.info(f"{self.__class__.__name__} Model initialized")
|
|
44
|
+
self.status = Status.READY
|
|
45
|
+
|
|
46
|
+
def inference(self, **kwargs) -> dict:
|
|
47
|
+
"""
|
|
48
|
+
Calls the models inference function and sets status accordingly
|
|
49
|
+
"""
|
|
50
|
+
if self.status is not Status.READY:
|
|
51
|
+
self.logger.error("Error: Inference called before initialization")
|
|
52
|
+
raise Exception("Inference called before initialization")
|
|
53
|
+
try:
|
|
54
|
+
data_model = (
|
|
55
|
+
inspect.signature(self._inference).parameters["data"].annotation
|
|
56
|
+
)
|
|
57
|
+
data = data_model(**kwargs)
|
|
58
|
+
except ValidationError:
|
|
59
|
+
self.logger.error("Validation Error occured for inference input")
|
|
60
|
+
raise
|
|
61
|
+
with self.lock:
|
|
62
|
+
result = self._inference(data)
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
def get_status(self):
|
|
66
|
+
"""Returns status of the model node"""
|
|
67
|
+
return self.status.name
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def _initialize(self, *_, **__) -> None:
|
|
71
|
+
"""Model specific initialization function, to be implemented in derived classes.
|
|
72
|
+
:param kwargs:
|
|
73
|
+
:rtype: None
|
|
74
|
+
"""
|
|
75
|
+
raise NotImplementedError(
|
|
76
|
+
"Model specific initializaion needs to be implemented by derived model classes"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def _inference(self, *_, **__) -> dict:
|
|
81
|
+
"""Model specific inference function, to be implemented in derived classes.
|
|
82
|
+
:rtype: dict
|
|
83
|
+
"""
|
|
84
|
+
raise NotImplementedError(
|
|
85
|
+
"Model specific inference needs to be implemented by derived model classes"
|
|
86
|
+
)
|