llumo 0.2.32__py3-none-any.whl → 0.2.34__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 CHANGED
@@ -22,16 +22,20 @@ from tqdm import tqdm
22
22
 
23
23
  pd.set_option("future.no_silent_downcasting", True)
24
24
 
25
- postUrl = (
26
- "https://redskull.llumo.ai/api/process-playground"
27
- )
28
- fetchUrl = (
29
- "https://redskull.llumo.ai/api/get-cells-data"
30
- )
31
- socketDataUrl = "https://app.llumo.ai/api/eval/get-awaited"
25
+ postUrl = "https://redskull.llumo.ai/api/process-playground"
26
+
27
+ # postUrl = "http://localhost:4747/api/process-playground"
28
+ fetchUrl = "https://redskull.llumo.ai/api/get-cells-data"
29
+
30
+ # fetchUrl = "http://localhost:4747/api/get-cells-data"
31
+ fetchMissingEvalUrl = "https://redskull.llumo.ai/api/get-missing-keys"
32
+ socketDataUrl = "https://redskull.llumo.ai/api/eval/get-awaited"
32
33
 
33
34
  validateUrl = "https://app.llumo.ai/api/workspace-details"
34
35
  socketUrl = "https://redskull.llumo.ai/"
36
+ # socketUrl = "http://localhost:4747/"
37
+ createEvalUrl = "https://backend-api.llumo.ai/api/v1/create-debug-log-for-sdk"
38
+ # createEvalUrl = "http://localhost:4545/api/v1/create-debug-log-for-sdk"
35
39
 
36
40
 
37
41
  class LlumoClient:
@@ -200,15 +204,87 @@ class LlumoClient:
200
204
  "Content-Type": "application/json",
201
205
  }
202
206
  try:
203
- # print(postUrl)
207
+ print(postUrl)
204
208
  response = requests.post(postUrl, json=payload, headers=headers)
205
- # print(f"Post API Status Code: {response.status_code}")
206
- # print(response.text)
209
+ print(f"Post API Status Code: {response.status_code}")
210
+ print(response.text)
211
+ # print(response.status_code)
212
+
213
+ except Exception as e:
214
+ print(f"Error in posting batch: {e}")
215
+
216
+
217
+ def fetchDataForMissingKeys(self, workspaceID, missingKeys: list):
218
+ # Define the URL and prepare the payload
219
+
220
+ payload = {
221
+ "workspaceID": workspaceID,
222
+ "missingKeys": missingKeys
223
+ }
224
+
225
+ headers = {
226
+ "Authorization": f"Bearer {self.socketToken}",
227
+ "Content-Type": "application/json",
228
+ }
229
+
230
+ try:
231
+ # Send a POST request to the API
232
+ response = requests.post(fetchMissingEvalUrl, json=payload, headers=headers)
233
+
234
+ # Check if the response is successful
235
+ if response.status_code == 200:
236
+ # Parse the JSON data from the response
237
+ data = response.json().get("data", {})
238
+
239
+
240
+ # Prepare the list of all data values in the desired format
241
+ result_list = []
242
+ for key, value in data.items():
243
+ # Create a dictionary for each item in the response data
244
+ result_list.append({
245
+ key: {
246
+ "value": value.get("value"),
247
+ "fullEval": value.get("fullEval", {}),
248
+ "runLog": value.get("runLog", {}),
249
+ "evallist": value.get("evallist", [])
250
+ }
251
+ })
252
+
253
+ print("Fetched data for missing keys:", result_list)
254
+ return result_list
255
+ else:
256
+ print(f"Failed to fetch data. Status Code: {response.status_code}")
257
+ return []
258
+
259
+ except Exception as e:
260
+ print(f"An error occurred: {e}")
261
+ return []
262
+
263
+
264
+ def createRunForEvalMultiple(self, smartLog):
265
+ payload = {
266
+ "log": smartLog
267
+ }
268
+ # socketToken here if the "JWD" token
269
+ headers = {
270
+ # "Authorization": f"Bearer {self.socketToken}",
271
+ "Content-Type": "application/json",
272
+ }
273
+ try:
274
+ # print(postUrl)
275
+ print(createEvalUrl)
276
+ print(payload)
277
+ print(headers)
278
+ response = requests.post(createEvalUrl, json=payload, headers=headers)
279
+
280
+ print(f"Post API Status Code: {response.status_code}")
281
+ print(response.text)
207
282
  # print(response.status_code)
208
283
 
209
284
  except Exception as e:
210
285
  print(f"Error in posting batch: {e}")
211
286
 
287
+
212
288
  def postDataStream(self, batch, workspaceID):
213
289
  payload = {
214
290
  "batch": json.dumps(batch),
@@ -700,8 +776,9 @@ class LlumoClient:
700
776
  def evaluateMultiple(
701
777
  self,
702
778
  data,
703
- evals: list,
704
- prompt_template="Give answer to the given query: {{query}} using the given context: {{context}}.",
779
+ evals: list = [],
780
+ # prompt_template="Give answer to the given query: {{query}} using the given context: {{context}}.",
781
+ prompt_template="",
705
782
  getDataFrame: bool = False,
706
783
  _tocheck=True,
707
784
  ):
@@ -718,8 +795,10 @@ class LlumoClient:
718
795
  email = None
719
796
  try:
720
797
  socketID = self.socket.connect(timeout=250)
798
+ print("Socket connected with ID:", socketID)
721
799
  except Exception as e:
722
800
  socketID = "DummySocketID"
801
+ print(f"Socket connection failed, using dummy ID. Error: {str(e)}")
723
802
 
724
803
  self.evalData = []
725
804
  self.evals = evals
@@ -736,27 +815,33 @@ class LlumoClient:
736
815
  # raise RuntimeError("Timeout waiting for server connection")
737
816
 
738
817
  # Start listener thread
739
- expectedResults = len(dataframe) * len(evals)
818
+ # expectedResults = len(dataframe) * len(evals)
819
+ expectedResults = len(dataframe)
740
820
  # print("expected result" ,expectedResults)
741
- timeout = max(100, min(150, expectedResults * 10))
821
+ timeout = max(100, min(250, expectedResults * 60))
742
822
  listener_thread = threading.Thread(
743
823
  target=self.socket.listenForResults,
744
824
  kwargs={
745
825
  "min_wait": 20,
746
826
  "max_wait": timeout,
747
- "inactivity_timeout": 35,
827
+ "inactivity_timeout": timeout,
748
828
  "expected_results": expectedResults,
749
829
  },
750
830
  daemon=True,
751
831
  )
752
832
  listener_thread.start()
753
- self.validateApiKey(evalName=evals[0])
833
+ # commenting validate api key as we don't need it logger does it for us. uncommented but we need different
834
+ # api for this which don't spend time on eval defintiion fetches and just bring hits
835
+ self.validateApiKey()
754
836
  activePlayground = self.playgroundID
755
837
  # print(f"\n======= Running evaluation for: {evalName} =======")
756
838
 
757
839
  # Validate API and dependencies
758
840
  # self.validateApiKey(evalName=evals[0])
759
- customAnalytics = getCustomAnalytics(self.workspaceID)
841
+
842
+ # why we need custom analytics here? there is no such usage below
843
+ # customAnalytics = getCustomAnalytics(self.workspaceID)
844
+
760
845
  # metricDependencies = checkDependency(
761
846
  # evalName,
762
847
  # list(dataframe.columns),
@@ -789,37 +874,72 @@ class LlumoClient:
789
874
  self.hitsAvailable,
790
875
  len(dataframe),
791
876
  )
877
+
878
+ #where does this remaining hit comes from?
879
+
880
+
792
881
  if not userHits["success"]:
793
882
  raise LlumoAIError.InsufficientCredits(userHits["message"])
794
883
 
795
884
  currentBatch = []
885
+
886
+
796
887
  for index, row in dataframe.iterrows():
