dhisana 0.0.1.dev4__tar.gz → 0.0.1.dev5__tar.gz

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.
Files changed (30) hide show
  1. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/PKG-INFO +1 -1
  2. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/setup.py +1 -1
  3. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/ui/components.py +1 -1
  4. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/openai_helpers.py +51 -39
  5. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana.egg-info/PKG-INFO +1 -1
  6. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/README.md +0 -0
  7. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/pyproject.toml +0 -0
  8. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/setup.cfg +0 -0
  9. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/__init__.py +0 -0
  10. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/cli/__init__.py +0 -0
  11. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/cli/cli.py +0 -0
  12. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/cli/datasets.py +0 -0
  13. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/cli/models.py +0 -0
  14. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/cli/predictions.py +0 -0
  15. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/ui/__init__.py +0 -0
  16. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/__init__.py +0 -0
  17. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/agent_tools.py +0 -0
  18. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/assistant_tool_tag.py +0 -0
  19. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
  20. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
  21. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
  22. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
  23. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
  24. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana/utils/tools_json.py +0 -0
  25. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana.egg-info/SOURCES.txt +0 -0
  26. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana.egg-info/dependency_links.txt +0 -0
  27. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana.egg-info/entry_points.txt +0 -0
  28. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana.egg-info/requires.txt +0 -0
  29. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/src/dhisana.egg-info/top_level.txt +0 -0
  30. {dhisana-0.0.1.dev4 → dhisana-0.0.1.dev5}/tests/test_agent_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dhisana
3
- Version: 0.0.1.dev4
3
+ Version: 0.0.1.dev5
4
4
  Summary: A Python SDK for Dhisana AI Platform
5
5
  Home-page: https://github.com/dhisana-ai/dhisana-python-sdk
6
6
  Author: Admin
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='dhisana',
5
- version='0.0.1-dev4',
5
+ version='0.0.1-dev5',
6
6
  description='A Python SDK for Dhisana AI Platform',
7
7
  author='Admin',
8
8
  author_email='contact@dhisana.ai',
@@ -110,7 +110,7 @@ class Chart(Component):
110
110
 
111
111
 
112
112
  class Form(Component):
113
- def __init__(self, children: List[Component], on_submit: str):
113
+ def __init__(self, children: List[Component], on_submit: List[str]):
114
114
  self.children = children
115
115
  self.on_submit = on_submit
116
116
 
