sunholo 0.136.2__py3-none-any.whl → 0.137.0__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.
@@ -486,35 +486,53 @@ class DiscoveryEngineClient:
486
486
  """Process a search response containing documents into a formatted string."""
487
487
  all_documents = []
488
488
  result_count = 0
489
+
489
490
  # Check if the response contains results
490
491
  if not response or not hasattr(response, 'results') or not response.results:
491
492
  log.info(f'No results found in response: {response=}')
492
493
  return []
493
494
 
494
- should_break=False
495
- # Process the pager properly
496
- for page in response.pages:
497
- if should_break:
498
- break
499
- if hasattr(page, 'results') and page.results:
500
- for result in page.results:
501
- if result_count >= max_limit:
502
- log.info("Breaking results loop as max limit reached")
503
- should_break = True # Set flag to break outer loop
504
- break
505
-
506
- if hasattr(result, 'document'):
507
- document = result.document
508
- all_documents.append(self.document_format(document))
509
- result_count += 1
510
-
511
- # Check if we've reached max_limit
512
- if max_limit is not None and result_count >= max_limit:
513
- log.info(f"Reached max_limit of {max_limit} results, stopping processing")
514
- should_break = True
515
- break
516
- else:
517
- log.warning("No document found in result")
495
+ # Simpler approach: Just process the direct results from the response
496
+ # This handles the case where it's just a simple SearchResponse
497
+ for result in response.results:
498
+ if hasattr(result, 'document'):
499
+ document = result.document
500
+ all_documents.append(self.document_format(document))
501
+ result_count += 1
502
+
503
+ # Check if we've reached max_limit
504
+ if max_limit is not None and result_count >= max_limit:
505
+ log.info(f"Reached max_limit of {max_limit} results, stopping processing")
506
+ break
507
+ else:
508
+ log.warning("No document found in result")
509
+
510
+ # If there are pages, check gently without assuming too much about the structure
511
+ # Only process if we haven't already hit the max_limit
512
+ if (max_limit is None or result_count < max_limit) and hasattr(response, 'pages'):
513
+ try:
514
+ # For each page (after the first which we've already processed)
515
+ for page in list(response.pages)[1:]: # Skip first page as we already processed it
516
+ if hasattr(page, 'results'):
517
+ for result in page.results:
518
+ if hasattr(result, 'document'):
519
+ document = result.document
520
+ all_documents.append(self.document_format(document))
521
+ result_count += 1
522
+
523
+ # Check if we've reached max_limit
524
+ if max_limit is not None and result_count >= max_limit:
525
+ log.info(f"Reached max_limit of {max_limit} results while processing pages")
526
+ break
527
+ else:
528
+ log.warning("No document found in result during page processing")
529
+
530
+ # Break out of the page loop if we've reached max_limit
531
+ if max_limit is not None and result_count >= max_limit:
532
+ break
533
+ except Exception as e:
534
+ # Don't let page iteration issues break the whole function
535
+ log.warning(f"Error during page iteration, proceeding with results already collected: {e}")
518
536
 
519
537
  # Combine all documents into one long string
520
538
  result_string = "\n\n".join(all_documents)
@@ -531,30 +549,52 @@ class DiscoveryEngineClient:
531
549
  log.info(f'No results found in response: {response=}')
532
550
  return []
533
551
 
