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
@@ -0,0 +1,305 @@
1
+ """
2
+ Demonstration of the new primitive component system.
3
+
4
+ This example shows how tools compose UI from primitive, domain-agnostic
5
+ components like StatusCardComponent, ProgressDisplayComponent, etc.
6
+
7
+ Usage:
8
+ PYTHONPATH=. python vanna/examples/primitive_components_demo.py
9
+ """
10
+
11
+ import asyncio
12
+ import uuid
13
+ from datetime import datetime
14
+ from typing import AsyncGenerator, Optional
15
+
16
+ from vanna import (
17
+ AgentConfig,
18
+ Agent,
19
+ MemoryConversationStore,
20
+ MockLlmService,
21
+ User,
22
+ )
23
+ from vanna.core.components import UiComponent
24
+ from vanna.core.rich_components import (
25
+ StatusCardComponent,
26
+ ProgressDisplayComponent,
27
+ LogViewerComponent,
28
+ BadgeComponent,
29
+ IconTextComponent,
30
+ RichTextComponent,
31
+ )
32
+
33
+
34
+ class PrimitiveComponentsAgent(Agent):
35
+ """Agent that demonstrates the new primitive component system."""
36
+
37
+ async def send_message(
38
+ self,
39
+ user: User,
40
+ message: str,
41
+ *,
42
+ conversation_id: Optional[str] = None,
43
+ ) -> AsyncGenerator[UiComponent, None]:
44
+ """Send message and demonstrate primitive component composition."""
45
+
46
+ session_id = str(uuid.uuid4())[:8]
47
+
48
+ # Demo 1: Tool execution using primitive components
49
+ yield UiComponent(
50
+ rich_component=RichTextComponent(
51
+ content="## Primitive Components Demo\n\nShowing how tools now compose UI from primitive components:",
52
+ markdown=True,
53
+ )
54
+ )
55
+
56
+ # Status card for overall operation
57
+ operation_status = StatusCardComponent(
58
+ id=f"operation-{session_id}",
59
+ title="Data Analysis Pipeline",
60
+ status="running",
61
+ description="Processing user data through multiple analysis stages",
62
+ icon="⚙️",
63
+ )
64
+ yield UiComponent(rich_component=operation_status)
65
+
66
+ # Progress display for overall progress
67
+ overall_progress = ProgressDisplayComponent(
68
+ id=f"progress-{session_id}",
69
+ label="Overall Progress",
70
+ value=0.0,
71
+ description="Starting analysis...",
72
+ animated=True,
73
+ )
74
+ yield UiComponent(rich_component=overall_progress)
75
+
76
+ # Log viewer for detailed output
77
+ log_viewer = LogViewerComponent(
78
+ id=f"logs-{session_id}",
79
+ title="Analysis Log",
80
+ entries=[],
81
+ show_timestamps=True,
82
+ auto_scroll=True,
83
+ )
84
+ yield UiComponent(rich_component=log_viewer)
85
+
86
+ # Simulate analysis stages
87
+ stages = [
88
+ ("Data Loading", "📊", 0.2),
89
+ ("Data Validation", "✅", 0.4),
90
+ ("Statistical Analysis", "🧮", 0.6),
91
+ ("Report Generation", "📄", 0.8),
92
+ ("Finalization", "🎯", 1.0),
93
+ ]
94
+
95
+ for i, (stage_name, stage_icon, progress_value) in enumerate(stages):
96
+ await asyncio.sleep(0.8)
97
+
98
+ # Update overall status
99
+ status = "success" if progress_value == 1.0 else "running"
100
+ yield UiComponent(
101
+ rich_component=operation_status.set_status(
102
+ status, f"Executing: {stage_name}"
103
+ )
104
+ )
105
+
106
+ # Update progress
107
+ yield UiComponent(
108
+ rich_component=overall_progress.update_progress(
109
+ progress_value, f"Executing {stage_name}..."
110
+ )
111
+ )
112
+
113
+ # Add log entry
114
+ yield UiComponent(
115
+ rich_component=log_viewer.add_entry(f"Starting {stage_name}", "info")
116
+ )
117
+
118
+ # Create a status card for this specific stage
119
+ stage_status = StatusCardComponent(
120
+ id=f"stage-{i}-{session_id}",
121
+ title=stage_name,
122
+ status="running" if progress_value < 1.0 else "success",
123
+ description=f"Processing stage {i + 1} of {len(stages)}",
124
+ icon=stage_icon,
125
+ )
126
+ yield UiComponent(rich_component=stage_status)
127
+
128
+ await asyncio.sleep(0.5)
129
+
130
+ # Complete the stage
131
+ final_stage_status = "success" if progress_value < 1.0 else "completed"
132
+ yield UiComponent(
133
+ rich_component=stage_status.set_status(
134
+ final_stage_status, f"{stage_name} completed successfully"
135
+ )
136
+ )
137
+ yield UiComponent(
138
+ rich_component=log_viewer.add_entry(f"Completed {stage_name}", "info")
139
+ )
140
+
141
+ # Demo 2: Badge and IconText primitives
142
+ yield UiComponent(
143
+ rich_component=RichTextComponent(
144
+ content="\n### Primitive Component Examples\n\nShowing individual primitive components:",
145
+ markdown=True,
146
+ )
147
+ )
148
+
149
+ # Various badge examples
150
+ badges = [
151
+ BadgeComponent(text="Processing", variant="primary", size="small"),
152
+ BadgeComponent(text="Complete", variant="success", size="medium"),
153
+ BadgeComponent(text="Warning", variant="warning", size="large", icon="⚠️"),
154
+ BadgeComponent(text="Error", variant="error", size="medium", icon="❌"),
155
+ ]
156
+
157
+ for badge in badges:
158
+ yield UiComponent(rich_component=badge)
159
+
160
+ # IconText examples
161
+ icon_texts = [
162
+ IconTextComponent(
163
+ icon="📊",
164
+ text="Data Analysis Complete",
165
+ variant="primary",
166
+ size="large",
167
+ ),
168
+ IconTextComponent(
169
+ icon="✅", text="All tests passed", variant="default", size="medium"
170
+ ),
171
+ IconTextComponent(
172
+ icon="⏱️",
173
+ text="Processing time: 2.3s",
174
+ variant="secondary",
175
+ size="small",
176
+ ),
177
+ ]
178
+
179
+ for icon_text in icon_texts:
180
+ yield UiComponent(rich_component=icon_text)
181
+
182
+ # Demo 3: Comparison with old approach
183
+ yield UiComponent(
184
+ rich_component=RichTextComponent(
185
+ content=f"""
186
+ ## Key Benefits of Primitive Components
187
+
188
+ **Primitive Component Approach:**
189
+ ```python
190
+ # Tool composes UI from primitives
191
+ status_card = StatusCardComponent(
192
+ title="Data Analysis",
193
+ status="running", # Pure UI state
194
+ icon="📊"
195
+ )
196
+ progress = ProgressDisplayComponent(
197
+ label="Analysis Progress",
198
+ value=0.5
199
+ )
200
+ logs = LogViewerComponent(
201
+ title="Analysis Log",
202
+ entries=log_entries
203
+ )
204
+ ```
205
+
206
+ ### Benefits:
207
+ - **Separation of Concerns**: UI components are purely presentational
208
+ - **Reusability**: Status cards work for any process, not just tools
209
+ - **Composability**: Tools build exactly the UI they need
210
+ - **Maintainability**: Changes to business logic don't affect UI components
211
+ - **Extensibility**: New tools don't require new component types
212
+
213
+ Your message was: "{message}"
214
+ """,
215
+ markdown=True,
216
+ )
217
+ )
218
+
219
+
220
+ def create_primitive_demo_agent() -> PrimitiveComponentsAgent:
221
+ """Create a primitive components demo agent.
222
+
223
+ Returns:
224
+ Configured PrimitiveComponentsAgent instance
225
+ """
226
+ llm_service = MockLlmService(response_content="Primitive components demo response")
227
+
228
+ return PrimitiveComponentsAgent(
229
+ llm_service=llm_service,
230
+ config=AgentConfig(
231
+ stream_responses=True,
232
+ include_thinking_indicators=False,
233
+ ),
234
+ )
235
+
236
+
237
+ async def main() -> None:
238
+ """Run the primitive components demo."""
239
+
240
+ # Create agent
241
+ agent = create_primitive_demo_agent()
242
+
243
+ # Create a test user
244
+ user = User(
245
+ id="user123", username="demo_user", email="demo@example.com", permissions=[]
246
+ )
247
+
248
+ # Start a conversation
249
+ conversation_id = "primitive_demo_123"
250
+ user_message = "Show me how the new primitive component system works!"
251
+
252
+ print(f"User: {user_message}")
253
+ print("Agent response (primitive components):")
254
+ print("=" * 60)
255
+
256
+ # Send message and display components
257
+ component_count = 0
258
+ async for component in agent.send_message(
259
+ user=user, message=user_message, conversation_id=conversation_id
260
+ ):
261
+ component_count += 1
262
+ component_type = getattr(component, "type", component.__class__.__name__)
263
+ component_id = getattr(component, "id", "N/A")
264
+
265
+ print(
266
+ f"[{component_count:2d}] {component_type.value if hasattr(component_type, 'value') else component_type} (id: {component_id[:12] if len(str(component_id)) > 12 else component_id})"
267
+ )
268
+
269
+ rich_comp = component.rich_component
270
+
271
+ # Show component details
272
+ if hasattr(rich_comp, "title"):
273
+ print(f" Title: {rich_comp.title}")
274
+ if hasattr(rich_comp, "status"):
275
+ print(f" Status: {rich_comp.status}")
276
+ if hasattr(rich_comp, "description") and rich_comp.description:
277
+ desc = (
278
+ rich_comp.description[:60] + "..."
279
+ if len(rich_comp.description) > 60
280
+ else rich_comp.description
281
+ )
282
+ print(f" Description: {desc}")
283
+ if (
284
+ hasattr(rich_comp, "value")
285
+ and hasattr(rich_comp.type, "value")
286
+ and rich_comp.type.value == "progress_display"
287
+ ):
288
+ print(f" Progress: {rich_comp.value:.1%}")
289
+
290
+ print()
291
+
292
+ print("=" * 60)
293
+ print(f"Total components emitted: {component_count}")
294
+ print("\nThis demonstrates how tools can now compose rich UIs")
295
+ print("from primitive, reusable components without semantic coupling!")
296
+
297
+
298
+ def run_interactive() -> None:
299
+ """Entry point for interactive usage."""
300
+ print("Starting Primitive Components Demo...")
301
+ asyncio.run(main())
302
+
303
+
304
+ if __name__ == "__main__":
305
+ run_interactive()
@@ -0,0 +1,139 @@
1
+ """
2
+ Example demonstrating lifecycle hooks for user quota management.
3
+
4
+ This example shows how to use lifecycle hooks to add custom functionality
5
+ like quota management without creating custom agent runner subclasses.
6
+ """
7
+
8
+ from typing import Any, Dict, Optional
9
+ from vanna.core import Agent, LifecycleHook, User
10
+ from vanna.core.errors import AgentError
11
+
12
+
13
+ class QuotaExceededError(AgentError):
14
+ """Raised when a user exceeds their message quota."""
15
+
16
+ pass
17
+
18
+
19
+ class QuotaCheckHook(LifecycleHook):
20
+ """Lifecycle hook that enforces user-based message quotas."""
21
+
22
+ def __init__(self, default_quota: int = 10) -> None:
23
+ """Initialize quota hook.
24
+
25
+ Args:
26
+ default_quota: Default quota per user if not specifically set
27
+ """
28
+ self._user_quotas: Dict[str, int] = {}
29
+ self._user_usage: Dict[str, int] = {}
30
+ self._default_quota = default_quota
31
+
32
+ def set_user_quota(self, user_id: str, quota: int) -> None:
33
+ """Set a specific quota for a user."""
34
+ self._user_quotas[user_id] = quota
35
+
36
+ def get_user_quota(self, user_id: str) -> int:
37
+ """Get the quota for a user."""
38
+ return self._user_quotas.get(user_id, self._default_quota)
39
+
40
+ def get_user_usage(self, user_id: str) -> int:
41
+ """Get current usage count for a user."""
42
+ return self._user_usage.get(user_id, 0)
43
+
44
+ def get_user_remaining(self, user_id: str) -> int:
45
+ """Get remaining messages for a user."""
46
+ return self.get_user_quota(user_id) - self.get_user_usage(user_id)
47
+
48
+ def reset_user_usage(self, user_id: str) -> None:
49
+ """Reset usage count for a user."""
50
+ self._user_usage[user_id] = 0
51
+
52
+ async def before_message(self, user: User, message: str) -> Optional[str]:
53
+ """Check quota before processing message.
54
+
55
+ Raises:
56
+ QuotaExceededError: If user has exceeded their quota
57
+ """
58
+ usage = self.get_user_usage(user.id)
59
+ quota = self.get_user_quota(user.id)
60
+
61
+ if usage >= quota:
62
+ raise QuotaExceededError(
63
+ f"User {user.username} has exceeded their quota of {quota} messages. "
64
+ f"Current usage: {usage}"
65
+ )
66
+
67
+ # Increment usage count
68
+ current_usage = self._user_usage.get(user.id, 0)
69
+ self._user_usage[user.id] = current_usage + 1
70
+
71
+ # Don't modify the message
72
+ return None
73
+
74
+
75
+ class LoggingHook(LifecycleHook):
76
+ """Example logging hook for demonstration."""
77
+
78
+ async def before_message(self, user: User, message: str) -> Optional[str]:
79
+ """Log incoming messages."""
80
+ print(f"[LOG] User {user.username} ({user.id}) sent message: {message[:50]}...")
81
+ return None
82
+
83
+ async def after_message(self, result: Any) -> None:
84
+ """Log message completion."""
85
+ print(f"[LOG] Message processing completed")
86
+
87
+
88
+ async def run_example() -> None:
89
+ """
90
+ Example showing how to use lifecycle hooks with Agent.
91
+
92
+ Instead of creating a custom subclass, we compose
93
+ the behavior using lifecycle hooks.
94
+ """
95
+ from vanna.core.registry import ToolRegistry
96
+ from vanna.integrations.anthropic import AnthropicLlmService
97
+ from vanna.integrations.local import MemoryConversationStore
98
+
99
+ # Create quota hook
100
+ quota_hook = QuotaCheckHook(default_quota=10)
101
+ quota_hook.set_user_quota("user123", 5) # Set custom quota for specific user
102
+
103
+ # Create logging hook
104
+ logging_hook = LoggingHook()
105
+
106
+ # Create agent with multiple hooks
107
+ agent = Agent(
108
+ llm_service=AnthropicLlmService(api_key="your-api-key"),
109
+ tool_registry=ToolRegistry(),
110
+ conversation_store=MemoryConversationStore(),
111
+ lifecycle_hooks=[
112
+ logging_hook, # Logs will happen first
113
+ quota_hook, # Then quota check
114
+ ],
115
+ )
116
+
117
+ # Create a test user
118
+ user = User(
119
+ id="user123", username="test_user", email="test@example.com", permissions=[]
120
+ )
121
+
122
+ # Send messages - will track quota
123
+ try:
124
+ async for component in agent.send_message(user=user, message="Hello, agent!"):
125
+ # Process UI components
126
+ pass
127
+
128
+ # Check remaining quota
129
+ remaining = quota_hook.get_user_remaining(user.id)
130
+ print(f"Remaining messages: {remaining}/{quota_hook.get_user_quota(user.id)}")
131
+
132
+ except QuotaExceededError as e:
133
+ print(f"Quota exceeded: {e}")
134
+
135
+
136
+ if __name__ == "__main__":
137
+ import asyncio
138
+
139
+ asyncio.run(run_example())
@@ -0,0 +1,251 @@
1
+ """
2
+ Example demonstrating SQL query execution with automatic visualization.
3
+
4
+ This example shows the integration of RunSqlTool and VisualizeDataTool,
5
+ demonstrating how SQL results are saved to CSV files and can be visualized
6
+ using the visualization tool with dependency injection.
7
+
8
+ Usage:
9
+ PYTHONPATH=. python vanna/examples/visualization_example.py
10
+ """
11
+
12
+ import asyncio
13
+ import os
14
+ import sys
15
+ import uuid
16
+ from typing import AsyncGenerator, List, Optional
17
+
18
+ from vanna import (
19
+ AgentConfig,
20
+ Agent,
21
+ ToolRegistry,
22
+ User,
23
+ )
24
+ from vanna.core import LlmService
25
+ from vanna.core import (
26
+ LlmRequest,
27
+ LlmResponse,
28
+ LlmStreamChunk,
29
+ ToolCall,
30
+ ToolSchema,
31
+ )
32
+ from vanna.integrations.sqlite import SqliteRunner
33
+ from vanna.tools import (
34
+ RunSqlTool,
35
+ VisualizeDataTool,
36
+ LocalFileSystem,
37
+ )
38
+
39
+
40
+ class VisualizationDemoLlmService(LlmService):
41
+ """Mock LLM that demonstrates SQL query and visualization workflow."""
42
+
43
+ def __init__(self) -> None:
44
+ self.step = 0
45
+ self.csv_filename: Optional[str] = None
46
+
47
+ async def send_request(self, request: LlmRequest) -> LlmResponse:
48
+ """Handle non-streaming requests."""
49
+ await asyncio.sleep(0.1)
50
+ return self._build_response(request)
51
+
52
+ async def stream_request(
53
+ self, request: LlmRequest
54
+ ) -> AsyncGenerator[LlmStreamChunk, None]:
55
+ """Handle streaming requests."""
56
+ await asyncio.sleep(0.1)
57
+ response = self._build_response(request)
58
+
59
+ if response.tool_calls:
60
+ yield LlmStreamChunk(tool_calls=response.tool_calls)
61
+ if response.content:
62
+ yield LlmStreamChunk(
63
+ content=response.content, finish_reason=response.finish_reason
64
+ )
65
+ else:
66
+ yield LlmStreamChunk(finish_reason=response.finish_reason)
67
+
68
+ async def validate_tools(self, tools: List[ToolSchema]) -> List[str]:
69
+ """Validate tools - no errors."""
70
+ return []
71
+
72
+ def _build_response(self, request: LlmRequest) -> LlmResponse:
73
+ """Build response based on conversation state."""
74
+ last_message = request.messages[-1] if request.messages else None
75
+
76
+ # If we got a tool result, process it
77
+ if last_message and last_message.role == "tool":
78
+ tool_result = last_message.content or ""
79
+
80
+ # Check if this was a SQL query result with a CSV file
81
+ if "Results saved to" in tool_result and ".csv" in tool_result:
82
+ # Extract filename from result
83
+ import re
84
+
85
+ match = re.search(r"'([^']*\.csv)'", tool_result)
86
+ if match:
87
+ self.csv_filename = match.group(1)
88
+ # Now visualize the data
89
+ return LlmResponse(
90
+ content=f"Great! I've saved the query results. Now let me create a visualization of the data.",
91
+ tool_calls=[
92
+ ToolCall(
93
+ id=f"call_{uuid.uuid4().hex[:8]}",
94
+ name="visualize_data",
95
+ arguments={"filename": self.csv_filename},
96
+ )
97
+ ],
98
+ finish_reason="tool_calls",
99
+ )
100
+
101
+ # If this was a visualization result, acknowledge it
102
+ if "Created visualization" in tool_result:
103
+ return LlmResponse(
104
+ content=f"Perfect! I've created a visualization of the data. {tool_result}",
105
+ finish_reason="stop",
106
+ )
107
+
108
+ # Default acknowledgment
109
+ return LlmResponse(
110
+ content=f"I've completed the operation. {tool_result}",
111
+ finish_reason="stop",
112
+ )
113
+
114
+ # Initial request - run SQL query
115
+ if self.step == 0:
116
+ self.step += 1
117
+ return LlmResponse(
118
+ content="I'll query the database for you and then create a visualization.",
119
+ tool_calls=[
120
+ ToolCall(
121
+ id=f"call_{uuid.uuid4().hex[:8]}",
122
+ name="run_sql",
123
+ arguments={
124
+ "sql": "SELECT Name, Milliseconds, Bytes FROM Track LIMIT 20"
125
+ },
126
+ )
127
+ ],
128
+ finish_reason="tool_calls",
129
+ )
130
+
131
+ # Default response
132
+ return LlmResponse(
133
+ content="I can help you query databases and visualize the results.",
134
+ finish_reason="stop",
135
+ )
136
+
137
+
138
+ def create_demo_agent() -> Agent:
139
+ """
140
+ Create a demo agent with SQL and visualization tools.
141
+
142
+ This function is called by the vanna server framework.
143
+
144
+ Returns:
145
+ Configured Agent with SQL and visualization tools
146
+ """
147
+ # Check for Chinook database
148
+ database_path = os.path.join(
149
+ os.path.dirname(__file__), "..", "..", "Chinook.sqlite"
150
+ )
151
+ database_path = os.path.abspath(database_path)
152
+
153
+ if not os.path.exists(database_path):
154
+ raise FileNotFoundError(
155
+ f"Chinook database not found at {database_path}. "
156
+ "Please download it from https://vanna.ai/Chinook.sqlite"
157
+ )
158
+
159
+ # Create shared FileSystem for both tools
160
+ file_system = LocalFileSystem(working_directory="./data_storage")
161
+
162
+ # Create SQL tool with FileSystem
163
+ sqlite_runner = SqliteRunner(database_path=database_path)
164
+ sql_tool = RunSqlTool(sql_runner=sqlite_runner, file_system=file_system)
165
+
166
+ # Create visualization tool with same FileSystem
167
+ viz_tool = VisualizeDataTool(file_system=file_system)
168
+
169
+ # Create tool registry
170
+ tool_registry = ToolRegistry()
171
+ tool_registry.register(sql_tool)
172
+ tool_registry.register(viz_tool)
173
+
174
+ # Create LLM service
175
+ llm_service = VisualizationDemoLlmService()
176
+
177
+ # Create agent with streaming enabled for web interface
178
+ return Agent(
179
+ llm_service=llm_service,
180
+ tool_registry=tool_registry,
181
+ config=AgentConfig(
182
+ stream_responses=True,
183
+ include_thinking_indicators=False,
184
+ ),
185
+ )
186
+
187
+
188
+ async def main() -> None:
189
+ """Demonstrate SQL query execution with automatic visualization."""
190
+ print("🎨 SQL + Visualization Demo")
191
+ print("=" * 60)
192
+ print("This example demonstrates:")
193
+ print("1. Running SQL queries that save results to CSV files")
194
+ print("2. Automatically visualizing the CSV data")
195
+ print("3. User isolation for file storage")
196
+ print("=" * 60)
197
+ print()
198
+
199
+ # Create agent using factory function
200
+ agent = create_demo_agent()
201
+
202
+ # Create test user
203
+ user = User(id="demo-user", username="demo")
204
+
205
+ # Show available tools
206
+ tools = await agent.get_available_tools(user)
207
+ print(f"Available tools: {[tool.name for tool in tools]}")
208
+ print()
209
+
210
+ # Run conversation
211
+ conversation_id = "viz-demo"
212
+
213
+ print("User: Show me some track data and visualize it")
214
+ print()
215
+
216
+ async for component in agent.send_message(
217
+ user=user,
218
+ message="Show me some track data and visualize it",
219
+ conversation_id=conversation_id,
220
+ ):
221
+ if (
222
+ component.simple_component
223
+ and hasattr(component.simple_component, "text")
224
+ and component.simple_component.text
225
+ ):
226
+ print(f"Agent: {component.simple_component.text}")
227
+ elif component.simple_component and hasattr(component.simple_component, "text"):
228
+ print(f"Agent: {component.simple_component.text}")
229
+ elif hasattr(component.rich_component, "content"):
230
+ if isinstance(component.rich_component.content, dict):
231
+ # This is the chart
232
+ print(
233
+ f"Agent: [Chart Generated - Plotly figure with {len(str(component.rich_component.content))} chars]"
234
+ )
235
+ else:
236
+ print(f"Agent: {component.rich_component.content}")
237
+
238
+ print()
239
+ print("=" * 60)
240
+ print("Demo complete!")
241
+ print()
242
+ print("Key features demonstrated:")
243
+ print("✅ SQL queries save results to user-isolated CSV files")
244
+ print("✅ Visualization tool reads CSV files using FileSystem")
245
+ print("✅ Automatic chart type selection based on data shape")
246
+ print("✅ Dependency injection allows customization")
247
+ print()
248
+
249
+
250
+ if __name__ == "__main__":
251
+ asyncio.run(main())