llumo 0.2.13b1__py3-none-any.whl → 0.2.14b2__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
@@ -80,17 +80,16 @@ class LlumoClient:
80
80
  raise LlumoAIError.InvalidApiResponse()
81
81
 
82
82
  try:
83
- self.hitsAvailable = data["data"]["data"].get("remainingHits", 0)
83
+ self.hitsAvailable = data['data']["data"].get("remainingHits", 0)
84
84
  self.workspaceID = data["data"]["data"].get("workspaceID")
85
- self.evalDefinition = data["data"]["data"].get("analyticsMapping")
85
+ self.evalDefinition = data["data"]["data"]["analyticsMapping"]
86
86
  self.socketToken = data["data"]["data"].get("token")
87
87
  self.hasSubscribed = data["data"]["data"].get("hasSubscribed", False)
88
88
  self.trialEndDate = data["data"]["data"].get("trialEndDate", None)
89
89
  self.subscriptionEndDate = data["data"]["data"].get("subscriptionEndDate", None)
90
90
  self.email = data["data"]["data"].get("email", None)
91
91
 
92
- self.definationMapping[evalName] = self.evalDefinition
93
-
92
+ self.definationMapping[evalName] = data["data"]["data"]["analyticsMapping"][evalName]
94
93
  except Exception as e:
95
94
  # print(f"Error extracting data from response: {str(e)}")
96
95
  raise LlumoAIError.UnexpectedError(detail=str(e))
@@ -159,12 +158,21 @@ class LlumoClient:
159
158
  # this function allows the users to run exactl one eval at a time
160
159
  def evaluate(
161
160
  self,
162
- dataframe,
161
+ data,
163
162
  eval="Response Completeness",
164
163
  prompt_template="",
165
164
  outputColName="output",
166
165
  createExperiment: bool = False,
166
+ _tocheck = True,
167
167
  ):
168
+
169
+ # converting it into a pandas dataframe object
170
+ dataframe = pd.DataFrame(data)
171
+
172
+ # check for dependencies for the selected eval metric
173
+ metricDependencies = checkDependency(eval,columns=list(dataframe.columns),tocheck=_tocheck)
174
+ if metricDependencies["status"] == False:
175
+ raise LlumoAIError.dependencyError(metricDependencies["message"])
168
176
 
169
177
  results = {}
170
178
  try:
@@ -206,7 +214,7 @@ class LlumoClient:
206
214
  # if self.hitsAvailable == 0 or len(dataframe) > self.hitsAvailable:
207
215
  # raise LlumoAIError.InsufficientCredits()
208
216
 
209
- evalDefinition = self.evalDefinition[eval]
217
+ evalDefinition = self.evalDefinition[eval].get("definition")
210
218
  model = "GPT_4"
211
219
  provider = "OPENAI"
212
220
  evalType = "LLM"
@@ -320,11 +328,11 @@ class LlumoClient:
320
328
  try:
321
329
 
322
330
  self.postBatch(batch=batch, workspaceID=workspaceID)
323
- # print("Betch Posted with item len: ", len(batch))
331
+ print("Betch Posted with item len: ", len(batch))
324
332
  except Exception as e:
325
333
  continue
326
334
 
327
- time.sleep(3)
335
+ # time.sleep(3)
328
336
 
329
337
  timeout = max(50, min(600, totalItems * 10))
330
338
 
@@ -377,32 +385,71 @@ class LlumoClient:
377
385
  # this function allows the users to run multiple evals at once
378
386
  def evaluateMultiple(
379
387
  self,
380
- dataframe,
388
+ data,
381
389
  eval=["Response Completeness"],
382
- prompt_template="",
390
+ prompt_template="Give answer to the given query:{{query}} , using the given context: {{context}}",
383
391
  outputColName="output",
384
392
  createExperiment: bool = False,
393
+ _tocheck = True,
385
394
  ):
