You are currently viewing Module 4: Interactive AI documentation search with DeepSeek and Flask

Module 4: Interactive AI documentation search with DeepSeek and Flask

  • Post author:
  • Post category:Python

Welcome to Module 4 of the tutorial series: “Build an AI-powered documentation assistant with Flask & DeepSeek”. In this module, you’ll extend the functionality of your Flask app by adding an interactive AI-powered documentation search system. Users will be able to query code functionality in natural language, and the system will generate explanations using DeepSeek’s language model. You’ll also build a chatbot-like interface for real-time code understanding.

Prerequisites

Lesson 7: Implementing a natural language query system

Objective

In this lesson, you’ll use DeepSeek’s language model to interpret and explain code functionality in response to natural language queries. You’ll also handle ambiguous or incomplete queries gracefully.


Step 1: Install Flask-Limiter

To secure the API with rate limiting, install the Flask-Limiter package.

Run the following command in your terminal:

pip install Flask-Limiter

Step 2: Create query_handler.py

This utility will handle natural language queries using DeepSeek.

File to Create: app/utils/query_handler.py

import requests
from dotenv import load_dotenv
import os

load_dotenv()

DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")

def explain_code(code_snippet, query):
    """
    Use DeepSeek LLM to explain code based on a natural language query.
    """
    prompt = f"""
    The user has provided the following Python code:
    {code_snippet}

    They are asking: "{query}"

    Provide a clear and concise explanation of what the code does, focusing on the user's query.
    """

    headers = {
        "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
        "Content-Type": "application/json"
    }
    data = {
        "model": "deepseek-chat",
        "prompt": prompt,
        "max_tokens": 200,
        "temperature": 0.7
    }
    response = requests.post(
        "https://api.deepseek.com/beta/completions",
        headers=headers,
        json=data
    )
    if response.status_code == 200:
        return response.json()["choices"][0]["text"].strip()
    else:
        raise Exception(f"Failed to generate explanation: {response.status_code}")

How it works:

  1. Load Environment Variables: The script uses dotenv to load the DEEPSEEK_API_KEY from the .env file.
  2. Construct a Prompt: The explain_code function builds a prompt that includes the user’s code snippet and their query.
  3. Send API Request: The function sends a POST request to the DeepSeek API with the prompt and other parameters.
  4. Handle API Response: If the request is successful, the function returns the generated explanation. If it fails, it raises an exception.

Step 3: Update routes.py

Add a new route to handle natural language queries.

File to Update: app/routes.py

from flask import Blueprint, jsonify, request, render_template
from .utils.docstring_generator import generate_docstring, improve_docstring
from app.utils.github_api import fetch_repo_contents, filter_python_files, download_file_contents
from app.utils.code_parser import extract_functions_and_classes, extract_function_signature, extract_class_metadata
from .utils.query_handler import explain_code
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address


main_bp = Blueprint('main', __name__)

# Rate limiting for API endpoints
limiter = Limiter(key_func=get_remote_address)

@main_bp.route('/')
def home():
    return render_template("index.html") 

@main_bp.route('/generate-docstring', methods=['POST'])
def generate_docstring_route():
    """
    Generate a docstring for a given code snippet.
    """
    data = request.json
    code_snippet = data.get("code")
    context = data.get("context", "")

    try:
        docstring = generate_docstring(code_snippet, context)
        return jsonify({"docstring": docstring})
    except Exception as e:
        return jsonify({"error": str(e)}), 500
    
