vanna 0.7.9__py3-none-any.whl → 2.0.0rc1__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 +439 -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 +224 -217
  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.0rc1.dist-info/METADATA +868 -0
  251. vanna-2.0.0rc1.dist-info/RECORD +289 -0
  252. vanna-2.0.0rc1.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.9.dist-info/METADATA +0 -408
  265. vanna-0.7.9.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.9.dist-info → vanna-2.0.0rc1.dist-info}/WHEEL +0 -0
  302. {vanna-0.7.9.dist-info → vanna-2.0.0rc1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,262 @@
1
+ """
2
+ Comprehensive example demonstrating all extensibility interfaces.
3
+
4
+ This example shows how to use:
5
+ - LlmMiddleware for caching
6
+ - ErrorRecoveryStrategy for retry logic
7
+ - ToolContextEnricher for adding user preferences
8
+ - ConversationFilter for context window management
9
+ - ObservabilityProvider for monitoring
10
+ """
11
+
12
+ import asyncio
13
+ import time
14
+ from typing import Any, Dict, List, Optional
15
+
16
+ from vanna.core import (
17
+ Agent,
18
+ LlmMiddleware,
19
+ ErrorRecoveryStrategy,
20
+ ToolContextEnricher,
21
+ ConversationFilter,
22
+ ObservabilityProvider,
23
+ User,
24
+ ToolContext,
25
+ Conversation,
26
+ Message,
27
+ LlmRequest,
28
+ LlmResponse,
29
+ Span,
30
+ Metric,
31
+ )
32
+ from vanna.core.recovery import RecoveryAction, RecoveryActionType
33
+ from vanna.core.registry import ToolRegistry
34
+
35
+
36
+ # 1. LlmMiddleware Example: Simple Caching
37
+ class CachingMiddleware(LlmMiddleware):
38
+ """Cache LLM responses to reduce costs and latency."""
39
+
40
+ def __init__(self) -> None:
41
+ self.cache: Dict[str, LlmResponse] = {}
42
+ self.hits = 0
43
+ self.misses = 0
44
+
45
+ def _compute_cache_key(self, request: LlmRequest) -> str:
46
+ """Create cache key from request."""
47
+ messages_str = str([(m.role, m.content) for m in request.messages])
48
+ return f"{messages_str}:{request.temperature}"
49
+
50
+ async def before_llm_request(self, request: LlmRequest) -> LlmRequest:
51
+ """Check cache before sending request."""
52
+ cache_key = self._compute_cache_key(request)
53
+ if cache_key in self.cache:
54
+ self.hits += 1
55
+ print(f"[CACHE HIT] Cache stats: {self.hits} hits, {self.misses} misses")
56
+ return request
57
+
58
+ async def after_llm_response(
59
+ self, request: LlmRequest, response: LlmResponse
60
+ ) -> LlmResponse:
61
+ """Cache the response."""
62
+ cache_key = self._compute_cache_key(request)
63
+ if cache_key not in self.cache:
64
+ self.cache[cache_key] = response
65
+ self.misses += 1
66
+ print(f"[CACHE MISS] Caching response")
67
+ return response
68
+
69
+
70
+ # 2. ErrorRecoveryStrategy Example: Exponential Backoff
71
+ class ExponentialBackoffStrategy(ErrorRecoveryStrategy):
72
+ """Retry failed operations with exponential backoff."""
73
+
74
+ def __init__(self, max_retries: int = 3) -> None:
75
+ self.max_retries = max_retries
76
+
77
+ async def handle_tool_error(
78
+ self, error: Exception, context: ToolContext, attempt: int = 1
79
+ ) -> RecoveryAction:
80
+ """Retry tool errors with exponential backoff."""
81
+ if attempt < self.max_retries:
82
+ delay_ms = (2 ** (attempt - 1)) * 1000
83
+ print(
84
+ f"[RETRY] Tool failed, retrying in {delay_ms}ms (attempt {attempt}/{self.max_retries})"
85
+ )
86
+ return RecoveryAction(
87
+ action=RecoveryActionType.RETRY,
88
+ retry_delay_ms=delay_ms,
89
+ message=f"Retrying after {delay_ms}ms",
90
+ )
91
+
92
+ print(f"[FAIL] Max retries exceeded for tool error: {error}")
93
+ return RecoveryAction(
94
+ action=RecoveryActionType.FAIL,
95
+ message=f"Tool error after {self.max_retries} attempts: {str(error)}",
96
+ )
97
+
98
+ async def handle_llm_error(
99
+ self, error: Exception, request: LlmRequest, attempt: int = 1
100
+ ) -> RecoveryAction:
101
+ """Retry LLM errors with backoff."""
102
+ if attempt < self.max_retries:
103
+ delay_ms = (2 ** (attempt - 1)) * 1000
104
+ print(
105
+ f"[RETRY] LLM failed, retrying in {delay_ms}ms (attempt {attempt}/{self.max_retries})"
106
+ )
107
+ return RecoveryAction(
108
+ action=RecoveryActionType.RETRY,
109
+ retry_delay_ms=delay_ms,
110
+ message=f"Retrying LLM after {delay_ms}ms",
111
+ )
112
+
113
+ print(f"[FAIL] Max retries exceeded for LLM error: {error}")
114
+ return RecoveryAction(
115
+ action=RecoveryActionType.FAIL,
116
+ message=f"LLM error after {self.max_retries} attempts: {str(error)}",
117
+ )
118
+
119
+
120
+ # 3. ToolContextEnricher Example: Add User Preferences
121
+ class UserPreferencesEnricher(ToolContextEnricher):
122
+ """Enrich context with user preferences."""
123
+
124
+ def __init__(self) -> None:
125
+ # Mock user preferences database
126
+ self.preferences: Dict[str, Dict[str, Any]] = {
127
+ "user123": {
128
+ "timezone": "America/New_York",
129
+ "language": "en",
130
+ "theme": "dark",
131
+ }
132
+ }
133
+
134
+ async def enrich_context(self, context: ToolContext) -> ToolContext:
135
+ """Add user preferences to context."""
136
+ prefs = self.preferences.get(context.user.id, {})
137
+ context.metadata["user_preferences"] = prefs
138
+ context.metadata["timezone"] = prefs.get("timezone", "UTC")
139
+ print(f"[ENRICH] Added preferences for user {context.user.id}: {prefs}")
140
+ return context
141
+
142
+
143
+ # 4. ConversationFilter Example: Context Window Management
144
+ class ContextWindowFilter(ConversationFilter):
145
+ """Limit conversation to fit within context window."""
146
+
147
+ def __init__(self, max_messages: int = 20) -> None:
148
+ self.max_messages = max_messages
149
+
150
+ async def filter_messages(self, messages: List[Message]) -> List[Message]:
151
+ """Keep only recent messages within limit."""
152
+ if len(messages) <= self.max_messages:
153
+ return messages
154
+
155
+ # Keep system messages and recent messages
156
+ system_messages = [m for m in messages if m.role == "system"]
157
+ other_messages = [m for m in messages if m.role != "system"]
158
+
159
+ # Take the most recent messages
160
+ recent_messages = other_messages[-self.max_messages :]
161
+ filtered = system_messages + recent_messages
162
+
163
+ print(f"[FILTER] Reduced {len(messages)} messages to {len(filtered)}")
164
+ return filtered
165
+
166
+
167
+ # 5. ObservabilityProvider Example: Simple Logging
168
+ class LoggingObservabilityProvider(ObservabilityProvider):
169
+ """Log metrics and spans for monitoring."""
170
+
171
+ def __init__(self) -> None:
172
+ self.metrics: List[Metric] = []
173
+ self.spans: List[Span] = []
174
+
175
+ async def record_metric(
176
+ self,
177
+ name: str,
178
+ value: float,
179
+ unit: str = "",
180
+ tags: Optional[Dict[str, str]] = None,
181
+ ) -> None:
182
+ """Record and log a metric."""
183
+ metric = Metric(name=name, value=value, unit=unit, tags=tags or {})
184
+ self.metrics.append(metric)
185
+ tags_str = ", ".join(f"{k}={v}" for k, v in (tags or {}).items())
186
+ print(f"[METRIC] {name}: {value}{unit} {tags_str}")
187
+
188
+ async def create_span(
189
+ self, name: str, attributes: Optional[Dict[str, Any]] = None
190
+ ) -> Span:
191
+ """Create a span for tracing."""
192
+ span = Span(name=name, attributes=attributes or {})
193
+ print(f"[SPAN START] {name}")
194
+ return span
195
+
196
+ async def end_span(self, span: Span) -> None:
197
+ """End and record a span."""
198
+ span.end()
199
+ self.spans.append(span)
200
+ duration = span.duration_ms() or 0
201
+ print(f"[SPAN END] {span.name}: {duration:.2f}ms")
202
+
203
+
204
+ async def run_example() -> None:
205
+ """
206
+ Example showing all extensibility interfaces working together.
207
+ """
208
+ from vanna.integrations.anthropic import AnthropicLlmService
209
+
210
+ # Create all extensibility components
211
+ caching_middleware = CachingMiddleware()
212
+ retry_strategy = ExponentialBackoffStrategy(max_retries=3)
213
+ preferences_enricher = UserPreferencesEnricher()
214
+ context_filter = ContextWindowFilter(max_messages=20)
215
+ observability = LoggingObservabilityProvider()
216
+
217
+ # Mock conversation store
218
+ class MockStore:
219
+ async def get_conversation(self, cid: str, uid: str) -> Optional[Conversation]:
220
+ return None
221
+
222
+ async def create_conversation(
223
+ self, cid: str, uid: str, title: str
224
+ ) -> Conversation:
225
+ return Conversation(
226
+ id=cid, user_id=uid, messages=[Message(role="user", content=title)]
227
+ )
228
+
229
+ async def update_conversation(self, conv: Conversation) -> None:
230
+ pass
231
+
232
+ async def delete_conversation(self, cid: str, uid: str) -> bool:
233
+ return False
234
+
235
+ async def list_conversations(
236
+ self, uid: str, limit: int = 50, offset: int = 0
237
+ ) -> List[Conversation]:
238
+ return []
239
+
240
+ # Create agent with all extensibility components
241
+ agent = Agent(
242
+ llm_service=AnthropicLlmService(api_key="test-key"),
243
+ tool_registry=ToolRegistry(),
244
+ conversation_store=MockStore(), # type: ignore
245
+ llm_middlewares=[caching_middleware],
246
+ error_recovery_strategy=retry_strategy,
247
+ context_enrichers=[preferences_enricher],
248
+ conversation_filters=[context_filter],
249
+ observability_provider=observability,
250
+ )
251
+
252
+ print("āœ“ Agent created with all extensibility components:")
253
+ print(f" - LLM Middleware: {len(agent.llm_middlewares)} middlewares")
254
+ print(f" - Error Recovery: {type(agent.error_recovery_strategy).__name__}")
255
+ print(f" - Context Enrichers: {len(agent.context_enrichers)} enrichers")
256
+ print(f" - Conversation Filters: {len(agent.conversation_filters)} filters")
257
+ print(f" - Observability: {type(agent.observability_provider).__name__}")
258
+ print("\nšŸŽ‰ All extensibility interfaces integrated successfully!")
259
+
260
+
261
+ if __name__ == "__main__":
262
+ asyncio.run(run_example())
@@ -0,0 +1,67 @@
1
+ """Minimal Claude + SQLite example ready for FastAPI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+
8
+ from vanna import AgentConfig, Agent
9
+ from vanna.core.registry import ToolRegistry
10
+ from vanna.integrations.anthropic import AnthropicLlmService
11
+ from vanna.integrations.sqlite import SqliteRunner
12
+ from vanna.integrations.local import LocalFileSystem
13
+ from vanna.tools import (
14
+ RunSqlTool,
15
+ # Visualization
16
+ VisualizeDataTool,
17
+ # Python execution
18
+ RunPythonFileTool,
19
+ PipInstallTool,
20
+ # File system (for coding agents)
21
+ SearchFilesTool,
22
+ ListFilesTool,
23
+ ReadFileTool,
24
+ WriteFileTool,
25
+ )
26
+
27
+ _DB = Path(__file__).resolve().parents[2] / "Chinook.sqlite"
28
+
29
+
30
+ def create_demo_agent() -> Agent:
31
+ # Load environment variables from .env file
32
+ from dotenv import load_dotenv
33
+
34
+ load_dotenv()
35
+
36
+ llm = AnthropicLlmService(model=os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-5"))
37
+
38
+ # Shared file system for all tools
39
+ file_system = LocalFileSystem("./claude_data")
40
+
41
+ tools = ToolRegistry()
42
+
43
+ # 1. Basic SQL agent - query databases
44
+ tools.register(
45
+ RunSqlTool(
46
+ sql_runner=SqliteRunner(database_path=str(_DB)),
47
+ file_system=file_system,
48
+ )
49
+ )
50
+
51
+ # 2. Add visualization - create charts from data
52
+ tools.register(VisualizeDataTool(file_system=file_system))
53
+
54
+ # 3. Add Python execution - build dashboards with artifacts
55
+ # tools.register(RunPythonFileTool(file_system=file_system))
56
+ # tools.register(PipInstallTool(file_system=file_system))
57
+
58
+ # 4. Full coding agent - read, write, search files
59
+ # tools.register(SearchFilesTool(file_system=file_system))
60
+ # tools.register(ListFilesTool(file_system=file_system))
61
+ # tools.register(ReadFileTool(file_system=file_system))
62
+ # tools.register(WriteFileTool(file_system=file_system))
63
+
64
+ return Agent(
65
+ llm_service=llm,
66
+ tool_registry=tools,
67
+ )
@@ -0,0 +1,227 @@
1
+ """
2
+ Mock authentication example to verify user resolution is working.
3
+
4
+ This example demonstrates the new UserResolver architecture where:
5
+ 1. UserResolver is a required parameter of Agent
6
+ 2. Agent.send_message() accepts RequestContext (not User directly)
7
+ 3. The Agent resolves the user internally using the UserResolver
8
+
9
+ The agent uses an LLM middleware to inject user info into the response,
10
+ so we can verify the authentication is working correctly.
11
+
12
+ Usage:
13
+ python -m vanna.examples.mock_auth_example
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import asyncio
19
+
20
+ from vanna import AgentConfig, Agent
21
+ from vanna.core.registry import ToolRegistry
22
+ from vanna.core.llm import LlmRequest, LlmResponse
23
+ from vanna.core.middleware import LlmMiddleware
24
+ from vanna.integrations.mock import MockLlmService
25
+ from vanna.core.user import CookieEmailUserResolver, RequestContext
26
+
27
+
28
+ class UserEchoMiddleware(LlmMiddleware):
29
+ """Middleware that injects user email into LLM responses."""
30
+
31
+ async def after_llm_response(
32
+ self, request: LlmRequest, response: LlmResponse
33
+ ) -> LlmResponse:
34
+ """Inject user email into response."""
35
+ # Extract user email from request user_id (which is set to user.id in the agent)
36
+ user_id = request.user_id
37
+
38
+ # Create a new response with user info
39
+ new_content = f"Hello! You are authenticated as: {user_id}"
40
+
41
+ return LlmResponse(
42
+ content=new_content,
43
+ finish_reason=response.finish_reason,
44
+ usage=response.usage,
45
+ )
46
+
47
+
48
+ def create_demo_agent() -> Agent:
49
+ """Create a demo agent for server usage.
50
+
51
+ Returns:
52
+ Configured Agent instance with cookie-based authentication
53
+ """
54
+ # Create a mock LLM
55
+ llm_service = MockLlmService(response_content="Mock response")
56
+
57
+ # Empty tool registry
58
+ tool_registry = ToolRegistry()
59
+
60
+ # Cookie-based user resolver
61
+ user_resolver = CookieEmailUserResolver(cookie_name="vanna_email")
62
+
63
+ # User echo middleware
64
+ middleware = UserEchoMiddleware()
65
+
66
+ # Create agent with user resolver and middleware
67
+ agent = Agent(
68
+ llm_service=llm_service,
69
+ tool_registry=tool_registry,
70
+ user_resolver=user_resolver,
71
+ llm_middlewares=[middleware],
72
+ config=AgentConfig(
73
+ stream_responses=True,
74
+ include_thinking_indicators=False,
75
+ ),
76
+ )
77
+
78
+ return agent
79
+
80
+
81
+ async def demo_authentication():
82
+ """Demonstrate authentication with different request contexts."""
83
+ agent = create_demo_agent()
84
+
85
+ print("=== Mock Authentication Demo ===")
86
+ print("This example verifies that user resolution is working correctly.\n")
87
+
88
+ # Test 1: Request with email cookie
89
+ print("šŸ”¹ Test 1: Authenticated user (alice@example.com)")
90
+ request_context = RequestContext(
91
+ cookies={"vanna_email": "alice@example.com"},
92
+ headers={},
93
+ remote_addr="127.0.0.1",
94
+ )
95
+
96
+ print(
97
+ "Request context:",
98
+ {
99
+ "cookies": request_context.cookies,
100
+ "headers": request_context.headers,
101
+ "remote_addr": request_context.remote_addr,
102
+ },
103
+ )
104
+
105
+ # Send message - Agent will resolve user internally
106
+ agent_response = ""
107
+ async for component in agent.send_message(
108
+ request_context=request_context,
109
+ message="Who am I?",
110
+ conversation_id="test_conv_1",
111
+ ):
112
+ # Extract and display user info from the resolved user
113
+ if hasattr(component, "rich_component"):
114
+ rich = component.rich_component
115
+ # Check if it's a text component
116
+ if rich.type.value == "text":
117
+ # Access content directly from the component (before serialization)
118
+ if hasattr(rich, "content"):
119
+ agent_response = rich.content
120
+
121
+ print(f"Agent response: {agent_response}")
122
+
123
+ # Verify user was resolved by checking the conversation store
124
+ user_resolver = agent.user_resolver
125
+ resolved_user = await user_resolver.resolve_user(request_context)
126
+ print(
127
+ f"āœ… Resolved user: {resolved_user.email} (username: {resolved_user.username}, id: {resolved_user.id})"
128
+ )
129
+ print(f" Permissions: {resolved_user.permissions}")
130
+ print(f" Metadata: {resolved_user.metadata}")
131
+
132
+ print("\n" + "=" * 60 + "\n")
133
+
134
+ # Test 2: Request without email cookie (anonymous)
135
+ print("šŸ”¹ Test 2: Anonymous user (no cookie)")
136
+ anonymous_context = RequestContext(cookies={}, headers={}, remote_addr="127.0.0.1")
137
+
138
+ print(
139
+ "Request context:",
140
+ {
141
+ "cookies": anonymous_context.cookies,
142
+ "headers": anonymous_context.headers,
143
+ "remote_addr": anonymous_context.remote_addr,
144
+ },
145
+ )
146
+
147
+ agent_response = ""
148
+ async for component in agent.send_message(
149
+ request_context=anonymous_context,
150
+ message="Who am I?",
151
+ conversation_id="test_conv_2",
152
+ ):
153
+ if hasattr(component, "rich_component"):
154
+ rich = component.rich_component
155
+ if rich.type.value == "text" and hasattr(rich, "content"):
156
+ agent_response = rich.content
157
+
158
+ print(f"Agent response: {agent_response}")
159
+
160
+ resolved_user = await user_resolver.resolve_user(anonymous_context)
161
+ print(
162
+ f"āœ… Resolved user: {resolved_user.email or 'None'} (username: {resolved_user.username}, id: {resolved_user.id})"
163
+ )
164
+ print(f" Permissions: {resolved_user.permissions}")
165
+ print(f" Metadata: {resolved_user.metadata}")
166
+
167
+ print("\n" + "=" * 60 + "\n")
168
+
169
+ # Test 3: Different user
170
+ print("šŸ”¹ Test 3: Different authenticated user (bob@company.com)")
171
+ bob_context = RequestContext(
172
+ cookies={"vanna_email": "bob@company.com"},
173
+ headers={"User-Agent": "Mozilla/5.0"},
174
+ remote_addr="192.168.1.100",
175
+ )
176
+
177
+ print(
178
+ "Request context:",
179
+ {
180
+ "cookies": bob_context.cookies,
181
+ "headers": bob_context.headers,
182
+ "remote_addr": bob_context.remote_addr,
183
+ },
184
+ )
185
+
186
+ agent_response = ""
187
+ async for component in agent.send_message(
188
+ request_context=bob_context, message="Who am I?", conversation_id="test_conv_3"
189
+ ):
190
+ if hasattr(component, "rich_component"):
191
+ rich = component.rich_component
192
+ if rich.type.value == "text" and hasattr(rich, "content"):
193
+ agent_response = rich.content
194
+
195
+ print(f"Agent response: {agent_response}")
196
+
197
+ resolved_user = await user_resolver.resolve_user(bob_context)
198
+ print(
199
+ f"āœ… Resolved user: {resolved_user.email} (username: {resolved_user.username}, id: {resolved_user.id})"
200
+ )
201
+ print(f" Permissions: {resolved_user.permissions}")
202
+ print(f" Metadata: {resolved_user.metadata}")
203
+
204
+ print("\n" + "=" * 60)
205
+ print("\nāœ… Authentication demo complete!")
206
+ print("\nKey Features Verified:")
207
+ print("• UserResolver is part of Agent")
208
+ print("• Agent.send_message() accepts RequestContext")
209
+ print("• User resolution happens internally in Agent")
210
+ print("• CookieEmailUserResolver extracts email from vanna_email cookie")
211
+ print("• Anonymous users are created when no cookie is present")
212
+ print("• Different users can be resolved from different request contexts")
213
+
214
+
215
+ async def main():
216
+ """Run the authentication example."""
217
+ await demo_authentication()
218
+
219
+
220
+ def run_interactive():
221
+ """Entry point for interactive usage."""
222
+ print("Starting mock authentication example...")
223
+ asyncio.run(main())
224
+
225
+
226
+ if __name__ == "__main__":
227
+ run_interactive()