dhisana 0.0.1.dev270__py3-none-any.whl → 0.0.1.dev271__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.
@@ -320,6 +320,9 @@ async def _get_structured_output_with_web_search(
320
320
  Handles structured output with web search using Google search and URL scraping tools.
321
321
  Works with both OpenAI and Azure OpenAI.
322
322
  """
323
+ logging.info(f"[WebSearch] Starting web search structured output: model={model}, effort={effort}")
324
+ logging.debug(f"[WebSearch] Prompt length: {len(prompt)} chars")
325
+
323
326
  tools = [SEARCH_GOOGLE_TOOL, FETCH_URL_CONTENT_TOOL]
324
327
 
325
328
  system_content = (
@@ -342,6 +345,7 @@ async def _get_structured_output_with_web_search(
342
345
 
343
346
  while tool_iteration < max_tool_iterations:
344
347
  tool_iteration += 1
348
+ logging.info(f"[WebSearch] Tool iteration {tool_iteration}/{max_tool_iterations}, conversation_history length: {len(conversation_history)}")
345
349
 
346
350
  # Build request with current conversation history
347
351
  request = {
@@ -358,7 +362,9 @@ async def _get_structured_output_with_web_search(
358
362
  # Retry logic for rate limits
359
363
  for attempt in range(2):
360
364
  try:
365
+ logging.debug(f"[WebSearch] Sending request attempt {attempt + 1}")
361
366
  completion = await client_async.responses.create(**request)
367
+ logging.info(f"[WebSearch] Response received, output items: {len(completion.output) if completion and completion.output else 0}")
362
368
  break
363
369
  except (RateLimitError, OpenAIError) as e:
364
370
  is_rl = (
@@ -368,99 +374,127 @@ async def _get_structured_output_with_web_search(
368
374
  )
369
375
  if attempt == 0 and is_rl:
370
376
  wait_time = 20 + random.uniform(0, 2.0)
371
- logging.warning(f"Rate-limit hit (429). Waiting {wait_time:.2f}s then retrying.")
377
+ logging.warning(f"[WebSearch] Rate-limit hit (429). Waiting {wait_time:.2f}s then retrying.")
372
378
  await asyncio.sleep(wait_time)
373
379
  continue
374
- logging.error(f"OpenAI API error: {e}")
375
- raise HTTPException(status_code=502, detail="Error communicating with the OpenAI API.")
380
+ logging.error(f"[WebSearch] OpenAI API error: {e}")
381
+ raise HTTPException(status_code=502, detail=f"Error communicating with the OpenAI API: {str(e)}")
376
382
 
377
383
  if not completion:
384
+ logging.error("[WebSearch] No completion returned after retries")
378
385
  raise HTTPException(status_code=502, detail="OpenAI request failed.")
379
386
 
380
387
  # Check for function tool calls in the response
381
388
  tool_calls = []
382
389
  for item in (completion.output or []):
383
390
  item_type = getattr(item, "type", None)
391
+ logging.debug(f"[WebSearch] Output item type: {item_type}")
384
392
  if item_type == "function_call":
385
393
  tool_calls.append(item)
386
394
 
387
395
  if not tool_calls:
388
396
  # No tool calls, we have the final response
397
+ logging.info(f"[WebSearch] No tool calls in iteration {tool_iteration}, returning final response")
389
398
  break
390
399
 
391
- # Add the model's complete output to conversation history (includes function_call items)
392
- # Per OpenAI docs: "input_list += response.output"
393
- for item in (completion.output or []):
394
- conversation_history.append(item)
395
-
396
400
  # Execute tool calls and add results to conversation history
397
- logging.info(f"Processing {len(tool_calls)} web search tool call(s) in iteration {tool_iteration}")
401
+ # Note: With store=False, we can't append raw output items (they have IDs that Azure
402
+ # can't resolve). We must create clean dicts with only the required fields.
403
+ logging.info(f"[WebSearch] Processing {len(tool_calls)} web search tool call(s) in iteration {tool_iteration}")
398
404
 
399
405
  for tc in tool_calls:
400
406
  func_name = getattr(tc, "name", "")
401
407
  call_id = getattr(tc, "call_id", "")
402
408
  args_str = getattr(tc, "arguments", "{}")
403
409
 
410
+ logging.info(f"[WebSearch] Tool call: {func_name}, call_id: {call_id}, args: {args_str[:200]}...")
411
+
404
412
  try:
405
413
  args = json.loads(args_str) if args_str else {}
406
- except json.JSONDecodeError:
414
+ except json.JSONDecodeError as e:
415
+ logging.warning(f"[WebSearch] Failed to parse tool arguments: {e}")
407
416
  args = {}
408
417
 
418
+ # Add the function_call to conversation history as a clean dict (no id field)
419
+ # Per OpenAI docs: function_call items need type, call_id, name, arguments
420
+ conversation_history.append({
421
+ "type": "function_call",
422
+ "call_id": call_id,
423
+ "name": func_name,
424
+ "arguments": args_str,
425
+ })
426
+
409
427
  # Execute the tool
410
428
  tool_result = await _execute_web_search_tool(func_name, args, tool_config)
411
429
 
412
- # Add tool result to conversation history (only function_call_output, not function_call)
430
+ # Add tool result to conversation history
413
431
  conversation_history.append({
414
432
  "type": "function_call_output",
415
433
  "call_id": call_id,
416
434
  "output": tool_result,
417
435
  })
418
436
 
419
- logging.info(f"Executed web search tool {func_name}, result length: {len(tool_result)}")
437
+ logging.info(f"[WebSearch] Executed tool {func_name}, result length: {len(tool_result)}")
438
+
439
+ logging.info(f"[WebSearch] Tool loop completed after {tool_iteration} iteration(s)")
420
440
 
421
441
  # Parse and return the final response
422
- return _parse_completion_response(completion, response_format, cache_key)
442
+ result, status = _parse_completion_response(completion, response_format, cache_key)
443
+ logging.info(f"[WebSearch] Parse result status: {status}")
444
+ return result, status
423
445
 
424
446
 
425
447
  def _parse_completion_response(completion, response_format: BaseModel, cache_key: str):
426
448
  """Parse completion response and return structured output."""
449
+ logging.debug(f"[ParseResponse] Parsing completion, has output: {bool(completion and completion.output)}")
450
+
427
451
  if completion and completion.output and len(completion.output) > 0:
452
+ logging.debug(f"[ParseResponse] Output items count: {len(completion.output)}")
428
453
  raw_text = None
429
454
  for out in completion.output:
455
+ logging.debug(f"[ParseResponse] Checking output item type: {out.type}")
430
456
  if out.type == "message" and out.content:
431
457
  for content_item in out.content:
432
458
  if hasattr(content_item, "text"):
433
459
  raw_text = content_item.text
460
+ logging.debug(f"[ParseResponse] Found text content, length: {len(raw_text) if raw_text else 0}")
434
461
  break
435
462
  else:
436
- logging.warning("request refused: %s", str(content_item))
463
+ logging.warning("[ParseResponse] Request refused: %s", str(content_item))
437
464
  return "Request refused.", "FAIL"
438
465
  if raw_text:
439
466
  break
440
467
 
441
468
  if not raw_text or not raw_text.strip():
469
+ logging.warning("[ParseResponse] No text returned (possibly refusal or empty response)")
442
470
  return "No text returned (possibly refusal or empty response)", "FAIL"
443
471
 
472
+ logging.debug(f"[ParseResponse] Raw text (first 500 chars): {raw_text[:500] if raw_text else 'None'}...")
473
+
444
474
  try:
445
475
  parsed_obj = response_format.parse_raw(raw_text)
446
476
  cache_output_tools.cache_output(
447
477
  "get_structured_output_internal", cache_key, parsed_obj.json()
448
478
  )
479
+ logging.info("[ParseResponse] Successfully parsed response")
449
480
  return parsed_obj, "SUCCESS"
450
481
 
451
- except Exception:
452
- logging.warning("ERROR: Could not parse JSON from model output.")
482
+ except Exception as e:
483
+ logging.warning(f"[ParseResponse] Could not parse JSON from model output: {e}")
453
484
  try:
454
485
  fixed_json = repair_json(raw_text)
455
486
  parsed_obj = response_format.parse_raw(fixed_json)
456
487
  cache_output_tools.cache_output(
457
488
  "get_structured_output_internal", cache_key, parsed_obj.json()
458
489
  )
490
+ logging.info("[ParseResponse] Successfully parsed response after JSON repair")
459
491
  return parsed_obj, "SUCCESS"
460
492
  except Exception as e2:
461
- logging.warning("JSON repair failed: %s", str(e2))
493
+ logging.warning(f"[ParseResponse] JSON repair also failed: {e2}")
494
+ logging.debug(f"[ParseResponse] Raw text that failed parsing: {raw_text[:1000] if raw_text else 'None'}...")
462
495
  return raw_text, "FAIL"
463
496
  else:
497
+ logging.warning("[ParseResponse] No output returned from completion")
464
498
  return "No output returned", "FAIL"
465
499
 
466
500
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhisana
3
- Version: 0.0.1.dev270
3
+ Version: 0.0.1.dev271
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
@@ -45,7 +45,7 @@ dhisana/utils/generate_flow.py,sha256=QMn6bWo0nH0fBvy2Ebub1XfH5udnVAqsPsbIqCtQPX
45
45
  dhisana/utils/generate_leads_salesnav.py,sha256=FG7q6GSm9IywZ9TgQnn5_N3QNfiI-Qk2gaO_3GS99nY,12236
46
46
  dhisana/utils/generate_linkedin_connect_message.py,sha256=QxsxDiT-3eQOqAAbW13d0HGJXV36WYPvC-7Zsw_2VTI,10208
47
47
  dhisana/utils/generate_linkedin_response_message.py,sha256=mWoSs5p2JSTIoFZFGm86x1kgs67J7dHPvGKZPzcdGdU,14569
48
- dhisana/utils/generate_structured_output_internal.py,sha256=G9eqgT5SYI60rlOi6qQTndtsUpaLDuNM509limO8Zfs,31061
48
+ dhisana/utils/generate_structured_output_internal.py,sha256=gJ6Bdwz2VexL505crMOgYYHOhmZo0MCghRoDkLP62WM,33654
49
49
  dhisana/utils/google_custom_search.py,sha256=5rQ4uAF-hjFpd9ooJkd6CjRvSmhZHhqM0jfHItsbpzk,10071
50
50
  dhisana/utils/google_oauth_tools.py,sha256=ReG5lCpXL3_e_s0yn6ai4U7B4-feOWHJVtbv_c0g0rE,28525
51
51
  dhisana/utils/google_workspace_tools.py,sha256=fuV0UcvAqF9drLzj7-p6D5zh7d5jMXl1jNJTICk4XOo,50224
@@ -95,8 +95,8 @@ dhisana/workflow/agent.py,sha256=esv7_i_XuMkV2j1nz_UlsHov_m6X5WZZiZm_tG4OBHU,565
95
95
  dhisana/workflow/flow.py,sha256=xWE3qQbM7j2B3FH8XnY3zOL_QXX4LbTW4ArndnEYJE0,1638
96
96
  dhisana/workflow/task.py,sha256=HlWz9mtrwLYByoSnePOemBUBrMEcj7KbgNjEE1oF5wo,1830
97
97
  dhisana/workflow/test.py,sha256=E7lRnXK0PguTNzyasHytLzTJdkqIPxG5_4qk4hMEeKc,3399
98
- dhisana-0.0.1.dev270.dist-info/METADATA,sha256=P7vlLN7X5NZFyM-Wzgqrdo20B69wmKjLpQWxlF-WS4o,1190
99
- dhisana-0.0.1.dev270.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
100
- dhisana-0.0.1.dev270.dist-info/entry_points.txt,sha256=jujxteZmNI9EkEaK-pOCoWuBujU8TCevdkfl9ZcKHek,49
101
- dhisana-0.0.1.dev270.dist-info/top_level.txt,sha256=NETTHt6YifG_P7XtRHbQiXZlgSFk9Qh9aR-ng1XTf4s,8
102
- dhisana-0.0.1.dev270.dist-info/RECORD,,
98
+ dhisana-0.0.1.dev271.dist-info/METADATA,sha256=t746AJjraGpmdydXXdzST-85-XvYPv2WueN1RfXINm8,1190
99
+ dhisana-0.0.1.dev271.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
100
+ dhisana-0.0.1.dev271.dist-info/entry_points.txt,sha256=jujxteZmNI9EkEaK-pOCoWuBujU8TCevdkfl9ZcKHek,49
101
+ dhisana-0.0.1.dev271.dist-info/top_level.txt,sha256=NETTHt6YifG_P7XtRHbQiXZlgSFk9Qh9aR-ng1XTf4s,8
102
+ dhisana-0.0.1.dev271.dist-info/RECORD,,