@main_bp.route("/fetch-repo", methods=["POST"])
def fetch_repo():
    """
    Fetch and display Python files from a GitHub repository.
    """
    data = request.json
    owner = data.get("owner")
    repo = data.get("repo")

    try:
        contents = fetch_repo_contents(owner, repo)
        python_files = filter_python_files(contents)
        return jsonify({"python_files": python_files})
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@main_bp.route("/parse-file", methods=["POST"])
def parse_file():
    """
    Parse a Python file and extract metadata.
    """
    data = request.json
    download_url = data.get("download_url")

    try:
        code = download_file_contents(download_url)
        functions, classes = extract_functions_and_classes(code)
        function_metadata = [extract_function_signature(func) for func in functions]
        class_metadata = [extract_class_metadata(cls) for cls in classes]
        return jsonify({
            "functions": function_metadata,
            "classes": class_metadata
        })
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@main_bp.route("/improve-docstring", methods=["POST"])
def improve_docstring_route():
    """
    Improve an existing docstring.
    """
    data = request.json
    existing_docstring = data.get("docstring")
    context = data.get("context", "")

    try:
        improved_docstring = improve_docstring(existing_docstring, context)
        return jsonify({"improved_docstring": improved_docstring})
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@main_bp.route("/explain-code", methods=["POST"])
@limiter.limit("10 per minute")
def explain_code_route():
    """
    Explain code functionality based on a natural language query.
    """
    data = request.json
    code_snippet = data.get("code")
    query = data.get("query")

    try:
        explanation = explain_code(code_snippet, query)
        return jsonify({"explanation": explanation})
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@main_bp.route("/chatbot", methods=["POST"])
@limiter.limit("10 per minute")  # Rate limit to prevent abuse
def chatbot_route():
    """
    Chatbot endpoint for interactive code understanding.
    """
    data = request.json
    user_input = data.get("input")
    code_snippet = data.get("code", "")

    try:
        # Use the same explain_code utility for chatbot responses
        response = explain_code(code_snippet, user_input)
        return jsonify({"response": response})
    except Exception as e:
        return jsonify({"error": str(e)}), 500

 

How it works:

  1. Render index.html: The home route now renders the index.html template.
  2. Extract JSON Data: The explain_code_route and chatbot_route functions retrieve the JSON payload from the request using request.json.
  3. Call explain_code: The function passes the code snippet and query to the explain_code utility.
  4. Return Explanation: If successful, the function returns the AI-generated explanation in JSON format.
  5. Handle Errors: If an error occurs, the function returns a JSON error message with a 500 status code.

Step 4: Test the /explain-code Endpoint

Start the Flask app:

python run.py

Use curl to call the /explain-code endpoint:

curl -X POST http://127.0.0.1:5000/explain-code \
     -H "Content-Type: application/json" \
     -d '{"code": "def add(a, b): return a + b", "query": "What does this function do?"}'

Expected Output:

{
  "explanation": "The function `add(a, b)` takes two arguments, `a` and `b`, and returns their sum. In other words, it adds the two numbers together and gives back the result.\n\nFor example:\n- `add(2, 3)` would return `5`.\n- `add(-1, 1)` would return `0`.\n\nThis is a simple function that performs basic addition."
}

Lesson 8: Creating a chatbot for code understanding (Flask API)

Objective

In this lesson, you’ll build a chatbot-like interface for interactive code understanding. Users can ask questions about their code, and the system will respond with AI-generated explanations. You’ll also enhance the user interface with Bootstrap to make it more intuitive and visually appealing.


Step 1: Update index.html with Bootstrap and Clear Labels

Update the index.html file to include clear labels and instructions for the input fields. We’ll also add a loading spinner and feedback messages to improve the user experience.

