vanna 2.0.0rc1__py3-none-any.whl → 2.0.2__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.
- vanna/integrations/chromadb/agent_memory.py +78 -6
- vanna/integrations/faiss/agent_memory.py +4 -0
- vanna/integrations/qdrant/agent_memory.py +41 -14
- vanna-2.0.2.dist-info/METADATA +485 -0
- {vanna-2.0.0rc1.dist-info → vanna-2.0.2.dist-info}/RECORD +8 -8
- vanna-2.0.0rc1.dist-info/METADATA +0 -868
- {vanna-2.0.0rc1.dist-info → vanna-2.0.2.dist-info}/WHEEL +0 -0
- {vanna-2.0.0rc1.dist-info → vanna-2.0.2.dist-info}/entry_points.txt +0 -0
- {vanna-2.0.0rc1.dist-info → vanna-2.0.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,868 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: vanna
|
|
3
|
-
Version: 2.0.0rc1
|
|
4
|
-
Summary: Generate SQL queries from natural language
|
|
5
|
-
Author-email: Zain Hoda <zain@vanna.ai>
|
|
6
|
-
Requires-Python: >=3.9
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Requires-Dist: pydantic>=2.0.0
|
|
13
|
-
Requires-Dist: click>=8.0.0
|
|
14
|
-
Requires-Dist: pandas
|
|
15
|
-
Requires-Dist: httpx>=0.28.0
|
|
16
|
-
Requires-Dist: PyYAML
|
|
17
|
-
Requires-Dist: plotly
|
|
18
|
-
Requires-Dist: tabulate
|
|
19
|
-
Requires-Dist: sqlparse
|
|
20
|
-
Requires-Dist: sqlalchemy
|
|
21
|
-
Requires-Dist: requests
|
|
22
|
-
Requires-Dist: psycopg2-binary ; extra == "all"
|
|
23
|
-
Requires-Dist: db-dtypes ; extra == "all"
|
|
24
|
-
Requires-Dist: PyMySQL ; extra == "all"
|
|
25
|
-
Requires-Dist: google-cloud-bigquery ; extra == "all"
|
|
26
|
-
Requires-Dist: snowflake-connector-python ; extra == "all"
|
|
27
|
-
Requires-Dist: duckdb ; extra == "all"
|
|
28
|
-
Requires-Dist: openai ; extra == "all"
|
|
29
|
-
Requires-Dist: qianfan ; extra == "all"
|
|
30
|
-
Requires-Dist: mistralai>=1.0.0 ; extra == "all"
|
|
31
|
-
Requires-Dist: chromadb>=1.1.0 ; extra == "all"
|
|
32
|
-
Requires-Dist: anthropic ; extra == "all"
|
|
33
|
-
Requires-Dist: zhipuai ; extra == "all"
|
|
34
|
-
Requires-Dist: marqo ; extra == "all"
|
|
35
|
-
Requires-Dist: google-generativeai ; extra == "all"
|
|
36
|
-
Requires-Dist: google-cloud-aiplatform ; extra == "all"
|
|
37
|
-
Requires-Dist: qdrant-client ; extra == "all"
|
|
38
|
-
Requires-Dist: fastembed ; extra == "all"
|
|
39
|
-
Requires-Dist: ollama ; extra == "all"
|
|
40
|
-
Requires-Dist: httpx ; extra == "all"
|
|
41
|
-
Requires-Dist: opensearch-py ; extra == "all"
|
|
42
|
-
Requires-Dist: opensearch-dsl ; extra == "all"
|
|
43
|
-
Requires-Dist: transformers ; extra == "all"
|
|
44
|
-
Requires-Dist: pinecone ; extra == "all"
|
|
45
|
-
Requires-Dist: pymilvus[model] ; extra == "all"
|
|
46
|
-
Requires-Dist: weaviate-client ; extra == "all"
|
|
47
|
-
Requires-Dist: azure-search-documents ; extra == "all"
|
|
48
|
-
Requires-Dist: azure-identity ; extra == "all"
|
|
49
|
-
Requires-Dist: azure-common ; extra == "all"
|
|
50
|
-
Requires-Dist: faiss-cpu ; extra == "all"
|
|
51
|
-
Requires-Dist: boto ; extra == "all"
|
|
52
|
-
Requires-Dist: boto3 ; extra == "all"
|
|
53
|
-
Requires-Dist: botocore ; extra == "all"
|
|
54
|
-
Requires-Dist: langchain_core ; extra == "all"
|
|
55
|
-
Requires-Dist: langchain_postgres ; extra == "all"
|
|
56
|
-
Requires-Dist: langchain-community ; extra == "all"
|
|
57
|
-
Requires-Dist: langchain-huggingface ; extra == "all"
|
|
58
|
-
Requires-Dist: xinference-client ; extra == "all"
|
|
59
|
-
Requires-Dist: anthropic ; extra == "anthropic"
|
|
60
|
-
Requires-Dist: openai ; extra == "azureopenai"
|
|
61
|
-
Requires-Dist: azure-identity ; extra == "azureopenai"
|
|
62
|
-
Requires-Dist: azure-search-documents ; extra == "azuresearch"
|
|
63
|
-
Requires-Dist: azure-identity ; extra == "azuresearch"
|
|
64
|
-
Requires-Dist: azure-common ; extra == "azuresearch"
|
|
65
|
-
Requires-Dist: fastembed ; extra == "azuresearch"
|
|
66
|
-
Requires-Dist: boto3 ; extra == "bedrock"
|
|
67
|
-
Requires-Dist: botocore ; extra == "bedrock"
|
|
68
|
-
Requires-Dist: google-cloud-bigquery ; extra == "bigquery"
|
|
69
|
-
Requires-Dist: chromadb>=1.1.0 ; extra == "chromadb"
|
|
70
|
-
Requires-Dist: clickhouse_connect ; extra == "clickhouse"
|
|
71
|
-
Requires-Dist: pytest>=7.0.0 ; extra == "dev"
|
|
72
|
-
Requires-Dist: pytest-asyncio>=0.21.0 ; extra == "dev"
|
|
73
|
-
Requires-Dist: pytest-mock>=3.10.0 ; extra == "dev"
|
|
74
|
-
Requires-Dist: pytest-cov>=4.0.0 ; extra == "dev"
|
|
75
|
-
Requires-Dist: tox>=4.0.0 ; extra == "dev"
|
|
76
|
-
Requires-Dist: mypy ; extra == "dev"
|
|
77
|
-
Requires-Dist: ruff ; extra == "dev"
|
|
78
|
-
Requires-Dist: pandas-stubs ; extra == "dev"
|
|
79
|
-
Requires-Dist: plotly-stubs ; extra == "dev"
|
|
80
|
-
Requires-Dist: types-PyYAML ; extra == "dev"
|
|
81
|
-
Requires-Dist: types-requests ; extra == "dev"
|
|
82
|
-
Requires-Dist: types-tabulate ; extra == "dev"
|
|
83
|
-
Requires-Dist: duckdb ; extra == "duckdb"
|
|
84
|
-
Requires-Dist: faiss-cpu ; extra == "faiss-cpu"
|
|
85
|
-
Requires-Dist: faiss-gpu ; extra == "faiss-gpu"
|
|
86
|
-
Requires-Dist: fastapi>=0.68.0 ; extra == "fastapi"
|
|
87
|
-
Requires-Dist: uvicorn>=0.15.0 ; extra == "fastapi"
|
|
88
|
-
Requires-Dist: flask>=2.0.0 ; extra == "flask"
|
|
89
|
-
Requires-Dist: flask-cors>=4.0.0 ; extra == "flask"
|
|
90
|
-
Requires-Dist: google-genai ; extra == "gemini"
|
|
91
|
-
Requires-Dist: google-generativeai ; extra == "google"
|
|
92
|
-
Requires-Dist: google-cloud-aiplatform ; extra == "google"
|
|
93
|
-
Requires-Dist: transformers ; extra == "hf"
|
|
94
|
-
Requires-Dist: pyhive ; extra == "hive"
|
|
95
|
-
Requires-Dist: thrift ; extra == "hive"
|
|
96
|
-
Requires-Dist: marqo ; extra == "marqo"
|
|
97
|
-
Requires-Dist: pymilvus[model] ; extra == "milvus"
|
|
98
|
-
Requires-Dist: mistralai>=1.0.0 ; extra == "mistralai"
|
|
99
|
-
Requires-Dist: pyodbc ; extra == "mssql"
|
|
100
|
-
Requires-Dist: PyMySQL ; extra == "mysql"
|
|
101
|
-
Requires-Dist: ollama ; extra == "ollama"
|
|
102
|
-
Requires-Dist: httpx ; extra == "ollama"
|
|
103
|
-
Requires-Dist: openai ; extra == "openai"
|
|
104
|
-
Requires-Dist: opensearch-py ; extra == "opensearch"
|
|
105
|
-
Requires-Dist: opensearch-dsl ; extra == "opensearch"
|
|
106
|
-
Requires-Dist: langchain-community ; extra == "opensearch"
|
|
107
|
-
Requires-Dist: langchain-huggingface ; extra == "opensearch"
|
|
108
|
-
Requires-Dist: oracledb ; extra == "oracle"
|
|
109
|
-
Requires-Dist: chromadb<1.0.0 ; extra == "oracle"
|
|
110
|
-
Requires-Dist: langchain-postgres>=0.0.12 ; extra == "pgvector"
|
|
111
|
-
Requires-Dist: pinecone ; extra == "pinecone"
|
|
112
|
-
Requires-Dist: fastembed ; extra == "pinecone"
|
|
113
|
-
Requires-Dist: psycopg2-binary ; extra == "postgres"
|
|
114
|
-
Requires-Dist: db-dtypes ; extra == "postgres"
|
|
115
|
-
Requires-Dist: pyhive ; extra == "presto"
|
|
116
|
-
Requires-Dist: thrift ; extra == "presto"
|
|
117
|
-
Requires-Dist: qdrant-client ; extra == "qdrant"
|
|
118
|
-
Requires-Dist: fastembed ; extra == "qdrant"
|
|
119
|
-
Requires-Dist: qianfan ; extra == "qianfan"
|
|
120
|
-
Requires-Dist: vanna[flask, fastapi] ; extra == "servers"
|
|
121
|
-
Requires-Dist: snowflake-connector-python ; extra == "snowflake"
|
|
122
|
-
Requires-Dist: pytest>=7.0.0 ; extra == "test"
|
|
123
|
-
Requires-Dist: pytest-asyncio>=0.21.0 ; extra == "test"
|
|
124
|
-
Requires-Dist: pytest-mock>=3.10.0 ; extra == "test"
|
|
125
|
-
Requires-Dist: pytest-cov>=4.0.0 ; extra == "test"
|
|
126
|
-
Requires-Dist: tox>=4.0.0 ; extra == "test"
|
|
127
|
-
Requires-Dist: vllm ; extra == "vllm"
|
|
128
|
-
Requires-Dist: weaviate-client ; extra == "weaviate"
|
|
129
|
-
Requires-Dist: xinference-client ; extra == "xinference-client"
|
|
130
|
-
Requires-Dist: zhipuai ; extra == "zhipuai"
|
|
131
|
-
Project-URL: Bug Tracker, https://github.com/vanna-ai/vanna/issues
|
|
132
|
-
Project-URL: Homepage, https://github.com/vanna-ai/vanna
|
|
133
|
-
Provides-Extra: all
|
|
134
|
-
Provides-Extra: anthropic
|
|
135
|
-
Provides-Extra: azureopenai
|
|
136
|
-
Provides-Extra: azuresearch
|
|
137
|
-
Provides-Extra: bedrock
|
|
138
|
-
Provides-Extra: bigquery
|
|
139
|
-
Provides-Extra: chromadb
|
|
140
|
-
Provides-Extra: clickhouse
|
|
141
|
-
Provides-Extra: dev
|
|
142
|
-
Provides-Extra: duckdb
|
|
143
|
-
Provides-Extra: faiss-cpu
|
|
144
|
-
Provides-Extra: faiss-gpu
|
|
145
|
-
Provides-Extra: fastapi
|
|
146
|
-
Provides-Extra: flask
|
|
147
|
-
Provides-Extra: gemini
|
|
148
|
-
Provides-Extra: google
|
|
149
|
-
Provides-Extra: hf
|
|
150
|
-
Provides-Extra: hive
|
|
151
|
-
Provides-Extra: marqo
|
|
152
|
-
Provides-Extra: milvus
|
|
153
|
-
Provides-Extra: mistralai
|
|
154
|
-
Provides-Extra: mssql
|
|
155
|
-
Provides-Extra: mysql
|
|
156
|
-
Provides-Extra: ollama
|
|
157
|
-
Provides-Extra: openai
|
|
158
|
-
Provides-Extra: opensearch
|
|
159
|
-
Provides-Extra: oracle
|
|
160
|
-
Provides-Extra: pgvector
|
|
161
|
-
Provides-Extra: pinecone
|
|
162
|
-
Provides-Extra: postgres
|
|
163
|
-
Provides-Extra: presto
|
|
164
|
-
Provides-Extra: qdrant
|
|
165
|
-
Provides-Extra: qianfan
|
|
166
|
-
Provides-Extra: servers
|
|
167
|
-
Provides-Extra: snowflake
|
|
168
|
-
Provides-Extra: test
|
|
169
|
-
Provides-Extra: vllm
|
|
170
|
-
Provides-Extra: weaviate
|
|
171
|
-
Provides-Extra: xinference-client
|
|
172
|
-
Provides-Extra: zhipuai
|
|
173
|
-
|
|
174
|
-
# Vanna 2.0+: Web-First, User-Aware Agent Framework
|
|
175
|
-
|
|
176
|
-
> [!WARNING]
|
|
177
|
-
> This version of Vanna is actively under development and may contain breaking changes until it is officially released to PyPI.
|
|
178
|
-
>
|
|
179
|
-
> To install while in development, use:
|
|
180
|
-
> ```bash
|
|
181
|
-
> pip install --force-reinstall --no-cache-dir 'vanna[flask,anthropic] @ git+https://github.com/vanna-ai/vanna.git@v2'
|
|
182
|
-
> ```
|
|
183
|
-
|
|
184
|
-
> [!IMPORTANT]
|
|
185
|
-
> If you're upgrading from an older version of Vanna, use the [Migration Guide](MIGRATION_GUIDE.md).
|
|
186
|
-
|
|
187
|
-
> **Turn natural language into data insights — with enterprise-grade security baked in**
|
|
188
|
-
|
|
189
|
-
Vanna is purpose-built for **data analytics** with **user awareness** as a first-class concern. Drop in our web component, connect your existing auth, and start querying data securely.
|
|
190
|
-
|
|
191
|
-
[](https://python.org)
|
|
192
|
-
[](LICENSE)
|
|
193
|
-
[](https://github.com/psf/black)
|
|
194
|
-
|
|
195
|
-

|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## Why Vanna?
|
|
200
|
-
|
|
201
|
-
```mermaid
|
|
202
|
-
graph LR
|
|
203
|
-
A[👤 User asks:<br/>'Show me Q4 sales'] --> B[🔐 Identity flows through<br/>every layer]
|
|
204
|
-
B --> C[🧰 Tools execute securely<br/>with permissions]
|
|
205
|
-
C --> D[📊 Rich UI streams back<br/>tables, charts, code]
|
|
206
|
-
|
|
207
|
-
style B fill:#FFD93D,stroke:#333,stroke-width:3px
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### What Makes Vanna Different
|
|
211
|
-
|
|
212
|
-
**Built for Production Data Analytics**
|
|
213
|
-
- **Pre-built web component + backend** — No need to build your own chat UI
|
|
214
|
-
- **User-aware at every layer** — Identity and permissions flow through the entire system
|
|
215
|
-
- **Rich streaming responses** — Tables, charts, SQL code blocks, not just text
|
|
216
|
-
- **Works with your existing auth** — Cookies, JWTs, session tokens
|
|
217
|
-
- **Enterprise security built-in** — Row-level security, audit logs, rate limiting
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
## Quick Start
|
|
224
|
-
|
|
225
|
-
### Installation
|
|
226
|
-
|
|
227
|
-
```bash
|
|
228
|
-
pip install vanna[anthropic] # or [openai]
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Basic Example
|
|
232
|
-
|
|
233
|
-
```python
|
|
234
|
-
from vanna import Agent, AgentConfig
|
|
235
|
-
from vanna.servers.fastapi import VannaFastAPIServer
|
|
236
|
-
from vanna.core.registry import ToolRegistry
|
|
237
|
-
from vanna.core.user import UserResolver, User, RequestContext
|
|
238
|
-
from vanna.integrations.anthropic import AnthropicLlmService
|
|
239
|
-
from vanna.tools import RunSqlTool
|
|
240
|
-
from vanna.integrations.sqlite import SqliteRunner
|
|
241
|
-
|
|
242
|
-
# 1. Define how to resolve users from requests
|
|
243
|
-
class SimpleUserResolver(UserResolver):
|
|
244
|
-
async def resolve_user(self, request_context: RequestContext) -> User:
|
|
245
|
-
# In production, validate cookies/JWTs here
|
|
246
|
-
user_id = request_context.get_cookie('user_id') or 'demo_user'
|
|
247
|
-
return User(id=user_id, group_memberships=['read_sales'])
|
|
248
|
-
|
|
249
|
-
# 2. Set up LLM and tools
|
|
250
|
-
llm = AnthropicLlmService(model="claude-sonnet-4-5")
|
|
251
|
-
tools = ToolRegistry()
|
|
252
|
-
tools.register(RunSqlTool(sql_runner=SqliteRunner(database_path="./data.db")))
|
|
253
|
-
|
|
254
|
-
# 3. Create agent
|
|
255
|
-
agent = Agent(
|
|
256
|
-
llm_service=llm,
|
|
257
|
-
tool_registry=tools,
|
|
258
|
-
user_resolver=SimpleUserResolver()
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
# 4. Create and run server
|
|
262
|
-
server = VannaFastAPIServer(agent)
|
|
263
|
-
app = server.create_app()
|
|
264
|
-
|
|
265
|
-
# Run with: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
|
266
|
-
# Visit http://localhost:8000 to see the web UI
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
---
|
|
270
|
-
|
|
271
|
-

|
|
272
|
-
|
|
273
|
-
---
|
|
274
|
-
|
|
275
|
-
## What Makes Vanna Unique
|
|
276
|
-
|
|
277
|
-
### 1. User-Aware by Design
|
|
278
|
-
|
|
279
|
-
Every layer of the system knows **who the user is** and **what they can access**.
|
|
280
|
-
|
|
281
|
-
```mermaid
|
|
282
|
-
sequenceDiagram
|
|
283
|
-
participant U as 👤 User
|
|
284
|
-
participant W as 🌐 Web Component
|
|
285
|
-
participant S as 🐍 Flask/FastAPI
|
|
286
|
-
participant R as 🪪 User Resolver
|
|
287
|
-
participant A as 🤖 Agent
|
|
288
|
-
participant T as 🧰 Tools
|
|
289
|
-
|
|
290
|
-
U->>W: "Show Q4 sales"
|
|
291
|
-
W->>S: POST /api/vanna/v2/chat_sse
|
|
292
|
-
S->>R: Extract user identity
|
|
293
|
-
R->>A: User(id=alice, group_memberships=[read_sales])
|
|
294
|
-
A->>A: Generate personalized system prompt
|
|
295
|
-
A->>T: Execute SQL tool (user-aware)
|
|
296
|
-
T->>T: Apply row-level security
|
|
297
|
-
T->>A: Return filtered results
|
|
298
|
-
A->>W: Stream: Table → Chart → Summary
|
|
299
|
-
W->>U: Display results
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
**Not just authentication — authorization at every step:**
|
|
303
|
-
|
|
304
|
-
- System prompt customized per user
|
|
305
|
-
- Tools check permissions before execution
|
|
306
|
-
- SQL queries filtered by row-level security
|
|
307
|
-
- Audit logs per user
|
|
308
|
-
- Rate limiting per user
|
|
309
|
-
|
|
310
|
-
### 2. Drop-in Web Component
|
|
311
|
-
|
|
312
|
-
```html
|
|
313
|
-
<!-- Works with any existing app -->
|
|
314
|
-
<vanna-chat
|
|
315
|
-
api-endpoint="/api/vanna/v2/chat_sse"
|
|
316
|
-
initial-message="What can I help you with?"
|
|
317
|
-
theme="dark">
|
|
318
|
-
</vanna-chat>
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
**Features:**
|
|
322
|
-
- Uses your existing cookies/JWTs (no new login system)
|
|
323
|
-
- Renders streaming tables, charts, SQL code blocks
|
|
324
|
-
- Responsive and customizable
|
|
325
|
-
- Framework-agnostic (works with React, Vue, plain HTML)
|
|
326
|
-
|
|
327
|
-
### 3. Purpose-Built for Data Analytics
|
|
328
|
-
|
|
329
|
-
**Out-of-the-box tools:**
|
|
330
|
-
- SQL generation and execution (with user permissions)
|
|
331
|
-
- Data visualization with Plotly
|
|
332
|
-
- File system operations (for coding agents)
|
|
333
|
-
- Python code execution (sandboxed)
|
|
334
|
-
|
|
335
|
-
```python
|
|
336
|
-
from vanna.tools import RunSqlTool, VisualizeDataTool
|
|
337
|
-
from vanna.integrations.sqlite import SqliteRunner
|
|
338
|
-
from vanna.integrations.local import LocalFileSystem
|
|
339
|
-
|
|
340
|
-
file_system = LocalFileSystem("./data")
|
|
341
|
-
|
|
342
|
-
tools.register(RunSqlTool(
|
|
343
|
-
sql_runner=SqliteRunner(database_path="./data.db"),
|
|
344
|
-
file_system=file_system
|
|
345
|
-
))
|
|
346
|
-
tools.register(VisualizeDataTool(file_system=file_system))
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### 4. Enterprise-Ready
|
|
350
|
-
|
|
351
|
-
| Feature | Description |
|
|
352
|
-
|---------|-------------|
|
|
353
|
-
| **Row-Level Security** | SQL tools respect database permissions |
|
|
354
|
-
| **Audit Logs** | Every query and tool call logged per user |
|
|
355
|
-
| **Rate Limiting** | Per-user token/request limits via lifecycle hooks |
|
|
356
|
-
| **Observability** | Built-in tracing and debugging hooks |
|
|
357
|
-
| **Conversation Management** | Persistent conversation storage |
|
|
358
|
-
| **Content Filtering** | Extensible filtering system |
|
|
359
|
-
|
|
360
|
-
---
|
|
361
|
-
|
|
362
|
-
## Architecture
|
|
363
|
-
|
|
364
|
-
```mermaid
|
|
365
|
-
graph TB
|
|
366
|
-
subgraph Frontend["🌐 Frontend"]
|
|
367
|
-
WC[Web Component<br/><vanna-chat>]
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
subgraph Backend["🐍 Python Server (Flask/FastAPI)"]
|
|
371
|
-
direction TB
|
|
372
|
-
SSE[SSE/WebSocket Handler]
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
subgraph Agent["🤖 User-Aware Agent"]
|
|
376
|
-
direction TB
|
|
377
|
-
UR[🪪 User Resolver<br/>YOUR auth system]
|
|
378
|
-
SP[⚙️ System Prompt<br/>Per-user customization]
|
|
379
|
-
LLM[🧠 LLM<br/>Claude/GPT/Gemini]
|
|
380
|
-
Tools[🧰 Tools<br/>SQL, Charts, Memory]
|
|
381
|
-
Comp[📄 UI Components<br/>Tables, Charts, Code]
|
|
382
|
-
|
|
383
|
-
UR --> SP
|
|
384
|
-
SP --> LLM
|
|
385
|
-
UR --> Tools
|
|
386
|
-
Tools <--> LLM
|
|
387
|
-
Tools --> Comp
|
|
388
|
-
LLM --> Comp
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
WC -->|User question<br/>+ cookies/JWT| SSE
|
|
392
|
-
SSE -->|Request context| UR
|
|
393
|
-
Comp -->|Streaming components| SSE
|
|
394
|
-
SSE -->|Progressive updates| WC
|
|
395
|
-
|
|
396
|
-
style UR fill:#95E1D3
|
|
397
|
-
style Tools fill:#FFD93D
|
|
398
|
-
style Comp fill:#C7F1FF
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
### Core Concepts
|
|
402
|
-
|
|
403
|
-
**1. User Resolver** — You define this!
|
|
404
|
-
|
|
405
|
-
```python
|
|
406
|
-
from vanna.core.user import UserResolver, User, RequestContext
|
|
407
|
-
|
|
408
|
-
class MyUserResolver(UserResolver):
|
|
409
|
-
async def resolve_user(self, request_context: RequestContext) -> User:
|
|
410
|
-
# Extract from your existing auth system
|
|
411
|
-
token = request_context.get_header('Authorization')
|
|
412
|
-
user_data = self.decode_jwt(token) # Your logic
|
|
413
|
-
|
|
414
|
-
return User(
|
|
415
|
-
id=user_data['id'],
|
|
416
|
-
email=user_data['email'],
|
|
417
|
-
group_memberships=user_data['groups'], # Key!
|
|
418
|
-
metadata={'role': user_data['role']}
|
|
419
|
-
)
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
**2. User-Aware Tools** — Check permissions automatically
|
|
423
|
-
|
|
424
|
-
```python
|
|
425
|
-
from vanna.core.tool import Tool, ToolContext, ToolResult
|
|
426
|
-
from pydantic import BaseModel, Field
|
|
427
|
-
from typing import Type
|
|
428
|
-
|
|
429
|
-
class QueryArgs(BaseModel):
|
|
430
|
-
query: str = Field(description="SQL query to execute")
|
|
431
|
-
|
|
432
|
-
class CustomSQLTool(Tool[QueryArgs]):
|
|
433
|
-
@property
|
|
434
|
-
def name(self) -> str:
|
|
435
|
-
return "query_database"
|
|
436
|
-
|
|
437
|
-
@property
|
|
438
|
-
def description(self) -> str:
|
|
439
|
-
return "Execute a SQL query against the database"
|
|
440
|
-
|
|
441
|
-
@property
|
|
442
|
-
def access_groups(self) -> list[str]:
|
|
443
|
-
return ["read_sales"] # Only users in this group can use this tool
|
|
444
|
-
|
|
445
|
-
def get_args_schema(self) -> Type[QueryArgs]:
|
|
446
|
-
return QueryArgs
|
|
447
|
-
|
|
448
|
-
async def execute(self, context: ToolContext, args: QueryArgs) -> ToolResult:
|
|
449
|
-
user = context.user # Automatically injected
|
|
450
|
-
|
|
451
|
-
# Apply row-level security
|
|
452
|
-
filtered_query = self.add_user_filters(args.query, user)
|
|
453
|
-
results = await self.db.execute(filtered_query)
|
|
454
|
-
|
|
455
|
-
return ToolResult(
|
|
456
|
-
success=True,
|
|
457
|
-
result_for_llm=str(results)
|
|
458
|
-
)
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
**3. Streaming UI Components**
|
|
462
|
-
|
|
463
|
-
```python
|
|
464
|
-
async for component in agent.send_message(request_context=ctx, message="Show sales"):
|
|
465
|
-
# Rich component: structured data (tables, charts, status cards)
|
|
466
|
-
print(type(component.rich_component).__name__)
|
|
467
|
-
|
|
468
|
-
# Simple component: plain text fallback
|
|
469
|
-
print(component.simple_component.text)
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
Output:
|
|
473
|
-
```
|
|
474
|
-
StatusBarUpdateComponent # "Processing..."
|
|
475
|
-
TaskTrackerUpdateComponent # "Load conversation context"
|
|
476
|
-
RichTextComponent # "Let me query the sales data..."
|
|
477
|
-
StatusCardComponent # "Executing run_sql"
|
|
478
|
-
DataFrameComponent # Tabular results
|
|
479
|
-
RichTextComponent # "Here are your top customers..."
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
---
|
|
483
|
-
|
|
484
|
-
## Add to Existing FastAPI App
|
|
485
|
-
|
|
486
|
-
If you already have a FastAPI application, you can add Vanna as additional routes:
|
|
487
|
-
|
|
488
|
-
```python
|
|
489
|
-
from fastapi import FastAPI
|
|
490
|
-
from vanna import Agent, AgentConfig
|
|
491
|
-
from vanna.servers.base import ChatHandler
|
|
492
|
-
from vanna.servers.fastapi.routes import register_chat_routes
|
|
493
|
-
from vanna.core.registry import ToolRegistry
|
|
494
|
-
from vanna.core.user import UserResolver, User, RequestContext
|
|
495
|
-
from vanna.integrations.anthropic import AnthropicLlmService
|
|
496
|
-
from vanna.tools import RunSqlTool
|
|
497
|
-
from vanna.integrations.sqlite import SqliteRunner
|
|
498
|
-
|
|
499
|
-
# Your existing FastAPI app
|
|
500
|
-
app = FastAPI()
|
|
501
|
-
|
|
502
|
-
# Your existing routes
|
|
503
|
-
@app.get('/api/users')
|
|
504
|
-
async def get_users():
|
|
505
|
-
return {'users': [...]}
|
|
506
|
-
|
|
507
|
-
@app.get('/api/products')
|
|
508
|
-
async def get_products():
|
|
509
|
-
return {'products': [...]}
|
|
510
|
-
|
|
511
|
-
# Add Vanna agent
|
|
512
|
-
class CookieUserResolver(UserResolver):
|
|
513
|
-
async def resolve_user(self, request_context: RequestContext) -> User:
|
|
514
|
-
user_id = request_context.get_cookie('user_id') or 'anonymous'
|
|
515
|
-
role = request_context.get_cookie('role') or 'guest'
|
|
516
|
-
|
|
517
|
-
groups = []
|
|
518
|
-
if role == 'admin':
|
|
519
|
-
groups = ['read_sales', 'read_confidential', 'admin']
|
|
520
|
-
elif role == 'analyst':
|
|
521
|
-
groups = ['read_sales']
|
|
522
|
-
|
|
523
|
-
return User(id=user_id, group_memberships=groups)
|
|
524
|
-
|
|
525
|
-
# Set up agent
|
|
526
|
-
llm = AnthropicLlmService(model="claude-sonnet-4-5")
|
|
527
|
-
tools = ToolRegistry()
|
|
528
|
-
tools.register(RunSqlTool(sql_runner=SqliteRunner(database_path="./data.db")))
|
|
529
|
-
|
|
530
|
-
agent = Agent(
|
|
531
|
-
llm_service=llm,
|
|
532
|
-
tool_registry=tools,
|
|
533
|
-
user_resolver=CookieUserResolver()
|
|
534
|
-
)
|
|
535
|
-
|
|
536
|
-
# Add Vanna routes to your existing app
|
|
537
|
-
chat_handler = ChatHandler(agent)
|
|
538
|
-
register_chat_routes(app, chat_handler)
|
|
539
|
-
|
|
540
|
-
# Run with: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
This adds these endpoints to your existing FastAPI app:
|
|
544
|
-
- `GET /` - Vanna web component UI (you may want to change this)
|
|
545
|
-
- `POST /api/vanna/v2/chat_sse` - Server-Sent Events streaming
|
|
546
|
-
- `POST /api/vanna/v2/chat_poll` - Polling endpoint
|
|
547
|
-
- `GET /health` - Health check
|
|
548
|
-
|
|
549
|
-
To customize the routes or serve the UI at a different path, see the [server configuration docs](https://docs.vanna.ai).
|
|
550
|
-
|
|
551
|
-
---
|
|
552
|
-
|
|
553
|
-
## Custom Tools
|
|
554
|
-
|
|
555
|
-
Create custom tools by extending the `Tool` base class:
|
|
556
|
-
|
|
557
|
-
```python
|
|
558
|
-
from vanna.core.tool import Tool, ToolContext, ToolResult
|
|
559
|
-
from vanna.components import UiComponent, NotificationComponent, SimpleTextComponent, ComponentType
|
|
560
|
-
from pydantic import BaseModel, Field
|
|
561
|
-
from typing import Type
|
|
562
|
-
|
|
563
|
-
class EmailArgs(BaseModel):
|
|
564
|
-
recipient: str = Field(description="Email recipient")
|
|
565
|
-
subject: str = Field(description="Email subject")
|
|
566
|
-
body: str = Field(description="Email body")
|
|
567
|
-
|
|
568
|
-
class EmailTool(Tool[EmailArgs]):
|
|
569
|
-
@property
|
|
570
|
-
def name(self) -> str:
|
|
571
|
-
return "send_email"
|
|
572
|
-
|
|
573
|
-
@property
|
|
574
|
-
def description(self) -> str:
|
|
575
|
-
return "Send an email to a user"
|
|
576
|
-
|
|
577
|
-
@property
|
|
578
|
-
def access_groups(self) -> list[str]:
|
|
579
|
-
return ["send_email"] # Only users in this group can use this tool
|
|
580
|
-
|
|
581
|
-
def get_args_schema(self) -> Type[EmailArgs]:
|
|
582
|
-
return EmailArgs
|
|
583
|
-
|
|
584
|
-
async def execute(self, context: ToolContext, args: EmailArgs) -> ToolResult:
|
|
585
|
-
user = context.user
|
|
586
|
-
|
|
587
|
-
# Check domain restrictions
|
|
588
|
-
if not args.recipient.endswith('@company.com'):
|
|
589
|
-
error_msg = "Can only send to company email addresses"
|
|
590
|
-
return ToolResult(
|
|
591
|
-
success=False,
|
|
592
|
-
result_for_llm=error_msg,
|
|
593
|
-
error=error_msg,
|
|
594
|
-
ui_component=UiComponent(
|
|
595
|
-
rich_component=NotificationComponent(
|
|
596
|
-
type=ComponentType.NOTIFICATION,
|
|
597
|
-
level="error",
|
|
598
|
-
message=error_msg
|
|
599
|
-
),
|
|
600
|
-
simple_component=SimpleTextComponent(text=error_msg)
|
|
601
|
-
)
|
|
602
|
-
)
|
|
603
|
-
|
|
604
|
-
# Send email (your logic)
|
|
605
|
-
await self.email_service.send(
|
|
606
|
-
from_email=user.email,
|
|
607
|
-
to=args.recipient,
|
|
608
|
-
subject=args.subject,
|
|
609
|
-
body=args.body
|
|
610
|
-
)
|
|
611
|
-
|
|
612
|
-
success_msg = f"Email sent to {args.recipient}"
|
|
613
|
-
return ToolResult(
|
|
614
|
-
success=True,
|
|
615
|
-
result_for_llm=success_msg,
|
|
616
|
-
ui_component=UiComponent(
|
|
617
|
-
rich_component=NotificationComponent(
|
|
618
|
-
type=ComponentType.NOTIFICATION,
|
|
619
|
-
level="success",
|
|
620
|
-
message=success_msg
|
|
621
|
-
),
|
|
622
|
-
simple_component=SimpleTextComponent(text=success_msg)
|
|
623
|
-
)
|
|
624
|
-
)
|
|
625
|
-
|
|
626
|
-
# Register tool
|
|
627
|
-
tools.register(EmailTool())
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
---
|
|
631
|
-
|
|
632
|
-
## Configuration
|
|
633
|
-
|
|
634
|
-
### Agent Configuration
|
|
635
|
-
|
|
636
|
-
```python
|
|
637
|
-
from vanna import AgentConfig
|
|
638
|
-
|
|
639
|
-
config = AgentConfig(
|
|
640
|
-
max_tool_iterations=10, # Max tool calls per message
|
|
641
|
-
stream_responses=True, # Enable streaming
|
|
642
|
-
temperature=0.7, # LLM temperature
|
|
643
|
-
include_thinking_indicators=True, # Show "Thinking..." states
|
|
644
|
-
auto_save_conversations=True, # Auto-persist conversations
|
|
645
|
-
max_tokens=None # Maximum response tokens
|
|
646
|
-
)
|
|
647
|
-
|
|
648
|
-
agent = Agent(
|
|
649
|
-
llm_service=llm,
|
|
650
|
-
tool_registry=tools,
|
|
651
|
-
user_resolver=user_resolver,
|
|
652
|
-
config=config
|
|
653
|
-
)
|
|
654
|
-
```
|
|
655
|
-
|
|
656
|
-
### Environment Variables
|
|
657
|
-
|
|
658
|
-
```bash
|
|
659
|
-
# Anthropic
|
|
660
|
-
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
661
|
-
export ANTHROPIC_MODEL="claude-sonnet-4-5"
|
|
662
|
-
|
|
663
|
-
# OpenAI
|
|
664
|
-
export OPENAI_API_KEY="sk-..."
|
|
665
|
-
export OPENAI_MODEL="gpt-5"
|
|
666
|
-
|
|
667
|
-
# Database
|
|
668
|
-
export DATABASE_URL="postgresql://localhost/mydb"
|
|
669
|
-
```
|
|
670
|
-
|
|
671
|
-
---
|
|
672
|
-
|
|
673
|
-
## Advanced Features
|
|
674
|
-
|
|
675
|
-
### 1. Conversation Storage
|
|
676
|
-
|
|
677
|
-
```python
|
|
678
|
-
from vanna.integrations.local import MemoryConversationStore
|
|
679
|
-
|
|
680
|
-
# Use in-memory storage (default)
|
|
681
|
-
store = MemoryConversationStore()
|
|
682
|
-
|
|
683
|
-
agent = Agent(
|
|
684
|
-
llm_service=llm,
|
|
685
|
-
tool_registry=tools,
|
|
686
|
-
user_resolver=user_resolver,
|
|
687
|
-
conversation_store=store
|
|
688
|
-
)
|
|
689
|
-
|
|
690
|
-
# List user's conversations
|
|
691
|
-
alice = User(id="alice")
|
|
692
|
-
conversations = await store.list_conversations(user=alice)
|
|
693
|
-
|
|
694
|
-
# Get conversation history
|
|
695
|
-
conversation = await store.get_conversation(
|
|
696
|
-
conversation_id="conv_123",
|
|
697
|
-
user=alice
|
|
698
|
-
)
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
### 2. Lifecycle Hooks
|
|
702
|
-
|
|
703
|
-
```python
|
|
704
|
-
from vanna.core.lifecycle import LifecycleHook
|
|
705
|
-
|
|
706
|
-
class QuotaCheckHook(LifecycleHook):
|
|
707
|
-
async def before_message(self, user: User, message: str) -> str:
|
|
708
|
-
# Check if user has quota remaining
|
|
709
|
-
if not await self.check_quota(user.id):
|
|
710
|
-
raise Exception("Quota exceeded")
|
|
711
|
-
return message
|
|
712
|
-
|
|
713
|
-
async def after_tool(self, result: ToolResult) -> ToolResult:
|
|
714
|
-
# Log tool execution
|
|
715
|
-
await self.log_tool_execution(result)
|
|
716
|
-
return result
|
|
717
|
-
|
|
718
|
-
agent = Agent(
|
|
719
|
-
llm_service=llm,
|
|
720
|
-
tool_registry=tools,
|
|
721
|
-
user_resolver=user_resolver,
|
|
722
|
-
lifecycle_hooks=[QuotaCheckHook()]
|
|
723
|
-
)
|
|
724
|
-
```
|
|
725
|
-
|
|
726
|
-
### 3. LLM Middlewares
|
|
727
|
-
|
|
728
|
-
```python
|
|
729
|
-
from vanna.core.middleware import LlmMiddleware
|
|
730
|
-
from vanna.core.llm import LlmRequest, LlmResponse
|
|
731
|
-
|
|
732
|
-
class CachingMiddleware(LlmMiddleware):
|
|
733
|
-
async def before_llm_request(self, request: LlmRequest) -> LlmRequest:
|
|
734
|
-
# Check cache before sending to LLM
|
|
735
|
-
cached = await self.cache.get(request)
|
|
736
|
-
if cached:
|
|
737
|
-
return cached
|
|
738
|
-
return request
|
|
739
|
-
|
|
740
|
-
async def after_llm_response(
|
|
741
|
-
self,
|
|
742
|
-
request: LlmRequest,
|
|
743
|
-
response: LlmResponse
|
|
744
|
-
) -> LlmResponse:
|
|
745
|
-
# Cache the response
|
|
746
|
-
await self.cache.set(request, response)
|
|
747
|
-
return response
|
|
748
|
-
|
|
749
|
-
agent = Agent(
|
|
750
|
-
llm_service=llm,
|
|
751
|
-
tool_registry=tools,
|
|
752
|
-
user_resolver=user_resolver,
|
|
753
|
-
llm_middlewares=[CachingMiddleware()]
|
|
754
|
-
)
|
|
755
|
-
```
|
|
756
|
-
|
|
757
|
-
### 4. Observability
|
|
758
|
-
|
|
759
|
-
```python
|
|
760
|
-
from vanna.core.observability import ObservabilityProvider
|
|
761
|
-
|
|
762
|
-
class LoggingProvider(ObservabilityProvider):
|
|
763
|
-
async def create_span(self, name: str, attributes: dict):
|
|
764
|
-
print(f"Starting: {name}")
|
|
765
|
-
return Span(name, attributes)
|
|
766
|
-
|
|
767
|
-
async def record_metric(self, name: str, value: float, unit: str, tags: dict):
|
|
768
|
-
print(f"Metric: {name} = {value}{unit}")
|
|
769
|
-
|
|
770
|
-
agent = Agent(
|
|
771
|
-
llm_service=llm,
|
|
772
|
-
tool_registry=tools,
|
|
773
|
-
user_resolver=user_resolver,
|
|
774
|
-
observability_provider=LoggingProvider()
|
|
775
|
-
)
|
|
776
|
-
```
|
|
777
|
-
|
|
778
|
-
### 5. Context Enrichers
|
|
779
|
-
|
|
780
|
-
```python
|
|
781
|
-
from vanna.core.enricher import ToolContextEnricher
|
|
782
|
-
from vanna.core.tool import ToolContext
|
|
783
|
-
|
|
784
|
-
class UserMetadataEnricher(ToolContextEnricher):
|
|
785
|
-
async def enrich_context(self, context: ToolContext) -> ToolContext:
|
|
786
|
-
# Add additional user metadata from database
|
|
787
|
-
user_metadata = await self.db.get_user_metadata(context.user.id)
|
|
788
|
-
context.user.metadata.update(user_metadata)
|
|
789
|
-
return context
|
|
790
|
-
|
|
791
|
-
agent = Agent(
|
|
792
|
-
llm_service=llm,
|
|
793
|
-
tool_registry=tools,
|
|
794
|
-
user_resolver=user_resolver,
|
|
795
|
-
context_enrichers=[UserMetadataEnricher()]
|
|
796
|
-
)
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
### 6. LLM Context Enhancers
|
|
800
|
-
|
|
801
|
-
Enhance LLM system prompts and messages with additional context (e.g., from memory, RAG, documentation):
|
|
802
|
-
|
|
803
|
-
```python
|
|
804
|
-
from vanna.core.enhancer import LlmContextEnhancer, DefaultLlmContextEnhancer
|
|
805
|
-
from vanna.core.llm import LlmMessage
|
|
806
|
-
from vanna.core.user import User
|
|
807
|
-
|
|
808
|
-
class CustomLlmContextEnhancer(LlmContextEnhancer):
|
|
809
|
-
async def enhance_system_prompt(
|
|
810
|
-
self,
|
|
811
|
-
system_prompt: str,
|
|
812
|
-
user_message: str,
|
|
813
|
-
user: User
|
|
814
|
-
) -> str:
|
|
815
|
-
# Add relevant context to system prompt based on user message
|
|
816
|
-
relevant_docs = await self.search_documentation(user_message)
|
|
817
|
-
return system_prompt + f"\n\nRelevant documentation:\n{relevant_docs}"
|
|
818
|
-
|
|
819
|
-
async def enhance_user_messages(
|
|
820
|
-
self,
|
|
821
|
-
messages: list[LlmMessage],
|
|
822
|
-
user: User
|
|
823
|
-
) -> list[LlmMessage]:
|
|
824
|
-
# Optionally modify user messages
|
|
825
|
-
return messages
|
|
826
|
-
|
|
827
|
-
# Use default implementation (uses AgentMemory for RAG)
|
|
828
|
-
agent = Agent(
|
|
829
|
-
llm_service=llm,
|
|
830
|
-
tool_registry=tools,
|
|
831
|
-
user_resolver=user_resolver,
|
|
832
|
-
agent_memory=agent_memory,
|
|
833
|
-
llm_context_enhancer=DefaultLlmContextEnhancer(agent_memory) # Default if not provided
|
|
834
|
-
)
|
|
835
|
-
|
|
836
|
-
# Or use custom implementation
|
|
837
|
-
agent = Agent(
|
|
838
|
-
llm_service=llm,
|
|
839
|
-
tool_registry=tools,
|
|
840
|
-
user_resolver=user_resolver,
|
|
841
|
-
llm_context_enhancer=CustomLlmContextEnhancer()
|
|
842
|
-
)
|
|
843
|
-
```
|
|
844
|
-
|
|
845
|
-
**Key difference:**
|
|
846
|
-
- **Context Enrichers** (ToolContextEnricher): Enrich tool execution context
|
|
847
|
-
- **LLM Context Enhancers** (LlmContextEnhancer): Enhance LLM prompts and messages
|
|
848
|
-
|
|
849
|
-
---
|
|
850
|
-
|
|
851
|
-
## When to Use Vanna
|
|
852
|
-
|
|
853
|
-
**Vanna is ideal for:**
|
|
854
|
-
- Building data analytics applications with natural language interfaces
|
|
855
|
-
- Applications requiring user-aware permissions throughout
|
|
856
|
-
- Teams that want a pre-built web component + backend integration
|
|
857
|
-
- Enterprise environments with strict security requirements
|
|
858
|
-
- Use cases needing rich streaming responses (tables, charts, SQL)
|
|
859
|
-
- Integrating with existing authentication systems
|
|
860
|
-
|
|
861
|
-
---
|
|
862
|
-
|
|
863
|
-
## Documentation
|
|
864
|
-
|
|
865
|
-
- **Migration Guide**: [Migrating from Vanna 1.x to 2.0+](MIGRATION_GUIDE.md)
|
|
866
|
-
- **GitHub Discussions**: [GitHub Discussions](https://github.com/vanna-ai/vanna/discussions)
|
|
867
|
-
- **Email**: support@vanna.ai
|
|
868
|
-
|