vanna 0.7.9__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 +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.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.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.0.dist-info}/WHEEL +0 -0
  302. {vanna-0.7.9.dist-info → vanna-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,311 @@
1
+ """
2
+ Mock example showing how to create and use custom tools.
3
+
4
+ This example demonstrates creating a simple calculator tool
5
+ and registering it with an agent that uses a mock LLM service.
6
+ It now includes a `MockCalculatorLlmService` that automatically
7
+ invokes the calculator tool with random numbers before echoing
8
+ back the computed answer.
9
+
10
+ Usage:
11
+ Template: Copy this file and modify for your custom tools
12
+ Interactive: python -m vanna.examples.mock_custom_tool
13
+ REPL: from vanna.examples.mock_custom_tool import create_demo_agent
14
+ Server: python -m vanna.servers --example mock_custom_tool
15
+ """
16
+
17
+ import asyncio
18
+ import random
19
+ import uuid
20
+ from typing import AsyncGenerator, Dict, List, Optional, Tuple, Type
21
+
22
+ from pydantic import BaseModel, Field
23
+
24
+ from vanna import (
25
+ AgentConfig,
26
+ Agent,
27
+ Tool,
28
+ ToolContext,
29
+ ToolRegistry,
30
+ ToolResult,
31
+ User,
32
+ UiComponent,
33
+ )
34
+ from vanna.core.interfaces import LlmService
35
+ from vanna.core.models import (
36
+ LlmRequest,
37
+ LlmResponse,
38
+ LlmStreamChunk,
39
+ ToolCall,
40
+ ToolSchema,
41
+ )
42
+ from vanna.core.rich_components import (
43
+ CardComponent,
44
+ NotificationComponent,
45
+ ComponentType,
46
+ )
47
+ from vanna.core.simple_components import (
48
+ SimpleTextComponent,
49
+ )
50
+
51
+
52
+ class CalculatorArgs(BaseModel):
53
+ """Arguments for the calculator tool."""
54
+
55
+ operation: str = Field(
56
+ description="The operation to perform: add, subtract, multiply, divide"
57
+ )
58
+ a: float = Field(description="First number")
59
+ b: float = Field(description="Second number")
60
+
61
+
62
+ class CalculatorTool(Tool[CalculatorArgs]):
63
+ """A simple calculator tool."""
64
+
65
+ @property
66
+ def name(self) -> str:
67
+ return "calculator"
68
+
69
+ @property
70
+ def description(self) -> str:
71
+ return "Perform basic arithmetic operations (add, subtract, multiply, divide)"
72
+
73
+ def get_args_schema(self) -> Type[CalculatorArgs]:
74
+ return CalculatorArgs
75
+
76
+ async def execute(self, context: ToolContext, args: CalculatorArgs) -> ToolResult:
77
+ """Execute the calculator operation."""
78
+ symbol_map = {"add": "+", "subtract": "-", "multiply": "Ɨ", "divide": "Ć·"}
79
+
80
+ try:
81
+ if args.operation == "add":
82
+ result = args.a + args.b
83
+ elif args.operation == "subtract":
84
+ result = args.a - args.b
85
+ elif args.operation == "multiply":
86
+ result = args.a * args.b
87
+ elif args.operation == "divide":
88
+ if args.b == 0:
89
+ message = "Cannot divide by zero"
90
+ await asyncio.sleep(3)
91
+ return ToolResult(
92
+ success=False,
93
+ result_for_llm=message,
94
+ ui_component=UiComponent(
95
+ rich_component=NotificationComponent(
96
+ type=ComponentType.NOTIFICATION,
97
+ level="error",
98
+ message=message,
99
+ ),
100
+ simple_component=SimpleTextComponent(text=message),
101
+ ),
102
+ error=message,
103
+ )
104
+ result = args.a / args.b
105
+ else:
106
+ message = f"Unknown operation: {args.operation}"
107
+ await asyncio.sleep(3)
108
+ return ToolResult(
109
+ success=False,
110
+ result_for_llm=message,
111
+ ui_component=UiComponent(
112
+ rich_component=NotificationComponent(
113
+ type=ComponentType.NOTIFICATION,
114
+ level="warning",
115
+ message=message,
116
+ ),
117
+ simple_component=SimpleTextComponent(text=message),
118
+ ),
119
+ error=message,
120
+ )
121
+
122
+ await asyncio.sleep(3)
123
+
124
+ symbol = symbol_map.get(args.operation, args.operation)
125
+ expression = f"{args.a:g} {symbol} {args.b:g} = {result:g}"
126
+ return ToolResult(
127
+ success=True,
128
+ result_for_llm=str(result),
129
+ ui_component=UiComponent(
130
+ rich_component=CardComponent(
131
+ type=ComponentType.CARD,
132
+ title="Calculator Result",
133
+ content=expression,
134
+ ),
135
+ simple_component=SimpleTextComponent(text=expression),
136
+ ),
137
+ error=None,
138
+ )
139
+
140
+ except Exception as e:
141
+ message = str(e)
142
+ await asyncio.sleep(3)
143
+ return ToolResult(
144
+ success=False,
145
+ result_for_llm=message,
146
+ ui_component=UiComponent(
147
+ rich_component=NotificationComponent(
148
+ type=ComponentType.NOTIFICATION,
149
+ level="error",
150
+ message=message,
151
+ ),
152
+ simple_component=SimpleTextComponent(text=message),
153
+ ),
154
+ error=message,
155
+ )
156
+
157
+
158
+ class MockCalculatorLlmService(LlmService):
159
+ """LLM service that exercises the calculator tool before echoing the result."""
160
+
161
+ def __init__(self, seed: Optional[int] = None):
162
+ self._random = random.Random(seed)
163
+
164
+ async def send_request(self, request: LlmRequest) -> LlmResponse:
165
+ """Handle non-streaming calculator interactions."""
166
+ await asyncio.sleep(0.05)
167
+ return self._build_response(request)
168
+
169
+ async def stream_request(
170
+ self, request: LlmRequest
171
+ ) -> AsyncGenerator[LlmStreamChunk, None]:
172
+ """Provide streaming compatibility by yielding a single chunk."""
173
+ await asyncio.sleep(0.05)
174
+ response = self._build_response(request)
175
+
176
+ if response.tool_calls:
177
+ yield LlmStreamChunk(tool_calls=response.tool_calls)
178
+ if response.content is not None:
179
+ yield LlmStreamChunk(
180
+ content=response.content, finish_reason=response.finish_reason
181
+ )
182
+ else:
183
+ yield LlmStreamChunk(finish_reason=response.finish_reason)
184
+
185
+ async def validate_tools(self, tools: List[ToolSchema]) -> List[str]:
186
+ """Mock validation - no errors."""
187
+ return []
188
+
189
+ def _build_response(self, request: LlmRequest) -> LlmResponse:
190
+ """Create a response that either calls the tool or echoes its result."""
191
+ last_message = request.messages[-1] if request.messages else None
192
+
193
+ if last_message and last_message.role == "tool":
194
+ answer = last_message.content or "No result provided"
195
+ return LlmResponse(
196
+ content=answer,
197
+ finish_reason="stop",
198
+ usage={
199
+ "prompt_tokens": 30,
200
+ "completion_tokens": 10,
201
+ "total_tokens": 40,
202
+ },
203
+ )
204
+
205
+ operation, a, b = self._random_operands()
206
+ tool_call = ToolCall(
207
+ id=f"call_{uuid.uuid4().hex[:8]}",
208
+ name="calculator",
209
+ arguments={"operation": operation, "a": a, "b": b},
210
+ )
211
+
212
+ return LlmResponse(
213
+ content="Let me ask my calculator friend for help...",
214
+ tool_calls=[tool_call],
215
+ finish_reason="tool_calls",
216
+ usage={"prompt_tokens": 30, "completion_tokens": 5, "total_tokens": 35},
217
+ )
218
+
219
+ def _random_operands(self) -> Tuple[str, float, float]:
220
+ """Generate operation and operands suited for the calculator tool."""
221
+ operation = self._random.choice(["add", "subtract", "multiply", "divide"])
222
+
223
+ if operation == "divide":
224
+ b = float(self._random.randint(1, 10))
225
+ multiplier = self._random.randint(1, 10)
226
+ a = float(b * multiplier)
227
+ elif operation == "subtract":
228
+ b = float(self._random.randint(1, 10))
229
+ a = b + float(self._random.randint(0, 10))
230
+ else:
231
+ a = float(self._random.randint(1, 12))
232
+ b = float(self._random.randint(1, 12))
233
+
234
+ return operation, a, b
235
+
236
+
237
+ def create_demo_agent() -> Agent:
238
+ """Create a demo agent with custom calculator tool.
239
+
240
+ Returns:
241
+ Configured Agent with calculator tool and mock calculator LLM
242
+ """
243
+ tool_registry = ToolRegistry()
244
+ calculator_tool = CalculatorTool()
245
+ tool_registry.register(calculator_tool)
246
+
247
+ llm_service = MockCalculatorLlmService()
248
+
249
+ return Agent(
250
+ llm_service=llm_service,
251
+ tool_registry=tool_registry,
252
+ config=AgentConfig(
253
+ stream_responses=False,
254
+ include_thinking_indicators=False,
255
+ ),
256
+ )
257
+
258
+
259
+ async def main() -> None:
260
+ """Run the mock custom tool example."""
261
+
262
+ # Create agent using factory function
263
+ agent = create_demo_agent()
264
+ tool_registry = agent.tool_registry
265
+
266
+ # Create a test user
267
+ user = User(id="user123", username="testuser", permissions=[])
268
+
269
+ # Test the tool directly
270
+ print("Testing calculator tool directly:")
271
+ tool_call = ToolCall(
272
+ id="test123", name="calculator", arguments={"operation": "add", "a": 5, "b": 3}
273
+ )
274
+
275
+ context = ToolContext(user=user, conversation_id="test", request_id="test")
276
+
277
+ result = await tool_registry.execute(tool_call, context)
278
+ print(f"5 + 3 = {result.result_for_llm if result.success else result.error}")
279
+
280
+ # Show available tools
281
+ schemas = await tool_registry.get_schemas(user)
282
+ print(f"\nAvailable tools for user: {[schema.name for schema in schemas]}")
283
+
284
+ # Demonstrate the mock LLM triggering a tool call
285
+ print("\nAgent conversation demo:")
286
+ conversation_id = "calc-demo"
287
+ async for component in agent.send_message(
288
+ user=user,
289
+ message="Can you compute something for me?",
290
+ conversation_id=conversation_id,
291
+ ):
292
+ print(f"- Component type: {component.rich_component.type}")
293
+ if (
294
+ hasattr(component.rich_component, "content")
295
+ and component.rich_component.content
296
+ ):
297
+ print(f"Assistant: {component.rich_component.content}")
298
+ elif component.simple_component and hasattr(component.simple_component, "text"):
299
+ print(f"Assistant: {component.simple_component.text}")
300
+ else:
301
+ print(f"- Component data: {component.rich_component.data}")
302
+
303
+
304
+ def run_interactive() -> None:
305
+ """Entry point for interactive usage."""
306
+ print("Starting mock custom tool example...")
307
+ asyncio.run(main())
308
+
309
+
310
+ if __name__ == "__main__":
311
+ run_interactive()
@@ -0,0 +1,79 @@
1
+ """
2
+ Mock quickstart example for the Vanna Agents framework.
3
+
4
+ This example shows how to create a basic agent with a mock LLM service
5
+ and have a simple conversation.
6
+
7
+ Usage:
8
+ Template: Copy this file and modify for your needs
9
+ Interactive: python -m vanna.examples.mock_quickstart
10
+ REPL: from vanna.examples.mock_quickstart import create_demo_agent
11
+ Server: python -m vanna.servers --example mock_quickstart
12
+ """
13
+
14
+ import asyncio
15
+
16
+ from vanna import (
17
+ AgentConfig,
18
+ Agent,
19
+ MemoryConversationStore,
20
+ MockLlmService,
21
+ User,
22
+ )
23
+
24
+
25
+ def create_demo_agent() -> Agent:
26
+ """Create a demo agent for REPL and server usage.
27
+
28
+ Returns:
29
+ Configured Agent instance
30
+ """
31
+ llm_service = MockLlmService(
32
+ response_content="Hello! I'm a helpful AI assistant created using the Vanna Agents framework."
33
+ )
34
+
35
+ return Agent(
36
+ llm_service=llm_service,
37
+ config=AgentConfig(
38
+ stream_responses=True, # Enable streaming for better server experience
39
+ include_thinking_indicators=True,
40
+ ),
41
+ )
42
+
43
+
44
+ async def main() -> None:
45
+ """Run the mock quickstart example."""
46
+
47
+ # Create agent using factory function
48
+ agent = create_demo_agent()
49
+
50
+ # Create a test user
51
+ user = User(
52
+ id="user123", username="testuser", email="test@example.com", permissions=[]
53
+ )
54
+
55
+ # Start a conversation
56
+ conversation_id = "conversation123"
57
+ user_message = "Hello! Can you introduce yourself?"
58
+
59
+ print(f"User: {user_message}")
60
+ print("Agent: ", end="")
61
+
62
+ # Send message and collect response
63
+ async for component in agent.send_message(
64
+ user=user, message=user_message, conversation_id=conversation_id
65
+ ):
66
+ if hasattr(component, "content"):
67
+ print(component.content, end="")
68
+
69
+ print()
70
+
71
+
72
+ def run_interactive() -> None:
73
+ """Entry point for interactive usage."""
74
+ print("Starting Vanna Agents mock quickstart demo...")
75
+ asyncio.run(main())
76
+
77
+
78
+ if __name__ == "__main__":
79
+ run_interactive()
@@ -0,0 +1,145 @@
1
+ """
2
+ Mock quota-based agent example using Mock LLM service.
3
+
4
+ This example demonstrates how to create a custom agent runner that
5
+ enforces user-based message quotas. It shows:
6
+ - Custom agent runner subclass
7
+ - Quota management and enforcement
8
+ - Error handling for quota exceeded cases
9
+ - Multiple users with different quotas
10
+
11
+ Run:
12
+ PYTHONPATH=. python vanna/examples/mock_quota_example.py
13
+ """
14
+
15
+ import asyncio
16
+
17
+ from vanna import (
18
+ AgentConfig,
19
+ MemoryConversationStore,
20
+ MockLlmService,
21
+ User,
22
+ )
23
+ from vanna.core.registry import ToolRegistry
24
+ from vanna.tools import ListFilesTool
25
+ from vanna.examples.quota_agent import QuotaAgentRunner, QuotaExceededError
26
+
27
+
28
+ async def demonstrate_quota_system() -> None:
29
+ """Demonstrate the quota-based agent system."""
30
+ print("šŸš€ Starting Mock Quota-based Agent Example\n")
31
+
32
+ # Create a mock LLM service
33
+ llm_service = MockLlmService(
34
+ response_content="Hello! I'm here to help you with your questions."
35
+ )
36
+
37
+ # Create tool registry with list_files tool
38
+ tool_registry = ToolRegistry()
39
+ list_files_tool = ListFilesTool()
40
+ tool_registry.register(list_files_tool)
41
+
42
+ # Create conversation store
43
+ conversation_store = MemoryConversationStore()
44
+
45
+ # Create the quota-based agent
46
+ agent = QuotaAgentRunner(
47
+ llm_service=llm_service,
48
+ tool_registry=tool_registry,
49
+ conversation_store=conversation_store,
50
+ config=AgentConfig(
51
+ stream_responses=False,
52
+ include_thinking_indicators=False,
53
+ ),
54
+ )
55
+
56
+ # Create users with different quota settings
57
+ regular_user = User(
58
+ id="user1", username="alice", email="alice@example.com", permissions=[]
59
+ )
60
+
61
+ premium_user = User(
62
+ id="user2", username="bob", email="bob@example.com", permissions=["premium"]
63
+ )
64
+
65
+ # Set custom quotas
66
+ agent.set_user_quota(regular_user.id, 3) # Alice gets 3 messages
67
+ agent.set_user_quota(premium_user.id, 5) # Bob gets 5 messages (premium)
68
+
69
+ print("šŸ“‹ User Quotas:")
70
+ print(
71
+ f" • {regular_user.username}: {agent.get_user_quota(regular_user.id)} messages"
72
+ )
73
+ print(
74
+ f" • {premium_user.username}: {agent.get_user_quota(premium_user.id)} messages"
75
+ )
76
+ print()
77
+
78
+ # Test regular user within quota
79
+ print("šŸ’¬ Testing regular user (Alice) within quota:")
80
+ for i in range(1, 4): # Send 3 messages (within quota)
81
+ print(f" Message {i}/3:")
82
+ async for component in agent.send_message(
83
+ user=regular_user,
84
+ message=f"Hello, this is message {i}",
85
+ conversation_id="alice-conv",
86
+ ):
87
+ if hasattr(component, "content") and component.content:
88
+ print(f" Agent: {component.content}")
89
+ print()
90
+
91
+ # Test regular user exceeding quota
92
+ print("āš ļø Testing regular user (Alice) exceeding quota:")
93
+ async for component in agent.send_message(
94
+ user=regular_user,
95
+ message="This message should be blocked",
96
+ conversation_id="alice-conv",
97
+ ):
98
+ if hasattr(component, "content") and component.content:
99
+ print(f" Agent: {component.content}")
100
+ print()
101
+
102
+ # Test premium user with higher quota
103
+ print("⭐ Testing premium user (Bob) with higher quota:")
104
+ for i in range(1, 4): # Send 3 messages
105
+ print(f" Message {i}/5:")
106
+ async for component in agent.send_message(
107
+ user=premium_user,
108
+ message=f"Premium user message {i}",
109
+ conversation_id="bob-conv",
110
+ ):
111
+ if hasattr(component, "content") and component.content:
112
+ print(f" Agent: {component.content}")
113
+ print()
114
+
115
+ # Demonstrate quota reset
116
+ print("šŸ”„ Resetting Alice's usage:")
117
+ agent.reset_user_usage(regular_user.id)
118
+ print(f" Alice's remaining messages: {agent.get_user_remaining(regular_user.id)}")
119
+ print()
120
+
121
+ print("āœ… After reset, Alice can send messages again:")
122
+ async for component in agent.send_message(
123
+ user=regular_user,
124
+ message="This should work after reset",
125
+ conversation_id="alice-conv2",
126
+ ):
127
+ if hasattr(component, "content") and component.content:
128
+ print(f" Agent: {component.content}")
129
+
130
+ print("\nšŸ“Š Final Usage Summary:")
131
+ print(
132
+ f" • Alice: {agent.get_user_usage(regular_user.id)}/{agent.get_user_quota(regular_user.id)} used"
133
+ )
134
+ print(
135
+ f" • Bob: {agent.get_user_usage(premium_user.id)}/{agent.get_user_quota(premium_user.id)} used"
136
+ )
137
+
138
+
139
+ async def main() -> None:
140
+ """Run the mock quota example."""
141
+ await demonstrate_quota_system()
142
+
143
+
144
+ if __name__ == "__main__":
145
+ asyncio.run(main())