File to Update: app/templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Doc Assistant</title>
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            padding: 20px;
        }
        .response-box {
            margin-top: 20px;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
            background-color: #f9f9f9;
        }
        .loading-spinner {
            display: none; /* Hidden by default */
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="text-center">Welcome to the AI-Powered Documentation Assistant!</h1>
        <p class="text-center">Use the chatbot below to ask questions about your code.</p>

        <!-- Chatbot Form -->
        <div class="row justify-content-center">
            <div class="col-md-8">
                <!-- Code Input Field -->
                <div class="mb-3">
                    <label for="codeInput" class="form-label">Paste your Python code here:</label>
                    <textarea class="form-control" id="codeInput" rows="5" placeholder="Example: def add(a, b): return a + b"></textarea>
                    <small class="form-text text-muted">Enter the Python code you want to ask questions about.</small>
                </div>

                <!-- Query Input Field -->
                <div class="mb-3">
                    <label for="userQuery" class="form-label">Ask a question about the code:</label>
                    <input type="text" class="form-control" id="userQuery" placeholder="Example: What does this function do?">
                    <small class="form-text text-muted">Type your question in natural language.</small>
                </div>

                <!-- Ask Button -->
                <button class="btn btn-primary" onclick="sendQuery()">Ask</button>

                <!-- Loading Spinner -->
                <div class="loading-spinner text-center">
                    <div class="spinner-border text-primary" role="status">
                        <span class="visually-hidden">Loading...</span>
                    </div>
                    <p class="mt-2">Your query is being processed. Please wait a few seconds...</p>
                </div>

                <!-- Response Box -->
                <div class="response-box mt-3" id="responseBox">
                    <strong>Response:</strong>
                    <p id="response"></p>
                </div>
            </div>
        </div>
    </div>

    <!-- Bootstrap JS and dependencies -->
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>

    <script>
        async function sendQuery() {
            const code = document.getElementById("codeInput").value;
            const query = document.getElementById("userQuery").value;
            const responseElement = document.getElementById("response");
            const responseBox = document.getElementById("responseBox");
            const loadingSpinner = document.querySelector(".loading-spinner");

            // Show loading spinner and hide response box
            loadingSpinner.style.display = "block";
            responseBox.style.display = "none";

            try {
                const response = await fetch("/chatbot", {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify({ input: query, code: code })
                });
                const data = await response.json();

                // Hide loading spinner and show response
                loadingSpinner.style.display = "none";
                responseBox.style.display = "block";

                if (data.response) {
                    responseElement.innerText = data.response;
                } else {
                    responseElement.innerText = "Error: " + (data.error || "No response received.");
                }
            } catch (error) {
                // Handle network or other errors
                loadingSpinner.style.display = "none";
                responseBox.style.display = "block";
                responseElement.innerText = "Error: " + error.message;
            }
        }
    </script>
</body>
</html>

Key features:

  1. Clear labels and placeholders:
    • The Code Input Field has a label (Paste your Python code here:) and a placeholder (Example: def add(a, b): return a + b).
    • The Query Input Field has a label (Ask a question about the code:) and a placeholder (Example: What does this function do?).
    • Both fields include helper text (<small class="form-text text-muted">...</small>) to guide the user.
  2. Loading spinner:
    • When the user clicks “Ask,” a loading spinner and a message (Your query is being processed. Please wait a few seconds...) are displayed.
  3. Response box:
    • The response from the API is displayed in a styled box with a border and background color.
    • If an error occurs, the error message is displayed in the same box.
  4. Error handling:
    • If the API request fails (e.g., network error), the error message is displayed in the response box.

How it works:

  1. User interaction:
    • The user pastes their code into the textarea and types a question into the input field.
    • When the user clicks “Ask,” the sendQuery function is triggered.
  2. Loading feedback:
    • The loading spinner and message are displayed to let the user know their query is being processed.
  3. API fequest:
    • The fetch API sends the code and query to the /chatbot endpoint.
  4. Display response:
    • Once the API responds, the loading spinner is hidden, and the response (or error message) is displayed in the response box.

Step 2: Test the Chatbot Interface

  1. Start the Flask app:
    python run.py
  2.  Open the app in your browser (http://127.0.0.1:5000).
  3. Paste your code into the Code Input Field and type a question into the Query Input Field.
  4. Click “Ask” and observe the loading spinner and response.

What you’ve achieved

  • You built a chatbot-like interface for interactive code understanding.
  • You enhanced the user interface with Bootstrap to make it more intuitive and visually appealing.
  • You added clear labels, placeholders, and feedback messages to guide the user.

Full code for module 4

You can find the complete code for this tutorial in the GitHub repository.


Next steps

  • Experiment: Customize the Bootstrap styles further to match your branding or preferences.
  • Proceed to Module 5: In the next module, you’ll learn how to automate API documentation. You’ll convert extracted information into structured Markdown/HTML docs, host them via GitHub Pages, and implement auto-updating documentation that tracks code changes with Git. You’ll also compare DeepSeek with other AI tools like OpenAI GPT and Claude.

Facebook Comments