386
- resultdf = dataframe.copy()
395
+ """
396
+ Runs multiple evaluation metrics on the same input dataset.
397
+
398
+ Parameters:
399
+ data (list of dict): Input data, where each dict represents a row.
400
+ eval (list of str): List of evaluation metric names to run.
401
+ prompt_template (str): Optional prompt template used in evaluation.
402
+ outputColName (str): Column name in data that holds the model output.
403
+ createExperiment (bool): Whether to log the results to Llumo playground.
404
+
405
+ Returns:
406
+ pandas.DataFrame: Final dataframe with all evaluation results.
407
+ """
408
+
409
+ # Convert input dict list into a DataFrame
410
+ dataframe = pd.DataFrame(data)
411
+
412
+ # Copy to hold final results
413
+ resultdf = dataframe.copy()
414
+
415
+ # Run each evaluation metric one by one
387
416
  for evalName in eval:
388
- time.sleep(2)
389
- resultdf = self.evaluate(dataframe = resultdf,eval=evalName,prompt_template=prompt_template,outputColName=outputColName,createExperiment = False)
417
+ # time.sleep(2) # small delay to avoid overload or rate limits
418
+
419
+ # Call evaluate (assumes evaluate takes dict, not dataframe)
420
+ resultdf = self.evaluate(
421
+ data=resultdf.to_dict(orient="records"), # convert df back to dict list
422
+ eval=evalName,
423
+ prompt_template=prompt_template,
424
+ outputColName=outputColName,
425
+ createExperiment=False,
426
+ _tocheck=_tocheck,
427
+ )
390
428
 
429
+ # Save to playground if requested
391
430
  if createExperiment:
392
431
  pd.set_option("future.no_silent_downcasting", True)
393
432
  df = resultdf.fillna("Some error occured").astype(object)
394
433
 
395
- if createPlayground(self.email, self.workspaceID, df,definationMapping=self.definationMapping,outputColName=outputColName,promptText=prompt_template):
434
+ if createPlayground(
435
+ self.email,
436
+ self.workspaceID,
437
+ df,
438
+ definationMapping=self.definationMapping,
439
+ outputColName=outputColName,
440
+ promptText=prompt_template
441
+ ):
396
442
  print(
397
- "Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results.Please rerun the experiment to see the results on playground."
443
+ "Your data has been saved in the Llumo Experiment. "
444
+ "Visit https://app.llumo.ai/evallm to see the results. "
445
+ "Please rerun the experiment to see the results on playground."
398
446
  )
399
447
  else:
400
448
  return resultdf
401
449
 
402
-
403
- def evaluateCompressor(self, dataframe, prompt_template):
450
+ def evaluateCompressor(self, data, prompt_template):
404
451
  results = []
405
-
452
+ dataframe = pd.DataFrame(data)
406
453
  try:
407
454
  socketID = self.socket.connect(timeout=150)
408
455
  max_wait_secs = 20
@@ -609,10 +656,11 @@ class LlumoClient:
609
656
  outputColName = f"output_{i}"
610
657
  try:
611
658
  res = self.evaluateMultiple(
612
- df,
659
+ df.to_dict("records"),
613
660
  eval=eval,
614
661
  prompt_template=str(templates[0]),
615
662
  outputColName=outputColName,
663
+ _tocheck=False,
616
664
  )
617
665
 
618
666
  # Rename all new columns with _i+1 (e.g., _1, _2)
@@ -634,7 +682,7 @@ class LlumoClient:
634
682
  if createExperiment:
635
683
  pd.set_option("future.no_silent_downcasting", True)
636
684
  dfWithEvals = dfWithEvals.fillna("Some error occurred")
637
- if createPlayground(email, workspaceID, dfWithEvals, promptText=templates[0]):
685
+ if createPlayground(email, workspaceID, dfWithEvals, promptText=templates[0],definationMapping=self.definationMapping):
638
686
 
639
687
  print("Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results.")
640
688
  else:
@@ -653,7 +701,7 @@ class LlumoClient:
653
701
  # this function generates an output using llm and tools and evaluate that output