@@ -58,6 +58,8 @@ async def run_assistant(client, assistant, thread, prompt, response_type, allowe
58
58
  while run.status == 'requires_action':
59
59
  if iteration_count >= max_iterations:
60
60
  print("Exceeded maximum number of iterations for requires_action.")
61
+ # Cancel the run before returning
62
+ await client.beta.threads.runs.cancel(run_id=run.id)
61
63
  return "Error: Exceeded maximum number of iterations for requires_action."
62
64
 
63
65
  tool_outputs = await handle_required_action(run)
@@ -69,6 +71,7 @@ async def run_assistant(client, assistant, thread, prompt, response_type, allowe
69
71
  return await handle_run_completion(client, thread, run)
70
72
 
71
73
 
74
+
72
75
  async def send_initial_message(client, thread, prompt):
73
76
  client.beta.threads.messages.create(
74
77
  thread_id=thread.id,
@@ -103,6 +106,7 @@ async def handle_required_action(run):
103
106
  tool_outputs = []
104
107
  current_batch_size = 0
105
108
  max_batch_size = 256 * 1024 # 256 KB
109
+ print(f"Handling required action")
106
110
 
107
111
  if hasattr(run, 'required_action') and hasattr(run.required_action, 'submit_tool_outputs'):
108
112
  for tool in run.required_action.submit_tool_outputs.tool_calls:
@@ -243,24 +247,19 @@ async def process_agent_request(row_batch: List[Dict], workflow: Dict, custom_in
243
247
  model="gpt-4o-2024-08-06"
244
248
  )
245
249
  thread = client.beta.threads.create()
246
-
247
250
  parsed_outputs = []
248
- for row in row_batch:
249
- try:
250
- task_outputs = {} # Dictionary to store outputs of tasks
251
- input_list = {}
252
- input_list['initial_input_list'] = {
253
- "data": [row],
254
- "format": "list"
255
- }
256
- task_outputs['initial_input'] = input_list
257
- for task in workflow['tasks']:
258
- # Process each task
259
- task_outputs = await process_task(client, assistant, thread, row, task, task_outputs)
260
- # Collect the final output
261
- parsed_outputs.append(task_outputs)
262
- except Exception as e:
263
- print(f"Error processing row {row}: {e}")
251
+ task_outputs = {} # Dictionary to store outputs of tasks
252
+ input_list = {}
253
+ input_list['initial_input_list'] = {
254
+ "data": row_batch,
255
+ "format": "list"
256
+ }
257
+ task_outputs['initial_input'] = input_list
258
+ for task in workflow['tasks']:
259
+ # Process each task
260
+ task_outputs = await process_task(client, assistant, thread, task, task_outputs)
261
+ # Collect the final output
262
+ parsed_outputs.append(task_outputs)
264
263
  return parsed_outputs
265
264
  except Exception as e:
266
265
  print(f"An error occurred: {e}")
@@ -272,24 +271,20 @@ async def process_agent_request(row_batch: List[Dict], workflow: Dict, custom_in
272
271
  print(f"Error deleting assistant: {e}")
273
272
 
274
273
 
275
- async def process_task(client, assistant, thread, row, task, task_outputs):
274
+ async def process_task(client, assistant, thread, task, task_outputs):
276
275
  """
277
276
  Process a single task in the workflow.
278
277
  """
279
- try:
280
- # Prepare inputs
281
- task_inputs = await prepare_task_inputs(row, task, task_outputs)
278
+ # Prepare inputs
279
+ task_inputs = await prepare_task_inputs(task, task_outputs)
282
280
 
283
- # Run the operation
284
- output = await run_task_operation(client, assistant, thread, task, task_inputs)
281
+ # Run the operation
282
+ output = await run_task_operation(client, assistant, thread, task, task_inputs)
285
283
 
286
- # Store outputs
287
- await store_task_outputs(task, output, task_outputs)
284
+ # Store outputs
285
+ await store_task_outputs(task, output, task_outputs)
288
286
 
289
- return task_outputs
290
- except Exception as e:
291
- print(f"Error processing task {task['id']}: {e}")
292
- return task_outputs
287
+ return task_outputs
293
288
 
294
289
  async def read_csv_rows(file_path):
295
290
  rows = []
@@ -299,7 +294,7 @@ async def read_csv_rows(file_path):
299
294
  rows.append(row)
300
295
  return rows
301
296
 
302
- async def prepare_task_inputs(row, task, task_outputs):
297
+ async def prepare_task_inputs(task, task_outputs):
303
298
  """
304
299
  Prepare the inputs for a task based on its input specifications.
305
300
  """
@@ -309,15 +304,11 @@ async def prepare_task_inputs(row, task, task_outputs):
309
304
  source = input_spec.get('source', {})
310
305
  source_type = source.get('type', '')
311
306
  format = input_spec.get('format', 'list')
312
- if source_type == 'external':
313
- # External source, get from initial input
314
- input_data = row.get(input_name, row)
315
- elif source_type == 'task_output':
307
+ if source_type == 'task_output':
316
308
  # Get from previous task output
317
309
  task_id = source.get('task_id')
318
310
  output_key = source.get('output_key')
319
- previous_task_output = task_outputs.get(task_id, {})
320
- print(f"Previous task output: {previous_task_output} Output key: {output_key}")
311
+ previous_task_output = task_outputs.get(task_id, {})
321
312
  if isinstance(previous_task_output, dict):
322
313
  output_item = previous_task_output.get(output_key)
323
314
  input_data = output_item['data']
@@ -361,14 +352,17 @@ async def run_task_operation(client, assistant, thread, task, task_inputs):
361
352
  args = operation.get('args', [])
362
353
  # Prepare prompt by substituting inputs
363
354
 
364
- formatted_prompt = prompt_template
355
+
365
356
  for key, value in task_inputs.items():
366
357
  format = value.get('format', 'list')
367
358
  if format == 'list':
368
- for item in value.get('data'):
359
+ items = value.get('data')
360
+ for item in items:
361
+ formatted_prompt = prompt_template
369
362
  formatted_prompt = formatted_prompt.replace(
370
363
  "{{ inputs." + key + " }}", json.dumps(item))
371
364
  # Run assistant with prompt
365
+ print(formatted_prompt)
372
366
  output = await extract_and_structure_data(
373
367
  client, assistant, thread, formatted_prompt, task_inputs, response_type, allowed_tools)
374
368
  outputs.append(output)
@@ -419,7 +413,7 @@ async def store_task_outputs(task, output, task_outputs):
419
413
  local_path = path
420
414
 
421
415
  if dest_type == 'google_drive':
422
- local_path = os.path.join('/tmp', path)
416
+ local_path = os.path.join('/tmp', task['id'], path)
423
417
 
424
418
  if dest_type == 'google_drive' or dest_type == 'local_path':
425
419
  directory = os.path.dirname(local_path)
@@ -429,6 +423,9 @@ async def store_task_outputs(task, output, task_outputs):
429
423
  if output.get("format", "") == 'list':
430
424
  data_list = [json.loads(item) for item in output.get("data", [])]
431
425
  if data_list:
426
+ dedup_by = output_spec.get('deduplication_properties', [])
427
+ if dedup_by:
428
+ data_list = deduplicate_list_by_properties(data_list, dedup_by)
432
429
  # Filter headers to include only simple types
433
430
  headers = [key for key, value in data_list[0].items() if isinstance(value, (str, int, float, bool))]
434
431
  writer = csv.DictWriter(file, fieldnames=headers)
@@ -450,6 +447,21 @@ async def store_task_outputs(task, output, task_outputs):
450
447
  # If no outputs are defined, store the output under the task id
451
448
  task_outputs[task['id']] = output
452
449
 
450
+ def deduplicate_list_by_properties(data_list, properties):
451
+ """
452
+ Deduplicate a list of dictionaries by a list of properties in order.
453
+ """
454
+ for property_name in properties:
455
+ seen = set()
456
+ deduplicated_list = []
457
+ for item in data_list:
458
+ value = item.get(property_name)
459
+ if value not in seen:
460
+ seen.add(value)
461
+ deduplicated_list.append(item)
462
+ data_list = deduplicated_list
463
+ return data_list
464
+
453
465
  async def write_to_google_drive(cloud_path, local_path):
454
466
  # Placeholder function for writing to Google Drive
455
467
  await write_content_to_googledrive(cloud_path, local_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dhisana
3
- Version: 0.0.1.dev4
3
+ Version: 0.0.1.dev5
4
4
  Summary: A Python SDK for Dhisana AI Platform
5
5
  Home-page: https://github.com/dhisana-ai/dhisana-python-sdk
6
6
  Author: Admin
File without changes
File without changes