Next, we'll implement the Jinja templating engine to create dynamic HTML pages. Jinja allows us to inject server-side data directly into our HTML templates, transforming static pages into dynamic, data-driven experiences. We'll configure our AI to return responses in JSON format, giving us structured data that's easy to parse and display.

The core concept involves embedding Jinja variables in HTML using double curly brace syntax ({{ variable_name }}). When Flask renders the template, it passes variables from the server-side Python code directly into the HTML, where they're seamlessly integrated into the page structure. This server-side rendering approach ensures that dynamic content is fully formed before reaching the browser, improving both performance and SEO.

Jinja stands as one of the most powerful templating engines in the Python ecosystem, designed specifically for generating HTML with dynamic, server-side content. Unlike client-side JavaScript solutions, Jinja processes templates on the server, allowing us to inject our AI's responses directly into the HTML before it reaches the user's browser. This approach provides better performance, improved accessibility, and more reliable content delivery.

Previously, we successfully connected to the OpenAI API and received responses for our Grand Slam question, but we simply dumped raw text into the browser. While functional, this approach lacks the polish and structure that users expect from modern web applications. Now we'll elevate our implementation by properly rendering HTML pages using Flask's render_template function combined with Jinja's variable injection capabilities.

The enhanced approach requires passing two arguments to render_template: first, the HTML template filename, and second, any variables we want to make available within that template. The syntax follows this pattern: render_template('index.html', ai_answer=your_ai_response). This creates a bridge between your server-side Python logic and your client-side HTML presentation.

Understanding this variable passing mechanism is crucial for modern web development. The first argument specifies which HTML template to render, while subsequent keyword arguments define the variables available within that template. These variable names don't need to match your Python variable names—you're essentially creating a mapping between server-side data and template variables. This flexibility allows for clean separation between your backend logic and frontend presentation.

To render variables in your HTML template, wrap them in Jinja's distinctive double curly brace syntax: {{ variable_name }}. This tells the Jinja engine to replace that placeholder with the actual value passed from your Flask route. The process happens entirely on the server, ensuring that users receive fully-formed HTML with all dynamic content already in place.

For comprehensive documentation and advanced features, consult the official Jinja templating engine documentation. Jinja offers sophisticated capabilities including template inheritance, filters, control structures, and custom functions—features that become invaluable as your applications grow in complexity. Mastering Jinja early in your Flask journey will pay dividends throughout your web development career.

Let's create our next server file, maintaining our systematic numbering approach. Starting from server_02.py, we'll use "Save As" to create server_03.py, keeping our lesson numbers and file names synchronized for easier project navigation and maintenance.

Begin by closing any unnecessary files to declutter your workspace. Open server_02.py, navigate to File > Save As, and create server_03.py. For our HTML template, we'll build upon index_01.html since lesson two focused exclusively on API connectivity without requiring a dedicated webpage. That lesson intentionally avoided HTML complexity to concentrate on establishing the OpenAI connection and validating response handling.

Our previous lesson prioritized the essential question: "Can we successfully communicate with the OpenAI API?" By deliberately outputting raw text responses, we eliminated potential HTML-related complications and focused entirely on the API integration. This methodical approach—mastering one layer before adding complexity—represents a best practice in software development.

Now we're ready to combine our proven API connectivity with professional web presentation. Since lesson two didn't require an HTML template, we'll skip directly from index_01.html to index_03.html, maintaining alignment between our lesson numbers and file names. This naming convention prevents confusion and makes project navigation intuitive.

Update your HTML template with appropriate headings and content structure. Modify the H1 tag to reflect our new focus: "OpenAI GPT-4o Model Response." This clearly communicates the page's purpose while setting user expectations about the content they'll receive.

Add a descriptive paragraph explaining the page's functionality: "OpenAI API GPT-4o model response text." This contextual information helps users understand what they're viewing and reinforces the professional presentation of your application. Clean, descriptive content contributes significantly to user experience and application credibility.


Implement the Jinja templating syntax by adding dynamic variables to your HTML structure. Variables must be enclosed in double curly braces: {{ variable_name }}. For our AI response display, we'll use {{ ai_answer }} within a paragraph tag, creating a placeholder that Jinja will populate with the actual AI response content.

This placeholder approach separates concerns elegantly—your HTML defines the structure and styling, while your Python code handles data processing and variable assignment. When Flask renders the template, Jinja seamlessly merges the template structure with the dynamic data, producing fully-formed HTML ready for browser consumption.

The render_template method serves as the bridge between your Flask routes and HTML templates. Its first argument specifies the template file, while subsequent keyword arguments define the variables available within that template. The syntax render_template('template.html', variable_name=value) creates a mapping that Jinja uses during the rendering process.

Enhance your template's visual presentation with appropriate CSS styling. While we won't detail every CSS rule here—that would detract from our core templating focus—feel free to copy the provided styles or create your own. Good visual design reinforces the professional quality of your application and improves user engagement.

