llumo 0.2.31__tar.gz → 0.2.33__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.
Files changed (26) hide show
  1. {llumo-0.2.31/llumo.egg-info → llumo-0.2.33}/PKG-INFO +1 -1
  2. {llumo-0.2.31 → llumo-0.2.33}/llumo/client.py +195 -122
  3. {llumo-0.2.31 → llumo-0.2.33}/llumo/helpingFuntions.py +1 -1
  4. {llumo-0.2.31 → llumo-0.2.33}/llumo/sockets.py +1 -1
  5. {llumo-0.2.31 → llumo-0.2.33/llumo.egg-info}/PKG-INFO +1 -1
  6. {llumo-0.2.31 → llumo-0.2.33}/LICENSE +0 -0
  7. {llumo-0.2.31 → llumo-0.2.33}/MANIFEST.in +0 -0
  8. {llumo-0.2.31 → llumo-0.2.33}/README.md +0 -0
  9. {llumo-0.2.31 → llumo-0.2.33}/llumo/__init__.py +0 -0
  10. {llumo-0.2.31 → llumo-0.2.33}/llumo/callback.py +0 -0
  11. {llumo-0.2.31 → llumo-0.2.33}/llumo/callbacks-0.py +0 -0
  12. {llumo-0.2.31 → llumo-0.2.33}/llumo/chains.py +0 -0
  13. {llumo-0.2.31 → llumo-0.2.33}/llumo/exceptions.py +0 -0
  14. {llumo-0.2.31 → llumo-0.2.33}/llumo/execution.py +0 -0
  15. {llumo-0.2.31 → llumo-0.2.33}/llumo/functionCalling.py +0 -0
  16. {llumo-0.2.31 → llumo-0.2.33}/llumo/google.py +0 -0
  17. {llumo-0.2.31 → llumo-0.2.33}/llumo/llumoLogger.py +0 -0
  18. {llumo-0.2.31 → llumo-0.2.33}/llumo/llumoSessionContext.py +0 -0
  19. {llumo-0.2.31 → llumo-0.2.33}/llumo/models.py +0 -0
  20. {llumo-0.2.31 → llumo-0.2.33}/llumo/openai.py +0 -0
  21. {llumo-0.2.31 → llumo-0.2.33}/llumo.egg-info/SOURCES.txt +0 -0
  22. {llumo-0.2.31 → llumo-0.2.33}/llumo.egg-info/dependency_links.txt +0 -0
  23. {llumo-0.2.31 → llumo-0.2.33}/llumo.egg-info/requires.txt +0 -0
  24. {llumo-0.2.31 → llumo-0.2.33}/llumo.egg-info/top_level.txt +0 -0
  25. {llumo-0.2.31 → llumo-0.2.33}/setup.cfg +0 -0
  26. {llumo-0.2.31 → llumo-0.2.33}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llumo
3
- Version: 0.2.31
3
+ Version: 0.2.33
4
4
  Summary: Python SDK for interacting with the Llumo ai API.
5
5
  Home-page: https://www.llumo.ai/
6
6
  Author: Llumo
@@ -22,16 +22,22 @@ from tqdm import tqdm
22
22
 
23
23
  pd.set_option("future.no_silent_downcasting", True)
24
24
 
25
- postUrl = (
26
- "https://red-skull-service-392377961931.us-central1.run.app/api/process-playground"
27
- )
28
- fetchUrl = (
29
- "https://red-skull-service-392377961931.us-central1.run.app/api/get-cells-data"
30
- )
31
- socketDataUrl = "https://app.llumo.ai/api/eval/get-awaited"
25
+ # postUrl = (
26
+ # "https://redskull.llumo.ai/api/process-playground"
27
+ # )
28
+ postUrl = "http://localhost:4747/api/process-playground"
29
+ # fetchUrl = (
30
+ # "https://redskull.llumo.ai/api/get-cells-data"
31
+ # )
32
+ fetchUrl = "http://localhost:4747/api/get-cells-data"
33
+ fetchMissingEvalUrl = "http://localhost:4747/api/get-missing-keys"
34
+ socketDataUrl = "http://localhost:4747/api/eval/get-awaited"
32
35
 
33
36
  validateUrl = "https://app.llumo.ai/api/workspace-details"
34
- socketUrl = "https://red-skull-service-392377961931.us-central1.run.app/"
37
+ # socketUrl = "https://redskull.llumo.ai/"
38
+ socketUrl = "http://localhost:4747/"
39
+ createEvalUrl = "https://backend-api.llumo.ai/api/v1/create-debug-log-for-sdk"
40
+ # createEvalUrl = "http://localhost:4545/api/v1/create-debug-log-for-sdk"
35
41
 
36
42
 
37
43
  class LlumoClient:
