lucidicai 1.2.20__py3-none-any.whl → 1.2.22__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 +1 -0
- lucidicai/event.py +30 -19
- lucidicai/session.py +1 -6
- lucidicai/telemetry/lucidic_span_processor.py +55 -3
- {lucidicai-1.2.20.dist-info → lucidicai-1.2.22.dist-info}/METADATA +1 -1
- {lucidicai-1.2.20.dist-info → lucidicai-1.2.22.dist-info}/RECORD +8 -8
- {lucidicai-1.2.20.dist-info → lucidicai-1.2.22.dist-info}/WHEEL +0 -0
- {lucidicai-1.2.20.dist-info → lucidicai-1.2.22.dist-info}/top_level.txt +0 -0
lucidicai/__init__.py
CHANGED
|
@@ -315,6 +315,7 @@ def _auto_end_session():
|
|
|
315
315
|
client = Client()
|
|
316
316
|
if hasattr(client, 'auto_end') and client.auto_end and client.session and not client.session.is_finished:
|
|
317
317
|
logger.info("Auto-ending active session on exit")
|
|
318
|
+
client.auto_end = False # To avoid repeating auto-end on exit
|
|
318
319
|
end_session()
|
|
319
320
|
except Exception as e:
|
|
320
321
|
logger.debug(f"Error during auto-end session: {e}")
|
lucidicai/event.py
CHANGED
|
@@ -14,37 +14,48 @@ class Event:
|
|
|
14
14
|
self.event_id = None
|
|
15
15
|
self.screenshots = []
|
|
16
16
|
self.is_finished = False
|
|
17
|
-
self.init_event()
|
|
18
|
-
self.update_event(**kwargs)
|
|
17
|
+
self.init_event(**kwargs)
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
def init_event(self) -> None:
|
|
19
|
+
# TODO: this is really bad, clean this up later
|
|
20
|
+
def init_event(self, **kwargs) -> None:
|
|
22
21
|
from .client import Client
|
|
23
|
-
request_data =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
22
|
+
request_data = self._build_request_data(**kwargs)
|
|
23
|
+
if self.step_id:
|
|
24
|
+
request_data['step_id'] = self.step_id
|
|
25
|
+
else:
|
|
26
|
+
request_data['session_id'] = self.session_id
|
|
29
27
|
data = Client().make_request('initevent', 'POST', request_data)
|
|
30
28
|
self.event_id = data["event_id"]
|
|
29
|
+
self.step_id = data["step_id"]
|
|
30
|
+
self._upload_screenshots(**kwargs)
|
|
31
|
+
return self.event_id
|
|
31
32
|
|
|
32
33
|
def update_event(self, **kwargs) -> None:
|
|
33
34
|
from .client import Client
|
|
34
|
-
if 'screenshots' in kwargs and kwargs['screenshots'] is not None:
|
|
35
|
-
for i in range(len(kwargs['screenshots'])):
|
|
36
|
-
presigned_url, bucket_name, object_key = get_presigned_url(Client().agent_id, session_id=self.session_id, event_id=self.event_id, nthscreenshot=len(self.screenshots))
|
|
37
|
-
upload_image_to_s3(presigned_url, kwargs['screenshots'][i], "JPEG")
|
|
38
|
-
self.screenshots.append(kwargs['screenshots'][i])
|
|
39
35
|
if 'is_finished' in kwargs:
|
|
40
36
|
self.is_finished = kwargs['is_finished']
|
|
41
|
-
request_data =
|
|
42
|
-
|
|
37
|
+
request_data = self._build_request_data(**kwargs)
|
|
38
|
+
request_data['event_id'] = self.event_id
|
|
39
|
+
Client().make_request('updateevent', 'PUT', request_data)
|
|
40
|
+
self._upload_screenshots(**kwargs)
|
|
41
|
+
|
|
42
|
+
def _build_request_data(self, **kwargs) -> dict:
|
|
43
|
+
from .client import Client
|
|
44
|
+
num_new_screenshots = len(kwargs.get("screenshots", []))
|
|
45
|
+
return {
|
|
43
46
|
"description": Client().mask(kwargs.get("description", None)),
|
|
44
47
|
"result": Client().mask(kwargs.get("result", None)),
|
|
45
48
|
"is_finished": self.is_finished,
|
|
46
49
|
"cost_added": kwargs.get("cost_added", None),
|
|
47
50
|
"model": kwargs.get("model", None),
|
|
48
|
-
"nscreenshots": len(self.screenshots)
|
|
51
|
+
"nscreenshots": len(self.screenshots) + num_new_screenshots,
|
|
52
|
+
"duration": kwargs.get("duration", None)
|
|
49
53
|
}
|
|
50
|
-
|
|
54
|
+
|
|
55
|
+
def _upload_screenshots(self, **kwargs) -> None:
|
|
56
|
+
from .client import Client
|
|
57
|
+
if 'screenshots' in kwargs and kwargs['screenshots'] is not None:
|
|
58
|
+
for i in range(len(kwargs['screenshots'])):
|
|
59
|
+
presigned_url, bucket_name, object_key = get_presigned_url(Client().agent_id, session_id=self.session_id, event_id=self.event_id, nthscreenshot=len(self.screenshots))
|
|
60
|
+
upload_image_to_s3(presigned_url, kwargs['screenshots'][i], "JPEG")
|
|
61
|
+
self.screenshots.append(kwargs['screenshots'][i])
|
lucidicai/session.py
CHANGED
|
@@ -110,14 +110,12 @@ class Session:
|
|
|
110
110
|
|
|
111
111
|
def create_event(self, **kwargs):
|
|
112
112
|
# Get step_id from kwargs or active step
|
|
113
|
-
temp_step_created = False
|
|
114
113
|
if 'step_id' in kwargs and kwargs['step_id'] is not None:
|
|
115
114
|
step_id = kwargs['step_id']
|
|
116
115
|
elif self._active_step:
|
|
117
116
|
step_id = self._active_step
|
|
118
117
|
else:
|
|
119
|
-
step_id =
|
|
120
|
-
temp_step_created = True
|
|
118
|
+
step_id = None
|
|
121
119
|
kwargs.pop('step_id', None)
|
|
122
120
|
event = Event(
|
|
123
121
|
session_id=self.session_id,
|
|
@@ -126,9 +124,6 @@ class Session:
|
|
|
126
124
|
)
|
|
127
125
|
self.event_history[event.event_id] = event
|
|
128
126
|
self._active_event = event
|
|
129
|
-
if temp_step_created:
|
|
130
|
-
self.update_step(step_id=step_id, is_finished=True)
|
|
131
|
-
self._active_step = None
|
|
132
127
|
return event.event_id
|
|
133
128
|
|
|
134
129
|
def update_event(self, **kwargs):
|
|
@@ -239,6 +239,12 @@ class LucidicSpanProcessor(SpanProcessor):
|
|
|
239
239
|
# Calculate cost
|
|
240
240
|
cost = self._calculate_cost(attributes)
|
|
241
241
|
|
|
242
|
+
# Calculate duration in seconds
|
|
243
|
+
duration_seconds = None
|
|
244
|
+
if span.start_time and span.end_time:
|
|
245
|
+
duration_ns = span.end_time - span.start_time
|
|
246
|
+
duration_seconds = duration_ns / 1_000_000_000
|
|
247
|
+
|
|
242
248
|
# Check success
|
|
243
249
|
is_successful = span.status.status_code != StatusCode.ERROR
|
|
244
250
|
|
|
@@ -247,7 +253,8 @@ class LucidicSpanProcessor(SpanProcessor):
|
|
|
247
253
|
'description': description,
|
|
248
254
|
'result': formatted_result,
|
|
249
255
|
'model': model,
|
|
250
|
-
'is_finished': True
|
|
256
|
+
'is_finished': True,
|
|
257
|
+
'duration': duration_seconds
|
|
251
258
|
}
|
|
252
259
|
|
|
253
260
|
if images:
|
|
@@ -288,6 +295,12 @@ class LucidicSpanProcessor(SpanProcessor):
|
|
|
288
295
|
# Calculate cost
|
|
289
296
|
cost = self._calculate_cost(attributes)
|
|
290
297
|
|
|
298
|
+
# Calculate duration in seconds
|
|
299
|
+
duration_seconds = None
|
|
300
|
+
if span.start_time and span.end_time:
|
|
301
|
+
duration_ns = span.end_time - span.start_time
|
|
302
|
+
duration_seconds = duration_ns / 1_000_000_000
|
|
303
|
+
|
|
291
304
|
# Check success
|
|
292
305
|
is_successful = span.status.status_code != StatusCode.ERROR
|
|
293
306
|
|
|
@@ -295,7 +308,8 @@ class LucidicSpanProcessor(SpanProcessor):
|
|
|
295
308
|
update_kwargs = {
|
|
296
309
|
'event_id': event_id,
|
|
297
310
|
'result': result,
|
|
298
|
-
'is_finished': True
|
|
311
|
+
'is_finished': True,
|
|
312
|
+
'duration': duration_seconds
|
|
299
313
|
}
|
|
300
314
|
|
|
301
315
|
if cost is not None:
|
|
@@ -352,7 +366,21 @@ class LucidicSpanProcessor(SpanProcessor):
|
|
|
352
366
|
if tool_name:
|
|
353
367
|
if DEBUG:
|
|
354
368
|
logger.info(f"[SpanProcessor] Found openai agents tool call: {tool_name}")
|
|
355
|
-
|
|
369
|
+
|
|
370
|
+
# Extract and format tool parameters
|
|
371
|
+
tool_params_str = attributes.get('gen_ai.tool.parameters')
|
|
372
|
+
if tool_params_str:
|
|
373
|
+
try:
|
|
374
|
+
# Parse the JSON string
|
|
375
|
+
tool_params = json.loads(tool_params_str)
|
|
376
|
+
# Format the parameters nicely
|
|
377
|
+
formatted_params = json.dumps(tool_params, indent=2)
|
|
378
|
+
return f"Agent Tool Call: {tool_name}\nParameters:{formatted_params}"
|
|
379
|
+
except json.JSONDecodeError:
|
|
380
|
+
# If parsing fails, just include the raw string
|
|
381
|
+
return f"Agent Tool Call: {tool_name}\nParameters: {tool_params_str}"
|
|
382
|
+
else:
|
|
383
|
+
return f"Agent Tool Call: {tool_name}"
|
|
356
384
|
|
|
357
385
|
# Fallback
|
|
358
386
|
if DEBUG:
|
|
@@ -491,6 +519,30 @@ class LucidicSpanProcessor(SpanProcessor):
|
|
|
491
519
|
if DEBUG:
|
|
492
520
|
logger.info(f"[SpanProcessor -- Agent Tool Call Response Received]") # span attributes: {attributes}")
|
|
493
521
|
|
|
522
|
+
# Check the operation type to determine what kind of response this is
|
|
523
|
+
operation_name = attributes.get('gen_ai.operation.name')
|
|
524
|
+
tool_result = attributes.get('gen_ai.tool.result')
|
|
525
|
+
agent_name = attributes.get('gen_ai.agent.name')
|
|
526
|
+
|
|
527
|
+
# For function/tool spans, just show the tool result
|
|
528
|
+
if operation_name == 'function' and tool_result:
|
|
529
|
+
return f"Tool Result: {tool_result}"
|
|
530
|
+
|
|
531
|
+
# For agent spans or response spans without tool results, this might be a handoff
|
|
532
|
+
# We can check if there's actual completion content
|
|
533
|
+
completion_content = None
|
|
534
|
+
for i in range(10): # Check up to 10 completions
|
|
535
|
+
content = attributes.get(f'gen_ai.completion.{i}.content')
|
|
536
|
+
if content:
|
|
537
|
+
completion_content = content
|
|
538
|
+
break
|
|
539
|
+
|
|
540
|
+
# If we have completion content, return it (this is the actual agent response)
|
|
541
|
+
if completion_content:
|
|
542
|
+
return completion_content
|
|
543
|
+
|
|
544
|
+
# Otherwise, this is likely a handoff scenario
|
|
545
|
+
# Since we can't determine the next agent, just indicate a handoff occurred
|
|
494
546
|
return "Agent Handoff"
|
|
495
547
|
|
|
496
548
|
return "Response received"
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
lucidicai/__init__.py,sha256=
|
|
1
|
+
lucidicai/__init__.py,sha256=8_qfdx62xyvrdyONXCHZ4iKDWCvmyncp1T5Epb1DIv8,20664
|
|
2
2
|
lucidicai/action.py,sha256=sPRd1hTIVXDqnvG9ZXWEipUFh0bsXcE0Fm7RVqmVccM,237
|
|
3
3
|
lucidicai/client.py,sha256=3yBVppvwBwesca_pZSKgTUDihzZe5JhVgh1AALCpJ_Q,6620
|
|
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=
|
|
7
|
+
lucidicai/event.py,sha256=SjPSGNBuW6YLDJUhY16IvQgE9lrg3bypgWOK9IKVJWI,2567
|
|
8
8
|
lucidicai/image_upload.py,sha256=6SRudg-BpInM2gzMx1Yf1Rz_Zyh8inwoJ7U4pBw7ruY,3807
|
|
9
9
|
lucidicai/model_pricing.py,sha256=o1yWCaF5Qxj4tloXxVG3SZXcTMKtk56J_Nfdo8M4uR0,11947
|
|
10
|
-
lucidicai/session.py,sha256=
|
|
10
|
+
lucidicai/session.py,sha256=mUPwXcGW5tFsK6Udl4i2jndV0STBYHbPyWqA8GxBdHM,5285
|
|
11
11
|
lucidicai/singleton.py,sha256=gfT3XdWLXSIWMqDXbY6-pnesMZ8RGRitaEPhIsgrRPw,1272
|
|
12
12
|
lucidicai/state.py,sha256=4Tb1X6l2or6w_e62FYSuEeghAv3xXm5gquKwzCpvdok,235
|
|
13
13
|
lucidicai/step.py,sha256=_oBIyTBZBvNkUkYHIrwWd75KMSlMtR9Ws2Lo71Lyff8,2522
|
|
@@ -33,7 +33,7 @@ lucidicai/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
33
33
|
lucidicai/telemetry/base_provider.py,sha256=nrZVr4Y9xcAiMn4uAN3t3k6DlHNTvlXrA4qQg7lANOQ,544
|
|
34
34
|
lucidicai/telemetry/litellm_bridge.py,sha256=mOdjEfvP--ToDv8snoOMU4pRQx_Yg4s2o3BMTMzeRK8,14979
|
|
35
35
|
lucidicai/telemetry/lucidic_exporter.py,sha256=h2GOnEk22Fpeke4Zc7SSk391yr0joUApVwolV5Q4hz4,10818
|
|
36
|
-
lucidicai/telemetry/lucidic_span_processor.py,sha256=
|
|
36
|
+
lucidicai/telemetry/lucidic_span_processor.py,sha256=9EKD4fvnEQIoPBLhGCAHRxsLAWmJNGFnb1NpwRUlMOI,30048
|
|
37
37
|
lucidicai/telemetry/openai_agents_instrumentor.py,sha256=__wIbeglMnEEf4AGTQ--FXeWCKmz2yy8SBupwprEdZA,12694
|
|
38
38
|
lucidicai/telemetry/opentelemetry_converter.py,sha256=xOHCqoTyO4hUkL6k7fxy84PbljPpYep6ET9ZqbkJehc,17665
|
|
39
39
|
lucidicai/telemetry/otel_handlers.py,sha256=HqGYIWJI_Vp8So2-HMpPjnrgTBSgBHHLDu01z_sq-Qk,14646
|
|
@@ -44,7 +44,7 @@ lucidicai/telemetry/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
44
44
|
lucidicai/telemetry/utils/image_storage.py,sha256=4Z59ZpVexr7-lcExfr8GsqXe0y2VZmr8Yjwa-3DeOxU,1457
|
|
45
45
|
lucidicai/telemetry/utils/text_storage.py,sha256=L62MMJ8E23TDqDTUv2aRntdKMCItsXV7XjY6cFwx2DE,1503
|
|
46
46
|
lucidicai/telemetry/utils/universal_image_interceptor.py,sha256=zPfVsMjtKxJP2n2OOjKbtPiQJTZ0sf5_28GWprOnJP4,12185
|
|
47
|
-
lucidicai-1.2.
|
|
48
|
-
lucidicai-1.2.
|
|
49
|
-
lucidicai-1.2.
|
|
50
|
-
lucidicai-1.2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|