llumo 0.2.14b7__py3-none-any.whl → 0.2.15__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.
- llumo/client.py +351 -145
- llumo/exceptions.py +4 -0
- llumo/execution.py +4 -5
- llumo/helpingFuntions.py +67 -22
- llumo/models.py +63 -26
- {llumo-0.2.14b7.dist-info → llumo-0.2.15.dist-info}/METADATA +1 -1
- llumo-0.2.15.dist-info/RECORD +13 -0
- llumo-0.2.14b7.dist-info/RECORD +0 -13
- {llumo-0.2.14b7.dist-info → llumo-0.2.15.dist-info}/WHEEL +0 -0
- {llumo-0.2.14b7.dist-info → llumo-0.2.15.dist-info}/licenses/LICENSE +0 -0
- {llumo-0.2.14b7.dist-info → llumo-0.2.15.dist-info}/top_level.txt +0 -0
llumo/client.py
CHANGED
@@ -5,12 +5,12 @@ import time
|
|
5
5
|
import re
|
6
6
|
import json
|
7
7
|
import uuid
|
8
|
-
|
8
|
+
import warnings
|
9
9
|
import os
|
10
10
|
import itertools
|
11
11
|
import pandas as pd
|
12
12
|
from typing import List, Dict
|
13
|
-
from .models import AVAILABLEMODELS, getProviderFromModel
|
13
|
+
from .models import AVAILABLEMODELS, getProviderFromModel, Provider
|
14
14
|
from .execution import ModelExecutor
|
15
15
|
from .exceptions import LlumoAIError
|
16
16
|
from .helpingFuntions import *
|
@@ -19,6 +19,8 @@ from .functionCalling import LlumoAgentExecutor
|
|
19
19
|
import threading
|
20
20
|
from tqdm import tqdm
|
21
21
|
|
22
|
+
pd.set_option('future.no_silent_downcasting', True)
|
23
|
+
|
22
24
|
postUrl = (
|
23
25
|
"https://red-skull-service-392377961931.us-central1.run.app/api/process-playground"
|
24
26
|
)
|
@@ -38,7 +40,7 @@ class LlumoClient:
|
|
38
40
|
|
39
41
|
def __init__(self, api_key):
|
40
42
|
self.apiKey = api_key
|
41
|
-
|
43
|
+
|
42
44
|
self.processMapping = {}
|
43
45
|
self.definationMapping = {}
|
44
46
|
|
@@ -50,6 +52,7 @@ class LlumoClient:
|
|
50
52
|
reqBody = {"analytics": [evalName]}
|
51
53
|
|
52
54
|
try:
|
55
|
+
|
53
56
|
response = requests.post(url=validateUrl, json=reqBody, headers=headers)
|
54
57
|
|
55
58
|
except requests.exceptions.RequestException as e:
|
@@ -581,7 +584,8 @@ class LlumoClient:
|
|
581
584
|
createExperiment: bool = False,
|
582
585
|
_tocheck=True,
|
583
586
|
):
|
584
|
-
|
587
|
+
self.socket = LlumoSocketClient(socketUrl)
|
588
|
+
dataframe = pd.DataFrame(data).astype(str)
|
585
589
|
workspaceID = None
|
586
590
|
email = None
|
587
591
|
socketID = self.socket.connect(timeout=250)
|
@@ -774,9 +778,6 @@ class LlumoClient:
|
|
774
778
|
rawResults.extend(dataFromDb)
|
775
779
|
|
776
780
|
|
777
|
-
|
778
|
-
|
779
|
-
|
780
781
|
|
781
782
|
# Initialize dataframe columns for each eval
|
782
783
|
for eval in evals:
|
@@ -797,7 +798,12 @@ class LlumoClient:
|
|
797
798
|
|
798
799
|
if createExperiment:
|
799
800
|
pd.set_option("future.no_silent_downcasting", True)
|
800
|
-
df = dataframe.fillna("Some error occured").astype(object)
|
801
|
+
# df = dataframe.fillna("Some error occured").astype(object)
|
802
|
+
with warnings.catch_warnings():
|
803
|
+
warnings.simplefilter(action='ignore', category=FutureWarning)
|
804
|
+
df = dataframe.fillna("Some error occurred").astype(str)
|
805
|
+
|
806
|
+
df = dataframe.fillna("Some error occured").infer_objects(copy=False)
|
801
807
|
if createPlayground(
|
802
808
|
email,
|
803
809
|
workspaceID,
|
@@ -812,7 +818,7 @@ class LlumoClient:
|
|
812
818
|
else:
|
813
819
|
return dataframe
|
814
820
|
|
815
|
-
def
|
821
|
+
def promptSweep(
|
816
822
|
self,
|
817
823
|
templates: List[str],
|
818
824
|
dataset: Dict[str, List[str]],
|
@@ -821,9 +827,15 @@ class LlumoClient:
|
|
821
827
|
evals=["Response Correctness"],
|
822
828
|
toEvaluate: bool = False,
|
823
829
|
createExperiment: bool = False,
|
830
|
+
|
831
|
+
|
824
832
|
) -> pd.DataFrame:
|
825
833
|
|
826
|
-
|
834
|
+
modelStatus = validateModels(model_aliases=model_aliases)
|
835
|
+
if modelStatus["status"]== False:
|
836
|
+
raise LlumoAIError.providerError(modelStatus["message"])
|
837
|
+
|
838
|
+
self.validateApiKey()
|
827
839
|
workspaceID = self.workspaceID
|
828
840
|
email = self.email
|
829
841
|
executor = ModelExecutor(apiKey)
|
@@ -928,6 +940,7 @@ class LlumoClient:
|
|
928
940
|
evals=["Final Task Alignment"],
|
929
941
|
prompt_template="Give answer for the given query: {{query}}",
|
930
942
|
createExperiment: bool = False,
|
943
|
+
|
931
944
|
):
|
932
945
|
if model.lower() not in ["openai", "google"]:
|
933
946
|
raise ValueError("Model must be 'openai' or 'google'")
|
@@ -1002,174 +1015,367 @@ class LlumoClient:
|
|
1002
1015
|
except Exception as e:
|
1003
1016
|
raise e
|
1004
1017
|
|
1005
|
-
def
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1018
|
+
def ragSweep(
|
1019
|
+
self,
|
1020
|
+
data,
|
1021
|
+
streamName: str,
|
1022
|
+
queryColName: str = "query",
|
1023
|
+
createExperiment: bool = False,
|
1024
|
+
modelAliases=[],
|
1025
|
+
apiKey="",
|
1026
|
+
prompt_template="Give answer to the given: {{query}} using the context:{{context}}",
|
1027
|
+
evals=["Context Utilization"],
|
1028
|
+
toEvaluate=False,
|
1029
|
+
generateOutput=True
|
1011
1030
|
):
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
)
|
1026
|
-
# print(f"Connected with socket ID: {socketID}")
|
1027
|
-
rowIdMapping = {}
|
1031
|
+
# Validate required parameters
|
1032
|
+
if generateOutput:
|
1033
|
+
if not modelAliases:
|
1034
|
+
raise ValueError("Model aliases must be provided when generateOutput is True.")
|
1035
|
+
if not apiKey or not isinstance(apiKey, str) or apiKey.strip() == "":
|
1036
|
+
raise ValueError("Valid API key must be provided when generateOutput is True.")
|
1037
|
+
|
1038
|
+
modelStatus = validateModels(model_aliases=modelAliases)
|
1039
|
+
if modelStatus["status"]== False:
|
1040
|
+
if len(modelAliases) == 0:
|
1041
|
+
raise LlumoAIError.providerError("No model selected.")
|
1042
|
+
else:
|
1043
|
+
raise LlumoAIError.providerError(modelStatus["message"])
|
1028
1044
|
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1045
|
+
# Copy the original dataframe
|
1046
|
+
original_df = pd.DataFrame(data)
|
1047
|
+
working_df = original_df.copy()
|
1032
1048
|
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1049
|
+
# Connect to socket
|
1050
|
+
self.socket = LlumoSocketClient(socketUrl)
|
1051
|
+
socketID = self.socket.connect(timeout=150)
|
1052
|
+
waited_secs = 0
|
1053
|
+
while not self.socket._connection_established.is_set():
|
1054
|
+
time.sleep(0.1)
|
1055
|
+
waited_secs += 0.1
|
1056
|
+
if waited_secs >= 20:
|
1057
|
+
raise RuntimeError("Timeout waiting for server 'connection-established' event.")
|
1042
1058
|
|
1043
|
-
|
1044
|
-
if not userHits["success"]:
|
1045
|
-
raise LlumoAIError.InsufficientCredits(userHits["message"])
|
1059
|
+
self.validateApiKey()
|
1046
1060
|
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
currentBatch = []
|
1061
|
+
# Check user credits
|
1062
|
+
userHits = checkUserHits(
|
1063
|
+
self.workspaceID, self.hasSubscribed, self.trialEndDate,
|
1064
|
+
self.subscriptionEndDate, self.hitsAvailable, len(working_df)
|
1065
|
+
)
|
1066
|
+
if not userHits["success"]:
|
1067
|
+
raise LlumoAIError.InsufficientCredits(userHits["message"])
|
1055
1068
|
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
rowID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
1061
|
-
columnID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
1069
|
+
print("====🚀Sit back while we fetch data from the stream 🚀====")
|
1070
|
+
workspaceID, email = self.workspaceID, self.email
|
1071
|
+
activePlayground = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
1072
|
+
streamId = getStreamId(workspaceID, self.apiKey, streamName)
|
1062
1073
|
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
"socketID": socketID,
|
1068
|
-
"processData": {
|
1069
|
-
"executionDependency": {"query": row[queryColName]},
|
1070
|
-
"dataStreamID": streamId,
|
1071
|
-
},
|
1072
|
-
"workspaceID": workspaceID,
|
1073
|
-
"email": email,
|
1074
|
-
"type": "DATA_STREAM",
|
1075
|
-
"playgroundID": activePlayground,
|
1076
|
-
"processType": "DATA_STREAM",
|
1077
|
-
"rowID": rowID,
|
1078
|
-
"columnID": columnID,
|
1079
|
-
"source": "SDK",
|
1080
|
-
}
|
1074
|
+
# Prepare batches
|
1075
|
+
rowIdMapping = {}
|
1076
|
+
self.allBatches = []
|
1077
|
+
currentBatch = []
|
1081
1078
|
|
1082
|
-
|
1079
|
+
expectedResults = len(working_df)
|
1080
|
+
timeout = max(100, min(150, expectedResults * 10))
|
1083
1081
|
|
1084
|
-
|
1082
|
+
listener_thread = threading.Thread(
|
1083
|
+
target=self.socket.listenForResults,
|
1084
|
+
kwargs={
|
1085
|
+
"min_wait": 40,
|
1086
|
+
"max_wait": timeout,
|
1087
|
+
"inactivity_timeout": 10,
|
1088
|
+
"expected_results": expectedResults,
|
1089
|
+
},
|
1090
|
+
daemon=True
|
1091
|
+
)
|
1092
|
+
listener_thread.start()
|
1093
|
+
|
1094
|
+
for index, row in working_df.iterrows():
|
1095
|
+
rowID, columnID = uuid.uuid4().hex, uuid.uuid4().hex
|
1096
|
+
compoundKey = f"{rowID}-{columnID}-{columnID}"
|
1097
|
+
rowIdMapping[compoundKey] = {"index": index}
|
1098
|
+
templateData = {
|
1099
|
+
"processID": getProcessID(),
|
1100
|
+
"socketID": socketID,
|
1101
|
+
"processData": {
|
1102
|
+
"executionDependency": {"query": row[queryColName]},
|
1103
|
+
"dataStreamID": streamId,
|
1104
|
+
},
|
1105
|
+
"workspaceID": workspaceID,
|
1106
|
+
"email": email,
|
1107
|
+
"type": "DATA_STREAM",
|
1108
|
+
"playgroundID": activePlayground,
|
1109
|
+
"processType": "DATA_STREAM",
|
1110
|
+
"rowID": rowID,
|
1111
|
+
"columnID": columnID,
|
1112
|
+
"source": "SDK",
|
1113
|
+
}
|
1114
|
+
currentBatch.append(templateData)
|
1115
|
+
if len(currentBatch) == 10 or index == len(working_df) - 1:
|
1085
1116
|
self.allBatches.append(currentBatch)
|
1086
1117
|
currentBatch = []
|
1087
1118
|
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1119
|
+
for batch in tqdm(self.allBatches, desc="Processing Batches", unit="batch", colour="magenta", ncols=80):
|
1120
|
+
try:
|
1121
|
+
self.postDataStream(batch=batch, workspaceID=workspaceID)
|
1122
|
+
time.sleep(3)
|
1123
|
+
except Exception as e:
|
1124
|
+
print(f"Error posting batch: {e}")
|
1125
|
+
raise
|
1091
1126
|
|
1092
|
-
|
1093
|
-
|
1127
|
+
time.sleep(3)
|
1128
|
+
listener_thread.join()
|
1129
|
+
|
1130
|
+
rawResults = self.socket.getReceivedData()
|
1131
|
+
expectedRowIDs = set(rowIdMapping.keys())
|
1132
|
+
receivedRowIDs = {key for item in rawResults for key in item.keys()}
|
1133
|
+
missingRowIDs = list(expectedRowIDs - receivedRowIDs)
|
1134
|
+
|
1135
|
+
if missingRowIDs:
|
1136
|
+
dataFromDb = fetchData(workspaceID, activePlayground, missingRowIDs)
|
1137
|
+
rawResults.extend(dataFromDb)
|
1138
|
+
|
1139
|
+
working_df["context"] = None
|
1140
|
+
for item in rawResults:
|
1141
|
+
for compound_key, value in item.items():
|
1142
|
+
if compound_key in rowIdMapping:
|
1143
|
+
idx = rowIdMapping[compound_key]["index"]
|
1144
|
+
working_df.at[idx, "context"] = value.get("value")
|
1145
|
+
|
1146
|
+
# Output generation
|
1147
|
+
if generateOutput == True:
|
1148
|
+
working_df = self._outputForStream(working_df, modelAliases, prompt_template, apiKey)
|
1149
|
+
|
1150
|
+
# Optional evaluation
|
1151
|
+
outputEvalMapping = None
|
1152
|
+
if toEvaluate:
|
1153
|
+
for evalName in evals:
|
1154
|
+
# Validate API and dependencies
|
1155
|
+
self.validateApiKey(evalName=evalName)
|
1156
|
+
metricDependencies = checkDependency(
|
1157
|
+
evalName, list(working_df.columns), tocheck=False
|
1158
|
+
)
|
1159
|
+
if not metricDependencies["status"]:
|
1160
|
+
raise LlumoAIError.dependencyError(metricDependencies["message"])
|
1161
|
+
|
1162
|
+
working_df, outputEvalMapping = self._evaluateForStream(working_df, evals, modelAliases, prompt_template,generateOutput)
|
1163
|
+
|
1164
|
+
|
1165
|
+
self.socket.disconnect()
|
1166
|
+
|
1167
|
+
# Create experiment if required
|
1168
|
+
if createExperiment:
|
1169
|
+
# df = working_df.fillna("Some error occured").astype(object)
|
1170
|
+
with warnings.catch_warnings():
|
1171
|
+
warnings.simplefilter(action='ignore', category=FutureWarning)
|
1172
|
+
df = working_df.fillna("Some error occurred").astype(str)
|
1173
|
+
if createPlayground(
|
1174
|
+
email, workspaceID, df,
|
1175
|
+
queryColName=queryColName,
|
1176
|
+
dataStreamName=streamId,
|
1177
|
+
promptText=prompt_template,
|
1178
|
+
definationMapping=self.definationMapping,
|
1179
|
+
evalOutputMap=outputEvalMapping
|
1180
|
+
):
|
1181
|
+
print(
|
1182
|
+
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results.")
|
1183
|
+
else:
|
1184
|
+
self.latestDataframe = working_df
|
1185
|
+
return working_df
|
1186
|
+
|
1187
|
+
def _outputForStream(self, df, modelAliases, prompt_template, apiKey):
|
1188
|
+
executor = ModelExecutor(apiKey)
|
1189
|
+
|
1190
|
+
for indx, row in df.iterrows():
|
1191
|
+
inputVariables = re.findall(r"{{(.*?)}}", prompt_template)
|
1192
|
+
if not all([k in df.columns for k in inputVariables]):
|
1193
|
+
raise LlumoAIError.InvalidPromptTemplate()
|
1194
|
+
|
1195
|
+
inputDict = {key: row[key] for key in inputVariables}
|
1196
|
+
for i, model in enumerate(modelAliases, 1):
|
1094
1197
|
try:
|
1095
|
-
|
1096
|
-
|
1198
|
+
|
1199
|
+
provider = getProviderFromModel(model)
|
1200
|
+
if provider == Provider.OPENAI:
|
1201
|
+
validateOpenaiKey(apiKey)
|
1202
|
+
elif provider == Provider.GOOGLE:
|
1203
|
+
validateGoogleKey(apiKey)
|
1204
|
+
|
1205
|
+
filled_template = getInputPopulatedPrompt(prompt_template, inputDict)
|
1206
|
+
response = executor.execute(provider, model.value, filled_template, apiKey)
|
1207
|
+
df.at[indx, f"output_{i}"] = response
|
1208
|
+
|
1097
1209
|
except Exception as e:
|
1098
|
-
|
1099
|
-
|
1210
|
+
# df.at[indx, f"output_{i}"] = str(e)
|
1211
|
+
raise e
|
1100
1212
|
|
1101
|
-
|
1102
|
-
time.sleep(1)
|
1213
|
+
return df
|
1103
1214
|
|
1104
|
-
|
1105
|
-
|
1106
|
-
# Calculate a reasonable timeout based on the data size
|
1107
|
-
timeout = max(60, min(600, total_items * 10))
|
1108
|
-
# print(f"All batches posted. Waiting up to {timeout} seconds for results...")
|
1215
|
+
def _evaluateForStream(self, df, evals, modelAliases, prompt_template,generateOutput):
|
1216
|
+
dfWithEvals = df.copy()
|
1109
1217
|
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1218
|
+
outputColMapping = {}
|
1219
|
+
for i, model in enumerate(modelAliases, 1):
|
1220
|
+
if generateOutput:
|
1221
|
+
outputColName = f"output_{i}"
|
1222
|
+
else:
|
1223
|
+
outputColName = "output"
|
1224
|
+
try:
|
1117
1225
|
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1226
|
+
res = self.evaluateMultiple(
|
1227
|
+
dfWithEvals.to_dict("records"),
|
1228
|
+
evals=evals,
|
1229
|
+
prompt_template=prompt_template,
|
1230
|
+
outputColName=outputColName,
|
1231
|
+
_tocheck=False,
|
1232
|
+
)
|
1233
|
+
for evalMetric in evals:
|
1234
|
+
scoreCol = f"{evalMetric}"
|
1235
|
+
reasonCol = f"{evalMetric} Reason"
|
1121
1236
|
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1237
|
+
if scoreCol in res.columns:
|
1238
|
+
res = res.rename(columns={scoreCol: f"{scoreCol}_{i}"})
|
1239
|
+
if reasonCol in res.columns:
|
1240
|
+
res = res.rename(columns={reasonCol: f"{evalMetric}_{i} Reason"})
|
1125
1241
|
|
1126
|
-
|
1242
|
+
outputColMapping[f"{scoreCol}_{i}"] = outputColName
|
1127
1243
|
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1244
|
+
newCols = [col for col in res.columns if col not in dfWithEvals.columns]
|
1245
|
+
dfWithEvals = pd.concat([dfWithEvals, res[newCols]], axis=1)
|
1246
|
+
except Exception as e:
|
1247
|
+
print(f"Evaluation failed for model {model.value}: {str(e)}")
|
1248
|
+
return dfWithEvals, outputColMapping
|
1249
|
+
|
1250
|
+
def runDataStream(
|
1251
|
+
self,
|
1252
|
+
data,
|
1253
|
+
streamName: str,
|
1254
|
+
queryColName: str = "query",
|
1255
|
+
createExperiment: bool = False,
|
1256
|
+
):
|
1257
|
+
|
1258
|
+
|
1259
|
+
# Copy the original dataframe
|
1260
|
+
original_df = pd.DataFrame(data)
|
1261
|
+
working_df = original_df.copy()
|
1262
|
+
|
1263
|
+
# Connect to socket
|
1264
|
+
self.socket = LlumoSocketClient(socketUrl)
|
1265
|
+
socketID = self.socket.connect(timeout=150)
|
1266
|
+
waited_secs = 0
|
1267
|
+
while not self.socket._connection_established.is_set():
|
1268
|
+
time.sleep(0.1)
|
1269
|
+
waited_secs += 0.1
|
1270
|
+
if waited_secs >= 20:
|
1271
|
+
raise RuntimeError("Timeout waiting for server 'connection-established' event.")
|
1272
|
+
|
1273
|
+
self.validateApiKey()
|
1274
|
+
|
1275
|
+
# Check user credits
|
1276
|
+
userHits = checkUserHits(
|
1277
|
+
self.workspaceID, self.hasSubscribed, self.trialEndDate,
|
1278
|
+
self.subscriptionEndDate, self.hitsAvailable, len(working_df)
|
1279
|
+
)
|
1280
|
+
if not userHits["success"]:
|
1281
|
+
raise LlumoAIError.InsufficientCredits(userHits["message"])
|
1282
|
+
|
1283
|
+
print("====🚀Sit back while we fetch data from the stream 🚀====")
|
1284
|
+
workspaceID, email = self.workspaceID, self.email
|
1285
|
+
activePlayground = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
1286
|
+
streamId = getStreamId(workspaceID, self.apiKey, streamName)
|
1287
|
+
|
1288
|
+
# Prepare batches
|
1289
|
+
rowIdMapping = {}
|
1290
|
+
self.allBatches = []
|
1291
|
+
currentBatch = []
|
1292
|
+
|
1293
|
+
expectedResults = len(working_df)
|
1294
|
+
timeout = max(100, min(150, expectedResults * 10))
|
1295
|
+
|
1296
|
+
listener_thread = threading.Thread(
|
1297
|
+
target=self.socket.listenForResults,
|
1298
|
+
kwargs={
|
1299
|
+
"min_wait": 40,
|
1300
|
+
"max_wait": timeout,
|
1301
|
+
"inactivity_timeout": 10,
|
1302
|
+
"expected_results": expectedResults,
|
1303
|
+
},
|
1304
|
+
daemon=True
|
1305
|
+
)
|
1306
|
+
listener_thread.start()
|
1307
|
+
|
1308
|
+
for index, row in working_df.iterrows():
|
1309
|
+
rowID, columnID = uuid.uuid4().hex, uuid.uuid4().hex
|
1310
|
+
compoundKey = f"{rowID}-{columnID}-{columnID}"
|
1311
|
+
rowIdMapping[compoundKey] = {"index": index}
|
1312
|
+
templateData = {
|
1313
|
+
"processID": getProcessID(),
|
1314
|
+
"socketID": socketID,
|
1315
|
+
"processData": {
|
1316
|
+
"executionDependency": {"query": row[queryColName]},
|
1317
|
+
"dataStreamID": streamId,
|
1318
|
+
},
|
1319
|
+
"workspaceID": workspaceID,
|
1320
|
+
"email": email,
|
1321
|
+
"type": "DATA_STREAM",
|
1322
|
+
"playgroundID": activePlayground,
|
1323
|
+
"processType": "DATA_STREAM",
|
1324
|
+
"rowID": rowID,
|
1325
|
+
"columnID": columnID,
|
1326
|
+
"source": "SDK",
|
1327
|
+
}
|
1328
|
+
currentBatch.append(templateData)
|
1329
|
+
if len(currentBatch) == 10 or index == len(working_df) - 1:
|
1330
|
+
self.allBatches.append(currentBatch)
|
1331
|
+
currentBatch = []
|
1332
|
+
|
1333
|
+
for batch in tqdm(self.allBatches, desc="Processing Batches", unit="batch", colour="magenta", ncols=80):
|
1133
1334
|
try:
|
1134
|
-
self.
|
1135
|
-
|
1335
|
+
self.postDataStream(batch=batch, workspaceID=workspaceID)
|
1336
|
+
time.sleep(3)
|
1136
1337
|
except Exception as e:
|
1137
|
-
print(f"Error
|
1338
|
+
print(f"Error posting batch: {e}")
|
1339
|
+
raise
|
1138
1340
|
|
1139
|
-
|
1140
|
-
|
1141
|
-
for item in records:
|
1142
|
-
for compound_key, value in item.items():
|
1143
|
-
# for compound_key, value in item['data'].items():
|
1341
|
+
time.sleep(3)
|
1342
|
+
listener_thread.join()
|
1144
1343
|
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
# dataframe.at[index, evalName] = value
|
1150
|
-
dataframe.at[index, streamName] = value["value"]
|
1344
|
+
rawResults = self.socket.getReceivedData()
|
1345
|
+
expectedRowIDs = set(rowIdMapping.keys())
|
1346
|
+
receivedRowIDs = {key for item in rawResults for key in item.keys()}
|
1347
|
+
missingRowIDs = list(expectedRowIDs - receivedRowIDs)
|
1151
1348
|
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1349
|
+
if missingRowIDs:
|
1350
|
+
dataFromDb = fetchData(workspaceID, activePlayground, missingRowIDs)
|
1351
|
+
rawResults.extend(dataFromDb)
|
1155
1352
|
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1353
|
+
working_df["context"] = None
|
1354
|
+
for item in rawResults:
|
1355
|
+
for compound_key, value in item.items():
|
1356
|
+
if compound_key in rowIdMapping:
|
1357
|
+
idx = rowIdMapping[compound_key]["index"]
|
1358
|
+
working_df.at[idx, "context"] = value.get("value")
|
1159
1359
|
|
1360
|
+
|
1361
|
+
|
1362
|
+
self.socket.disconnect()
|
1363
|
+
|
1364
|
+
# Create experiment if required
|
1365
|
+
if createExperiment:
|
1366
|
+
df = working_df.fillna("Some error occured").astype(object)
|
1160
1367
|
if createPlayground(
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
dataStreamName=streamId,
|
1368
|
+
email, workspaceID, df,
|
1369
|
+
queryColName=queryColName,
|
1370
|
+
dataStreamName=streamId,
|
1371
|
+
definationMapping=self.definationMapping,
|
1166
1372
|
):
|
1167
1373
|
print(
|
1168
|
-
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results."
|
1169
|
-
)
|
1374
|
+
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results.")
|
1170
1375
|
else:
|
1171
|
-
self.latestDataframe =
|
1172
|
-
return
|
1376
|
+
self.latestDataframe = working_df
|
1377
|
+
return working_df
|
1378
|
+
|
1173
1379
|
|
1174
1380
|
def createExperiment(self, dataframe):
|
1175
1381
|
try:
|
llumo/exceptions.py
CHANGED
@@ -50,6 +50,10 @@ class LlumoAIError(Exception):
|
|
50
50
|
def dependencyError(details):
|
51
51
|
return LlumoAIError(details)
|
52
52
|
|
53
|
+
@staticmethod
|
54
|
+
def providerError(details):
|
55
|
+
return LlumoAIError(details)
|
56
|
+
|
53
57
|
# @staticmethod
|
54
58
|
# def dateNotFound():
|
55
59
|
# return LlumoAIError("Trial end date or subscription end date not found for the given user.")
|
llumo/execution.py
CHANGED
@@ -25,15 +25,14 @@ class ModelExecutor:
|
|
25
25
|
return response.choices[0].message.content
|
26
26
|
|
27
27
|
def _executeGoogle(self, modelName: str, prompt: str,api_key) -> str:
|
28
|
-
|
28
|
+
|
29
29
|
# Configure GenAI with API Key
|
30
30
|
genai.configure(api_key=api_key)
|
31
|
-
|
31
|
+
|
32
32
|
# Select Generative Model
|
33
33
|
model = genai.GenerativeModel("gemini-2.0-flash-lite")
|
34
34
|
# Generate Response
|
35
35
|
response = model.generate_content(prompt)
|
36
36
|
return response.text
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
|
38
|
+
|
llumo/helpingFuntions.py
CHANGED
@@ -8,7 +8,11 @@ import json
|
|
8
8
|
import base64
|
9
9
|
import os
|
10
10
|
import re
|
11
|
+
import openai
|
12
|
+
import google.generativeai as genai
|
11
13
|
|
14
|
+
|
15
|
+
from .models import _MODEL_METADATA, AVAILABLEMODELS
|
12
16
|
subscriptionUrl = "https://app.llumo.ai/api/workspace/record-extra-usage"
|
13
17
|
getStreamdataUrl = "https://app.llumo.ai/api/data-stream/all"
|
14
18
|
createPlayUrl = "https://app.llumo.ai/api/New-Eval-API/create-new-eval-playground"
|
@@ -212,7 +216,8 @@ def deleteColumnListInPlayground(workspaceID: str, playgroundID: str):
|
|
212
216
|
print("❌ Error:", response.status_code, response.text)
|
213
217
|
return None
|
214
218
|
|
215
|
-
def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColName=None,
|
219
|
+
def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColName=None,
|
220
|
+
outputColName= "output",dataStreamName=None,definationMapping=None,evalOutputMap = None):
|
216
221
|
if len(dataframe) > 100:
|
217
222
|
dataframe = dataframe.head(100)
|
218
223
|
print("⚠️ Dataframe truncated to 100 rows for upload.")
|
@@ -232,11 +237,11 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
|
|
232
237
|
# Iterate over each column in the dataframe
|
233
238
|
for indx, col in enumerate(dataframe.columns):
|
234
239
|
# Generate a unique column ID using uuid
|
235
|
-
columnID = str(uuid.uuid4().hex[:8])
|
240
|
+
columnID = str(uuid.uuid4().hex[:8])
|
236
241
|
|
237
242
|
columnIDMapping[col] = columnID
|
238
243
|
|
239
|
-
|
244
|
+
|
240
245
|
if col.startswith('output') and promptText!=None:
|
241
246
|
# For output columns, create the prompt template with promptText
|
242
247
|
if promptText:
|
@@ -248,12 +253,12 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
|
|
248
253
|
|
249
254
|
# Loop through each variable and check if it exists as a column name
|
250
255
|
for var in variables:
|
251
|
-
varName = var.strip()
|
256
|
+
varName = var.strip()
|
252
257
|
if varName in columnIDMapping: # Check if the variable is a column name
|
253
258
|
dependencies.append(columnIDMapping[varName]) # Add its columnID
|
254
259
|
|
255
260
|
# Now update the template for the output column
|
256
|
-
|
261
|
+
|
257
262
|
template={
|
258
263
|
"provider": "OPENAI",
|
259
264
|
"model": "GPT_4o",
|
@@ -275,8 +280,8 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
|
|
275
280
|
"type": "PROMPT",
|
276
281
|
"order": indx,
|
277
282
|
}
|
278
|
-
|
279
|
-
elif col.startswith('
|
283
|
+
|
284
|
+
elif col.startswith('context') and dataStreamName != None :
|
280
285
|
if queryColName and dataStreamName:
|
281
286
|
dependencies = []
|
282
287
|
dependencies.append(columnIDMapping[queryColName])
|
@@ -286,22 +291,27 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
|
|
286
291
|
"dataStreamName": dataStreamName,
|
287
292
|
"query": columnIDMapping[queryColName],
|
288
293
|
"columnID": columnID, # Use the generated column ID
|
289
|
-
"label": "
|
294
|
+
"label": "context",
|
290
295
|
"type": "DATA_STREAM",
|
291
296
|
"order": indx}
|
292
297
|
|
293
|
-
elif col in allEvals and promptText!=None:
|
294
298
|
|
299
|
+
elif any(col.startswith(eval + "_") or col == eval for eval in allEvals) and not " Reason" in col and promptText is not None:
|
300
|
+
if evalOutputMap != None:
|
301
|
+
outputColName = evalOutputMap[col]
|
302
|
+
else:
|
303
|
+
outputColName = outputColName
|
295
304
|
dependencies = []
|
296
305
|
variables = re.findall(r'{{(.*?)}}', promptText)
|
297
306
|
|
298
307
|
# Loop through each variable and check if it exists as a column name
|
299
308
|
for var in variables:
|
300
|
-
varName = var.strip()
|
309
|
+
varName = var.strip()
|
301
310
|
if varName in columnIDMapping: # Check if the variable is a column name
|
302
311
|
dependencies.append(columnIDMapping[varName])
|
303
|
-
|
312
|
+
|
304
313
|
dependencies.append(columnIDMapping[outputColName]) # Add the output column ID
|
314
|
+
|
305
315
|
longDef = definationMapping.get(col, {}).get('definition', "")
|
306
316
|
shortDef =definationMapping.get(col, {}).get('briefDefinition', "")
|
307
317
|
enum = col.upper().replace(" ","_")
|
@@ -341,11 +351,11 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
|
|
341
351
|
}
|
342
352
|
|
343
353
|
elif col.endswith(' Reason') and promptText!=None:
|
344
|
-
continue
|
354
|
+
continue
|
355
|
+
|
345
356
|
|
346
|
-
|
347
357
|
else:
|
348
|
-
|
358
|
+
|
349
359
|
template = {
|
350
360
|
"label": col, # Label is the column name
|
351
361
|
"type": "VARIABLE", # Default type for non-output columns
|
@@ -370,25 +380,27 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
|
|
370
380
|
row_dict = {}
|
371
381
|
|
372
382
|
# For each column, we need to map the column ID to the corresponding value in the row
|
383
|
+
|
373
384
|
for col in dataframe.columns:
|
374
385
|
columnID = columnIDMapping[col]
|
375
|
-
|
376
|
-
if col in allEvals and promptText!=None:
|
386
|
+
|
387
|
+
if any(col.startswith(eval + "_") or col == eval for eval in allEvals) and not " Reason" in col and promptText!=None:
|
388
|
+
|
377
389
|
row_dict[columnID] = {
|
378
|
-
|
390
|
+
|
379
391
|
"value": row[col],
|
380
392
|
"type": "EVAL",
|
381
393
|
"isValid": True,
|
382
394
|
"reasoning": row[col+" Reason"],
|
383
395
|
"edgeCase": "minorHallucinationDetailNotInContext",
|
384
396
|
"kpi": col
|
385
|
-
|
386
|
-
|
397
|
+
|
398
|
+
}
|
387
399
|
elif col.endswith(' Reason') and promptText!=None:
|
388
400
|
continue
|
389
401
|
else:# Get the columnID from the mapping
|
390
402
|
row_dict[columnID] = row[col]
|
391
|
-
|
403
|
+
|
392
404
|
# row_dict[columnID] = row[col] # Directly map the column ID to the row value
|
393
405
|
# Add the row index (if necessary)
|
394
406
|
row_dict["pIndex"] = indx
|
@@ -440,11 +452,11 @@ def uploadRowsInDBPlayground(payload):
|
|
440
452
|
return None
|
441
453
|
|
442
454
|
|
443
|
-
def createPlayground(email, workspaceID, df, promptText=None,queryColName=None,dataStreamName=None,definationMapping=None,outputColName="output"):
|
455
|
+
def createPlayground(email, workspaceID, df, promptText=None,queryColName=None,dataStreamName=None,definationMapping=None,outputColName="output",evalOutputMap = None):
|
444
456
|
|
445
457
|
playgroundId = str(createEvalPlayground(email=email, workspaceID=workspaceID))
|
446
458
|
payload1, payload2 = createColumn(
|
447
|
-
workspaceID=workspaceID, dataframe=df, playgroundID=playgroundId, promptText=promptText,queryColName=queryColName,dataStreamName=dataStreamName,definationMapping=definationMapping,outputColName=outputColName
|
459
|
+
workspaceID=workspaceID, dataframe=df, playgroundID=playgroundId, promptText=promptText,queryColName=queryColName,dataStreamName=dataStreamName,definationMapping=definationMapping,outputColName=outputColName,evalOutputMap=evalOutputMap
|
448
460
|
)
|
449
461
|
|
450
462
|
# Debugging line to check the payload2 structure
|
@@ -606,3 +618,36 @@ def fetchData(workspaceID, playgroundID, missingList: list):
|
|
606
618
|
except Exception as e:
|
607
619
|
print(f"An error occurred: {e}")
|
608
620
|
return []
|
621
|
+
|
622
|
+
def validateModels(model_aliases):
|
623
|
+
|
624
|
+
selectedProviders = []
|
625
|
+
for name in model_aliases:
|
626
|
+
for alias ,(provider , modelName ) in _MODEL_METADATA.items():
|
627
|
+
if modelName == name:
|
628
|
+
selectedProviders.append(provider)
|
629
|
+
|
630
|
+
if len(set(selectedProviders)) > 1:
|
631
|
+
return {"status": False,"message":"All selected models should be of same provider."}
|
632
|
+
else:
|
633
|
+
return {"status": True,"message":"All selected models are of same provider."}
|
634
|
+
|
635
|
+
|
636
|
+
|
637
|
+
def validateOpenaiKey(api_key):
|
638
|
+
try:
|
639
|
+
client = openai.OpenAI(api_key=api_key)
|
640
|
+
_ = client.models.list() # Light call to list models
|
641
|
+
except openai.AuthenticationError:
|
642
|
+
raise ValueError("❌ Invalid OpenAI API key.")
|
643
|
+
except Exception as e:
|
644
|
+
raise RuntimeError(f"⚠️ Error validating OpenAI key: {e}")
|
645
|
+
|
646
|
+
def validateGoogleKey(api_key):
|
647
|
+
try:
|
648
|
+
genai.configure(api_key=api_key)
|
649
|
+
_ = genai.GenerativeModel("gemini-2.0-flash-lite").generate_content("test")
|
650
|
+
except Exception as e:
|
651
|
+
if "PERMISSION_DENIED" in str(e) or "API key not valid" in str(e):
|
652
|
+
raise ValueError("❌ Invalid Google API key.")
|
653
|
+
raise RuntimeError(f"⚠️ Error validating Gemini key: {e}")
|
llumo/models.py
CHANGED
@@ -6,35 +6,72 @@ class Provider(str, Enum):
|
|
6
6
|
|
7
7
|
# Maps model aliases → (provider, actual model name for API)
|
8
8
|
_MODEL_METADATA = {
|
9
|
-
"
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
13
|
-
"
|
14
|
-
"
|
15
|
-
|
16
|
-
"
|
17
|
-
"
|
18
|
-
"
|
19
|
-
"
|
20
|
-
"
|
21
|
-
"
|
9
|
+
"GPT_4O": (Provider.OPENAI, "GPT_4O"),
|
10
|
+
"GPT_4_5": (Provider.OPENAI, "GPT_4_5"),
|
11
|
+
"GPT_4": (Provider.OPENAI, "GPT_4"),
|
12
|
+
"GPT_4_32K": (Provider.OPENAI, "GPT_4_32K"),
|
13
|
+
"GPT_3_5_Turbo": (Provider.OPENAI, "GPT_35T"),
|
14
|
+
"GPT_3_5_Turbo_Instruct": (Provider.OPENAI, "GPT_35T_INS"),
|
15
|
+
"GPT_3_5_Turbo_16K": (Provider.OPENAI, "GPT_35T_16K"),
|
16
|
+
"GPT_4_o_Mini": (Provider.OPENAI, "GPT_4O_MINI"),
|
17
|
+
"o4_MINI": (Provider.OPENAI, "O4_MINI"),
|
18
|
+
"o4_MINI_HIGH": (Provider.OPENAI, "O4_MINI_HIGH"),
|
19
|
+
"GPT_4_1": (Provider.OPENAI, "GPT_4_1"),
|
20
|
+
"GPT_4_1_Mini": (Provider.OPENAI, "GPT_4_1_MINI"),
|
21
|
+
"GPT_4_1_nano": (Provider.OPENAI, "GPT_4_1_NANO"),
|
22
|
+
"o3": (Provider.OPENAI, "O3"),
|
23
|
+
"o3_MINI": (Provider.OPENAI, "O3_MINI"),
|
24
|
+
"o1": (Provider.OPENAI, "O1"),
|
25
|
+
"o1_MINI": (Provider.OPENAI, "O1_MINI"),
|
26
|
+
|
27
|
+
|
28
|
+
"Gemini_2_5_Pro": (Provider.GOOGLE, "GEMINI_2_5_PRO"),
|
29
|
+
"Gemini_2_5_Flash": (Provider.GOOGLE, "GEMINI_2_5_FLASH"),
|
30
|
+
"Gemini_2_0": (Provider.GOOGLE, "GEMINI_2_0"),
|
31
|
+
"Gemini_2_0_Flash": (Provider.GOOGLE, "GEMINI_2_0_FLASH"),
|
32
|
+
"Gemini_Pro": (Provider.GOOGLE, "GEMINI_PRO"),
|
33
|
+
"Text_Bison": (Provider.GOOGLE, "TEXT_BISON"),
|
34
|
+
"Chat_Bison": (Provider.GOOGLE, "CHAT_BISON"),
|
35
|
+
"Text_Bison_32k": (Provider.GOOGLE, "TEXT_BISON_32K"),
|
36
|
+
"Text_Unicorn": (Provider.GOOGLE, "TEXT_UNICORN"),
|
37
|
+
"Google_1_5_Flash": (Provider.GOOGLE, "GOOGLE_15_FLASH"),
|
38
|
+
"Gemma_3_9B": (Provider.GOOGLE, "GEMMA_3_9B"),
|
39
|
+
"Gemma_3_27B": (Provider.GOOGLE, "GEMMA_3_27B"),
|
22
40
|
}
|
23
41
|
|
24
42
|
class AVAILABLEMODELS(str, Enum):
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
43
|
+
GPT_4o= "GPT_4O",
|
44
|
+
GPT_4o_Mini= "GPT_4O_MINI",
|
45
|
+
GPT_4_5= "GPT_4_5",
|
46
|
+
GPT_4= "GPT_4",
|
47
|
+
GPT_4_32K= "GPT_4_32K",
|
48
|
+
GPT_3_5_Turbo= "GPT_35T",
|
49
|
+
GPT_3_5_Turbo_Instruct= "GPT_35T_INS",
|
50
|
+
GPT_3_5_Turbo_16K= "GPT_35T_16K",
|
51
|
+
GPT_4_o_Mini= "GPT_4O_MINI",
|
52
|
+
o4_MINI = "O4_MINI",
|
53
|
+
o4_MINI_HIGH = "O4_MINI_HIGH",
|
54
|
+
GPT_4_1 = "GPT_4_1",
|
55
|
+
GPT_4_1_Mini = "GPT_4_1_MINI",
|
56
|
+
GPT_4_1_nano = "GPT_4_1_NANO",
|
57
|
+
o3 = "O3",
|
58
|
+
o3_MINI = "O3_MINI",
|
59
|
+
o1 = "O1",
|
60
|
+
o1_MINI = "O1_MINI",
|
61
|
+
|
62
|
+
Gemini_2_5_Pro = "GEMINI_2_5_PRO",
|
63
|
+
Gemini_2_5_Flash = "GEMINI_2_5_FLASH",
|
64
|
+
Gemini_2_0 = "GEMINI_2_0",
|
65
|
+
Gemini_2_0_Flash = "GEMINI_2_0_FLASH",
|
66
|
+
Gemini_Pro = "GEMINI_PRO",
|
67
|
+
Text_Bison = "TEXT_BISON",
|
68
|
+
Chat_Bison = "CHAT_BISON",
|
69
|
+
Text_Bison_32k = "TEXT_BISON_32K",
|
70
|
+
Text_Unicorn = "TEXT_UNICORN",
|
71
|
+
Google_1_5_Flash = "GOOGLE_15_FLASH",
|
72
|
+
Gemma_3_9B = "GEMMA_3_9B",
|
73
|
+
Gemma_3_27B = "GEMMA_3_27B",
|
74
|
+
|
38
75
|
|
39
76
|
def getProviderFromModel(model: AVAILABLEMODELS) -> Provider:
|
40
77
|
for alias, (provider, apiName) in _MODEL_METADATA.items():
|
@@ -0,0 +1,13 @@
|
|
1
|
+
llumo/__init__.py,sha256=O04b4yW1BnOvcHzxWFddAKhtdBEhBNhLdb6xgnpHH_Q,205
|
2
|
+
llumo/client.py,sha256=zh6fpKpjlYcvzrPZkPviF1hDRzfnA1K0U1gweoKfkwc,54675
|
3
|
+
llumo/exceptions.py,sha256=Vp_MnanHbnd1Yjuoi6WLrKiwwZbJL3znCox2URMmGU4,2032
|
4
|
+
llumo/execution.py,sha256=nWbJ7AvWuUPcOb6i-JzKRna_PvF-ewZTiK8skS-5n3w,1380
|
5
|
+
llumo/functionCalling.py,sha256=D5jYapu1rIvdIJNUYPYMTyhQ1H-6nkwoOLMi6eekfUE,7241
|
6
|
+
llumo/helpingFuntions.py,sha256=-9GA9X0KBUVZb3_25D8AlninWnVc9ajFp4QkR_mDePY,23545
|
7
|
+
llumo/models.py,sha256=aVEZsOOoQx5LeNtwSyBxqvrINq0izH3QWu_YjsMPE6o,2910
|
8
|
+
llumo/sockets.py,sha256=I2JO_eNEctRo_ikgvFVp5zDd-m0VDu04IEUhhsa1Tic,5950
|
9
|
+
llumo-0.2.15.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
|
10
|
+
llumo-0.2.15.dist-info/METADATA,sha256=OQApH-0Gj918OaMbyQasOtE6lAhU5__No3SK9xge-NM,1519
|
11
|
+
llumo-0.2.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
12
|
+
llumo-0.2.15.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
|
13
|
+
llumo-0.2.15.dist-info/RECORD,,
|
llumo-0.2.14b7.dist-info/RECORD
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
llumo/__init__.py,sha256=O04b4yW1BnOvcHzxWFddAKhtdBEhBNhLdb6xgnpHH_Q,205
|
2
|
-
llumo/client.py,sha256=HpvUyucrGPbcPQMz_cTRDcEsBFpmNt8jfW1zJU4Nyss,46781
|
3
|
-
llumo/exceptions.py,sha256=i3Qv4_g7XjRuho7-b7ybjw2bwSh_NhvICR6ZAgiLQX8,1944
|
4
|
-
llumo/execution.py,sha256=x88wQV8eL99wNN5YtjFaAMCIfN1PdfQVlAZQb4vzgQ0,1413
|
5
|
-
llumo/functionCalling.py,sha256=D5jYapu1rIvdIJNUYPYMTyhQ1H-6nkwoOLMi6eekfUE,7241
|
6
|
-
llumo/helpingFuntions.py,sha256=RgWok8DoE1R-Tc0kJ9B5En6LEUEk5EvQU8iJiGPbUsw,21911
|
7
|
-
llumo/models.py,sha256=YH-qAMnShmUpmKE2LQAzQdpRsaXkFSlOqMxHwU4zBUI,1560
|
8
|
-
llumo/sockets.py,sha256=I2JO_eNEctRo_ikgvFVp5zDd-m0VDu04IEUhhsa1Tic,5950
|
9
|
-
llumo-0.2.14b7.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
|
10
|
-
llumo-0.2.14b7.dist-info/METADATA,sha256=kdeDmcNgV8uRyH7gXhhAqeb3se5U_Gqo3bA3Cf4SLlM,1521
|
11
|
-
llumo-0.2.14b7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
12
|
-
llumo-0.2.14b7.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
|
13
|
-
llumo-0.2.14b7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|