@@ -200,15 +206,87 @@ class LlumoClient:
200
206
  "Content-Type": "application/json",
201
207
  }
202
208
  try:
203
- # print(postUrl)
209
+ print(postUrl)
204
210
  response = requests.post(postUrl, json=payload, headers=headers)
205
- # print(f"Post API Status Code: {response.status_code}")
206
- # print(response.text)
211
+ print(f"Post API Status Code: {response.status_code}")
212
+ print(response.text)
207
213
  # print(response.status_code)
208
214
 
209
215
  except Exception as e:
210
216
  print(f"Error in posting batch: {e}")
211
217
 
218
+
219
+ def fetchDataForMissingKeys(self, workspaceID, missingKeys: list):
220
+ # Define the URL and prepare the payload
221
+
222
+ payload = {
223
+ "workspaceID": workspaceID,
224
+ "missingKeys": missingKeys
225
+ }
226
+
227
+ headers = {
228
+ "Authorization": f"Bearer {self.socketToken}",
229
+ "Content-Type": "application/json",
230
+ }
231
+
232
+ try:
233
+ # Send a POST request to the API
234
+ response = requests.post(fetchMissingEvalUrl, json=payload, headers=headers)
235
+
236
+ # Check if the response is successful
237
+ if response.status_code == 200:
238
+ # Parse the JSON data from the response
239
+ data = response.json().get("data", {})
240
+
241
+
242
+ # Prepare the list of all data values in the desired format
243
+ result_list = []
244
+ for key, value in data.items():
245
+ # Create a dictionary for each item in the response data
246
+ result_list.append({
247
+ key: {
248
+ "value": value.get("value"),
249
+ "fullEval": value.get("fullEval", {}),
250
+ "runLog": value.get("runLog", {}),
251
+ "evallist": value.get("evallist", [])
252
+ }
253
+ })
254
+
255
+ print("Fetched data for missing keys:", result_list)
256
+ return result_list
257
+ else:
258
+ print(f"Failed to fetch data. Status Code: {response.status_code}")
259
+ return []
260
+
261
+ except Exception as e:
262
+ print(f"An error occurred: {e}")
263
+ return []
264
+
265
+
266
+ def createRunForEvalMultiple(self, smartLog):
267
+ payload = {
268
+ "log": smartLog
269
+ }
270
+ # socketToken here if the "JWD" token
271
+ headers = {
272
+ # "Authorization": f"Bearer {self.socketToken}",
273
+ "Content-Type": "application/json",
274
+ }
275
+ try:
276
+ # print(postUrl)
277
+ print(createEvalUrl)
278
+ print(payload)
279
+ print(headers)
280
+ response = requests.post(createEvalUrl, json=payload, headers=headers)
281
+
282
+ print(f"Post API Status Code: {response.status_code}")
283
+ print(response.text)
284
+ # print(response.status_code)
285
+
286
+ except Exception as e:
287
+ print(f"Error in posting batch: {e}")
288
+
289
+
212
290
  def postDataStream(self, batch, workspaceID):
213
291
  payload = {
214
292
  "batch": json.dumps(batch),
@@ -700,8 +778,9 @@ class LlumoClient:
700
778
  def evaluateMultiple(
701
779
  self,
702
780
  data,
703
- evals: list,
704
- prompt_template="Give answer to the given query: {{query}} using the given context: {{context}}.",
781
+ evals: list = [],
782
+ # prompt_template="Give answer to the given query: {{query}} using the given context: {{context}}.",
783
+ prompt_template="",
705
784
  getDataFrame: bool = False,
706
785
  _tocheck=True,
707
786
  ):
@@ -718,8 +797,10 @@ class LlumoClient:
718
797
  email = None
719
798
  try:
720
799
  socketID = self.socket.connect(timeout=250)
800
+ print("Socket connected with ID:", socketID)
721
801
  except Exception as e:
722
802
  socketID = "DummySocketID"
803
+ print(f"Socket connection failed, using dummy ID. Error: {str(e)}")
723
804
 
724
805
  self.evalData = []
725
806
  self.evals = evals
@@ -736,27 +817,33 @@ class LlumoClient:
736
817
  # raise RuntimeError("Timeout waiting for server connection")
737
818
 
738
819
  # Start listener thread
739
- expectedResults = len(dataframe) * len(evals)
820
+ # expectedResults = len(dataframe) * len(evals)
821
+ expectedResults = len(dataframe)
740
822
  # print("expected result" ,expectedResults)
741
- timeout = max(100, min(150, expectedResults * 10))
823
+ timeout = max(100, min(250, expectedResults * 60))
742
824
  listener_thread = threading.Thread(
743
825
  target=self.socket.listenForResults,
744
826
  kwargs={
745
827
  "min_wait": 20,
746
828
  "max_wait": timeout,
747
- "inactivity_timeout": 35,
829
+ "inactivity_timeout": timeout,
748
830
  "expected_results": expectedResults,
749
831
  },
750
832
  daemon=True,
751
833
  )
752
834
  listener_thread.start()
753
- self.validateApiKey(evalName=evals[0])
835
+ # commenting validate api key as we don't need it logger does it for us. uncommented but we need different
836
+ # api for this which don't spend time on eval defintiion fetches and just bring hits
837
+ self.validateApiKey()
754
838
  activePlayground = self.playgroundID
755
839
  # print(f"\n======= Running evaluation for: {evalName} =======")
756
840
 
757
841
  # Validate API and dependencies
758
842
  # self.validateApiKey(evalName=evals[0])
759
- customAnalytics = getCustomAnalytics(self.workspaceID)
843
+
844
+ # why we need custom analytics here? there is no such usage below
845
+ # customAnalytics = getCustomAnalytics(self.workspaceID)
846
+
760
847
  # metricDependencies = checkDependency(
761
848
  # evalName,
762
849
  # list(dataframe.columns),
@@ -789,37 +876,72 @@ class LlumoClient:
789
876
  self.hitsAvailable,
790
877
  len(dataframe),
791
878
  )
