llumo 0.1.9__py3-none-any.whl → 0.2.0__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/__init__.py +7 -7
- llumo/client.py +782 -561
- llumo/exceptions.py +51 -45
- llumo/execution.py +38 -38
- llumo/functionCalling.py +190 -190
- llumo/helpingFuntions.py +137 -50
- llumo/models.py +42 -42
- llumo/sockets.py +148 -148
- llumo-0.2.0.dist-info/METADATA +17 -0
- llumo-0.2.0.dist-info/RECORD +13 -0
- {llumo-0.1.9.dist-info → llumo-0.2.0.dist-info}/WHEEL +1 -1
- {llumo-0.1.9.dist-info → llumo-0.2.0.dist-info}/licenses/LICENSE +4 -4
- llumo/.env +0 -6
- llumo-0.1.9.dist-info/METADATA +0 -26
- llumo-0.1.9.dist-info/RECORD +0 -14
- {llumo-0.1.9.dist-info → llumo-0.2.0.dist-info}/top_level.txt +0 -0
llumo/helpingFuntions.py
CHANGED
@@ -1,50 +1,137 @@
|
|
1
|
-
import time
|
2
|
-
import uuid
|
3
|
-
import numpy as np
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
for
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
1
|
+
import time
|
2
|
+
import uuid
|
3
|
+
import numpy as np
|
4
|
+
from datetime import datetime
|
5
|
+
from dateutil import parser
|
6
|
+
import requests
|
7
|
+
import json
|
8
|
+
import base64
|
9
|
+
import os
|
10
|
+
from dotenv import load_dotenv
|
11
|
+
load_dotenv()
|
12
|
+
|
13
|
+
subscriptionUrl = os.getenv("SUBSCRIPTION_URL")
|
14
|
+
getStreamdataUrl = os.getenv("DATA_STREAM_URL")
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
def getProcessID():
|
19
|
+
return f"{int(time.time() * 1000)}{uuid.uuid4()}"
|
20
|
+
|
21
|
+
|
22
|
+
def getInputPopulatedPrompt(promptTemplate, tempObj):
|
23
|
+
for key, value in tempObj.items():
|
24
|
+
promptTemplate = promptTemplate.replace(f"{{{{{key}}}}}", value)
|
25
|
+
return promptTemplate
|
26
|
+
|
27
|
+
def costColumnMapping(costResults, allProcess):
|
28
|
+
# this dict will store cost column data for each row
|
29
|
+
cost_cols = {}
|
30
|
+
|
31
|
+
compressed_prompt = []
|
32
|
+
compressed_prompt_output = []
|
33
|
+
cost = []
|
34
|
+
cost_saving = []
|
35
|
+
|
36
|
+
for record in allProcess:
|
37
|
+
cost_cols[record] = []
|
38
|
+
for item in costResults:
|
39
|
+
if list(item.keys())[0].split("-")[0] == record.split("-")[0]:
|
40
|
+
cost_cols[record].append(list(item.values())[0])
|
41
|
+
|
42
|
+
for ky, val in cost_cols.items():
|
43
|
+
try:
|
44
|
+
compressed_prompt.append(val[0])
|
45
|
+
except IndexError:
|
46
|
+
compressed_prompt.append("error occured")
|
47
|
+
|
48
|
+
try:
|
49
|
+
compressed_prompt_output.append(val[1])
|
50
|
+
except IndexError:
|
51
|
+
compressed_prompt_output.append("error occured")
|
52
|
+
|
53
|
+
try:
|
54
|
+
cost.append(val[2])
|
55
|
+
except IndexError:
|
56
|
+
cost.append("error occured")
|
57
|
+
|
58
|
+
try:
|
59
|
+
cost_saving.append(val[3])
|
60
|
+
except IndexError:
|
61
|
+
cost_saving.append("error occured")
|
62
|
+
|
63
|
+
return compressed_prompt, compressed_prompt_output, cost, cost_saving
|
64
|
+
|
65
|
+
|
66
|
+
def checkUserHits(workspaceID, hasSubscribed, trialEndDate, subscriptionEndDate, remainingHits, datasetLength):
|
67
|
+
# Get the current date (only the date part)
|
68
|
+
current_date = datetime.now().date()
|
69
|
+
|
70
|
+
# Parse trialEndDate if provided
|
71
|
+
if trialEndDate is not None:
|
72
|
+
try:
|
73
|
+
trialEndDate = parser.parse(trialEndDate).date()
|
74
|
+
except Exception:
|
75
|
+
return {"success": False, "message": "Invalid trialEndDate format"}
|
76
|
+
|
77
|
+
# Parse subscriptionEndDate if provided
|
78
|
+
if subscriptionEndDate is not None:
|
79
|
+
try:
|
80
|
+
subscriptionEndDate = parser.parse(subscriptionEndDate).date()
|
81
|
+
except Exception:
|
82
|
+
return {"success": False, "message": "Invalid subscriptionEndDate format"}
|
83
|
+
|
84
|
+
# If user is on a free trial
|
85
|
+
if not hasSubscribed and trialEndDate is not None:
|
86
|
+
if current_date > trialEndDate:
|
87
|
+
return {"success": False, "message": "Trial expired. Access denied"}
|
88
|
+
|
89
|
+
if remainingHits < datasetLength or remainingHits <= 0:
|
90
|
+
return {"success": False, "message": "Trial Hits Exhausted"}
|
91
|
+
|
92
|
+
else:
|
93
|
+
if subscriptionEndDate and current_date > subscriptionEndDate:
|
94
|
+
return {"success": False, "message": "Subscription expired. Access denied."}
|
95
|
+
|
96
|
+
if remainingHits <= 0 or remainingHits < datasetLength:
|
97
|
+
headers = {
|
98
|
+
"Authorization": f"Bearer {base64.b64encode(workspaceID.encode()).decode()}",
|
99
|
+
"Content-Type": "application/json",
|
100
|
+
}
|
101
|
+
reqBody = {"unitsToSet": 1}
|
102
|
+
responseBody = requests.post(url=subscriptionUrl, json=reqBody, headers=headers)
|
103
|
+
response = json.loads(responseBody.text)
|
104
|
+
|
105
|
+
proceed = response.get("execution", "")
|
106
|
+
print(proceed)
|
107
|
+
|
108
|
+
if proceed:
|
109
|
+
return {"success": True, "message": "Hits added and access granted."}
|
110
|
+
|
111
|
+
return {"success": True, "message": "Access granted."}
|
112
|
+
|
113
|
+
def getStreamId(workspaceID: str, token, dataStreamName):
|
114
|
+
headers = {
|
115
|
+
"Authorization": f"Bearer {token}",
|
116
|
+
"Content-Type": "application/json",
|
117
|
+
}
|
118
|
+
reqBody = {"workspaceID": workspaceID}
|
119
|
+
response = requests.post(url=getStreamdataUrl, json=reqBody, headers=headers)
|
120
|
+
|
121
|
+
if response.status_code == 200:
|
122
|
+
responseJson = response.json()
|
123
|
+
data = responseJson.get("data", [])
|
124
|
+
|
125
|
+
# Find stream by name
|
126
|
+
matchedStream = next((stream for stream in data if stream.get("name") == dataStreamName), None)
|
127
|
+
|
128
|
+
if matchedStream:
|
129
|
+
|
130
|
+
return matchedStream.get("dataStreamID")
|
131
|
+
|
132
|
+
else:
|
133
|
+
print(f"No stream found with name: {dataStreamName}")
|
134
|
+
return None
|
135
|
+
else:
|
136
|
+
print("Error:", response.status_code, response.text)
|
137
|
+
return None
|
llumo/models.py
CHANGED
@@ -1,43 +1,43 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
|
3
|
-
class Provider(str, Enum):
|
4
|
-
OPENAI = "OPENAI"
|
5
|
-
GOOGLE = "GOOGLE"
|
6
|
-
|
7
|
-
# Maps model aliases → (provider, actual model name for API)
|
8
|
-
_MODEL_METADATA = {
|
9
|
-
"GPT_4": (Provider.OPENAI, "gpt-4"),
|
10
|
-
"GPT_4_32K": (Provider.OPENAI, "gpt-4-32k"),
|
11
|
-
"GPT_35T": (Provider.OPENAI, "gpt-3.5-turbo"),
|
12
|
-
"GPT_35T_INS": (Provider.OPENAI, "gpt-3.5-turbo-instruct"),
|
13
|
-
"GPT_35T_16K": (Provider.OPENAI, "gpt-3.5-turbo-16k"),
|
14
|
-
"GPT_35_TURBO": (Provider.OPENAI, "gpt-3.5-turbo"),
|
15
|
-
|
16
|
-
"GOOGLE_15_FLASH": (Provider.GOOGLE, "gemini-1.5-flash-latest"),
|
17
|
-
"GEMINI_PRO": (Provider.GOOGLE, "gemini-pro"),
|
18
|
-
"TEXT_BISON": (Provider.GOOGLE, "text-bison-001"),
|
19
|
-
"CHAT_BISON": (Provider.GOOGLE, "chat-bison-001"),
|
20
|
-
"TEXT_BISON_32K": (Provider.GOOGLE, "text-bison-32k"),
|
21
|
-
"TEXT_UNICORN": (Provider.GOOGLE, "text-unicorn-experimental"),
|
22
|
-
}
|
23
|
-
|
24
|
-
class AVAILABLEMODELS(str, Enum):
|
25
|
-
GPT_4 = "gpt-4"
|
26
|
-
GPT_4_32K = "gpt-4-32k"
|
27
|
-
GPT_35T = "gpt-3.5-turbo"
|
28
|
-
GPT_35T_INS = "gpt-3.5-turbo-instruct"
|
29
|
-
GPT_35T_16K = "gpt-3.5-turbo-16k"
|
30
|
-
GPT_35_TURBO = "gpt-3.5-turbo"
|
31
|
-
|
32
|
-
GOOGLE_15_FLASH = "gemini-1.5-flash-latest"
|
33
|
-
GEMINI_PRO = ""
|
34
|
-
TEXT_BISON = "text-bison-001"
|
35
|
-
CHAT_BISON = "chat-bison-001"
|
36
|
-
TEXT_BISON_32K = "text-bison-32k"
|
37
|
-
TEXT_UNICORN = "text-unicorn-experimental"
|
38
|
-
|
39
|
-
def getProviderFromModel(model: AVAILABLEMODELS) -> Provider:
|
40
|
-
for alias, (provider, apiName) in _MODEL_METADATA.items():
|
41
|
-
if model.value == apiName:
|
42
|
-
return provider
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
class Provider(str, Enum):
|
4
|
+
OPENAI = "OPENAI"
|
5
|
+
GOOGLE = "GOOGLE"
|
6
|
+
|
7
|
+
# Maps model aliases → (provider, actual model name for API)
|
8
|
+
_MODEL_METADATA = {
|
9
|
+
"GPT_4": (Provider.OPENAI, "gpt-4"),
|
10
|
+
"GPT_4_32K": (Provider.OPENAI, "gpt-4-32k"),
|
11
|
+
"GPT_35T": (Provider.OPENAI, "gpt-3.5-turbo"),
|
12
|
+
"GPT_35T_INS": (Provider.OPENAI, "gpt-3.5-turbo-instruct"),
|
13
|
+
"GPT_35T_16K": (Provider.OPENAI, "gpt-3.5-turbo-16k"),
|
14
|
+
"GPT_35_TURBO": (Provider.OPENAI, "gpt-3.5-turbo"),
|
15
|
+
|
16
|
+
"GOOGLE_15_FLASH": (Provider.GOOGLE, "gemini-1.5-flash-latest"),
|
17
|
+
"GEMINI_PRO": (Provider.GOOGLE, "gemini-pro"),
|
18
|
+
"TEXT_BISON": (Provider.GOOGLE, "text-bison-001"),
|
19
|
+
"CHAT_BISON": (Provider.GOOGLE, "chat-bison-001"),
|
20
|
+
"TEXT_BISON_32K": (Provider.GOOGLE, "text-bison-32k"),
|
21
|
+
"TEXT_UNICORN": (Provider.GOOGLE, "text-unicorn-experimental"),
|
22
|
+
}
|
23
|
+
|
24
|
+
class AVAILABLEMODELS(str, Enum):
|
25
|
+
GPT_4 = "gpt-4"
|
26
|
+
GPT_4_32K = "gpt-4-32k"
|
27
|
+
GPT_35T = "gpt-3.5-turbo"
|
28
|
+
GPT_35T_INS = "gpt-3.5-turbo-instruct"
|
29
|
+
GPT_35T_16K = "gpt-3.5-turbo-16k"
|
30
|
+
GPT_35_TURBO = "gpt-3.5-turbo"
|
31
|
+
|
32
|
+
GOOGLE_15_FLASH = "gemini-1.5-flash-latest"
|
33
|
+
GEMINI_PRO = ""
|
34
|
+
TEXT_BISON = "text-bison-001"
|
35
|
+
CHAT_BISON = "chat-bison-001"
|
36
|
+
TEXT_BISON_32K = "text-bison-32k"
|
37
|
+
TEXT_UNICORN = "text-unicorn-experimental"
|
38
|
+
|
39
|
+
def getProviderFromModel(model: AVAILABLEMODELS) -> Provider:
|
40
|
+
for alias, (provider, apiName) in _MODEL_METADATA.items():
|
41
|
+
if model.value == apiName:
|
42
|
+
return provider
|
43
43
|
raise ValueError(f"Provider not found for model: {model}")
|
llumo/sockets.py
CHANGED
@@ -1,148 +1,148 @@
|
|
1
|
-
import socketio
|
2
|
-
import threading
|
3
|
-
import time
|
4
|
-
|
5
|
-
|
6
|
-
class LlumoSocketClient:
|
7
|
-
def __init__(self, socket_url):
|
8
|
-
self.socket_url = socket_url
|
9
|
-
self._received_data = []
|
10
|
-
self._last_update_time = None
|
11
|
-
self._listening_done = threading.Event()
|
12
|
-
self._connection_established = threading.Event()
|
13
|
-
self._lock = threading.Lock()
|
14
|
-
self._connected = False
|
15
|
-
self.server_socket_id = None # Store the server-assigned socket ID
|
16
|
-
self._expected_results = None # NEW
|
17
|
-
|
18
|
-
# Initialize client
|
19
|
-
self.sio = socketio.Client(
|
20
|
-
# logger=True,
|
21
|
-
# engineio_logger=True,
|
22
|
-
reconnection=True,
|
23
|
-
reconnection_attempts=10,
|
24
|
-
reconnection_delay=1,
|
25
|
-
)
|
26
|
-
|
27
|
-
@self.sio.on("connect")
|
28
|
-
def on_connect():
|
29
|
-
# print("Socket connection established")
|
30
|
-
self._connected = True
|
31
|
-
# Don't set connection_established yet - wait for server confirmation
|
32
|
-
|
33
|
-
# Listen for the connection-established event from the server
|
34
|
-
@self.sio.on("connection-established")
|
35
|
-
def on_connection_established(data):
|
36
|
-
# print(
|
37
|
-
# f"Server acknowledged connection with 'connection-established' event: {data}"
|
38
|
-
# )
|
39
|
-
if isinstance(data, dict) and "socketId" in data:
|
40
|
-
self.server_socket_id = data["socketId"]
|
41
|
-
# print(f"Received server socket ID: {self.server_socket_id}")
|
42
|
-
self._connection_established.set()
|
43
|
-
|
44
|
-
@self.sio.on("result-update")
|
45
|
-
def on_result_update(data):
|
46
|
-
with self._lock:
|
47
|
-
# print(f"Received result-update event: {data}")
|
48
|
-
self._received_data.append(data)
|
49
|
-
self._last_update_time = time.time()
|
50
|
-
|
51
|
-
# ✅ Stop if all expected results are received
|
52
|
-
if self._expected_results and len(self._received_data) >= self._expected_results:
|
53
|
-
# print("✅ All expected results received.")
|
54
|
-
self._listening_done.set()
|
55
|
-
|
56
|
-
@self.sio.on("disconnect")
|
57
|
-
def on_disconnect():
|
58
|
-
# print("Socket disconnected")
|
59
|
-
self._connected = False
|
60
|
-
|
61
|
-
@self.sio.on("connect_error")
|
62
|
-
def on_connect_error(error):
|
63
|
-
print(f"Socket connection error: {error}")
|
64
|
-
|
65
|
-
@self.sio.on("error")
|
66
|
-
def on_error(error):
|
67
|
-
print(f"Socket error event: {error}")
|
68
|
-
|
69
|
-
def connect(self, timeout=20):
|
70
|
-
self._received_data = []
|
71
|
-
self._connection_established.clear()
|
72
|
-
self._listening_done.clear()
|
73
|
-
self.server_socket_id = None
|
74
|
-
self._connected = False
|
75
|
-
|
76
|
-
try:
|
77
|
-
# print("[DEBUG] Connecting to socket...")
|
78
|
-
self.sio.connect(self.socket_url, transports=["websocket"], wait=True)
|
79
|
-
|
80
|
-
# Wait for socket connection
|
81
|
-
start = time.time()
|
82
|
-
while not self.sio.connected:
|
83
|
-
if time.time() - start > timeout:
|
84
|
-
raise RuntimeError("Timed out waiting for low-level socket connection.")
|
85
|
-
time.sleep(0.1)
|
86
|
-
# print("[DEBUG] SocketIO low-level connection established.")
|
87
|
-
|
88
|
-
# Wait for server "connection-established" event
|
89
|
-
if not self._connection_established.wait(timeout):
|
90
|
-
raise RuntimeError("Timed out waiting for connection-established event.")
|
91
|
-
|
92
|
-
self._connected = True
|
93
|
-
self._last_update_time = time.time()
|
94
|
-
# print(f"[DEBUG] Full connection established. Connected: {self._connected}")
|
95
|
-
|
96
|
-
return self.server_socket_id or self.sio.sid
|
97
|
-
|
98
|
-
except Exception as e:
|
99
|
-
# print(f"[DEBUG] Connection failed with error: {e}")
|
100
|
-
self._connected = False
|
101
|
-
raise RuntimeError(f"WebSocket connection failed: {e}")
|
102
|
-
|
103
|
-
def listenForResults(self, min_wait=30, max_wait=300, inactivity_timeout=50, expected_results=None):
|
104
|
-
if not self._connected:
|
105
|
-
raise RuntimeError("WebSocket is not connected. Call connect() first.")
|
106
|
-
|
107
|
-
self._expected_results = expected_results # NEW
|
108
|
-
start_time = time.time()
|
109
|
-
self._last_update_time = time.time()
|
110
|
-
|
111
|
-
def timeout_watcher():
|
112
|
-
while not self._listening_done.is_set():
|
113
|
-
current_time = time.time()
|
114
|
-
time_since_last_update = current_time - self._last_update_time
|
115
|
-
total_elapsed = current_time - start_time
|
116
|
-
|
117
|
-
if total_elapsed < min_wait:
|
118
|
-
time.sleep(0.5)
|
119
|
-
continue
|
120
|
-
|
121
|
-
if total_elapsed > max_wait:
|
122
|
-
# print(f"⚠️ Max wait time {max_wait}s exceeded.")
|
123
|
-
self._listening_done.set()
|
124
|
-
break
|
125
|
-
|
126
|
-
if time_since_last_update > inactivity_timeout:
|
127
|
-
# print(f"⚠️ Inactivity timeout {inactivity_timeout}s exceeded.")
|
128
|
-
self._listening_done.set()
|
129
|
-
break
|
130
|
-
|
131
|
-
time.sleep(1)
|
132
|
-
|
133
|
-
timeout_thread = threading.Thread(target=timeout_watcher, daemon=True)
|
134
|
-
timeout_thread.start()
|
135
|
-
self._listening_done.wait()
|
136
|
-
|
137
|
-
def getReceivedData(self):
|
138
|
-
with self._lock:
|
139
|
-
return self._received_data.copy()
|
140
|
-
|
141
|
-
def disconnect(self):
|
142
|
-
try:
|
143
|
-
if self._connected:
|
144
|
-
self.sio.disconnect()
|
145
|
-
self._connected = False
|
146
|
-
# print("WebSocket client disconnected")
|
147
|
-
except Exception as e:
|
148
|
-
print(f"Error during WebSocket disconnect: {e}")
|
1
|
+
import socketio
|
2
|
+
import threading
|
3
|
+
import time
|
4
|
+
|
5
|
+
|
6
|
+
class LlumoSocketClient:
|
7
|
+
def __init__(self, socket_url):
|
8
|
+
self.socket_url = socket_url
|
9
|
+
self._received_data = []
|
10
|
+
self._last_update_time = None
|
11
|
+
self._listening_done = threading.Event()
|
12
|
+
self._connection_established = threading.Event()
|
13
|
+
self._lock = threading.Lock()
|
14
|
+
self._connected = False
|
15
|
+
self.server_socket_id = None # Store the server-assigned socket ID
|
16
|
+
self._expected_results = None # NEW
|
17
|
+
|
18
|
+
# Initialize client
|
19
|
+
self.sio = socketio.Client(
|
20
|
+
# logger=True,
|
21
|
+
# engineio_logger=True,
|
22
|
+
reconnection=True,
|
23
|
+
reconnection_attempts=10,
|
24
|
+
reconnection_delay=1,
|
25
|
+
)
|
26
|
+
|
27
|
+
@self.sio.on("connect")
|
28
|
+
def on_connect():
|
29
|
+
# print("Socket connection established")
|
30
|
+
self._connected = True
|
31
|
+
# Don't set connection_established yet - wait for server confirmation
|
32
|
+
|
33
|
+
# Listen for the connection-established event from the server
|
34
|
+
@self.sio.on("connection-established")
|
35
|
+
def on_connection_established(data):
|
36
|
+
# print(
|
37
|
+
# f"Server acknowledged connection with 'connection-established' event: {data}"
|
38
|
+
# )
|
39
|
+
if isinstance(data, dict) and "socketId" in data:
|
40
|
+
self.server_socket_id = data["socketId"]
|
41
|
+
# print(f"Received server socket ID: {self.server_socket_id}")
|
42
|
+
self._connection_established.set()
|
43
|
+
|
44
|
+
@self.sio.on("result-update")
|
45
|
+
def on_result_update(data):
|
46
|
+
with self._lock:
|
47
|
+
# print(f"Received result-update event: {data}")
|
48
|
+
self._received_data.append(data)
|
49
|
+
self._last_update_time = time.time()
|
50
|
+
|
51
|
+
# ✅ Stop if all expected results are received
|
52
|
+
if self._expected_results and len(self._received_data) >= self._expected_results:
|
53
|
+
# print("✅ All expected results received.")
|
54
|
+
self._listening_done.set()
|
55
|
+
|
56
|
+
@self.sio.on("disconnect")
|
57
|
+
def on_disconnect():
|
58
|
+
# print("Socket disconnected")
|
59
|
+
self._connected = False
|
60
|
+
|
61
|
+
@self.sio.on("connect_error")
|
62
|
+
def on_connect_error(error):
|
63
|
+
print(f"Socket connection error: {error}")
|
64
|
+
|
65
|
+
@self.sio.on("error")
|
66
|
+
def on_error(error):
|
67
|
+
print(f"Socket error event: {error}")
|
68
|
+
|
69
|
+
def connect(self, timeout=20):
|
70
|
+
self._received_data = []
|
71
|
+
self._connection_established.clear()
|
72
|
+
self._listening_done.clear()
|
73
|
+
self.server_socket_id = None
|
74
|
+
self._connected = False
|
75
|
+
|
76
|
+
try:
|
77
|
+
# print("[DEBUG] Connecting to socket...")
|
78
|
+
self.sio.connect(self.socket_url, transports=["websocket"], wait=True)
|
79
|
+
|
80
|
+
# Wait for socket connection
|
81
|
+
start = time.time()
|
82
|
+
while not self.sio.connected:
|
83
|
+
if time.time() - start > timeout:
|
84
|
+
raise RuntimeError("Timed out waiting for low-level socket connection.")
|
85
|
+
time.sleep(0.1)
|
86
|
+
# print("[DEBUG] SocketIO low-level connection established.")
|
87
|
+
|
88
|
+
# Wait for server "connection-established" event
|
89
|
+
if not self._connection_established.wait(timeout):
|
90
|
+
raise RuntimeError("Timed out waiting for connection-established event.")
|
91
|
+
|
92
|
+
self._connected = True
|
93
|
+
self._last_update_time = time.time()
|
94
|
+
# print(f"[DEBUG] Full connection established. Connected: {self._connected}")
|
95
|
+
|
96
|
+
return self.server_socket_id or self.sio.sid
|
97
|
+
|
98
|
+
except Exception as e:
|
99
|
+
# print(f"[DEBUG] Connection failed with error: {e}")
|
100
|
+
self._connected = False
|
101
|
+
raise RuntimeError(f"WebSocket connection failed: {e}")
|
102
|
+
|
103
|
+
def listenForResults(self, min_wait=30, max_wait=300, inactivity_timeout=50, expected_results=None):
|
104
|
+
if not self._connected:
|
105
|
+
raise RuntimeError("WebSocket is not connected. Call connect() first.")
|
106
|
+
|
107
|
+
self._expected_results = expected_results # NEW
|
108
|
+
start_time = time.time()
|
109
|
+
self._last_update_time = time.time()
|
110
|
+
|
111
|
+
def timeout_watcher():
|
112
|
+
while not self._listening_done.is_set():
|
113
|
+
current_time = time.time()
|
114
|
+
time_since_last_update = current_time - self._last_update_time
|
115
|
+
total_elapsed = current_time - start_time
|
116
|
+
|
117
|
+
if total_elapsed < min_wait:
|
118
|
+
time.sleep(0.5)
|
119
|
+
continue
|
120
|
+
|
121
|
+
if total_elapsed > max_wait:
|
122
|
+
# print(f"⚠️ Max wait time {max_wait}s exceeded.")
|
123
|
+
self._listening_done.set()
|
124
|
+
break
|
125
|
+
|
126
|
+
if time_since_last_update > inactivity_timeout:
|
127
|
+
# print(f"⚠️ Inactivity timeout {inactivity_timeout}s exceeded.")
|
128
|
+
self._listening_done.set()
|
129
|
+
break
|
130
|
+
|
131
|
+
time.sleep(1)
|
132
|
+
|
133
|
+
timeout_thread = threading.Thread(target=timeout_watcher, daemon=True)
|
134
|
+
timeout_thread.start()
|
135
|
+
self._listening_done.wait()
|
136
|
+
|
137
|
+
def getReceivedData(self):
|
138
|
+
with self._lock:
|
139
|
+
return self._received_data.copy()
|
140
|
+
|
141
|
+
def disconnect(self):
|
142
|
+
try:
|
143
|
+
if self._connected:
|
144
|
+
self.sio.disconnect()
|
145
|
+
self._connected = False
|
146
|
+
# print("WebSocket client disconnected")
|
147
|
+
except Exception as e:
|
148
|
+
print(f"Error during WebSocket disconnect: {e}")
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: llumo
|
3
|
+
Version: 0.2.0
|
4
|
+
Summary: Python SDK for interacting with the Llumo ai API.
|
5
|
+
Home-page: https://www.llumo.ai/
|
6
|
+
Author: Llumo
|
7
|
+
Author-email: product@llumo.ai
|
8
|
+
Requires-Python: >=3.7
|
9
|
+
Description-Content-Type: text/markdown
|
10
|
+
License-File: LICENSE
|
11
|
+
Dynamic: author
|
12
|
+
Dynamic: author-email
|
13
|
+
Dynamic: description-content-type
|
14
|
+
Dynamic: home-page
|
15
|
+
Dynamic: license-file
|
16
|
+
Dynamic: requires-python
|
17
|
+
Dynamic: summary
|
@@ -0,0 +1,13 @@
|
|
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.2.0.dist-info/licenses/LICENSE,sha256=tF9yAcfPV9xGT3ViWmC8hPvOo8BEk4ZICbUfcEo8Dlk,182
|
10
|
+
llumo-0.2.0.dist-info/METADATA,sha256=z8Ec84QAe1khloO38UkicxqVGukFid_3aeptYpHra0I,426
|
11
|
+
llumo-0.2.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
12
|
+
llumo-0.2.0.dist-info/top_level.txt,sha256=d5zUTMI99llPtLRB8rtSrqELm_bOqX-bNC5IcwlDk88,6
|
13
|
+
llumo-0.2.0.dist-info/RECORD,,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2025 Llumo AI
|
2
|
-
|
3
|
-
All rights reserved. This software is proprietary and confidential.
|
4
|
-
Unauthorized copying, distribution, or use of this software is strictly prohibited.
|
1
|
+
Copyright (c) 2025 Llumo AI
|
2
|
+
|
3
|
+
All rights reserved. This software is proprietary and confidential.
|
4
|
+
Unauthorized copying, distribution, or use of this software is strictly prohibited.
|
llumo/.env
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
|
2
|
-
BASE_URL="https://app.llumo.ai/api"
|
3
|
-
postUrl = "https://red-skull-service-392377961931.us-central1.run.app/api/process-playground"
|
4
|
-
fetchUrl = "https://red-skull-service-392377961931.us-central1.run.app/api/get-cells-data"
|
5
|
-
validateUrl = "https://backend-api.llumo.ai/api/v1/workspace-key-details"
|
6
|
-
SOCKET_URL="https://red-skull-service-392377961931.us-central1.run.app/"
|
llumo-0.1.9.dist-info/METADATA
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: llumo
|
3
|
-
Version: 0.1.9
|
4
|
-
Summary: Python SDK for interacting with the Llumo ai API.
|
5
|
-
Home-page: https://www.llumo.ai/
|
6
|
-
Author: Llumo
|
7
|
-
Author-email: product@llumo.ai
|
8
|
-
License: Proprietary
|
9
|
-
Requires-Python: >=3.7
|
10
|
-
License-File: LICENSE
|
11
|
-
Requires-Dist: requests>=2.0.0
|
12
|
-
Requires-Dist: websocket-client>=1.0.0
|
13
|
-
Requires-Dist: pandas>=1.0.0
|
14
|
-
Requires-Dist: numpy>=1.0.0
|
15
|
-
Requires-Dist: python-socketio[client]==5.13.0
|
16
|
-
Requires-Dist: python-dotenv==1.1.0
|
17
|
-
Requires-Dist: openai==1.75.0
|
18
|
-
Requires-Dist: google-generativeai==0.8.5
|
19
|
-
Dynamic: author
|
20
|
-
Dynamic: author-email
|
21
|
-
Dynamic: home-page
|
22
|
-
Dynamic: license
|
23
|
-
Dynamic: license-file
|
24
|
-
Dynamic: requires-dist
|
25
|
-
Dynamic: requires-python
|
26
|
-
Dynamic: summary
|