sunholo 0.124.0__tar.gz → 0.125.1__tar.gz
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.
- {sunholo-0.124.0/src/sunholo.egg-info → sunholo-0.125.1}/PKG-INFO +1 -1
- {sunholo-0.124.0 → sunholo-0.125.1}/pyproject.toml +1 -1
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/alloydb_client.py +181 -19
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/gcs/download_url.py +1 -1
- {sunholo-0.124.0 → sunholo-0.125.1/src/sunholo.egg-info}/PKG-INFO +1 -1
- {sunholo-0.124.0 → sunholo-0.125.1}/LICENSE.txt +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/MANIFEST.in +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/README.md +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/setup.cfg +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/flask/qna_routes.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/flask/vac_routes.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/langserve.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/route.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/agents/swagger.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/archive/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/archive/archive.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/auth/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/auth/gcloud.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/auth/refresh.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/auth/run.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/azure/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/azure/auth.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/azure/blobs.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/azure/event_grid.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/bots/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/bots/discord.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/bots/webapp.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/azure.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/encode_metadata.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/images.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/process_chunker_data.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/publish.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/pubsub.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/chat_vac.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/cli.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/configs.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/deploy.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/embedder.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/run_proxy.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/swagger.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/cli/vertex.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/components/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/components/llm.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/components/retriever.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/custom_logging.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/alloydb.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/database.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/lancedb.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/database/uuid.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/discovery_engine/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/discovery_engine/cli.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/discovery_engine/create_new.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/discovery_engine/discovery_engine_client.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/embedder/embed_metadata.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/excel/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/excel/plugin.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/gcs/download_folder.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/gcs/extract_and_sign.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/genai/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/genai/file_handling.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/genai/genaiv2.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/genai/images.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/genai/init.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/genai/process_funcs_cls.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/genai/safety.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/invoke/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/invoke/async_class.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/invoke/direct_vac_func.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/langchain_types.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/langfuse/evals.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/llamaindex/get_files.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/llamaindex/user_history.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/mcp/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/mcp/cli.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/ollama/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/ollama/ollama_images.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/qna/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/qna/parsers.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/qna/retry.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/senses/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/senses/stream_voice.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/streaming/stream_lookup.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/agent/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/agent/agent_service.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/agent/app.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/agent/my_log.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/agent/tools/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/agent/vac_service.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/project/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/project/app.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/project/my_log.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/project/vac_service.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/system_services/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/system_services/app.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/templates/system_services/my_log.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/terraform/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/terraform/tfvars_editor.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/tools/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/tools/web_browser.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/api_key.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/big_context.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/config.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/config_class.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/gcp.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/gcp_project.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/mime.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/parsers.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/timedelta.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/utils/version.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/vertex/extensions_call.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/vertex/extensions_class.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/vertex/genai_functions.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/vertex/init.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/vertex/memory_tools.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/vertex/safety.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo/vertex/type_dict_to_json.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo.egg-info/SOURCES.txt +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/src/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/tests/test_async.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/tests/test_async_genai2.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/tests/test_chat_history.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/tests/test_config.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/tests/test_genai2.py +0 -0
- {sunholo-0.124.0 → sunholo-0.125.1}/tests/test_unstructured.py +0 -0
@@ -584,7 +584,8 @@ class AlloyDBClient:
|
|
584
584
|
|
585
585
|
async def create_table_from_schema(self, table_name: str, schema_data: dict, users: list = None):
|
586
586
|
"""
|
587
|
-
Creates or ensures a table exists based on the structure of the provided schema data
|
587
|
+
Creates or ensures a table exists based on the structure of the provided schema data,
|
588
|
+
with special handling for expandable lists.
|
588
589
|
|
589
590
|
Args:
|
590
591
|
table_name (str): Name of the table to create
|
@@ -596,22 +597,32 @@ class AlloyDBClient:
|
|
596
597
|
"""
|
597
598
|
# Generate column definitions from schema data
|
598
599
|
columns = []
|
600
|
+
|
599
601
|
for key, value in schema_data.items():
|
600
|
-
if
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
602
|
+
# Check if this is a specially marked expandable list
|
603
|
+
if isinstance(value, dict) and value.get("__is_expandable_list__", False):
|
604
|
+
# Handle expandable lists - we need to examine the first item to determine column types
|
605
|
+
items = value.get("items", [])
|
606
|
+
|
607
|
+
# Add an index column for this list
|
608
|
+
columns.append(f'"{key}_index" INTEGER')
|
609
|
+
|
610
|
+
if items and isinstance(items[0], dict):
|
611
|
+
# If the first item is a dictionary, we need to create columns for all its keys
|
612
|
+
sample_item = items[0]
|
613
|
+
# Flatten the dictionary with the key as prefix
|
614
|
+
flattened_item = self._flatten_dict_for_schema(sample_item, key, "_")
|
615
|
+
|
616
|
+
# Add columns for all keys in the flattened dictionary
|
617
|
+
for item_key, item_value in flattened_item.items():
|
618
|
+
item_column = self._get_column_definition(item_key, item_value)
|
619
|
+
columns.append(item_column)
|
620
|
+
else:
|
621
|
+
# If items are simple values, just add a column for the list key itself
|
622
|
+
columns.append(self._get_column_definition(key, items[0] if items else None))
|
612
623
|
else:
|
613
|
-
#
|
614
|
-
columns.append(
|
624
|
+
# Regular handling for non-list fields
|
625
|
+
columns.append(self._get_column_definition(key, value))
|
615
626
|
|
616
627
|
# Add metadata columns
|
617
628
|
columns.extend([
|
@@ -651,9 +662,159 @@ class AlloyDBClient:
|
|
651
662
|
|
652
663
|
return result
|
653
664
|
|
665
|
+
def _get_column_definition(self, key, value):
|
666
|
+
"""
|
667
|
+
Helper method to get SQL column definition from a key and value.
|
668
|
+
|
669
|
+
Args:
|
670
|
+
key (str): The column name
|
671
|
+
value: The value to determine the column type
|
672
|
+
|
673
|
+
Returns:
|
674
|
+
str: SQL column definition
|
675
|
+
"""
|
676
|
+
if value is None:
|
677
|
+
# For unknown types (None), default to TEXT
|
678
|
+
return f'"{key}" TEXT'
|
679
|
+
elif isinstance(value, dict):
|
680
|
+
# For nested objects, store as JSONB
|
681
|
+
return f'"{key}" JSONB'
|
682
|
+
elif isinstance(value, list):
|
683
|
+
# For arrays, store as JSONB
|
684
|
+
return f'"{key}" JSONB'
|
685
|
+
elif isinstance(value, int):
|
686
|
+
return f'"{key}" INTEGER'
|
687
|
+
elif isinstance(value, float):
|
688
|
+
return f'"{key}" NUMERIC'
|
689
|
+
elif isinstance(value, bool):
|
690
|
+
return f'"{key}" BOOLEAN'
|
691
|
+
else:
|
692
|
+
# Default to TEXT for strings and other types
|
693
|
+
return f'"{key}" TEXT'
|
694
|
+
|
695
|
+
def _flatten_dict_for_schema(self, nested_dict, parent_key='', separator='.'):
|
696
|
+
"""
|
697
|
+
Flatten a nested dictionary for schema creation.
|
698
|
+
|
699
|
+
Args:
|
700
|
+
nested_dict (dict): The nested dictionary to flatten
|
701
|
+
parent_key (str): The parent key for the current recursion level
|
702
|
+
separator (str): The separator to use between key levels
|
703
|
+
|
704
|
+
Returns:
|
705
|
+
dict: A flattened dictionary
|
706
|
+
"""
|
707
|
+
flattened = {}
|
708
|
+
|
709
|
+
for key, value in nested_dict.items():
|
710
|
+
# Create the new key with parent_key if it exists
|
711
|
+
new_key = f"{parent_key}{separator}{key}" if parent_key else key
|
712
|
+
|
713
|
+
# If value is a dictionary, recursively flatten it
|
714
|
+
if isinstance(value, dict):
|
715
|
+
flattened.update(self._flatten_dict_for_schema(value, new_key, separator))
|
716
|
+
else:
|
717
|
+
# For simple values, just add them with the new key
|
718
|
+
flattened[new_key] = value
|
719
|
+
|
720
|
+
return flattened
|
721
|
+
|
722
|
+
def _flatten_dict(self, nested_dict, parent_key='', separator='.'):
|
723
|
+
"""
|
724
|
+
Flatten a nested dictionary into a single-level dictionary with dot notation for keys.
|
725
|
+
|
726
|
+
Args:
|
727
|
+
nested_dict (dict): The nested dictionary to flatten
|
728
|
+
parent_key (str): The parent key for the current recursion level
|
729
|
+
separator (str): The separator to use between key levels (default: '.')
|
730
|
+
|
731
|
+
Returns:
|
732
|
+
dict: A flattened dictionary with special handling for lists
|
733
|
+
"""
|
734
|
+
flattened = {}
|
735
|
+
|
736
|
+
for key, value in nested_dict.items():
|
737
|
+
# Create the new key with parent_key if it exists
|
738
|
+
new_key = f"{parent_key}{separator}{key}" if parent_key else key
|
739
|
+
|
740
|
+
# If value is a dictionary, recursively flatten it
|
741
|
+
if isinstance(value, dict):
|
742
|
+
flattened.update(self._flatten_dict(value, new_key, separator))
|
743
|
+
# Handle lists containing dictionaries or other values
|
744
|
+
elif isinstance(value, list):
|
745
|
+
# Mark lists for special processing during database insertion
|
746
|
+
# We'll use a special format to indicate this is a list that needs expansion
|
747
|
+
flattened[new_key] = {
|
748
|
+
"__is_expandable_list__": True,
|
749
|
+
"items": value
|
750
|
+
}
|
751
|
+
else:
|
752
|
+
# For simple values, just add them with the new key
|
753
|
+
flattened[new_key] = value
|
754
|
+
|
755
|
+
return flattened
|
756
|
+
|
654
757
|
async def write_data_to_table(self, table_name: str, data: dict, metadata: dict = None):
|
655
758
|
"""
|
656
|
-
Writes data to the specified table.
|
759
|
+
Writes data to the specified table, with special handling for expandable lists.
|
760
|
+
|
761
|
+
Args:
|
762
|
+
table_name (str): Name of the table
|
763
|
+
data (dict): Data to write to the table
|
764
|
+
metadata (dict, optional): Additional metadata to include
|
765
|
+
|
766
|
+
Returns:
|
767
|
+
List of results from SQL executions
|
768
|
+
"""
|
769
|
+
# Find any expandable lists in the data
|
770
|
+
expandable_lists = {}
|
771
|
+
regular_data = {}
|
772
|
+
|
773
|
+
for key, value in data.items():
|
774
|
+
if isinstance(value, dict) and value.get("__is_expandable_list__", False):
|
775
|
+
expandable_lists[key] = value["items"]
|
776
|
+
else:
|
777
|
+
regular_data[key] = value
|
778
|
+
|
779
|
+
# If no expandable lists are found, do a simple insert
|
780
|
+
if not expandable_lists:
|
781
|
+
return await self._insert_single_row(table_name, regular_data, metadata)
|
782
|
+
|
783
|
+
# For expandable lists, we need to create multiple rows
|
784
|
+
results = []
|
785
|
+
|
786
|
+
# Create combinations of rows based on expandable lists
|
787
|
+
if expandable_lists:
|
788
|
+
# Get the first expandable list to start with
|
789
|
+
primary_list_key = next(iter(expandable_lists))
|
790
|
+
primary_list = expandable_lists[primary_list_key]
|
791
|
+
|
792
|
+
# For each item in the primary list, create a new row
|
793
|
+
for item_idx, item in enumerate(primary_list):
|
794
|
+
# Create a copy of the regular data
|
795
|
+
row_data = dict(regular_data)
|
796
|
+
|
797
|
+
# Add the current item from the primary list
|
798
|
+
if isinstance(item, dict):
|
799
|
+
# If it's a dictionary, flatten it with the primary key as prefix
|
800
|
+
flattened_item = self._flatten_dict(item, primary_list_key, "_")
|
801
|
+
row_data.update(flattened_item)
|
802
|
+
else:
|
803
|
+
# If it's a simple value, just add it with the list key
|
804
|
+
row_data[primary_list_key] = item
|
805
|
+
|
806
|
+
# Add item index for reference
|
807
|
+
row_data[f"{primary_list_key}_index"] = item_idx
|
808
|
+
|
809
|
+
# Insert this row
|
810
|
+
result = await self._insert_single_row(table_name, row_data, metadata)
|
811
|
+
results.append(result)
|
812
|
+
|
813
|
+
return results
|
814
|
+
|
815
|
+
async def _insert_single_row(self, table_name: str, data: dict, metadata: dict = None):
|
816
|
+
"""
|
817
|
+
Inserts a single row of data into the specified table.
|
657
818
|
|
658
819
|
Args:
|
659
820
|
table_name (str): Name of the table
|
@@ -663,14 +824,15 @@ class AlloyDBClient:
|
|
663
824
|
Returns:
|
664
825
|
Result of SQL execution
|
665
826
|
"""
|
827
|
+
|
666
828
|
# Create copies to avoid modifying the original data
|
667
829
|
insert_data = dict(data)
|
668
830
|
|
669
831
|
# Add metadata if provided
|
670
832
|
if metadata:
|
671
|
-
insert_data["source"] = metadata.get("objectId", metadata.get("source", "
|
672
|
-
insert_data["extraction_backend"] = metadata.get("extraction_backend", "
|
673
|
-
insert_data["extraction_model"] = metadata.get("extraction_model", "
|
833
|
+
insert_data["source"] = metadata.get("objectId", metadata.get("source", "not-in-metadata"))
|
834
|
+
insert_data["extraction_backend"] = metadata.get("extraction_backend", "not-in-metadata")
|
835
|
+
insert_data["extraction_model"] = metadata.get("extraction_model", "not-in-metadata")
|
674
836
|
|
675
837
|
# Prepare column names and values for SQL
|
676
838
|
columns = [f'"{key}"' for key in insert_data.keys()]
|
@@ -36,7 +36,7 @@ def get_image_from_gcs(gs_uri: str) -> Image.Image: # type: ignore
|
|
36
36
|
except IOError as e:
|
37
37
|
raise ValueError("Unable to open image from bytes:", e)
|
38
38
|
|
39
|
-
def get_bytes_from_gcs(gs_uri) -> Optional[bytes]:
|
39
|
+
def get_bytes_from_gcs(gs_uri: str) -> Optional[bytes]:
|
40
40
|
"""
|
41
41
|
Downloads a file from Google Cloud Storage and returns its bytes.
|
42
42
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|