Stay Hungry,Stay Foolish!

VANNA -- Vanna AI Demo App with Qdrant and OpenRouter

Vanna AI Demo App with Qdrant and OpenRouter

https://github.com/fanqingsong/vanna-qdrant-openrouter-demo-app

A Flask-based web application that demonstrates natural language to SQL query conversion using Vanna AI, with Qdrant as the vector store and OpenRouter for LLM capabilities. This application allows users to ask questions in natural language and get SQL queries and their results from a Snowflake database.

Features

  • Natural language to SQL query conversion
  • Vector storage with Qdrant for efficient similarity search
  • Integration with OpenRouter for LLM capabilities
  • Snowflake database integration
  • RESTful API endpoints for:
    • Generating SQL queries from natural language questions
    • Executing SQL queries
    • Managing training data
    • Retrieving available questions
    • Vector collection management 

The application will be available at http://localhost:8000.

API Endpoints

  • GET /: Health check endpoint
  • GET /api/collections: Get all vector collections
  • GET /api/questions: Get suggested questions
  • POST /api/generate-sql: Generate SQL from natural language
  • POST /api/run-sql: Execute SQL query
  • GET /api/training-data: Get current training data
  • DELETE /api/remove-all-training-data: Remove all training data
  • POST /api/ingest-training-data-from-file: Add new training data

 

from flask import Blueprint, jsonify, request
from app.models.responses import success_response, error_response
from app.services.question_service import QuestionService
from app.services.training_service import TrainingService
from app.api.validators.question_validators import QuestionValidator
import json

question_bp = Blueprint('questions', __name__)

@question_bp.route('/collections')
def get_collections():
    try:
        collections = QuestionService.get_collections()
        return jsonify(success_response(collections))
    except Exception as e:
        return jsonify(error_response(str(e))), 500

@question_bp.route('/questions')
def get_questions():
    try:
        questions = QuestionService.get_questions()
        return jsonify(success_response(questions))
    except Exception as e:
        return jsonify(error_response(str(e))), 500

@question_bp.route('/ask', methods=['POST'])
def ask_question():
    try:
        data = request.get_json()
        error = QuestionValidator.validate_question_request(data)
        if error:
            return jsonify(error_response(error)), 400

        answer = QuestionService.ask_question(question=data['question'])
        return jsonify(success_response(answer))
    except Exception as e:
        return jsonify(error_response(str(e))), 500

@question_bp.route('/generate-sql', methods=['POST'])
def generate_sql():
    try:
        data = request.get_json()
        error = QuestionValidator.validate_question_request(data)
        if error:
            return jsonify(error_response(error)), 400

        sql = QuestionService.generate_sql_query(question=data['question'])
        return jsonify(success_response(sql))
    except Exception as e:
        return jsonify(error_response(str(e))), 500

@question_bp.route('/run-sql', methods=['POST'])
def run_sql():
    try:
        data = request.get_json()
        error = QuestionValidator.validate_sql_request(data)
        if error:
            return jsonify(error_response(error)), 400

        df = QuestionService.execute_sql(sql=data['sql'])
        return jsonify(success_response(json.loads(df.head(10).to_json(orient="records"))))
    except Exception as e:
        return jsonify(error_response(str(e))), 500 

@question_bp.route('/training-data')
def get_training_data():
    try:
        data = TrainingService.get_training_data()
        return jsonify(success_response(json.loads(data.head(10).to_json(orient="records"))))
    except Exception as e:
        return jsonify(error_response(str(e))), 500

@question_bp.route('/remove-all-training-data', methods=['DELETE'])
def remove_all_training_data():
    try:
        TrainingService.remove_all_training_data()
        return jsonify(success_response("Training data removed"))
    except Exception as e:
        return jsonify(error_response(str(e))), 500

@question_bp.route('/add-training-data', methods=['POST'])
def add_training_data():
    try:
        data = request.get_json()
        error = QuestionValidator.validate_training_data_request(data)
        if error:
            return jsonify(error_response(error)), 400

        TrainingService.add_training_data(
            question=data['question'],
            sql=data['sql'],
            ddl=data['ddl']
        )
        return jsonify(success_response("Training data added"))
    except Exception as e:
        return jsonify(error_response(str(e))), 500

@question_bp.route('/ingest-training-data-from-file', methods=['POST'])
def ingest_training_data_from_file():
    try:
        result = TrainingService.ingest_training_data_from_file('app/training_data/question-sql-pairs.json')
        return jsonify(success_response(result))
    except Exception as e:
        return jsonify(error_response(str(e))), 500

 

posted @ 2025-03-12 09:16  lightsong  阅读(210)  评论(0)    收藏  举报
千山鸟飞绝,万径人踪灭