879
+
880
+ #where does this remaining hit comes from?
881
+
882
+
792
883
  if not userHits["success"]:
793
884
  raise LlumoAIError.InsufficientCredits(userHits["message"])
794
885
 
795
886
  currentBatch = []
887
+
888
+
796
889
  for index, row in dataframe.iterrows():
797
- tools = [row["tools"]] if "tools" in dataframe.columns else []
890
+ # Extract required fields
891
+ tools = row.get("tools", "")
798
892
  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","")
893
+ messageHistory = row.get("messageHistory", "")
813
894
  intermediateSteps = row.get("intermediateSteps", "")
814
-
895
+ output = row.get("output", "")
896
+
897
+ # Initialize query and context
898
+ query = ""
899
+ context = ""
900
+
901
+ # Process prompt template if provided
902
+ if prompt_template:
903
+ # Extract template variables
904
+ keys = re.findall(r"{{(.*?)}}", prompt_template)
905
+
906
+ if not all([key in dataframe.columns for key in keys]):
907
+ raise LlumoAIError.InvalidPromptTemplate()
908
+
909
+ # Populate template and separate query/context
910
+ populated_template = prompt_template
911
+ for key in keys:
912
+ value = row.get(key, "")
913
+ if isinstance(value, str):
914
+ length = len(value.split()) * 1.5
915
+ if length <= 50:
916
+ # Short value - include in query via template
917
+ temp_obj = {key: value}
918
+ populated_template = getInputPopulatedPrompt(populated_template, temp_obj)
919
+ else:
920
+ # Long value - add to context
921
+ context += f" {key}: {value}, "
922
+
923
+ query = populated_template.strip()
924
+
925
+ # Add any remaining context from other fields
926
+ if not context.strip():
927
+ for key, value in row.items():
928
+ if key not in keys and isinstance(value, str) and value.strip():
929
+ context += f" {key}: {value}, "
930
+ else:
931
+ # No prompt template - use direct query and context fields
932
+ query = row.get("query", "")
933
+ context = row.get("context", "")
934
+
935
+ # Generate unique IDs
815
936
  rowID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
816
937
  columnID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
817
-
938
+
818
939
  compoundKey = f"{rowID}-{columnID}-{columnID}"
819
- # rowIdMapping[compoundKey] = {"index": index, "eval": evalName}
820
940
  rowIdMapping[compoundKey] = {"index": index}
941
+ print("rowIdMapping:", rowIdMapping)
821
942
 
822
-
943
+ # Create evaluation payload
944
+ print("socketID in before templateData: ", socketID)
823
945
  templateData = {
824
946
  "processID": getProcessID(),
825
947
  "socketID": socketID,
@@ -832,65 +954,22 @@ class LlumoClient:
832
954
  "playgroundID": activePlayground,
833
955
  "source": "SDK",
834
956
  "processData": {
835
- # "analyticsName": evalName,
836
- # "definition": evalDefinition,
837
957
  "executionDependency": {
838
- "query": "",
839
- "context": "",
958
+ "query": query,
959
+ "context": context.strip(),
840
960
  "output": output,
841
961
  "tools": tools,
842
962
  "groundTruth": groundTruth,
843
963
  "messageHistory": messageHistory,
844
964
  "intermediateSteps": intermediateSteps,
845
965
  },
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,
966
+ "evallist": evals,
967
+ "sessionID": self.sessionID
857
968
  },
858
969
  "type": "FULL_EVAL_RUN",
859
- # "kpi": evalName,
860
- # "fieldMappig": fieldMapping,
861
970
  }
