@@ -52,10 +52,11 @@ def __init__(
5252 """
5353 super ().__init__ (** kwargs )
5454 self .connection_string = connection_string or os .getenv (
55- "FLOW_PGVECTOR_CONNECTION_STRING" , "postgresql://localhost/postgres"
55+ "FLOW_PGVECTOR_CONNECTION_STRING" ,
56+ "postgresql://localhost/postgres" ,
5657 )
5758 self .async_connection_string = async_connection_string or os .getenv (
58- "FLOW_PGVECTOR_ASYNC_CONNECTION_STRING"
59+ "FLOW_PGVECTOR_ASYNC_CONNECTION_STRING" ,
5960 )
6061 self .batch_size = batch_size
6162
@@ -68,15 +69,15 @@ def __init__(
6869 # Initialize async connection if async_connection_string is provided
6970 self ._async_conn = None
7071 if self .async_connection_string :
71- import asyncpg
72-
7372 # We'll create the async connection lazily in async methods
7473 self ._async_conn_string = self .async_connection_string
7574 else :
7675 # Convert sync connection string to asyncpg format
7776 if self .connection_string .startswith ("postgresql://" ):
7877 self ._async_conn_string = self .connection_string .replace (
79- "postgresql://" , "postgresql+asyncpg://" , 1
78+ "postgresql://" ,
79+ "postgresql+asyncpg://" ,
80+ 1 ,
8081 )
8182 else :
8283 self ._async_conn_string = self .connection_string
@@ -87,7 +88,7 @@ def __init__(
8788 self ._conn .commit ()
8889
8990 logger .info (
90- f"PostgreSQL pgvector client initialized with connection_string={ self .connection_string } "
91+ f"PostgreSQL pgvector client initialized with connection_string={ self .connection_string } " ,
9192 )
9293
9394 def _get_table_name (self , workspace_id : str ) -> str :
@@ -166,22 +167,22 @@ def create_workspace(self, workspace_id: str, **kwargs):
166167 metadata JSONB NOT NULL,
167168 vector vector({ dimensions } ) NOT NULL
168169 )
169- """
170+ """ ,
170171 )
171172 # Create index for vector similarity search
172173 cur .execute (
173174 f"""
174175 CREATE INDEX IF NOT EXISTS "{ table_name } _vector_idx"
175176 ON "{ table_name } " USING ivfflat (vector vector_cosine_ops)
176177 WITH (lists = 100)
177- """
178+ """ ,
178179 )
179180 # Create index for metadata filtering
180181 cur .execute (
181182 f"""
182183 CREATE INDEX IF NOT EXISTS "{ table_name } _metadata_idx"
183184 ON "{ table_name } " USING gin (metadata)
184- """
185+ """ ,
185186 )
186187 self ._conn .commit ()
187188 logger .info (f"Created workspace table: { table_name } with vector({ dimensions } )" )
@@ -201,7 +202,7 @@ def list_workspace(self, **kwargs) -> List[str]:
201202 WHERE table_schema = 'public'
202203 AND table_name LIKE 'workspace_%'
203204 ORDER BY table_name
204- """
205+ """ ,
205206 )
206207 table_names = [row [0 ] for row in cur .fetchall ()]
207208 # Remove 'workspace_' prefix
@@ -249,7 +250,6 @@ def refresh(self, workspace_id: str):
249250 workspace_id: The identifier of the workspace (unused).
250251 """
251252 # PostgreSQL doesn't need explicit refresh like Elasticsearch
252- pass
253253
254254 @staticmethod
255255 def _row2node (row : Tuple , workspace_id : str ) -> VectorNode :
@@ -268,13 +268,13 @@ def _row2node(row: Tuple, workspace_id: str) -> VectorNode:
268268
269269 # pgvector returns vector as string like '[0.1,0.2,0.3]'
270270 vector = json .loads (vector_str )
271-
271+
272272 # Parse metadata if it's a string (psycopg may return JSONB as string in some cases)
273273 if isinstance (metadata , str ):
274274 metadata = json .loads (metadata )
275275 elif metadata is None :
276276 metadata = {}
277-
277+
278278 node = VectorNode (
279279 unique_id = unique_id ,
280280 workspace_id = workspace_id_col or workspace_id ,
@@ -285,7 +285,10 @@ def _row2node(row: Tuple, workspace_id: str) -> VectorNode:
285285 return node
286286
287287 @staticmethod
288- def _build_sql_filters (filter_dict : Optional [Dict [str , Any ]] = None , use_async : bool = False ) -> Tuple [str , List [Any ]]:
288+ def _build_sql_filters (
289+ filter_dict : Optional [Dict [str , Any ]] = None ,
290+ use_async : bool = False ,
291+ ) -> Tuple [str , List [Any ]]:
289292 """Build SQL WHERE clause from filter_dict.
290293
291294 Converts a filter dictionary into SQL WHERE conditions.
@@ -306,7 +309,6 @@ def _build_sql_filters(filter_dict: Optional[Dict[str, Any]] = None, use_async:
306309 conditions = []
307310 params = []
308311 param_idx = 1
309- placeholder = "$%d" if use_async else "%s"
310312
311313 for key , filter_value in filter_dict .items ():
312314 # Handle nested keys by using JSONB path operators
@@ -394,7 +396,7 @@ def search(
394396 # Cosine similarity = 1 - cosine distance
395397 # Convert query_vector to string format for pgvector
396398 query_vector_str = "[" + "," .join (str (v ) for v in query_vector ) + "]"
397-
399+
398400 with self ._conn .cursor () as cur :
399401 # Parameter order in SQL: SELECT %s::vector, WHERE %s..., ORDER BY %s::vector, LIMIT %s
400402 # So parameters should be: [query_vector_str] + filter_params + [query_vector_str, top_k]
@@ -418,13 +420,13 @@ def search(
418420
419421 # pgvector returns vector as string like '[0.1,0.2,0.3]'
420422 vector = json .loads (vector_str )
421-
423+
422424 # Parse metadata if it's a string (psycopg may return JSONB as string in some cases)
423425 if isinstance (metadata , str ):
424426 metadata = json .loads (metadata )
425427 elif metadata is None :
426428 metadata = {}
427-
429+
428430 node = VectorNode (
429431 unique_id = unique_id ,
430432 workspace_id = workspace_id_col or workspace_id ,
@@ -437,13 +439,12 @@ def search(
437439
438440 return nodes
439441
440- def insert (self , nodes : VectorNode | List [VectorNode ], workspace_id : str , refresh : bool = True , ** kwargs ):
442+ def insert (self , nodes : VectorNode | List [VectorNode ], workspace_id : str , ** kwargs ):
441443 """Insert vector nodes into the PostgreSQL table.
442444
443445 Args:
444446 nodes: A single VectorNode or list of VectorNodes to insert.
445447 workspace_id: The identifier of the workspace to insert into.
446- refresh: Whether to refresh after insertion (default: True, kept for API compatibility).
447448 **kwargs: Additional keyword arguments (unused).
448449 """
449450 if not self .exist_workspace (workspace_id = workspace_id ):
@@ -474,15 +475,15 @@ def insert(self, nodes: VectorNode | List[VectorNode], workspace_id: str, refres
474475 vector_str = "[" + "," .join (str (v ) for v in vector_value ) + "]"
475476 else :
476477 vector_str = str (vector_value )
477-
478+
478479 values .append (
479480 (
480481 node .unique_id ,
481482 workspace_id ,
482483 node .content ,
483484 json .dumps (node .metadata ),
484485 vector_str ,
485- )
486+ ),
486487 )
487488
488489 # Use INSERT ... ON CONFLICT for upsert
@@ -502,13 +503,12 @@ def insert(self, nodes: VectorNode | List[VectorNode], workspace_id: str, refres
502503 self ._conn .commit ()
503504 logger .info (f"insert nodes.size={ len (all_nodes )} into workspace_id={ workspace_id } " )
504505
505- def delete (self , node_ids : str | List [str ], workspace_id : str , refresh : bool = True , ** kwargs ):
506+ def delete (self , node_ids : str | List [str ], workspace_id : str , ** kwargs ):
506507 """Delete vector nodes from the PostgreSQL table.
507508
508509 Args:
509510 node_ids: A single node ID or list of node IDs to delete.
510511 workspace_id: The identifier of the workspace to delete from.
511- refresh: Whether to refresh after deletion (default: True, kept for API compatibility).
512512 **kwargs: Additional keyword arguments (unused).
513513 """
514514 if not self .exist_workspace (workspace_id = workspace_id ):
@@ -543,7 +543,7 @@ async def _get_async_conn(self):
543543
544544 logger .debug (f"Establishing async PostgreSQL connection: { conn_str } " )
545545 self ._async_conn = await asyncpg .connect (conn_str )
546- logger .debug (f "Async PostgreSQL connection established successfully" )
546+ logger .debug ("Async PostgreSQL connection established successfully" )
547547 return self ._async_conn
548548
549549 async def async_exist_workspace (self , workspace_id : str , ** kwargs ) -> bool :
@@ -607,22 +607,22 @@ async def async_create_workspace(self, workspace_id: str, **kwargs):
607607 metadata JSONB NOT NULL,
608608 vector vector({ dimensions } ) NOT NULL
609609 )
610- """
610+ """ ,
611611 )
612612 # Create index for vector similarity search
613613 await conn .execute (
614614 f"""
615615 CREATE INDEX IF NOT EXISTS "{ table_name } _vector_idx"
616616 ON "{ table_name } " USING ivfflat (vector vector_cosine_ops)
617617 WITH (lists = 100)
618- """
618+ """ ,
619619 )
620620 # Create index for metadata filtering
621621 await conn .execute (
622622 f"""
623623 CREATE INDEX IF NOT EXISTS "{ table_name } _metadata_idx"
624624 ON "{ table_name } " USING gin (metadata)
625- """
625+ """ ,
626626 )
627627 logger .info (f"Created workspace table: { table_name } with vector({ dimensions } )" )
628628
@@ -633,7 +633,6 @@ async def async_refresh(self, workspace_id: str):
633633 workspace_id: The identifier of the workspace (unused).
634634 """
635635 # PostgreSQL doesn't need explicit refresh like Elasticsearch
636- pass
637636
638637 async def async_search (
639638 self ,
@@ -686,7 +685,7 @@ async def async_search(
686685 new_placeholder = f"${ param_offset + i + 1 } "
687686 adjusted_where = adjusted_where .replace (old_placeholder , new_placeholder )
688687 where_sql = f"WHERE { adjusted_where } " if adjusted_where else ""
689-
688+
690689 # Calculate the last parameter index for top_k
691690 top_k_param_idx = 1 + len (filter_params ) + 1 # $1 (query_vector) + filter_params + 1
692691
@@ -700,7 +699,7 @@ async def async_search(
700699 ORDER BY vector <=> $1::vector
701700 LIMIT ${ top_k_param_idx }
702701 """
703-
702+
704703 # Parameter order: query_vector ($1), filter_params ($2, $3, ...), top_k ($last)
705704 rows = await conn .fetch (query_sql , query_vector_str , * filter_params , top_k )
706705
@@ -718,13 +717,13 @@ async def async_search(
718717
719718 # pgvector returns vector as string like '[0.1,0.2,0.3]'
720719 vector = json .loads (vector_str )
721-
720+
722721 # Parse metadata if it's a string (asyncpg may return JSONB as string)
723722 if isinstance (metadata , str ):
724723 metadata = json .loads (metadata )
725724 elif metadata is None :
726725 metadata = {}
727-
726+
728727 node = VectorNode (
729728 unique_id = unique_id ,
730729 workspace_id = workspace_id_col or workspace_id ,
@@ -741,15 +740,13 @@ async def async_insert(
741740 self ,
742741 nodes : VectorNode | List [VectorNode ],
743742 workspace_id : str ,
744- refresh : bool = True ,
745743 ** kwargs ,
746744 ):
747745 """Insert vector nodes into the PostgreSQL table (async).
748746
749747 Args:
750748 nodes: A single VectorNode or list of VectorNodes to insert.
751749 workspace_id: The identifier of the workspace to insert into.
752- refresh: Whether to refresh after insertion (default: True, kept for API compatibility).
753750 **kwargs: Additional keyword arguments (unused).
754751 """
755752 if not await self .async_exist_workspace (workspace_id = workspace_id ):
@@ -802,13 +799,12 @@ async def async_insert(
802799
803800 logger .info (f"async insert nodes.size={ len (all_nodes )} into workspace_id={ workspace_id } " )
804801
805- async def async_delete (self , node_ids : str | List [str ], workspace_id : str , refresh : bool = True , ** kwargs ):
802+ async def async_delete (self , node_ids : str | List [str ], workspace_id : str , ** kwargs ):
806803 """Delete vector nodes from the PostgreSQL table (async).
807804
808805 Args:
809806 node_ids: A single node ID or list of node IDs to delete.
810807 workspace_id: The identifier of the workspace to delete from.
811- refresh: Whether to refresh after deletion (default: True, kept for API compatibility).
812808 **kwargs: Additional keyword arguments (unused).
813809 """
814810 if not await self .async_exist_workspace (workspace_id = workspace_id ):
@@ -836,4 +832,3 @@ async def async_close(self):
836832 if self ._async_conn :
837833 await self ._async_conn .close ()
838834 self ._async_conn = None
839-
0 commit comments