vanna 0.7.8__py3-none-any.whl → 2.0.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 (302) hide show
  1. vanna/__init__.py +167 -395
  2. vanna/agents/__init__.py +7 -0
  3. vanna/capabilities/__init__.py +17 -0
  4. vanna/capabilities/agent_memory/__init__.py +21 -0
  5. vanna/capabilities/agent_memory/base.py +103 -0
  6. vanna/capabilities/agent_memory/models.py +53 -0
  7. vanna/capabilities/file_system/__init__.py +14 -0
  8. vanna/capabilities/file_system/base.py +71 -0
  9. vanna/capabilities/file_system/models.py +25 -0
  10. vanna/capabilities/sql_runner/__init__.py +13 -0
  11. vanna/capabilities/sql_runner/base.py +37 -0
  12. vanna/capabilities/sql_runner/models.py +13 -0
  13. vanna/components/__init__.py +92 -0
  14. vanna/components/base.py +11 -0
  15. vanna/components/rich/__init__.py +83 -0
  16. vanna/components/rich/containers/__init__.py +7 -0
  17. vanna/components/rich/containers/card.py +20 -0
  18. vanna/components/rich/data/__init__.py +9 -0
  19. vanna/components/rich/data/chart.py +17 -0
  20. vanna/components/rich/data/dataframe.py +93 -0
  21. vanna/components/rich/feedback/__init__.py +21 -0
  22. vanna/components/rich/feedback/badge.py +16 -0
  23. vanna/components/rich/feedback/icon_text.py +14 -0
  24. vanna/components/rich/feedback/log_viewer.py +41 -0
  25. vanna/components/rich/feedback/notification.py +19 -0
  26. vanna/components/rich/feedback/progress.py +37 -0
  27. vanna/components/rich/feedback/status_card.py +28 -0
  28. vanna/components/rich/feedback/status_indicator.py +14 -0
  29. vanna/components/rich/interactive/__init__.py +21 -0
  30. vanna/components/rich/interactive/button.py +95 -0
  31. vanna/components/rich/interactive/task_list.py +58 -0
  32. vanna/components/rich/interactive/ui_state.py +93 -0
  33. vanna/components/rich/specialized/__init__.py +7 -0
  34. vanna/components/rich/specialized/artifact.py +20 -0
  35. vanna/components/rich/text.py +16 -0
  36. vanna/components/simple/__init__.py +15 -0
  37. vanna/components/simple/image.py +15 -0
  38. vanna/components/simple/link.py +15 -0
  39. vanna/components/simple/text.py +11 -0
  40. vanna/core/__init__.py +193 -0
  41. vanna/core/_compat.py +19 -0
  42. vanna/core/agent/__init__.py +10 -0
  43. vanna/core/agent/agent.py +1407 -0
  44. vanna/core/agent/config.py +123 -0
  45. vanna/core/audit/__init__.py +28 -0
  46. vanna/core/audit/base.py +299 -0
  47. vanna/core/audit/models.py +131 -0
  48. vanna/core/component_manager.py +329 -0
  49. vanna/core/components.py +53 -0
  50. vanna/core/enhancer/__init__.py +11 -0
  51. vanna/core/enhancer/base.py +94 -0
  52. vanna/core/enhancer/default.py +118 -0
  53. vanna/core/enricher/__init__.py +10 -0
  54. vanna/core/enricher/base.py +59 -0
  55. vanna/core/errors.py +47 -0
  56. vanna/core/evaluation/__init__.py +81 -0
  57. vanna/core/evaluation/base.py +186 -0
  58. vanna/core/evaluation/dataset.py +254 -0
  59. vanna/core/evaluation/evaluators.py +376 -0
  60. vanna/core/evaluation/report.py +289 -0
  61. vanna/core/evaluation/runner.py +313 -0
  62. vanna/core/filter/__init__.py +10 -0
  63. vanna/core/filter/base.py +67 -0
  64. vanna/core/lifecycle/__init__.py +10 -0
  65. vanna/core/lifecycle/base.py +83 -0
  66. vanna/core/llm/__init__.py +16 -0
  67. vanna/core/llm/base.py +40 -0
  68. vanna/core/llm/models.py +61 -0
  69. vanna/core/middleware/__init__.py +10 -0
  70. vanna/core/middleware/base.py +69 -0
  71. vanna/core/observability/__init__.py +11 -0
  72. vanna/core/observability/base.py +88 -0
  73. vanna/core/observability/models.py +47 -0
  74. vanna/core/recovery/__init__.py +11 -0
  75. vanna/core/recovery/base.py +84 -0
  76. vanna/core/recovery/models.py +32 -0
  77. vanna/core/registry.py +278 -0
  78. vanna/core/rich_component.py +156 -0
  79. vanna/core/simple_component.py +27 -0
  80. vanna/core/storage/__init__.py +14 -0
  81. vanna/core/storage/base.py +46 -0
  82. vanna/core/storage/models.py +46 -0
  83. vanna/core/system_prompt/__init__.py +13 -0
  84. vanna/core/system_prompt/base.py +36 -0
  85. vanna/core/system_prompt/default.py +157 -0
  86. vanna/core/tool/__init__.py +18 -0
  87. vanna/core/tool/base.py +70 -0
  88. vanna/core/tool/models.py +84 -0
  89. vanna/core/user/__init__.py +17 -0
  90. vanna/core/user/base.py +29 -0
  91. vanna/core/user/models.py +25 -0
  92. vanna/core/user/request_context.py +70 -0
  93. vanna/core/user/resolver.py +42 -0
  94. vanna/core/validation.py +164 -0
  95. vanna/core/workflow/__init__.py +12 -0
  96. vanna/core/workflow/base.py +254 -0
  97. vanna/core/workflow/default.py +789 -0
  98. vanna/examples/__init__.py +1 -0
  99. vanna/examples/__main__.py +44 -0
  100. vanna/examples/anthropic_quickstart.py +80 -0
  101. vanna/examples/artifact_example.py +293 -0
  102. vanna/examples/claude_sqlite_example.py +236 -0
  103. vanna/examples/coding_agent_example.py +300 -0
  104. vanna/examples/custom_system_prompt_example.py +174 -0
  105. vanna/examples/default_workflow_handler_example.py +208 -0
  106. vanna/examples/email_auth_example.py +340 -0
  107. vanna/examples/evaluation_example.py +269 -0
  108. vanna/examples/extensibility_example.py +262 -0
  109. vanna/examples/minimal_example.py +67 -0
  110. vanna/examples/mock_auth_example.py +227 -0
  111. vanna/examples/mock_custom_tool.py +311 -0
  112. vanna/examples/mock_quickstart.py +79 -0
  113. vanna/examples/mock_quota_example.py +145 -0
  114. vanna/examples/mock_rich_components_demo.py +396 -0
  115. vanna/examples/mock_sqlite_example.py +223 -0
  116. vanna/examples/openai_quickstart.py +83 -0
  117. vanna/examples/primitive_components_demo.py +305 -0
  118. vanna/examples/quota_lifecycle_example.py +139 -0
  119. vanna/examples/visualization_example.py +251 -0
  120. vanna/integrations/__init__.py +17 -0
  121. vanna/integrations/anthropic/__init__.py +9 -0
  122. vanna/integrations/anthropic/llm.py +270 -0
  123. vanna/integrations/azureopenai/__init__.py +9 -0
  124. vanna/integrations/azureopenai/llm.py +329 -0
  125. vanna/integrations/azuresearch/__init__.py +7 -0
  126. vanna/integrations/azuresearch/agent_memory.py +413 -0
  127. vanna/integrations/bigquery/__init__.py +5 -0
  128. vanna/integrations/bigquery/sql_runner.py +81 -0
  129. vanna/integrations/chromadb/__init__.py +104 -0
  130. vanna/integrations/chromadb/agent_memory.py +416 -0
  131. vanna/integrations/clickhouse/__init__.py +5 -0
  132. vanna/integrations/clickhouse/sql_runner.py +82 -0
  133. vanna/integrations/duckdb/__init__.py +5 -0
  134. vanna/integrations/duckdb/sql_runner.py +65 -0
  135. vanna/integrations/faiss/__init__.py +7 -0
  136. vanna/integrations/faiss/agent_memory.py +431 -0
  137. vanna/integrations/google/__init__.py +9 -0
  138. vanna/integrations/google/gemini.py +370 -0
  139. vanna/integrations/hive/__init__.py +5 -0
  140. vanna/integrations/hive/sql_runner.py +87 -0
  141. vanna/integrations/local/__init__.py +17 -0
  142. vanna/integrations/local/agent_memory/__init__.py +7 -0
  143. vanna/integrations/local/agent_memory/in_memory.py +285 -0
  144. vanna/integrations/local/audit.py +59 -0
  145. vanna/integrations/local/file_system.py +242 -0
  146. vanna/integrations/local/file_system_conversation_store.py +255 -0
  147. vanna/integrations/local/storage.py +62 -0
  148. vanna/integrations/marqo/__init__.py +7 -0
  149. vanna/integrations/marqo/agent_memory.py +354 -0
  150. vanna/integrations/milvus/__init__.py +7 -0
  151. vanna/integrations/milvus/agent_memory.py +458 -0
  152. vanna/integrations/mock/__init__.py +9 -0
  153. vanna/integrations/mock/llm.py +65 -0
  154. vanna/integrations/mssql/__init__.py +5 -0
  155. vanna/integrations/mssql/sql_runner.py +66 -0
  156. vanna/integrations/mysql/__init__.py +5 -0
  157. vanna/integrations/mysql/sql_runner.py +92 -0
  158. vanna/integrations/ollama/__init__.py +7 -0
  159. vanna/integrations/ollama/llm.py +252 -0
  160. vanna/integrations/openai/__init__.py +10 -0
  161. vanna/integrations/openai/llm.py +267 -0
  162. vanna/integrations/openai/responses.py +163 -0
  163. vanna/integrations/opensearch/__init__.py +7 -0
  164. vanna/integrations/opensearch/agent_memory.py +411 -0
  165. vanna/integrations/oracle/__init__.py +5 -0
  166. vanna/integrations/oracle/sql_runner.py +75 -0
  167. vanna/integrations/pinecone/__init__.py +7 -0
  168. vanna/integrations/pinecone/agent_memory.py +329 -0
  169. vanna/integrations/plotly/__init__.py +5 -0
  170. vanna/integrations/plotly/chart_generator.py +313 -0
  171. vanna/integrations/postgres/__init__.py +9 -0
  172. vanna/integrations/postgres/sql_runner.py +112 -0
  173. vanna/integrations/premium/agent_memory/__init__.py +7 -0
  174. vanna/integrations/premium/agent_memory/premium.py +186 -0
  175. vanna/integrations/presto/__init__.py +5 -0
  176. vanna/integrations/presto/sql_runner.py +107 -0
  177. vanna/integrations/qdrant/__init__.py +7 -0
  178. vanna/integrations/qdrant/agent_memory.py +461 -0
  179. vanna/integrations/snowflake/__init__.py +5 -0
  180. vanna/integrations/snowflake/sql_runner.py +147 -0
  181. vanna/integrations/sqlite/__init__.py +9 -0
  182. vanna/integrations/sqlite/sql_runner.py +65 -0
  183. vanna/integrations/weaviate/__init__.py +7 -0
  184. vanna/integrations/weaviate/agent_memory.py +428 -0
  185. vanna/{ZhipuAI → legacy/ZhipuAI}/ZhipuAI_embeddings.py +11 -11
  186. vanna/legacy/__init__.py +403 -0
  187. vanna/legacy/adapter.py +463 -0
  188. vanna/{advanced → legacy/advanced}/__init__.py +3 -1
  189. vanna/{anthropic → legacy/anthropic}/anthropic_chat.py +9 -7
  190. vanna/{azuresearch → legacy/azuresearch}/azuresearch_vector.py +79 -41
  191. vanna/{base → legacy/base}/base.py +247 -223
  192. vanna/legacy/bedrock/__init__.py +1 -0
  193. vanna/{bedrock → legacy/bedrock}/bedrock_converse.py +13 -12
  194. vanna/{chromadb → legacy/chromadb}/chromadb_vector.py +3 -1
  195. vanna/legacy/cohere/__init__.py +2 -0
  196. vanna/{cohere → legacy/cohere}/cohere_chat.py +19 -14
  197. vanna/{cohere → legacy/cohere}/cohere_embeddings.py +25 -19
  198. vanna/{deepseek → legacy/deepseek}/deepseek_chat.py +5 -6
  199. vanna/legacy/faiss/__init__.py +1 -0
  200. vanna/{faiss → legacy/faiss}/faiss.py +113 -59
  201. vanna/{flask → legacy/flask}/__init__.py +84 -43
  202. vanna/{flask → legacy/flask}/assets.py +5 -5
  203. vanna/{flask → legacy/flask}/auth.py +5 -4
  204. vanna/{google → legacy/google}/bigquery_vector.py +75 -42
  205. vanna/{google → legacy/google}/gemini_chat.py +7 -3
  206. vanna/{hf → legacy/hf}/hf.py +0 -1
  207. vanna/{milvus → legacy/milvus}/milvus_vector.py +58 -35
  208. vanna/{mock → legacy/mock}/llm.py +0 -1
  209. vanna/legacy/mock/vectordb.py +67 -0
  210. vanna/legacy/ollama/ollama.py +110 -0
  211. vanna/{openai → legacy/openai}/openai_chat.py +2 -6
  212. vanna/legacy/opensearch/opensearch_vector.py +369 -0
  213. vanna/legacy/opensearch/opensearch_vector_semantic.py +200 -0
  214. vanna/legacy/oracle/oracle_vector.py +584 -0
  215. vanna/{pgvector → legacy/pgvector}/pgvector.py +42 -13
  216. vanna/{qdrant → legacy/qdrant}/qdrant.py +2 -6
  217. vanna/legacy/qianfan/Qianfan_Chat.py +170 -0
  218. vanna/legacy/qianfan/Qianfan_embeddings.py +36 -0
  219. vanna/legacy/qianwen/QianwenAI_chat.py +132 -0
  220. vanna/{remote.py → legacy/remote.py} +28 -26
  221. vanna/{utils.py → legacy/utils.py} +6 -11
  222. vanna/{vannadb → legacy/vannadb}/vannadb_vector.py +115 -46
  223. vanna/{vllm → legacy/vllm}/vllm.py +5 -6
  224. vanna/{weaviate → legacy/weaviate}/weaviate_vector.py +59 -40
  225. vanna/{xinference → legacy/xinference}/xinference.py +6 -6
  226. vanna/py.typed +0 -0
  227. vanna/servers/__init__.py +16 -0
  228. vanna/servers/__main__.py +8 -0
  229. vanna/servers/base/__init__.py +18 -0
  230. vanna/servers/base/chat_handler.py +65 -0
  231. vanna/servers/base/models.py +111 -0
  232. vanna/servers/base/rich_chat_handler.py +141 -0
  233. vanna/servers/base/templates.py +331 -0
  234. vanna/servers/cli/__init__.py +7 -0
  235. vanna/servers/cli/server_runner.py +204 -0
  236. vanna/servers/fastapi/__init__.py +7 -0
  237. vanna/servers/fastapi/app.py +163 -0
  238. vanna/servers/fastapi/routes.py +183 -0
  239. vanna/servers/flask/__init__.py +7 -0
  240. vanna/servers/flask/app.py +132 -0
  241. vanna/servers/flask/routes.py +137 -0
  242. vanna/tools/__init__.py +41 -0
  243. vanna/tools/agent_memory.py +322 -0
  244. vanna/tools/file_system.py +879 -0
  245. vanna/tools/python.py +222 -0
  246. vanna/tools/run_sql.py +165 -0
  247. vanna/tools/visualize_data.py +195 -0
  248. vanna/utils/__init__.py +0 -0
  249. vanna/web_components/__init__.py +44 -0
  250. vanna-2.0.0.dist-info/METADATA +485 -0
  251. vanna-2.0.0.dist-info/RECORD +289 -0
  252. vanna-2.0.0.dist-info/entry_points.txt +3 -0
  253. vanna/bedrock/__init__.py +0 -1
  254. vanna/cohere/__init__.py +0 -2
  255. vanna/faiss/__init__.py +0 -1
  256. vanna/mock/vectordb.py +0 -55
  257. vanna/ollama/ollama.py +0 -103
  258. vanna/opensearch/opensearch_vector.py +0 -392
  259. vanna/opensearch/opensearch_vector_semantic.py +0 -175
  260. vanna/oracle/oracle_vector.py +0 -585
  261. vanna/qianfan/Qianfan_Chat.py +0 -165
  262. vanna/qianfan/Qianfan_embeddings.py +0 -36
  263. vanna/qianwen/QianwenAI_chat.py +0 -133
  264. vanna-0.7.8.dist-info/METADATA +0 -408
  265. vanna-0.7.8.dist-info/RECORD +0 -79
  266. /vanna/{ZhipuAI → legacy/ZhipuAI}/ZhipuAI_Chat.py +0 -0
  267. /vanna/{ZhipuAI → legacy/ZhipuAI}/__init__.py +0 -0
  268. /vanna/{anthropic → legacy/anthropic}/__init__.py +0 -0
  269. /vanna/{azuresearch → legacy/azuresearch}/__init__.py +0 -0
  270. /vanna/{base → legacy/base}/__init__.py +0 -0
  271. /vanna/{chromadb → legacy/chromadb}/__init__.py +0 -0
  272. /vanna/{deepseek → legacy/deepseek}/__init__.py +0 -0
  273. /vanna/{exceptions → legacy/exceptions}/__init__.py +0 -0
  274. /vanna/{google → legacy/google}/__init__.py +0 -0
  275. /vanna/{hf → legacy/hf}/__init__.py +0 -0
  276. /vanna/{local.py → legacy/local.py} +0 -0
  277. /vanna/{marqo → legacy/marqo}/__init__.py +0 -0
  278. /vanna/{marqo → legacy/marqo}/marqo.py +0 -0
  279. /vanna/{milvus → legacy/milvus}/__init__.py +0 -0
  280. /vanna/{mistral → legacy/mistral}/__init__.py +0 -0
  281. /vanna/{mistral → legacy/mistral}/mistral.py +0 -0
  282. /vanna/{mock → legacy/mock}/__init__.py +0 -0
  283. /vanna/{mock → legacy/mock}/embedding.py +0 -0
  284. /vanna/{ollama → legacy/ollama}/__init__.py +0 -0
  285. /vanna/{openai → legacy/openai}/__init__.py +0 -0
  286. /vanna/{openai → legacy/openai}/openai_embeddings.py +0 -0
  287. /vanna/{opensearch → legacy/opensearch}/__init__.py +0 -0
  288. /vanna/{oracle → legacy/oracle}/__init__.py +0 -0
  289. /vanna/{pgvector → legacy/pgvector}/__init__.py +0 -0
  290. /vanna/{pinecone → legacy/pinecone}/__init__.py +0 -0
  291. /vanna/{pinecone → legacy/pinecone}/pinecone_vector.py +0 -0
  292. /vanna/{qdrant → legacy/qdrant}/__init__.py +0 -0
  293. /vanna/{qianfan → legacy/qianfan}/__init__.py +0 -0
  294. /vanna/{qianwen → legacy/qianwen}/QianwenAI_embeddings.py +0 -0
  295. /vanna/{qianwen → legacy/qianwen}/__init__.py +0 -0
  296. /vanna/{types → legacy/types}/__init__.py +0 -0
  297. /vanna/{vannadb → legacy/vannadb}/__init__.py +0 -0
  298. /vanna/{vllm → legacy/vllm}/__init__.py +0 -0
  299. /vanna/{weaviate → legacy/weaviate}/__init__.py +0 -0
  300. /vanna/{xinference → legacy/xinference}/__init__.py +0 -0
  301. {vanna-0.7.8.dist-info → vanna-2.0.0.dist-info}/WHEEL +0 -0
  302. {vanna-0.7.8.dist-info → vanna-2.0.0.dist-info}/licenses/LICENSE +0 -0
