sunholo 0.132.0__py3-none-any.whl → 0.134.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.
@@ -941,6 +941,65 @@ class AlloyDBClient:
941
941
  log.info(f"Updated row in {table_name} with {primary_key_column}={primary_key_value}")
942
942
 
943
943
  return result
944
+
945
+ async def check_row(self, table_name: str, primary_key_column: str, primary_key_value: str,
946
+ columns: list = None, condition: str = None):
947
+ """
948
+ Retrieves a row from the specified table based on the primary key.
949
+
950
+ Args:
951
+ table_name (str): Name of the table to query
952
+ primary_key_column (str): Name of the primary key column (e.g., 'id')
953
+ primary_key_value (str): Value of the primary key for the row to retrieve
954
+ columns (list, optional): List of column names to retrieve. If None, retrieves all columns
955
+ condition (str, optional): Additional condition for the WHERE clause
956
+
957
+ Returns:
958
+ The row data if found, None otherwise
959
+ """
960
+ # Determine which columns to select
961
+ if columns and isinstance(columns, list):
962
+ columns_str = ", ".join([f'"{col}"' for col in columns])
963
+ else:
964
+ columns_str = "*" # Select all columns if none specified
965
+
966
+ # Create the WHERE clause
967
+ where_clause = f'"{primary_key_column}" = :pk_value'
968
+ values = {'pk_value': primary_key_value}
969
+
970
+ if condition:
971
+ where_clause += f" AND ({condition})"
972
+
973
+ # Construct the SQL statement
974
+ sql = f'SELECT {columns_str} FROM "{table_name}" WHERE {where_clause} LIMIT 1'
975
+
976
+ log.info(f"Checking row in {table_name} with {primary_key_column}={primary_key_value}")
977
+
978
+ # Execute SQL based on engine type
979
+ try:
980
+ if self.engine_type == "pg8000":
981
+ # Use the synchronous method for pg8000
982
+ result = self._execute_sql_pg8000(sql, values)
983
+ # Extract the row data from the result
984
+ if result and hasattr(result, 'fetchone'):
985
+ row = result.fetchone()
986
+ if row:
987
+ # If we have column names, convert to dictionary
988
+ if hasattr(result, 'keys'):
989
+ column_names = result.keys()
990
+ return dict(zip(column_names, row))
991
+ return row
992
+ return None
993
+ else:
994
+ # Use the async method for langchain
995
+ result = await self._execute_sql_async_langchain(sql, values)
996
+ # For langchain engine, check result format and return first row if exists
997
+ if result and len(result) > 0:
998
+ return result[0]
999
+ return None
1000
+ except Exception as e:
1001
+ log.error(f"Error checking row: {e}")
1002
+ return None
944
1003
 
945
1004
  async def get_table_columns(self, table_name, schema="public"):
946
1005
  """
@@ -306,9 +306,7 @@ def search_engine_command(args):
306
306
  try:
307
307
  client = DiscoveryEngineClient(
308
308
  project_id=args.project,
309
- # data_store_id is required by __init__ but less relevant here.
310
- # Provide a default or the primary one associated with the project/engine.
311
- data_store_id=args.data_store_id_for_init,
309
+ engine_id=args.engine_id,
312
310
  location=args.location
313
311
  )
314
312
 
@@ -525,8 +523,6 @@ def setup_discovery_engine_subparser(subparsers):
525
523
  search_engine_parser = discovery_engine_subparsers.add_parser('search-engine', help='Search a Discovery Engine (fetches documents/summary)')
526
524
  search_engine_parser.add_argument('--query', required=True, help='The search query')
527
525
  search_engine_parser.add_argument('--engine-id', required=True, help='Engine ID to search')
528
- # Add data_store_id needed for client init, maybe make it optional if client handles it?
529
- search_engine_parser.add_argument('--data-store-id-for-init', required=True, help='A primary data store ID associated with the project/engine (for client init)')
530
526
  search_engine_parser.add_argument('--serving-config-id', default='default_config', help='Serving config ID for the engine')
531
527
  search_engine_parser.add_argument('--collection-id', default='default_collection', help='Collection ID for the engine path')
532
528
  search_engine_parser.add_argument('--page-size', type=int, default=10, help='Max results per page')
@@ -67,12 +67,19 @@ class DiscoveryEngineClient:
67
67
  print(f"Document Name: {chunk_document_name}")
68
68
  ```
69
69
  """
70
- def __init__(self, data_store_id, project_id, location="eu"):
70
+ def __init__(self, data_store_id=None, engine_id=None, project_id=None, location="eu"):
71
71
  if not discoveryengine:
72
72
  raise ImportError("Google Cloud Discovery Engine not available, install via `pip install sunholo[gcp]`")
73
+
74
+ if project_id is None:
75
+ raise ValueError("Must specify project_id")
76
+
77
+ if data_store_id is None and engine_id is None:
78
+ raise ValueError("Must specify at least one of data_store_id or engine_id")
73
79
 
74
80
  self.project_id = project_id
75
81
  self.data_store_id = data_store_id
82
+ self.engine_id = engine_id
76
83
  self.location = location
77
84
  client_options = (
78
85
  ClientOptions(api_endpoint=f"{location}-discoveryengine.googleapis.com")
@@ -469,7 +476,7 @@ class DiscoveryEngineClient:
469
476
  operation = self.engine_client.create_engine(request=request)
470
477
  except AlreadyExists as err:
471
478
  log.info(f"Engine already exists: - {str(err)}")
472
-
479
+ self.engine_id = engine_id
473
480
  return engine_id
474
481
 
475
482
  log.info(f"Waiting for create vertex ai search operation to complete: {operation.operation.name}")
@@ -481,7 +488,7 @@ class DiscoveryEngineClient:
481
488
 
482
489
  # Handle the response
483
490
  log.info(f"{response=} {metadata=}")
484
-
491
+ self.engine_id = engine_id
485
492
  return operation.operation.name
486
493
 
487
494
  def _import_document_request(self,
@@ -862,7 +869,7 @@ class DiscoveryEngineClient:
862
869
  def search_engine(
863
870
  self,
864
871
  search_query: str,
865
- engine_id: str,
872
+ engine_id: str = None,
866
873
  serving_config_id: str = "default_config",
867
874
  page_size: int = 10,
868
875
  return_snippet: bool = True,
@@ -890,7 +897,7 @@ class DiscoveryEngineClient:
890
897
 
891
898
  Args:
892
899
  search_query: The user's search query string.
893
- engine_id: The ID of the search engine to query.
900
+ engine_id: The ID of the search engine to query or uses class engine_id it init with.
894
901
  serving_config_id: The ID of the specific serving config for the engine.
895
902
  page_size: Maximum number of results per page.
896
903
  return_snippet: Whether to request snippets in the results.
@@ -961,13 +968,20 @@ class DiscoveryEngineClient:
961
968
  log.error("Discovery Engine library not available at runtime.")
962
969
  return None
963
970
 
971
+ if engine_id:
972
+ self.engine_id = engine_id
973
+
974
+ if engine_id is None and self.engine_id is None:
975
+ raise ValueError("Could not find self.engine_id")
976
+
977
+
964
978
  try:
965
979
  # Construct the serving config path for an ENGINE
966
980
  # Note: The client library path helper is for data stores/serving configs within them.
967
981
  # We need the path for an engine's serving config.
968
982
  serving_config_path = (
969
983
  f"projects/{self.project_id}/locations/{self.location}/"
970
- f"collections/{collection_id}/engines/{engine_id}/"
984
+ f"collections/{collection_id}/engines/{self.engine_id}/"
971
985
  f"servingConfigs/{serving_config_id}"
972
986
  )
973
987
  log.info(f"Using Engine Serving Config Path: {serving_config_path}")
@@ -1029,22 +1043,22 @@ class DiscoveryEngineClient:
1029
1043
  # Add other relevant fields like facet_specs if needed
1030
1044
  )
1031
1045
 
1032
- log.info(f"Searching engine '{engine_id}' with request: {request}")
1046
+ log.info(f"Searching engine '{self.engine_id}' with request: {request}")
1033
1047
  response_pager = self.search_client.search(request)
1034
- log.info(f"Search successful for query '{search_query}' against engine '{engine_id}'.")
1048
+ log.info(f"Search successful for query '{search_query}' against engine '{self.engine_id}'.")
1035
1049
  return response_pager
1036
1050
 
1037
1051
  except GoogleAPIError as e:
1038
- log.error(f"API error searching engine '{engine_id}': {e}")
1052
+ log.error(f"API error searching engine '{self.engine_id}': {e}")
1039
1053
  return None
1040
1054
  except Exception as e:
1041
- log.error(f"Unexpected error searching engine '{engine_id}': {e}\n{traceback.format_exc()}")
1055
+ log.error(f"Unexpected error searching engine '{self.engine_id}': {e}\n{traceback.format_exc()}")
1042
1056
  return None
1043
1057
 
1044
1058
  async def async_search_engine(
1045
1059
  self,
1046
1060
  search_query: str,
1047
- engine_id: str,
1061
+ engine_id: str=None,
1048
1062
  serving_config_id: str = "default_config",
1049
1063
  page_size: int = 10,
1050
1064
  return_snippet: bool = True,
@@ -1077,6 +1091,12 @@ class DiscoveryEngineClient:
1077
1091
  An SearchAsyncPager object to iterate through results asynchronously,
1078
1092
  or None if an error occurs or the async client is not available.
1079
1093
  """
1094
+ if engine_id:
1095
+ self.engine_id = engine_id
1096
+
1097
+ if engine_id is None and self.engine_id is None:
1098
+ raise ValueError("Could not find self.engine_id")
1099
+
1080
1100
  if not self.async_search_client:
1081
1101
  log.error("Cannot call async_search_engine: Async client not initialized.")
1082
1102
  raise RuntimeError("Async client not initialized. Ensure class is instantiated within an async context.")
@@ -1085,7 +1105,7 @@ class DiscoveryEngineClient:
1085
1105
  # Construct the serving config path for an ENGINE (same as sync)
1086
1106
  serving_config_path = (
1087
1107
  f"projects/{self.project_id}/locations/{self.location}/"
1088
- f"collections/{collection_id}/engines/{engine_id}/"
1108
+ f"collections/{collection_id}/engines/{self.engine_id}/"
1089
1109
  f"servingConfigs/{serving_config_id}"
1090
1110
  )
1091
1111
  log.info(f"Using Async Engine Serving Config Path: {serving_config_path}")
@@ -1138,16 +1158,16 @@ class DiscoveryEngineClient:
1138
1158
  user_pseudo_id=user_pseudo_id,
1139
1159
  )
1140
1160
 
1141
- log.info(f"Async searching engine '{engine_id}' with request: {request}")
1161
+ log.info(f"Async searching engine '{self.engine_id}' with request: {request}")
1142
1162
  response_pager = await self.async_search_client.search(request)
1143
- log.info(f"Async search successful for query '{search_query}' against engine '{engine_id}'.")
1163
+ log.info(f"Async search successful for query '{search_query}' against engine '{self.engine_id}'.")
1144
1164
  return response_pager
1145
1165
 
1146
1166
  except GoogleAPIError as e:
1147
- log.error(f"Async API error searching engine '{engine_id}': {e}")
1167
+ log.error(f"Async API error searching engine '{self.engine_id}': {e}")
1148
1168
  return None
1149
1169
  except Exception as e:
1150
- log.error(f"Async unexpected error searching engine '{engine_id}': {e}\n{traceback.format_exc()}")
1170
+ log.error(f"Async unexpected error searching engine '{self.engine_id}': {e}\n{traceback.format_exc()}")
1151
1171
  return None
1152
1172
 
1153
1173
  # --- End of DiscoveryEngineClient class ---
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.132.0
3
+ Version: 0.134.0
4
4
  Summary: AI DevOps - a package to help deploy GenAI to the Cloud.
5
5
  Author-email: Holosun ApS <multivac@sunholo.com>
6
6
  License: Apache License, Version 2.0
@@ -60,7 +60,7 @@ sunholo/components/retriever.py,sha256=Wmchv3huAM4w7DIS-a5Lp9Hi7M8pE6vZdxgseiT9S
60
60
  sunholo/components/vectorstore.py,sha256=k7GS1Y5c6ZGXSDAJvyCes6dTjhDAi0fjGbVLqpyfzBc,5918
61
61
  sunholo/database/__init__.py,sha256=bpB5Nk21kwqYj-qdVnvNgXjLsbflnH4g-San7OHMqR4,283
62
62
  sunholo/database/alloydb.py,sha256=x1zUMB-EVWbE2Zvp4nAs2Z-tB_kOZmS45H2lwVHdYnk,11678
63
- sunholo/database/alloydb_client.py,sha256=VCnjwzY3pw75wUmlHXIfF5oGhy7iyzo7VQVEs3MT4YY,53350
63
+ sunholo/database/alloydb_client.py,sha256=gDYlg3sD88Nd5llGRpO7maRQ1KOnFT9XBFvS7Tx1fXM,56026
64
64
  sunholo/database/database.py,sha256=VqhZdkXUNdvWn8sUcUV3YNby1JDVf7IykPVXWBtxo9U,7361
65
65
  sunholo/database/lancedb.py,sha256=DyfZntiFKBlVPaFooNN1Z6Pl-LAs4nxWKKuq8GBqN58,715
66
66
  sunholo/database/static_dbs.py,sha256=8cvcMwUK6c32AS2e_WguKXWMkFf5iN3g9WHzsh0C07Q,442
@@ -73,9 +73,9 @@ sunholo/database/sql/sb/return_sources.sql,sha256=89KAnxfK8n_qGK9jy1OQT8f9n4uYUt
73
73
  sunholo/database/sql/sb/setup.sql,sha256=CvoFvZQev2uWjmFa3aj3m3iuPFzAAJZ0S7Qi3L3-zZI,89
74
74
  sunholo/discovery_engine/__init__.py,sha256=hLgqRDJ22Aov9o2QjAEfsVgnL3kMdM-g5p8RJ9OyKdQ,130
75
75
  sunholo/discovery_engine/chunker_handler.py,sha256=wkvXl4rFtYfN6AZUKdW9_QD49Whf77BukDbO82UwlAg,7480
76
- sunholo/discovery_engine/cli.py,sha256=BWYNG3V1vq3mOcmkAeQItSdTw7HeFzbCpHkfYuau2BE,32382
76
+ sunholo/discovery_engine/cli.py,sha256=5C53qLRojGBbHYedL5ndgSnfnfxNx4cQfbr5Yt0XaJQ,31943
77
77
  sunholo/discovery_engine/create_new.py,sha256=WUi4_xh_dFaGX3xA9jkNKZhaR6LCELjMPeRb0hyj4FU,1226
78
- sunholo/discovery_engine/discovery_engine_client.py,sha256=nV4loBBnOqGPRlEbFIFRJEB6cXKDVgJOMRMdf4-v510,51775
78
+ sunholo/discovery_engine/discovery_engine_client.py,sha256=lB6D05ZOXm9Avl6hM6vJZvPZD_TzNroyBl-E5cJYWAk,52661
79
79
  sunholo/discovery_engine/get_ai_search_chunks.py,sha256=I6Dt1CznqEvE7XIZ2PkLqopmjpO96iVEWJJqL5cJjOU,5554
80
80
  sunholo/embedder/__init__.py,sha256=sI4N_CqgEVcrMDxXgxKp1FsfsB4FpjoXgPGkl4N_u4I,44
81
81
  sunholo/embedder/embed_chunk.py,sha256=did2pKkWM2o0KkRcb0H9l2x_WjCq6OyuHDxGbITFKPM,6530
@@ -168,9 +168,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
168
168
  sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
169
169
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
170
170
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
171
- sunholo-0.132.0.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
172
- sunholo-0.132.0.dist-info/METADATA,sha256=9Mpo28cEMCX2kwTQQrXK7sih-dNgQ_4JwXTbB0Kicvc,10067
173
- sunholo-0.132.0.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
174
- sunholo-0.132.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
175
- sunholo-0.132.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
176
- sunholo-0.132.0.dist-info/RECORD,,
171
+ sunholo-0.134.0.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
172
+ sunholo-0.134.0.dist-info/METADATA,sha256=HmMI41CeVVPHSbxjzplCValCd4WANWPtuLUvR2V9unE,10067
173
+ sunholo-0.134.0.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
174
+ sunholo-0.134.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
175
+ sunholo-0.134.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
176
+ sunholo-0.134.0.dist-info/RECORD,,