ragaai-catalyst 1.0.8.2__tar.gz → 2.0__tar.gz
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.
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/PKG-INFO +13 -14
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/pyproject.toml +13 -14
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/__init__.py +2 -1
- ragaai_catalyst-2.0/ragaai_catalyst/dataset.py +287 -0
- ragaai_catalyst-2.0/ragaai_catalyst/evaluation.py +369 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/experiment.py +1 -1
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/prompt_manager.py +112 -54
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/ragaai_catalyst.py +45 -20
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/exporters/file_span_exporter.py +16 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/exporters/raga_exporter.py +50 -27
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/tracer.py +33 -26
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst.egg-info/PKG-INFO +13 -14
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst.egg-info/SOURCES.txt +1 -0
- ragaai_catalyst-2.0/ragaai_catalyst.egg-info/requires.txt +23 -0
- ragaai_catalyst-1.0.8.2/ragaai_catalyst/dataset.py +0 -227
- ragaai_catalyst-1.0.8.2/ragaai_catalyst.egg-info/requires.txt +0 -24
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/.gitignore +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/README.md +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/__init__.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/docs/dataset_management.md +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/docs/prompt_management.md +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/examples/prompt_management_litellm.ipynb +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/examples/prompt_management_openai.ipynb +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/_version.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/__init__.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/exporters/__init__.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/instrumentators/__init__.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/instrumentators/langchain.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/instrumentators/llamaindex.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/instrumentators/openai.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/utils/__init__.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/tracers/utils/utils.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst/utils.py +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst.egg-info/dependency_links.txt +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/ragaai_catalyst.egg-info/top_level.txt +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/requirements.txt +0 -0
- {ragaai_catalyst-1.0.8.2 → ragaai_catalyst-2.0}/setup.cfg +0 -0
@@ -1,26 +1,25 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ragaai_catalyst
|
3
|
-
Version:
|
3
|
+
Version: 2.0
|
4
4
|
Summary: RAGA AI CATALYST
|
5
5
|
Author-email: Kiran Scaria <kiran.scaria@raga.ai>, Kedar Gaikwad <kedar.gaikwad@raga.ai>, Dushyant Mahajan <dushyant.mahajan@raga.ai>, Siddhartha Kosti <siddhartha.kosti@raga.ai>, Ritika Goel <ritika.goel@raga.ai>, Vijay Chaurasia <vijay.chaurasia@raga.ai>
|
6
6
|
Requires-Python: >=3.9
|
7
7
|
Description-Content-Type: text/markdown
|
8
8
|
Requires-Dist: aiohttp>=3.10.2
|
9
|
-
Requires-Dist: opentelemetry-api==1.
|
10
|
-
Requires-Dist: opentelemetry-sdk==1.
|
11
|
-
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.
|
12
|
-
Requires-Dist: opentelemetry-instrumentation==0.
|
13
|
-
Requires-Dist: opentelemetry-instrumentation-fastapi==0.
|
14
|
-
Requires-Dist: opentelemetry-instrumentation-asgi==0.
|
15
|
-
Requires-Dist: opentelemetry-semantic-conventions==0.
|
16
|
-
Requires-Dist: opentelemetry-util-http==0.
|
17
|
-
Requires-Dist: opentelemetry-instrumentation-langchain
|
18
|
-
Requires-Dist: opentelemetry-instrumentation-openai
|
19
|
-
Requires-Dist: langchain-core~=0.
|
20
|
-
Requires-Dist: langchain~=0.
|
9
|
+
Requires-Dist: opentelemetry-api==1.27.0
|
10
|
+
Requires-Dist: opentelemetry-sdk==1.27.0
|
11
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.27.0
|
12
|
+
Requires-Dist: opentelemetry-instrumentation==0.48b0
|
13
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi==0.48b0
|
14
|
+
Requires-Dist: opentelemetry-instrumentation-asgi==0.48b0
|
15
|
+
Requires-Dist: opentelemetry-semantic-conventions==0.48b0
|
16
|
+
Requires-Dist: opentelemetry-util-http==0.48b0
|
17
|
+
Requires-Dist: opentelemetry-instrumentation-langchain==0.30.0
|
18
|
+
Requires-Dist: opentelemetry-instrumentation-openai==0.30.0
|
19
|
+
Requires-Dist: langchain-core~=0.3.6
|
20
|
+
Requires-Dist: langchain~=0.3.1
|
21
21
|
Requires-Dist: openai>=1.35.10
|
22
22
|
Requires-Dist: pandas>=2.1.1
|
23
|
-
Requires-Dist: tenacity==8.3.0
|
24
23
|
Provides-Extra: dev
|
25
24
|
Requires-Dist: pytest; extra == "dev"
|
26
25
|
Requires-Dist: pytest-cov; extra == "dev"
|
@@ -8,7 +8,7 @@ description = "RAGA AI CATALYST"
|
|
8
8
|
readme = "README.md"
|
9
9
|
requires-python = ">=3.9"
|
10
10
|
# license = {file = "LICENSE"}
|
11
|
-
version = "
|
11
|
+
version = "2.0"
|
12
12
|
authors = [
|
13
13
|
{name = "Kiran Scaria", email = "kiran.scaria@raga.ai"},
|
14
14
|
{name = "Kedar Gaikwad", email = "kedar.gaikwad@raga.ai"},
|
@@ -20,21 +20,20 @@ authors = [
|
|
20
20
|
|
21
21
|
dependencies = [
|
22
22
|
"aiohttp>=3.10.2", # Ref: https://github.com/raga-ai-hub/ragaai-catalyst/security/dependabot/1
|
23
|
-
"opentelemetry-api==1.
|
24
|
-
"opentelemetry-sdk==1.
|
25
|
-
"opentelemetry-exporter-otlp-proto-grpc==1.
|
26
|
-
"opentelemetry-instrumentation==0.
|
27
|
-
"opentelemetry-instrumentation-fastapi==0.
|
28
|
-
"opentelemetry-instrumentation-asgi==0.
|
29
|
-
"opentelemetry-semantic-conventions==0.
|
30
|
-
"opentelemetry-util-http==0.
|
31
|
-
"opentelemetry-instrumentation-langchain
|
32
|
-
"opentelemetry-instrumentation-openai
|
33
|
-
"langchain-core~=0.
|
34
|
-
"langchain~=0.
|
23
|
+
"opentelemetry-api==1.27.0",
|
24
|
+
"opentelemetry-sdk==1.27.0",
|
25
|
+
"opentelemetry-exporter-otlp-proto-grpc==1.27.0",
|
26
|
+
"opentelemetry-instrumentation==0.48b0",
|
27
|
+
"opentelemetry-instrumentation-fastapi==0.48b0",
|
28
|
+
"opentelemetry-instrumentation-asgi==0.48b0",
|
29
|
+
"opentelemetry-semantic-conventions==0.48b0",
|
30
|
+
"opentelemetry-util-http==0.48b0",
|
31
|
+
"opentelemetry-instrumentation-langchain==0.30.0",
|
32
|
+
"opentelemetry-instrumentation-openai==0.30.0",
|
33
|
+
"langchain-core~=0.3.6",
|
34
|
+
"langchain~=0.3.1",
|
35
35
|
"openai>=1.35.10",
|
36
36
|
"pandas>=2.1.1",
|
37
|
-
"tenacity==8.3.0",
|
38
37
|
]
|
39
38
|
|
40
39
|
[project.optional-dependencies]
|
@@ -4,5 +4,6 @@ from .tracers import Tracer
|
|
4
4
|
from .utils import response_checker
|
5
5
|
from .dataset import Dataset
|
6
6
|
from .prompt_manager import PromptManager
|
7
|
+
from .evaluation import Evaluation
|
7
8
|
|
8
|
-
__all__ = ["Experiment", "RagaAICatalyst", "Tracer", "PromptManager"]
|
9
|
+
__all__ = ["Experiment", "RagaAICatalyst", "Tracer", "PromptManager", "Evaluation"]
|
@@ -0,0 +1,287 @@
|
|
1
|
+
import os
|
2
|
+
import requests
|
3
|
+
from .utils import response_checker
|
4
|
+
from typing import Union
|
5
|
+
import logging
|
6
|
+
from .ragaai_catalyst import RagaAICatalyst
|
7
|
+
import pandas as pd
|
8
|
+
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
get_token = RagaAICatalyst.get_token
|
11
|
+
|
12
|
+
|
13
|
+
class Dataset:
|
14
|
+
BASE_URL = None
|
15
|
+
TIMEOUT = 30
|
16
|
+
|
17
|
+
def __init__(self, project_name):
|
18
|
+
self.project_name = project_name
|
19
|
+
self.num_projects = 100
|
20
|
+
Dataset.BASE_URL = (
|
21
|
+
os.getenv("RAGAAI_CATALYST_BASE_URL")
|
22
|
+
if os.getenv("RAGAAI_CATALYST_BASE_URL")
|
23
|
+
else "https://catalyst.raga.ai/api"
|
24
|
+
)
|
25
|
+
headers = {
|
26
|
+
"Authorization": f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
|
27
|
+
}
|
28
|
+
try:
|
29
|
+
response = requests.get(
|
30
|
+
f"{Dataset.BASE_URL}/v2/llm/projects?size={self.num_projects}",
|
31
|
+
headers=headers,
|
32
|
+
timeout=self.TIMEOUT,
|
33
|
+
)
|
34
|
+
response.raise_for_status()
|
35
|
+
logger.debug("Projects list retrieved successfully")
|
36
|
+
|
37
|
+
project_list = [
|
38
|
+
project["name"] for project in response.json()["data"]["content"]
|
39
|
+
]
|
40
|
+
|
41
|
+
if project_name not in project_list:
|
42
|
+
raise ValueError("Project not found. Please enter a valid project name")
|
43
|
+
|
44
|
+
self.project_id = [
|
45
|
+
project["id"] for project in response.json()["data"]["content"] if project["name"] == project_name
|
46
|
+
][0]
|
47
|
+
|
48
|
+
except requests.exceptions.RequestException as e:
|
49
|
+
logger.error(f"Failed to retrieve projects list: {e}")
|
50
|
+
raise
|
51
|
+
|
52
|
+
def list_datasets(self):
|
53
|
+
"""
|
54
|
+
Retrieves a list of datasets for a given project.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
list: A list of dataset names.
|
58
|
+
|
59
|
+
Raises:
|
60
|
+
None.
|
61
|
+
"""
|
62
|
+
|
63
|
+
def make_request():
|
64
|
+
headers = {
|
65
|
+
'Content-Type': 'application/json',
|
66
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
67
|
+
"X-Project-Id": str(self.project_id),
|
68
|
+
}
|
69
|
+
json_data = {"size": 12, "page": "0", "projectId": str(self.project_id), "search": ""}
|
70
|
+
try:
|
71
|
+
response = requests.post(
|
72
|
+
f"{Dataset.BASE_URL}/v2/llm/dataset",
|
73
|
+
headers=headers,
|
74
|
+
json=json_data,
|
75
|
+
timeout=Dataset.TIMEOUT,
|
76
|
+
)
|
77
|
+
response.raise_for_status()
|
78
|
+
return response
|
79
|
+
except requests.exceptions.RequestException as e:
|
80
|
+
logger.error(f"Failed to list datasets: {e}")
|
81
|
+
raise
|
82
|
+
|
83
|
+
try:
|
84
|
+
response = make_request()
|
85
|
+
response_checker(response, "Dataset.list_datasets")
|
86
|
+
if response.status_code == 401:
|
87
|
+
get_token() # Fetch a new token and set it in the environment
|
88
|
+
response = make_request() # Retry the request
|
89
|
+
if response.status_code != 200:
|
90
|
+
return {
|
91
|
+
"status_code": response.status_code,
|
92
|
+
"message": response.json(),
|
93
|
+
}
|
94
|
+
datasets = response.json()["data"]["content"]
|
95
|
+
dataset_list = [dataset["name"] for dataset in datasets]
|
96
|
+
return dataset_list
|
97
|
+
except Exception as e:
|
98
|
+
logger.error(f"Error in list_datasets: {e}")
|
99
|
+
raise
|
100
|
+
|
101
|
+
def get_schema_mapping(self):
|
102
|
+
return ["traceid", "prompt", "context", "response", "expected_response", "expected_context", "timestamp", "metadata", "pipeline", "cost", "feedBack", "latency", "sanitized_response", "system_prompt", "traceUri"]
|
103
|
+
|
104
|
+
def create_from_trace(self, dataset_name, filter_list):
|
105
|
+
"""
|
106
|
+
Creates a new dataset with the given `dataset_name` and `filter_list`.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
dataset_name (str): The name of the dataset to be created.
|
110
|
+
filter_list (list): A list of filters to be applied to the dataset.
|
111
|
+
|
112
|
+
Returns:
|
113
|
+
str: A message indicating the success of the dataset creation and the name of the created dataset.
|
114
|
+
|
115
|
+
Raises:
|
116
|
+
None
|
117
|
+
|
118
|
+
"""
|
119
|
+
|
120
|
+
def request_trace_creation():
|
121
|
+
headers = {
|
122
|
+
"Content-Type": "application/json",
|
123
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
124
|
+
"X-Project-Name": self.project_name,
|
125
|
+
}
|
126
|
+
json_data = {
|
127
|
+
"projectName": self.project_name,
|
128
|
+
"subDatasetName": dataset_name,
|
129
|
+
"filterList": filter_list,
|
130
|
+
}
|
131
|
+
try:
|
132
|
+
response = requests.post(
|
133
|
+
f"{Dataset.BASE_URL}/v1/llm/sub-dataset",
|
134
|
+
headers=headers,
|
135
|
+
json=json_data,
|
136
|
+
timeout=Dataset.TIMEOUT,
|
137
|
+
)
|
138
|
+
response.raise_for_status()
|
139
|
+
return response
|
140
|
+
except requests.exceptions.RequestException as e:
|
141
|
+
logger.error(f"Failed to create dataset from trace: {e}")
|
142
|
+
raise
|
143
|
+
|
144
|
+
try:
|
145
|
+
response = request_trace_creation()
|
146
|
+
response_checker(response, "Dataset.create_dataset")
|
147
|
+
if response.status_code == 401:
|
148
|
+
get_token() # Fetch a new token and set it in the environment
|
149
|
+
response = request_trace_creation() # Retry the request
|
150
|
+
if response.status_code != 200:
|
151
|
+
return response.json()["message"]
|
152
|
+
message = response.json()["message"]
|
153
|
+
return f"{message} {dataset_name}"
|
154
|
+
except Exception as e:
|
155
|
+
logger.error(f"Error in create_from_trace: {e}")
|
156
|
+
raise
|
157
|
+
|
158
|
+
###################### CSV Upload APIs ###################
|
159
|
+
|
160
|
+
def get_csv_schema(self):
|
161
|
+
headers = {
|
162
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
163
|
+
"X-Project-Name": self.project_name,
|
164
|
+
}
|
165
|
+
try:
|
166
|
+
response = requests.get(
|
167
|
+
f"{Dataset.BASE_URL}/v1/llm/schema-elements",
|
168
|
+
headers=headers,
|
169
|
+
timeout=Dataset.TIMEOUT,
|
170
|
+
)
|
171
|
+
response.raise_for_status()
|
172
|
+
response_data = response.json()
|
173
|
+
if not response_data['success']:
|
174
|
+
raise ValueError('Unable to fetch Schema Elements for the CSV')
|
175
|
+
return response_data
|
176
|
+
except requests.exceptions.RequestException as e:
|
177
|
+
logger.error(f"Failed to get CSV schema: {e}")
|
178
|
+
raise
|
179
|
+
|
180
|
+
def create_from_csv(self, csv_path, dataset_name, schema_mapping):
|
181
|
+
list_dataset = self.list_datasets()
|
182
|
+
if dataset_name in list_dataset:
|
183
|
+
raise ValueError(f"Dataset name {dataset_name} already exists. Please enter a unique dataset name")
|
184
|
+
|
185
|
+
#### get presigned URL
|
186
|
+
def get_presignedUrl():
|
187
|
+
headers = {
|
188
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
189
|
+
"X-Project-Id": str(self.project_id),
|
190
|
+
}
|
191
|
+
try:
|
192
|
+
response = requests.get(
|
193
|
+
f"{Dataset.BASE_URL}/v2/llm/dataset/csv/presigned-url",
|
194
|
+
headers=headers,
|
195
|
+
timeout=Dataset.TIMEOUT,
|
196
|
+
)
|
197
|
+
response.raise_for_status()
|
198
|
+
return response.json()
|
199
|
+
except requests.exceptions.RequestException as e:
|
200
|
+
logger.error(f"Failed to get presigned URL: {e}")
|
201
|
+
raise
|
202
|
+
|
203
|
+
try:
|
204
|
+
presignedUrl = get_presignedUrl()
|
205
|
+
if presignedUrl['success']:
|
206
|
+
url = presignedUrl['data']['presignedUrl']
|
207
|
+
filename = presignedUrl['data']['fileName']
|
208
|
+
else:
|
209
|
+
raise ValueError('Unable to fetch presignedUrl')
|
210
|
+
except Exception as e:
|
211
|
+
logger.error(f"Error in get_presignedUrl: {e}")
|
212
|
+
raise
|
213
|
+
|
214
|
+
#### put csv to presigned URL
|
215
|
+
def put_csv_to_presignedUrl(url):
|
216
|
+
headers = {
|
217
|
+
'Content-Type': 'text/csv',
|
218
|
+
'x-ms-blob-type': 'BlockBlob',
|
219
|
+
}
|
220
|
+
try:
|
221
|
+
with open(csv_path, 'rb') as file:
|
222
|
+
response = requests.put(
|
223
|
+
url,
|
224
|
+
headers=headers,
|
225
|
+
data=file,
|
226
|
+
timeout=Dataset.TIMEOUT,
|
227
|
+
)
|
228
|
+
response.raise_for_status()
|
229
|
+
return response
|
230
|
+
except requests.exceptions.RequestException as e:
|
231
|
+
logger.error(f"Failed to put CSV to presigned URL: {e}")
|
232
|
+
raise
|
233
|
+
|
234
|
+
try:
|
235
|
+
put_csv_response = put_csv_to_presignedUrl(url)
|
236
|
+
if put_csv_response.status_code != 200:
|
237
|
+
raise ValueError('Unable to put csv to the presignedUrl')
|
238
|
+
except Exception as e:
|
239
|
+
logger.error(f"Error in put_csv_to_presignedUrl: {e}")
|
240
|
+
raise
|
241
|
+
|
242
|
+
## Upload csv to elastic
|
243
|
+
def upload_csv_to_elastic(data):
|
244
|
+
header = {
|
245
|
+
'Content-Type': 'application/json',
|
246
|
+
'Authorization': f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
247
|
+
"X-Project-Id": str(self.project_id)
|
248
|
+
}
|
249
|
+
try:
|
250
|
+
response = requests.post(
|
251
|
+
f"{Dataset.BASE_URL}/v2/llm/dataset/csv",
|
252
|
+
headers=header,
|
253
|
+
json=data,
|
254
|
+
timeout=Dataset.TIMEOUT,
|
255
|
+
)
|
256
|
+
if response.status_code==400:
|
257
|
+
raise ValueError(response.json()["message"])
|
258
|
+
response.raise_for_status()
|
259
|
+
return response.json()
|
260
|
+
except requests.exceptions.RequestException as e:
|
261
|
+
logger.error(f"Failed to upload CSV to elastic: {e}")
|
262
|
+
raise
|
263
|
+
|
264
|
+
def generate_schema(mapping):
|
265
|
+
result = {}
|
266
|
+
for column, schema_element in mapping.items():
|
267
|
+
result[column] = {"columnType": schema_element}
|
268
|
+
return result
|
269
|
+
|
270
|
+
try:
|
271
|
+
schema_mapping = generate_schema(schema_mapping)
|
272
|
+
data = {
|
273
|
+
"projectId": str(self.project_id),
|
274
|
+
"datasetName": dataset_name,
|
275
|
+
"fileName": filename,
|
276
|
+
"schemaMapping": schema_mapping,
|
277
|
+
"opType": "insert",
|
278
|
+
"description": ""
|
279
|
+
}
|
280
|
+
upload_csv_response = upload_csv_to_elastic(data)
|
281
|
+
if not upload_csv_response['success']:
|
282
|
+
raise ValueError('Unable to upload csv')
|
283
|
+
else:
|
284
|
+
print(upload_csv_response['message'])
|
285
|
+
except Exception as e:
|
286
|
+
logger.error(f"Error in create_from_csv: {e}")
|
287
|
+
raise
|