interactiveai 0.0.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.
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interactive Language Processing Package
|
|
3
|
+
|
|
4
|
+
A comprehensive package for managing Langfuse utilities including initialization,
|
|
5
|
+
observation, scoring, dataset management, and parallel processing.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .interactive import Interactive, CallbackHandler
|
|
9
|
+
|
|
10
|
+
__version__ = "1.0.0"
|
|
11
|
+
__author__ = "Your Name"
|
|
12
|
+
__email__ = "your.email@example.com"
|
|
13
|
+
|
|
14
|
+
__all__ = ["Interactive", "CallbackHandler"]
|
|
15
|
+
|
|
16
|
+
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import Dict, Any, List, Optional, Callable, Union
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
4
|
+
from langfuse import Langfuse
|
|
5
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
6
|
+
from langfuse.langchain import CallbackHandler
|
|
7
|
+
from loguru import logger
|
|
8
|
+
from schemas import Evaluator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Interactive(Langfuse):
|
|
13
|
+
"""
|
|
14
|
+
A comprehensive manager class for InteractiveAI utilities including initialization,
|
|
15
|
+
observation, scoring, dataset management, and parallel processing.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self,
|
|
19
|
+
public_key: str,
|
|
20
|
+
secret_key: str,
|
|
21
|
+
host: Optional[str] = "https://app.interactiveai.com",
|
|
22
|
+
**kwargs):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the InteractiveAI Client
|
|
25
|
+
|
|
26
|
+
To get your keys go to https://app.interactiveai.com/settings/api-keys
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
public_key: InteractiveAI public key
|
|
30
|
+
secret_key: InteractiveAI secret key
|
|
31
|
+
host: InteractiveAI host (defaults to https://app.interactiveai.com)
|
|
32
|
+
"""
|
|
33
|
+
# Initialize Langfuse client
|
|
34
|
+
super().__init__(
|
|
35
|
+
public_key=public_key,
|
|
36
|
+
secret_key=secret_key,
|
|
37
|
+
host=host,
|
|
38
|
+
**kwargs
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def generate_thread_id(self) -> str:
|
|
42
|
+
"""Generate a unique thread ID"""
|
|
43
|
+
thread_id = f"thread_{int(time.time() * 1000)}"
|
|
44
|
+
logger.debug(f"Generated thread ID: {thread_id}")
|
|
45
|
+
return thread_id
|
|
46
|
+
|
|
47
|
+
def get_prompt(self, name: str, version: Optional[int] = None, label: Optional[str] = None) -> ChatPromptTemplate:
|
|
48
|
+
prompt = self.get_prompt(name=name, version=version, label=label)
|
|
49
|
+
return ChatPromptTemplate.from_template(
|
|
50
|
+
prompt.get_langchain_prompt(),
|
|
51
|
+
metadata={"langfuse_prompt": prompt},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_or_get_dataset(self, dataset_name: str, description: str = "") -> Any:
|
|
56
|
+
"""
|
|
57
|
+
Create a dataset or get existing one
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
dataset_name: Name of the dataset
|
|
61
|
+
description: Dataset description
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Dataset object
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
logger.debug(f"Attempting to retrieve dataset: {dataset_name}")
|
|
68
|
+
dataset = self.get_dataset(dataset_name)
|
|
69
|
+
logger.info(f"Retrieved existing dataset: {dataset_name}")
|
|
70
|
+
return dataset
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.info(f"Dataset '{dataset_name}' not found, creating new one")
|
|
73
|
+
try:
|
|
74
|
+
dataset = self.create_dataset(
|
|
75
|
+
name=dataset_name,
|
|
76
|
+
description=description or "Dataset for response evaluation",
|
|
77
|
+
)
|
|
78
|
+
logger.success(f"Successfully created dataset: {dataset_name}")
|
|
79
|
+
return dataset
|
|
80
|
+
except Exception as create_error:
|
|
81
|
+
logger.error(f"Error creating dataset '{dataset_name}': {create_error}")
|
|
82
|
+
raise
|
|
83
|
+
|
|
84
|
+
def add_dataset_item(self, dataset_name: str, item_id: str, input_data: Dict[str, Any],
|
|
85
|
+
expected_output: Dict[str, Any], metadata: Dict[str, Any]):
|
|
86
|
+
"""
|
|
87
|
+
Add an item to a dataset
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
dataset_name: Name of the dataset
|
|
91
|
+
item_id: Unique identifier for the item
|
|
92
|
+
input_data: Input data
|
|
93
|
+
expected_output: Expected output
|
|
94
|
+
metadata: Additional metadata
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
logger.debug(f"Adding item to dataset '{dataset_name}'")
|
|
98
|
+
self.create_dataset_item(
|
|
99
|
+
dataset_name=dataset_name,
|
|
100
|
+
input=input_data,
|
|
101
|
+
expected_output=expected_output,
|
|
102
|
+
metadata=metadata,
|
|
103
|
+
id=item_id
|
|
104
|
+
)
|
|
105
|
+
logger.debug(f"Successfully added item to dataset {dataset_name}")
|
|
106
|
+
except Exception as e:
|
|
107
|
+
logger.error(f"Error adding dataset item to '{dataset_name}': {e}")
|
|
108
|
+
raise
|
|
109
|
+
|
|
110
|
+
def add_dataset_items_parallel(self, dataset_name: str, data: List[tuple[str, Any]], max_workers: int = 4):
|
|
111
|
+
"""
|
|
112
|
+
Add multiple items to a dataset in parallel
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
dataset_name: Name of the dataset
|
|
116
|
+
data: List of tuples containing (item_id, input_data, expected_output, metadata)
|
|
117
|
+
max_workers: Maximum number of parallel workers (default: 4)
|
|
118
|
+
"""
|
|
119
|
+
try:
|
|
120
|
+
logger.debug(f"Adding {len(data)} items to dataset '{dataset_name}' in parallel")
|
|
121
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
122
|
+
futures = []
|
|
123
|
+
for item_id, input_data, expected_output, metadata in data:
|
|
124
|
+
futures.append(
|
|
125
|
+
executor.submit(
|
|
126
|
+
self.add_dataset_item,
|
|
127
|
+
dataset_name,
|
|
128
|
+
item_id,
|
|
129
|
+
input_data,
|
|
130
|
+
expected_output,
|
|
131
|
+
metadata
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
for future in as_completed(futures):
|
|
135
|
+
future.result() # Wait for all tasks to complete
|
|
136
|
+
logger.success(f"Successfully added {len(data)} items to dataset '{dataset_name}'")
|
|
137
|
+
except Exception as e:
|
|
138
|
+
logger.error(f"Error adding dataset items to '{dataset_name}': {e}")
|
|
139
|
+
raise
|
|
140
|
+
|
|
141
|
+
def process_single_item(
|
|
142
|
+
self,
|
|
143
|
+
idx: int,
|
|
144
|
+
item: Any,
|
|
145
|
+
experiment_name: str,
|
|
146
|
+
handler: CallbackHandler,
|
|
147
|
+
workflow: Any,
|
|
148
|
+
evaluations: List[Evaluator]
|
|
149
|
+
) -> Dict[str, Union[str, Dict[str, Any]]]:
|
|
150
|
+
"""Process a single dataset item through the workflow and evaluate results.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
idx: Index of the item in the dataset
|
|
154
|
+
item: Dataset item containing input and expected output
|
|
155
|
+
experiment_name: Name of the experiment for tracking
|
|
156
|
+
handler: Langfuse callback handler for logging
|
|
157
|
+
workflow: The workflow/model to process the item
|
|
158
|
+
evaluations: List of evaluation functions to apply
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Dictionary containing processing status and results or error information
|
|
162
|
+
"""
|
|
163
|
+
logger.debug(f"Processing item {idx} for experiment '{experiment_name}'")
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
# Create a separate trace for each item using the item.run() context manager
|
|
167
|
+
with item.run(run_name=experiment_name) as root_span:
|
|
168
|
+
logger.debug(f"Invoking workflow for item {idx}")
|
|
169
|
+
|
|
170
|
+
# Process the item through the workflow
|
|
171
|
+
output = workflow.invoke(
|
|
172
|
+
state=item.input,
|
|
173
|
+
config={
|
|
174
|
+
"callbacks": [handler],
|
|
175
|
+
"configurable": {"thread_id": idx} # Use idx as thread_id for uniqueness
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Update the trace with input/output information
|
|
180
|
+
root_span.update_trace(
|
|
181
|
+
name=f"{experiment_name}_{idx}",
|
|
182
|
+
input=item.input,
|
|
183
|
+
output=output,
|
|
184
|
+
tags=[experiment_name],
|
|
185
|
+
metadata=item.metadata
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
logger.debug(f"Running {len(evaluations)} evaluations for item {idx}")
|
|
189
|
+
|
|
190
|
+
# Apply all evaluation functions to the output
|
|
191
|
+
for evaluation in evaluations:
|
|
192
|
+
try:
|
|
193
|
+
evaluation_result = evaluation.function(output, item.expected_output)
|
|
194
|
+
root_span.score_trace(
|
|
195
|
+
name=evaluation.name,
|
|
196
|
+
value=evaluation_result.score,
|
|
197
|
+
data_type=evaluation.data_type,
|
|
198
|
+
comment=evaluation_result.reasoning
|
|
199
|
+
)
|
|
200
|
+
logger.debug(
|
|
201
|
+
f"Evaluation '{evaluation.name}' completed for item {idx}: {evaluation_result.score}")
|
|
202
|
+
except Exception as eval_error:
|
|
203
|
+
logger.warning(f"Evaluation '{evaluation.name}' failed for item {idx}: {eval_error}")
|
|
204
|
+
# Continue with other evaluations even if one fails
|
|
205
|
+
|
|
206
|
+
logger.debug(f"Successfully processed item {idx}")
|
|
207
|
+
return {"status": "success", "output": output}
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.error(f"Error processing item {idx}: {str(e)}")
|
|
211
|
+
return {"status": "error", "error": str(e)}
|
|
212
|
+
|
|
213
|
+
def run_langchain_experiment_multi(
|
|
214
|
+
self,
|
|
215
|
+
experiment_name: str,
|
|
216
|
+
dataset: Any,
|
|
217
|
+
workflow: Any,
|
|
218
|
+
evaluations: List[Evaluator],
|
|
219
|
+
max_workers: int = 4,
|
|
220
|
+
start_index: int = 0,
|
|
221
|
+
end_index: Optional[int] = None,
|
|
222
|
+
process_single_item_func: Optional[Callable] = None
|
|
223
|
+
) -> List[Dict[str, Union[str, Dict[str, Any]]]]:
|
|
224
|
+
"""Run an experiment with parallel processing using ThreadPool.
|
|
225
|
+
|
|
226
|
+
This function processes multiple dataset items in parallel, applies evaluations,
|
|
227
|
+
and tracks results using Langfuse for monitoring and analysis.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
experiment_name: Name of the experiment for tracking and logging
|
|
231
|
+
dataset: Dataset containing items to process
|
|
232
|
+
workflow: The workflow/model to process items
|
|
233
|
+
evaluations: List of evaluation functions to apply to results
|
|
234
|
+
max_workers: Maximum number of parallel workers (default: 4)
|
|
235
|
+
start_index: Index to start processing from (default: 0)
|
|
236
|
+
end_index: Index to stop processing at (default: None for all items)
|
|
237
|
+
process_single_item_func: Optional custom processing function (default: None)
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
List of result dictionaries containing status and output/error information
|
|
241
|
+
|
|
242
|
+
Raises:
|
|
243
|
+
ValueError: If start_index >= end_index or if indices are out of bounds
|
|
244
|
+
"""
|
|
245
|
+
# Set end_index to dataset length if not provided
|
|
246
|
+
if end_index is None:
|
|
247
|
+
end_index = len(dataset.items)
|
|
248
|
+
|
|
249
|
+
# Validate indices
|
|
250
|
+
if start_index >= end_index:
|
|
251
|
+
raise ValueError(f"start_index ({start_index}) must be less than end_index ({end_index})")
|
|
252
|
+
|
|
253
|
+
if start_index < 0 or end_index > len(dataset.items):
|
|
254
|
+
raise ValueError(f"Indices out of bounds: dataset has {len(dataset.items)} items")
|
|
255
|
+
|
|
256
|
+
# Use provided processing function or default
|
|
257
|
+
if process_single_item_func:
|
|
258
|
+
processing_func = process_single_item_func
|
|
259
|
+
else:
|
|
260
|
+
processing_func = self.process_single_item
|
|
261
|
+
logger.warning(f" Using default processing function: {self.process_single_item.__name__}")
|
|
262
|
+
|
|
263
|
+
# Calculate actual number of items to process
|
|
264
|
+
items_to_process = end_index - start_index
|
|
265
|
+
|
|
266
|
+
logger.info(f"Starting experiment '{experiment_name}'")
|
|
267
|
+
logger.info(f"Processing {items_to_process} items (indices {start_index} to {end_index - 1})")
|
|
268
|
+
logger.info(f"Using {max_workers} parallel workers")
|
|
269
|
+
logger.info(f"Applying {len(evaluations)} evaluation metrics")
|
|
270
|
+
|
|
271
|
+
# Initialize Langfuse handler for tracking
|
|
272
|
+
handler = CallbackHandler()
|
|
273
|
+
|
|
274
|
+
# Process items in parallel using ThreadPoolExecutor
|
|
275
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
276
|
+
logger.debug(f"Creating thread pool with {max_workers} workers")
|
|
277
|
+
|
|
278
|
+
# Prepare arguments for each item to process
|
|
279
|
+
process_args = [
|
|
280
|
+
(idx, item, experiment_name, handler, workflow, evaluations)
|
|
281
|
+
for idx, item in enumerate(dataset.items[start_index:end_index], start=start_index)
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
# Submit all tasks to the executor
|
|
285
|
+
future_to_args = {
|
|
286
|
+
executor.submit(processing_func, *args): args
|
|
287
|
+
for args in process_args
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
logger.info(f"Submitted {len(future_to_args)} tasks to thread pool")
|
|
291
|
+
|
|
292
|
+
# Collect results as they complete
|
|
293
|
+
results: List[Dict[str, Union[str, Dict[str, Any]]]] = []
|
|
294
|
+
completed_count = 0
|
|
295
|
+
|
|
296
|
+
for future in as_completed(future_to_args):
|
|
297
|
+
args = future_to_args[future]
|
|
298
|
+
item_idx = args[0] # First argument is the index
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
result = future.result()
|
|
302
|
+
results.append(result)
|
|
303
|
+
completed_count += 1
|
|
304
|
+
|
|
305
|
+
# Log progress with appropriate level based on result
|
|
306
|
+
if result["status"] == "success":
|
|
307
|
+
logger.success(f"Item {item_idx} processed successfully ({completed_count}/{items_to_process})")
|
|
308
|
+
else:
|
|
309
|
+
logger.warning(
|
|
310
|
+
f"Item {item_idx} failed ({completed_count}/{items_to_process}): {result.get('error', 'Unknown error')}")
|
|
311
|
+
|
|
312
|
+
except Exception as e:
|
|
313
|
+
logger.error(
|
|
314
|
+
f"Exception processing item {item_idx} ({completed_count + 1}/{items_to_process}): {str(e)}")
|
|
315
|
+
results.append({"status": "exception", "error": str(e)})
|
|
316
|
+
completed_count += 1
|
|
317
|
+
|
|
318
|
+
# Calculate and log summary statistics
|
|
319
|
+
successful = sum(1 for r in results if r["status"] == "success")
|
|
320
|
+
failed = len(results) - successful
|
|
321
|
+
success_rate = (successful / len(results)) * 100 if results else 0
|
|
322
|
+
|
|
323
|
+
logger.info(f" Experiment '{experiment_name}' completed!")
|
|
324
|
+
logger.info(f" Results Summary:")
|
|
325
|
+
logger.info(f" • Total items processed: {len(results)}")
|
|
326
|
+
logger.info(f" • Successful: {successful}")
|
|
327
|
+
logger.info(f" • Failed: {failed}")
|
|
328
|
+
logger.info(f" • Success rate: {success_rate:.1f}%")
|
|
329
|
+
|
|
330
|
+
if failed > 0:
|
|
331
|
+
logger.warning(f"{failed} items failed processing - check logs for details")
|
|
332
|
+
|
|
333
|
+
return results
|
|
334
|
+
|
|
335
|
+
def run_langchain_experiment_sequential(
|
|
336
|
+
self,
|
|
337
|
+
experiment_name: str,
|
|
338
|
+
dataset: Any,
|
|
339
|
+
workflow: Any,
|
|
340
|
+
evaluations: List[Evaluator],
|
|
341
|
+
start_index: int = 0,
|
|
342
|
+
end_index: Optional[int] = None,
|
|
343
|
+
process_single_item_func: Optional[Callable] = None
|
|
344
|
+
) -> List[Dict[str, Union[str, Dict[str, Any]]]]:
|
|
345
|
+
"""Run an experiment with sequential processing (no parallel processing).
|
|
346
|
+
|
|
347
|
+
This function processes multiple dataset items sequentially, applies evaluations,
|
|
348
|
+
and tracks results using Langfuse for monitoring and analysis.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
experiment_name: Name of the experiment for tracking and logging
|
|
352
|
+
dataset: Dataset containing items to process
|
|
353
|
+
workflow: The workflow/model to process items
|
|
354
|
+
evaluations: List of evaluation functions to apply to results
|
|
355
|
+
start_index: Index to start processing from (default: 0)
|
|
356
|
+
end_index: Index to stop processing at (default: None for all items)
|
|
357
|
+
process_single_item_func: Optional custom processing function (default: None)
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
List of result dictionaries containing status and output/error information
|
|
361
|
+
|
|
362
|
+
Raises:
|
|
363
|
+
ValueError: If start_index >= end_index or if indices are out of bounds
|
|
364
|
+
"""
|
|
365
|
+
# Set end_index to dataset length if not provided
|
|
366
|
+
if end_index is None:
|
|
367
|
+
end_index = len(dataset.items)
|
|
368
|
+
|
|
369
|
+
# Validate indices
|
|
370
|
+
if start_index >= end_index:
|
|
371
|
+
raise ValueError(f"start_index ({start_index}) must be less than end_index ({end_index})")
|
|
372
|
+
|
|
373
|
+
if start_index < 0 or end_index > len(dataset.items):
|
|
374
|
+
raise ValueError(f"Indices out of bounds: dataset has {len(dataset.items)} items")
|
|
375
|
+
|
|
376
|
+
# Use provided processing function or default
|
|
377
|
+
if process_single_item_func:
|
|
378
|
+
processing_func = process_single_item_func
|
|
379
|
+
else:
|
|
380
|
+
processing_func = self.process_single_item
|
|
381
|
+
logger.warning(f" Using default processing function: {self.process_single_item.__name__}")
|
|
382
|
+
|
|
383
|
+
# Calculate actual number of items to process
|
|
384
|
+
items_to_process = end_index - start_index
|
|
385
|
+
|
|
386
|
+
logger.info(f"Starting experiment '{experiment_name}'")
|
|
387
|
+
logger.info(f"Processing {items_to_process} items (indices {start_index} to {end_index - 1})")
|
|
388
|
+
logger.info(f"Processing sequentially (max_workers parameter ignored)")
|
|
389
|
+
logger.info(f"Applying {len(evaluations)} evaluation metrics")
|
|
390
|
+
|
|
391
|
+
# Initialize Langfuse handler for tracking
|
|
392
|
+
handler = CallbackHandler()
|
|
393
|
+
|
|
394
|
+
# Process items sequentially
|
|
395
|
+
results: List[Dict[str, Union[str, Dict[str, Any]]]] = []
|
|
396
|
+
completed_count = 0
|
|
397
|
+
|
|
398
|
+
for idx, item in enumerate(dataset.items[start_index:end_index], start=start_index):
|
|
399
|
+
logger.debug(f"Processing item {idx} sequentially")
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
result = processing_func(idx, item, experiment_name, handler, workflow, evaluations)
|
|
403
|
+
results.append(result)
|
|
404
|
+
completed_count += 1
|
|
405
|
+
|
|
406
|
+
# Log progress with appropriate level based on result
|
|
407
|
+
if result["status"] == "success":
|
|
408
|
+
logger.success(f"Item {idx} processed successfully ({completed_count}/{items_to_process})")
|
|
409
|
+
else:
|
|
410
|
+
logger.warning(
|
|
411
|
+
f"Item {idx} failed ({completed_count}/{items_to_process}): {result.get('error', 'Unknown error')}")
|
|
412
|
+
|
|
413
|
+
except Exception as e:
|
|
414
|
+
logger.error(
|
|
415
|
+
f"Exception processing item {idx} ({completed_count + 1}/{items_to_process}): {str(e)}")
|
|
416
|
+
results.append({"status": "exception", "error": str(e)})
|
|
417
|
+
completed_count += 1
|
|
418
|
+
|
|
419
|
+
# Calculate and log summary statistics
|
|
420
|
+
successful = sum(1 for r in results if r["status"] == "success")
|
|
421
|
+
failed = len(results) - successful
|
|
422
|
+
success_rate = (successful / len(results)) * 100 if results else 0
|
|
423
|
+
|
|
424
|
+
logger.info(f" Experiment '{experiment_name}' completed!")
|
|
425
|
+
logger.info(f" Results Summary:")
|
|
426
|
+
logger.info(f" • Total items processed: {len(results)}")
|
|
427
|
+
logger.info(f" • Successful: {successful}")
|
|
428
|
+
logger.info(f" • Failed: {failed}")
|
|
429
|
+
logger.info(f" • Success rate: {success_rate:.1f}%")
|
|
430
|
+
|
|
431
|
+
if failed > 0:
|
|
432
|
+
logger.warning(f"{failed} items failed processing - check logs for details")
|
|
433
|
+
|
|
434
|
+
return results
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def flush(self):
|
|
438
|
+
"""Flush any pending Langfuse operations"""
|
|
439
|
+
try:
|
|
440
|
+
logger.debug("Flushing Langfuse operations...")
|
|
441
|
+
self.flush()
|
|
442
|
+
logger.success("Langfuse operations flushed successfully")
|
|
443
|
+
except Exception as e:
|
|
444
|
+
logger.error(f"Error flushing Langfuse operations: {e}")
|
|
445
|
+
|
|
446
|
+
def __enter__(self):
|
|
447
|
+
"""Context manager entry"""
|
|
448
|
+
return self
|
|
449
|
+
|
|
450
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
451
|
+
"""Context manager exit - flush operations"""
|
|
452
|
+
if exc_type:
|
|
453
|
+
logger.error(f"Exiting context manager due to exception: {exc_type.__name__}: {exc_val}")
|
|
454
|
+
self.flush()
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: interactiveai
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: InteractiveAI client package
|
|
5
|
+
Author: InteractiveAI
|
|
6
|
+
License: MIT License
|
|
7
|
+
Project-URL: Homepage, https://interactive.ai
|
|
8
|
+
Project-URL: App, https://app.interactive.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/Interactive-AI-Labs/interactiveai-sdk
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# interactive-sdk
|
|
16
|
+
InteractiveAI platform SDK
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
interactiveai/__init__.py,sha256=0c4BSy0x0awPyM1uE0dIPVxikx-zOsIOaEB9C5HIDCQ,386
|
|
2
|
+
interactiveai/interactive.py,sha256=ouiqdM-fKscyIUbfr2qkZYT5XvD-kNkZQpPKRXstqcM,19274
|
|
3
|
+
interactiveai-0.0.1.dist-info/METADATA,sha256=QrRGF41Ofq1KzEh1KVIS7EDQBF_oCsreye-0859TuFU,515
|
|
4
|
+
interactiveai-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
5
|
+
interactiveai-0.0.1.dist-info/top_level.txt,sha256=GkGPBUGTyN4pdleSXMxvlfNHKNsQZpn6YBabTx-2sao,14
|
|
6
|
+
interactiveai-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
interactiveai
|