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.
Files changed (291) hide show
  1. asksql-2.1.0.dist-info/METADATA +517 -0
  2. asksql-2.1.0.dist-info/RECORD +291 -0
  3. asksql-2.1.0.dist-info/WHEEL +4 -0
  4. asksql-2.1.0.dist-info/entry_points.txt +4 -0
  5. asksql-2.1.0.dist-info/licenses/LICENSE +21 -0
  6. vanna/__init__.py +172 -0
  7. vanna/agents/__init__.py +7 -0
  8. vanna/capabilities/__init__.py +17 -0
  9. vanna/capabilities/agent_memory/__init__.py +21 -0
  10. vanna/capabilities/agent_memory/base.py +103 -0
  11. vanna/capabilities/agent_memory/models.py +53 -0
  12. vanna/capabilities/file_system/__init__.py +14 -0
  13. vanna/capabilities/file_system/base.py +71 -0
  14. vanna/capabilities/file_system/models.py +25 -0
  15. vanna/capabilities/sql_runner/__init__.py +13 -0
  16. vanna/capabilities/sql_runner/base.py +37 -0
  17. vanna/capabilities/sql_runner/models.py +13 -0
  18. vanna/components/__init__.py +92 -0
  19. vanna/components/base.py +11 -0
  20. vanna/components/rich/__init__.py +83 -0
  21. vanna/components/rich/containers/__init__.py +7 -0
  22. vanna/components/rich/containers/card.py +20 -0
  23. vanna/components/rich/data/__init__.py +9 -0
  24. vanna/components/rich/data/chart.py +17 -0
  25. vanna/components/rich/data/dataframe.py +93 -0
  26. vanna/components/rich/feedback/__init__.py +21 -0
  27. vanna/components/rich/feedback/badge.py +16 -0
  28. vanna/components/rich/feedback/icon_text.py +14 -0
  29. vanna/components/rich/feedback/log_viewer.py +41 -0
  30. vanna/components/rich/feedback/notification.py +19 -0
  31. vanna/components/rich/feedback/progress.py +37 -0
  32. vanna/components/rich/feedback/status_card.py +28 -0
  33. vanna/components/rich/feedback/status_indicator.py +14 -0
  34. vanna/components/rich/interactive/__init__.py +21 -0
  35. vanna/components/rich/interactive/button.py +95 -0
  36. vanna/components/rich/interactive/task_list.py +58 -0
  37. vanna/components/rich/interactive/ui_state.py +93 -0
  38. vanna/components/rich/specialized/__init__.py +7 -0
  39. vanna/components/rich/specialized/artifact.py +20 -0
  40. vanna/components/rich/text.py +16 -0
  41. vanna/components/simple/__init__.py +15 -0
  42. vanna/components/simple/image.py +15 -0
  43. vanna/components/simple/link.py +15 -0
  44. vanna/components/simple/text.py +11 -0
  45. vanna/core/__init__.py +193 -0
  46. vanna/core/_compat.py +19 -0
  47. vanna/core/agent/__init__.py +10 -0
  48. vanna/core/agent/agent.py +1407 -0
  49. vanna/core/agent/config.py +123 -0
  50. vanna/core/audit/__init__.py +28 -0
  51. vanna/core/audit/base.py +299 -0
  52. vanna/core/audit/models.py +131 -0
  53. vanna/core/component_manager.py +329 -0
  54. vanna/core/components.py +53 -0
  55. vanna/core/enhancer/__init__.py +11 -0
  56. vanna/core/enhancer/base.py +94 -0
  57. vanna/core/enhancer/default.py +118 -0
  58. vanna/core/enricher/__init__.py +10 -0
  59. vanna/core/enricher/base.py +59 -0
  60. vanna/core/errors.py +47 -0
  61. vanna/core/evaluation/__init__.py +81 -0
  62. vanna/core/evaluation/base.py +186 -0
  63. vanna/core/evaluation/dataset.py +254 -0
  64. vanna/core/evaluation/evaluators.py +376 -0
  65. vanna/core/evaluation/report.py +289 -0
  66. vanna/core/evaluation/runner.py +313 -0
  67. vanna/core/filter/__init__.py +10 -0
  68. vanna/core/filter/base.py +67 -0
  69. vanna/core/lifecycle/__init__.py +10 -0
  70. vanna/core/lifecycle/base.py +83 -0
  71. vanna/core/llm/__init__.py +16 -0
  72. vanna/core/llm/base.py +40 -0
  73. vanna/core/llm/models.py +61 -0
  74. vanna/core/middleware/__init__.py +10 -0
  75. vanna/core/middleware/base.py +69 -0
  76. vanna/core/observability/__init__.py +11 -0
  77. vanna/core/observability/base.py +88 -0
  78. vanna/core/observability/models.py +47 -0
  79. vanna/core/recovery/__init__.py +11 -0
  80. vanna/core/recovery/base.py +84 -0
  81. vanna/core/recovery/models.py +32 -0
  82. vanna/core/registry.py +278 -0
  83. vanna/core/rich_component.py +156 -0
  84. vanna/core/simple_component.py +27 -0
  85. vanna/core/storage/__init__.py +14 -0
  86. vanna/core/storage/base.py +46 -0
  87. vanna/core/storage/models.py +46 -0
  88. vanna/core/system_prompt/__init__.py +13 -0
  89. vanna/core/system_prompt/base.py +36 -0
  90. vanna/core/system_prompt/default.py +157 -0
  91. vanna/core/tool/__init__.py +18 -0
  92. vanna/core/tool/base.py +70 -0
  93. vanna/core/tool/models.py +84 -0
  94. vanna/core/user/__init__.py +17 -0
  95. vanna/core/user/base.py +29 -0
  96. vanna/core/user/models.py +25 -0
  97. vanna/core/user/request_context.py +70 -0
  98. vanna/core/user/resolver.py +42 -0
  99. vanna/core/validation.py +164 -0
  100. vanna/core/workflow/__init__.py +12 -0
  101. vanna/core/workflow/base.py +254 -0
  102. vanna/core/workflow/default.py +789 -0
  103. vanna/examples/__init__.py +1 -0
  104. vanna/examples/__main__.py +44 -0
  105. vanna/examples/anthropic_quickstart.py +80 -0
  106. vanna/examples/artifact_example.py +293 -0
  107. vanna/examples/claude_sqlite_example.py +236 -0
  108. vanna/examples/coding_agent_example.py +300 -0
  109. vanna/examples/custom_system_prompt_example.py +174 -0
  110. vanna/examples/default_workflow_handler_example.py +208 -0
  111. vanna/examples/email_auth_example.py +340 -0
  112. vanna/examples/evaluation_example.py +269 -0
  113. vanna/examples/extensibility_example.py +262 -0
  114. vanna/examples/minimal_example.py +67 -0
  115. vanna/examples/mock_auth_example.py +227 -0
  116. vanna/examples/mock_custom_tool.py +311 -0
  117. vanna/examples/mock_quickstart.py +79 -0
  118. vanna/examples/mock_quota_example.py +145 -0
  119. vanna/examples/mock_rich_components_demo.py +396 -0
  120. vanna/examples/mock_sqlite_example.py +223 -0
  121. vanna/examples/openai_quickstart.py +83 -0
  122. vanna/examples/primitive_components_demo.py +305 -0
  123. vanna/examples/quota_lifecycle_example.py +139 -0
  124. vanna/examples/visualization_example.py +251 -0
  125. vanna/integrations/__init__.py +17 -0
  126. vanna/integrations/anthropic/__init__.py +9 -0
  127. vanna/integrations/anthropic/llm.py +270 -0
  128. vanna/integrations/azureopenai/__init__.py +9 -0
  129. vanna/integrations/azureopenai/llm.py +329 -0
  130. vanna/integrations/azuresearch/__init__.py +7 -0
  131. vanna/integrations/azuresearch/agent_memory.py +413 -0
  132. vanna/integrations/bigquery/__init__.py +5 -0
  133. vanna/integrations/bigquery/sql_runner.py +81 -0
  134. vanna/integrations/chromadb/__init__.py +104 -0
  135. vanna/integrations/chromadb/agent_memory.py +488 -0
  136. vanna/integrations/clickhouse/__init__.py +5 -0
  137. vanna/integrations/clickhouse/sql_runner.py +82 -0
  138. vanna/integrations/duckdb/__init__.py +5 -0
  139. vanna/integrations/duckdb/sql_runner.py +65 -0
  140. vanna/integrations/faiss/__init__.py +7 -0
  141. vanna/integrations/faiss/agent_memory.py +435 -0
  142. vanna/integrations/google/__init__.py +9 -0
  143. vanna/integrations/google/gemini.py +370 -0
  144. vanna/integrations/hive/__init__.py +5 -0
  145. vanna/integrations/hive/sql_runner.py +87 -0
  146. vanna/integrations/local/__init__.py +17 -0
  147. vanna/integrations/local/agent_memory/__init__.py +7 -0
  148. vanna/integrations/local/agent_memory/in_memory.py +285 -0
  149. vanna/integrations/local/audit.py +59 -0
  150. vanna/integrations/local/file_system.py +242 -0
  151. vanna/integrations/local/file_system_conversation_store.py +255 -0
  152. vanna/integrations/local/storage.py +62 -0
  153. vanna/integrations/marqo/__init__.py +7 -0
  154. vanna/integrations/marqo/agent_memory.py +354 -0
  155. vanna/integrations/milvus/__init__.py +7 -0
  156. vanna/integrations/milvus/agent_memory.py +458 -0
  157. vanna/integrations/mock/__init__.py +9 -0
  158. vanna/integrations/mock/llm.py +65 -0
  159. vanna/integrations/mssql/__init__.py +5 -0
  160. vanna/integrations/mssql/sql_runner.py +66 -0
  161. vanna/integrations/mysql/__init__.py +5 -0
  162. vanna/integrations/mysql/sql_runner.py +92 -0
  163. vanna/integrations/ollama/__init__.py +7 -0
  164. vanna/integrations/ollama/llm.py +252 -0
  165. vanna/integrations/openai/__init__.py +10 -0
  166. vanna/integrations/openai/llm.py +267 -0
  167. vanna/integrations/openai/responses.py +163 -0
  168. vanna/integrations/opensearch/__init__.py +7 -0
  169. vanna/integrations/opensearch/agent_memory.py +411 -0
  170. vanna/integrations/oracle/__init__.py +5 -0
  171. vanna/integrations/oracle/sql_runner.py +75 -0
  172. vanna/integrations/pinecone/__init__.py +7 -0
  173. vanna/integrations/pinecone/agent_memory.py +329 -0
  174. vanna/integrations/plotly/__init__.py +5 -0
  175. vanna/integrations/plotly/chart_generator.py +313 -0
  176. vanna/integrations/postgres/__init__.py +9 -0
  177. vanna/integrations/postgres/sql_runner.py +112 -0
  178. vanna/integrations/premium/agent_memory/__init__.py +7 -0
  179. vanna/integrations/premium/agent_memory/premium.py +186 -0
  180. vanna/integrations/presto/__init__.py +5 -0
  181. vanna/integrations/presto/sql_runner.py +107 -0
  182. vanna/integrations/qdrant/__init__.py +7 -0
  183. vanna/integrations/qdrant/agent_memory.py +466 -0
  184. vanna/integrations/snowflake/__init__.py +5 -0
  185. vanna/integrations/snowflake/sql_runner.py +147 -0
  186. vanna/integrations/sqlite/__init__.py +9 -0
  187. vanna/integrations/sqlite/sql_runner.py +65 -0
  188. vanna/integrations/weaviate/__init__.py +7 -0
  189. vanna/integrations/weaviate/agent_memory.py +428 -0
  190. vanna/legacy/ZhipuAI/ZhipuAI_Chat.py +233 -0
  191. vanna/legacy/ZhipuAI/ZhipuAI_embeddings.py +79 -0
  192. vanna/legacy/ZhipuAI/__init__.py +2 -0
  193. vanna/legacy/__init__.py +403 -0
  194. vanna/legacy/adapter.py +468 -0
  195. vanna/legacy/advanced/__init__.py +28 -0
  196. vanna/legacy/anthropic/__init__.py +1 -0
  197. vanna/legacy/anthropic/anthropic_chat.py +80 -0
  198. vanna/legacy/azuresearch/__init__.py +1 -0
  199. vanna/legacy/azuresearch/azuresearch_vector.py +274 -0
  200. vanna/legacy/base/__init__.py +1 -0
  201. vanna/legacy/base/base.py +2133 -0
  202. vanna/legacy/base/safe_exec.py +190 -0
  203. vanna/legacy/bedrock/__init__.py +1 -0
  204. vanna/legacy/bedrock/bedrock_converse.py +86 -0
  205. vanna/legacy/chromadb/__init__.py +1 -0
  206. vanna/legacy/chromadb/chromadb_vector.py +259 -0
  207. vanna/legacy/cohere/__init__.py +2 -0
  208. vanna/legacy/cohere/cohere_chat.py +99 -0
  209. vanna/legacy/cohere/cohere_embeddings.py +77 -0
  210. vanna/legacy/deepseek/__init__.py +1 -0
  211. vanna/legacy/deepseek/deepseek_chat.py +59 -0
  212. vanna/legacy/exceptions/__init__.py +46 -0
  213. vanna/legacy/faiss/__init__.py +1 -0
  214. vanna/legacy/faiss/faiss.py +230 -0
  215. vanna/legacy/flask/__init__.py +1355 -0
  216. vanna/legacy/flask/assets.py +58 -0
  217. vanna/legacy/flask/auth.py +56 -0
  218. vanna/legacy/google/__init__.py +2 -0
  219. vanna/legacy/google/bigquery_vector.py +293 -0
  220. vanna/legacy/google/gemini_chat.py +78 -0
  221. vanna/legacy/hf/__init__.py +1 -0
  222. vanna/legacy/hf/hf.py +80 -0
  223. vanna/legacy/local.py +8 -0
  224. vanna/legacy/marqo/__init__.py +1 -0
  225. vanna/legacy/marqo/marqo.py +168 -0
  226. vanna/legacy/milvus/__init__.py +1 -0
  227. vanna/legacy/milvus/milvus_vector.py +328 -0
  228. vanna/legacy/mistral/__init__.py +1 -0
  229. vanna/legacy/mistral/mistral.py +51 -0
  230. vanna/legacy/mock/__init__.py +3 -0
  231. vanna/legacy/mock/embedding.py +11 -0
  232. vanna/legacy/mock/llm.py +18 -0
  233. vanna/legacy/mock/vectordb.py +67 -0
  234. vanna/legacy/ollama/__init__.py +1 -0
  235. vanna/legacy/ollama/ollama.py +110 -0
  236. vanna/legacy/openai/__init__.py +2 -0
  237. vanna/legacy/openai/openai_chat.py +124 -0
  238. vanna/legacy/openai/openai_embeddings.py +46 -0
  239. vanna/legacy/opensearch/__init__.py +2 -0
  240. vanna/legacy/opensearch/opensearch_vector.py +369 -0
  241. vanna/legacy/opensearch/opensearch_vector_semantic.py +200 -0
  242. vanna/legacy/oracle/__init__.py +1 -0
  243. vanna/legacy/oracle/oracle_vector.py +584 -0
  244. vanna/legacy/pgvector/__init__.py +1 -0
  245. vanna/legacy/pgvector/pgvector.py +282 -0
  246. vanna/legacy/pinecone/__init__.py +3 -0
  247. vanna/legacy/pinecone/pinecone_vector.py +275 -0
  248. vanna/legacy/qdrant/__init__.py +3 -0
  249. vanna/legacy/qdrant/qdrant.py +329 -0
  250. vanna/legacy/qianfan/Qianfan_Chat.py +170 -0
  251. vanna/legacy/qianfan/Qianfan_embeddings.py +36 -0
  252. vanna/legacy/qianfan/__init__.py +2 -0
  253. vanna/legacy/qianwen/QianwenAI_chat.py +132 -0
  254. vanna/legacy/qianwen/QianwenAI_embeddings.py +46 -0
  255. vanna/legacy/qianwen/__init__.py +2 -0
  256. vanna/legacy/remote.py +79 -0
  257. vanna/legacy/types/__init__.py +292 -0
  258. vanna/legacy/utils.py +72 -0
  259. vanna/legacy/vannadb/__init__.py +1 -0
  260. vanna/legacy/vannadb/vannadb_vector.py +468 -0
  261. vanna/legacy/vllm/__init__.py +1 -0
  262. vanna/legacy/vllm/vllm.py +97 -0
  263. vanna/legacy/weaviate/__init__.py +1 -0
  264. vanna/legacy/weaviate/weaviate_vector.py +194 -0
  265. vanna/legacy/xinference/__init__.py +1 -0
  266. vanna/legacy/xinference/xinference.py +53 -0
  267. vanna/py.typed +0 -0
  268. vanna/servers/__init__.py +16 -0
  269. vanna/servers/__main__.py +8 -0
  270. vanna/servers/base/__init__.py +18 -0
  271. vanna/servers/base/chat_handler.py +65 -0
  272. vanna/servers/base/models.py +111 -0
  273. vanna/servers/base/rich_chat_handler.py +141 -0
  274. vanna/servers/base/templates.py +331 -0
  275. vanna/servers/cli/__init__.py +7 -0
  276. vanna/servers/cli/server_runner.py +204 -0
  277. vanna/servers/fastapi/__init__.py +7 -0
  278. vanna/servers/fastapi/app.py +163 -0
  279. vanna/servers/fastapi/routes.py +183 -0
  280. vanna/servers/flask/__init__.py +7 -0
  281. vanna/servers/flask/app.py +132 -0
  282. vanna/servers/flask/routes.py +137 -0
  283. vanna/tools/__init__.py +41 -0
  284. vanna/tools/agent_memory.py +322 -0
  285. vanna/tools/file_system.py +879 -0
  286. vanna/tools/python.py +222 -0
  287. vanna/tools/run_sql.py +173 -0
  288. vanna/tools/visualize_data.py +195 -0
  289. vanna/utils/__init__.py +0 -0
  290. vanna/utils/sql_safety.py +84 -0
  291. 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,3 @@
1
+ from .pinecone_vector import PineconeDB_VectorStore
2
+
3
+ __all__ = ["PineconeDB_VectorStore"]
@@ -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()
@@ -0,0 +1,3 @@
1
+ from .qdrant import Qdrant_VectorStore
2
+
3
+ __all__ = ["Qdrant_VectorStore"]