Skip to content

Commit 2a6540d

Browse files
committed
phase 3 adiitons for qdrant support
1 parent 300ce92 commit 2a6540d

File tree

5 files changed

+906
-2
lines changed

5 files changed

+906
-2
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ dependencies = [
3737
"pyyaml>=6.0.0",
3838
"pydantic-settings>=2.0.0",
3939
"redis>=5.0.0",
40+
"qdrant-client>=1.7.0",
4041
]
4142

4243
[project.optional-dependencies]

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ pyjwt>=2.8.0
55
cryptography>=41.0.0
66
redis>=5.0.0
77
pyyaml>=6.0.0
8-
pydantic-settings>=2.0.0
8+
pydantic-settings>=2.0.0
9+
qdrant-client>=1.7.0

symbiont/client.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
AgentMetrics,
2727
AgentStatusResponse,
2828
AnalysisResults,
29+
# Phase 3 Qdrant Integration models
30+
CollectionCreateRequest,
31+
CollectionInfo,
32+
CollectionResponse,
2933
ConsolidationResponse,
3034
# Configuration models (Phase 1)
3135
ContextQuery,
@@ -66,8 +70,10 @@
6670
SigningRequest,
6771
SigningResponse,
6872
SystemMetrics,
73+
UpsertResponse,
6974
VectorSearchRequest,
7075
VectorSearchResponse,
76+
VectorUpsertRequest,
7177
WebhookTriggerRequest,
7278
WebhookTriggerResponse,
7379
WorkflowExecutionRequest,
@@ -1147,3 +1153,131 @@ def list_agent_memories(self, agent_id: str, limit: int = 100) -> MemorySearchRe
11471153
}
11481154
response = self._request("GET", "memory/agent", params=params)
11491155
return MemorySearchResponse(**response.json())
1156+
1157+
# =============================================================================
1158+
# Phase 3 Qdrant Vector Database Methods
1159+
# =============================================================================
1160+
1161+
def create_vector_collection(self, collection_request: Union[CollectionCreateRequest, Dict[str, Any]]) -> CollectionResponse:
1162+
"""Create a new vector collection.
1163+
1164+
Args:
1165+
collection_request: Collection creation request
1166+
1167+
Returns:
1168+
CollectionResponse: Collection creation result
1169+
"""
1170+
if isinstance(collection_request, dict):
1171+
collection_request = CollectionCreateRequest(**collection_request)
1172+
1173+
response = self._request("POST", "vectors/collections", json=collection_request.model_dump())
1174+
return CollectionResponse(**response.json())
1175+
1176+
def delete_vector_collection(self, collection_name: str) -> Dict[str, Any]:
1177+
"""Delete a vector collection.
1178+
1179+
Args:
1180+
collection_name: Name of the collection to delete
1181+
1182+
Returns:
1183+
Dict[str, Any]: Deletion confirmation
1184+
"""
1185+
response = self._request("DELETE", f"vectors/collections/{collection_name}")
1186+
return response.json()
1187+
1188+
def get_collection_info(self, collection_name: str) -> CollectionInfo:
1189+
"""Get information about a vector collection.
1190+
1191+
Args:
1192+
collection_name: Name of the collection
1193+
1194+
Returns:
1195+
CollectionInfo: Collection information
1196+
"""
1197+
response = self._request("GET", f"vectors/collections/{collection_name}")
1198+
return CollectionInfo(**response.json())
1199+
1200+
def list_vector_collections(self) -> List[str]:
1201+
"""List all vector collections.
1202+
1203+
Returns:
1204+
List[str]: List of collection names
1205+
"""
1206+
response = self._request("GET", "vectors/collections")
1207+
return response.json()
1208+
1209+
def add_vectors(self, upsert_request: Union[VectorUpsertRequest, Dict[str, Any]]) -> UpsertResponse:
1210+
"""Add vectors to a collection.
1211+
1212+
Args:
1213+
upsert_request: Vector upsert request
1214+
1215+
Returns:
1216+
UpsertResponse: Upsert operation result
1217+
"""
1218+
if isinstance(upsert_request, dict):
1219+
upsert_request = VectorUpsertRequest(**upsert_request)
1220+
1221+
response = self._request("POST", "vectors/upsert", json=upsert_request.model_dump())
1222+
return UpsertResponse(**response.json())
1223+
1224+
def get_vectors(self, collection_name: str, vector_ids: List[Union[str, int]]) -> List[Dict[str, Any]]:
1225+
"""Get vectors by IDs from a collection.
1226+
1227+
Args:
1228+
collection_name: Name of the collection
1229+
vector_ids: List of vector IDs to retrieve
1230+
1231+
Returns:
1232+
List[Dict[str, Any]]: Retrieved vectors
1233+
"""
1234+
params = {
1235+
"collection_name": collection_name,
1236+
"ids": vector_ids
1237+
}
1238+
response = self._request("GET", "vectors/retrieve", params=params)
1239+
return response.json()
1240+
1241+
def search_vectors(self, search_request: Union[VectorSearchRequest, Dict[str, Any]]) -> VectorSearchResponse:
1242+
"""Search vectors using similarity search.
1243+
1244+
Args:
1245+
search_request: Vector search request
1246+
1247+
Returns:
1248+
VectorSearchResponse: Search results
1249+
"""
1250+
if isinstance(search_request, dict):
1251+
search_request = VectorSearchRequest(**search_request)
1252+
1253+
response = self._request("POST", "vectors/search", json=search_request.model_dump())
1254+
return VectorSearchResponse(**response.json())
1255+
1256+
def delete_vectors(self, collection_name: str, vector_ids: List[Union[str, int]]) -> Dict[str, Any]:
1257+
"""Delete vectors from a collection.
1258+
1259+
Args:
1260+
collection_name: Name of the collection
1261+
vector_ids: List of vector IDs to delete
1262+
1263+
Returns:
1264+
Dict[str, Any]: Deletion confirmation
1265+
"""
1266+
data = {
1267+
"collection_name": collection_name,
1268+
"ids": vector_ids
1269+
}
1270+
response = self._request("DELETE", "vectors/delete", json=data)
1271+
return response.json()
1272+
1273+
def count_vectors(self, collection_name: str) -> int:
1274+
"""Count vectors in a collection.
1275+
1276+
Args:
1277+
collection_name: Name of the collection
1278+
1279+
Returns:
1280+
int: Number of vectors in the collection
1281+
"""
1282+
response = self._request("GET", f"vectors/collections/{collection_name}/count")
1283+
return response.json()["count"]

symbiont/models.py

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from datetime import datetime
44
from enum import Enum
5-
from typing import Any, Dict, List, Optional
5+
from typing import Any, Dict, List, Optional, Union
66

77
from pydantic import BaseModel, Field
88

@@ -788,3 +788,99 @@ class MemorySearchResult(BaseModel):
788788
relevance_score: float = Field(..., description="Relevance score for the search query")
789789
match_reason: str = Field(..., description="Reason for the match")
790790
highlighted_content: Optional[Dict[str, Any]] = Field(None, description="Content with search highlights")
791+
792+
793+
# =============================================================================
794+
# Phase 3 Qdrant Integration Models
795+
# =============================================================================
796+
797+
class Vector(BaseModel):
798+
"""Vector representation for Qdrant."""
799+
id: Union[str, int] = Field(..., description="Vector identifier")
800+
values: List[float] = Field(..., description="Vector values/embeddings")
801+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Vector metadata")
802+
803+
804+
class Point(BaseModel):
805+
"""Point representation for Qdrant."""
806+
id: Union[str, int] = Field(..., description="Point identifier")
807+
vector: Union[List[float], Dict[str, List[float]]] = Field(..., description="Vector data")
808+
payload: Dict[str, Any] = Field(default_factory=dict, description="Point payload/metadata")
809+
810+
811+
class SearchQuery(BaseModel):
812+
"""Search query for vector similarity search."""
813+
vector: List[float] = Field(..., description="Query vector")
814+
limit: int = Field(10, description="Maximum number of results")
815+
score_threshold: Optional[float] = Field(None, description="Minimum similarity score")
816+
filter: Optional[Dict[str, Any]] = Field(None, description="Payload filter conditions")
817+
with_payload: bool = Field(True, description="Include payload in results")
818+
with_vector: bool = Field(False, description="Include vectors in results")
819+
820+
821+
class CollectionCreateRequest(BaseModel):
822+
"""Request to create a new vector collection."""
823+
name: str = Field(..., description="Collection name")
824+
vector_size: int = Field(..., description="Vector dimension size")
825+
distance: str = Field("Cosine", description="Distance metric (Cosine, Euclidean, Dot)")
826+
on_disk_payload: bool = Field(False, description="Store payload on disk")
827+
hnsw_config: Optional[Dict[str, Any]] = Field(None, description="HNSW configuration")
828+
optimizers_config: Optional[Dict[str, Any]] = Field(None, description="Optimizer configuration")
829+
830+
831+
class CollectionResponse(BaseModel):
832+
"""Response from collection operations."""
833+
collection_name: str = Field(..., description="Collection name")
834+
status: str = Field(..., description="Operation status")
835+
result: Optional[Dict[str, Any]] = Field(None, description="Operation result details")
836+
837+
838+
class CollectionInfo(BaseModel):
839+
"""Information about a vector collection."""
840+
collection_name: str = Field(..., description="Collection name")
841+
config: Dict[str, Any] = Field(..., description="Collection configuration")
842+
status: str = Field(..., description="Collection status")
843+
vectors_count: int = Field(..., description="Number of vectors")
844+
indexed_vectors_count: int = Field(..., description="Number of indexed vectors")
845+
points_count: int = Field(..., description="Number of points")
846+
847+
848+
class VectorUpsertRequest(BaseModel):
849+
"""Request to upsert vectors into a collection."""
850+
collection_name: str = Field(..., description="Target collection name")
851+
points: List[Point] = Field(..., description="Points to upsert")
852+
wait: bool = Field(True, description="Wait for operation completion")
853+
854+
855+
class UpsertResponse(BaseModel):
856+
"""Response from vector upsert operation."""
857+
collection_name: str = Field(..., description="Collection name")
858+
operation_id: Optional[str] = Field(None, description="Operation identifier")
859+
status: str = Field(..., description="Operation status")
860+
points_count: int = Field(..., description="Number of points processed")
861+
862+
863+
class VectorPoint(BaseModel):
864+
"""Vector point with metadata."""
865+
id: Union[str, int] = Field(..., description="Point identifier")
866+
vector: List[float] = Field(..., description="Vector values")
867+
payload: Dict[str, Any] = Field(default_factory=dict, description="Point metadata")
868+
score: Optional[float] = Field(None, description="Similarity score (for search results)")
869+
870+
871+
class EmbeddingRequest(BaseModel):
872+
"""Request to generate embeddings."""
873+
texts: List[str] = Field(..., description="Texts to embed")
874+
model: str = Field("default", description="Embedding model to use")
875+
collection_name: Optional[str] = Field(None, description="Target collection")
876+
metadata: Optional[List[Dict[str, Any]]] = Field(None, description="Metadata for each text")
877+
878+
879+
class EmbeddingResponse(BaseModel):
880+
"""Response from embedding generation."""
881+
embeddings: List[List[float]] = Field(..., description="Generated embeddings")
882+
model: str = Field(..., description="Model used for embedding")
883+
processing_time_ms: float = Field(..., description="Processing time in milliseconds")
884+
token_count: Optional[int] = Field(None, description="Total token count processed")
885+
886+

0 commit comments

Comments
 (0)