534
- should_break=False
535
- # Process the pager properly
536
- async for page in response.pages:
537
- if should_break:
538
- break
539
- if hasattr(page, 'results') and page.results:
540
- for result in page.results:
541
- if result_count >= max_limit:
542
- log.info("Breaking results loop as max limit reached")
543
- should_break = True # Set flag to break outer loop
544
- break
545
-
546
- if hasattr(result, 'document'):
547
- document = result.document
548
- all_documents.append(self.document_format(document))
549
- result_count += 1
550
-
551
- # Check if we've reached max_limit
552
- if max_limit is not None and result_count >= max_limit:
553
- log.info(f"Reached max_limit of {max_limit} results, stopping processing")
554
- should_break = True
555
- break
556
- else:
557
- log.warning("No document found in result")
552
+ # Simpler approach: Just process the direct results from the response
553
+ # This handles the case where it's just a simple SearchResponse
554
+ for result in response.results:
555
+ if hasattr(result, 'document'):
556
+ document = result.document
557
+ all_documents.append(self.document_format(document))
558
+ result_count += 1
559
+
560
+ # Check if we've reached max_limit
561
+ if max_limit is not None and result_count >= max_limit:
562
+ log.info(f"Reached max_limit of {max_limit} results, stopping processing")
563
+ break
564
+ else:
565
+ log.warning("No document found in result")
566
+
567
+ # If there are pages, check gently without assuming too much about the structure
568
+ # Only process if we haven't already hit the max_limit
569
+ if (max_limit is None or result_count < max_limit) and hasattr(response, 'pages'):
570
+ try:
571
+ # Since pages is an async iterator, we need special handling
572
+ first_page = True # Flag to skip first page (already processed above)
573
+ async for page in response.pages:
574
+ if first_page:
575
+ first_page = False
576
+ continue # Skip first page as we already processed it
577
+
578
+ if hasattr(page, 'results'):
579
+ for result in page.results:
580
+ if hasattr(result, 'document'):
581
+ document = result.document
582
+ all_documents.append(self.document_format(document))
583
+ result_count += 1
584
+
585
+ # Check if we've reached max_limit
586
+ if max_limit is not None and result_count >= max_limit:
587
+ log.info(f"Reached max_limit of {max_limit} results while processing pages")
588
+ break
589
+ else:
590
+ log.warning("No document found in result during page processing")
591
+
592
+ # Break out of the page loop if we've reached max_limit
593
+ if max_limit is not None and result_count >= max_limit:
594
+ break
595
+ except Exception as e:
596
+ # Don't let page iteration issues break the whole function
597
+ log.warning(f"Error during async page iteration, proceeding with results already collected: {e}")
558
598
 
559
599
  # Combine all documents into one long string
560
600
  result_string = "\n\n".join(all_documents)
@@ -8,11 +8,12 @@ from tenacity import AsyncRetrying, retry_if_exception_type, wait_random_exponen
8
8
  log = setup_logging("sunholo_AsyncTaskRunner")
9
9
 
10
10
  class AsyncTaskRunner:
11
- def __init__(self, retry_enabled=False, retry_kwargs=None, timeout=120):
11
+ def __init__(self, retry_enabled:bool=False, retry_kwargs:dict=None, timeout:int=120, max_concurrency:int=20):
12
12
  self.tasks = []
13
13
  self.retry_enabled = retry_enabled
14
14
  self.retry_kwargs = retry_kwargs or {}
15
15
  self.timeout = timeout
16
+ self.semaphore = asyncio.Semaphore(max_concurrency)
16
17
 
17
18
  def add_task(self, func: Callable[..., Any], *args: Any, **kwargs: Any):
18
19
  """
@@ -136,10 +137,11 @@ class AsyncTaskRunner:
136
137
  Returns:
137
138
  Any: The result of the task.
138
139
  """
139
- if asyncio.iscoroutinefunction(func):
140
- return await func(*args, **kwargs)
141
- else:
142
- return await asyncio.to_thread(func, *args, **kwargs)
140
+ async with self.semaphore: # Use semaphore to limit concurrent executions
141
+ if asyncio.iscoroutinefunction(func):
142
+ return await func(*args, **kwargs)
143
+ else:
144
+ return await asyncio.to_thread(func, *args, **kwargs)
143
145
 
144
146
  async def _send_heartbeat(self, func_name: str, completion_event: asyncio.Event, queue: asyncio.Queue, interval: int = 2):
145
147
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.136.2
3
+ Version: 0.137.0
4
4
  Summary: AI DevOps - a package to help deploy GenAI to the Cloud.
5
5
  Author-email: Holosun ApS <multivac@sunholo.com>
6
6
  License: Apache License, Version 2.0
@@ -75,7 +75,7 @@ sunholo/discovery_engine/__init__.py,sha256=hLgqRDJ22Aov9o2QjAEfsVgnL3kMdM-g5p8R
75
75
  sunholo/discovery_engine/chunker_handler.py,sha256=wkvXl4rFtYfN6AZUKdW9_QD49Whf77BukDbO82UwlAg,7480
76
76
  sunholo/discovery_engine/cli.py,sha256=tsKqNSDCEsDTz5-wuNwjttb3Xt35D97-KyyEiaqolMQ,35628
77
77
  sunholo/discovery_engine/create_new.py,sha256=WUi4_xh_dFaGX3xA9jkNKZhaR6LCELjMPeRb0hyj4FU,1226
78
- sunholo/discovery_engine/discovery_engine_client.py,sha256=f-KjBzS5ILMDWLBF-ozRynKCjxGi7Mm3rRNnynlhWtY,60822
78
+ sunholo/discovery_engine/discovery_engine_client.py,sha256=VSInta9IZE_LmA3CFYqxLpxdoB7w0IuSRSFM2UnmrRk,63705
79
79
  sunholo/discovery_engine/get_ai_search_chunks.py,sha256=I6Dt1CznqEvE7XIZ2PkLqopmjpO96iVEWJJqL5cJjOU,5554
80
80
  sunholo/embedder/__init__.py,sha256=sI4N_CqgEVcrMDxXgxKp1FsfsB4FpjoXgPGkl4N_u4I,44
81
81
  sunholo/embedder/embed_chunk.py,sha256=did2pKkWM2o0KkRcb0H9l2x_WjCq6OyuHDxGbITFKPM,6530
@@ -96,7 +96,7 @@ sunholo/genai/init.py,sha256=yG8E67TduFCTQPELo83OJuWfjwTnGZsyACospahyEaY,687
96
96
  sunholo/genai/process_funcs_cls.py,sha256=D6eNrc3vtTZzwdkacZNOSfit499N_o0C5AHspyUJiYE,33690
97
97
  sunholo/genai/safety.py,sha256=mkFDO_BeEgiKjQd9o2I4UxB6XI7a9U-oOFjZ8LGRUC4,1238
98
98
  sunholo/invoke/__init__.py,sha256=o1RhwBGOtVK0MIdD55fAIMCkJsxTksi8GD5uoqVKI-8,184
99
- sunholo/invoke/async_class.py,sha256=G8vD2H94fpBc37mSJSQODEKJ67P2mPQEHabtDaLOvxE,8033
99
+ sunholo/invoke/async_class.py,sha256=OmRHaRf_yXNDWftWPpziytXh1TAhMumxnNhN_PGpQFs,8230
100
100
  sunholo/invoke/direct_vac_func.py,sha256=dACx3Zh7uZnuWLIFYiyLoyXUhh5-eUpd2RatDUd9ov8,9753
101
101
  sunholo/invoke/invoke_vac_utils.py,sha256=sJc1edHTHMzMGXjji1N67c3iUaP7BmAL5nj82Qof63M,2053
102
102
  sunholo/langfuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -168,9 +168,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
168
168
  sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
169
169
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
170
170
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
171
- sunholo-0.136.2.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
172
- sunholo-0.136.2.dist-info/METADATA,sha256=3RZ3YKpfmyWWwg2yUp8ayE4a2ECu47f9Kp35IV1MjyM,10067
173
- sunholo-0.136.2.dist-info/WHEEL,sha256=ooBFpIzZCPdw3uqIQsOo4qqbA4ZRPxHnOH7peeONza0,91
174
- sunholo-0.136.2.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
175
- sunholo-0.136.2.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
176
- sunholo-0.136.2.dist-info/RECORD,,
171
+ sunholo-0.137.0.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
172
+ sunholo-0.137.0.dist-info/METADATA,sha256=N8GrL3vKpaLx1WfUXOTSlI0I1o0StuJ88DsDL1kkKaE,10067
173
+ sunholo-0.137.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
174
+ sunholo-0.137.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
175
+ sunholo-0.137.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
176
+ sunholo-0.137.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.0.1)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5