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,190 @@
1
+ """Guarded execution of LLM-generated Plotly code (vanna-ai/vanna#1098).
2
+
3
+ `get_plotly_figure` runs Python written by the LLM, and the LLM prompt can be
4
+ influenced by users (e.g. the Flask `chart_instructions` query param). Without
5
+ a guard, a prompt injection becomes remote code execution.
6
+
7
+ This is defence in depth, NOT a true sandbox - CPython cannot be sandboxed
8
+ in-process. It closes the easy paths: arbitrary imports, `_`/dunder tricks,
9
+ file/network IO helpers, dangerous builtins and access to vanna's module globals.
10
+ """
11
+
12
+ import ast
13
+ import builtins
14
+
15
+ import pandas as pd
16
+ import plotly
17
+ import plotly.express as px
18
+ import plotly.graph_objects as go
19
+
20
+ ALLOWED_IMPORT_ROOTS = {"plotly", "pandas", "numpy", "math"}
21
+
22
+ # Attribute/import names that lead to the OS, the interpreter, or file/network IO.
23
+ DENIED_NAMES = {
24
+ # modules reachable as attributes of other modules
25
+ "os",
26
+ "sys",
27
+ "subprocess",
28
+ "builtins",
29
+ "importlib",
30
+ "io",
31
+ "ctypes",
32
+ "ctypeslib",
33
+ "socket",
34
+ "shutil",
35
+ "pickle",
36
+ "marshal",
37
+ "pty",
38
+ "requests",
39
+ # process / code execution
40
+ "system",
41
+ "popen",
42
+ "Popen",
43
+ "spawn",
44
+ "fork",
45
+ "eval",
46
+ "exec",
47
+ "compile",
48
+ "query",
49
+ "globals",
50
+ "locals",
51
+ "vars",
52
+ "getattr",
53
+ "setattr",
54
+ "delattr",
55
+ "open",
56
+ "input",
57
+ "breakpoint",
58
+ # pandas / numpy / plotly file IO
59
+ "to_csv",
60
+ "to_excel",
61
+ "to_json",
62
+ "to_html",
63
+ "to_parquet",
64
+ "to_pickle",
65
+ "to_sql",
66
+ "to_hdf",
67
+ "to_feather",
68
+ "to_stata",
69
+ "to_xml",
70
+ "to_orc",
71
+ "to_latex",
72
+ "to_markdown",
73
+ "to_string",
74
+ "to_clipboard",
75
+ "tofile",
76
+ "write_html",
77
+ "write_image",
78
+ "write_json",
79
+ "save",
80
+ "savez",
81
+ "savez_compressed",
82
+ "savetxt",
83
+ "load",
84
+ "loadtxt",
85
+ "genfromtxt",
86
+ "fromfile",
87
+ "memmap",
88
+ }
89
+
90
+ SAFE_BUILTIN_NAMES = [
91
+ "abs",
92
+ "all",
93
+ "any",
94
+ "bool",
95
+ "dict",
96
+ "enumerate",
97
+ "filter",
98
+ "float",
99
+ "int",
100
+ "isinstance",
101
+ "len",
102
+ "list",
103
+ "map",
104
+ "max",
105
+ "min",
106
+ "print",
107
+ "range",
108
+ "reversed",
109
+ "round",
110
+ "set",
111
+ "sorted",
112
+ "str",
113
+ "sum",
114
+ "tuple",
115
+ "zip",
116
+ "ValueError",
117
+ "TypeError",
118
+ "KeyError",
119
+ "IndexError",
120
+ "Exception",
121
+ ]
122
+
123
+
124
+ class UnsafePlotlyCodeError(ValueError):
125
+ pass
126
+
127
+
128
+ def _is_denied(name: str) -> bool:
129
+ # A bare "_" is the usual throwaway loop variable; any other leading "_" is private/dunder.
130
+ if name != "_" and name.startswith("_"):
131
+ return True
132
+ return name in DENIED_NAMES or name.startswith("read_")
133
+
134
+
135
+ def validate_plotly_code(code: str) -> None:
136
+ """Raise UnsafePlotlyCodeError if `code` uses anything outside the allowed subset."""
137
+ try:
138
+ tree = ast.parse(code)
139
+ except SyntaxError as e:
140
+ raise UnsafePlotlyCodeError(f"invalid Python: {e}") from e
141
+
142
+ for node in ast.walk(tree):
143
+ if isinstance(node, ast.Import):
144
+ for alias in node.names:
145
+ if alias.name.split(".")[0] not in ALLOWED_IMPORT_ROOTS:
146
+ raise UnsafePlotlyCodeError(
147
+ f"import of '{alias.name}' is not allowed"
148
+ )
149
+ elif isinstance(node, ast.ImportFrom):
150
+ if (
151
+ node.level
152
+ or (node.module or "").split(".")[0] not in ALLOWED_IMPORT_ROOTS
153
+ ):
154
+ raise UnsafePlotlyCodeError(
155
+ f"import from '{node.module}' is not allowed"
156
+ )
157
+ for alias in node.names:
158
+ if alias.name == "*" or _is_denied(alias.name):
159
+ raise UnsafePlotlyCodeError(
160
+ f"import of '{alias.name}' is not allowed"
161
+ )
162
+ elif isinstance(node, ast.Attribute) and _is_denied(node.attr):
163
+ raise UnsafePlotlyCodeError(f"access to '.{node.attr}' is not allowed")
164
+ elif isinstance(node, ast.Name) and _is_denied(node.id):
165
+ raise UnsafePlotlyCodeError(f"use of '{node.id}' is not allowed")
166
+
167
+
168
+ def _restricted_import(name, globals=None, locals=None, fromlist=(), level=0):
169
+ if level != 0 or name.split(".")[0] not in ALLOWED_IMPORT_ROOTS:
170
+ raise ImportError(f"import of '{name}' is not allowed in chart code")
171
+ return builtins.__import__(name, globals, locals, fromlist, level)
172
+
173
+
174
+ def exec_plotly_code(code: str, df: pd.DataFrame):
175
+ """Validate and run LLM-generated Plotly code; return the `fig` it defines (or None)."""
176
+ validate_plotly_code(code)
177
+
178
+ safe_builtins = {name: getattr(builtins, name) for name in SAFE_BUILTIN_NAMES}
179
+ safe_builtins["__import__"] = _restricted_import
180
+
181
+ namespace = {
182
+ "__builtins__": safe_builtins,
183
+ "df": df,
184
+ "pd": pd,
185
+ "plotly": plotly,
186
+ "px": px,
187
+ "go": go,
188
+ }
189
+ exec(code, namespace)
190
+ return namespace.get("fig")
@@ -0,0 +1 @@
1
+ from .bedrock_converse import Bedrock_Converse
@@ -0,0 +1,86 @@
1
+ from ..base import VannaBase
2
+
3
+ try:
4
+ import boto3
5
+ from botocore.exceptions import ClientError
6
+ except ImportError:
7
+ raise ImportError("Please install boto3 and botocore to use Amazon Bedrock models")
8
+
9
+
10
+ class Bedrock_Converse(VannaBase):
11
+ def __init__(self, client=None, config=None):
12
+ VannaBase.__init__(self, config=config)
13
+
14
+ # default parameters
15
+ self.temperature = 0.0
16
+ self.max_tokens = 500
17
+
18
+ if client is None:
19
+ raise ValueError(
20
+ "A valid Bedrock runtime client must be provided to invoke Bedrock models"
21
+ )
22
+ else:
23
+ self.client = client
24
+
25
+ if config is None:
26
+ raise ValueError(
27
+ "Config is required with model_id and inference parameters"
28
+ )
29
+
30
+ if "modelId" not in config:
31
+ raise ValueError("config must contain a modelId to invoke")
32
+ else:
33
+ self.model = config["modelId"]
34
+
35
+ if "temperature" in config:
36
+ self.temperature = config["temperature"]
37
+
38
+ if "max_tokens" in config:
39
+ self.max_tokens = config["max_tokens"]
40
+
41
+ def system_message(self, message: str) -> dict:
42
+ return {"role": "system", "content": message}
43
+
44
+ def user_message(self, message: str) -> dict:
45
+ return {"role": "user", "content": message}
46
+
47
+ def assistant_message(self, message: str) -> dict:
48
+ return {"role": "assistant", "content": message}
49
+
50
+ def submit_prompt(self, prompt, **kwargs) -> str:
51
+ inference_config = {
52
+ "temperature": self.temperature,
53
+ "maxTokens": self.max_tokens,
54
+ }
55
+ additional_model_fields = {
56
+ "top_p": 1, # setting top_p value for nucleus sampling
57
+ }
58
+
59
+ system_message = None
60
+ no_system_prompt = []
61
+ for prompt_message in prompt:
62
+ role = prompt_message["role"]
63
+ if role == "system":
64
+ system_message = prompt_message["content"]
65
+ else:
66
+ no_system_prompt.append(
67
+ {"role": role, "content": [{"text": prompt_message["content"]}]}
68
+ )
69
+
70
+ converse_api_params = {
71
+ "modelId": self.model,
72
+ "messages": no_system_prompt,
73
+ "inferenceConfig": inference_config,
74
+ "additionalModelRequestFields": additional_model_fields,
75
+ }
76
+
77
+ if system_message:
78
+ converse_api_params["system"] = [{"text": system_message}]
79
+
80
+ try:
81
+ response = self.client.converse(**converse_api_params)
82
+ text_content = response["output"]["message"]["content"][0]["text"]
83
+ return text_content
84
+ except ClientError as err:
85
+ message = err.response["Error"]["Message"]
86
+ raise Exception(f"A Bedrock client error occurred: {message}")
@@ -0,0 +1 @@
1
+ from .chromadb_vector import ChromaDB_VectorStore
@@ -0,0 +1,259 @@
1
+ import json
2
+ from typing import List
3
+
4
+ import chromadb
5
+ import pandas as pd
6
+ from chromadb.config import Settings
7
+ from chromadb.utils import embedding_functions
8
+
9
+ from ..base import VannaBase
10
+ from ..utils import deterministic_uuid
11
+
12
+ default_ef = embedding_functions.DefaultEmbeddingFunction()
13
+
14
+
15
+ class ChromaDB_VectorStore(VannaBase):
16
+ def __init__(self, config=None):
17
+ VannaBase.__init__(self, config=config)
18
+ if config is None:
19
+ config = {}
20
+
21
+ path = config.get("path", ".")
22
+ self.embedding_function = config.get("embedding_function", default_ef)
23
+ curr_client = config.get("client", "persistent")
24
+ collection_metadata = config.get("collection_metadata", None)
25
+ self.n_results_sql = config.get("n_results_sql", config.get("n_results", 10))
26
+ self.n_results_documentation = config.get(
27
+ "n_results_documentation", config.get("n_results", 10)
28
+ )
29
+ self.n_results_ddl = config.get("n_results_ddl", config.get("n_results", 10))
30
+
31
+ if curr_client == "persistent":
32
+ self.chroma_client = chromadb.PersistentClient(
33
+ path=path, settings=Settings(anonymized_telemetry=False)
34
+ )
35
+ elif curr_client == "in-memory":
36
+ self.chroma_client = chromadb.EphemeralClient(
37
+ settings=Settings(anonymized_telemetry=False)
38
+ )
39
+ elif isinstance(curr_client, chromadb.api.client.Client):
40
+ # allow providing client directly
41
+ self.chroma_client = curr_client
42
+ else:
43
+ raise ValueError(f"Unsupported client was set in config: {curr_client}")
44
+
45
+ self.documentation_collection = self.chroma_client.get_or_create_collection(
46
+ name="documentation",
47
+ embedding_function=self.embedding_function,
48
+ metadata=collection_metadata,
49
+ )
50
+ self.ddl_collection = self.chroma_client.get_or_create_collection(
51
+ name="ddl",
52
+ embedding_function=self.embedding_function,
53
+ metadata=collection_metadata,
54
+ )
55
+ self.sql_collection = self.chroma_client.get_or_create_collection(
56
+ name="sql",
57
+ embedding_function=self.embedding_function,
58
+ metadata=collection_metadata,
59
+ )
60
+
61
+ def generate_embedding(self, data: str, **kwargs) -> List[float]:
62
+ embedding = self.embedding_function([data])
63
+ if len(embedding) == 1:
64
+ return embedding[0]
65
+ return embedding
66
+
67
+ def add_question_sql(self, question: str, sql: str, **kwargs) -> str:
68
+ question_sql_json = json.dumps(
69
+ {
70
+ "question": question,
71
+ "sql": sql,
72
+ },
73
+ ensure_ascii=False,
74
+ )
75
+ id = deterministic_uuid(question_sql_json) + "-sql"
76
+ self.sql_collection.add(
77
+ documents=question_sql_json,
78
+ embeddings=self.generate_embedding(question_sql_json),
79
+ ids=id,
80
+ )
81
+
82
+ return id
83
+
84
+ def add_ddl(self, ddl: str, **kwargs) -> str:
85
+ id = deterministic_uuid(ddl) + "-ddl"
86
+ self.ddl_collection.add(
87
+ documents=ddl,
88
+ embeddings=self.generate_embedding(ddl),
89
+ ids=id,
90
+ )
91
+ return id
92
+
93
+ def add_documentation(self, documentation: str, **kwargs) -> str:
94
+ id = deterministic_uuid(documentation) + "-doc"
95
+ self.documentation_collection.add(
96
+ documents=documentation,
97
+ embeddings=self.generate_embedding(documentation),
98
+ ids=id,
99
+ )
100
+ return id
101
+
102
+ def get_training_data(self, **kwargs) -> pd.DataFrame:
103
+ sql_data = self.sql_collection.get()
104
+
105
+ df = pd.DataFrame()
106
+
107
+ if sql_data is not None:
108
+ # Extract the documents and ids
109
+ documents = [json.loads(doc) for doc in sql_data["documents"]]
110
+ ids = sql_data["ids"]
111
+
112
+ # Create a DataFrame
113
+ df_sql = pd.DataFrame(
114
+ {
115
+ "id": ids,
116
+ "question": [doc["question"] for doc in documents],
117
+ "content": [doc["sql"] for doc in documents],
118
+ }
119
+ )
120
+
121
+ df_sql["training_data_type"] = "sql"
122
+
123
+ df = pd.concat([df, df_sql])
124
+
125
+ ddl_data = self.ddl_collection.get()
126
+
127
+ if ddl_data is not None:
128
+ # Extract the documents and ids
129
+ documents = [doc for doc in ddl_data["documents"]]
130
+ ids = ddl_data["ids"]
131
+
132
+ # Create a DataFrame
133
+ df_ddl = pd.DataFrame(
134
+ {
135
+ "id": ids,
136
+ "question": [None for doc in documents],
137
+ "content": [doc for doc in documents],
138
+ }
139
+ )
140
+
141
+ df_ddl["training_data_type"] = "ddl"
142
+
143
+ df = pd.concat([df, df_ddl])
144
+
145
+ doc_data = self.documentation_collection.get()
146
+
147
+ if doc_data is not None:
148
+ # Extract the documents and ids
149
+ documents = [doc for doc in doc_data["documents"]]
150
+ ids = doc_data["ids"]
151
+
152
+ # Create a DataFrame
153
+ df_doc = pd.DataFrame(
154
+ {
155
+ "id": ids,
156
+ "question": [None for doc in documents],
157
+ "content": [doc for doc in documents],
158
+ }
159
+ )
160
+
161
+ df_doc["training_data_type"] = "documentation"
162
+
163
+ df = pd.concat([df, df_doc])
164
+
165
+ return df
166
+
167
+ def remove_training_data(self, id: str, **kwargs) -> bool:
168
+ if id.endswith("-sql"):
169
+ self.sql_collection.delete(ids=id)
170
+ return True
171
+ elif id.endswith("-ddl"):
172
+ self.ddl_collection.delete(ids=id)
173
+ return True
174
+ elif id.endswith("-doc"):
175
+ self.documentation_collection.delete(ids=id)
176
+ return True
177
+ else:
178
+ return False
179
+
180
+ def remove_collection(self, collection_name: str) -> bool:
181
+ """
182
+ This function can reset the collection to empty state.
183
+
184
+ Args:
185
+ collection_name (str): sql or ddl or documentation
186
+
187
+ Returns:
188
+ bool: True if collection is deleted, False otherwise
189
+ """
190
+ if collection_name == "sql":
191
+ self.chroma_client.delete_collection(name="sql")
192
+ self.sql_collection = self.chroma_client.get_or_create_collection(
193
+ name="sql", embedding_function=self.embedding_function
194
+ )
195
+ return True
196
+ elif collection_name == "ddl":
197
+ self.chroma_client.delete_collection(name="ddl")
198
+ self.ddl_collection = self.chroma_client.get_or_create_collection(
199
+ name="ddl", embedding_function=self.embedding_function
200
+ )
201
+ return True
202
+ elif collection_name == "documentation":
203
+ self.chroma_client.delete_collection(name="documentation")
204
+ self.documentation_collection = self.chroma_client.get_or_create_collection(
205
+ name="documentation", embedding_function=self.embedding_function
206
+ )
207
+ return True
208
+ else:
209
+ return False
210
+
211
+ @staticmethod
212
+ def _extract_documents(query_results) -> list:
213
+ """
214
+ Static method to extract the documents from the results of a query.
215
+
216
+ Args:
217
+ query_results (pd.DataFrame): The dataframe to use.
218
+
219
+ Returns:
220
+ List[str] or None: The extracted documents, or an empty list or
221
+ single document if an error occurred.
222
+ """
223
+ if query_results is None:
224
+ return []
225
+
226
+ if "documents" in query_results:
227
+ documents = query_results["documents"]
228
+
229
+ if len(documents) == 1 and isinstance(documents[0], list):
230
+ try:
231
+ documents = [json.loads(doc) for doc in documents[0]]
232
+ except Exception as e:
233
+ return documents[0]
234
+
235
+ return documents
236
+
237
+ def get_similar_question_sql(self, question: str, **kwargs) -> list:
238
+ return ChromaDB_VectorStore._extract_documents(
239
+ self.sql_collection.query(
240
+ query_texts=[question],
241
+ n_results=self.n_results_sql,
242
+ )
243
+ )
244
+
245
+ def get_related_ddl(self, question: str, **kwargs) -> list:
246
+ return ChromaDB_VectorStore._extract_documents(
247
+ self.ddl_collection.query(
248
+ query_texts=[question],
249
+ n_results=self.n_results_ddl,
250
+ )
251
+ )
252
+
253
+ def get_related_documentation(self, question: str, **kwargs) -> list:
254
+ return ChromaDB_VectorStore._extract_documents(
255
+ self.documentation_collection.query(
256
+ query_texts=[question],
257
+ n_results=self.n_results_documentation,
258
+ )
259
+ )
@@ -0,0 +1,2 @@
1
+ from .cohere_chat import Cohere_Chat
2
+ from .cohere_embeddings import Cohere_Embeddings
@@ -0,0 +1,99 @@
1
+ import os
2
+
3
+ from openai import OpenAI
4
+
5
+ from ..base import VannaBase
6
+
7
+
8
+ class Cohere_Chat(VannaBase):
9
+ def __init__(self, client=None, config=None):
10
+ VannaBase.__init__(self, config=config)
11
+
12
+ # default parameters - can be overridden using config
13
+ self.temperature = 0.2 # Lower temperature for more precise SQL generation
14
+ self.model = "command-a-03-2025" # Cohere's default model
15
+
16
+ if config is not None:
17
+ if "temperature" in config:
18
+ self.temperature = config["temperature"]
19
+ if "model" in config:
20
+ self.model = config["model"]
21
+
22
+ if client is not None:
23
+ self.client = client
24
+ return
25
+
26
+ # Check for API key in environment variable
27
+ api_key = os.getenv("COHERE_API_KEY")
28
+
29
+ # Check for API key in config
30
+ if config is not None and "api_key" in config:
31
+ api_key = config["api_key"]
32
+
33
+ # Validate API key
34
+ if not api_key:
35
+ raise ValueError(
36
+ "Cohere API key is required. Please provide it via config or set the COHERE_API_KEY environment variable."
37
+ )
38
+
39
+ # Initialize client with validated API key
40
+ self.client = OpenAI(
41
+ base_url="https://api.cohere.ai/compatibility/v1",
42
+ api_key=api_key,
43
+ )
44
+
45
+ def system_message(self, message: str) -> any:
46
+ return {
47
+ "role": "developer",
48
+ "content": message,
49
+ } # Cohere uses 'developer' for system role
50
+
51
+ def user_message(self, message: str) -> any:
52
+ return {"role": "user", "content": message}
53
+
54
+ def assistant_message(self, message: str) -> any:
55
+ return {"role": "assistant", "content": message}
56
+
57
+ def submit_prompt(self, prompt, **kwargs) -> str:
58
+ if prompt is None:
59
+ raise Exception("Prompt is None")
60
+
61
+ if len(prompt) == 0:
62
+ raise Exception("Prompt is empty")
63
+
64
+ # Count the number of tokens in the message log
65
+ # Use 4 as an approximation for the number of characters per token
66
+ num_tokens = 0
67
+ for message in prompt:
68
+ num_tokens += len(message["content"]) / 4
69
+
70
+ # Use model from kwargs, config, or default
71
+ model = kwargs.get("model", self.model)
72
+ if self.config is not None and "model" in self.config and model == self.model:
73
+ model = self.config["model"]
74
+
75
+ print(f"Using model {model} for {num_tokens} tokens (approx)")
76
+ try:
77
+ response = self.client.chat.completions.create(
78
+ model=model,
79
+ messages=prompt,
80
+ temperature=self.temperature,
81
+ )
82
+
83
+ # Check if response has expected structure
84
+ if not response or not hasattr(response, "choices") or not response.choices:
85
+ raise ValueError("Received empty or malformed response from API")
86
+
87
+ if not response.choices[0] or not hasattr(response.choices[0], "message"):
88
+ raise ValueError("Response is missing expected 'message' field")
89
+
90
+ if not hasattr(response.choices[0].message, "content"):
91
+ raise ValueError("Response message is missing expected 'content' field")
92
+
93
+ return response.choices[0].message.content
94
+
95
+ except Exception as e:
96
+ # Log the error and raise a more informative exception
97
+ error_msg = f"Error processing Cohere chat response: {str(e)}"
98
+ print(error_msg)
99
+ raise Exception(error_msg)