654
702
  def evaluateAgents(
655
703
  self,
656
- dataframe,
704
+ data,
657
705
  model,
658
706
  agents,
659
707
  model_api_key=None,
@@ -664,6 +712,9 @@ class LlumoClient:
664
712
  if model.lower() not in ["openai", "google"]:
665
713
  raise ValueError("Model must be 'openai' or 'google'")
666
714
 
715
+ # converting into pandas dataframe object
716
+ dataframe = pd.DataFrame(data)
717
+
667
718
  # Run unified agent execution
668
719
  toolResponseDf = LlumoAgentExecutor.run(
669
720
  dataframe, agents, model=model, model_api_key=model_api_key
@@ -680,7 +731,7 @@ class LlumoClient:
680
731
  for eval in evals:
681
732
  # Perform evaluation
682
733
  toolResponseDf = self.evaluate(
683
- toolResponseDf,
734
+ toolResponseDf.to_dict(orient = "records"),
684
735
  eval=eval,
685
736
  prompt_template=prompt_template,
686
737
  createExperiment=False,
@@ -698,32 +749,34 @@ class LlumoClient:
698
749
  # this function evaluate that tools output given by the user
699
750
  def evaluateAgentResponses(
700
751
  self,
701
- dataframe,
752
+ data,
702
753
  evals=["Final Task Alignment"],
703
754
  outputColName="output",
704
755
  createExperiment: bool = False,
705
756
  ):
757
+ dataframe = pd.DataFrame(data)
758
+
706
759
  try:
707
760
  if "query" and "messageHistory" and "tools" not in dataframe.columns:
708
761
  raise ValueError(
709
762
  "DataFrame must contain 'query', 'messageHistory','output' ,and 'tools' columns. Make sure the columns names are same as mentioned here."
710
763
  )
711
- prompt_template="Give answer for the given query: {{query}}"
712
-
764
+
765
+
713
766
  # evals = [
714
767
  # "Tool Reliability",
715
768
  # "Stepwise Progression",
716
769
  # "Tool Selection Accuracy",
717
770
  # "Final Task Alignment",
718
771
  # ]
772
+
719
773
  toolResponseDf = dataframe.copy()
720
774
  for eval in evals:
721
775
  # Perform evaluation
722
776
  toolResponseDf = self.evaluate(
723
- toolResponseDf, eval=eval, prompt_template=prompt_template,outputColName=outputColName
777
+ toolResponseDf.to_dict(orient = "records"), eval=eval, prompt_template="Give answer for the given query: {{query}}",outputColName=outputColName
724
778
  )
725
-
726
-
779
+
727
780
  return toolResponseDf
728
781
 
729
782
  except Exception as e:
llumo/exceptions.py CHANGED
@@ -46,6 +46,10 @@ class LlumoAIError(Exception):
46
46
  def modelHitsExhausted(details = "Your credits for the selected model exhausted."):
47
47
  return LlumoAIError(details)
48
48
 
49
+ @staticmethod
50
+ def dependencyError(details):
51
+ return LlumoAIError(details)
52
+
49
53
  # @staticmethod
50
54
  # def dateNotFound():
51
55
  # return LlumoAIError("Trial end date or subscription end date not found for the given user.")
llumo/helpingFuntions.py CHANGED
@@ -303,8 +303,8 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
303
303
  dependencies.append(columnIDMapping[varName])
304
304
 
305
305
  dependencies.append(columnIDMapping[outputColName]) # Add the output column ID
306
- longDef = definationMapping.get(col, {}).get(col, "")
307
- shortDef ="You have run this from SDK"
306
+ longDef = definationMapping.get(col, {}).get('definition', "")
307
+ shortDef =definationMapping.get(col, {}).get('briefDefinition', "")
308
308
  enum = col.upper().replace(" ","_")
309
309
 
310
310
  template = {
@@ -312,7 +312,7 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
312
312
  col.lower().replace(" ","_")
313
313
  ],
314
314
  "evaluationMetric": "ALL",
315
- "evaluationModel": "GEMINI_PRO",
315
+ "evaluationModel": "LLUMO_EVALLM",
316
316
  "selectPrompt": columnIDMapping[outputColName],
317
317
  "scoreCondition": "GREATER_THAN",
318
318
  "scoreValue": "50",
@@ -336,7 +336,7 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
336
336
  "analyticsENUM": enum,
337
337
  "prompt": shortDef,
338
338
  "analyticsName": col,
339
- "columnID": str(uuid.uuid4().hex[:8]),
339
+ "columnID": columnID,
340
340
  "label": col,
341
341
  "order": indx
342
342
  }
@@ -371,9 +371,26 @@ def createColumn(workspaceID, dataframe, playgroundID, promptText=None,queryColN
371
371
 
372
372
  # For each column, we need to map the column ID to the corresponding value in the row
373
373
  for col in dataframe.columns:
374
- columnID = columnIDMapping[col] # Get the columnID from the mapping
375
- row_dict[columnID] = row[col] # Map the columnID to the value in the row
376
-
374
+ columnID = columnIDMapping[col]
375
+
376
+ # if col in allEvals:
377
+ # row_dict[columnID] = {
378
+
379
+ # "value": row[col],
380
+ # "type": "EVAL",
381
+ # "isValid": True,
382
+ # "reasoning": row[col+" Reason"],
383
+ # "edgeCase": "minorHallucinationDetailNotInContext",
384
+ # "kpi": col
385
+
386
+ # }
387
+ # else:# Get the columnID from the mapping
388
+ # row_dict[columnID] = {
389
+ # "value": row[col],
390
+ # "type":"VARIABLE"# Map the columnID to the value in the row
391
+ # }
392
+
393
+ row_dict[columnID] = row[col] # Directly map the column ID to the row value
377
394
  # Add the row index (if necessary)
378
395
  row_dict["pIndex"] = indx
379
396
  rowTemplate["dataToUploadList"].append(row_dict)
@@ -430,6 +447,9 @@ def createPlayground(email, workspaceID, df, promptText=None,queryColName=None,d
430
447
  payload1, payload2 = createColumn(
431
448
  workspaceID=workspaceID, dataframe=df, playgroundID=playgroundId, promptText=promptText,queryColName=queryColName,dataStreamName=dataStreamName,definationMapping=definationMapping,outputColName=outputColName
432
449
  )
450
+
451
+
452
+
433
453
  deleteExistingRows = deleteColumnListInPlayground(
434
454
  workspaceID=workspaceID, playgroundID=playgroundId
435
455
  )
@@ -490,4 +510,58 @@ def getPlaygroundInsights(workspaceID: str, activePlayground: str):
490
510
  else:
491
511
  print(f"Error generating insight: {responseGenerate.status_code} - {responseGenerate.text}")
492
512
  return None
493
-
513
+ def checkDependency(selectedEval, columns,tocheck=True):
514
+ """
515
+ Checks if all the required input columns for the selected evaluation metric are present.
516
+
517
+ Parameters:
518
+ - selectedEval (str): The name of the selected evaluation metric.
519
+ - columns (list): List of column names present in the dataset.
520
+
521
+ Raises:
522
+ - LlumoAIError.dependencyError: If any required column is missing.
523
+ """
524
+ if tocheck:
525
+ # Define required dependencies for each evaluation metric
526
+ metricDependencies = {
527
+ 'Response Completeness': ['context', 'query', 'output'],
528
+ 'Response Bias': ['output'],
529
+ 'Response Harmfulness': ['output'],
530
+ 'Input Toxicity': ['query'],
531
+ 'Input Harmfulness': ['query'],
532
+ 'Context Utilization': ['output', 'context'],
533
+ 'Relevance Retention': ['context', 'query'],
534
+ 'Semantic Cohesion': ['context'],
535
+ 'Final Task Alignment': ['messageHistory'],
536
+ 'Tool Reliability': ['messageHistory'],
537
+ 'Response Correctness': ['output', 'query', 'context'],
538
+ 'Response Toxicity': ['output'],
539
+ 'Input Bias': ['query'],
540
+ 'Input Relevancy': ['context', 'query'],
541
+ 'Redundancy Reduction': ['context'],
542
+ 'Response Sentiment': ['output'],
543
+ 'Tool Selection Accuracy': ['tools', 'messageHistory'],
544
+ 'Stepwise Progression': ['tools', 'messageHistory'],
545
+ 'Hallucination': ['query', 'context', 'output'],
546
+ 'Groundedness': ['groundTruth', 'output'],
547
+ 'Memory Utilization': ['context', 'messageHistory'],
548
+ 'Input Relevancy (Multi-turn)': ['context', 'query']
549
+ }
550
+
551
+ # Check if the selected evaluation metric is known
552
+ if selectedEval not in metricDependencies:
553
+ return {"status": False,"message":f"Unknown evaluation metric: {selectedEval}"}
554
+
555
+ # Get the required columns for the selected evaluation
556
+ columnsRequired = metricDependencies[selectedEval]
557
+
558
+ # Check if each required column is present in the provided columns
559
+ for requirement in columnsRequired:
560
+ if requirement not in columns:
561
+ return {"status":False,
562
+ "message":f"'{selectedEval}' requires columns: {columnsRequired}. "
563
+ f"Missing: '{requirement}'. Please ensure your data includes all required columns."
564
+ }
565
+ return {"status":True,"message":"success"}
566
+ else:
567
+ return {"status":True,"message":"success"}
llumo/sockets.py CHANGED
@@ -128,7 +128,7 @@ class LlumoSocketClient:
128
128
  self._listening_done.set()
129
129
  break
130
130
 
131
- time.sleep(3)
131
+
132
132
 
133
133
  timeout_thread = threading.Thread(target=timeout_watcher, daemon=True)
134
134
  timeout_thread.start()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llumo
3
- Version: 0.2.13b1
3
+ Version: 0.2.14b2
4
4
  Summary: Python SDK for interacting with the Llumo ai API.
5
5
  Home-page: https://www.llumo.ai/
6
6
  Author: Llumo
@@ -0,0 +1,13 @@
1
+ llumo/__init__.py,sha256=O04b4yW1BnOvcHzxWFddAKhtdBEhBNhLdb6xgnpHH_Q,205
2
+ llumo/client.py,sha256=Iy16dr-bPDWQ9iRRaNDnlJvZ_j52qEzLJWK2-CcDpwM,38135
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=mFoRtxpG4T7enXINTigN7Xztdbj1IKeGNvhrFvwrgSg,20360
7
+ llumo/models.py,sha256=YH-qAMnShmUpmKE2LQAzQdpRsaXkFSlOqMxHwU4zBUI,1560
8
+ llumo/sockets.py,sha256=4X1KSdCJX8_sRY5E_m9bv2kd8B8Jymg_QM59de-FqLw,5570
9
+ llumo-0.2.14b2.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
10
+ llumo-0.2.14b2.dist-info/METADATA,sha256=SMGdn7-5oxYWRpz1IN-sJxU8NtaQz_CqQFOe9J8mDws,1493
11
+ llumo-0.2.14b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
+ llumo-0.2.14b2.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
13
+ llumo-0.2.14b2.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- llumo/__init__.py,sha256=O04b4yW1BnOvcHzxWFddAKhtdBEhBNhLdb6xgnpHH_Q,205
2
- llumo/client.py,sha256=pzmJkz5LRF3h1WgjmezNnJEUAZ9_5nF47eW489F9-y4,36026
3
- llumo/exceptions.py,sha256=iCj7HhtO_ckC2EaVBdXbAudNpuMDsYmmMEV5lwynZ-E,1854
4
- llumo/execution.py,sha256=x88wQV8eL99wNN5YtjFaAMCIfN1PdfQVlAZQb4vzgQ0,1413
5
- llumo/functionCalling.py,sha256=D5jYapu1rIvdIJNUYPYMTyhQ1H-6nkwoOLMi6eekfUE,7241
6
- llumo/helpingFuntions.py,sha256=ah0FUQcRV3gfguvjQQ_aZzq59hpJttqAPJdjJVNYdFc,17110
7
- llumo/models.py,sha256=YH-qAMnShmUpmKE2LQAzQdpRsaXkFSlOqMxHwU4zBUI,1560
8
- llumo/sockets.py,sha256=0BCcdCaiXDR7LO_9NIYA6urtpgdmyWW2M1US67G9Eus,5583
9
- llumo-0.2.13b1.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
10
- llumo-0.2.13b1.dist-info/METADATA,sha256=Kyb0OFYTsOosmZ6Rcok4LNgWqVsUldzjeXmnw2vOnGA,1493
11
- llumo-0.2.13b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- llumo-0.2.13b1.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
13
- llumo-0.2.13b1.dist-info/RECORD,,