asksql 2.1.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.
- asksql-2.1.0.dist-info/METADATA +517 -0
- asksql-2.1.0.dist-info/RECORD +291 -0
- asksql-2.1.0.dist-info/WHEEL +4 -0
- asksql-2.1.0.dist-info/entry_points.txt +4 -0
- asksql-2.1.0.dist-info/licenses/LICENSE +21 -0
- vanna/__init__.py +172 -0
- vanna/agents/__init__.py +7 -0
- vanna/capabilities/__init__.py +17 -0
- vanna/capabilities/agent_memory/__init__.py +21 -0
- vanna/capabilities/agent_memory/base.py +103 -0
- vanna/capabilities/agent_memory/models.py +53 -0
- vanna/capabilities/file_system/__init__.py +14 -0
- vanna/capabilities/file_system/base.py +71 -0
- vanna/capabilities/file_system/models.py +25 -0
- vanna/capabilities/sql_runner/__init__.py +13 -0
- vanna/capabilities/sql_runner/base.py +37 -0
- vanna/capabilities/sql_runner/models.py +13 -0
- vanna/components/__init__.py +92 -0
- vanna/components/base.py +11 -0
- vanna/components/rich/__init__.py +83 -0
- vanna/components/rich/containers/__init__.py +7 -0
- vanna/components/rich/containers/card.py +20 -0
- vanna/components/rich/data/__init__.py +9 -0
- vanna/components/rich/data/chart.py +17 -0
- vanna/components/rich/data/dataframe.py +93 -0
- vanna/components/rich/feedback/__init__.py +21 -0
- vanna/components/rich/feedback/badge.py +16 -0
- vanna/components/rich/feedback/icon_text.py +14 -0
- vanna/components/rich/feedback/log_viewer.py +41 -0
- vanna/components/rich/feedback/notification.py +19 -0
- vanna/components/rich/feedback/progress.py +37 -0
- vanna/components/rich/feedback/status_card.py +28 -0
- vanna/components/rich/feedback/status_indicator.py +14 -0
- vanna/components/rich/interactive/__init__.py +21 -0
- vanna/components/rich/interactive/button.py +95 -0
- vanna/components/rich/interactive/task_list.py +58 -0
- vanna/components/rich/interactive/ui_state.py +93 -0
- vanna/components/rich/specialized/__init__.py +7 -0
- vanna/components/rich/specialized/artifact.py +20 -0
- vanna/components/rich/text.py +16 -0
- vanna/components/simple/__init__.py +15 -0
- vanna/components/simple/image.py +15 -0
- vanna/components/simple/link.py +15 -0
- vanna/components/simple/text.py +11 -0
- vanna/core/__init__.py +193 -0
- vanna/core/_compat.py +19 -0
- vanna/core/agent/__init__.py +10 -0
- vanna/core/agent/agent.py +1407 -0
- vanna/core/agent/config.py +123 -0
- vanna/core/audit/__init__.py +28 -0
- vanna/core/audit/base.py +299 -0
- vanna/core/audit/models.py +131 -0
- vanna/core/component_manager.py +329 -0
- vanna/core/components.py +53 -0
- vanna/core/enhancer/__init__.py +11 -0
- vanna/core/enhancer/base.py +94 -0
- vanna/core/enhancer/default.py +118 -0
- vanna/core/enricher/__init__.py +10 -0
- vanna/core/enricher/base.py +59 -0
- vanna/core/errors.py +47 -0
- vanna/core/evaluation/__init__.py +81 -0
- vanna/core/evaluation/base.py +186 -0
- vanna/core/evaluation/dataset.py +254 -0
- vanna/core/evaluation/evaluators.py +376 -0
- vanna/core/evaluation/report.py +289 -0
- vanna/core/evaluation/runner.py +313 -0
- vanna/core/filter/__init__.py +10 -0
- vanna/core/filter/base.py +67 -0
- vanna/core/lifecycle/__init__.py +10 -0
- vanna/core/lifecycle/base.py +83 -0
- vanna/core/llm/__init__.py +16 -0
- vanna/core/llm/base.py +40 -0
- vanna/core/llm/models.py +61 -0
- vanna/core/middleware/__init__.py +10 -0
- vanna/core/middleware/base.py +69 -0
- vanna/core/observability/__init__.py +11 -0
- vanna/core/observability/base.py +88 -0
- vanna/core/observability/models.py +47 -0
- vanna/core/recovery/__init__.py +11 -0
- vanna/core/recovery/base.py +84 -0
- vanna/core/recovery/models.py +32 -0
- vanna/core/registry.py +278 -0
- vanna/core/rich_component.py +156 -0
- vanna/core/simple_component.py +27 -0
- vanna/core/storage/__init__.py +14 -0
- vanna/core/storage/base.py +46 -0
- vanna/core/storage/models.py +46 -0
- vanna/core/system_prompt/__init__.py +13 -0
- vanna/core/system_prompt/base.py +36 -0
- vanna/core/system_prompt/default.py +157 -0
- vanna/core/tool/__init__.py +18 -0
- vanna/core/tool/base.py +70 -0
- vanna/core/tool/models.py +84 -0
- vanna/core/user/__init__.py +17 -0
- vanna/core/user/base.py +29 -0
- vanna/core/user/models.py +25 -0
- vanna/core/user/request_context.py +70 -0
- vanna/core/user/resolver.py +42 -0
- vanna/core/validation.py +164 -0
- vanna/core/workflow/__init__.py +12 -0
- vanna/core/workflow/base.py +254 -0
- vanna/core/workflow/default.py +789 -0
- vanna/examples/__init__.py +1 -0
- vanna/examples/__main__.py +44 -0
- vanna/examples/anthropic_quickstart.py +80 -0
- vanna/examples/artifact_example.py +293 -0
- vanna/examples/claude_sqlite_example.py +236 -0
- vanna/examples/coding_agent_example.py +300 -0
- vanna/examples/custom_system_prompt_example.py +174 -0
- vanna/examples/default_workflow_handler_example.py +208 -0
- vanna/examples/email_auth_example.py +340 -0
- vanna/examples/evaluation_example.py +269 -0
- vanna/examples/extensibility_example.py +262 -0
- vanna/examples/minimal_example.py +67 -0
- vanna/examples/mock_auth_example.py +227 -0
- vanna/examples/mock_custom_tool.py +311 -0
- vanna/examples/mock_quickstart.py +79 -0
- vanna/examples/mock_quota_example.py +145 -0
- vanna/examples/mock_rich_components_demo.py +396 -0
- vanna/examples/mock_sqlite_example.py +223 -0
- vanna/examples/openai_quickstart.py +83 -0
- vanna/examples/primitive_components_demo.py +305 -0
- vanna/examples/quota_lifecycle_example.py +139 -0
- vanna/examples/visualization_example.py +251 -0
- vanna/integrations/__init__.py +17 -0
- vanna/integrations/anthropic/__init__.py +9 -0
- vanna/integrations/anthropic/llm.py +270 -0
- vanna/integrations/azureopenai/__init__.py +9 -0
- vanna/integrations/azureopenai/llm.py +329 -0
- vanna/integrations/azuresearch/__init__.py +7 -0
- vanna/integrations/azuresearch/agent_memory.py +413 -0
- vanna/integrations/bigquery/__init__.py +5 -0
- vanna/integrations/bigquery/sql_runner.py +81 -0
- vanna/integrations/chromadb/__init__.py +104 -0
- vanna/integrations/chromadb/agent_memory.py +488 -0
- vanna/integrations/clickhouse/__init__.py +5 -0
- vanna/integrations/clickhouse/sql_runner.py +82 -0
- vanna/integrations/duckdb/__init__.py +5 -0
- vanna/integrations/duckdb/sql_runner.py +65 -0
- vanna/integrations/faiss/__init__.py +7 -0
- vanna/integrations/faiss/agent_memory.py +435 -0
- vanna/integrations/google/__init__.py +9 -0
- vanna/integrations/google/gemini.py +370 -0
- vanna/integrations/hive/__init__.py +5 -0
- vanna/integrations/hive/sql_runner.py +87 -0
- vanna/integrations/local/__init__.py +17 -0
- vanna/integrations/local/agent_memory/__init__.py +7 -0
- vanna/integrations/local/agent_memory/in_memory.py +285 -0
- vanna/integrations/local/audit.py +59 -0
- vanna/integrations/local/file_system.py +242 -0
- vanna/integrations/local/file_system_conversation_store.py +255 -0
- vanna/integrations/local/storage.py +62 -0
- vanna/integrations/marqo/__init__.py +7 -0
- vanna/integrations/marqo/agent_memory.py +354 -0
- vanna/integrations/milvus/__init__.py +7 -0
- vanna/integrations/milvus/agent_memory.py +458 -0
- vanna/integrations/mock/__init__.py +9 -0
- vanna/integrations/mock/llm.py +65 -0
- vanna/integrations/mssql/__init__.py +5 -0
- vanna/integrations/mssql/sql_runner.py +66 -0
- vanna/integrations/mysql/__init__.py +5 -0
- vanna/integrations/mysql/sql_runner.py +92 -0
- vanna/integrations/ollama/__init__.py +7 -0
- vanna/integrations/ollama/llm.py +252 -0
- vanna/integrations/openai/__init__.py +10 -0
- vanna/integrations/openai/llm.py +267 -0
- vanna/integrations/openai/responses.py +163 -0
- vanna/integrations/opensearch/__init__.py +7 -0
- vanna/integrations/opensearch/agent_memory.py +411 -0
- vanna/integrations/oracle/__init__.py +5 -0
- vanna/integrations/oracle/sql_runner.py +75 -0
- vanna/integrations/pinecone/__init__.py +7 -0
- vanna/integrations/pinecone/agent_memory.py +329 -0
- vanna/integrations/plotly/__init__.py +5 -0
- vanna/integrations/plotly/chart_generator.py +313 -0
- vanna/integrations/postgres/__init__.py +9 -0
- vanna/integrations/postgres/sql_runner.py +112 -0
- vanna/integrations/premium/agent_memory/__init__.py +7 -0
- vanna/integrations/premium/agent_memory/premium.py +186 -0
- vanna/integrations/presto/__init__.py +5 -0
- vanna/integrations/presto/sql_runner.py +107 -0
- vanna/integrations/qdrant/__init__.py +7 -0
- vanna/integrations/qdrant/agent_memory.py +466 -0
- vanna/integrations/snowflake/__init__.py +5 -0
- vanna/integrations/snowflake/sql_runner.py +147 -0
- vanna/integrations/sqlite/__init__.py +9 -0
- vanna/integrations/sqlite/sql_runner.py +65 -0
- vanna/integrations/weaviate/__init__.py +7 -0
- vanna/integrations/weaviate/agent_memory.py +428 -0
- vanna/legacy/ZhipuAI/ZhipuAI_Chat.py +233 -0
- vanna/legacy/ZhipuAI/ZhipuAI_embeddings.py +79 -0
- vanna/legacy/ZhipuAI/__init__.py +2 -0
- vanna/legacy/__init__.py +403 -0
- vanna/legacy/adapter.py +468 -0
- vanna/legacy/advanced/__init__.py +28 -0
- vanna/legacy/anthropic/__init__.py +1 -0
- vanna/legacy/anthropic/anthropic_chat.py +80 -0
- vanna/legacy/azuresearch/__init__.py +1 -0
- vanna/legacy/azuresearch/azuresearch_vector.py +274 -0
- vanna/legacy/base/__init__.py +1 -0
- vanna/legacy/base/base.py +2133 -0
- vanna/legacy/base/safe_exec.py +190 -0
- vanna/legacy/bedrock/__init__.py +1 -0
- vanna/legacy/bedrock/bedrock_converse.py +86 -0
- vanna/legacy/chromadb/__init__.py +1 -0
- vanna/legacy/chromadb/chromadb_vector.py +259 -0
- vanna/legacy/cohere/__init__.py +2 -0
- vanna/legacy/cohere/cohere_chat.py +99 -0
- vanna/legacy/cohere/cohere_embeddings.py +77 -0
- vanna/legacy/deepseek/__init__.py +1 -0
- vanna/legacy/deepseek/deepseek_chat.py +59 -0
- vanna/legacy/exceptions/__init__.py +46 -0
- vanna/legacy/faiss/__init__.py +1 -0
- vanna/legacy/faiss/faiss.py +230 -0
- vanna/legacy/flask/__init__.py +1355 -0
- vanna/legacy/flask/assets.py +58 -0
- vanna/legacy/flask/auth.py +56 -0
- vanna/legacy/google/__init__.py +2 -0
- vanna/legacy/google/bigquery_vector.py +293 -0
- vanna/legacy/google/gemini_chat.py +78 -0
- vanna/legacy/hf/__init__.py +1 -0
- vanna/legacy/hf/hf.py +80 -0
- vanna/legacy/local.py +8 -0
- vanna/legacy/marqo/__init__.py +1 -0
- vanna/legacy/marqo/marqo.py +168 -0
- vanna/legacy/milvus/__init__.py +1 -0
- vanna/legacy/milvus/milvus_vector.py +328 -0
- vanna/legacy/mistral/__init__.py +1 -0
- vanna/legacy/mistral/mistral.py +51 -0
- vanna/legacy/mock/__init__.py +3 -0
- vanna/legacy/mock/embedding.py +11 -0
- vanna/legacy/mock/llm.py +18 -0
- vanna/legacy/mock/vectordb.py +67 -0
- vanna/legacy/ollama/__init__.py +1 -0
- vanna/legacy/ollama/ollama.py +110 -0
- vanna/legacy/openai/__init__.py +2 -0
- vanna/legacy/openai/openai_chat.py +124 -0
- vanna/legacy/openai/openai_embeddings.py +46 -0
- vanna/legacy/opensearch/__init__.py +2 -0
- vanna/legacy/opensearch/opensearch_vector.py +369 -0
- vanna/legacy/opensearch/opensearch_vector_semantic.py +200 -0
- vanna/legacy/oracle/__init__.py +1 -0
- vanna/legacy/oracle/oracle_vector.py +584 -0
- vanna/legacy/pgvector/__init__.py +1 -0
- vanna/legacy/pgvector/pgvector.py +282 -0
- vanna/legacy/pinecone/__init__.py +3 -0
- vanna/legacy/pinecone/pinecone_vector.py +275 -0
- vanna/legacy/qdrant/__init__.py +3 -0
- vanna/legacy/qdrant/qdrant.py +329 -0
- vanna/legacy/qianfan/Qianfan_Chat.py +170 -0
- vanna/legacy/qianfan/Qianfan_embeddings.py +36 -0
- vanna/legacy/qianfan/__init__.py +2 -0
- vanna/legacy/qianwen/QianwenAI_chat.py +132 -0
- vanna/legacy/qianwen/QianwenAI_embeddings.py +46 -0
- vanna/legacy/qianwen/__init__.py +2 -0
- vanna/legacy/remote.py +79 -0
- vanna/legacy/types/__init__.py +292 -0
- vanna/legacy/utils.py +72 -0
- vanna/legacy/vannadb/__init__.py +1 -0
- vanna/legacy/vannadb/vannadb_vector.py +468 -0
- vanna/legacy/vllm/__init__.py +1 -0
- vanna/legacy/vllm/vllm.py +97 -0
- vanna/legacy/weaviate/__init__.py +1 -0
- vanna/legacy/weaviate/weaviate_vector.py +194 -0
- vanna/legacy/xinference/__init__.py +1 -0
- vanna/legacy/xinference/xinference.py +53 -0
- vanna/py.typed +0 -0
- vanna/servers/__init__.py +16 -0
- vanna/servers/__main__.py +8 -0
- vanna/servers/base/__init__.py +18 -0
- vanna/servers/base/chat_handler.py +65 -0
- vanna/servers/base/models.py +111 -0
- vanna/servers/base/rich_chat_handler.py +141 -0
- vanna/servers/base/templates.py +331 -0
- vanna/servers/cli/__init__.py +7 -0
- vanna/servers/cli/server_runner.py +204 -0
- vanna/servers/fastapi/__init__.py +7 -0
- vanna/servers/fastapi/app.py +163 -0
- vanna/servers/fastapi/routes.py +183 -0
- vanna/servers/flask/__init__.py +7 -0
- vanna/servers/flask/app.py +132 -0
- vanna/servers/flask/routes.py +137 -0
- vanna/tools/__init__.py +41 -0
- vanna/tools/agent_memory.py +322 -0
- vanna/tools/file_system.py +879 -0
- vanna/tools/python.py +222 -0
- vanna/tools/run_sql.py +173 -0
- vanna/tools/visualize_data.py +195 -0
- vanna/utils/__init__.py +0 -0
- vanna/utils/sql_safety.py +84 -0
- vanna/web_components/__init__.py +44 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from langchain_core.documents import Document
|
|
8
|
+
from langchain_postgres.vectorstores import PGVector
|
|
9
|
+
from sqlalchemy import create_engine, text
|
|
10
|
+
|
|
11
|
+
from .. import ValidationError
|
|
12
|
+
from ..base import VannaBase
|
|
13
|
+
from ..types import TrainingPlan, TrainingPlanItem
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PG_VectorStore(VannaBase):
|
|
17
|
+
def __init__(self, config=None):
|
|
18
|
+
if not config or "connection_string" not in config:
|
|
19
|
+
raise ValueError(
|
|
20
|
+
"A valid 'config' dictionary with a 'connection_string' is required."
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
VannaBase.__init__(self, config=config)
|
|
24
|
+
|
|
25
|
+
if config and "connection_string" in config:
|
|
26
|
+
self.connection_string = config.get("connection_string")
|
|
27
|
+
self.n_results = config.get("n_results", 10)
|
|
28
|
+
|
|
29
|
+
if config and "embedding_function" in config:
|
|
30
|
+
self.embedding_function = config.get("embedding_function")
|
|
31
|
+
else:
|
|
32
|
+
from langchain_huggingface import HuggingFaceEmbeddings
|
|
33
|
+
|
|
34
|
+
self.embedding_function = HuggingFaceEmbeddings(
|
|
35
|
+
model_name="all-MiniLM-L6-v2"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
self.sql_collection = PGVector(
|
|
39
|
+
embeddings=self.embedding_function,
|
|
40
|
+
collection_name="sql",
|
|
41
|
+
connection=self.connection_string,
|
|
42
|
+
)
|
|
43
|
+
self.ddl_collection = PGVector(
|
|
44
|
+
embeddings=self.embedding_function,
|
|
45
|
+
collection_name="ddl",
|
|
46
|
+
connection=self.connection_string,
|
|
47
|
+
)
|
|
48
|
+
self.documentation_collection = PGVector(
|
|
49
|
+
embeddings=self.embedding_function,
|
|
50
|
+
collection_name="documentation",
|
|
51
|
+
connection=self.connection_string,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def add_question_sql(self, question: str, sql: str, **kwargs) -> str:
|
|
55
|
+
question_sql_json = json.dumps(
|
|
56
|
+
{
|
|
57
|
+
"question": question,
|
|
58
|
+
"sql": sql,
|
|
59
|
+
},
|
|
60
|
+
ensure_ascii=False,
|
|
61
|
+
)
|
|
62
|
+
id = str(uuid.uuid4()) + "-sql"
|
|
63
|
+
createdat = kwargs.get("createdat")
|
|
64
|
+
doc = Document(
|
|
65
|
+
page_content=question_sql_json,
|
|
66
|
+
metadata={"id": id, "createdat": createdat},
|
|
67
|
+
)
|
|
68
|
+
self.sql_collection.add_documents([doc], ids=[doc.metadata["id"]])
|
|
69
|
+
|
|
70
|
+
return id
|
|
71
|
+
|
|
72
|
+
def add_ddl(self, ddl: str, **kwargs) -> str:
|
|
73
|
+
_id = str(uuid.uuid4()) + "-ddl"
|
|
74
|
+
doc = Document(
|
|
75
|
+
page_content=ddl,
|
|
76
|
+
metadata={"id": _id},
|
|
77
|
+
)
|
|
78
|
+
self.ddl_collection.add_documents([doc], ids=[doc.metadata["id"]])
|
|
79
|
+
return _id
|
|
80
|
+
|
|
81
|
+
def add_documentation(self, documentation: str, **kwargs) -> str:
|
|
82
|
+
_id = str(uuid.uuid4()) + "-doc"
|
|
83
|
+
doc = Document(
|
|
84
|
+
page_content=documentation,
|
|
85
|
+
metadata={"id": _id},
|
|
86
|
+
)
|
|
87
|
+
self.documentation_collection.add_documents([doc], ids=[doc.metadata["id"]])
|
|
88
|
+
return _id
|
|
89
|
+
|
|
90
|
+
def get_collection(self, collection_name):
|
|
91
|
+
match collection_name:
|
|
92
|
+
case "sql":
|
|
93
|
+
return self.sql_collection
|
|
94
|
+
case "ddl":
|
|
95
|
+
return self.ddl_collection
|
|
96
|
+
case "documentation":
|
|
97
|
+
return self.documentation_collection
|
|
98
|
+
case _:
|
|
99
|
+
raise ValueError("Specified collection does not exist.")
|
|
100
|
+
|
|
101
|
+
def get_similar_question_sql(self, question: str) -> list:
|
|
102
|
+
documents = self.sql_collection.similarity_search(
|
|
103
|
+
query=question, k=self.n_results
|
|
104
|
+
)
|
|
105
|
+
return [ast.literal_eval(document.page_content) for document in documents]
|
|
106
|
+
|
|
107
|
+
def get_related_ddl(self, question: str, **kwargs) -> list:
|
|
108
|
+
documents = self.ddl_collection.similarity_search(
|
|
109
|
+
query=question, k=self.n_results
|
|
110
|
+
)
|
|
111
|
+
return [document.page_content for document in documents]
|
|
112
|
+
|
|
113
|
+
def get_related_documentation(self, question: str, **kwargs) -> list:
|
|
114
|
+
documents = self.documentation_collection.similarity_search(
|
|
115
|
+
query=question, k=self.n_results
|
|
116
|
+
)
|
|
117
|
+
return [document.page_content for document in documents]
|
|
118
|
+
|
|
119
|
+
def train(
|
|
120
|
+
self,
|
|
121
|
+
question: str | None = None,
|
|
122
|
+
sql: str | None = None,
|
|
123
|
+
ddl: str | None = None,
|
|
124
|
+
documentation: str | None = None,
|
|
125
|
+
plan: TrainingPlan | None = None,
|
|
126
|
+
createdat: str | None = None,
|
|
127
|
+
):
|
|
128
|
+
if question and not sql:
|
|
129
|
+
raise ValidationError("Please provide a SQL query.")
|
|
130
|
+
|
|
131
|
+
if documentation:
|
|
132
|
+
logging.info(f"Adding documentation: {documentation}")
|
|
133
|
+
return self.add_documentation(documentation)
|
|
134
|
+
|
|
135
|
+
if sql and question:
|
|
136
|
+
return self.add_question_sql(
|
|
137
|
+
question=question, sql=sql, createdat=createdat
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if ddl:
|
|
141
|
+
logging.info(f"Adding ddl: {ddl}")
|
|
142
|
+
return self.add_ddl(ddl)
|
|
143
|
+
|
|
144
|
+
if plan:
|
|
145
|
+
for item in plan._plan:
|
|
146
|
+
if item.item_type == TrainingPlanItem.ITEM_TYPE_DDL:
|
|
147
|
+
self.add_ddl(item.item_value)
|
|
148
|
+
elif item.item_type == TrainingPlanItem.ITEM_TYPE_IS:
|
|
149
|
+
self.add_documentation(item.item_value)
|
|
150
|
+
elif (
|
|
151
|
+
item.item_type == TrainingPlanItem.ITEM_TYPE_SQL and item.item_name
|
|
152
|
+
):
|
|
153
|
+
self.add_question_sql(question=item.item_name, sql=item.item_value)
|
|
154
|
+
|
|
155
|
+
def get_training_data(self, **kwargs) -> pd.DataFrame:
|
|
156
|
+
# Establishing the connection
|
|
157
|
+
engine = create_engine(self.connection_string)
|
|
158
|
+
|
|
159
|
+
# Querying the 'langchain_pg_embedding' table
|
|
160
|
+
query_embedding = "SELECT cmetadata, document FROM langchain_pg_embedding"
|
|
161
|
+
df_embedding = pd.read_sql(query_embedding, engine)
|
|
162
|
+
|
|
163
|
+
# List to accumulate the processed rows
|
|
164
|
+
processed_rows = []
|
|
165
|
+
|
|
166
|
+
# Process each row in the DataFrame
|
|
167
|
+
for _, row in df_embedding.iterrows():
|
|
168
|
+
custom_id = row["cmetadata"]["id"]
|
|
169
|
+
document = row["document"]
|
|
170
|
+
training_data_type = (
|
|
171
|
+
"documentation" if custom_id[-3:] == "doc" else custom_id[-3:]
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if training_data_type == "sql":
|
|
175
|
+
# Convert the document string to a dictionary
|
|
176
|
+
try:
|
|
177
|
+
doc_dict = ast.literal_eval(document)
|
|
178
|
+
question = doc_dict.get("question")
|
|
179
|
+
content = doc_dict.get("sql")
|
|
180
|
+
except (ValueError, SyntaxError):
|
|
181
|
+
logging.info(
|
|
182
|
+
f"Skipping row with custom_id {custom_id} due to parsing error."
|
|
183
|
+
)
|
|
184
|
+
continue
|
|
185
|
+
elif training_data_type in ["documentation", "ddl"]:
|
|
186
|
+
question = None # Default value for question
|
|
187
|
+
content = document
|
|
188
|
+
else:
|
|
189
|
+
# If the suffix is not recognized, skip this row
|
|
190
|
+
logging.info(
|
|
191
|
+
f"Skipping row with custom_id {custom_id} due to unrecognized training data type."
|
|
192
|
+
)
|
|
193
|
+
continue
|
|
194
|
+
|
|
195
|
+
# Append the processed data to the list
|
|
196
|
+
processed_rows.append(
|
|
197
|
+
{
|
|
198
|
+
"id": custom_id,
|
|
199
|
+
"question": question,
|
|
200
|
+
"content": content,
|
|
201
|
+
"training_data_type": training_data_type,
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Create a DataFrame from the list of processed rows
|
|
206
|
+
df_processed = pd.DataFrame(processed_rows)
|
|
207
|
+
|
|
208
|
+
return df_processed
|
|
209
|
+
|
|
210
|
+
def remove_training_data(self, id: str, **kwargs) -> bool:
|
|
211
|
+
# Create the database engine
|
|
212
|
+
engine = create_engine(self.connection_string)
|
|
213
|
+
|
|
214
|
+
# SQL DELETE statement
|
|
215
|
+
delete_statement = text(
|
|
216
|
+
"""
|
|
217
|
+
DELETE FROM langchain_pg_embedding
|
|
218
|
+
WHERE cmetadata ->> 'id' = :id
|
|
219
|
+
"""
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Connect to the database and execute the delete statement
|
|
223
|
+
with engine.connect() as connection:
|
|
224
|
+
# Start a transaction
|
|
225
|
+
with connection.begin() as transaction:
|
|
226
|
+
try:
|
|
227
|
+
result = connection.execute(delete_statement, {"id": id})
|
|
228
|
+
# Commit the transaction if the delete was successful
|
|
229
|
+
transaction.commit()
|
|
230
|
+
# Check if any row was deleted and return True or False accordingly
|
|
231
|
+
return result.rowcount > 0
|
|
232
|
+
except Exception as e:
|
|
233
|
+
# Rollback the transaction in case of error
|
|
234
|
+
logging.error(f"An error occurred: {e}")
|
|
235
|
+
transaction.rollback()
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
def remove_collection(self, collection_name: str) -> bool:
|
|
239
|
+
engine = create_engine(self.connection_string)
|
|
240
|
+
|
|
241
|
+
# Determine the suffix to look for based on the collection name
|
|
242
|
+
suffix_map = {"ddl": "ddl", "sql": "sql", "documentation": "doc"}
|
|
243
|
+
suffix = suffix_map.get(collection_name)
|
|
244
|
+
|
|
245
|
+
if not suffix:
|
|
246
|
+
logging.info(
|
|
247
|
+
"Invalid collection name. Choose from 'ddl', 'sql', or 'documentation'."
|
|
248
|
+
)
|
|
249
|
+
return False
|
|
250
|
+
|
|
251
|
+
# SQL query to delete rows based on the condition
|
|
252
|
+
query = text(
|
|
253
|
+
f"""
|
|
254
|
+
DELETE FROM langchain_pg_embedding
|
|
255
|
+
WHERE cmetadata->>'id' LIKE '%{suffix}'
|
|
256
|
+
"""
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Execute the deletion within a transaction block
|
|
260
|
+
with engine.connect() as connection:
|
|
261
|
+
with connection.begin() as transaction:
|
|
262
|
+
try:
|
|
263
|
+
result = connection.execute(query)
|
|
264
|
+
transaction.commit() # Explicitly commit the transaction
|
|
265
|
+
if result.rowcount > 0:
|
|
266
|
+
logging.info(
|
|
267
|
+
f"Deleted {result.rowcount} rows from "
|
|
268
|
+
f"langchain_pg_embedding where collection is {collection_name}."
|
|
269
|
+
)
|
|
270
|
+
return True
|
|
271
|
+
else:
|
|
272
|
+
logging.info(
|
|
273
|
+
f"No rows deleted for collection {collection_name}."
|
|
274
|
+
)
|
|
275
|
+
return False
|
|
276
|
+
except Exception as e:
|
|
277
|
+
logging.error(f"An error occurred: {e}")
|
|
278
|
+
transaction.rollback() # Rollback in case of error
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
def generate_embedding(self, *args, **kwargs):
|
|
282
|
+
pass
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from pinecone import Pinecone, PodSpec, ServerlessSpec
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from ..base import VannaBase
|
|
7
|
+
from ..utils import deterministic_uuid
|
|
8
|
+
|
|
9
|
+
from fastembed import TextEmbedding
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PineconeDB_VectorStore(VannaBase):
|
|
13
|
+
"""
|
|
14
|
+
Vectorstore using PineconeDB
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
config (dict): Configuration dictionary. Defaults to {}. You must provide either a Pinecone Client or an API key in the config.
|
|
18
|
+
- client (Pinecone, optional): Pinecone client. Defaults to None.
|
|
19
|
+
- api_key (str, optional): Pinecone API key. Defaults to None.
|
|
20
|
+
- n_results (int, optional): Number of results to return. Defaults to 10.
|
|
21
|
+
- dimensions (int, optional): Dimensions of the embeddings. Defaults to 384 which coresponds to the dimensions of BAAI/bge-small-en-v1.5.
|
|
22
|
+
- fastembed_model (str, optional): Fastembed model to use. Defaults to "BAAI/bge-small-en-v1.5".
|
|
23
|
+
- documentation_namespace (str, optional): Namespace for documentation. Defaults to "documentation".
|
|
24
|
+
- distance_metric (str, optional): Distance metric to use. Defaults to "cosine".
|
|
25
|
+
- ddl_namespace (str, optional): Namespace for DDL. Defaults to "ddl".
|
|
26
|
+
- sql_namespace (str, optional): Namespace for SQL. Defaults to "sql".
|
|
27
|
+
- index_name (str, optional): Name of the index. Defaults to "vanna-index".
|
|
28
|
+
- metadata_config (dict, optional): Metadata configuration if using a pinecone pod. Defaults to {}.
|
|
29
|
+
- server_type (str, optional): Type of Pinecone server to use. Defaults to "serverless". Options are "serverless" or "pod".
|
|
30
|
+
- podspec (PodSpec, optional): PodSpec configuration if using a pinecone pod. Defaults to PodSpec(environment="us-west-2", pod_type="p1.x1", metadata_config=self.metadata_config).
|
|
31
|
+
- serverless_spec (ServerlessSpec, optional): ServerlessSpec configuration if using a pinecone serverless index. Defaults to ServerlessSpec(cloud="aws", region="us-west-2").
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: If config is None, api_key is not provided OR client is not provided, client is not an instance of Pinecone, or server_type is not "serverless" or "pod".
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, config=None):
|
|
37
|
+
VannaBase.__init__(self, config=config)
|
|
38
|
+
if config is None:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
"config is required, pass either a Pinecone client or an API key in the config."
|
|
41
|
+
)
|
|
42
|
+
client = config.get("client")
|
|
43
|
+
api_key = config.get("api_key")
|
|
44
|
+
if not api_key and not client:
|
|
45
|
+
raise ValueError(
|
|
46
|
+
"api_key is required in config or pass a configured client"
|
|
47
|
+
)
|
|
48
|
+
if not client and api_key:
|
|
49
|
+
self._client = Pinecone(api_key=api_key)
|
|
50
|
+
elif not isinstance(client, Pinecone):
|
|
51
|
+
raise ValueError("client must be an instance of Pinecone")
|
|
52
|
+
else:
|
|
53
|
+
self._client = client
|
|
54
|
+
|
|
55
|
+
self.n_results = config.get("n_results", 10)
|
|
56
|
+
self.dimensions = config.get("dimensions", 384)
|
|
57
|
+
self.fastembed_model = config.get("fastembed_model", "BAAI/bge-small-en-v1.5")
|
|
58
|
+
self.documentation_namespace = config.get(
|
|
59
|
+
"documentation_namespace", "documentation"
|
|
60
|
+
)
|
|
61
|
+
self.distance_metric = config.get("distance_metric", "cosine")
|
|
62
|
+
self.ddl_namespace = config.get("ddl_namespace", "ddl")
|
|
63
|
+
self.sql_namespace = config.get("sql_namespace", "sql")
|
|
64
|
+
self.index_name = config.get("index_name", "vanna-index")
|
|
65
|
+
self.metadata_config = config.get("metadata_config", {})
|
|
66
|
+
self.server_type = config.get("server_type", "serverless")
|
|
67
|
+
if self.server_type not in ["serverless", "pod"]:
|
|
68
|
+
raise ValueError("server_type must be either 'serverless' or 'pod'")
|
|
69
|
+
self.podspec = config.get(
|
|
70
|
+
"podspec",
|
|
71
|
+
PodSpec(
|
|
72
|
+
environment="us-west-2",
|
|
73
|
+
pod_type="p1.x1",
|
|
74
|
+
metadata_config=self.metadata_config,
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
self.serverless_spec = config.get(
|
|
78
|
+
"serverless_spec", ServerlessSpec(cloud="aws", region="us-west-2")
|
|
79
|
+
)
|
|
80
|
+
self._setup_index()
|
|
81
|
+
|
|
82
|
+
def _set_index_host(self, host: str) -> None:
|
|
83
|
+
self.Index = self._client.Index(host=host)
|
|
84
|
+
|
|
85
|
+
def _setup_index(self) -> None:
|
|
86
|
+
existing_indexes = self._get_indexes()
|
|
87
|
+
if self.index_name not in existing_indexes and self.server_type == "serverless":
|
|
88
|
+
self._client.create_index(
|
|
89
|
+
name=self.index_name,
|
|
90
|
+
dimension=self.dimensions,
|
|
91
|
+
metric=self.distance_metric,
|
|
92
|
+
spec=self.serverless_spec,
|
|
93
|
+
)
|
|
94
|
+
pinecone_index_host = self._client.describe_index(self.index_name)["host"]
|
|
95
|
+
self._set_index_host(pinecone_index_host)
|
|
96
|
+
elif self.index_name not in existing_indexes and self.server_type == "pod":
|
|
97
|
+
self._client.create_index(
|
|
98
|
+
name=self.index_name,
|
|
99
|
+
dimension=self.dimensions,
|
|
100
|
+
metric=self.distance_metric,
|
|
101
|
+
spec=self.podspec,
|
|
102
|
+
)
|
|
103
|
+
pinecone_index_host = self._client.describe_index(self.index_name)["host"]
|
|
104
|
+
self._set_index_host(pinecone_index_host)
|
|
105
|
+
else:
|
|
106
|
+
pinecone_index_host = self._client.describe_index(self.index_name)["host"]
|
|
107
|
+
self._set_index_host(pinecone_index_host)
|
|
108
|
+
|
|
109
|
+
def _get_indexes(self) -> list:
|
|
110
|
+
return [index["name"] for index in self._client.list_indexes()]
|
|
111
|
+
|
|
112
|
+
def _check_if_embedding_exists(self, id: str, namespace: str) -> bool:
|
|
113
|
+
fetch_response = self.Index.fetch(ids=[id], namespace=namespace)
|
|
114
|
+
if fetch_response.vectors == {}:
|
|
115
|
+
return False
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
def add_ddl(self, ddl: str, **kwargs) -> str:
|
|
119
|
+
id = deterministic_uuid(ddl) + "-ddl"
|
|
120
|
+
if self._check_if_embedding_exists(id=id, namespace=self.ddl_namespace):
|
|
121
|
+
print(f"DDL with id: {id} already exists in the index. Skipping...")
|
|
122
|
+
return id
|
|
123
|
+
self.Index.upsert(
|
|
124
|
+
vectors=[(id, self.generate_embedding(ddl), {"ddl": ddl})],
|
|
125
|
+
namespace=self.ddl_namespace,
|
|
126
|
+
)
|
|
127
|
+
return id
|
|
128
|
+
|
|
129
|
+
def add_documentation(self, doc: str, **kwargs) -> str:
|
|
130
|
+
id = deterministic_uuid(doc) + "-doc"
|
|
131
|
+
|
|
132
|
+
if self._check_if_embedding_exists(
|
|
133
|
+
id=id, namespace=self.documentation_namespace
|
|
134
|
+
):
|
|
135
|
+
print(
|
|
136
|
+
f"Documentation with id: {id} already exists in the index. Skipping..."
|
|
137
|
+
)
|
|
138
|
+
return id
|
|
139
|
+
self.Index.upsert(
|
|
140
|
+
vectors=[(id, self.generate_embedding(doc), {"documentation": doc})],
|
|
141
|
+
namespace=self.documentation_namespace,
|
|
142
|
+
)
|
|
143
|
+
return id
|
|
144
|
+
|
|
145
|
+
def add_question_sql(self, question: str, sql: str, **kwargs) -> str:
|
|
146
|
+
question_sql_json = json.dumps(
|
|
147
|
+
{
|
|
148
|
+
"question": question,
|
|
149
|
+
"sql": sql,
|
|
150
|
+
},
|
|
151
|
+
ensure_ascii=False,
|
|
152
|
+
)
|
|
153
|
+
id = deterministic_uuid(question_sql_json) + "-sql"
|
|
154
|
+
if self._check_if_embedding_exists(id=id, namespace=self.sql_namespace):
|
|
155
|
+
print(
|
|
156
|
+
f"Question-SQL with id: {id} already exists in the index. Skipping..."
|
|
157
|
+
)
|
|
158
|
+
return id
|
|
159
|
+
self.Index.upsert(
|
|
160
|
+
vectors=[
|
|
161
|
+
(
|
|
162
|
+
id,
|
|
163
|
+
self.generate_embedding(question_sql_json),
|
|
164
|
+
{"sql": question_sql_json},
|
|
165
|
+
)
|
|
166
|
+
],
|
|
167
|
+
namespace=self.sql_namespace,
|
|
168
|
+
)
|
|
169
|
+
return id
|
|
170
|
+
|
|
171
|
+
def get_related_ddl(self, question: str, **kwargs) -> list:
|
|
172
|
+
res = self.Index.query(
|
|
173
|
+
namespace=self.ddl_namespace,
|
|
174
|
+
vector=self.generate_embedding(question),
|
|
175
|
+
top_k=self.n_results,
|
|
176
|
+
include_values=True,
|
|
177
|
+
include_metadata=True,
|
|
178
|
+
)
|
|
179
|
+
return [match["metadata"]["ddl"] for match in res["matches"]] if res else []
|
|
180
|
+
|
|
181
|
+
def get_related_documentation(self, question: str, **kwargs) -> list:
|
|
182
|
+
res = self.Index.query(
|
|
183
|
+
namespace=self.documentation_namespace,
|
|
184
|
+
vector=self.generate_embedding(question),
|
|
185
|
+
top_k=self.n_results,
|
|
186
|
+
include_values=True,
|
|
187
|
+
include_metadata=True,
|
|
188
|
+
)
|
|
189
|
+
return (
|
|
190
|
+
[match["metadata"]["documentation"] for match in res["matches"]]
|
|
191
|
+
if res
|
|
192
|
+
else []
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def get_similar_question_sql(self, question: str, **kwargs) -> list:
|
|
196
|
+
res = self.Index.query(
|
|
197
|
+
namespace=self.sql_namespace,
|
|
198
|
+
vector=self.generate_embedding(question),
|
|
199
|
+
top_k=self.n_results,
|
|
200
|
+
include_values=True,
|
|
201
|
+
include_metadata=True,
|
|
202
|
+
)
|
|
203
|
+
return (
|
|
204
|
+
[
|
|
205
|
+
{
|
|
206
|
+
key: value
|
|
207
|
+
for key, value in json.loads(match["metadata"]["sql"]).items()
|
|
208
|
+
}
|
|
209
|
+
for match in res["matches"]
|
|
210
|
+
]
|
|
211
|
+
if res
|
|
212
|
+
else []
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def get_training_data(self, **kwargs) -> pd.DataFrame:
|
|
216
|
+
# Pinecone does not support getting all vectors in a namespace, so we have to query for the top_k vectors with a dummy vector
|
|
217
|
+
df = pd.DataFrame()
|
|
218
|
+
namespaces = {
|
|
219
|
+
"sql": self.sql_namespace,
|
|
220
|
+
"ddl": self.ddl_namespace,
|
|
221
|
+
"documentation": self.documentation_namespace,
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
for data_type, namespace in namespaces.items():
|
|
225
|
+
data = self.Index.query(
|
|
226
|
+
top_k=10000, # max results that pinecone allows
|
|
227
|
+
namespace=namespace,
|
|
228
|
+
include_values=True,
|
|
229
|
+
include_metadata=True,
|
|
230
|
+
vector=[0.0] * self.dimensions,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
if data is not None:
|
|
234
|
+
id_list = [match["id"] for match in data["matches"]]
|
|
235
|
+
content_list = [
|
|
236
|
+
match["metadata"][data_type] for match in data["matches"]
|
|
237
|
+
]
|
|
238
|
+
question_list = [
|
|
239
|
+
(
|
|
240
|
+
json.loads(match["metadata"][data_type])["question"]
|
|
241
|
+
if data_type == "sql"
|
|
242
|
+
else None
|
|
243
|
+
)
|
|
244
|
+
for match in data["matches"]
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
df_data = pd.DataFrame(
|
|
248
|
+
{
|
|
249
|
+
"id": id_list,
|
|
250
|
+
"question": question_list,
|
|
251
|
+
"content": content_list,
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
df_data["training_data_type"] = data_type
|
|
255
|
+
df = pd.concat([df, df_data])
|
|
256
|
+
|
|
257
|
+
return df
|
|
258
|
+
|
|
259
|
+
def remove_training_data(self, id: str, **kwargs) -> bool:
|
|
260
|
+
if id.endswith("-sql"):
|
|
261
|
+
self.Index.delete(ids=[id], namespace=self.sql_namespace)
|
|
262
|
+
return True
|
|
263
|
+
elif id.endswith("-ddl"):
|
|
264
|
+
self.Index.delete(ids=[id], namespace=self.ddl_namespace)
|
|
265
|
+
return True
|
|
266
|
+
elif id.endswith("-doc"):
|
|
267
|
+
self.Index.delete(ids=[id], namespace=self.documentation_namespace)
|
|
268
|
+
return True
|
|
269
|
+
else:
|
|
270
|
+
return False
|
|
271
|
+
|
|
272
|
+
def generate_embedding(self, data: str, **kwargs) -> List[float]:
|
|
273
|
+
embedding_model = TextEmbedding(model_name=self.fastembed_model)
|
|
274
|
+
embedding = next(embedding_model.embed(data))
|
|
275
|
+
return embedding.tolist()
|