vanna/tools/python.py ADDED
@@ -0,0 +1,222 @@
1
+ """Python-specific tooling built on top of the file system service."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import shlex
6
+ import sys
7
+ from typing import Any, List, Optional, Sequence, Type
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+ from vanna.components import (
12
+ UiComponent,
13
+ CardComponent,
14
+ ComponentType,
15
+ NotificationComponent,
16
+ SimpleTextComponent,
17
+ )
18
+ from vanna.core.tool import Tool, ToolContext, ToolResult
19
+
20
+ from .file_system import CommandResult, FileSystem, LocalFileSystem
21
+
22
+ MAX_OUTPUT_LENGTH = 4000
23
+
24
+
25
+ class RunPythonFileArgs(BaseModel):
26
+ """Arguments required to execute a Python file."""
27
+
28
+ filename: str = Field(
29
+ description="Python file to execute (relative to the workspace root)"
30
+ )
31
+ arguments: Sequence[str] = Field(
32
+ default_factory=list,
33
+ description="Optional arguments to pass to the Python script",
34
+ )
35
+ timeout_seconds: Optional[float] = Field(
36
+ default=None,
37
+ ge=0,
38
+ description="Optional timeout for the command in seconds",
39
+ )
40
+
41
+
42
+ class RunPythonFileTool(Tool[RunPythonFileArgs]):
43
+ """Execute a Python file using the provided file system service."""
44
+
45
+ def __init__(self, file_system: Optional[FileSystem] = None):
46
+ self.file_system = file_system or LocalFileSystem()
47
+
48
+ @property
49
+ def name(self) -> str:
50
+ return "run_python_file"
51
+
52
+ @property
53
+ def description(self) -> str:
54
+ return "Execute a Python file using the workspace interpreter"
55
+
56
+ def get_args_schema(self) -> Type[RunPythonFileArgs]:
57
+ return RunPythonFileArgs
58
+
59
+ async def execute(
60
+ self, context: ToolContext, args: RunPythonFileArgs
61
+ ) -> ToolResult:
62
+ exists = await self.file_system.exists(args.filename, context)
63
+ if not exists:
64
+ message = f"Cannot execute '{args.filename}' because it does not exist."
65
+ return _error_result(message)
66
+
67
+ command_parts = [sys.executable, args.filename]
68
+ command_parts.extend(args.arguments)
69
+ command = _quote_command(command_parts)
70
+
71
+ try:
72
+ result = await self.file_system.run_bash(
73
+ command,
74
+ context,
75
+ timeout=args.timeout_seconds,
76
+ )
77
+ except TimeoutError as exc:
78
+ message = str(exc)
79
+ return _error_result(message)
80
+
81
+ summary = f"Executed python {args.filename} (exit code {result.returncode})."
82
+ success = result.returncode == 0
83
+ return _result_from_command(summary, command, result, success=success)
84
+
85
+
86
+ class PipInstallArgs(BaseModel):
87
+ """Arguments required to run pip install."""
88
+
89
+ packages: List[str] = Field(
90
+ description="Packages (with optional specifiers) to install", min_length=1
91
+ )
92
+ upgrade: bool = Field(
93
+ default=False,
94
+ description="Whether to include --upgrade in the pip invocation",
95
+ )
96
+ extra_args: Sequence[str] = Field(
97
+ default_factory=list,
98
+ description="Additional arguments to pass to pip install",
99
+ )
100
+ timeout_seconds: Optional[float] = Field(
101
+ default=None,
102
+ ge=0,
103
+ description="Optional timeout for the command in seconds",
104
+ )
105
+
106
+
107
+ class PipInstallTool(Tool[PipInstallArgs]):
108
+ """Install Python packages using pip inside the workspace environment."""
109
+
110
+ def __init__(self, file_system: Optional[FileSystem] = None):
111
+ self.file_system = file_system or LocalFileSystem()
112
+
113
+ @property
114
+ def name(self) -> str:
115
+ return "pip_install"
116
+
117
+ @property
118
+ def description(self) -> str:
119
+ return "Install Python packages using pip"
120
+
121
+ def get_args_schema(self) -> Type[PipInstallArgs]:
122
+ return PipInstallArgs
123
+
124
+ async def execute(self, context: ToolContext, args: PipInstallArgs) -> ToolResult:
125
+ command_parts = [sys.executable, "-m", "pip", "install"]
126
+ if args.upgrade:
127
+ command_parts.append("--upgrade")
128
+ command_parts.extend(args.packages)
129
+ command_parts.extend(args.extra_args)
130
+ command = _quote_command(command_parts)
131
+
132
+ try:
133
+ result = await self.file_system.run_bash(
134
+ command,
135
+ context,
136
+ timeout=args.timeout_seconds,
137
+ )
138
+ except TimeoutError as exc:
139
+ return _error_result(str(exc))
140
+
141
+ success = result.returncode == 0
142
+ summary = (
143
+ "pip install completed successfully"
144
+ if success
145
+ else f"pip install failed (exit code {result.returncode})."
146
+ )
147
+
148
+ return _result_from_command(summary, command, result, success=success)
149
+
150
+
151
+ def create_python_tools(file_system: Optional[FileSystem] = None) -> List[Tool[Any]]:
152
+ """Create Python-specific tools backed by a shared file system service."""
153
+
154
+ fs = file_system or LocalFileSystem()
155
+ return [
156
+ RunPythonFileTool(fs),
157
+ PipInstallTool(fs),
158
+ ]
159
+
160
+
161
+ def _quote_command(parts: Sequence[str]) -> str:
162
+ return " ".join(shlex.quote(part) for part in parts)
163
+
164
+
165
+ def _truncate(text: str, limit: int = MAX_OUTPUT_LENGTH) -> str:
166
+ if len(text) <= limit:
167
+ return text
168
+ return f"{text[: limit - 1]}…"
169
+
170
+
171
+ def _result_from_command(
172
+ summary: str,
173
+ command: str,
174
+ result: CommandResult,
175
+ *,
176
+ success: bool = True,
177
+ ) -> ToolResult:
178
+ stdout = result.stdout.strip()
179
+ stderr = result.stderr.strip()
180
+
181
+ blocks: List[str] = [f"$ {command}"]
182
+ if stdout:
183
+ blocks.append("STDOUT:\n" + _truncate(stdout))
184
+ if stderr:
185
+ blocks.append("STDERR:\n" + _truncate(stderr))
186
+ if not stdout and not stderr:
187
+ blocks.append("(no output)")
188
+
189
+ content = "\n\n".join(blocks)
190
+ card_status = "success" if success else "error"
191
+ component = CardComponent(
192
+ type=ComponentType.CARD,
193
+ title="Command Result",
194
+ content=content,
195
+ status=card_status,
196
+ )
197
+
198
+ return ToolResult(
199
+ success=success,
200
+ result_for_llm=f"{summary}\n\n{content}",
201
+ ui_component=UiComponent(
202
+ rich_component=component,
203
+ simple_component=SimpleTextComponent(text=summary),
204
+ ),
205
+ error=None if success else content,
206
+ )
207
+
208
+
209
+ def _error_result(message: str) -> ToolResult:
210
+ return ToolResult(
211
+ success=False,
212
+ result_for_llm=message,
213
+ ui_component=UiComponent(
214
+ rich_component=NotificationComponent(
215
+ type=ComponentType.NOTIFICATION,
216
+ level="error",
217
+ message=message,
218
+ ),
219
+ simple_component=SimpleTextComponent(text=message),
220
+ ),
221
+ error=message,
222
+ )
vanna/tools/run_sql.py ADDED
@@ -0,0 +1,165 @@
1
+ """Generic SQL query execution tool with dependency injection."""
2
+
3
+ from typing import Any, Dict, List, Optional, Type, cast
4
+ import uuid
5
+ from vanna.core.tool import Tool, ToolContext, ToolResult
6
+ from vanna.components import (
7
+ UiComponent,
8
+ DataFrameComponent,
9
+ NotificationComponent,
10
+ ComponentType,
11
+ SimpleTextComponent,
12
+ )
13
+ from vanna.capabilities.sql_runner import SqlRunner, RunSqlToolArgs
14
+ from vanna.capabilities.file_system import FileSystem
15
+ from vanna.integrations.local import LocalFileSystem
16
+
17
+
18
+ class RunSqlTool(Tool[RunSqlToolArgs]):
19
+ """Tool that executes SQL queries using an injected SqlRunner implementation."""
20
+
21
+ def __init__(
22
+ self,
23
+ sql_runner: SqlRunner,
24
+ file_system: Optional[FileSystem] = None,
25
+ custom_tool_name: Optional[str] = None,
26
+ custom_tool_description: Optional[str] = None,
27
+ ):
28
+ """Initialize the tool with a SqlRunner implementation.
29
+
30
+ Args:
31
+ sql_runner: SqlRunner implementation that handles actual query execution
32
+ file_system: FileSystem implementation for saving results (defaults to LocalFileSystem)
33
+ custom_tool_name: Optional custom name for the tool (overrides default "run_sql")
34
+ custom_tool_description: Optional custom description for the tool (overrides default description)
35
+ """
36
+ self.sql_runner = sql_runner
37
+ self.file_system = file_system or LocalFileSystem()
38
+ self._custom_name = custom_tool_name
39
+ self._custom_description = custom_tool_description
40
+
41
+ @property
42
+ def name(self) -> str:
43
+ return self._custom_name if self._custom_name else "run_sql"
44
+
45
+ @property
46
+ def description(self) -> str:
47
+ return (
48
+ self._custom_description
49
+ if self._custom_description
50
+ else "Execute SQL queries against the configured database"
51
+ )
52
+
53
+ def get_args_schema(self) -> Type[RunSqlToolArgs]:
54
+ return RunSqlToolArgs
55
+
56
+ async def execute(self, context: ToolContext, args: RunSqlToolArgs) -> ToolResult:
57
+ """Execute a SQL query using the injected SqlRunner."""
58
+ try:
59
+ # Use the injected SqlRunner to execute the query
60
+ df = await self.sql_runner.run_sql(args, context)
61
+
62
+ # Determine query type
63
+ query_type = args.sql.strip().upper().split()[0]
64
+
65
+ if query_type == "SELECT":
66
+ # Handle SELECT queries with results
67
+ if df.empty:
68
+ result = "Query executed successfully. No rows returned."
69
+ ui_component = UiComponent(
70
+ rich_component=DataFrameComponent(
71
+ rows=[],
72
+ columns=[],
73
+ title="Query Results",
74
+ description="No rows returned",
75
+ ),
76
+ simple_component=SimpleTextComponent(text=result),
77
+ )
78
+ metadata = {
79
+ "row_count": 0,
80
+ "columns": [],
81
+ "query_type": query_type,
82
+ "results": [],
83
+ }
84
+ else:
85
+ # Convert DataFrame to records
86
+ results_data = df.to_dict("records")
87
+ columns = df.columns.tolist()
88
+ row_count = len(df)
89
+
90
+ # Write DataFrame to CSV file for downstream tools
91
+ file_id = str(uuid.uuid4())[:8]
92
+ filename = f"query_results_{file_id}.csv"
93
+ csv_content = df.to_csv(index=False)
94
+ await self.file_system.write_file(
95
+ filename, csv_content, context, overwrite=True
96
+ )
97
+
98
+ # Create result text for LLM with truncated results
99
+ results_preview = csv_content
100
+ if len(results_preview) > 1000:
101
+ results_preview = (
102
+ results_preview[:1000]
103
+ + "\n(Results truncated to 1000 characters. FOR LARGE RESULTS YOU DO NOT NEED TO SUMMARIZE THESE RESULTS OR PROVIDE OBSERVATIONS. THE NEXT STEP SHOULD BE A VISUALIZE_DATA CALL)"
104
+ )
105
+
106
+ result = f"{results_preview}\n\nResults saved to file: {filename}\n\n**IMPORTANT: FOR VISUALIZE_DATA USE FILENAME: {filename}**"
107
+
108
+ # Create DataFrame component for UI
109
+ dataframe_component = DataFrameComponent.from_records(
110
+ records=cast(List[Dict[str, Any]], results_data),
111
+ title="Query Results",
112
+ description=f"SQL query returned {row_count} rows with {len(columns)} columns",
113
+ )
114
+
115
+ ui_component = UiComponent(
116
+ rich_component=dataframe_component,
117
+ simple_component=SimpleTextComponent(text=result),
118
+ )
119
+
120
+ metadata = {
121
+ "row_count": row_count,
122
+ "columns": columns,
123
+ "query_type": query_type,
124
+ "results": results_data,
125
+ "output_file": filename,
126
+ }
127
+ else:
128
+ # For non-SELECT queries (INSERT, UPDATE, DELETE, etc.)
129
+ # The SqlRunner should return a DataFrame with affected row count
130
+ rows_affected = len(df) if not df.empty else 0
131
+ result = (
132
+ f"Query executed successfully. {rows_affected} row(s) affected."
133
+ )
134
+
135
+ metadata = {"rows_affected": rows_affected, "query_type": query_type}
136
+ ui_component = UiComponent(
137
+ rich_component=NotificationComponent(
138
+ type=ComponentType.NOTIFICATION, level="success", message=result
139
+ ),
140
+ simple_component=SimpleTextComponent(text=result),
141
+ )
142
+
143
+ return ToolResult(
144
+ success=True,
145
+ result_for_llm=result,
146
+ ui_component=ui_component,
147
+ metadata=metadata,
148
+ )
149
+
150
+ except Exception as e:
151
+ error_message = f"Error executing query: {str(e)}"
152
+ return ToolResult(
153
+ success=False,
154
+ result_for_llm=error_message,
155
+ ui_component=UiComponent(
156
+ rich_component=NotificationComponent(
157
+ type=ComponentType.NOTIFICATION,
158
+ level="error",
159
+ message=error_message,
160
+ ),
161
+ simple_component=SimpleTextComponent(text=error_message),
162
+ ),
163
+ error=str(e),
164
+ metadata={"error_type": "sql_error"},
165
+ )
@@ -0,0 +1,195 @@
1
+ """Tool for visualizing DataFrame data from CSV files."""
2
+
3
+ from typing import Optional, Type
4
+ import logging
5
+ import pandas as pd
6
+ from pydantic import BaseModel, Field
7
+
8
+ from vanna.core.tool import Tool, ToolContext, ToolResult
9
+ from vanna.components import (
10
+ UiComponent,
11
+ ChartComponent,
12
+ NotificationComponent,
13
+ ComponentType,
14
+ SimpleTextComponent,
15
+ )
16
+
17
+ from .file_system import FileSystem, LocalFileSystem
18
+ from vanna.integrations.plotly import PlotlyChartGenerator
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class VisualizeDataArgs(BaseModel):
24
+ """Arguments for visualize_data tool."""
25
+
26
+ filename: str = Field(description="Name of the CSV file to visualize")
27
+ title: Optional[str] = Field(
28
+ default=None, description="Optional title for the chart"
29
+ )
30
+
31
+
32
+ class VisualizeDataTool(Tool[VisualizeDataArgs]):
33
+ """Tool that reads CSV files and generates visualizations using dependency injection."""
34
+
35
+ def __init__(
36
+ self,
37
+ file_system: Optional[FileSystem] = None,
38
+ plotly_generator: Optional[PlotlyChartGenerator] = None,
39
+ ):
40
+ """Initialize the tool with FileSystem and PlotlyChartGenerator.
41
+
42
+ Args:
43
+ file_system: FileSystem implementation for reading CSV files (defaults to LocalFileSystem)
44
+ plotly_generator: PlotlyChartGenerator for creating Plotly charts (defaults to PlotlyChartGenerator())
45
+ """
46
+ self.file_system = file_system or LocalFileSystem()
47
+ self.plotly_generator = plotly_generator or PlotlyChartGenerator()
48
+
49
+ @property
50
+ def name(self) -> str:
51
+ return "visualize_data"
52
+
53
+ @property
54
+ def description(self) -> str:
55
+ return "Create a visualization from a CSV file. The tool automatically selects an appropriate chart type based on the data."
56
+
57
+ def get_args_schema(self) -> Type[VisualizeDataArgs]:
58
+ return VisualizeDataArgs
59
+
60
+ async def execute(
61
+ self, context: ToolContext, args: VisualizeDataArgs
62
+ ) -> ToolResult:
63
+ """Read CSV file and generate visualization."""
64
+ try:
65
+ logger.info(f"Starting visualization for file: {args.filename}")
66
+
67
+ # Read the CSV file using FileSystem
68
+ csv_content = await self.file_system.read_file(args.filename, context)
69
+ logger.info(f"Read {len(csv_content)} bytes from CSV file")
70
+
71
+ # Parse CSV into DataFrame
72
+ import io
73
+
74
+ df = pd.read_csv(io.StringIO(csv_content))
75
+ logger.info(
76
+ f"Parsed DataFrame with shape {df.shape}, columns: {df.columns.tolist()}, dtypes: {df.dtypes.to_dict()}"
77
+ )
78
+
79
+ # Generate title
80
+ title = args.title or f"Visualization of {args.filename}"
81
+
82
+ # Generate chart using PlotlyChartGenerator
83
+ logger.info("Generating chart...")
84
+ chart_dict = self.plotly_generator.generate_chart(df, title)
85
+ logger.info(
86
+ f"Chart generated, type: {type(chart_dict)}, keys: {list(chart_dict.keys()) if isinstance(chart_dict, dict) else 'N/A'}"
87
+ )
88
+
89
+ # Create result message
90
+ row_count = len(df)
91
+ col_count = len(df.columns)
92
+ result = f"Created visualization from '{args.filename}' ({row_count} rows, {col_count} columns)."
93
+
94
+ # Create ChartComponent
95
+ logger.info("Creating ChartComponent...")
96
+ chart_component = ChartComponent(
97
+ chart_type="plotly",
98
+ data=chart_dict,
99
+ title=title,
100
+ config={
101
+ "data_shape": {"rows": row_count, "columns": col_count},
102
+ "source_file": args.filename,
103
+ },
104
+ )
105
+ logger.info("ChartComponent created successfully")
106
+
107
+ logger.info("Creating ToolResult...")
108
+ tool_result = ToolResult(
109
+ success=True,
110
+ result_for_llm=result,
111
+ ui_component=UiComponent(
112
+ rich_component=chart_component,
113
+ simple_component=SimpleTextComponent(text=result),
114
+ ),
115
+ metadata={
116
+ "filename": args.filename,
117
+ "rows": row_count,
118
+ "columns": col_count,
119
+ "chart": chart_dict,
120
+ },
121
+ )
122
+ logger.info("ToolResult created successfully")
123
+ return tool_result
124
+
125
+ except FileNotFoundError as e:
126
+ logger.error(f"File not found: {args.filename}", exc_info=True)
127
+ error_message = f"File not found: {args.filename}"
128
+ return ToolResult(
129
+ success=False,
130
+ result_for_llm=error_message,
131
+ ui_component=UiComponent(
132
+ rich_component=NotificationComponent(
133
+ type=ComponentType.NOTIFICATION,
134
+ level="error",
135
+ message=error_message,
136
+ ),
137
+ simple_component=SimpleTextComponent(text=error_message),
138
+ ),
139
+ error=str(e),
140
+ metadata={"error_type": "file_not_found"},
141
+ )
142
+ except pd.errors.ParserError as e:
143
+ logger.error(f"CSV parse error for {args.filename}", exc_info=True)
144
+ error_message = f"Failed to parse CSV file '{args.filename}': {str(e)}"
145
+ return ToolResult(
146
+ success=False,
147
+ result_for_llm=error_message,
148
+ ui_component=UiComponent(
149
+ rich_component=NotificationComponent(
150
+ type=ComponentType.NOTIFICATION,
151
+ level="error",
152
+ message=error_message,
153
+ ),
154
+ simple_component=SimpleTextComponent(text=error_message),
155
+ ),
156
+ error=str(e),
157
+ metadata={"error_type": "csv_parse_error"},
158
+ )
159
+ except ValueError as e:
160
+ logger.error(f"Visualization error for {args.filename}", exc_info=True)
161
+ error_message = f"Cannot visualize data: {str(e)}"
162
+ return ToolResult(
163
+ success=False,
164
+ result_for_llm=error_message,
165
+ ui_component=UiComponent(
166
+ rich_component=NotificationComponent(
167
+ type=ComponentType.NOTIFICATION,
168
+ level="error",
169
+ message=error_message,
170
+ ),
171
+ simple_component=SimpleTextComponent(text=error_message),
172
+ ),
173
+ error=str(e),
174
+ metadata={"error_type": "visualization_error"},
175
+ )
176
+ except Exception as e:
177
+ logger.error(
178
+ f"Unexpected error creating visualization for {args.filename}",
179
+ exc_info=True,
180
+ )
181
+ error_message = f"Error creating visualization: {str(e)}"
182
+ return ToolResult(
183
+ success=False,
184
+ result_for_llm=error_message,
185
+ ui_component=UiComponent(
186
+ rich_component=NotificationComponent(
187
+ type=ComponentType.NOTIFICATION,
188
+ level="error",
189
+ message=error_message,
190
+ ),
191
+ simple_component=SimpleTextComponent(text=error_message),
192
+ ),
193
+ error=str(e),
194
+ metadata={"error_type": "general_error"},
195
+ )
File without changes
@@ -0,0 +1,44 @@
1
+ """
2
+ Web components for Vanna Agents.
3
+
4
+ This module provides web components built with Lit that can be embedded
5
+ in web applications to provide rich UI for Vanna agent interactions.
6
+ """
7
+
8
+ import os
9
+ from pathlib import Path
10
+ from typing import Dict
11
+
12
+
13
+ def get_component_files() -> Dict[str, Path]:
14
+ """Get paths to all web component files."""
15
+ component_dir = Path(__file__).parent
16
+ return {
17
+ "js": component_dir / "index.js",
18
+ "css": component_dir / "style.css",
19
+ }
20
+
21
+
22
+ def get_component_html() -> str:
23
+ """Get HTML template for including components."""
24
+ files = get_component_files()
25
+
26
+ html = """
27
+ <!DOCTYPE html>
28
+ <html>
29
+ <head>
30
+ <meta charset="utf-8">
31
+ <meta name="viewport" content="width=device-width, initial-scale=1">
32
+ <title>Vanna AI Chat</title>
33
+ </head>
34
+ <body>
35
+ <vanna-chat title="Vanna AI Assistant"></vanna-chat>
36
+ <script type="module" src="{js_file}"></script>
37
+ </body>
38
+ </html>
39
+ """.format(js_file=files["js"].name)
40
+
41
+ return html
42
+
43
+
44
+ __all__ = ["get_component_files", "get_component_html"]