Focus on the templating concepts rather than getting bogged down in CSS details. The styling serves to make our final result more visually appealing, but the real learning lies in understanding how Jinja processes templates and injects dynamic content. Master the templating engine first, then refine the presentation.

Now we'll modify our server code to properly handle JSON responses and template rendering. Start by importing the JSON module in server_03.py—we'll need this for parsing the structured responses from OpenAI. Add `import json` to your imports section alongside the existing OpenAI and Flask imports.

Configure the OpenAI API call to return JSON-formatted responses by adding the response_format parameter to your chat completion request. Set `response_format={"type": "json_object"}` to ensure the AI returns structured data that we can easily parse and manipulate. This approach provides much more flexibility than parsing free-form text responses.

Rather than directly returning the API response, capture it in a variable for processing. Save the response content as `ai_json = response.choices[0].message.content`. This gives us the raw JSON string returned by the OpenAI API, which we can then parse into a usable Python dictionary.

Add a print statement to display the JSON response in your terminal: `print(f"AI JSON response: {ai_json}")`. This debugging output helps you understand exactly what data you're receiving from the API and troubleshoot any parsing issues that might arise during development.

Update your prompt to explicitly request JSON-formatted responses with specific key names. Modify your user message to something like: "What is a Grand Slam? Please respond in JSON format with 'short_answer' and 'long_answer' keys." This dual specification—both in the prompt and the response_format parameter—ensures consistent JSON output.

Parse the JSON string into a Python dictionary using `json.loads()`. Create a new variable: `ai_dictionary = json.loads(ai_json)`. This converts the JSON string into a native Python data structure that you can easily navigate and extract specific values from.

Extract the specific answer you want to display by accessing the appropriate dictionary key. For our long-form response, use: `ai_answer = ai_dictionary["long_answer"]`. This gives you just the text content you want to display, cleanly separated from the JSON structure.

Add print statements to verify your data types at each step: `print(f"AI JSON type: {type(ai_json)}")` and `print(f"AI dictionary type: {type(ai_dictionary)}")`. These debugging statements confirm that your JSON parsing is working correctly—you should see "string" for ai_json and "dict" for ai_dictionary.


Return the rendered template with your dynamic variable using Flask's render_template function. The complete call should look like: `return render_template('index_03.html', ai_answer=ai_answer)`. This passes your extracted AI response to the HTML template, where Jinja will inject it into the {{ ai_answer }} placeholder.

The variable name in your template (ai_answer) must match the keyword argument in your render_template call. This creates the connection that allows Jinja to find and inject the correct value. While the names don't have to match your Python variables, consistency in naming helps maintain clean, readable code.

Save your changes and test the complete implementation. Stop any running Flask server with Ctrl+C, then start your new server with `python server_03.py`. This ensures you're running the updated code with all your new templating and JSON handling features.

When you navigate to your application in the browser, the complete flow executes seamlessly: Flask receives the request, sends your prompt to the OpenAI API, receives the JSON response, parses it into a dictionary, extracts the desired answer, and renders it within your HTML template. The result is a professional-looking webpage with dynamically generated AI content.

Monitor your terminal output to see the debugging information you added. You should observe the print statements showing data types and content, confirming that each step of your JSON processing pipeline is working correctly. This debugging approach becomes invaluable when building more complex applications.

The browser display should now show your AI's response properly formatted within the paragraph tag, styled according to your CSS rules, and integrated seamlessly into your page layout. This represents a significant upgrade from the raw text output of previous lessons—you now have a foundation for building sophisticated, data-driven web applications.

Refresh the browser to trigger another API call and see the dynamic templating in action. Each refresh sends a new request through your complete pipeline: route handling, API communication, JSON parsing, variable extraction, and template rendering. This end-to-end process forms the backbone of modern web application development.

Fine-tune your presentation for optimal user experience. Consider adding contextual information like displaying the original question alongside the answer, using appropriate heading tags for better content hierarchy, and applying styling that makes the AI response clearly distinguishable from static page content.

For enhanced visual appeal, wrap key elements in appropriate HTML tags. Use `` tags for emphasis, structure your content with proper heading hierarchy, and ensure your CSS creates an engaging, professional appearance. Remember that good design reinforces the quality and credibility of your application.

Your final implementation demonstrates the power of combining Flask routing, OpenAI API integration, JSON processing, and Jinja templating. This foundational pattern scales to support much more complex applications—you now understand how to dynamically generate web content using server-side data processing and professional template rendering.

The complete code architecture includes JSON importing, structured API requests with response format specification, JSON parsing and dictionary manipulation, variable extraction and type verification, and template rendering with dynamic variable injection. Each component plays a crucial role in creating robust, maintainable web applications that deliver dynamic content with professional presentation.