797
- tools = [row["tools"]] if "tools" in dataframe.columns else []
888
+ # Extract required fields
889
+ tools = row.get("tools", "")
798
890
  groundTruth = row.get("groundTruth", "")
799
- messageHistory = (
800
- [row["messageHistory"]]
801
- if "messageHistory" in dataframe.columns
802
- else []
803
- )
804
- promptTemplate = prompt_template
805
- keys = re.findall(r"{{(.*?)}}", promptTemplate)
806
-
807
- if not all([ky in dataframe.columns for ky in keys]):
808
- raise LlumoAIError.InvalidPromptTemplate()
809
-
810
- inputDict = {key: row[key] for key in keys if key in row}
811
- # output = row.get(outputColName, "")
812
- output = row.get("output","")
891
+ messageHistory = row.get("messageHistory", "")
813
892
  intermediateSteps = row.get("intermediateSteps", "")
814
-
893
+ output = row.get("output", "")
894
+
895
+ # Initialize query and context
896
+ query = ""
897
+ context = ""
898
+
899
+ # Process prompt template if provided
900
+ if prompt_template:
901
+ # Extract template variables
902
+ keys = re.findall(r"{{(.*?)}}", prompt_template)
903
+
904
+ if not all([key in dataframe.columns for key in keys]):
905
+ raise LlumoAIError.InvalidPromptTemplate()
906
+
907
+ # Populate template and separate query/context
908
+ populated_template = prompt_template
909
+ for key in keys:
910
+ value = row.get(key, "")
911
+ if isinstance(value, str):
912
+ length = len(value.split()) * 1.5
913
+ if length <= 50:
914
+ # Short value - include in query via template
915
+ temp_obj = {key: value}
916
+ populated_template = getInputPopulatedPrompt(populated_template, temp_obj)
917
+ else:
918
+ # Long value - add to context
919
+ context += f" {key}: {value}, "
920
+
921
+ query = populated_template.strip()
922
+
923
+ # Add any remaining context from other fields
924
+ if not context.strip():
925
+ for key, value in row.items():
926
+ if key not in keys and isinstance(value, str) and value.strip():
927
+ context += f" {key}: {value}, "
928
+ else:
929
+ # No prompt template - use direct query and context fields
930
+ query = row.get("query", "")
931
+ context = row.get("context", "")
932
+
933
+ # Generate unique IDs
815
934
  rowID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
816
935
  columnID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
817
-
936
+
818
937
  compoundKey = f"{rowID}-{columnID}-{columnID}"
819
- # rowIdMapping[compoundKey] = {"index": index, "eval": evalName}
820
938
  rowIdMapping[compoundKey] = {"index": index}
939
+ print("rowIdMapping:", rowIdMapping)
821
940
 
822
-
941
+ # Create evaluation payload
942
+ print("socketID in before templateData: ", socketID)
823
943
  templateData = {
824
944
  "processID": getProcessID(),
825
945
  "socketID": socketID,
@@ -832,65 +952,22 @@ class LlumoClient:
832
952
  "playgroundID": activePlayground,
833
953
  "source": "SDK",
834
954
  "processData": {
835
- # "analyticsName": evalName,
836
- # "definition": evalDefinition,
837
955
  "executionDependency": {
838
- "query": "",
839
- "context": "",
956
+ "query": query,
957
+ "context": context.strip(),
840
958
  "output": output,
841
959
  "tools": tools,
842
960
  "groundTruth": groundTruth,
843
961
  "messageHistory": messageHistory,
844
962
  "intermediateSteps": intermediateSteps,
845
963
  },
846
- "evallist":evals,
847
- # "model": model,
848
- # "provider": provider,
849
- "sessionID":self.sessionID
850
- # "categories": categories,
851
- # "evaluationStrictness": evaluationStrictness,
852
- # "grammarCheckOutput": grammarCheckOutput,
853
- # "insightLength": insightLength,
854
- # "numJudges": numJudges,
855
- # "penaltyBonusInstructions": penaltyBonusInstructions,
856
- # "probableEdgeCases": probableEdgeCases,
964
+ "evallist": evals,
965
+ "sessionID": self.sessionID
857
966
  },
858
967
  "type": "FULL_EVAL_RUN",
859
- # "kpi": evalName,
860
- # "fieldMappig": fieldMapping,
861
968
  }
862
969
 
863
- query = ""
864
- context = ""
865
- for key, value in inputDict.items():
866
- if isinstance(value, str):
867
- length = len(value.split()) * 1.5
868
- if length > 50:
869
- context += f" {key}: {value}, "
870
- else:
871
- if promptTemplate:
872
- tempObj = {key: value}
873
- promptTemplate = getInputPopulatedPrompt(
874
- promptTemplate, tempObj
875
- )
876
- else:
877
- query += f" {key}: {value}, "
878
-
879
- if not context.strip():
880
- for key, value in inputDict.items():
881
- context += f" {key}: {value}, "
882
-
883
- templateData["processData"]["executionDependency"][
884
- "context"
885
- ] = context.strip()
886
- templateData["processData"]["executionDependency"][
887
- "query"
888
- ] = query.strip()
889
- if promptTemplate and not query.strip():
890
- templateData["processData"]["executionDependency"][
891
- "query"
892
- ] = promptTemplate
893
-
970
+ # Add to batch
894
971
  currentBatch.append(templateData)
895
972
  if len(currentBatch) == 10:
896
973
  self.allBatches.append(currentBatch)
@@ -920,19 +997,33 @@ class LlumoClient:
920
997
 
921
998
  rawResults = self.socket.getReceivedData()
922
999
 
1000
+ print(f"Total results received: {len(rawResults)}")
1001
+ # print("Raw results:", rawResults)
1002
+
923
1003
  # print("data from db #####################",dataFromDb)
924
1004
  # Fix here: keep full keys, do not split keys
925
1005
  receivedRowIDs = {key for item in rawResults for key in item.keys()}
1006
+ print("Received Row IDs:", receivedRowIDs)
926
1007
  expectedRowIDs = set(rowIdMapping.keys())
927
1008
  missingRowIDs = expectedRowIDs - receivedRowIDs
928
1009
  # print("All expected keys:", expectedRowIDs)
929
1010
  # print("All received keys:", receivedRowIDs)
930
1011
  # print("Missing keys:", len(missingRowIDs))
931
1012
  missingRowIDs = list(missingRowIDs)
932
- if len(missingRowIDs) > 0:
933
- dataFromDb = fetchData(workspaceID, activePlayground, missingRowIDs)
934
- rawResults.extend(dataFromDb)
935
1013
 
1014
+ print("Missing Row IDs:", missingRowIDs)
1015
+ print(f"Total results before fetching missing data: {len(rawResults)}")
1016
+ if len(missingRowIDs) > 0:
1017
+ print('''It's taking longer than expected to get results for some rows. You can close this now.
1018
+ Please wait for 15 mins while we create the flow graph for you. You can check the graph at app.llumo.ai/debugger''')
1019
+ else:
1020
+ print('''All results received successfully. You can check flowgraph in 5 mins at app.llumo.ai/debugger''')
1021
+ # if len(missingRowIDs) > 0:
1022
+ # dataFromDb = self.fetchDataForMissingKeys(workspaceID, missingRowIDs)
1023
+ # # print("Fetched missing data from DB:", dataFromDb)
1024
+ # rawResults.extend(dataFromDb)
1025
+ # print(f"Total results after fetching missing data: {len(rawResults)}")
1026
+
936
1027
  self.evalData = rawResults
937
1028
  # print("RAW RESULTS: ", self.evalData)
938
1029
 
@@ -950,9 +1041,6 @@ class LlumoClient:
950
1041
  index = rowIdMapping[compound_key]["index"]
951
1042
  rowID, columnID, _ = compound_key.split("-", 2)
952
1043
 