862
971
 
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
-
972
+ # Add to batch
894
973
  currentBatch.append(templateData)
895
974
  if len(currentBatch) == 10:
896
975
  self.allBatches.append(currentBatch)
@@ -920,19 +999,33 @@ class LlumoClient:
920
999
 
921
1000
  rawResults = self.socket.getReceivedData()
922
1001
 
1002
+ print(f"Total results received: {len(rawResults)}")
1003
+ # print("Raw results:", rawResults)
1004
+
923
1005
  # print("data from db #####################",dataFromDb)
924
1006
  # Fix here: keep full keys, do not split keys
925
1007
  receivedRowIDs = {key for item in rawResults for key in item.keys()}
1008
+ print("Received Row IDs:", receivedRowIDs)
926
1009
  expectedRowIDs = set(rowIdMapping.keys())
927
1010
  missingRowIDs = expectedRowIDs - receivedRowIDs
928
1011
  # print("All expected keys:", expectedRowIDs)
929
1012
  # print("All received keys:", receivedRowIDs)
930
1013
  # print("Missing keys:", len(missingRowIDs))
931
1014
  missingRowIDs = list(missingRowIDs)
932
- if len(missingRowIDs) > 0:
933
- dataFromDb = fetchData(workspaceID, activePlayground, missingRowIDs)
934
- rawResults.extend(dataFromDb)
935
1015
 
1016
+ print("Missing Row IDs:", missingRowIDs)
1017
+ print(f"Total results before fetching missing data: {len(rawResults)}")
1018
+ if len(missingRowIDs) > 0:
1019
+ print('''It's taking longer than expected to get results for some rows. You can close this now.
1020
+ Please wait for 15 mins while we create the flow graph for you. You can check the graph at app.llumo.ai/debugger''')
1021
+ else:
1022
+ print('''All results received successfully. You can check flowgraph in 5 mins at app.llumo.ai/debugger''')
1023
+ # if len(missingRowIDs) > 0:
1024
+ # dataFromDb = self.fetchDataForMissingKeys(workspaceID, missingRowIDs)
1025
+ # # print("Fetched missing data from DB:", dataFromDb)
1026
+ # rawResults.extend(dataFromDb)
1027
+ # print(f"Total results after fetching missing data: {len(rawResults)}")
1028
+
936
1029
  self.evalData = rawResults
937
1030
  # print("RAW RESULTS: ", self.evalData)
938
1031
 
@@ -950,9 +1043,6 @@ class LlumoClient:
950
1043
  index = rowIdMapping[compound_key]["index"]
951
1044
  rowID, columnID, _ = compound_key.split("-", 2)
952
1045
 
953
- if hasattr(self, "startLlumoRun"):
954
- self.startLlumoRun(runName="evaluateMultiple",rowID = rowID, columnID = columnID)
955
-
956
1046
  # get the dataframe row at this index
957
1047
  row = dataframe.iloc[index].to_dict()
958
1048
 
@@ -964,10 +1054,10 @@ class LlumoClient:
964
1054
  fullEval = value.get("fullEval") if isinstance(value, dict) else None
965
1055
  if fullEval:
966
1056
  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")
1057
+ for evalItem in fullEval["evalMetrics"]:
1058
+ evalName = evalItem.get("evalName") or evalItem.get("kpiName")
1059
+ score = str(evalItem.get("score")) or evalItem.get("value")
1060
+ reasoning = evalItem.get("reasoning")
971
1061
  # edgeCase = eval_item.get("edgeCase")
972
1062
 
973
1063
  if evalName:
@@ -975,33 +1065,16 @@ class LlumoClient:
975
1065
  dataframe.at[index, f"{evalName} Reason"] = reasoning
976
1066
  # dataframe.at[index, f"{evalName} EdgeCase"] = edgeCase
977
1067
 
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
1068
+
1069
+ # runLog = value.get("runLog") if isinstance(value, dict) else None
1070
+ # if runLog:
1071
+ # try:
1072
+ # self.createRunForEvalMultiple(smartLog=runLog)
1073
+ # except Exception as e:
1074
+ # print(f"Error posting smartlog: {e}")
1075
+
1076
+
1077
+
1005
1078
  try:
1006
1079
  self.socket.disconnect()
1007
1080
  except Exception:
@@ -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 = "http://localhost:4747/api/eval/get-awaited"
589
589
  payload = {
590
590
  "workspaceID": workspaceID,
591
591
  "playgroundID": playgroundID,
@@ -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.31
3
+ Version: 0.2.33
4
4
  Summary: Python SDK for interacting with the Llumo ai API.
5
5
  Home-page: https://www.llumo.ai/
6
6
  Author: Llumo
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes