sunholo 0.123.0__py3-none-any.whl → 0.123.2__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.
@@ -531,128 +531,182 @@ class AlloyDBClient:
531
531
 
532
532
  return await self.engine._afetch(query=sql_statement)
533
533
 
534
- async def create_table_from_schema(self, table_name: str, schema_data: dict, users: list = None):
535
- """
536
- Creates or ensures a table exists based on the structure of the provided schema data.
537
-
538
- Args:
539
- table_name (str): Name of the table to create
540
- schema_data (dict): Data structure that matches the expected schema
541
- users (list, optional): List of users to grant permissions to
534
+ async def check_connection(self):
535
+ """
536
+ Checks if the database connection is still valid.
542
537
 
543
- Returns:
544
- Result of SQL execution
545
- """
546
- # Generate column definitions from schema data
547
- columns = []
548
- for key, value in schema_data.items():
549
- if isinstance(value, dict):
550
- # For nested objects, store as JSONB
551
- columns.append(f'"{key}" JSONB')
552
- elif isinstance(value, list):
553
- # For arrays, store as JSONB
554
- columns.append(f'"{key}" JSONB')
555
- elif isinstance(value, int):
556
- columns.append(f'"{key}" INTEGER')
557
- elif isinstance(value, float):
558
- columns.append(f'"{key}" NUMERIC')
559
- elif isinstance(value, bool):
560
- columns.append(f'"{key}" BOOLEAN')
561
- else:
562
- # Default to TEXT for strings and other types
563
- columns.append(f'"{key}" TEXT')
564
-
565
- # Add metadata columns
566
- columns.extend([
567
- '"source" TEXT',
568
- '"extraction_date" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP',
569
- '"extraction_backend" TEXT',
570
- '"extraction_model" TEXT'
571
- ])
572
-
573
- # Create SQL statement for table creation
574
- columns_sql = ", ".join(columns)
575
- sql = f'''
576
- CREATE TABLE IF NOT EXISTS "{table_name}" (
577
- id SERIAL PRIMARY KEY,
578
- {columns_sql}
579
- )
580
- '''
581
-
582
- # Execute SQL to create table
583
- result = await self.execute_sql_async(sql)
584
- log.info(f"Created or ensured table {table_name} exists")
585
-
586
- # Grant permissions if users are provided
587
- if users:
588
- for user in users:
589
- await self.execute_sql_async(f'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "{table_name}" TO "{user}";')
590
-
591
- return result
538
+ Returns:
539
+ bool: True if connection is valid, False otherwise
540
+ """
541
+ try:
542
+ # Simple query to check connection
543
+ result = await self.execute_sql_async("SELECT 1")
544
+ return True
545
+ except Exception as e:
546
+ log.warning(f"Database connection check failed: {e}")
547
+ return False
592
548
 
593
- async def write_data_to_table(self, table_name: str, data: dict, metadata: dict = None):
594
- """
595
- Writes data to the specified table.
596
-
597
- Args:
598
- table_name (str): Name of the table
599
- data (dict): Data to write to the table
600
- metadata (dict, optional): Additional metadata to include
549
+ async def ensure_connected(self):
550
+ """
551
+ Ensures the database connection is valid, attempting to reconnect if necessary.
601
552
 
602
- Returns:
603
- Result of SQL execution
604
- """
605
- # Create copies to avoid modifying the original data
606
- insert_data = dict(data)
607
-
608
- # Add metadata if provided
609
- if metadata:
610
- insert_data["source"] = metadata.get("objectId", metadata.get("source", "unknown"))
611
- insert_data["extraction_backend"] = metadata.get("extraction_backend", "unknown")
612
- insert_data["extraction_model"] = metadata.get("extraction_model", "unknown")
613
-
614
- # Prepare column names and placeholders for values
615
- columns = [f'"{key}"' for key in insert_data.keys()]
616
- placeholders = []
617
- values = []
618
-
619
- # Process values and create properly formatted placeholders
620
- for key, value in insert_data.items():
621
- values.append(json.dumps(value) if isinstance(value, (dict, list)) else value)
622
- placeholders.append("%s")
623
-
624
- # Create SQL statement for insertion
625
- columns_str = ", ".join(columns)
626
- placeholders_str = ", ".join(placeholders)
627
-
628
- sql = f'''
629
- INSERT INTO "{table_name}" ({columns_str})
630
- VALUES ({placeholders_str})
631
- RETURNING id
632
- '''
633
-
634
- # Execute SQL to insert data
635
- result = await self.execute_sql_async(sql, values)
636
- log.info(f"Inserted data into table {table_name}")
637
-
638
- return result
553
+ Returns:
554
+ bool: True if connection is valid or reconnection successful, False otherwise
555
+ """
556
+ if await self.check_connection():
557
+ return True
558
+
559
+ try:
560
+ # Attempt to reconnect - implementation depends on your database driver
561
+ if self.engine_type == "pg8000":
562
+ # Re-create the engine
563
+ self.engine = self._create_engine_from_pg8000(self.user, self.password, self.database)
564
+ elif self.engine_type == "langchain":
565
+ # Re-create the engine
566
+ self.engine = self._create_engine()
567
+
568
+ log.info(f"Successfully reconnected to AlloyDB")
569
+ return True
570
+ except Exception as e:
571
+ log.error(f"Failed to reconnect to AlloyDB: {e}")
572
+ return False
639
573
 
640
- async def execute_sql_async(self, sql_statement, values=None):
641
- """
642
- Executes a given SQL statement asynchronously with optional parameter values.
643
-
644
- Args:
645
- sql_statement (str): The SQL statement to execute
646
- values (list, optional): Values for parameterized query
574
+ async def close(self):
575
+ """
576
+ Properly close the database connection.
577
+ """
578
+ try:
579
+ if self.engine_type == "pg8000":
580
+ # Close engine or connector
581
+ if hasattr(self, 'connector'):
582
+ await self.connector.close()
583
+ # For langchain engine, additional cleanup might be needed
584
+ log.info("Closed AlloyDB connection")
585
+ except Exception as e:
586
+ log.warning(f"Error closing AlloyDB connection: {e}")
587
+
588
+ async def create_table_from_schema(self, table_name: str, schema_data: dict, users: list = None):
589
+ """
590
+ Creates or ensures a table exists based on the structure of the provided schema data.
647
591
 
648
- Returns:
649
- Result of SQL execution
650
- """
651
- log.info(f"Executing async SQL statement: {sql_statement}")
652
- if self.engine_type == "pg8000":
653
- result = await self._execute_sql_async_pg8000(sql_statement, values)
654
- elif self.engine_type == "langchain":
655
- result = await self._execute_sql_async_langchain(sql_statement, values)
656
-
657
- return result
592
+ Args:
593
+ table_name (str): Name of the table to create
594
+ schema_data (dict): Data structure that matches the expected schema
595
+ users (list, optional): List of users to grant permissions to
596
+
597
+ Returns:
598
+ Result of SQL execution
599
+ """
600
+ # Generate column definitions from schema data
601
+ columns = []
602
+ for key, value in schema_data.items():
603
+ if isinstance(value, dict):
604
+ # For nested objects, store as JSONB
605
+ columns.append(f'"{key}" JSONB')
606
+ elif isinstance(value, list):
607
+ # For arrays, store as JSONB
608
+ columns.append(f'"{key}" JSONB')
609
+ elif isinstance(value, int):
610
+ columns.append(f'"{key}" INTEGER')
611
+ elif isinstance(value, float):
612
+ columns.append(f'"{key}" NUMERIC')
613
+ elif isinstance(value, bool):
614
+ columns.append(f'"{key}" BOOLEAN')
615
+ else:
616
+ # Default to TEXT for strings and other types
617
+ columns.append(f'"{key}" TEXT')
618
+
619
+ # Add metadata columns
620
+ columns.extend([
621
+ '"source" TEXT',
622
+ '"extraction_date" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP',
623
+ '"extraction_backend" TEXT',
624
+ '"extraction_model" TEXT'
625
+ ])
626
+
627
+ # Create SQL statement for table creation
628
+ columns_sql = ", ".join(columns)
629
+ sql = f'''
630
+ CREATE TABLE IF NOT EXISTS "{table_name}" (
631
+ id SERIAL PRIMARY KEY,
632
+ {columns_sql}
633
+ )
634
+ '''
635
+
636
+ # Execute SQL to create table
637
+ result = await self.execute_sql_async(sql)
638
+ log.info(f"Created or ensured table {table_name} exists")
639
+
640
+ # Grant permissions if users are provided
641
+ if users:
642
+ for user in users:
643
+ await self.execute_sql_async(f'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "{table_name}" TO "{user}";')
644
+
645
+ return result
646
+
647
+ async def write_data_to_table(self, table_name: str, data: dict, metadata: dict = None):
648
+ """
649
+ Writes data to the specified table.
650
+
651
+ Args:
652
+ table_name (str): Name of the table
653
+ data (dict): Data to write to the table
654
+ metadata (dict, optional): Additional metadata to include
655
+
656
+ Returns:
657
+ Result of SQL execution
658
+ """
659
+ # Create copies to avoid modifying the original data
660
+ insert_data = dict(data)
661
+
662
+ # Add metadata if provided
663
+ if metadata:
664
+ insert_data["source"] = metadata.get("objectId", metadata.get("source", "unknown"))
665
+ insert_data["extraction_backend"] = metadata.get("extraction_backend", "unknown")
666
+ insert_data["extraction_model"] = metadata.get("extraction_model", "unknown")
667
+
668
+ # Prepare column names and placeholders for values
669
+ columns = [f'"{key}"' for key in insert_data.keys()]
670
+ placeholders = []
671
+ values = []
672
+
673
+ # Process values and create properly formatted placeholders
674
+ for key, value in insert_data.items():
675
+ values.append(json.dumps(value) if isinstance(value, (dict, list)) else value)
676
+ placeholders.append("%s")
677
+
678
+ # Create SQL statement for insertion
679
+ columns_str = ", ".join(columns)
680
+ placeholders_str = ", ".join(placeholders)
681
+
682
+ sql = f'''
683
+ INSERT INTO "{table_name}" ({columns_str})
684
+ VALUES ({placeholders_str})
685
+ RETURNING id
686
+ '''
687
+
688
+ # Execute SQL to insert data
689
+ result = await self.execute_sql_async(sql, values)
690
+ log.info(f"Inserted data into table {table_name}")
691
+
692
+ return result
693
+
694
+ async def execute_sql_async(self, sql_statement, values=None):
695
+ """
696
+ Executes a given SQL statement asynchronously with optional parameter values.
697
+
698
+ Args:
699
+ sql_statement (str): The SQL statement to execute
700
+ values (list, optional): Values for parameterized query
701
+
702
+ Returns:
703
+ Result of SQL execution
704
+ """
705
+ log.info(f"Executing async SQL statement: {sql_statement}")
706
+ if self.engine_type == "pg8000":
707
+ result = await self._execute_sql_async_pg8000(sql_statement, values)
708
+ elif self.engine_type == "langchain":
709
+ result = await self._execute_sql_async_langchain(sql_statement, values)
710
+
711
+ return result
658
712
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.123.0
3
+ Version: 0.123.2
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs 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=uUJN-FYPmGAb9rzGVdg7ds38YhwKBQCXPqUtdOzBQCg,25123
63
+ sunholo/database/alloydb_client.py,sha256=uPRcdfrE1YXCUoDbaKopjlKXXP5deceEn08bJKUeWI4,27596
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
@@ -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.123.0.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
172
- sunholo-0.123.0.dist-info/METADATA,sha256=ue-wXoQBj7xkEm3nzOg0to1O3counnyi7K3RJfzcv0M,10001
173
- sunholo-0.123.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
174
- sunholo-0.123.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
175
- sunholo-0.123.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
176
- sunholo-0.123.0.dist-info/RECORD,,
171
+ sunholo-0.123.2.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
172
+ sunholo-0.123.2.dist-info/METADATA,sha256=0w8jC9b0-_LEB8VnRcKIIbSlHCIJRFPyiuqwfTnbsm8,10001
173
+ sunholo-0.123.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
174
+ sunholo-0.123.2.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
175
+ sunholo-0.123.2.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
176
+ sunholo-0.123.2.dist-info/RECORD,,