|
142 | 142 | "from langchain.schema import Document\n",
|
143 | 143 | "from langchain.document_loaders import TextLoader, PyPDFLoader, DirectoryLoader\n",
|
144 | 144 | "\n",
|
145 |
| - "# Milvus imports\n", |
146 |
| - "from pymilvus import connections, Collection, CollectionSchema, FieldSchema, DataType, utility\n", |
| 145 | + "# Milvus imports - using modern MilvusClient only\n", |
| 146 | + "from pymilvus import MilvusClient\n", |
147 | 147 | "\n",
|
148 | 148 | "# Embedding and LLM imports\n",
|
149 | 149 | "from sentence_transformers import SentenceTransformer\n",
|
|
193 | 193 | " top_k: int = 5\n",
|
194 | 194 | " \n",
|
195 | 195 | " # Anthropic configuration\n",
|
196 |
| - " ANTHROPIC_API_KEY: Optional[str] = None\n", |
| 196 | + " ANTHROPIC_API_KEY: Optional[str] = \"your_api_key\"\n", |
197 | 197 | " model_name: str = \"claude-sonnet-4-20250514\"\n",
|
198 | 198 | " max_tokens: int = 1000\n",
|
199 | 199 | " \n",
|
|
387 | 387 | "outputs": [],
|
388 | 388 | "source": [
|
389 | 389 | "class MilvusVectorStore:\n",
|
390 |
| - " \"\"\"Handles Milvus vector database operations.\"\"\"\n", |
| 390 | + " \"\"\"Handles Milvus vector database operations using modern MilvusClient.\"\"\"\n", |
391 | 391 | " \n",
|
392 | 392 | " def __init__(self, host: str = \"localhost\", port: str = \"19530\", collection_name: str = \"rag_documents\"):\n",
|
393 | 393 | " \"\"\"\n",
|
|
401 | 401 | " self.host = host\n",
|
402 | 402 | " self.port = port\n",
|
403 | 403 | " self.collection_name = collection_name\n",
|
404 |
| - " self.collection = None\n", |
405 | 404 | " \n",
|
406 |
| - " # Connect to Milvus\n", |
| 405 | + " # Connect to Milvus using the modern MilvusClient\n", |
407 | 406 | " self._connect()\n",
|
408 | 407 | " \n",
|
409 | 408 | " def _connect(self) -> None:\n",
|
410 | 409 | " \"\"\"\n",
|
411 |
| - " Establish connection to Milvus server.\n", |
| 410 | + " Establish connection to Milvus server using MilvusClient.\n", |
412 | 411 | " \"\"\"\n",
|
413 | 412 | " try:\n",
|
414 |
| - " connections.connect(\"default\", host=self.host, port=self.port)\n", |
415 |
| - " logger.info(f\"Connected to Milvus at {self.host}:{self.port}\")\n", |
| 413 | + " # Use the modern MilvusClient with uri endpoint\n", |
| 414 | + " uri = f\"http://{self.host}:{self.port}\"\n", |
| 415 | + " self.client = MilvusClient(uri=uri)\n", |
| 416 | + " logger.info(f\"Connected to Milvus at {uri}\")\n", |
416 | 417 | " except Exception as e:\n",
|
417 | 418 | " logger.error(f\"Failed to connect to Milvus: {e}\")\n",
|
418 | 419 | " raise\n",
|
419 | 420 | " \n",
|
420 | 421 | " def create_collection(self, embedding_dim: int) -> None:\n",
|
421 | 422 | " \"\"\"\n",
|
422 |
| - " Create a new collection with the specified schema.\n", |
| 423 | + " Create a new collection using MilvusClient's simplified approach.\n", |
423 | 424 | " \n",
|
424 | 425 | " Args:\n",
|
425 | 426 | " embedding_dim: Dimension of the embedding vectors\n",
|
426 | 427 | " \"\"\"\n",
|
427 |
| - " # Define collection schema\n", |
428 |
| - " fields = [\n", |
429 |
| - " FieldSchema(name=\"id\", dtype=DataType.INT64, is_primary=True, auto_id=True),\n", |
430 |
| - " FieldSchema(name=\"text\", dtype=DataType.VARCHAR, max_length=65535),\n", |
431 |
| - " FieldSchema(name=\"embedding\", dtype=DataType.FLOAT_VECTOR, dim=embedding_dim),\n", |
432 |
| - " FieldSchema(name=\"metadata\", dtype=DataType.VARCHAR, max_length=65535)\n", |
433 |
| - " ]\n", |
434 |
| - " \n", |
435 |
| - " schema = CollectionSchema(fields, \"RAG document collection\")\n", |
436 |
| - " \n", |
437 |
| - " # Drop existing collection if it exists\n", |
438 |
| - " if utility.has_collection(self.collection_name):\n", |
439 |
| - " utility.drop_collection(self.collection_name)\n", |
440 |
| - " logger.info(f\"Dropped existing collection: {self.collection_name}\")\n", |
441 |
| - " \n", |
442 |
| - " # Create collection\n", |
443 |
| - " self.collection = Collection(self.collection_name, schema)\n", |
444 |
| - " logger.info(f\"Created collection: {self.collection_name}\")\n", |
445 |
| - " \n", |
446 |
| - " # Create index for vector field\n", |
447 |
| - " index_params = {\n", |
448 |
| - " \"metric_type\": \"COSINE\",\n", |
449 |
| - " \"index_type\": \"IVF_FLAT\",\n", |
450 |
| - " \"params\": {\"nlist\": 1024}\n", |
451 |
| - " }\n", |
452 |
| - " \n", |
453 |
| - " self.collection.create_index(\"embedding\", index_params)\n", |
454 |
| - " logger.info(\"Created index for embedding field\")\n", |
| 428 | + " try:\n", |
| 429 | + " # Drop existing collection if it exists\n", |
| 430 | + " if self.client.has_collection(self.collection_name):\n", |
| 431 | + " self.client.drop_collection(self.collection_name)\n", |
| 432 | + " logger.info(f\"Dropped existing collection: {self.collection_name}\")\n", |
| 433 | + " \n", |
| 434 | + " # MilvusClient uses a simplified schema creation approach\n", |
| 435 | + " self.client.create_collection(\n", |
| 436 | + " collection_name=self.collection_name,\n", |
| 437 | + " dimension=embedding_dim,\n", |
| 438 | + " metric_type=\"COSINE\",\n", |
| 439 | + " index_type=\"IVF_FLAT\",\n", |
| 440 | + " index_params={\"nlist\": 1024}\n", |
| 441 | + " )\n", |
| 442 | + " \n", |
| 443 | + " logger.info(f\"Created collection: {self.collection_name} with dimension {embedding_dim}\")\n", |
| 444 | + " \n", |
| 445 | + " except Exception as e:\n", |
| 446 | + " logger.error(f\"Error creating collection: {e}\")\n", |
| 447 | + " raise\n", |
455 | 448 | " \n",
|
456 | 449 | " def load_collection(self) -> None:\n",
|
457 | 450 | " \"\"\"\n",
|
458 | 451 | " Load existing collection.\n",
|
459 | 452 | " \"\"\"\n",
|
460 |
| - " if utility.has_collection(self.collection_name):\n", |
461 |
| - " self.collection = Collection(self.collection_name)\n", |
462 |
| - " self.collection.load()\n", |
463 |
| - " logger.info(f\"Loaded collection: {self.collection_name}\")\n", |
| 453 | + " if self.client.has_collection(self.collection_name):\n", |
| 454 | + " logger.info(f\"Collection {self.collection_name} exists and is ready\")\n", |
464 | 455 | " else:\n",
|
465 | 456 | " raise ValueError(f\"Collection {self.collection_name} does not exist\")\n",
|
466 | 457 | " \n",
|
467 | 458 | " def add_documents(self, texts: List[str], embeddings: List[np.ndarray], metadata: List[Dict[str, Any]]) -> None:\n",
|
468 | 459 | " \"\"\"\n",
|
469 |
| - " Add documents to the collection.\n", |
| 460 | + " Add documents to the collection using MilvusClient.\n", |
470 | 461 | " \n",
|
471 | 462 | " Args:\n",
|
472 | 463 | " texts: List of document texts\n",
|
473 | 464 | " embeddings: List of embedding vectors\n",
|
474 | 465 | " metadata: List of metadata dictionaries\n",
|
475 | 466 | " \"\"\"\n",
|
476 |
| - " # Convert metadata to JSON strings\n", |
477 |
| - " metadata_strs = [json.dumps(meta) for meta in metadata]\n", |
478 |
| - " \n", |
479 |
| - " # Prepare data for insertion\n", |
480 |
| - " data = [\n", |
481 |
| - " texts,\n", |
482 |
| - " embeddings.tolist(),\n", |
483 |
| - " metadata_strs\n", |
484 |
| - " ]\n", |
| 467 | + " # Prepare data for MilvusClient insertion (include id field)\n", |
| 468 | + " data = []\n", |
| 469 | + " for i in range(len(texts)):\n", |
| 470 | + " data.append({\n", |
| 471 | + " \"id\": i, # Add required id field\n", |
| 472 | + " \"text\": texts[i],\n", |
| 473 | + " \"vector\": embeddings[i].tolist(),\n", |
| 474 | + " \"metadata\": json.dumps(metadata[i])\n", |
| 475 | + " })\n", |
485 | 476 | " \n",
|
486 |
| - " # Insert data\n", |
487 |
| - " mr = self.collection.insert(data)\n", |
488 |
| - " self.collection.flush()\n", |
| 477 | + " # Insert data using MilvusClient\n", |
| 478 | + " result = self.client.insert(\n", |
| 479 | + " collection_name=self.collection_name,\n", |
| 480 | + " data=data\n", |
| 481 | + " )\n", |
489 | 482 | " \n",
|
490 |
| - " # Load the collection after adding documents\n", |
491 |
| - " self.collection.load()\n", |
492 |
| - " logger.info(f\"Added {len(texts)} documents to collection and loaded it\")\n", |
493 |
| - " return mr\n", |
| 483 | + " logger.info(f\"Added {len(texts)} documents to collection\")\n", |
| 484 | + " return result\n", |
494 | 485 | " \n",
|
495 | 486 | " def search(self, query_embedding: np.ndarray, top_k: int = 5) -> List[Dict[str, Any]]:\n",
|
496 | 487 | " \"\"\"\n",
|
497 |
| - " Search for similar documents.\n", |
| 488 | + " Search for similar documents using MilvusClient.\n", |
498 | 489 | " \n",
|
499 | 490 | " Args:\n",
|
500 | 491 | " query_embedding: Query embedding vector\n",
|
|
503 | 494 | " Returns:\n",
|
504 | 495 | " List of search results with text, metadata, and similarity scores\n",
|
505 | 496 | " \"\"\"\n",
|
506 |
| - " # Ensure collection is loaded\n", |
507 |
| - " if self.collection is None:\n", |
508 |
| - " self.load_collection()\n", |
509 |
| - " \n", |
510 |
| - " search_params = {\"metric_type\": \"COSINE\", \"params\": {\"nprobe\": 10}}\n", |
| 497 | + " # Ensure collection exists\n", |
| 498 | + " if not self.client.has_collection(self.collection_name):\n", |
| 499 | + " raise ValueError(f\"Collection {self.collection_name} does not exist\")\n", |
511 | 500 | " \n",
|
512 |
| - " results = self.collection.search(\n", |
| 501 | + " # Use MilvusClient search method with proper vector field specification\n", |
| 502 | + " results = self.client.search(\n", |
| 503 | + " collection_name=self.collection_name,\n", |
513 | 504 | " data=[query_embedding.tolist()],\n",
|
514 |
| - " anns_field=\"embedding\",\n", |
515 |
| - " param=search_params,\n", |
| 505 | + " anns_field=\"vector\", # Specify the vector field name\n", |
| 506 | + " search_params={\"metric_type\": \"COSINE\", \"params\": {\"nprobe\": 10}},\n", |
516 | 507 | " limit=top_k,\n",
|
517 | 508 | " output_fields=[\"text\", \"metadata\"]\n",
|
518 | 509 | " )\n",
|
|
521 | 512 | " formatted_results = []\n",
|
522 | 513 | " for hit in results[0]:\n",
|
523 | 514 | " formatted_results.append({\n",
|
524 |
| - " \"text\": hit.entity.get(\"text\"),\n", |
525 |
| - " \"metadata\": json.loads(hit.entity.get(\"metadata\")),\n", |
526 |
| - " \"score\": hit.score,\n", |
527 |
| - " \"id\": hit.id\n", |
| 515 | + " \"text\": hit[\"text\"],\n", |
| 516 | + " \"metadata\": json.loads(hit[\"metadata\"]),\n", |
| 517 | + " \"score\": 1.0 - hit[\"distance\"], # Convert distance to similarity score for COSINE\n", |
| 518 | + " \"id\": hit[\"id\"]\n", |
528 | 519 | " })\n",
|
529 | 520 | " \n",
|
530 |
| - " return formatted_results" |
| 521 | + " return formatted_results\n", |
| 522 | + " \n", |
| 523 | + " def close(self) -> None:\n", |
| 524 | + " \"\"\"\n", |
| 525 | + " Close the MilvusClient connection.\n", |
| 526 | + " \"\"\"\n", |
| 527 | + " if hasattr(self, 'client'):\n", |
| 528 | + " self.client.close()\n", |
| 529 | + " logger.info(\"Closed MilvusClient connection\")" |
531 | 530 | ]
|
532 | 531 | },
|
533 | 532 | {
|
|
613 | 612 | " \n",
|
614 | 613 | " except Exception as e:\n",
|
615 | 614 | " logger.error(f\"Error generating response: {e}\")\n",
|
616 |
| - " return f\"Error generating response: {str(e)}\"" |
| 615 | + " raise e " |
617 | 616 | ]
|
618 | 617 | },
|
619 | 618 | {
|
|
656 | 655 | " )\n",
|
657 | 656 | " \n",
|
658 | 657 | " self.llm = ClaudeGenerator(\n",
|
659 |
| - " api_key=config.anthropic_api_key,\n", |
| 658 | + " api_key=config.ANTHROPIC_API_KEY,\n", |
660 | 659 | " model_name=config.model_name,\n",
|
661 | 660 | " max_tokens=config.max_tokens\n",
|
662 | 661 | " )\n",
|
|
716 | 715 | " logger.info(f\"Processing query: {question[:100]}...\")\n",
|
717 | 716 | " \n",
|
718 | 717 | " try:\n",
|
719 |
| - " # Load collection if not already loaded\n", |
720 |
| - " if self.vector_store.collection is None:\n", |
721 |
| - " self.vector_store.load_collection()\n", |
| 718 | + " # Ensure collection exists\n", |
| 719 | + " if not self.vector_store.client.has_collection(self.vector_store.collection_name):\n", |
| 720 | + " raise ValueError(f\"Collection {self.vector_store.collection_name} does not exist. Please index documents first.\")\n", |
722 | 721 | " \n",
|
723 | 722 | " # Generate query embedding\n",
|
724 | 723 | " query_embedding = self.embedding_generator.embed_text(question)\n",
|
|
758 | 757 | " \n",
|
759 | 758 | " def get_collection_stats(self) -> Dict[str, Any]:\n",
|
760 | 759 | " \"\"\"\n",
|
761 |
| - " Get statistics about the current collection.\n", |
| 760 | + " Get statistics about the current collection using MilvusClient.\n", |
762 | 761 | " \n",
|
763 | 762 | " Returns:\n",
|
764 | 763 | " Dictionary with collection statistics\n",
|
765 | 764 | " \"\"\"\n",
|
766 | 765 | " try:\n",
|
767 |
| - " if self.vector_store.collection is None:\n", |
768 |
| - " self.vector_store.load_collection()\n", |
| 766 | + " # Check if collection exists\n", |
| 767 | + " if not self.vector_store.client.has_collection(self.vector_store.collection_name):\n", |
| 768 | + " return {\n", |
| 769 | + " \"error\": f\"Collection {self.vector_store.collection_name} does not exist. Please index documents first.\"\n", |
| 770 | + " }\n", |
769 | 771 | " \n",
|
| 772 | + " # Get collection statistics using MilvusClient\n", |
| 773 | + " collection_info = self.vector_store.client.describe_collection(self.vector_store.collection_name)\n", |
| 774 | + " \n", |
| 775 | + " # Get entity count - this might not be available in MilvusClient, so we'll provide what we can\n", |
770 | 776 | " stats = {\n",
|
771 | 777 | " \"collection_name\": self.config.collection_name,\n",
|
772 |
| - " \"num_entities\": self.vector_store.collection.num_entities,\n", |
773 | 778 | " \"embedding_dim\": self.embedding_generator.embedding_dim,\n",
|
774 |
| - " \"embedding_model\": self.config.embedding_model\n", |
| 779 | + " \"embedding_model\": self.config.embedding_model,\n", |
| 780 | + " \"collection_exists\": True,\n", |
| 781 | + " \"schema\": collection_info if collection_info else \"Schema information not available\"\n", |
775 | 782 | " }\n",
|
776 | 783 | " \n",
|
777 | 784 | " return stats\n",
|
|
966 | 973 | "def cleanup_resources() -> None:\n",
|
967 | 974 | " \"\"\"Clean up resources and connections.\"\"\"\n",
|
968 | 975 | " try:\n",
|
969 |
| - " connections.disconnect(\"default\")\n", |
970 |
| - " print(\"Disconnected from Milvus\")\n", |
| 976 | + " # Close MilvusClient connections if they exist\n", |
| 977 | + " if 'rag' in globals() and hasattr(rag.vector_store, 'client'):\n", |
| 978 | + " rag.vector_store.close()\n", |
| 979 | + " \n", |
| 980 | + " if 'pdf_rag' in globals() and hasattr(pdf_rag.vector_store, 'client'):\n", |
| 981 | + " pdf_rag.vector_store.close()\n", |
| 982 | + " \n", |
| 983 | + " print(\"Closed MilvusClient connections\")\n", |
971 | 984 | " except Exception as e:\n",
|
972 | 985 | " print(f\"Error during cleanup: {e}\")\n",
|
973 | 986 | "\n",
|
|
0 commit comments