mantisdk 0.1.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.

Potentially problematic release.


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

Files changed (190) hide show
  1. mantisdk/__init__.py +22 -0
  2. mantisdk/adapter/__init__.py +15 -0
  3. mantisdk/adapter/base.py +94 -0
  4. mantisdk/adapter/messages.py +270 -0
  5. mantisdk/adapter/triplet.py +1028 -0
  6. mantisdk/algorithm/__init__.py +39 -0
  7. mantisdk/algorithm/apo/__init__.py +5 -0
  8. mantisdk/algorithm/apo/apo.py +889 -0
  9. mantisdk/algorithm/apo/prompts/apply_edit_variant01.poml +22 -0
  10. mantisdk/algorithm/apo/prompts/apply_edit_variant02.poml +18 -0
  11. mantisdk/algorithm/apo/prompts/text_gradient_variant01.poml +18 -0
  12. mantisdk/algorithm/apo/prompts/text_gradient_variant02.poml +16 -0
  13. mantisdk/algorithm/apo/prompts/text_gradient_variant03.poml +107 -0
  14. mantisdk/algorithm/base.py +162 -0
  15. mantisdk/algorithm/decorator.py +264 -0
  16. mantisdk/algorithm/fast.py +250 -0
  17. mantisdk/algorithm/gepa/__init__.py +59 -0
  18. mantisdk/algorithm/gepa/adapter.py +459 -0
  19. mantisdk/algorithm/gepa/gepa.py +364 -0
  20. mantisdk/algorithm/gepa/lib/__init__.py +18 -0
  21. mantisdk/algorithm/gepa/lib/adapters/README.md +12 -0
  22. mantisdk/algorithm/gepa/lib/adapters/__init__.py +0 -0
  23. mantisdk/algorithm/gepa/lib/adapters/anymaths_adapter/README.md +341 -0
  24. mantisdk/algorithm/gepa/lib/adapters/anymaths_adapter/__init__.py +1 -0
  25. mantisdk/algorithm/gepa/lib/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
  26. mantisdk/algorithm/gepa/lib/adapters/anymaths_adapter/requirements.txt +1 -0
  27. mantisdk/algorithm/gepa/lib/adapters/default_adapter/README.md +0 -0
  28. mantisdk/algorithm/gepa/lib/adapters/default_adapter/__init__.py +0 -0
  29. mantisdk/algorithm/gepa/lib/adapters/default_adapter/default_adapter.py +209 -0
  30. mantisdk/algorithm/gepa/lib/adapters/dspy_adapter/README.md +7 -0
  31. mantisdk/algorithm/gepa/lib/adapters/dspy_adapter/__init__.py +0 -0
  32. mantisdk/algorithm/gepa/lib/adapters/dspy_adapter/dspy_adapter.py +307 -0
  33. mantisdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/README.md +99 -0
  34. mantisdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
  35. mantisdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/full_program_adapter.py +266 -0
  36. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/GEPA_RAG.md +621 -0
  37. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/__init__.py +56 -0
  38. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
  39. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
  40. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
  41. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
  42. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
  43. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
  44. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
  45. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
  46. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
  47. mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
  48. mantisdk/algorithm/gepa/lib/adapters/mcp_adapter/README.md +552 -0
  49. mantisdk/algorithm/gepa/lib/adapters/mcp_adapter/__init__.py +37 -0
  50. mantisdk/algorithm/gepa/lib/adapters/mcp_adapter/mcp_adapter.py +705 -0
  51. mantisdk/algorithm/gepa/lib/adapters/mcp_adapter/mcp_client.py +364 -0
  52. mantisdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/README.md +9 -0
  53. mantisdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/__init__.py +0 -0
  54. mantisdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
  55. mantisdk/algorithm/gepa/lib/api.py +375 -0
  56. mantisdk/algorithm/gepa/lib/core/__init__.py +0 -0
  57. mantisdk/algorithm/gepa/lib/core/adapter.py +180 -0
  58. mantisdk/algorithm/gepa/lib/core/data_loader.py +74 -0
  59. mantisdk/algorithm/gepa/lib/core/engine.py +356 -0
  60. mantisdk/algorithm/gepa/lib/core/result.py +233 -0
  61. mantisdk/algorithm/gepa/lib/core/state.py +636 -0
  62. mantisdk/algorithm/gepa/lib/examples/__init__.py +0 -0
  63. mantisdk/algorithm/gepa/lib/examples/aime.py +24 -0
  64. mantisdk/algorithm/gepa/lib/examples/anymaths-bench/eval_default.py +111 -0
  65. mantisdk/algorithm/gepa/lib/examples/anymaths-bench/prompt-templates/instruction_prompt.txt +9 -0
  66. mantisdk/algorithm/gepa/lib/examples/anymaths-bench/prompt-templates/optimal_prompt.txt +24 -0
  67. mantisdk/algorithm/gepa/lib/examples/anymaths-bench/train_anymaths.py +177 -0
  68. mantisdk/algorithm/gepa/lib/examples/dspy_full_program_evolution/arc_agi.ipynb +25705 -0
  69. mantisdk/algorithm/gepa/lib/examples/dspy_full_program_evolution/example.ipynb +348 -0
  70. mantisdk/algorithm/gepa/lib/examples/mcp_adapter/__init__.py +4 -0
  71. mantisdk/algorithm/gepa/lib/examples/mcp_adapter/mcp_optimization_example.py +455 -0
  72. mantisdk/algorithm/gepa/lib/examples/rag_adapter/RAG_GUIDE.md +613 -0
  73. mantisdk/algorithm/gepa/lib/examples/rag_adapter/__init__.py +9 -0
  74. mantisdk/algorithm/gepa/lib/examples/rag_adapter/rag_optimization.py +824 -0
  75. mantisdk/algorithm/gepa/lib/examples/rag_adapter/requirements-rag.txt +29 -0
  76. mantisdk/algorithm/gepa/lib/examples/terminal-bench/prompt-templates/instruction_prompt.txt +16 -0
  77. mantisdk/algorithm/gepa/lib/examples/terminal-bench/prompt-templates/terminus.txt +9 -0
  78. mantisdk/algorithm/gepa/lib/examples/terminal-bench/train_terminus.py +161 -0
  79. mantisdk/algorithm/gepa/lib/gepa_utils.py +117 -0
  80. mantisdk/algorithm/gepa/lib/logging/__init__.py +0 -0
  81. mantisdk/algorithm/gepa/lib/logging/experiment_tracker.py +187 -0
  82. mantisdk/algorithm/gepa/lib/logging/logger.py +75 -0
  83. mantisdk/algorithm/gepa/lib/logging/utils.py +103 -0
  84. mantisdk/algorithm/gepa/lib/proposer/__init__.py +0 -0
  85. mantisdk/algorithm/gepa/lib/proposer/base.py +31 -0
  86. mantisdk/algorithm/gepa/lib/proposer/merge.py +357 -0
  87. mantisdk/algorithm/gepa/lib/proposer/reflective_mutation/__init__.py +0 -0
  88. mantisdk/algorithm/gepa/lib/proposer/reflective_mutation/base.py +49 -0
  89. mantisdk/algorithm/gepa/lib/proposer/reflective_mutation/reflective_mutation.py +176 -0
  90. mantisdk/algorithm/gepa/lib/py.typed +0 -0
  91. mantisdk/algorithm/gepa/lib/strategies/__init__.py +0 -0
  92. mantisdk/algorithm/gepa/lib/strategies/batch_sampler.py +77 -0
  93. mantisdk/algorithm/gepa/lib/strategies/candidate_selector.py +50 -0
  94. mantisdk/algorithm/gepa/lib/strategies/component_selector.py +36 -0
  95. mantisdk/algorithm/gepa/lib/strategies/eval_policy.py +64 -0
  96. mantisdk/algorithm/gepa/lib/strategies/instruction_proposal.py +127 -0
  97. mantisdk/algorithm/gepa/lib/utils/__init__.py +10 -0
  98. mantisdk/algorithm/gepa/lib/utils/stop_condition.py +196 -0
  99. mantisdk/algorithm/gepa/tracing.py +105 -0
  100. mantisdk/algorithm/utils.py +177 -0
  101. mantisdk/algorithm/verl/__init__.py +5 -0
  102. mantisdk/algorithm/verl/interface.py +202 -0
  103. mantisdk/cli/__init__.py +56 -0
  104. mantisdk/cli/prometheus.py +115 -0
  105. mantisdk/cli/store.py +131 -0
  106. mantisdk/cli/vllm.py +29 -0
  107. mantisdk/client.py +408 -0
  108. mantisdk/config.py +348 -0
  109. mantisdk/emitter/__init__.py +43 -0
  110. mantisdk/emitter/annotation.py +370 -0
  111. mantisdk/emitter/exception.py +54 -0
  112. mantisdk/emitter/message.py +61 -0
  113. mantisdk/emitter/object.py +117 -0
  114. mantisdk/emitter/reward.py +320 -0
  115. mantisdk/env_var.py +156 -0
  116. mantisdk/execution/__init__.py +15 -0
  117. mantisdk/execution/base.py +64 -0
  118. mantisdk/execution/client_server.py +443 -0
  119. mantisdk/execution/events.py +69 -0
  120. mantisdk/execution/inter_process.py +16 -0
  121. mantisdk/execution/shared_memory.py +282 -0
  122. mantisdk/instrumentation/__init__.py +119 -0
  123. mantisdk/instrumentation/agentops.py +314 -0
  124. mantisdk/instrumentation/agentops_langchain.py +45 -0
  125. mantisdk/instrumentation/litellm.py +83 -0
  126. mantisdk/instrumentation/vllm.py +81 -0
  127. mantisdk/instrumentation/weave.py +500 -0
  128. mantisdk/litagent/__init__.py +11 -0
  129. mantisdk/litagent/decorator.py +536 -0
  130. mantisdk/litagent/litagent.py +252 -0
  131. mantisdk/llm_proxy.py +1890 -0
  132. mantisdk/logging.py +370 -0
  133. mantisdk/reward.py +7 -0
  134. mantisdk/runner/__init__.py +11 -0
  135. mantisdk/runner/agent.py +845 -0
  136. mantisdk/runner/base.py +182 -0
  137. mantisdk/runner/legacy.py +309 -0
  138. mantisdk/semconv.py +170 -0
  139. mantisdk/server.py +401 -0
  140. mantisdk/store/__init__.py +23 -0
  141. mantisdk/store/base.py +897 -0
  142. mantisdk/store/client_server.py +2092 -0
  143. mantisdk/store/collection/__init__.py +30 -0
  144. mantisdk/store/collection/base.py +587 -0
  145. mantisdk/store/collection/memory.py +970 -0
  146. mantisdk/store/collection/mongo.py +1412 -0
  147. mantisdk/store/collection_based.py +1823 -0
  148. mantisdk/store/insight.py +648 -0
  149. mantisdk/store/listener.py +58 -0
  150. mantisdk/store/memory.py +396 -0
  151. mantisdk/store/mongo.py +165 -0
  152. mantisdk/store/sqlite.py +3 -0
  153. mantisdk/store/threading.py +357 -0
  154. mantisdk/store/utils.py +142 -0
  155. mantisdk/tracer/__init__.py +16 -0
  156. mantisdk/tracer/agentops.py +242 -0
  157. mantisdk/tracer/base.py +287 -0
  158. mantisdk/tracer/dummy.py +106 -0
  159. mantisdk/tracer/otel.py +555 -0
  160. mantisdk/tracer/weave.py +677 -0
  161. mantisdk/trainer/__init__.py +6 -0
  162. mantisdk/trainer/init_utils.py +263 -0
  163. mantisdk/trainer/legacy.py +367 -0
  164. mantisdk/trainer/registry.py +12 -0
  165. mantisdk/trainer/trainer.py +618 -0
  166. mantisdk/types/__init__.py +6 -0
  167. mantisdk/types/core.py +553 -0
  168. mantisdk/types/resources.py +204 -0
  169. mantisdk/types/tracer.py +515 -0
  170. mantisdk/types/tracing.py +218 -0
  171. mantisdk/utils/__init__.py +1 -0
  172. mantisdk/utils/id.py +18 -0
  173. mantisdk/utils/metrics.py +1025 -0
  174. mantisdk/utils/otel.py +578 -0
  175. mantisdk/utils/otlp.py +536 -0
  176. mantisdk/utils/server_launcher.py +1045 -0
  177. mantisdk/utils/system_snapshot.py +81 -0
  178. mantisdk/verl/__init__.py +8 -0
  179. mantisdk/verl/__main__.py +6 -0
  180. mantisdk/verl/async_server.py +46 -0
  181. mantisdk/verl/config.yaml +27 -0
  182. mantisdk/verl/daemon.py +1154 -0
  183. mantisdk/verl/dataset.py +44 -0
  184. mantisdk/verl/entrypoint.py +248 -0
  185. mantisdk/verl/trainer.py +549 -0
  186. mantisdk-0.1.0.dist-info/METADATA +119 -0
  187. mantisdk-0.1.0.dist-info/RECORD +190 -0
  188. mantisdk-0.1.0.dist-info/WHEEL +4 -0
  189. mantisdk-0.1.0.dist-info/entry_points.txt +2 -0
  190. mantisdk-0.1.0.dist-info/licenses/LICENSE +19 -0