953
- if hasattr(self, "startLlumoRun"):
954
- self.startLlumoRun(runName="evaluateMultiple",rowID = rowID, columnID = columnID)
955
-
956
1044
  # get the dataframe row at this index
957
1045
  row = dataframe.iloc[index].to_dict()
958
1046
 
@@ -964,10 +1052,10 @@ class LlumoClient:
964
1052
  fullEval = value.get("fullEval") if isinstance(value, dict) else None
965
1053
  if fullEval:
966
1054
  if "evalMetrics" in fullEval and isinstance(fullEval["evalMetrics"], list):
967
- for eval_item in fullEval["evalMetrics"]:
968
- evalName = eval_item.get("evalName") or eval_item.get("kpiName")
969
- score = str(eval_item.get("score")) or eval_item.get("value")
970
- reasoning = eval_item.get("reasoning")
1055
+ for evalItem in fullEval["evalMetrics"]:
1056
+ evalName = evalItem.get("evalName") or evalItem.get("kpiName")
1057
+ score = str(evalItem.get("score")) or evalItem.get("value")
1058
+ reasoning = evalItem.get("reasoning")
971
1059
  # edgeCase = eval_item.get("edgeCase")
972
1060
 
973
1061
  if evalName:
@@ -975,33 +1063,16 @@ class LlumoClient:
975
1063
  dataframe.at[index, f"{evalName} Reason"] = reasoning
976
1064
  # dataframe.at[index, f"{evalName} EdgeCase"] = edgeCase
977
1065
 
978
- # logEvalStep if available
979
- if hasattr(self, "logEvalStep"):
980
- try:
981
- start_time = time.time()
982
- self.logEvalStep(
983
- stepName=f"EVAL-{evalName}",
984
- output=row.get("output", ""),
985
- context=row.get("context", ""),
986
- query=row.get("query", ""),
987
- messageHistory=row.get("messageHistory", ""),
988
- tools=row.get("tools", ""),
989
- intermediateSteps=row.get("intermediateSteps", ""),
990
- groundTruth=row.get("groundTruth", ""),
991
- analyticsScore=score,
992
- reasoning=reasoning,
993
- classification=eval_item.get("classification", {}),
994
- evalLabel=eval_item.get("evalLabel", {}),
995
- latencyMs=int((time.time() - start_time) * 1000),
996
- status="SUCCESS",
997
- message="",
998
- )
999
- except Exception as e:
1000
- print(f"⚠️ logEvalStep failed: {e}")
1001
- if hasattr(self, "endLlumoRun"):
1002
- self.endEvalRun()
1003
-
1004
- # Clean up and finish
1066
+
1067
+ # runLog = value.get("runLog") if isinstance(value, dict) else None
1068
+ # if runLog:
1069
+ # try:
1070
+ # self.createRunForEvalMultiple(smartLog=runLog)
1071
+ # except Exception as e:
1072
+ # print(f"Error posting smartlog: {e}")
1073
+
1074
+
1075
+
1005
1076
  try:
1006
1077
  self.socket.disconnect()
1007
1078
  except Exception:
