Vanna AI

repository·main·Indexed 12 days ago

https://github.com/vanna-ai/vanna

A natural language to SQL engine for enterprise production environments. Vanna 2.0 allows developers to generate SQL queries from natural language and turn questions into data insights via streaming tables, charts, and summaries. It features an Agent-based API, built-in user-aware permissions and security, and a rich component system including a pre-built <vanna-chat> web component and <plotly-chart> for data visualization.

Tokens
29.2K
Snippets
79
Records
103
Agent score
98%

What's inside Vanna

  1. Understand the Webcomponent Architecture

    main

    The Vanna Webcomponent follows a specific data and interaction flow:

    Backend Flow (SSE)

    1. FastAPI receives a POST to /api/vanna/v2/chat_sse.
    2. An async generator creates ChatStreamChunk objects.
    3. Chunks are serialized to Server-Sent Events (SSE) format: `data: {json}

    . 4. The stream terminates with data: [DONE]

    `.

    Frontend Flow

    1. The <vanna-chat> component opens an SSE connection.
    2. The ComponentManager parses incoming JSON chunks.
    3. The ComponentRegistry renders HTML elements into the Shadow DOM.
    4. A MutationObserver detects new elements and updates the visual checklist.

    Button Action Flow

    1. User clicks a button in the UI.
    2. The button's action property is sent as a new message via POST to /api/vanna/v2/chat_sse.
    3. The backend's handle_action_message() processes the action and streams new response components back to the client.
  2. Context strategies to maximize SQL generation accuracy

    main

    The accuracy of SQL generated by an LLM is primarily determined by the context provided in the prompt. The research identifies three main context strategies, ranked from least to most effective:

    1. Schema only: Providing only the database schema (using DDL statements) in the context window.
    2. Static examples: Providing a fixed set of example SQL queries in the context window.
    3. Contextually relevant examples (Winner): Using a vector search (based on embeddings) to find and inject the most relevant context—such as specific DDL, documentation, or prior SQL queries—into the context window based on the user's question.

    Combining schema definitions, documentation, and prior SQL queries via relevance search can improve accuracy from ~3% to ~80%.

  3. Compare Vanna 0.x and Vanna 2.0+ Architectures

    main

    Understanding the fundamental shifts between the legacy 0.x version and the current 2.0+ framework:

    FeatureVanna 0.xVanna 2.0+
    User ContextNoneUser object with permissions flows through entire system
    Interaction ModelDirect method calls (vn.ask())Agent-based with streaming components
    ToolsMonolithic methodsModular Tool classes with schemas
    ResponsesPlain text/DataFramesRich UI components (tables, charts, code)
    Trainingvn.train() with vector DBSystem prompts, context enrichers, RAG tools
    Database Connectionvn.connect_to_postgres()SqlRunner implementations as dependencies
    Web UINone (custom implementation)Built-in web component + backend
    StreamingNoneServer-Sent Events by default
    PermissionsNoneGroup-based access control on tools
    Audit LogsNoneBuilt-in audit logging system
  4. How Vanna works: The RAG workflow

    main

    Vanna uses a Retrieval-Augmented Generation (RAG) framework to convert natural language questions into SQL queries. The workflow consists of two main steps:

    1. Train a RAG "model" on your data: You provide metadata (DDL, documentation, or existing SQL) which is stored in a vector database.
    2. Ask questions: You ask questions in natural language, and Vanna retrieves relevant metadata to generate and execute SQL queries.

    This approach is portable across LLMs, easier to update than fine-tuning, and more cost-effective.

  5. Understanding Vanna nomenclature

    main

    To use Vanna effectively, distinguish between these two core concepts:

    • Foundational Model: The underlying Large Language Model (LLM) used for reasoning (e.g., GPT-4, Claude, Llama 2).
    • Context Model (aka Vanna Model): The layer that sits on top of the LLM. It manages the retrieval of relevant schema, documentation, and SQL examples to provide the LLM with the necessary context.
    • Training: In the context of Vanna, 'training' refers to adding information to the Context Model (populating the vector database), not fine-tuning the Foundational Model.
  6. The 5-step architecture for AI SQL generation

    main

    To build a system that converts natural language questions into SQL, follow this five-step architectural pattern:

    1. Question: Define the business question in plain English.
    2. Prompt: Construct a prompt that includes the question and instructions for the LLM.
    3. Generate SQL: Send the prompt to an LLM via an API to receive the generated SQL.
    4. Run SQL: Execute the generated SQL against your database.
    5. Validate results: Verify that the returned data matches the expected business outcome.

    This process highlights that the LLM's ability to generate correct SQL is heavily dependent on the context provided during the prompt stage.

    # 1. Question
    question = "how many clients are there in germany"
    
    # 2. Prompt
    prompt = f"""
    Write a SQL statement for the following question:
    {question}
    """
    
    # 3. Generate SQL
    sql = llm.api(api_key=api_key, prompt=prompt, parameters=parameters)
    
    # 4. Run SQL
    df = db.conn.execute(sql)
    
    # 5. Validate results
    # (Manual or automated evaluation of df)
  7. How User-Awareness and Tools work together

    main

    Vanna 2.0 uses a multi-layered approach to security and identity:

    1. User Resolver: Extracts identity from the request (e.g., JWT/Cookies).
    2. User-Aware Tools: When an agent executes a tool, the tool checks the user's group_memberships against the tool's access_groups.
    3. Row-Level Security: Tools (like RunSqlTool) can use the user identity to apply SQL filters, ensuring users only see data they are permitted to access.
    4. Streaming UI: The backend streams structured components (Tables, Charts, Summaries) to the <vanna-chat> component based on the tool outputs.
  8. Why providing context is necessary for SQL generation

    main

    Standard LLMs (like base ChatGPT) lack knowledge of an organization's unique data structures and schemas. If you ask an LLM to write SQL without providing the schema, it will often hallucinate table and column names (e.g., using revenue_table instead of the actual table name), leading to execution errors.

    To prevent this, you must provide the LLM with the specific context of your database, such as:

    • DDL (Data Definition Language): To define table and column names.
    • Documentation: To explain business logic or column meanings.
    • Prior SQL queries: To show the LLM how similar questions have been answered correctly in the past.
  9. Use the Test Suite for Webcomponent Pruning

    main

    The test suite is designed to ensure that removing unused code (pruning) does not break functionality. Follow this iterative workflow:

    1. Run baseline test: Start the backend in realistic mode and verify all 19 components render with 0 errors in the browser.
    2. Identify cruft: Look for unused imports, dead code paths, or deprecated components.
    3. Remove one piece of cruft: Delete a single unused import or utility.
    4. Rebuild: Run npm run build.
    5. Refresh and Verify: Reload the browser and run the test again.
      • If the checklist is Green (no errors): The change is safe; continue pruning.
      • If the checklist is Red (errors detected): Revert the change; that code was required.
  10. Migrate from Vanna 0.x to Vanna 2.0+ using the Legacy Adapter

    main

    If you have an existing Vanna 0.x codebase and want to adopt the Vanna 2.0+ agent framework with minimal changes, use the LegacyVannaAdapter. This strategy allows you to keep your existing VannaBase instance (including database connections and training data) while gaining access to new features like the Web UI and streaming responses.

    Key steps:

    1. Install Vanna 2.0+ with necessary extras: pip install 'vanna[flask,anthropic]' (adjust extras based on your provider).
    2. Implement a UserResolver (required in 2.0+).
    3. Wrap your existing vn object with LegacyVannaAdapter(vn).
    4. Initialize an Agent using an llm_service and the adapter as the tool_registry.
    5. Run the VannaFastAPIServer.

    What the adapter provides:

    • Wraps vn.run_sql() as the run_sql tool.
    • Exposes training data via search_saved_correct_tool_uses.
    • Allows admins to save new training data via save_question_tool_args.
    from vanna import Agent, AgentConfig
    from vanna.servers.fastapi import VannaFastAPIServer
    from vanna.core.user import UserResolver, User, RequestContext
    from vanna.legacy.adapter import LegacyVannaAdapter
    from vanna.integrations.anthropic import AnthropicLlmService
    
    # 1. Your existing 0.x object
    # vn = MyVanna(config={'model': 'gpt-4', 'api_key': 'your-key'})
    # vn.connect_to_postgres(...)
    
    # 2. Define required UserResolver
    class SimpleUserResolver(UserResolver):
        async def resolve_user(self, request_context: RequestContext) -> User:
            user_email = request_context.get_cookie('vanna_email')
            if not user_email:
                raise ValueError("Missing 'vanna_email' cookie")
            return User(id=user_email, email=user_email, group_memberships=['user'])
    
    # 3. Wrap existing vn with adapter
    tools = LegacyVannaAdapter(vn)
    
    # 4. Setup LLM and Agent
    llm = AnthropicLlmService(model="claude-haiku-4-5", api_key="YOUR_KEY")
    agent = Agent(
        llm_service=llm,
        tool_registry=tools,
        user_resolver=SimpleUserResolver(),
        config=AgentConfig()
    )
    
    # 5. Run server
    server = VannaFastAPIServer(agent)
    if __name__ == "__main__":
        server.run(host='0.0.0.0', port=8000)
  11. Production Setup with FastAPI and Authentication

    main

    To deploy Vanna in production with your own authentication system, you need to implement a UserResolver to extract user identity (from cookies, JWTs, etc.) and register Vanna's chat routes with your FastAPI application. This ensures that every query is user-aware and can be filtered by permissions.

    from fastapi import FastAPI
    from vanna import Agent
    from vanna.servers.fastapi.routes import register_chat_routes
    from vanna.servers.base import ChatHandler
    from vanna.core.user import UserResolver, User, RequestContext
    from vanna.integrations.anthropic import AnthropicLlmService
    from vanna.tools import RunSqlTool
    from vanna.integrations.sqlite import SqliteRunner
    from vanna.core.registry import ToolRegistry
    
    # Your existing FastAPI app
    app = FastAPI()
    
    # 1. Define your user resolver (using YOUR auth system)
    class MyUserResolver(UserResolver):
        async def resolve_user(self, request_context: RequestContext) -> User:
            # Extract from cookies, JWTs, or session
            token = request_context.get_header('Authorization')
            user_data = self.decode_jwt(token)  # Your existing logic
    
            return User(
                id=user_data['id'],
                email=user_data['email'],
                group_memberships=user_data['groups']  # Used for permissions
            )
    
    # 2. Set up agent with tools
    llm = AnthropicLlmService(model="claude-sonnet-4-5")
    tools = ToolRegistry()
    tools.register(RunSqlTool(sql_runner=SqliteRunner("./data.db")))
    
    agent = Agent(
        llm_service=llm,
        tool_registry=tools,
        user_resolver=MyUserResolver()
    )
    
    # 3. Add Vanna routes to your app
    chat_handler = ChatHandler(agent)
    register_chat_routes(app, chat_handler)