@@ -0,0 +1,409 @@
1
+ # Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
2
+ # https://github.com/gepa-ai/gepa
3
+
4
+ from typing import Any
5
+
6
+ from mantisdk.algorithm.gepa.lib.adapters.generic_rag_adapter.vector_store_interface import VectorStoreInterface
7
+
8
+
9
+ class MilvusVectorStore(VectorStoreInterface):
10
+ """
11
+ Milvus implementation of the VectorStoreInterface.
12
+
13
+ Milvus is a cloud-native vector database built for scalable similarity search
14
+ and AI applications. It provides excellent performance for large-scale deployments
15
+ and supports various index types and distance metrics.
16
+ """
17
+
18
+ def __init__(self, client, collection_name: str, embedding_function=None):
19
+ """
20
+ Initialize MilvusVectorStore.
21
+
22
+ Args:
23
+ client: MilvusClient instance
24
+ collection_name: Name of the collection to use
25
+ embedding_function: Optional function to compute embeddings for queries
26
+ """
27
+ import importlib.util
28
+
29
+ if importlib.util.find_spec("pymilvus") is None:
30
+ raise ImportError(
31
+ "Milvus client is required for MilvusVectorStore. Install with: pip install litellm pymilvus"
32
+ )
33
+
34
+ self.client = client
35
+ self.collection_name = collection_name
36
+ self.embedding_function = embedding_function
37
+
38
+ # Verify collection exists
39
+ if not self.client.has_collection(collection_name):
40
+ raise ValueError(f"Collection '{collection_name}' not found. Please create the collection first.")
41
+
42
+ # Load collection into memory for search operations
43
+ try:
44
+ self.client.load_collection(collection_name)
45
+ except Exception:
46
+ # Collection might already be loaded, which is fine
47
+ pass
48
+
49
+ def similarity_search(
50
+ self,
51
+ query: str,
52
+ k: int = 5,
53
+ filters: dict[str, Any] | None = None,
54
+ ) -> list[dict[str, Any]]:
55
+ """Search for documents similar to the query text using embeddings."""
56
+ if self.embedding_function is None:
57
+ raise ValueError("No embedding function provided for similarity search")
58
+
59
+ # Compute embeddings for the query
60
+ try:
61
+ query_vector = self.embedding_function(query)
62
+ if hasattr(query_vector, "tolist"):
63
+ query_vector = query_vector.tolist()
64
+ except Exception as e:
65
+ raise RuntimeError(f"Failed to compute embeddings for query: {e!s}") from e
66
+
67
+ # Use vector search with computed embeddings
68
+ return self.vector_search(query_vector, k, filters)
69
+
70
+ def vector_search(
71
+ self,
72
+ query_vector: list[float],
73
+ k: int = 5,
74
+ filters: dict[str, Any] | None = None,
75
+ ) -> list[dict[str, Any]]:
76
+ """Search using a pre-computed query vector."""
77
+ try:
78
+ # Convert filters to Milvus expression format
79
+ filter_expr = self._convert_filters(filters) if filters else None
80
+
81
+ # Perform vector search
82
+ results = self.client.search(
83
+ collection_name=self.collection_name,
84
+ data=[query_vector], # Milvus expects list of vectors
85
+ limit=k,
86
+ filter=filter_expr,
87
+ output_fields=["*"], # Return all fields
88
+ )
89
+
90
+ return self._format_results(results)
91
+
92
+ except Exception as e:
93
+ raise RuntimeError(f"Milvus vector search failed: {e!s}") from e
94
+
95
+ def add_documents(
96
+ self,
97
+ documents: list[dict[str, Any]],
98
+ embeddings: list[list[float]],
99
+ ids: list[str] | None = None,
100
+ ) -> list[str]:
101
+ """Add documents with their embeddings to the collection."""
102
+ if len(documents) != len(embeddings):
103
+ raise ValueError("Number of documents must match number of embeddings")
104
+
105
+ # Generate IDs if not provided
106
+ if ids is None:
107
+ ids = [f"doc_{i}" for i in range(len(documents))]
108
+ elif len(ids) != len(documents):
109
+ raise ValueError("Number of IDs must match number of documents")
110
+
111
+ # Prepare data for insertion
112
+ data_to_insert = []
113
+ for doc_id, doc, embedding in zip(ids, documents, embeddings, strict=False):
114
+ # Milvus requires consistent field structure
115
+ record = {
116
+ "id": doc_id,
117
+ "vector": embedding,
118
+ **doc, # Include all document fields
119
+ }
120
+ data_to_insert.append(record)
121
+
122
+ try:
123
+ # Insert data into collection
124
+ result = self.client.insert(collection_name=self.collection_name, data=data_to_insert)
125
+
126
+ # Check if insertion was successful
127
+ if "insert_count" in result and result["insert_count"] == len(data_to_insert):
128
+ return ids
129
+ else:
130
+ raise RuntimeError(
131
+ f"Insertion failed. Expected {len(data_to_insert)}, got {result.get('insert_count', 0)}"
132
+ )
133
+
134
+ except Exception as e:
135
+ raise RuntimeError(f"Failed to add documents to Milvus: {e!s}") from e
136
+
137
+ def delete_documents(self, ids: list[str]) -> bool:
138
+ """Delete documents by their IDs."""
139
+ try:
140
+ # Use parameterized ID-based deletion to avoid injection
141
+ result = self.client.delete(collection_name=self.collection_name, ids=ids)
142
+
143
+ return "delete_count" in result and result["delete_count"] > 0
144
+
145
+ except Exception as e:
146
+ raise RuntimeError(f"Failed to delete documents from Milvus: {e!s}") from e
147
+
148
+ def get_collection_info(self) -> dict[str, Any]:
149
+ """Get metadata about the Milvus collection."""
150
+ try:
151
+ # Get collection description
152
+ description = self.client.describe_collection(self.collection_name)
153
+
154
+ # Get collection statistics
155
+ stats = self.client.get_collection_stats(self.collection_name)
156
+
157
+ # Extract vector field information
158
+ vector_field = None
159
+ dimension = 0
160
+ for field in description.get("fields", []):
161
+ if field.get("type") == "FloatVector":
162
+ vector_field = field.get("name", "vector")
163
+ dimension = field.get("params", {}).get("dim", 0)
164
+ break
165
+
166
+ return {
167
+ "name": self.collection_name,
168
+ "document_count": stats.get("row_count", 0),
169
+ "dimension": dimension,
170
+ "vector_store_type": "milvus",
171
+ "vector_field": vector_field,
172
+ "schema": description,
173
+ }
174
+
175
+ except Exception as e:
176
+ # Fallback info if detailed info fails
177
+ return {
178
+ "name": self.collection_name,
179
+ "document_count": 0,
180
+ "dimension": 0,
181
+ "vector_store_type": "milvus",
182
+ "error": str(e),
183
+ }
184
+
185
+ def supports_hybrid_search(self) -> bool:
186
+ """Milvus supports hybrid search through dense + sparse vectors."""
187
+ return True
188
+
189
+ def hybrid_search(
190
+ self,
191
+ query: str,
192
+ k: int = 5,
193
+ alpha: float = 0.5,
194
+ filters: dict[str, Any] | None = None,
195
+ ) -> list[dict[str, Any]]:
196
+ """
197
+ Hybrid search combining vector similarity and other search methods.
198
+
199
+ Note: This implementation focuses on vector search with filtering.
200
+ Full hybrid search with sparse vectors would require additional setup.
201
+ """
202
+ # For now, implement as filtered vector search
203
+ # Future enhancement could include sparse vector search for text matching
204
+ return self.similarity_search(query, k, filters)
205
+
206
+ def _format_results(self, results) -> list[dict[str, Any]]:
207
+ """Convert Milvus results to standardized format."""
208
+ documents = []
209
+
210
+ # Milvus returns nested list: results[0] contains hits for first query vector
211
+ if not results or not results[0]:
212
+ return documents
213
+
214
+ hits = results[0] # Get hits for our single query vector
215
+
216
+ for hit in hits:
217
+ # Extract fields from hit
218
+ hit_id = hit.get("id", "")
219
+ distance = hit.get("distance", 0.0)
220
+
221
+ # Extract content - try different field names
222
+ content = ""
223
+ content_fields = ["content", "text", "document", "body", "description"]
224
+ for field in content_fields:
225
+ if field in hit:
226
+ content = hit[field]
227
+ break
228
+
229
+ # If no content field found, combine text fields
230
+ if not content:
231
+ text_properties = []
232
+ for key, value in hit.items():
233
+ if isinstance(value, str) and value.strip() and key not in ["id", "distance"]:
234
+ text_properties.append(f"{key}: {value}")
235
+ content = " | ".join(text_properties)
236
+
237
+ # Create metadata (all fields except content and system fields)
238
+ metadata = {}
239
+ system_fields = ["id", "distance", "vector"] + content_fields
240
+ for key, value in hit.items():
241
+ if key not in system_fields:
242
+ metadata[key] = value
243
+ metadata["doc_id"] = str(hit_id)
244
+
245
+ # Convert distance to similarity score (Milvus returns distance, lower is better)
246
+ # For cosine distance: similarity = 1 - distance
247
+ # For L2 distance: similarity = 1 / (1 + distance)
248
+ score = max(0.0, 1.0 - distance) if distance <= 1.0 else 1.0 / (1.0 + distance)
249
+
250
+ documents.append(
251
+ {
252
+ "content": content,
253
+ "metadata": metadata,
254
+ "score": score,
255
+ }
256
+ )
257
+
258
+ return documents
259
+
260
+ def _convert_filters(self, filters: dict[str, Any]) -> str:
261
+ """Convert generic filters to Milvus expression format."""
262
+ if not filters:
263
+ return None
264
+
265
+ expressions = []
266
+
267
+ for key, value in filters.items():
268
+ if isinstance(value, str):
269
+ # String exact match
270
+ expressions.append(f'{key} == "{value}"')
271
+ elif isinstance(value, int | float):
272
+ # Numeric exact match
273
+ expressions.append(f"{key} == {value}")
274
+ elif isinstance(value, list):
275
+ # IN clause for multiple values
276
+ if all(isinstance(v, str) for v in value):
277
+ values_str = '", "'.join(value)
278
+ expressions.append(f'{key} in ["{values_str}"]')
279
+ else:
280
+ values_str = ", ".join(str(v) for v in value)
281
+ expressions.append(f"{key} in [{values_str}]")
282
+ elif isinstance(value, dict):
283
+ # Range queries
284
+ range_conditions = []
285
+ if "gte" in value:
286
+ range_conditions.append(f"{key} >= {value['gte']}")
287
+ if "gt" in value:
288
+ range_conditions.append(f"{key} > {value['gt']}")
289
+ if "lte" in value:
290
+ range_conditions.append(f"{key} <= {value['lte']}")
291
+ if "lt" in value:
292
+ range_conditions.append(f"{key} < {value['lt']}")
293
+
294
+ if range_conditions:
295
+ expressions.append(" and ".join(range_conditions))
296
+
297
+ if expressions:
298
+ return " and ".join(expressions)
299
+
300
+ return None
301
+
302
+ @classmethod
303
+ def create_local(
304
+ cls, collection_name: str, embedding_function=None, vector_size: int = 384, uri: str = "./milvus_demo.db"
305
+ ):
306
+ """Create a local Milvus vector store (Milvus Lite)."""
307
+ try:
308
+ from pymilvus import DataType, MilvusClient
309
+ except ImportError as e:
310
+ raise ImportError("Milvus client is required. Install with: pip install litellm pymilvus") from e
311
+
312
+ client = MilvusClient(uri=uri)
313
+
314
+ # Create collection if it doesn't exist
315
+ if not client.has_collection(collection_name):
316
+ # Create collection with explicit schema to avoid max_length issues
317
+ schema = client.create_schema(auto_id=False, enable_dynamic_field=True)
318
+
319
+ # Add ID field (string)
320
+ schema.add_field(
321
+ field_name="id",
322
+ datatype=DataType.VARCHAR,
323
+ is_primary=True,
324
+ max_length=512, # Maximum length for ID strings
325
+ )
326
+
327
+ # Add vector field
328
+ schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=vector_size)
329
+
330
+ # Add content field
331
+ schema.add_field(
332
+ field_name="content",
333
+ datatype=DataType.VARCHAR,
334
+ max_length=65535, # Large max length for content
335
+ )
336
+
337
+ # Create collection with schema
338
+ client.create_collection(
339
+ collection_name=collection_name,
340
+ schema=schema,
341
+ )
342
+
343
+ # Create index for vector field
344
+ index_params = client.prepare_index_params()
345
+ index_params.add_index(field_name="vector", metric_type="COSINE")
346
+ client.create_index(collection_name=collection_name, index_params=index_params)
347
+
348
+ return cls(client, collection_name, embedding_function)
349
+
350
+ @classmethod
351
+ def create_remote(
352
+ cls,
353
+ collection_name: str,
354
+ embedding_function=None,
355
+ uri: str = "http://localhost:19530",
356
+ user: str = "",
357
+ password: str = "",
358
+ token: str = "",
359
+ vector_size: int = 384,
360
+ ):
361
+ """Create a remote Milvus vector store."""
362
+ try:
363
+ from pymilvus import DataType, MilvusClient
364
+ except ImportError as e:
365
+ raise ImportError("Milvus client is required. Install with: pip install litellm pymilvus") from e
366
+
367
+ # Connect to remote Milvus
368
+ client = MilvusClient(
369
+ uri=uri,
370
+ user=user if user else None,
371
+ password=password if password else None,
372
+ token=token if token else None,
373
+ )
374
+
375
+ # Create collection if it doesn't exist
376
+ if not client.has_collection(collection_name):
377
+ # Create collection with explicit schema to avoid max_length issues
378
+ schema = client.create_schema(auto_id=False, enable_dynamic_field=True)
379
+
380
+ # Add ID field (string)
381
+ schema.add_field(
382
+ field_name="id",
383
+ datatype=DataType.VARCHAR,
384
+ is_primary=True,
385
+ max_length=512, # Maximum length for ID strings
386
+ )
387
+
388
+ # Add vector field
389
+ schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=vector_size)
390
+
391
+ # Add content field
392
+ schema.add_field(
393
+ field_name="content",
394
+ datatype=DataType.VARCHAR,
395
+ max_length=65535, # Large max length for content
396
+ )
397
+
398
+ # Create collection with schema
399
+ client.create_collection(
400
+ collection_name=collection_name,
401
+ schema=schema,
402
+ )
403
+
404
+ # Create index for vector field
405
+ index_params = client.prepare_index_params()
406
+ index_params.add_index(field_name="vector", metric_type="COSINE")
407
+ client.create_index(collection_name=collection_name, index_params=index_params)
408
+
409
+ return cls(client, collection_name, embedding_function)