lucidicai 2.1.1__py3-none-any.whl → 2.1.3__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.
@@ -1,9 +1,5 @@
1
- import os
2
1
  import logging
3
2
  from typing import Optional, Dict, List, Any
4
- from dotenv import load_dotenv
5
-
6
- from ...core.errors import APIKeyVerificationError
7
3
 
8
4
  logger = logging.getLogger("Lucidic")
9
5
 
@@ -36,37 +32,15 @@ def get_dataset(
36
32
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
37
33
  ValueError: If dataset_id is not provided.
38
34
  """
39
- load_dotenv()
40
-
41
35
  # Validation
42
36
  if not dataset_id:
43
37
  raise ValueError("Dataset ID is required")
44
38
 
45
- # Get credentials
46
- if api_key is None:
47
- api_key = os.getenv("LUCIDIC_API_KEY", None)
48
- if api_key is None:
49
- raise APIKeyVerificationError(
50
- "Make sure to either pass your API key into get_dataset() or set the LUCIDIC_API_KEY environment variable."
51
- )
52
-
53
- if agent_id is None:
54
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
55
- if agent_id is None:
56
- raise APIKeyVerificationError(
57
- "Lucidic agent ID not specified. Make sure to either pass your agent ID into get_dataset() or set the LUCIDIC_AGENT_ID environment variable."
58
- )
59
-
60
- # Get HTTP client
61
- from ..init import get_http
62
- from ...core.config import SDKConfig
63
- from ...api.client import HttpClient
39
+ from ..init import ensure_http_and_resources, get_http
64
40
 
41
+ # Ensure HTTP client is initialized and stored in SDK state
42
+ ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
65
43
  http = get_http()
66
- if not http:
67
- # Create a new HTTP client if needed
68
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
69
- http = HttpClient(config)
70
44
 
71
45
  # Make request to get dataset
72
46
  response = http.get('getdataset', {'dataset_id': dataset_id})
@@ -126,31 +100,9 @@ def list_datasets(
126
100
  Raises:
127
101
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
128
102
  """
129
- from ..init import get_resources, get_http
130
- from ...core.config import SDKConfig
131
- from ...api.client import HttpClient
132
-
133
- # Get or create resources
134
- resources = get_resources()
135
- if not resources or 'datasets' not in resources:
136
- load_dotenv()
137
-
138
- # Get credentials
139
- if api_key is None:
140
- api_key = os.getenv("LUCIDIC_API_KEY", None)
141
- if api_key is None:
142
- raise APIKeyVerificationError(
143
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
144
- )
145
-
146
- if agent_id is None:
147
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
148
-
149
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
150
- http = HttpClient(config)
151
- from ...api.resources.dataset import DatasetResource
152
- resources = {'datasets': DatasetResource(http)}
103
+ from ..init import ensure_http_and_resources
153
104
 
105
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
154
106
  return resources['datasets'].list_datasets(agent_id)
155
107
 
156
108
 
@@ -179,30 +131,9 @@ def create_dataset(
179
131
  Raises:
180
132
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
181
133
  """
182
- from ..init import get_resources, get_http
183
- from ...core.config import SDKConfig
184
- from ...api.client import HttpClient
185
-
186
- # Get or create resources
187
- resources = get_resources()
188
- if not resources or 'datasets' not in resources:
189
- load_dotenv()
190
-
191
- if api_key is None:
192
- api_key = os.getenv("LUCIDIC_API_KEY", None)
193
- if api_key is None:
194
- raise APIKeyVerificationError(
195
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
196
- )
197
-
198
- if agent_id is None:
199
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
200
-
201
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
202
- http = HttpClient(config)
203
- from ...api.resources.dataset import DatasetResource
204
- resources = {'datasets': DatasetResource(http)}
134
+ from ..init import ensure_http_and_resources
205
135
 
136
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
206
137
  return resources['datasets'].create_dataset(name, description, tags, suggested_flag_config, agent_id)
207
138
 
208
139
 
@@ -233,29 +164,9 @@ def update_dataset(
233
164
  Raises:
234
165
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
235
166
  """
236
- from ..init import get_resources, get_http
237
- from ...core.config import SDKConfig
238
- from ...api.client import HttpClient
239
-
240
- # Get or create resources
241
- resources = get_resources()
242
- if not resources or 'datasets' not in resources:
243
- load_dotenv()
244
-
245
- if api_key is None:
246
- api_key = os.getenv("LUCIDIC_API_KEY", None)
247
- if api_key is None:
248
- raise APIKeyVerificationError(
249
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
250
- )
251
-
252
- if agent_id is None:
253
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
254
-
255
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
256
- http = HttpClient(config)
257
- from ...api.resources.dataset import DatasetResource
258
- resources = {'datasets': DatasetResource(http)}
167
+ from ..init import ensure_http_and_resources
168
+
169
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
259
170
 
260
171
  kwargs = {}
261
172
  if name is not None:
@@ -289,30 +200,9 @@ def delete_dataset(
289
200
  Raises:
290
201
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
291
202
  """
292
- from ..init import get_resources, get_http
293
- from ...core.config import SDKConfig
294
- from ...api.client import HttpClient
295
-
296
- # Get or create resources
297
- resources = get_resources()
298
- if not resources or 'datasets' not in resources:
299
- load_dotenv()
300
-
301
- if api_key is None:
302
- api_key = os.getenv("LUCIDIC_API_KEY", None)
303
- if api_key is None:
304
- raise APIKeyVerificationError(
305
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
306
- )
307
-
308
- if agent_id is None:
309
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
310
-
311
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
312
- http = HttpClient(config)
313
- from ...api.resources.dataset import DatasetResource
314
- resources = {'datasets': DatasetResource(http)}
203
+ from ..init import ensure_http_and_resources
315
204
 
205
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
316
206
  return resources['datasets'].delete_dataset(dataset_id)
317
207
 
318
208
 
@@ -349,30 +239,9 @@ def create_dataset_item(
349
239
  Raises:
350
240
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
351
241
  """
352
- from ..init import get_resources, get_http
353
- from ...core.config import SDKConfig
354
- from ...api.client import HttpClient
355
-
356
- # Get or create resources
357
- resources = get_resources()
358
- if not resources or 'datasets' not in resources:
359
- load_dotenv()
360
-
361
- if api_key is None:
362
- api_key = os.getenv("LUCIDIC_API_KEY", None)
363
- if api_key is None:
364
- raise APIKeyVerificationError(
365
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
366
- )
367
-
368
- if agent_id is None:
369
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
370
-
371
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
372
- http = HttpClient(config)
373
- from ...api.resources.dataset import DatasetResource
374
- resources = {'datasets': DatasetResource(http)}
242
+ from ..init import ensure_http_and_resources
375
243
 
244
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
376
245
  return resources['datasets'].create_item(
377
246
  dataset_id, name, input_data,
378
247
  expected_output=expected_output,
@@ -404,30 +273,9 @@ def get_dataset_item(
404
273
  Raises:
405
274
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
406
275
  """
407
- from ..init import get_resources, get_http
408
- from ...core.config import SDKConfig
409
- from ...api.client import HttpClient
410
-
411
- # Get or create resources
412
- resources = get_resources()
413
- if not resources or 'datasets' not in resources:
414
- load_dotenv()
415
-
416
- if api_key is None:
417
- api_key = os.getenv("LUCIDIC_API_KEY", None)
418
- if api_key is None:
419
- raise APIKeyVerificationError(
420
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
421
- )
422
-
423
- if agent_id is None:
424
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
425
-
426
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
427
- http = HttpClient(config)
428
- from ...api.resources.dataset import DatasetResource
429
- resources = {'datasets': DatasetResource(http)}
276
+ from ..init import ensure_http_and_resources
430
277
 
278
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
431
279
  return resources['datasets'].get_item(dataset_id, item_id)
432
280
 
433
281
 
@@ -466,29 +314,9 @@ def update_dataset_item(
466
314
  Raises:
467
315
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
468
316
  """
469
- from ..init import get_resources, get_http
470
- from ...core.config import SDKConfig
471
- from ...api.client import HttpClient
472
-
473
- # Get or create resources
474
- resources = get_resources()
475
- if not resources or 'datasets' not in resources:
476
- load_dotenv()
477
-
478
- if api_key is None:
479
- api_key = os.getenv("LUCIDIC_API_KEY", None)
480
- if api_key is None:
481
- raise APIKeyVerificationError(
482
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
483
- )
484
-
485
- if agent_id is None:
486
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
487
-
488
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
489
- http = HttpClient(config)
490
- from ...api.resources.dataset import DatasetResource
491
- resources = {'datasets': DatasetResource(http)}
317
+ from ..init import ensure_http_and_resources
318
+
319
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
492
320
 
493
321
  kwargs = {}
494
322
  if name is not None:
@@ -530,30 +358,9 @@ def delete_dataset_item(
530
358
  Raises:
531
359
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
532
360
  """
533
- from ..init import get_resources, get_http
534
- from ...core.config import SDKConfig
535
- from ...api.client import HttpClient
536
-
537
- # Get or create resources
538
- resources = get_resources()
539
- if not resources or 'datasets' not in resources:
540
- load_dotenv()
541
-
542
- if api_key is None:
543
- api_key = os.getenv("LUCIDIC_API_KEY", None)
544
- if api_key is None:
545
- raise APIKeyVerificationError(
546
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
547
- )
548
-
549
- if agent_id is None:
550
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
551
-
552
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
553
- http = HttpClient(config)
554
- from ...api.resources.dataset import DatasetResource
555
- resources = {'datasets': DatasetResource(http)}
361
+ from ..init import ensure_http_and_resources
556
362
 
363
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
557
364
  return resources['datasets'].delete_item(dataset_id, item_id)
558
365
 
559
366
 
@@ -578,28 +385,7 @@ def list_dataset_item_sessions(
578
385
  Raises:
579
386
  APIKeyVerificationError: If API key or agent ID is missing or invalid.
580
387
  """
581
- from ..init import get_resources, get_http
582
- from ...core.config import SDKConfig
583
- from ...api.client import HttpClient
584
-
585
- # Get or create resources
586
- resources = get_resources()
587
- if not resources or 'datasets' not in resources:
588
- load_dotenv()
589
-
590
- if api_key is None:
591
- api_key = os.getenv("LUCIDIC_API_KEY", None)
592
- if api_key is None:
593
- raise APIKeyVerificationError(
594
- "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
595
- )
596
-
597
- if agent_id is None:
598
- agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
599
-
600
- config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
601
- http = HttpClient(config)
602
- from ...api.resources.dataset import DatasetResource
603
- resources = {'datasets': DatasetResource(http)}
388
+ from ..init import ensure_http_and_resources
604
389
 
390
+ resources = ensure_http_and_resources(api_key=api_key, agent_id=agent_id)
605
391
  return resources['datasets'].list_item_sessions(dataset_id, item_id)
lucidicai/sdk/init.py CHANGED
@@ -353,6 +353,80 @@ def get_resources() -> dict:
353
353
  return _sdk_state.resources
354
354
 
355
355
 
356
+ def set_http(http: HttpClient) -> None:
357
+ """Set the HTTP client instance in SDK state."""
358
+ global _sdk_state
359
+ _sdk_state.http = http
360
+
361
+
362
+ def set_resources(resources: dict) -> None:
363
+ """Set API resource instances in SDK state."""
364
+ global _sdk_state
365
+ _sdk_state.resources = resources
366
+
367
+
368
+ def ensure_http_and_resources(api_key: Optional[str] = None, agent_id: Optional[str] = None) -> dict:
369
+ """Ensure HTTP client and resources are initialized, creating them if needed.
370
+
371
+ This function checks if the HTTP client and resources already exist in SDK state.
372
+ If not, it creates them and stores them in SDK state for reuse.
373
+
374
+ Args:
375
+ api_key: API key (uses env if not provided)
376
+ agent_id: Agent ID (uses env if not provided)
377
+
378
+ Returns:
379
+ Dictionary of API resources with 'datasets' key
380
+
381
+ Raises:
382
+ APIKeyVerificationError: If API key is not available
383
+ """
384
+ global _sdk_state
385
+
386
+ # If we already have resources with datasets, return them
387
+ if _sdk_state.resources and 'datasets' in _sdk_state.resources:
388
+ return _sdk_state.resources
389
+
390
+ # Need to create HTTP client and resources
391
+ from dotenv import load_dotenv
392
+ import os
393
+ from ..core.errors import APIKeyVerificationError
394
+
395
+ load_dotenv()
396
+
397
+ # Get credentials
398
+ if api_key is None:
399
+ api_key = os.getenv("LUCIDIC_API_KEY", None)
400
+ if api_key is None:
401
+ raise APIKeyVerificationError(
402
+ "Make sure to either pass your API key or set the LUCIDIC_API_KEY environment variable."
403
+ )
404
+
405
+ if agent_id is None:
406
+ agent_id = os.getenv("LUCIDIC_AGENT_ID", None)
407
+
408
+ # Create or reuse HTTP client
409
+ if not _sdk_state.http:
410
+ debug("[SDK] Creating HTTP client for standalone use")
411
+ config = SDKConfig.from_env(api_key=api_key, agent_id=agent_id)
412
+ _sdk_state.http = HttpClient(config)
413
+
414
+ # Create resources if not already present
415
+ if not _sdk_state.resources:
416
+ _sdk_state.resources = {}
417
+
418
+ if 'datasets' not in _sdk_state.resources:
419
+ debug("[SDK] Creating DatasetResource for standalone use")
420
+ _sdk_state.resources['datasets'] = DatasetResource(_sdk_state.http)
421
+
422
+ return _sdk_state.resources
423
+
424
+
425
+ def get_tracer_provider() -> Optional[TracerProvider]:
426
+ """Get the tracer provider instance."""
427
+ return _sdk_state.tracer_provider
428
+
429
+
356
430
  def clear_state() -> None:
357
431
  """Clear SDK state (for testing)."""
358
432
  global _sdk_state
@@ -46,6 +46,14 @@ class LucidicSpanExporter(SpanExporter):
46
46
 
47
47
  attributes = dict(span.attributes or {})
48
48
 
49
+ # Debug: Check what attributes we have for responses.create
50
+ if span.name == "openai.responses.create":
51
+ debug(f"[Telemetry] responses.create span has {len(attributes)} attributes")
52
+ # Check for specific attributes we're interested in
53
+ has_prompts = any(k.startswith('gen_ai.prompt') for k in attributes.keys())
54
+ has_completions = any(k.startswith('gen_ai.completion') for k in attributes.keys())
55
+ debug(f"[Telemetry] Has prompt attrs: {has_prompts}, Has completion attrs: {has_completions}")
56
+
49
57
  # Skip spans that are likely duplicates or incomplete
50
58
  # Check if this is a responses.parse span that was already handled
51
59
  if span.name == "openai.responses.create" and not attributes.get("lucidic.instrumented"):
@@ -93,6 +101,11 @@ class LucidicSpanExporter(SpanExporter):
93
101
  params = self._extract_params(attributes)
94
102
  output_text = extract_completions(span, attributes)
95
103
 
104
+ # Debug for responses.create
105
+ if span.name == "openai.responses.create":
106
+ debug(f"[Telemetry] Extracted messages: {messages}")
107
+ debug(f"[Telemetry] Extracted output: {output_text}")
108
+
96
109
  # Skip spans with no meaningful output (likely incomplete or duplicate instrumentation)
97
110
  if not output_text or output_text == "Response received":
98
111
  # Only use "Response received" if we have other meaningful data