crewplus 0.2.56__tar.gz → 0.2.59__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.
Potentially problematic release.
This version of crewplus might be problematic. Click here for more details.
- {crewplus-0.2.56 → crewplus-0.2.59}/PKG-INFO +1 -1
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/services/init_services.py +8 -3
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/vectorstores/milvus/vdb_service.py +101 -19
- {crewplus-0.2.56 → crewplus-0.2.59}/pyproject.toml +1 -1
- {crewplus-0.2.56 → crewplus-0.2.59}/LICENSE +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/README.md +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/__init__.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/callbacks/__init__.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/callbacks/async_langfuse_handler.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/services/__init__.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/services/azure_chat_model.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/services/gemini_chat_model.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/services/model_load_balancer.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/services/tracing_manager.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/utils/__init__.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/utils/schema_action.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/utils/schema_document_updater.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/vectorstores/milvus/__init__.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/vectorstores/milvus/milvus_schema_manager.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/crewplus/vectorstores/milvus/schema_milvus.py +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/docs/GeminiChatModel.md +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/docs/ModelLoadBalancer.md +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/docs/VDBService.md +0 -0
- {crewplus-0.2.56 → crewplus-0.2.59}/docs/index.md +0 -0
|
@@ -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
|
-
|
|
35
|
+
"MODEL_CONFIG_PATH",
|
|
36
|
+
default_config_path
|
|
32
37
|
)
|
|
33
38
|
try:
|
|
34
39
|
# 1. Create a local instance first.
|
|
@@ -16,7 +16,7 @@ import uuid
|
|
|
16
16
|
|
|
17
17
|
from ...services.init_services import get_model_balancer
|
|
18
18
|
from .schema_milvus import SchemaMilvus, DEFAULT_SCHEMA
|
|
19
|
-
from .milvus_schema_manager import MilvusSchemaManager
|
|
19
|
+
#from .milvus_schema_manager import MilvusSchemaManager
|
|
20
20
|
|
|
21
21
|
class VDBService(object):
|
|
22
22
|
"""
|
|
@@ -93,7 +93,7 @@ class VDBService(object):
|
|
|
93
93
|
>>> assert vector_store is same_vector_store
|
|
94
94
|
"""
|
|
95
95
|
_client: MilvusClient
|
|
96
|
-
_async_client: AsyncMilvusClient
|
|
96
|
+
_async_client: Optional[AsyncMilvusClient] = None
|
|
97
97
|
_instances: Dict[str, Milvus] = {}
|
|
98
98
|
_async_instances: Dict[str, Milvus] = {}
|
|
99
99
|
|
|
@@ -154,6 +154,8 @@ class VDBService(object):
|
|
|
154
154
|
self.logger.error(msg)
|
|
155
155
|
raise ValueError(msg)
|
|
156
156
|
|
|
157
|
+
self._provider = provider # Store provider for lazy initialization
|
|
158
|
+
|
|
157
159
|
# Create separate aliases for sync and async clients to avoid connection handler race conditions.
|
|
158
160
|
self.sync_alias = f"crewplus-vdb-sync-{uuid.uuid4()}"
|
|
159
161
|
self.async_alias = f"crewplus-vdb-async-{uuid.uuid4()}"
|
|
@@ -163,12 +165,13 @@ class VDBService(object):
|
|
|
163
165
|
self.connection_args['alias'] = self.sync_alias
|
|
164
166
|
|
|
165
167
|
self._client = self._initialize_milvus_client(provider)
|
|
166
|
-
|
|
168
|
+
# lazy-initialize async milvus
|
|
169
|
+
# self._async_client = self._initialize_async_milvus_client(provider)
|
|
167
170
|
|
|
168
171
|
self.schema = schema
|
|
169
172
|
self.index_params = self.settings.get("index_params")
|
|
170
173
|
|
|
171
|
-
self.schema_manager = MilvusSchemaManager(client=self._client, async_client=self._async_client)
|
|
174
|
+
#self.schema_manager = MilvusSchemaManager(client=self._client, async_client=self._async_client)
|
|
172
175
|
|
|
173
176
|
self.logger.info("VDBService initialized successfully")
|
|
174
177
|
|
|
@@ -231,7 +234,7 @@ class VDBService(object):
|
|
|
231
234
|
return AsyncMilvusClient(**client_args)
|
|
232
235
|
except Exception as e:
|
|
233
236
|
self.logger.error(f"Failed to initialize AsyncMilvusClient, trying again. Error: {e}")
|
|
234
|
-
#
|
|
237
|
+
time.sleep(1) # sync sleep is fine, we are in a thread
|
|
235
238
|
try:
|
|
236
239
|
return AsyncMilvusClient(**client_args)
|
|
237
240
|
except Exception as e_retry:
|
|
@@ -240,21 +243,25 @@ class VDBService(object):
|
|
|
240
243
|
|
|
241
244
|
def get_vector_client(self) -> MilvusClient:
|
|
242
245
|
"""
|
|
243
|
-
Returns the active MilvusClient instance.
|
|
246
|
+
Returns the active MilvusClient instance, initializing it if necessary.
|
|
244
247
|
|
|
245
248
|
Returns:
|
|
246
249
|
MilvusClient: The initialized client for interacting with the vector database.
|
|
247
250
|
"""
|
|
251
|
+
if self._client is None:
|
|
252
|
+
self.logger.debug("Initializing synchronous MilvusClient...")
|
|
253
|
+
self._client = self._initialize_milvus_client(self._provider)
|
|
254
|
+
|
|
248
255
|
return self._client
|
|
249
256
|
|
|
250
|
-
def
|
|
257
|
+
async def aget_async_vector_client(self) -> AsyncMilvusClient:
|
|
251
258
|
"""
|
|
252
|
-
|
|
259
|
+
Asynchronously returns the active AsyncMilvusClient instance, initializing it if necessary.
|
|
253
260
|
|
|
254
261
|
Returns:
|
|
255
262
|
AsyncMilvusClient: The initialized async client for interacting with the vector database.
|
|
256
263
|
"""
|
|
257
|
-
return self.
|
|
264
|
+
return await self._get_or_create_async_client()
|
|
258
265
|
|
|
259
266
|
def get_vector_field(self, collection_name: str) -> str:
|
|
260
267
|
"""
|
|
@@ -363,7 +370,7 @@ class VDBService(object):
|
|
|
363
370
|
Asynchronously checks if a collection exists and creates it if it doesn't.
|
|
364
371
|
"""
|
|
365
372
|
try:
|
|
366
|
-
client = self.
|
|
373
|
+
client = await self.aget_async_vector_client()
|
|
367
374
|
if check_existence and not await client.has_collection(collection_name):
|
|
368
375
|
self.logger.info(f"Collection '{collection_name}' does not exist. Creating it.")
|
|
369
376
|
|
|
@@ -374,6 +381,9 @@ class VDBService(object):
|
|
|
374
381
|
index_params=self.index_params
|
|
375
382
|
)
|
|
376
383
|
|
|
384
|
+
#ensure using async connection alias
|
|
385
|
+
schema_milvus.aclient._using = self.async_alias
|
|
386
|
+
|
|
377
387
|
schema_to_use = self.schema or DEFAULT_SCHEMA
|
|
378
388
|
if not self.schema:
|
|
379
389
|
self.logger.warning(f"No schema provided for VDBService. Using DEFAULT_SCHEMA for collection '{collection_name}'.")
|
|
@@ -498,6 +508,35 @@ class VDBService(object):
|
|
|
498
508
|
|
|
499
509
|
return vdb
|
|
500
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
|
+
|
|
501
540
|
async def aget_vector_store(self, collection_name: str, embeddings: Embeddings = None, metric_type: str = "IP") -> Milvus:
|
|
502
541
|
"""
|
|
503
542
|
Asynchronously gets a vector store instance, creating it if it doesn't exist.
|
|
@@ -525,12 +564,12 @@ class VDBService(object):
|
|
|
525
564
|
if embeddings is None:
|
|
526
565
|
embeddings = self.get_embeddings()
|
|
527
566
|
|
|
528
|
-
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)
|
|
529
568
|
|
|
530
569
|
try:
|
|
531
|
-
self.logger.
|
|
570
|
+
self.logger.info(f"Testing embedding function for collection '{collection_name}'...")
|
|
532
571
|
await embeddings.aembed_query("validation_test_string")
|
|
533
|
-
self.logger.
|
|
572
|
+
self.logger.info("Embedding function is valid.")
|
|
534
573
|
except Exception as e:
|
|
535
574
|
self.logger.error(
|
|
536
575
|
f"The provided embedding function is invalid and failed with error: {e}. "
|
|
@@ -544,24 +583,67 @@ class VDBService(object):
|
|
|
544
583
|
"params": {}
|
|
545
584
|
}
|
|
546
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
|
+
|
|
547
590
|
# For async operations, we MUST instantiate the Milvus object using the SYNCHRONOUS alias
|
|
548
|
-
# because its __init__ method is synchronous.
|
|
549
|
-
vdb = self.
|
|
591
|
+
# because its __init__ method is synchronous. This is now done in a separate thread.
|
|
592
|
+
vdb = await self._acreate_milvus_instance_with_retry(
|
|
550
593
|
collection_name=collection_name,
|
|
551
594
|
embeddings=embeddings,
|
|
552
595
|
index_params=index_params,
|
|
553
|
-
connection_args=
|
|
596
|
+
connection_args=async_conn_args # Pass the async-specific connection args
|
|
554
597
|
)
|
|
555
598
|
|
|
556
599
|
# After successful synchronous initialization, we hot-swap the alias on the
|
|
557
600
|
# ASYNCHRONOUS client to ensure future async operations use the correct connection.
|
|
558
|
-
self.logger.
|
|
601
|
+
self.logger.info(f"Swapping to async alias for instance of collection {collection_name}")
|
|
602
|
+
await self._get_or_create_async_client()
|
|
559
603
|
vdb.aclient._using = self.async_alias
|
|
560
604
|
|
|
561
605
|
self._async_instances[collection_name] = vdb
|
|
562
606
|
|
|
563
607
|
return vdb
|
|
564
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
|
+
|
|
565
647
|
def _create_milvus_instance_with_retry(self, collection_name: str, embeddings: Embeddings, index_params: dict, connection_args: Optional[dict] = None) -> Milvus:
|
|
566
648
|
"""
|
|
567
649
|
Creates a Milvus instance with a retry mechanism for connection failures.
|
|
@@ -637,7 +719,7 @@ class VDBService(object):
|
|
|
637
719
|
self.logger.info(f"Attempting to drop collection asynchronously: {collection_name}")
|
|
638
720
|
|
|
639
721
|
try:
|
|
640
|
-
client = self.
|
|
722
|
+
client = await self.aget_async_vector_client()
|
|
641
723
|
await client.drop_collection(collection_name=collection_name)
|
|
642
724
|
self.logger.info(f"Successfully dropped collection asynchronously: {collection_name}")
|
|
643
725
|
except Exception as e:
|
|
@@ -676,7 +758,7 @@ class VDBService(object):
|
|
|
676
758
|
self.logger.info(f"Delete data by filter asynchronously:{filter}")
|
|
677
759
|
|
|
678
760
|
try:
|
|
679
|
-
client=self.
|
|
761
|
+
client= await self.aget_async_vector_client()
|
|
680
762
|
if collection_name is None or client is None or filter is None:
|
|
681
763
|
return RuntimeError(f"collection_name must be not null or check out your client to link milvus")
|
|
682
764
|
await client.delete(collection_name=collection_name, filter=filter)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|