llumo 0.1.9b10__py3-none-any.whl → 0.2.1__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 +291 -145
- llumo/helpingFuntions.py +179 -10
- {llumo-0.1.9b10.dist-info → llumo-0.2.1.dist-info}/METADATA +1 -1
- llumo-0.2.1.dist-info/RECORD +13 -0
- llumo-0.1.9b10.dist-info/RECORD +0 -13
- {llumo-0.1.9b10.dist-info → llumo-0.2.1.dist-info}/WHEEL +0 -0
- {llumo-0.1.9b10.dist-info → llumo-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {llumo-0.1.9b10.dist-info → llumo-0.2.1.dist-info}/top_level.txt +0 -0
llumo/client.py
CHANGED
@@ -12,7 +12,7 @@ import os
|
|
12
12
|
import itertools
|
13
13
|
import pandas as pd
|
14
14
|
from typing import List, Dict
|
15
|
-
from .models import AVAILABLEMODELS,getProviderFromModel
|
15
|
+
from .models import AVAILABLEMODELS, getProviderFromModel
|
16
16
|
from .execution import ModelExecutor
|
17
17
|
from .helpingFuntions import *
|
18
18
|
from .sockets import LlumoSocketClient
|
@@ -20,13 +20,15 @@ from .functionCalling import LlumoAgentExecutor
|
|
20
20
|
|
21
21
|
|
22
22
|
# 👇 NEW: Explicitly load .env from the package folder
|
23
|
-
envPath = os.path.join(os.path.dirname(__file__),
|
24
|
-
load_dotenv(
|
23
|
+
envPath = os.path.join(os.path.dirname(__file__), ".env")
|
24
|
+
load_dotenv(
|
25
|
+
dotenv_path=envPath, override=False
|
26
|
+
) # Automatically looks for .env in current directory
|
25
27
|
|
26
|
-
postUrl =
|
27
|
-
fetchUrl =
|
28
|
-
validateUrl =
|
29
|
-
socketUrl =
|
28
|
+
postUrl = "https://app.llumo.ai/api/eval/run-multiple-column"
|
29
|
+
fetchUrl = "https://app.llumo.ai/api/eval/fetch-rows-data-by-column"
|
30
|
+
validateUrl = "https://app.llumo.ai/api/workspace-details"
|
31
|
+
socketUrl = "https://red-skull-service-392377961931.us-central1.run.app/"
|
30
32
|
|
31
33
|
|
32
34
|
class LlumoClient:
|
@@ -35,27 +37,17 @@ class LlumoClient:
|
|
35
37
|
self.apiKey = api_key
|
36
38
|
self.socket = LlumoSocketClient(socketUrl)
|
37
39
|
self.processMapping = {}
|
38
|
-
|
39
40
|
|
40
|
-
|
41
|
-
def validateApiKey(self, evalName = ""):
|
41
|
+
def validateApiKey(self, evalName=""):
|
42
42
|
headers = {
|
43
43
|
"Authorization": f"Bearer {self.apiKey}",
|
44
44
|
"Content-Type": "application/json",
|
45
45
|
}
|
46
46
|
reqBody = {"analytics": [evalName]}
|
47
47
|
|
48
|
-
# print(f"Making API key validation request to: {validateUrl}")
|
49
|
-
# print(f"Request body: {reqBody}")
|
50
|
-
|
51
48
|
try:
|
52
49
|
response = requests.post(url=validateUrl, json=reqBody, headers=headers)
|
53
|
-
# print(response.text)
|
54
|
-
# Print response info for debugging
|
55
|
-
# print(f"Response status code: {response.status_code}")
|
56
|
-
# print(f"Response headers: {response.headers}")
|
57
50
|
|
58
|
-
# Try to get at least some of the response content
|
59
51
|
try:
|
60
52
|
response_preview = response.text[:500] # First 500 chars
|
61
53
|
# print(f"Response preview: {response_preview}")
|
@@ -75,11 +67,6 @@ class LlumoClient:
|
|
75
67
|
detail=f"Endpoint not found (404): {validateUrl}"
|
76
68
|
)
|
77
69
|
|
78
|
-
# if response.status_code >= 500:
|
79
|
-
# raise LlumoAIError.ServerError(
|
80
|
-
# detail=f"Server error ({response.status_code})"
|
81
|
-
# )
|
82
|
-
|
83
70
|
if response.status_code != 200:
|
84
71
|
raise LlumoAIError.RequestFailed(
|
85
72
|
detail=f"Unexpected status code: {response.status_code}"
|
@@ -102,14 +89,10 @@ class LlumoClient:
|
|
102
89
|
self.workspaceID = data["data"].get("workspaceID")
|
103
90
|
self.evalDefinition = data["data"].get("analyticsMapping")
|
104
91
|
self.socketToken = data["data"].get("token")
|
105
|
-
self.hasSubscribed = data["data"].get("hasSubscribed",False)
|
106
|
-
self.trialEndDate = data["data"].get("trialEndDate",None)
|
92
|
+
self.hasSubscribed = data["data"].get("hasSubscribed", False)
|
93
|
+
self.trialEndDate = data["data"].get("trialEndDate", None)
|
107
94
|
self.subscriptionEndDate = data["data"].get("subscriptionEndDate", None)
|
108
|
-
|
109
|
-
# print(f"API key validation successful:")
|
110
|
-
# print(f"- Remaining hits: {self.hitsAvailable}")
|
111
|
-
# print(f"- Workspace ID: {self.workspaceID}")
|
112
|
-
# print(f"- Token received: {'Yes' if self.socketToken else 'No'}")
|
95
|
+
self.email = data["data"].get("email", None)
|
113
96
|
|
114
97
|
except Exception as e:
|
115
98
|
# print(f"Error extracting data from response: {str(e)}")
|
@@ -134,7 +117,6 @@ class LlumoClient:
|
|
134
117
|
|
135
118
|
except Exception as e:
|
136
119
|
print(f"Error in posting batch: {e}")
|
137
|
-
|
138
120
|
|
139
121
|
def postDataStream(self, batch, workspaceID):
|
140
122
|
payload = {
|
@@ -155,31 +137,36 @@ class LlumoClient:
|
|
155
137
|
|
156
138
|
except Exception as e:
|
157
139
|
print(f"Error in posting batch: {e}")
|
158
|
-
|
159
140
|
|
160
141
|
def AllProcessMapping(self):
|
161
142
|
for batch in self.allBatches:
|
162
143
|
for record in batch:
|
163
|
-
rowId = record[
|
164
|
-
colId = record[
|
165
|
-
pid = f
|
144
|
+
rowId = record["rowID"]
|
145
|
+
colId = record["columnID"]
|
146
|
+
pid = f"{rowId}-{colId}-{colId}"
|
166
147
|
self.processMapping[pid] = record
|
167
148
|
|
168
|
-
|
169
|
-
def finalResp(self,results):
|
149
|
+
def finalResp(self, results):
|
170
150
|
seen = set()
|
171
151
|
uniqueResults = []
|
172
152
|
|
173
153
|
for item in results:
|
174
154
|
for rowID in item: # Each item has only one key
|
175
|
-
|
155
|
+
# for rowID in item["data"]:
|
176
156
|
if rowID not in seen:
|
177
157
|
seen.add(rowID)
|
178
158
|
uniqueResults.append(item)
|
179
159
|
|
180
160
|
return uniqueResults
|
181
161
|
|
182
|
-
def evaluate(
|
162
|
+
def evaluate(
|
163
|
+
self,
|
164
|
+
dataframe,
|
165
|
+
eval="Response Completeness",
|
166
|
+
prompt_template="",
|
167
|
+
outputColName="output",
|
168
|
+
createExperiment: bool = False,
|
169
|
+
):
|
183
170
|
|
184
171
|
results = {}
|
185
172
|
try:
|
@@ -192,11 +179,12 @@ class LlumoClient:
|
|
192
179
|
time.sleep(0.1)
|
193
180
|
waited_secs += 0.1
|
194
181
|
if waited_secs >= max_wait_secs:
|
195
|
-
raise RuntimeError(
|
182
|
+
raise RuntimeError(
|
183
|
+
"Timeout waiting for server 'connection-established' event."
|
184
|
+
)
|
196
185
|
|
197
186
|
rowIdMapping = {}
|
198
187
|
|
199
|
-
|
200
188
|
print(f"\n======= Running evaluation for: {eval} =======")
|
201
189
|
|
202
190
|
try:
|
@@ -205,8 +193,14 @@ class LlumoClient:
|
|
205
193
|
if hasattr(e, "response") and getattr(e, "response", None) is not None:
|
206
194
|
pass
|
207
195
|
raise
|
208
|
-
userHits = checkUserHits(
|
209
|
-
|
196
|
+
userHits = checkUserHits(
|
197
|
+
self.workspaceID,
|
198
|
+
self.hasSubscribed,
|
199
|
+
self.trialEndDate,
|
200
|
+
self.subscriptionEndDate,
|
201
|
+
self.hitsAvailable,
|
202
|
+
len(dataframe),
|
203
|
+
)
|
210
204
|
|
211
205
|
if not userHits["success"]:
|
212
206
|
raise LlumoAIError.InsufficientCredits(userHits["message"])
|
@@ -219,14 +213,21 @@ class LlumoClient:
|
|
219
213
|
provider = "OPENAI"
|
220
214
|
evalType = "LLM"
|
221
215
|
workspaceID = self.workspaceID
|
216
|
+
email = self.email
|
222
217
|
|
223
218
|
self.allBatches = []
|
224
219
|
currentBatch = []
|
225
220
|
|
226
221
|
for index, row in dataframe.iterrows():
|
227
222
|
tools = [row["tools"]] if "tools" in dataframe.columns else []
|
228
|
-
groundTruth =
|
229
|
-
|
223
|
+
groundTruth = (
|
224
|
+
row["groundTruth"] if "groundTruth" in dataframe.columns else ""
|
225
|
+
)
|
226
|
+
messageHistory = (
|
227
|
+
[row["messageHistory"]]
|
228
|
+
if "messageHistory" in dataframe.columns
|
229
|
+
else []
|
230
|
+
)
|
230
231
|
promptTemplate = prompt_template
|
231
232
|
|
232
233
|
keys = re.findall(r"{{(.*?)}}", promptTemplate)
|
@@ -235,9 +236,13 @@ class LlumoClient:
|
|
235
236
|
raise LlumoAIError.InvalidPromptTemplate()
|
236
237
|
|
237
238
|
inputDict = {key: row[key] for key in keys if key in row}
|
238
|
-
output =
|
239
|
+
output = (
|
240
|
+
row[outputColName] if outputColName in dataframe.columns else ""
|
241
|
+
)
|
239
242
|
|
240
|
-
activePlayground = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace(
|
243
|
+
activePlayground = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace(
|
244
|
+
"-", ""
|
245
|
+
)
|
241
246
|
rowID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
242
247
|
columnID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
243
248
|
|
@@ -270,6 +275,7 @@ class LlumoClient:
|
|
270
275
|
"rowID": rowID,
|
271
276
|
"playgroundID": activePlayground,
|
272
277
|
"processType": "EVAL",
|
278
|
+
"email": email,
|
273
279
|
}
|
274
280
|
|
275
281
|
query = ""
|
@@ -282,7 +288,9 @@ class LlumoClient:
|
|
282
288
|
else:
|
283
289
|
if promptTemplate:
|
284
290
|
tempObj = {key: value}
|
285
|
-
promptTemplate = getInputPopulatedPrompt(
|
291
|
+
promptTemplate = getInputPopulatedPrompt(
|
292
|
+
promptTemplate, tempObj
|
293
|
+
)
|
286
294
|
else:
|
287
295
|
query += f" {key}: {value}, "
|
288
296
|
|
@@ -290,11 +298,17 @@ class LlumoClient:
|
|
290
298
|
for key, value in inputDict.items():
|
291
299
|
context += f" {key}: {value}, "
|
292
300
|
|
293
|
-
templateData["processData"]["executionDependency"][
|
294
|
-
|
301
|
+
templateData["processData"]["executionDependency"][
|
302
|
+
"context"
|
303
|
+
] = context.strip()
|
304
|
+
templateData["processData"]["executionDependency"][
|
305
|
+
"query"
|
306
|
+
] = query.strip()
|
295
307
|
|
296
308
|
if promptTemplate and not query.strip():
|
297
|
-
templateData["processData"]["executionDependency"][
|
309
|
+
templateData["processData"]["executionDependency"][
|
310
|
+
"query"
|
311
|
+
] = promptTemplate
|
298
312
|
|
299
313
|
currentBatch.append(templateData)
|
300
314
|
|
@@ -316,7 +330,10 @@ class LlumoClient:
|
|
316
330
|
timeout = max(50, min(600, totalItems * 10))
|
317
331
|
|
318
332
|
self.socket.listenForResults(
|
319
|
-
min_wait=40,
|
333
|
+
min_wait=40,
|
334
|
+
max_wait=timeout,
|
335
|
+
inactivity_timeout=150,
|
336
|
+
expected_results=totalItems,
|
320
337
|
)
|
321
338
|
|
322
339
|
eval_results = self.socket.getReceivedData()
|
@@ -334,26 +351,33 @@ class LlumoClient:
|
|
334
351
|
dataframe[evalName] = None
|
335
352
|
for item in records:
|
336
353
|
for compound_key, value in item.items():
|
337
|
-
|
354
|
+
# for compound_key, value in item['data'].items():
|
338
355
|
|
339
|
-
rowID = compound_key.split(
|
356
|
+
rowID = compound_key.split("-")[0]
|
340
357
|
# looking for the index of each rowID , in the original dataframe
|
341
358
|
if rowID in rowIdMapping:
|
342
359
|
index = rowIdMapping[rowID]
|
343
360
|
# dataframe.at[index, evalName] = value
|
344
361
|
dataframe.at[index, evalName] = value["value"]
|
345
|
-
dataframe.at[index, f
|
346
|
-
|
362
|
+
dataframe.at[index, f"{evalName} Reason"] = value["reasoning"]
|
347
363
|
|
348
364
|
else:
|
349
365
|
pass
|
350
366
|
# print(f"⚠️ Warning: Could not find rowID {rowID} in mapping")
|
367
|
+
if createExperiment:
|
368
|
+
pd.set_option("future.no_silent_downcasting", True)
|
369
|
+
df = dataframe.fillna("Some error occured").astype(object)
|
351
370
|
|
352
|
-
|
371
|
+
if createPlayground(email, workspaceID, df):
|
372
|
+
print(
|
373
|
+
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results."
|
374
|
+
)
|
375
|
+
else:
|
376
|
+
return dataframe
|
353
377
|
|
354
378
|
def evaluateCompressor(self, dataframe, prompt_template):
|
355
379
|
results = []
|
356
|
-
|
380
|
+
|
357
381
|
try:
|
358
382
|
# Connect to socket first
|
359
383
|
# print("Connecting to socket server...")
|
@@ -366,7 +390,9 @@ class LlumoClient:
|
|
366
390
|
time.sleep(0.1)
|
367
391
|
waited_secs += 0.1
|
368
392
|
if waited_secs >= max_wait_secs:
|
369
|
-
raise RuntimeError(
|
393
|
+
raise RuntimeError(
|
394
|
+
"Timeout waiting for server 'connection-established' event."
|
395
|
+
)
|
370
396
|
|
371
397
|
# print(f"Connected with socket ID: {socketID}")
|
372
398
|
|
@@ -382,10 +408,16 @@ class LlumoClient:
|
|
382
408
|
raise
|
383
409
|
|
384
410
|
# check for available hits and trial limit
|
385
|
-
userHits = checkUserHits(
|
386
|
-
|
411
|
+
userHits = checkUserHits(
|
412
|
+
self.workspaceID,
|
413
|
+
self.hasSubscribed,
|
414
|
+
self.trialEndDate,
|
415
|
+
self.subscriptionEndDate,
|
416
|
+
self.hitsAvailable,
|
417
|
+
len(dataframe),
|
418
|
+
)
|
387
419
|
|
388
|
-
|
420
|
+
# do not proceed if subscription or trial limit has exhausted
|
389
421
|
if not userHits["success"]:
|
390
422
|
raise LlumoAIError.InsufficientCredits(userHits["message"])
|
391
423
|
|
@@ -396,7 +428,7 @@ class LlumoClient:
|
|
396
428
|
provider = "OPENAI"
|
397
429
|
evalType = "LLUMO"
|
398
430
|
workspaceID = self.workspaceID
|
399
|
-
|
431
|
+
email = self.email
|
400
432
|
# Prepare all batches before sending
|
401
433
|
# print("Preparing batches...")
|
402
434
|
self.allBatches = []
|
@@ -412,14 +444,22 @@ class LlumoClient:
|
|
412
444
|
if not all([ky in dataframe.columns for ky in keys]):
|
413
445
|
raise LlumoAIError.InvalidPromptTemplate()
|
414
446
|
|
415
|
-
activePlayground = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace(
|
447
|
+
activePlayground = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace(
|
448
|
+
"-", ""
|
449
|
+
)
|
416
450
|
rowID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
417
451
|
columnID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
418
452
|
|
419
|
-
compressed_prompt_id =
|
420
|
-
|
453
|
+
compressed_prompt_id = (
|
454
|
+
f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
455
|
+
)
|
456
|
+
compressed_prompt_output_id = (
|
457
|
+
f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
458
|
+
)
|
421
459
|
cost_id = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
422
|
-
cost_saving_id = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace(
|
460
|
+
cost_saving_id = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace(
|
461
|
+
"-", ""
|
462
|
+
)
|
423
463
|
|
424
464
|
# Use the server-provided socket ID here
|
425
465
|
templateData = {
|
@@ -435,7 +475,7 @@ class LlumoClient:
|
|
435
475
|
"compressed_prompt": compressed_prompt_id,
|
436
476
|
"compressed_prompt_output": compressed_prompt_output_id,
|
437
477
|
"cost": cost_id,
|
438
|
-
"cost_saving": cost_saving_id
|
478
|
+
"cost_saving": cost_saving_id,
|
439
479
|
},
|
440
480
|
"processData": {
|
441
481
|
"rowData": {
|
@@ -451,15 +491,14 @@ class LlumoClient:
|
|
451
491
|
"compressed_prompt": compressed_prompt_id,
|
452
492
|
"compressed_prompt_output": compressed_prompt_output_id,
|
453
493
|
"cost": cost_id,
|
454
|
-
"cost_saving": cost_saving_id
|
455
|
-
}
|
494
|
+
"cost_saving": cost_saving_id,
|
495
|
+
},
|
456
496
|
},
|
457
497
|
"workspaceID": workspaceID,
|
458
|
-
"email":
|
459
|
-
"playgroundID": activePlayground
|
498
|
+
"email": email,
|
499
|
+
"playgroundID": activePlayground,
|
460
500
|
}
|
461
501
|
|
462
|
-
|
463
502
|
# Build query/context from input
|
464
503
|
query = ""
|
465
504
|
context = ""
|
@@ -471,7 +510,9 @@ class LlumoClient:
|
|
471
510
|
context += f" {key}: {value}, "
|
472
511
|
else:
|
473
512
|
if promptTemplate:
|
474
|
-
populatedPrompt = getInputPopulatedPrompt(
|
513
|
+
populatedPrompt = getInputPopulatedPrompt(
|
514
|
+
promptTemplate, {key: value}
|
515
|
+
)
|
475
516
|
query += f"{populatedPrompt} "
|
476
517
|
else:
|
477
518
|
query += f" {key}: {value}, "
|
@@ -480,18 +521,22 @@ class LlumoClient:
|
|
480
521
|
for key, value in inputDict.items():
|
481
522
|
context += f" {key}: {value}, "
|
482
523
|
|
483
|
-
templateData["processData"]["rowData"]["context"][
|
524
|
+
templateData["processData"]["rowData"]["context"][
|
525
|
+
"value"
|
526
|
+
] = context.strip()
|
484
527
|
templateData["processData"]["rowData"]["query"]["value"] = query.strip()
|
485
528
|
|
486
529
|
if promptTemplate and not query.strip():
|
487
|
-
templateData["processData"]["rowData"]["query"][
|
530
|
+
templateData["processData"]["rowData"]["query"][
|
531
|
+
"value"
|
532
|
+
] = promptTemplate
|
488
533
|
|
489
534
|
# print(templateData)
|
490
535
|
currentBatch.append(templateData)
|
491
536
|
|
492
537
|
if len(currentBatch) == 10 or index == len(dataframe) - 1:
|
493
|
-
|
494
|
-
|
538
|
+
self.allBatches.append(currentBatch)
|
539
|
+
currentBatch = []
|
495
540
|
|
496
541
|
# Post all batches
|
497
542
|
total_items = sum(len(batch) for batch in self.allBatches)
|
@@ -516,7 +561,12 @@ class LlumoClient:
|
|
516
561
|
# print(f"All batches posted. Waiting up to {timeout} seconds for results...")
|
517
562
|
|
518
563
|
# Listen for results
|
519
|
-
self.socket.listenForResults(
|
564
|
+
self.socket.listenForResults(
|
565
|
+
min_wait=20,
|
566
|
+
max_wait=timeout,
|
567
|
+
inactivity_timeout=30,
|
568
|
+
expected_results=None,
|
569
|
+
)
|
520
570
|
|
521
571
|
# Get results for this evaluation
|
522
572
|
eval_results = self.socket.getReceivedData()
|
@@ -539,17 +589,33 @@ class LlumoClient:
|
|
539
589
|
except Exception as e:
|
540
590
|
print(f"Error disconnecting socket: {e}")
|
541
591
|
|
542
|
-
compressed_prompt
|
592
|
+
compressed_prompt, compressed_prompt_output, cost, cost_saving = (
|
593
|
+
costColumnMapping(results, self.processMapping)
|
594
|
+
)
|
543
595
|
dataframe["compressed_prompt"] = compressed_prompt
|
544
596
|
dataframe["compressed_prompt_output"] = compressed_prompt_output
|
545
597
|
dataframe["cost"] = cost
|
546
598
|
dataframe["cost_saving"] = cost_saving
|
547
|
-
return dataframe
|
548
599
|
|
600
|
+
return dataframe
|
549
601
|
|
550
|
-
def run_sweep(
|
602
|
+
def run_sweep(
|
603
|
+
self,
|
604
|
+
templates: List[str],
|
605
|
+
dataset: Dict[str, List[str]],
|
606
|
+
model_aliases: List[AVAILABLEMODELS],
|
607
|
+
apiKey: str,
|
608
|
+
eval=["Response Correctness"],
|
609
|
+
toEvaluate: bool = False,
|
610
|
+
createExperiment: bool = False,
|
611
|
+
) -> pd.DataFrame:
|
612
|
+
try:
|
613
|
+
self.validateApiKey()
|
614
|
+
except Exception as e:
|
615
|
+
raise "Some error ocuured please check your API key"
|
616
|
+
workspaceID = self.workspaceID
|
617
|
+
email = self.email
|
551
618
|
executor = ModelExecutor(apiKey)
|
552
|
-
|
553
619
|
keys = list(dataset.keys())
|
554
620
|
value_combinations = list(itertools.product(*dataset.values()))
|
555
621
|
combinations = [dict(zip(keys, values)) for values in value_combinations]
|
@@ -568,65 +634,123 @@ class LlumoClient:
|
|
568
634
|
"template": template,
|
569
635
|
"prompt": prompt,
|
570
636
|
**combo,
|
571
|
-
"model": model.value
|
637
|
+
"model": model.value,
|
572
638
|
}
|
573
639
|
|
574
|
-
|
575
640
|
try:
|
576
641
|
provider = getProviderFromModel(model)
|
577
|
-
response = executor.execute(
|
642
|
+
response = executor.execute(
|
643
|
+
provider, model.value, prompt, apiKey
|
644
|
+
)
|
578
645
|
row["output"] = response
|
579
646
|
except Exception as e:
|
580
647
|
row["output"] = f"Error: {str(e)}"
|
581
|
-
|
648
|
+
|
582
649
|
results.append(row)
|
583
|
-
df=pd.DataFrame(results)
|
650
|
+
df = pd.DataFrame(results)
|
584
651
|
if toEvaluate:
|
585
|
-
|
586
|
-
res = self.evaluate(df,eval
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
652
|
+
|
653
|
+
res = self.evaluate(df, eval=eval, prompt_template=str(templates[0]))
|
654
|
+
|
655
|
+
if createExperiment:
|
656
|
+
pd.set_option("future.no_silent_downcasting", True)
|
657
|
+
res = res.fillna("Some error occured")
|
658
|
+
if createPlayground(email, workspaceID, res):
|
659
|
+
print(
|
660
|
+
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results."
|
661
|
+
)
|
662
|
+
else:
|
663
|
+
return res
|
664
|
+
|
665
|
+
else:
|
666
|
+
if createExperiment:
|
667
|
+
pd.set_option("future.no_silent_downcasting", True)
|
668
|
+
df = df.fillna("Some error occured")
|
669
|
+
if createPlayground(email, workspaceID, df):
|
670
|
+
print(
|
671
|
+
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results."
|
672
|
+
)
|
673
|
+
else:
|
674
|
+
return df
|
675
|
+
|
676
|
+
def evaluateAgents(
|
677
|
+
self,
|
678
|
+
dataframe,
|
679
|
+
model,
|
680
|
+
agents,
|
681
|
+
model_api_key=None,
|
682
|
+
prompt_template="Give answer for the given query: {{query}}",
|
683
|
+
createExperiment: bool = False,
|
684
|
+
):
|
593
685
|
if model.lower() not in ["openai", "google"]:
|
594
686
|
raise ValueError("Model must be 'openai' or 'google'")
|
595
687
|
|
596
688
|
# Run unified agent execution
|
597
|
-
toolResponseDf = LlumoAgentExecutor.run(
|
598
|
-
|
689
|
+
toolResponseDf = LlumoAgentExecutor.run(
|
690
|
+
dataframe, agents, model=model, model_api_key=model_api_key
|
691
|
+
)
|
692
|
+
evals = [
|
693
|
+
"Tool Reliability",
|
694
|
+
"Stepwise Progression",
|
695
|
+
"Tool Selection Accuracy",
|
696
|
+
"Final Task Alignment",
|
697
|
+
]
|
599
698
|
|
600
699
|
for eval in evals:
|
601
700
|
# Perform evaluation
|
602
701
|
toolResponseDf = self.evaluate(
|
603
702
|
toolResponseDf,
|
604
|
-
eval
|
605
|
-
prompt_template=prompt_template
|
703
|
+
eval=eval,
|
704
|
+
prompt_template=prompt_template,
|
705
|
+
createExperiment=False,
|
606
706
|
)
|
607
|
-
|
707
|
+
if createExperiment:
|
708
|
+
pd.set_option("future.no_silent_downcasting", True)
|
709
|
+
df = toolResponseDf.fillna("Some error occured")
|
710
|
+
if createPlayground(self.email, self.workspaceID, df):
|
711
|
+
print(
|
712
|
+
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results."
|
713
|
+
)
|
714
|
+
else:
|
715
|
+
return toolResponseDf
|
608
716
|
|
609
|
-
def evaluateAgentResponses(
|
717
|
+
def evaluateAgentResponses(
|
718
|
+
self,
|
719
|
+
dataframe,
|
720
|
+
prompt_template="Give answer for the given query: {{query}}",
|
721
|
+
createExperiment: bool = False,
|
722
|
+
):
|
610
723
|
try:
|
611
724
|
if "query" and "messageHistory" and "tools" not in dataframe.columns:
|
612
|
-
raise ValueError(
|
613
|
-
|
725
|
+
raise ValueError(
|
726
|
+
"DataFrame must contain 'query', 'messageHistory', and 'tools' columns"
|
727
|
+
)
|
728
|
+
evals = [
|
729
|
+
"Tool Reliability",
|
730
|
+
"Stepwise Progression",
|
731
|
+
"Tool Selection Accuracy",
|
732
|
+
"Final Task Alignment",
|
733
|
+
]
|
614
734
|
toolResponseDf = dataframe.copy()
|
615
735
|
for eval in evals:
|
616
736
|
# Perform evaluation
|
617
737
|
toolResponseDf = self.evaluate(
|
618
|
-
toolResponseDf,
|
619
|
-
eval = eval,
|
620
|
-
prompt_template=prompt_template
|
738
|
+
toolResponseDf, eval=eval, prompt_template=prompt_template
|
621
739
|
)
|
622
740
|
return toolResponseDf
|
623
|
-
|
741
|
+
|
624
742
|
except Exception as e:
|
625
743
|
raise e
|
626
|
-
|
627
|
-
def runDataStream(
|
744
|
+
|
745
|
+
def runDataStream(
|
746
|
+
self,
|
747
|
+
dataframe,
|
748
|
+
streamName: str,
|
749
|
+
queryColName: str = "query",
|
750
|
+
createExperiment: bool = False,
|
751
|
+
):
|
628
752
|
results = {}
|
629
|
-
|
753
|
+
|
630
754
|
try:
|
631
755
|
socketID = self.socket.connect(timeout=150)
|
632
756
|
# Ensure full connection before proceeding
|
@@ -636,7 +760,9 @@ class LlumoClient:
|
|
636
760
|
time.sleep(0.1)
|
637
761
|
waited_secs += 0.1
|
638
762
|
if waited_secs >= max_wait_secs:
|
639
|
-
raise RuntimeError(
|
763
|
+
raise RuntimeError(
|
764
|
+
"Timeout waiting for server 'connection-established' event."
|
765
|
+
)
|
640
766
|
# print(f"Connected with socket ID: {socketID}")
|
641
767
|
rowIdMapping = {}
|
642
768
|
try:
|
@@ -650,25 +776,32 @@ class LlumoClient:
|
|
650
776
|
print(f"Response content: {e.response.text[:500]}...")
|
651
777
|
raise
|
652
778
|
# check for available hits and trial limit
|
653
|
-
userHits = checkUserHits(
|
654
|
-
|
779
|
+
userHits = checkUserHits(
|
780
|
+
self.workspaceID,
|
781
|
+
self.hasSubscribed,
|
782
|
+
self.trialEndDate,
|
783
|
+
self.subscriptionEndDate,
|
784
|
+
self.hitsAvailable,
|
785
|
+
len(dataframe),
|
786
|
+
)
|
655
787
|
|
656
|
-
|
788
|
+
# do not proceed if subscription or trial limit has exhausted
|
657
789
|
if not userHits["success"]:
|
658
790
|
raise LlumoAIError.InsufficientCredits(userHits["message"])
|
659
791
|
|
660
|
-
|
661
|
-
|
662
792
|
print("====🚀Sit back while we fetch data from the stream 🚀====")
|
663
793
|
workspaceID = self.workspaceID
|
664
|
-
|
794
|
+
email = self.email
|
795
|
+
streamId = getStreamId(workspaceID, self.apiKey, streamName)
|
665
796
|
# Prepare all batches before sending
|
666
797
|
# print("Preparing batches...")
|
667
798
|
self.allBatches = []
|
668
799
|
currentBatch = []
|
669
800
|
|
670
801
|
for index, row in dataframe.iterrows():
|
671
|
-
activePlayground = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace(
|
802
|
+
activePlayground = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace(
|
803
|
+
"-", ""
|
804
|
+
)
|
672
805
|
rowID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
673
806
|
columnID = f"{int(time.time() * 1000)}{uuid.uuid4()}".replace("-", "")
|
674
807
|
|
@@ -678,28 +811,24 @@ class LlumoClient:
|
|
678
811
|
"processID": getProcessID(),
|
679
812
|
"socketID": socketID,
|
680
813
|
"processData": {
|
681
|
-
"executionDependency": {
|
682
|
-
|
683
|
-
},
|
684
|
-
"dataStreamID": streamId
|
814
|
+
"executionDependency": {"query": row[queryColName]},
|
815
|
+
"dataStreamID": streamId,
|
685
816
|
},
|
686
817
|
"workspaceID": workspaceID,
|
687
|
-
"email":
|
818
|
+
"email": email,
|
688
819
|
"type": "DATA_STREAM",
|
689
820
|
"playgroundID": activePlayground,
|
690
821
|
"processType": "DATA_STREAM",
|
691
822
|
"rowID": rowID,
|
692
823
|
"columnID": columnID,
|
693
|
-
"source": "SDK"
|
694
|
-
|
824
|
+
"source": "SDK",
|
695
825
|
}
|
696
826
|
|
697
|
-
|
698
827
|
currentBatch.append(templateData)
|
699
828
|
|
700
829
|
if len(currentBatch) == 10 or index == len(dataframe) - 1:
|
701
|
-
|
702
|
-
|
830
|
+
self.allBatches.append(currentBatch)
|
831
|
+
currentBatch = []
|
703
832
|
|
704
833
|
# Post all batches
|
705
834
|
total_items = sum(len(batch) for batch in self.allBatches)
|
@@ -724,7 +853,12 @@ class LlumoClient:
|
|
724
853
|
# print(f"All batches posted. Waiting up to {timeout} seconds for results...")
|
725
854
|
|
726
855
|
# Listen for results
|
727
|
-
self.socket.listenForResults(
|
856
|
+
self.socket.listenForResults(
|
857
|
+
min_wait=20,
|
858
|
+
max_wait=timeout,
|
859
|
+
inactivity_timeout=30,
|
860
|
+
expected_results=None,
|
861
|
+
)
|
728
862
|
|
729
863
|
# Get results for this evaluation
|
730
864
|
eval_results = self.socket.getReceivedData()
|
@@ -734,7 +868,6 @@ class LlumoClient:
|
|
734
868
|
results["Data Stream"] = self.finalResp(eval_results)
|
735
869
|
print(f"=======You are all set! continue your expectations 🚀======\n")
|
736
870
|
|
737
|
-
|
738
871
|
# print("All evaluations completed successfully")
|
739
872
|
|
740
873
|
except Exception as e:
|
@@ -752,31 +885,44 @@ class LlumoClient:
|
|
752
885
|
dataframe[streamName] = None
|
753
886
|
for item in records:
|
754
887
|
for compound_key, value in item.items():
|
755
|
-
|
888
|
+
# for compound_key, value in item['data'].items():
|
756
889
|
|
757
|
-
rowID = compound_key.split(
|
890
|
+
rowID = compound_key.split("-")[0]
|
758
891
|
# looking for the index of each rowID , in the original dataframe
|
759
892
|
if rowID in rowIdMapping:
|
760
893
|
index = rowIdMapping[rowID]
|
761
894
|
# dataframe.at[index, evalName] = value
|
762
895
|
dataframe.at[index, streamName] = value["value"]
|
763
|
-
|
764
|
-
|
765
896
|
|
766
897
|
else:
|
767
898
|
pass
|
768
899
|
# print(f"⚠️ Warning: Could not find rowID {rowID} in mapping")
|
769
900
|
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
901
|
+
if createExperiment:
|
902
|
+
pd.set_option("future.no_silent_downcasting", True)
|
903
|
+
df = dataframe.fillna("Some error occured").astype(object)
|
904
|
+
|
905
|
+
if createPlayground(email, workspaceID, df):
|
906
|
+
print(
|
907
|
+
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results."
|
908
|
+
)
|
909
|
+
else:
|
910
|
+
self.latestDataframe = dataframe
|
911
|
+
return dataframe
|
912
|
+
|
913
|
+
def createExperiment(self, dataframe):
|
914
|
+
try:
|
915
|
+
self.validateApiKey()
|
916
|
+
|
917
|
+
flag = createPlayground(self.email, self.workspaceID, dataframe)
|
918
|
+
if flag:
|
919
|
+
print(
|
920
|
+
"Your data has been saved in the Llumo Experiment. Visit https://app.llumo.ai/evallm to see the results."
|
921
|
+
)
|
922
|
+
except Exception as e:
|
923
|
+
raise "Some error ocuured please check your API key"
|
924
|
+
|
925
|
+
|
780
926
|
class SafeDict(dict):
|
781
927
|
def __missing__(self, key):
|
782
928
|
return ""
|
llumo/helpingFuntions.py
CHANGED
@@ -1,18 +1,27 @@
|
|
1
1
|
import time
|
2
2
|
import uuid
|
3
3
|
import numpy as np
|
4
|
-
from
|
4
|
+
from datetime import datetime
|
5
5
|
from dateutil import parser
|
6
6
|
import requests
|
7
7
|
import json
|
8
8
|
import base64
|
9
9
|
import os
|
10
10
|
from dotenv import load_dotenv
|
11
|
-
load_dotenv()
|
12
11
|
|
13
|
-
subscriptionUrl = os.getenv("SUBSCRIPTION_URL")
|
14
|
-
getStreamdataUrl = os.getenv("DATA_STREAM_URL")
|
15
12
|
|
13
|
+
load_dotenv()
|
14
|
+
|
15
|
+
subscriptionUrl = "https://app.llumo.ai/api/workspace/record-extra-usage"
|
16
|
+
getStreamdataUrl = "https://app.llumo.ai/api/data-stream/all"
|
17
|
+
createPlayUrl = "https://app.llumo.ai/api/New-Eval-API/create-new-eval-playground"
|
18
|
+
deletePlayUrl = "https://app.llumo.ai/api/New-Eval-API/new-upload-flow/delete-columnlist-in-playground"
|
19
|
+
uploadColList = (
|
20
|
+
"https://app.llumo.ai/api/New-Eval-API/new-upload-flow/uploadColumnListInPlayground"
|
21
|
+
)
|
22
|
+
uploadRowList = (
|
23
|
+
"https://app.llumo.ai/api/New-Eval-API/new-upload-flow/uploadRowsInDBPlayground"
|
24
|
+
)
|
16
25
|
|
17
26
|
|
18
27
|
def getProcessID():
|
@@ -24,6 +33,7 @@ def getInputPopulatedPrompt(promptTemplate, tempObj):
|
|
24
33
|
promptTemplate = promptTemplate.replace(f"{{{{{key}}}}}", value)
|
25
34
|
return promptTemplate
|
26
35
|
|
36
|
+
|
27
37
|
def costColumnMapping(costResults, allProcess):
|
28
38
|
# this dict will store cost column data for each row
|
29
39
|
cost_cols = {}
|
@@ -63,7 +73,14 @@ def costColumnMapping(costResults, allProcess):
|
|
63
73
|
return compressed_prompt, compressed_prompt_output, cost, cost_saving
|
64
74
|
|
65
75
|
|
66
|
-
def checkUserHits(
|
76
|
+
def checkUserHits(
|
77
|
+
workspaceID,
|
78
|
+
hasSubscribed,
|
79
|
+
trialEndDate,
|
80
|
+
subscriptionEndDate,
|
81
|
+
remainingHits,
|
82
|
+
datasetLength,
|
83
|
+
):
|
67
84
|
# Get the current date (only the date part)
|
68
85
|
current_date = datetime.now().date()
|
69
86
|
|
@@ -99,7 +116,9 @@ def checkUserHits(workspaceID, hasSubscribed, trialEndDate, subscriptionEndDate,
|
|
99
116
|
"Content-Type": "application/json",
|
100
117
|
}
|
101
118
|
reqBody = {"unitsToSet": 1}
|
102
|
-
responseBody = requests.post(
|
119
|
+
responseBody = requests.post(
|
120
|
+
url=subscriptionUrl, json=reqBody, headers=headers
|
121
|
+
)
|
103
122
|
response = json.loads(responseBody.text)
|
104
123
|
|
105
124
|
proceed = response.get("execution", "")
|
@@ -110,6 +129,7 @@ def checkUserHits(workspaceID, hasSubscribed, trialEndDate, subscriptionEndDate,
|
|
110
129
|
|
111
130
|
return {"success": True, "message": "Access granted."}
|
112
131
|
|
132
|
+
|
113
133
|
def getStreamId(workspaceID: str, token, dataStreamName):
|
114
134
|
headers = {
|
115
135
|
"Authorization": f"Bearer {token}",
|
@@ -123,15 +143,164 @@ def getStreamId(workspaceID: str, token, dataStreamName):
|
|
123
143
|
data = responseJson.get("data", [])
|
124
144
|
|
125
145
|
# Find stream by name
|
126
|
-
matchedStream = next(
|
146
|
+
matchedStream = next(
|
147
|
+
(stream for stream in data if stream.get("name") == dataStreamName), None
|
148
|
+
)
|
127
149
|
|
128
150
|
if matchedStream:
|
129
|
-
|
151
|
+
|
130
152
|
return matchedStream.get("dataStreamID")
|
131
|
-
|
153
|
+
|
132
154
|
else:
|
133
155
|
print(f"No stream found with name: {dataStreamName}")
|
134
156
|
return None
|
135
157
|
else:
|
136
158
|
print("Error:", response.status_code, response.text)
|
137
|
-
return None
|
159
|
+
return None
|
160
|
+
|
161
|
+
|
162
|
+
def createEvalPlayground(email: str, workspaceID: str):
|
163
|
+
url = createPlayUrl
|
164
|
+
headers = {
|
165
|
+
"Content-Type": "application/json",
|
166
|
+
}
|
167
|
+
payload = {
|
168
|
+
"email": email,
|
169
|
+
"workspaceID": workspaceID,
|
170
|
+
}
|
171
|
+
|
172
|
+
response = requests.post(url, json=payload, headers=headers)
|
173
|
+
|
174
|
+
if response.status_code == 200:
|
175
|
+
try:
|
176
|
+
responseJson = response.json()
|
177
|
+
# print(responseJson)
|
178
|
+
return responseJson.get("data", {}).get("playgroundID", None)
|
179
|
+
|
180
|
+
except Exception as e:
|
181
|
+
print("Failed to parse JSON:", e)
|
182
|
+
return None
|
183
|
+
else:
|
184
|
+
print("Error:", response.status_code, response.text)
|
185
|
+
return None
|
186
|
+
|
187
|
+
|
188
|
+
def deleteColumnListInPlayground(workspaceID: str, playgroundID: str):
|
189
|
+
url = deletePlayUrl
|
190
|
+
headers = {
|
191
|
+
"Content-Type": "application/json",
|
192
|
+
}
|
193
|
+
payload = {
|
194
|
+
"workspaceID": workspaceID,
|
195
|
+
"playgroundID": playgroundID,
|
196
|
+
}
|
197
|
+
|
198
|
+
response = requests.post(url, json=payload, headers=headers)
|
199
|
+
|
200
|
+
if response.status_code == 200:
|
201
|
+
try:
|
202
|
+
|
203
|
+
return response.json()
|
204
|
+
except Exception as e:
|
205
|
+
print("⚠️ Failed to parse JSON:", e)
|
206
|
+
return None
|
207
|
+
else:
|
208
|
+
print("❌ Error:", response.status_code, response.text)
|
209
|
+
return None
|
210
|
+
|
211
|
+
|
212
|
+
def createColumn(workspaceID, dataframe, playgroundID):
|
213
|
+
|
214
|
+
if len(dataframe) > 100:
|
215
|
+
dataframe = dataframe.head(100)
|
216
|
+
print("⚠️ Dataframe truncated to 100 rows for upload.")
|
217
|
+
|
218
|
+
playgroundID = playgroundID
|
219
|
+
|
220
|
+
coltemplate = {
|
221
|
+
"workspaceID": workspaceID,
|
222
|
+
"playgroundID": playgroundID,
|
223
|
+
"columnListToUpload": [],
|
224
|
+
}
|
225
|
+
|
226
|
+
for indx, col in enumerate(dataframe.columns):
|
227
|
+
template = {
|
228
|
+
"label": col,
|
229
|
+
"type": "VARIABLE",
|
230
|
+
"variableType": "STRING",
|
231
|
+
"order": indx,
|
232
|
+
"columnID": col,
|
233
|
+
}
|
234
|
+
coltemplate["columnListToUpload"].append(template)
|
235
|
+
|
236
|
+
rowTemplate = {
|
237
|
+
"workspaceID": workspaceID,
|
238
|
+
"playgroundID": playgroundID,
|
239
|
+
"dataToUploadList": [],
|
240
|
+
"columnList": coltemplate["columnListToUpload"],
|
241
|
+
}
|
242
|
+
|
243
|
+
for indx, row in dataframe.iterrows():
|
244
|
+
row_dict = row.to_dict()
|
245
|
+
row_dict["pIndex"] = indx
|
246
|
+
rowTemplate["dataToUploadList"].append(row_dict)
|
247
|
+
|
248
|
+
return coltemplate, rowTemplate
|
249
|
+
|
250
|
+
|
251
|
+
def uploadColumnListInPlayground(payload):
|
252
|
+
url = uploadColList
|
253
|
+
headers = {
|
254
|
+
"Content-Type": "application/json",
|
255
|
+
}
|
256
|
+
payload = payload
|
257
|
+
|
258
|
+
response = requests.post(url, json=payload, headers=headers)
|
259
|
+
|
260
|
+
if response.status_code == 200:
|
261
|
+
try:
|
262
|
+
|
263
|
+
return response.json()
|
264
|
+
except Exception as e:
|
265
|
+
print("⚠️ Failed to parse JSON:", e)
|
266
|
+
return None
|
267
|
+
else:
|
268
|
+
print("❌ Error:", response.status_code, response.text)
|
269
|
+
return None
|
270
|
+
|
271
|
+
|
272
|
+
def uploadRowsInDBPlayground(payload):
|
273
|
+
url = uploadRowList
|
274
|
+
headers = {
|
275
|
+
"Content-Type": "application/json",
|
276
|
+
}
|
277
|
+
|
278
|
+
payload = payload
|
279
|
+
|
280
|
+
response = requests.post(url, json=payload, headers=headers)
|
281
|
+
|
282
|
+
if response.status_code == 200:
|
283
|
+
try:
|
284
|
+
|
285
|
+
return response.json()
|
286
|
+
except Exception as e:
|
287
|
+
print("⚠️ Failed to parse JSON:", e)
|
288
|
+
return None
|
289
|
+
else:
|
290
|
+
print("❌ Error:", response.status_code, response.text)
|
291
|
+
return None
|
292
|
+
|
293
|
+
|
294
|
+
def createPlayground(email, workspaceID, df):
|
295
|
+
playgroundId = str(createEvalPlayground(email=email, workspaceID=workspaceID))
|
296
|
+
payload1, payload2 = createColumn(
|
297
|
+
workspaceID=workspaceID, dataframe=df, playgroundID=playgroundId
|
298
|
+
)
|
299
|
+
deleteExistingRows = deleteColumnListInPlayground(
|
300
|
+
workspaceID=workspaceID, playgroundID=playgroundId
|
301
|
+
)
|
302
|
+
colListUpload = uploadColumnListInPlayground(payload=payload1)
|
303
|
+
rowListUpload = uploadRowsInDBPlayground(payload=payload2)
|
304
|
+
|
305
|
+
if rowListUpload:
|
306
|
+
return True
|
@@ -0,0 +1,13 @@
|
|
1
|
+
llumo/__init__.py,sha256=O04b4yW1BnOvcHzxWFddAKhtdBEhBNhLdb6xgnpHH_Q,205
|
2
|
+
llumo/client.py,sha256=YBTWDVqJTBh0IKNDXzD8vvFgUoi8uuZDbA-Po72cfxI,35849
|
3
|
+
llumo/exceptions.py,sha256=iCj7HhtO_ckC2EaVBdXbAudNpuMDsYmmMEV5lwynZ-E,1854
|
4
|
+
llumo/execution.py,sha256=x88wQV8eL99wNN5YtjFaAMCIfN1PdfQVlAZQb4vzgQ0,1413
|
5
|
+
llumo/functionCalling.py,sha256=QtuTtyoz5rnfNUrNT1kzegNPOrMFjrlgxZfwTqRMdiA,7190
|
6
|
+
llumo/helpingFuntions.py,sha256=mDGOjeqJI1NOdoaT5FK3tlhWwhpZnk1nCnZOan0AFk0,8886
|
7
|
+
llumo/models.py,sha256=YH-qAMnShmUpmKE2LQAzQdpRsaXkFSlOqMxHwU4zBUI,1560
|
8
|
+
llumo/sockets.py,sha256=Qxxqtx3Hg07HLhA4QfcipK1ChiOYhHZBu02iA6MfYlQ,5579
|
9
|
+
llumo-0.2.1.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
|
10
|
+
llumo-0.2.1.dist-info/METADATA,sha256=E2hanseFfMf8jIbj54qVdZUgKu2JTOu_DV3nweGG_zs,426
|
11
|
+
llumo-0.2.1.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
12
|
+
llumo-0.2.1.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
|
13
|
+
llumo-0.2.1.dist-info/RECORD,,
|
llumo-0.1.9b10.dist-info/RECORD
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
llumo/__init__.py,sha256=O04b4yW1BnOvcHzxWFddAKhtdBEhBNhLdb6xgnpHH_Q,205
|
2
|
-
llumo/client.py,sha256=F4zabvAjLnu0N61qSw5DqerNlV5ybC2DbxaI55olldg,31916
|
3
|
-
llumo/exceptions.py,sha256=iCj7HhtO_ckC2EaVBdXbAudNpuMDsYmmMEV5lwynZ-E,1854
|
4
|
-
llumo/execution.py,sha256=x88wQV8eL99wNN5YtjFaAMCIfN1PdfQVlAZQb4vzgQ0,1413
|
5
|
-
llumo/functionCalling.py,sha256=QtuTtyoz5rnfNUrNT1kzegNPOrMFjrlgxZfwTqRMdiA,7190
|
6
|
-
llumo/helpingFuntions.py,sha256=9w1J4wMnJV1v_5yFMyxIHcvc16sEq5MZzOFBPagij5Q,4467
|
7
|
-
llumo/models.py,sha256=YH-qAMnShmUpmKE2LQAzQdpRsaXkFSlOqMxHwU4zBUI,1560
|
8
|
-
llumo/sockets.py,sha256=Qxxqtx3Hg07HLhA4QfcipK1ChiOYhHZBu02iA6MfYlQ,5579
|
9
|
-
llumo-0.1.9b10.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
|
10
|
-
llumo-0.1.9b10.dist-info/METADATA,sha256=QkiIW2Z2Lk86q5v9Reg4Pq0bNPTpWXR6NWGT0aW-Esk,429
|
11
|
-
llumo-0.1.9b10.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
12
|
-
llumo-0.1.9b10.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
|
13
|
-
llumo-0.1.9b10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|