llumo/helpingFuntions.py CHANGED
@@ -585,7 +585,7 @@ def checkDependency(selectedEval:list = [], columns:list = [],tocheck=True,_retu
585
585
 
586
586
  def fetchData(workspaceID, playgroundID, missingList: list):
587
587
  # Define the URL and prepare the payload
588
- socket_data_url = "https://app.llumo.ai/api/eval/get-awaited"
588
+ socket_data_url = "https://redskull.llumo.ai/api/eval/get-awaited"
589
589
  payload = {
590
590
  "workspaceID": workspaceID,
591
591
  "playgroundID": playgroundID,
llumo/sockets.py CHANGED
@@ -150,7 +150,7 @@ class LlumoSocketClient:
150
150
 
151
151
  def getReceivedData(self):
152
152
  with self._lock:
153
- # print("Total received:", len(self._received_data)) # DEBUG
153
+ print("Total received:", len(self._received_data)) # DEBUG
154
154
  return self._received_data.copy()
155
155
 
156
156
  def disconnect(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llumo
3
- Version: 0.2.32
3
+ Version: 0.2.34
4
4
  Summary: Python SDK for interacting with the Llumo ai API.
5
5
  Home-page: https://www.llumo.ai/
6
6
  Author: Llumo
@@ -2,19 +2,19 @@ llumo/__init__.py,sha256=kkuppu7ZPiVZFdnYzJ9BM3syMbYHOSZLpwKwAvGHsnY,311
2
2
  llumo/callback.py,sha256=K8O_bXgeIOXr5gougWF2Y7wnXTf7c3gSEvJZFiVoCmA,23829
3
3
  llumo/callbacks-0.py,sha256=TEIOCWRvk2UYsTmBMBsnlgpqWvr-2y3a6d0w_e96NRM,8958
4
4
  llumo/chains.py,sha256=6lCgLseh04RUgc6SahhmvQj82quay2Mi1j8gPUlx8Es,2923
5
- llumo/client.py,sha256=p28b0F2NLgggwkA9BchcMp0sEZ_V_SFeMCR2Sj0pndE,70673
5
+ llumo/client.py,sha256=ZFey1s56-bdYEUQwDfH9crx4ylNt7XHZFUI0WGofjmY,73029
6
6
  llumo/exceptions.py,sha256=1OyhN9YL9LcyUPUsqYHq6Rret0udATZAwMVJaio2_Ec,2123
7
7
  llumo/execution.py,sha256=nWbJ7AvWuUPcOb6i-JzKRna_PvF-ewZTiK8skS-5n3w,1380
8
8
  llumo/functionCalling.py,sha256=D5jYapu1rIvdIJNUYPYMTyhQ1H-6nkwoOLMi6eekfUE,7241
9
9
  llumo/google.py,sha256=6y9YnDFDRHv6-sQNT5LIsV9p31BCN0B9eow5KTRBWfM,2185
10
- llumo/helpingFuntions.py,sha256=RSXMA5XLP6qiMsJW7axp1PZj8RFwY9HkkbG7nwJkbIM,27279
10
+ llumo/helpingFuntions.py,sha256=jhB14o5e0YuRp-lCnu1c4vXjoG_y8ZinFHxUrZsnZAk,27284
11
11
  llumo/llumoLogger.py,sha256=grdjhu6Ngxg7nhnrMOP5Pd5ALR7U2ROws48yhf_N7y0,1912
12
12
  llumo/llumoSessionContext.py,sha256=s1yu8dP_lrVPdiPQdg80n7Pm_IXoc6_XR2-6oBq5UtI,12585
13
13
  llumo/models.py,sha256=aVEZsOOoQx5LeNtwSyBxqvrINq0izH3QWu_YjsMPE6o,2910
14
14
  llumo/openai.py,sha256=VstBzaORe8Tq0feUIIEszzcN1oq6TJfkPviaCr5d3Bw,8950
15
- llumo/sockets.py,sha256=pBDo-U65hMIMwKMwZQl3iBkEjISEt-9BkXxZTWfSHF4,6116
16
- llumo-0.2.32.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
17
- llumo-0.2.32.dist-info/METADATA,sha256=sKCxyfRxjeOUiVDUxDvSxpDHJJDul1XddlxhGF_NLEo,1558
18
- llumo-0.2.32.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- llumo-0.2.32.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
20
- llumo-0.2.32.dist-info/RECORD,,
15
+ llumo/sockets.py,sha256=2zp9xzIV46naR3kuotroIvZfJp13TTxpgg2iSxtJofU,6114
16
+ llumo-0.2.34.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
17
+ llumo-0.2.34.dist-info/METADATA,sha256=3eiB92kNLh_IvOlesPckMDo92v3kPTa5qhNJndbGrHQ,1558
18
+ llumo-0.2.34.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ llumo-0.2.34.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
20
+ llumo-0.2.34.dist-info/RECORD,,
File without changes