lucidicai 1.2.22__py3-none-any.whl → 1.3.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.
lucidicai/__init__.py CHANGED
@@ -111,7 +111,7 @@ __all__ = [
111
111
  def init(
112
112
  session_name: Optional[str] = None,
113
113
  session_id: Optional[str] = None,
114
- lucidic_api_key: Optional[str] = None,
114
+ api_key: Optional[str] = None,
115
115
  agent_id: Optional[str] = None,
116
116
  task: Optional[str] = None,
117
117
  providers: Optional[List[ProviderType]] = [],
@@ -128,7 +128,7 @@ def init(
128
128
  Args:
129
129
  session_name: The display name of the session.
130
130
  session_id: Custom ID of the session. If not provided, a random ID will be generated.
131
- lucidic_api_key: API key for authentication. If not provided, will use the LUCIDIC_API_KEY environment variable.
131
+ api_key: API key for authentication. If not provided, will use the LUCIDIC_API_KEY environment variable.
132
132
  agent_id: Agent ID. If not provided, will use the LUCIDIC_AGENT_ID environment variable.
133
133
  task: Task description.
134
134
  providers: List of provider types ("openai", "anthropic", "langchain", "pydantic_ai").
@@ -142,27 +142,29 @@ def init(
142
142
  InvalidOperationError: If the client is already initialized.
143
143
  APIKeyVerificationError: If the API key is invalid.
144
144
  """
145
- if lucidic_api_key is None:
146
- lucidic_api_key = os.getenv("LUCIDIC_API_KEY", None)
147
- if lucidic_api_key is None:
148
- raise APIKeyVerificationError("Make sure to either pass your API key into lai.init() or set the LUCIDIC_API_KEY environment variable.")
149
- if agent_id is None:
150
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
151
- if agent_id is None:
152
- raise APIKeyVerificationError("Lucidic agent ID not specified. Make sure to either pass your agent ID into lai.init() or set the LUCIDIC_AGENT_ID environment variable.")
153
-
145
+
154
146
  # get current client which will be NullClient if never lai is never initialized
155
147
  client = Client()
156
- # ff not yet initialized or still the NullClient -> creaet a real client when init is called
148
+ # if not yet initialized or still the NullClient -> creaet a real client when init is called
157
149
  if not getattr(client, 'initialized', False):
158
- client = Client(lucidic_api_key=lucidic_api_key, agent_id=agent_id)
159
-
160
- if not production_monitoring:
161
- production_monitoring = os.getenv("LUCIDIC_PRODUCTION_MONITORING", False)
162
- if production_monitoring == "True":
163
- production_monitoring = True
164
- else:
165
- production_monitoring = False
150
+ if api_key is None:
151
+ api_key = os.getenv("LUCIDIC_API_KEY", None)
152
+ if api_key is None:
153
+ raise APIKeyVerificationError("Make sure to either pass your API key into lai.init() or set the LUCIDIC_API_KEY environment variable.")
154
+ if agent_id is None:
155
+ agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
156
+ if agent_id is None:
157
+ raise APIKeyVerificationError("Lucidic agent ID not specified. Make sure to either pass your agent ID into lai.init() or set the LUCIDIC_AGENT_ID environment variable.")
158
+ client = Client(api_key=api_key, agent_id=agent_id)
159
+ else:
160
+ # Already initialized, this is a re-init
161
+ api_key = api_key or os.getenv("LUCIDIC_API_KEY", None)
162
+ agent_id = agent_id or os.getenv("LUCIDIC_AGENT_ID", None)
163
+ client.agent_id = agent_id
164
+ if api_key is not None and agent_id is not None and (api_key != client.api_key or agent_id != client.agent_id):
165
+ client.set_api_key(api_key)
166
+ client.agent_id = agent_id
167
+
166
168
 
167
169
  # Handle auto_end with environment variable support
168
170
  if auto_end is None:
@@ -170,14 +172,14 @@ def init(
170
172
 
171
173
  # Set up providers
172
174
  _setup_providers(client, providers)
173
- session_id = client.init_session(
175
+ real_session_id = client.init_session(
174
176
  session_name=session_name,
175
177
  mass_sim_id=mass_sim_id,
176
178
  task=task,
177
179
  rubrics=rubrics,
178
180
  tags=tags,
179
181
  production_monitoring=production_monitoring,
180
- custom_session_id=session_id,
182
+ session_id=session_id,
181
183
  )
182
184
  if masking_function:
183
185
  client.masking_function = masking_function
@@ -186,20 +188,20 @@ def init(
186
188
  client.auto_end = auto_end
187
189
 
188
190
  logger.info("Session initialized successfully")
189
- return session_id
191
+ return real_session_id
190
192
 
191
193
 
192
194
  def continue_session(
193
195
  session_id: str,
194
- lucidic_api_key: Optional[str] = None,
196
+ api_key: Optional[str] = None,
195
197
  agent_id: Optional[str] = None,
196
198
  providers: Optional[List[ProviderType]] = [],
197
199
  masking_function = None,
198
200
  auto_end: Optional[bool] = True,
199
201
  ):
200
- if lucidic_api_key is None:
201
- lucidic_api_key = os.getenv("LUCIDIC_API_KEY", None)
202
- if lucidic_api_key is None:
202
+ if api_key is None:
203
+ api_key = os.getenv("LUCIDIC_API_KEY", None)
204
+ if api_key is None:
203
205
  raise APIKeyVerificationError("Make sure to either pass your API key into lai.init() or set the LUCIDIC_API_KEY environment variable.")
204
206
  if agent_id is None:
205
207
  agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
@@ -211,7 +213,7 @@ def continue_session(
211
213
  raise InvalidOperationError("[Lucidic] Session already in progress. Please call lai.end_session() or lai.reset_sdk() first.")
212
214
  # if not yet initialized or still the NullClient -> create a real client when init is called
213
215
  if not getattr(client, 'initialized', False):
214
- client = Client(lucidic_api_key=lucidic_api_key, agent_id=agent_id)
216
+ client = Client(api_key=api_key, agent_id=agent_id)
215
217
 
216
218
  # Handle auto_end with environment variable support
217
219
  if auto_end is None:
@@ -284,8 +286,10 @@ def end_session(
284
286
 
285
287
  def reset_sdk() -> None:
286
288
  """
287
- Reset the SDK.
289
+ DEPRECATED: Reset the SDK.
288
290
  """
291
+ return
292
+
289
293
  client = Client()
290
294
  if not client.initialized:
291
295
  return
@@ -342,7 +346,7 @@ signal.signal(signal.SIGTERM, _signal_handler)
342
346
  def create_mass_sim(
343
347
  mass_sim_name: str,
344
348
  total_num_sessions: int,
345
- lucidic_api_key: Optional[str] = None,
349
+ api_key: Optional[str] = None,
346
350
  agent_id: Optional[str] = None,
347
351
  task: Optional[str] = None,
348
352
  tags: Optional[list] = None
@@ -353,7 +357,7 @@ def create_mass_sim(
353
357
  Args:
354
358
  mass_sim_name: Name of the mass simulation.
355
359
  total_num_sessions: Total intended number of sessions. More sessions can be added later.
356
- lucidic_api_key: API key for authentication. If not provided, will use the LUCIDIC_API_KEY environment variable.
360
+ api_key: API key for authentication. If not provided, will use the LUCIDIC_API_KEY environment variable.
357
361
  agent_id: Agent ID. If not provided, will use the LUCIDIC_AGENT_ID environment variable.
358
362
  task: Task description.
359
363
  tags: Tags for the mass simulation.
@@ -361,9 +365,9 @@ def create_mass_sim(
361
365
  Returns:
362
366
  mass_sim_id: ID of the created mass simulation. Pass this to lai.init() to create a new session in the mass sim.
363
367
  """
364
- if lucidic_api_key is None:
365
- lucidic_api_key = os.getenv("LUCIDIC_API_KEY", None)
366
- if lucidic_api_key is None:
368
+ if api_key is None:
369
+ api_key = os.getenv("LUCIDIC_API_KEY", None)
370
+ if api_key is None:
367
371
  raise APIKeyVerificationError("Make sure to either pass your API key into lai.init() or set the LUCIDIC_API_KEY environment variable.")
368
372
  if agent_id is None:
369
373
  agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
@@ -373,7 +377,7 @@ def create_mass_sim(
373
377
  client = Client()
374
378
  except LucidicNotInitializedError:
375
379
  client = Client( # TODO: fail hard if incorrect API key or agent ID provided and wrong, fail silently if not provided
376
- lucidic_api_key=lucidic_api_key,
380
+ api_key=api_key,
377
381
  agent_id=agent_id,
378
382
  )
379
383
  mass_sim_id = client.init_mass_sim(mass_sim_name=mass_sim_name, total_num_sims=total_num_sessions, task=task, tags=tags) # TODO: change total_num_sims to total_num_sessions everywhere
lucidicai/client.py CHANGED
@@ -13,7 +13,7 @@ from .errors import APIKeyVerificationError, InvalidOperationError, LucidicNotIn
13
13
  from .telemetry.base_provider import BaseProvider
14
14
  from .session import Session
15
15
  from .singleton import singleton, clear_singletons
16
-
16
+ from .lru import LRUCache
17
17
 
18
18
  NETWORK_RETRIES = 3
19
19
 
@@ -22,14 +22,16 @@ NETWORK_RETRIES = 3
22
22
  class Client:
23
23
  def __init__(
24
24
  self,
25
- lucidic_api_key: str,
25
+ api_key: str,
26
26
  agent_id: str,
27
27
  ):
28
28
  self.base_url = "https://analytics.lucidic.ai/api" if not (os.getenv("LUCIDIC_DEBUG", 'False') == 'True') else "http://localhost:8000/api"
29
29
  self.initialized = False
30
30
  self.session = None
31
+ self.previous_sessions = LRUCache(500) # For LRU cache of previously initialized sessions
32
+ self.custom_session_id_translations = LRUCache(500) # For translations of custom session IDs to real session IDs
31
33
  self.providers = []
32
- self.api_key = lucidic_api_key
34
+ self.api_key = api_key
33
35
  self.agent_id = agent_id
34
36
  self.masking_function = None
35
37
  self.auto_end = False # Default to False until explicitly set during init
@@ -42,13 +44,17 @@ class Client:
42
44
  )
43
45
  adapter = HTTPAdapter(max_retries=retry_cfg, pool_connections=20, pool_maxsize=100)
44
46
  self.request_session.mount("https://", adapter)
45
- self.request_session.headers.update({"Authorization": f"Api-Key {self.api_key}", "User-Agent": "lucidic-sdk/1.1"})
47
+ self.set_api_key(api_key)
46
48
  self.prompts = dict()
49
+
50
+ def set_api_key(self, api_key: str):
51
+ self.api_key = api_key
52
+ self.request_session.headers.update({"Authorization": f"Api-Key {self.api_key}", "User-Agent": "lucidic-sdk/1.1"})
47
53
  try:
48
- self.verify_api_key(self.base_url, lucidic_api_key)
54
+ self.verify_api_key(self.base_url, api_key)
49
55
  except APIKeyVerificationError:
50
56
  raise APIKeyVerificationError("Invalid API Key")
51
-
57
+
52
58
  def clear(self):
53
59
  self.undo_overrides()
54
60
  clear_singletons()
@@ -69,7 +75,7 @@ class Client:
69
75
  def undo_overrides(self):
70
76
  for provider in self.providers:
71
77
  provider.undo_override()
72
-
78
+
73
79
  def init_session(
74
80
  self,
75
81
  session_name: str,
@@ -78,30 +84,69 @@ class Client:
78
84
  rubrics: Optional[list] = None,
79
85
  tags: Optional[list] = None,
80
86
  production_monitoring: Optional[bool] = False,
81
- custom_session_id: Optional[str] = None,
87
+ session_id: Optional[str] = None,
82
88
  ) -> None:
89
+ if session_id:
90
+ # Check if it's a known session ID, maybe custom and maybe real
91
+ if session_id in self.custom_session_id_translations:
92
+ session_id = self.custom_session_id_translations[session_id]
93
+ # Check if it's the same as the current session
94
+ if self.session and self.session.session_id == session_id:
95
+ return self.session.session_id
96
+ # Check if it's a previous session that we have saved
97
+ if session_id in self.previous_sessions:
98
+ if self.session:
99
+ self.previous_sessions[self.session.session_id] = self.session
100
+ self.session = self.previous_sessions.pop(session_id) # Remove from previous sessions because it's now the current session
101
+ return self.session.session_id
102
+
103
+ # Either there's no session ID, or we don't know about the old session
104
+ # We need to go to the backend in both cases
105
+ request_data = {
106
+ "agent_id": self.agent_id,
107
+ "session_name": session_name,
108
+ "task": task,
109
+ "mass_sim_id": mass_sim_id,
110
+ "rubrics": rubrics,
111
+ "tags": tags,
112
+ "session_id": session_id
113
+ }
114
+ data = self.make_request('initsession', 'POST', request_data)
115
+ real_session_id = data["session_id"]
116
+ if session_id and session_id != real_session_id:
117
+ self.custom_session_id_translations[session_id] = real_session_id
118
+
119
+ if self.session:
120
+ self.previous_sessions[self.session.session_id] = self.session
121
+
83
122
  self.session = Session(
84
123
  agent_id=self.agent_id,
124
+ session_id=real_session_id,
85
125
  session_name=session_name,
86
126
  mass_sim_id=mass_sim_id,
87
127
  task=task,
88
128
  rubrics=rubrics,
89
129
  tags=tags,
90
- production_monitoring=production_monitoring,
91
- custom_session_id=custom_session_id
92
130
  )
93
131
  self.initialized = True
94
132
  return self.session.session_id
95
133
 
96
134
  def continue_session(self, session_id: str):
135
+ if session_id in self.custom_session_id_translations:
136
+ session_id = self.custom_session_id_translations[session_id]
137
+ if self.session and self.session.session_id == session_id:
138
+ return self.session.session_id
139
+ if self.session:
140
+ self.previous_sessions[self.session.session_id] = self.session
141
+ data = self.make_request('continuesession', 'POST', {"session_id": session_id})
142
+ real_session_id = data["session_id"]
143
+ if session_id != real_session_id:
144
+ self.custom_session_id_translations[session_id] = real_session_id
97
145
  self.session = Session(
98
146
  agent_id=self.agent_id,
99
- session_id=session_id
147
+ session_id=real_session_id
100
148
  )
101
- if self.session.session_id != session_id:
102
- # Custom session ID provided
103
- self.session.custom_session_id = session_id
104
- self.initialized = True
149
+ logger.info(f"Session {data.get('session_name', '')} continuing...")
105
150
  return self.session.session_id
106
151
 
107
152
  def init_mass_sim(self, **kwargs) -> str:
lucidicai/event.py CHANGED
@@ -16,7 +16,6 @@ class Event:
16
16
  self.is_finished = False
17
17
  self.init_event(**kwargs)
18
18
 
19
- # TODO: this is really bad, clean this up later
20
19
  def init_event(self, **kwargs) -> None:
21
20
  from .client import Client
22
21
  request_data = self._build_request_data(**kwargs)
@@ -41,7 +40,7 @@ class Event:
41
40
 
42
41
  def _build_request_data(self, **kwargs) -> dict:
43
42
  from .client import Client
44
- num_new_screenshots = len(kwargs.get("screenshots", []))
43
+ num_new_screenshots = len(kwargs.get("screenshots", []) or [])
45
44
  return {
46
45
  "description": Client().mask(kwargs.get("description", None)),
47
46
  "result": Client().mask(kwargs.get("result", None)),
lucidicai/lru.py ADDED
@@ -0,0 +1,19 @@
1
+ from collections import OrderedDict
2
+
3
+ class LRUCache(OrderedDict):
4
+ def __init__(self, capacity: int):
5
+ super().__init__()
6
+ self.capacity = capacity
7
+
8
+ def __getitem__(self, key):
9
+ if key not in self:
10
+ raise KeyError(key)
11
+ self.move_to_end(key) # Mark as recently used
12
+ return super().__getitem__(key)
13
+
14
+ def __setitem__(self, key, value):
15
+ if key in self:
16
+ self.move_to_end(key) # Update position
17
+ super().__setitem__(key, value)
18
+ if len(self) > self.capacity:
19
+ self.popitem(last=False) # Evict least recently used item
lucidicai/session.py CHANGED
@@ -15,11 +15,12 @@ logger = logging.getLogger("Lucidic")
15
15
  class Session:
16
16
  def __init__(
17
17
  self,
18
- agent_id: str,
18
+ agent_id: str,
19
+ session_id = None,
19
20
  **kwargs
20
21
  ):
21
22
  self.agent_id = agent_id
22
- self.session_id = None
23
+ self.session_id = session_id
23
24
  self.step_history = dict()
24
25
  self._active_step: Optional[str] = None # Step ID, not Step object
25
26
  self.event_history = dict()
@@ -30,33 +31,6 @@ class Session:
30
31
  self.session_eval = None
31
32
  self.session_eval_reason = None
32
33
  self.has_gif = None
33
- if kwargs.get("session_id", None) is None: # The kwarg, not the attribute
34
- self.init_session(**kwargs)
35
- else:
36
- self.continue_session(kwargs["session_id"])
37
-
38
- def init_session(self, **kwargs) -> None:
39
- from .client import Client
40
- request_data = {
41
- "agent_id": self.agent_id,
42
- "session_name": kwargs.get("session_name", None),
43
- "task": kwargs.get("task", None),
44
- "mass_sim_id": kwargs.get("mass_sim_id", None),
45
- "rubrics": kwargs.get("rubrics", None),
46
- "tags": kwargs.get("tags", None),
47
- "production_monitoring": kwargs.get("production_monitoring", False),
48
- "custom_session_id": kwargs.get("custom_session_id", None)
49
- }
50
- data = Client().make_request('initsession', 'POST', request_data)
51
- self.session_id = data["session_id"]
52
-
53
- def continue_session(self, session_id: str) -> None:
54
- from .client import Client
55
- self.session_id = session_id
56
- data = Client().make_request('continuesession', 'POST', {"session_id": session_id})
57
- self.session_id = data["session_id"]
58
- logger.info(f"Session {data['session_name']} continuing...")
59
- return self.session_id
60
34
 
61
35
  @property
62
36
  def active_step(self) -> Optional[Step]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lucidicai
3
- Version: 1.2.22
3
+ Version: 1.3.1
4
4
  Summary: Lucidic AI Python SDK
5
5
  Author: Andy Liang
6
6
  Author-email: andy@lucidic.ai
@@ -1,13 +1,14 @@
1
- lucidicai/__init__.py,sha256=8_qfdx62xyvrdyONXCHZ4iKDWCvmyncp1T5Epb1DIv8,20664
1
+ lucidicai/__init__.py,sha256=-eSSR9UaMubE0KpbesR2jLU0xUn37oucMNxjgKaCfkA,20738
2
2
  lucidicai/action.py,sha256=sPRd1hTIVXDqnvG9ZXWEipUFh0bsXcE0Fm7RVqmVccM,237
3
- lucidicai/client.py,sha256=3yBVppvwBwesca_pZSKgTUDihzZe5JhVgh1AALCpJ_Q,6620
3
+ lucidicai/client.py,sha256=MZuNd1S6MTwNRDJ8cUrZfnTZgBBHFwTO2Pc_-wbVNEM,8981
4
4
  lucidicai/constants.py,sha256=_u0z3M4geZgS1g-CrOZUVjtcew8l70dKQnpVQvlXh9w,2172
5
5
  lucidicai/decorators.py,sha256=oqXyfHk9f9UmeaIquuU8mtzed1qZtO_-svwadpoat6g,13950
6
6
  lucidicai/errors.py,sha256=gTg0bdzjuTcUnakRbZnxjngO4gZnRLVwRHRglpZZJsM,970
7
- lucidicai/event.py,sha256=SjPSGNBuW6YLDJUhY16IvQgE9lrg3bypgWOK9IKVJWI,2567
7
+ lucidicai/event.py,sha256=EF-KNPMBHGdNnhchu5HF7QnYJSMofUaq-QuGL0j_OAE,2521
8
8
  lucidicai/image_upload.py,sha256=6SRudg-BpInM2gzMx1Yf1Rz_Zyh8inwoJ7U4pBw7ruY,3807
9
+ lucidicai/lru.py,sha256=PXiDSoUCOxjamG1QlQx6pDbQCm8h5hKAnnr_NI0PEgE,618
9
10
  lucidicai/model_pricing.py,sha256=o1yWCaF5Qxj4tloXxVG3SZXcTMKtk56J_Nfdo8M4uR0,11947
10
- lucidicai/session.py,sha256=mUPwXcGW5tFsK6Udl4i2jndV0STBYHbPyWqA8GxBdHM,5285
11
+ lucidicai/session.py,sha256=NFkWJioqGcRxHjfqwWw-1DZSiScNpnUlY1gtGGApqSQ,4060
11
12
  lucidicai/singleton.py,sha256=gfT3XdWLXSIWMqDXbY6-pnesMZ8RGRitaEPhIsgrRPw,1272
12
13
  lucidicai/state.py,sha256=4Tb1X6l2or6w_e62FYSuEeghAv3xXm5gquKwzCpvdok,235
13
14
  lucidicai/step.py,sha256=_oBIyTBZBvNkUkYHIrwWd75KMSlMtR9Ws2Lo71Lyff8,2522
@@ -44,7 +45,7 @@ lucidicai/telemetry/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
44
45
  lucidicai/telemetry/utils/image_storage.py,sha256=4Z59ZpVexr7-lcExfr8GsqXe0y2VZmr8Yjwa-3DeOxU,1457
45
46
  lucidicai/telemetry/utils/text_storage.py,sha256=L62MMJ8E23TDqDTUv2aRntdKMCItsXV7XjY6cFwx2DE,1503
46
47
  lucidicai/telemetry/utils/universal_image_interceptor.py,sha256=zPfVsMjtKxJP2n2OOjKbtPiQJTZ0sf5_28GWprOnJP4,12185
47
- lucidicai-1.2.22.dist-info/METADATA,sha256=jFsbWQNg8hHEu2sHwDzC8xOlRdgUFo1KsL4QjvJDN50,903
48
- lucidicai-1.2.22.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
49
- lucidicai-1.2.22.dist-info/top_level.txt,sha256=vSSdM3lclF4I5tyVC0xxUk8eIRnnYXMe1hW-eO91HUo,10
50
- lucidicai-1.2.22.dist-info/RECORD,,
48
+ lucidicai-1.3.1.dist-info/METADATA,sha256=fMRzVo9mOgHvcC6PGrH5zdyLaMRrz3KBch6fMkHtgEE,902
49
+ lucidicai-1.3.1.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
50
+ lucidicai-1.3.1.dist-info/top_level.txt,sha256=vSSdM3lclF4I5tyVC0xxUk8eIRnnYXMe1hW-eO91HUo,10
51
+ lucidicai-1.3.1.dist-info/RECORD,,