Introduction and Context Setting

Welcome to the lesson on creating the LLM Manager, a central component of the AI Cooking Helper project. In previous lessons, you learned how to render prompt templates and make basic calls to a language model.

Now, we will consolidate these skills into a dedicated manager. To make this tool truly versatile, the manager is designed to accept specific prompt file names as arguments. This flexibility allows you to use the same logic for different tasks — whether you are generating a recipe, summarizing a grocery list, or suggesting substitutions — simply by specifying which files to use for the system and user instructions.

Setting Up the OpenAI Client

To communicate with the language model, we need to initialize a client and configure a logging system to keep track of what happens during the process. We use an API key stored in an environment variable to ensure security.

import logging
import os
from openai import OpenAI

# Configure logging to display the level and message
logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s")

# Initialize the client using an environment variable
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

In this setup:

  • We use the logging module to print helpful status updates to the console.
  • The OpenAI client is initialized with a key retrieved via os.getenv. This prevents sensitive credentials from being hardcoded into your scripts.
Understanding the generate_response Function

The generate_response function handles the entire lifecycle of an AI interaction: it prepares the text, sends it to the model, and retrieves the result.

The first step is rendering the prompts. Instead of using hardcoded names, we pass the names of the files we want to use.

system_prompt = render_prompt_from_file(system_prompt_name, variables)
user_prompt = render_prompt_from_file(user_prompt_name, variables)

By passing system_prompt_name and user_prompt_name, the function can locate the correct templates in your project folder. The variables dictionary then fills in the placeholders (like {{dish_name}}} or {{ingredients}}) as we discussed in the previous lesson.

Next, we send these rendered strings to the model:

completion = client.chat.completions.create(
    model=model,
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt},
    ],
    temperature=temperature,
)
  • messages: This list defines the conversation history. The system role sets the behavior (e.g., "You are a chef"), and the user role provides the specific request.
  • temperature: This controls how creative or predictable the response is.

Finally, we extract the text from the response object:

return completion.choices[0].message.content.strip()

The strip() method ensures that any unnecessary leading or trailing spaces are removed from the chef's response.

Error Handling and Logging

When working with external APIs, things can occasionally go wrong — such as network issues or an invalid API key. To prevent the entire application from crashing, we wrap our logic in a try-except block.

try:
    # Logic for rendering and calling the API
except APIError as exc:
    logging.error("LLM API Error: %s", exc)
    return None
except Exception as exc:
    logging.error("Unexpected error: %s", exc)
    return None
  • APIError: Specifically catches issues related to the OpenAI service.
  • Exception: A "catch-all" for any other unexpected errors.
  • If an error occurs, we log the details so we can fix it later, and return None to indicate that no response could be generated.
Putting It All Together

Below is the complete implementation of the LLM Manager. It uses type hints to clarify what kind of data each parameter expects, making the code easier to maintain and understand.

import logging
import os
from typing import Dict, Optional

from openai import APIError, OpenAI

from .prompts import render_prompt_from_file

logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s")

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))


def generate_response(
    system_prompt_name: str,
    user_prompt_name: str,
    variables: Dict[str, object],
    model: str = "gpt-4o",
    temperature: float = 0.7,
) -> Optional[str]:
    """
    Render system and user prompts, send to LLM, and return the response text.
    """
    try:
        system_prompt = render_prompt_from_file(system_prompt_name, variables)
        user_prompt = render_prompt_from_file(user_prompt_name, variables)

        completion = client.chat.completions.create(
            model=model,
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt},
            ],
            temperature=temperature,
        )

        return completion.choices[0].message.content.strip()
    except APIError as exc:
        logging.error("LLM API Error: %s", exc)
        return None
    except Exception as exc:
        logging.error("Unexpected error: %s", exc)
        return None
Summary and Preparation for Practice

In this lesson, you built a robust LLM Manager that serves as the "brain" of the AI Cooking Helper. You learned how to make your functions flexible by passing prompt filenames as arguments, how to set up professional logging, and how to protect your application with error handling.

In the upcoming practice, you will use this manager to generate actual cooking advice. Try experimenting with different prompt files and see how the generate_response function adapts to various tasks!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal