crewplus 0.2.58__py3-none-any.whl → 0.2.60__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.

Potentially problematic release.


This version of crewplus might be problematic. Click here for more details.

@@ -1,6 +1,7 @@
1
- import os
2
1
  import logging
2
+ import os
3
3
  from typing import Optional
4
+
4
5
  from .model_load_balancer import ModelLoadBalancer
5
6
 
6
7
  model_balancer = None
@@ -26,9 +27,13 @@ def init_load_balancer(
26
27
  global model_balancer
27
28
  if model_balancer is None:
28
29
  # Use parameter if provided, otherwise check env var, then default
30
+ current_dir = os.path.dirname(os.path.abspath(__file__))
31
+ base_package_dir = os.path.dirname(os.path.dirname(current_dir))
32
+ default_config_path = os.path.join(base_package_dir, "_config", "models_config.json")
33
+
29
34
  final_config_path = config_path or os.getenv(
30
- "MODEL_CONFIG_PATH",
31
- "config/models_config.json"
35
+ "MODEL_CONFIG_PATH",
36
+ default_config_path
32
37
  )
33
38
  try:
34
39
  # 1. Create a local instance first.
@@ -234,7 +234,7 @@ class VDBService(object):
234
234
  return AsyncMilvusClient(**client_args)
235
235
  except Exception as e:
236
236
  self.logger.error(f"Failed to initialize AsyncMilvusClient, trying again. Error: {e}")
237
- # Second attempt after failure
237
+ time.sleep(1) # sync sleep is fine, we are in a thread
238
238
  try:
239
239
  return AsyncMilvusClient(**client_args)
240
240
  except Exception as e_retry:
@@ -254,18 +254,14 @@ class VDBService(object):
254
254
 
255
255
  return self._client
256
256
 
257
- def get_async_vector_client(self) -> AsyncMilvusClient:
257
+ async def aget_async_vector_client(self) -> AsyncMilvusClient:
258
258
  """
259
- Returns the active AsyncMilvusClient instance, initializing it if necessary.
259
+ Asynchronously returns the active AsyncMilvusClient instance, initializing it if necessary.
260
260
 
261
261
  Returns:
262
262
  AsyncMilvusClient: The initialized async client for interacting with the vector database.
263
263
  """
264
- if self._async_client is None:
265
- self.logger.debug("Initializing asynchronous AsyncMilvusClient...")
266
- self._async_client = self._initialize_async_milvus_client(self._provider)
267
-
268
- return self._async_client
264
+ return await self._get_or_create_async_client()
269
265
 
270
266
  def get_vector_field(self, collection_name: str) -> str:
271
267
  """
@@ -374,7 +370,7 @@ class VDBService(object):
374
370
  Asynchronously checks if a collection exists and creates it if it doesn't.
375
371
  """
376
372
  try:
377
- client = self.get_async_vector_client()
373
+ client = await self.aget_async_vector_client()
378
374
  if check_existence and not await client.has_collection(collection_name):
379
375
  self.logger.info(f"Collection '{collection_name}' does not exist. Creating it.")
380
376
 
@@ -512,6 +508,35 @@ class VDBService(object):
512
508
 
513
509
  return vdb
514
510
 
511
+ async def _get_or_create_async_client(self) -> AsyncMilvusClient:
512
+ """
513
+ Lazily initializes and returns the AsyncMilvusClient.
514
+ This runs the blocking constructor in a separate thread, but also creates
515
+ a temporary event loop inside that thread to satisfy the client's
516
+ initialization requirements.
517
+ """
518
+ if self._async_client is None:
519
+ self.logger.info("Lazily initializing AsyncMilvusClient...")
520
+
521
+ def _create_with_loop():
522
+ # This function runs in a separate thread via asyncio.to_thread
523
+ try:
524
+ # Check if an event loop exists in this new thread
525
+ asyncio.get_running_loop()
526
+ except RuntimeError: # 'RuntimeError: There is no current event loop...'
527
+ # If not, create and set a new one
528
+ loop = asyncio.new_event_loop()
529
+ asyncio.set_event_loop(loop)
530
+
531
+ # Now, with an event loop present in this thread, initialize the client.
532
+ # This is still a blocking call, but it's contained in the thread.
533
+ provider = self.settings.get("vector_store", {}).get("provider")
534
+ return self._initialize_async_milvus_client(provider)
535
+
536
+ self._async_client = await asyncio.to_thread(_create_with_loop)
537
+
538
+ return self._async_client
539
+
515
540
  async def aget_vector_store(self, collection_name: str, embeddings: Embeddings = None, metric_type: str = "IP") -> Milvus:
516
541
  """
517
542
  Asynchronously gets a vector store instance, creating it if it doesn't exist.
@@ -539,12 +564,12 @@ class VDBService(object):
539
564
  if embeddings is None:
540
565
  embeddings = self.get_embeddings()
541
566
 
542
- await self._aensure_collection_exists(collection_name, embeddings, check_existence=check_existence)
567
+ # await self._aensure_collection_exists(collection_name, embeddings, check_existence=check_existence)
543
568
 
544
569
  try:
545
- self.logger.debug(f"Testing embedding function for collection '{collection_name}'...")
570
+ self.logger.info(f"Testing embedding function for collection '{collection_name}'...")
546
571
  await embeddings.aembed_query("validation_test_string")
547
- self.logger.debug("Embedding function is valid.")
572
+ self.logger.info("Embedding function is valid.")
548
573
  except Exception as e:
549
574
  self.logger.error(
550
575
  f"The provided embedding function is invalid and failed with error: {e}. "
@@ -558,24 +583,67 @@ class VDBService(object):
558
583
  "params": {}
559
584
  }
560
585
 
586
+ # Create a dedicated connection_args for the async path with the correct alias
587
+ async_conn_args = self.connection_args.copy()
588
+ async_conn_args['alias'] = self.async_alias
589
+
561
590
  # For async operations, we MUST instantiate the Milvus object using the SYNCHRONOUS alias
562
- # because its __init__ method is synchronous.
563
- vdb = self._create_milvus_instance_with_retry(
591
+ # because its __init__ method is synchronous. This is now done in a separate thread.
592
+ vdb = await self._acreate_milvus_instance_with_retry(
564
593
  collection_name=collection_name,
565
594
  embeddings=embeddings,
566
595
  index_params=index_params,
567
- connection_args=self.connection_args # This uses the sync_alias by default
596
+ connection_args=async_conn_args # Pass the async-specific connection args
568
597
  )
569
598
 
570
599
  # After successful synchronous initialization, we hot-swap the alias on the
571
600
  # ASYNCHRONOUS client to ensure future async operations use the correct connection.
572
- self.logger.debug(f"Swapping to async alias for instance of collection {collection_name}")
601
+ self.logger.info(f"Swapping to async alias for instance of collection {collection_name}")
602
+ await self._get_or_create_async_client()
573
603
  vdb.aclient._using = self.async_alias
574
604
 
575
605
  self._async_instances[collection_name] = vdb
576
606
 
577
607
  return vdb
578
608
 
609
+ async def _acreate_milvus_instance_with_retry(self, collection_name: str, embeddings: Embeddings, index_params: dict, connection_args: Optional[dict] = None) -> Milvus:
610
+ """
611
+ Asynchronously creates a Milvus instance with a retry mechanism, running the synchronous
612
+ constructor in a separate thread to avoid blocking the event loop.
613
+ """
614
+ retries = 2
615
+ conn_args = connection_args if connection_args is not None else self.connection_args
616
+
617
+ def _create_instance():
618
+ # This synchronous function will be run in a thread
619
+ return Milvus(
620
+ embedding_function=embeddings,
621
+ collection_name=collection_name,
622
+ connection_args=conn_args,
623
+ index_params=index_params
624
+ )
625
+
626
+ self.logger.info(f"Creating Milvus instance for collection '{collection_name}' in a separate thread...")
627
+ self.logger.info(f"Connection args: {conn_args}")
628
+
629
+ for attempt in range(retries + 1):
630
+ try:
631
+ # Run the blocking constructor in a separate thread
632
+ vdb = await asyncio.to_thread(_create_instance)
633
+
634
+ self.logger.info(f"Successfully connected to Milvus for collection '{collection_name}' on attempt {attempt + 1}.")
635
+ return vdb # Return on success
636
+ except Exception as e:
637
+ self.logger.warning(
638
+ f"Attempt {attempt + 1}/{retries + 1} to connect to Milvus for collection '{collection_name}' failed: {e}"
639
+ )
640
+ if attempt < retries:
641
+ self.logger.info("Retrying in 3 seconds...")
642
+ await asyncio.sleep(3) # Use async sleep
643
+ else:
644
+ self.logger.error(f"Failed to connect to Milvus for collection '{collection_name}' after {retries + 1} attempts.")
645
+ raise RuntimeError(f"Could not connect to Milvus after {retries + 1} attempts.") from e
646
+
579
647
  def _create_milvus_instance_with_retry(self, collection_name: str, embeddings: Embeddings, index_params: dict, connection_args: Optional[dict] = None) -> Milvus:
580
648
  """
581
649
  Creates a Milvus instance with a retry mechanism for connection failures.
@@ -651,7 +719,7 @@ class VDBService(object):
651
719
  self.logger.info(f"Attempting to drop collection asynchronously: {collection_name}")
652
720
 
653
721
  try:
654
- client = self.get_async_vector_client()
722
+ client = await self.aget_async_vector_client()
655
723
  await client.drop_collection(collection_name=collection_name)
656
724
  self.logger.info(f"Successfully dropped collection asynchronously: {collection_name}")
657
725
  except Exception as e:
@@ -690,7 +758,7 @@ class VDBService(object):
690
758
  self.logger.info(f"Delete data by filter asynchronously:{filter}")
691
759
 
692
760
  try:
693
- client=self.get_async_vector_client()
761
+ client= await self.aget_async_vector_client()
694
762
  if collection_name is None or client is None or filter is None:
695
763
  return RuntimeError(f"collection_name must be not null or check out your client to link milvus")
696
764
  await client.delete(collection_name=collection_name, filter=filter)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crewplus
3
- Version: 0.2.58
3
+ Version: 0.2.60
4
4
  Summary: Base services for CrewPlus AI applications
5
5
  Author-Email: Tim Liu <tim@opsmateai.com>
6
6
  License: MIT
@@ -15,7 +15,6 @@ Requires-Dist: google-genai>=1.21.1
15
15
  Requires-Dist: langchain-milvus<0.3.0,>=0.2.1
16
16
  Requires-Dist: langfuse<4.0.0,>=3.1.3
17
17
  Requires-Dist: langchain-mcp-adapters>=0.1.4
18
- Requires-Dist: protobuf<6.0,>=5.27
19
18
  Description-Content-Type: text/markdown
20
19
 
21
20
  # CrewPlus
@@ -1,14 +1,14 @@
1
- crewplus-0.2.58.dist-info/METADATA,sha256=bcVORIdlBj4keo_14dBUc9N6UDu9ch2p8JmBXSvFqIk,5459
2
- crewplus-0.2.58.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
- crewplus-0.2.58.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- crewplus-0.2.58.dist-info/licenses/LICENSE,sha256=2_NHSHRTKB_cTcT_GXgcenOCtIZku8j343mOgAguTfc,1087
1
+ crewplus-0.2.60.dist-info/METADATA,sha256=PBmO4kKCatXJ6RnPqFdLqyHtncR3HfDzCgnyly9kcsc,5424
2
+ crewplus-0.2.60.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
+ crewplus-0.2.60.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
+ crewplus-0.2.60.dist-info/licenses/LICENSE,sha256=2_NHSHRTKB_cTcT_GXgcenOCtIZku8j343mOgAguTfc,1087
5
5
  crewplus/__init__.py,sha256=m46HkZL1Y4toD619NL47Sn2Qe084WFFSFD7e6VoYKZc,284
6
6
  crewplus/callbacks/__init__.py,sha256=YG7ieeb91qEjp1zF0-inEN7mjZ7yT_D2yzdWFT8Z1Ws,63
7
7
  crewplus/callbacks/async_langfuse_handler.py,sha256=A4uFeLpvOUdc58M7sZoE65_C1V98u0QCvx5jUquM0pM,7006
8
8
  crewplus/services/__init__.py,sha256=V1CG8b2NOmRzNgQH7BPl4KVxWSYJH5vfEsW1wVErKNE,375
9
9
  crewplus/services/azure_chat_model.py,sha256=iWzJ2GQFSNmwJx-2O5_xKPSB6VVc-7T6bcfFI8_WezA,5521
10
10
  crewplus/services/gemini_chat_model.py,sha256=DYqz01H2TIHiCDQesSozVfOsMigno6QGwOtIweg7UHk,40103
11
- crewplus/services/init_services.py,sha256=7oZ1GmesK32EDB_DYnTzW17MEpXjXK41_U_1pmqu_m4,2183
11
+ crewplus/services/init_services.py,sha256=tc1ti8Yufo2ixlJpwg8uH0KmoyQ4EqxCOe4uTEWnlRM,2413
12
12
  crewplus/services/model_load_balancer.py,sha256=Q9Gx3GrbKworU-Ytxeqp0ggHSgZ1Q6brtTk-nCl4sak,12095
13
13
  crewplus/services/tracing_manager.py,sha256=0KR-F0BKYEMdADANWofFZH9D9jcWDHzDICUE7nDhzJc,6579
14
14
  crewplus/utils/__init__.py,sha256=2Gk1n5srFJQnFfBuYTxktdtKOVZyNrFcNaZKhXk35Pw,142
@@ -17,9 +17,9 @@ crewplus/utils/schema_document_updater.py,sha256=frvffxn2vbi71fHFPoGb9hq7gH2azmm
17
17
  crewplus/vectorstores/milvus/__init__.py,sha256=OeYv2rdyG7tcREIjBJPyt2TbE54NvyeRoWMe7LwopRE,245
18
18
  crewplus/vectorstores/milvus/milvus_schema_manager.py,sha256=-QRav-hzu-XWeJ_yKUMolal_EyMUspSg-nvh5sqlrlQ,11442
19
19
  crewplus/vectorstores/milvus/schema_milvus.py,sha256=wwNpfqsKS0xeozZES40IvB0iNwUtpCall_7Hkg0dL1g,27223
20
- crewplus/vectorstores/milvus/vdb_service.py,sha256=sVDThSfsSq_aISJToU4yWY0nRfm9cOyIpIj_0YeJCXg,35111
20
+ crewplus/vectorstores/milvus/vdb_service.py,sha256=qlmFWO_ZRXIeCGZGSEAlS2ciHhTPTH1Dg2ovovA1ZEY,38792
21
21
  docs/GeminiChatModel.md,sha256=zZYyl6RmjZTUsKxxMiC9O4yV70MC4TD-IGUmWhIDBKA,8677
22
22
  docs/ModelLoadBalancer.md,sha256=aGHES1dcXPz4c7Y8kB5-vsCNJjriH2SWmjBkSGoYKiI,4398
23
23
  docs/VDBService.md,sha256=Dw286Rrf_fsi13jyD3Bo4Sy7nZ_G7tYm7d8MZ2j9hxk,9375
24
24
  docs/index.md,sha256=3tlc15uR8lzFNM5WjdoZLw0Y9o1P1gwgbEnOdIBspqc,1643
25
- crewplus-0.2.58.dist-info/RECORD,,
25
+ crewplus-0.2.60.dist-info/RECORD,,