Source code for gpt_engineer.applications.cli.learning

"""
The `learning` module is designed to facilitate the collection and storage of user feedback on the outputs generated by the GPT Engineer tool. It provides mechanisms for obtaining user consent, capturing user reviews, and storing this information for future analysis and enhancement of the tool's performance.

Classes
-------
Review : dataclass
    Represents a user's review of the generated code, including whether it ran, was perfect, was useful, and any additional comments.
Learning : dataclass
    Encapsulates the metadata and feedback collected during a session of using the GPT Engineer tool, including the prompt, model, temperature, configuration, logs, session identifier, user review, and timestamp.

Functions
---------
human_review_input() -> Optional[Review]
    Interactively gathers feedback from the user regarding the performance of generated code and returns a Review instance.
check_collection_consent() -> bool
    Checks if the user has previously given consent to store their data and, if not, asks for it.
ask_collection_consent() -> bool
    Prompts the user for consent to store their data for the purpose of improving GPT Engineer.
extract_learning(prompt: Prompt, model: str, temperature: float, config: Tuple[str, ...], memory: DiskMemory, review: Review) -> Learning
    Extracts feedback and session details to create a Learning instance based on the provided parameters.
get_session() -> str
    Retrieves a unique identifier for the current user session, creating one if it does not exist.

Constants
---------
TERM_CHOICES : tuple
    Terminal color choices for user interactive prompts, formatted with termcolor for readability.
"""

import json
import random
import tempfile

from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Optional, Tuple

from dataclasses_json import dataclass_json
from termcolor import colored

from gpt_engineer.core.default.disk_memory import DiskMemory
from gpt_engineer.core.prompt import Prompt


@dataclass_json
@dataclass
class Review:
    """
    A dataclass that represents a user's review of the generated code.

    Attributes
    ----------
    ran : Optional[bool]
        Indicates whether the generated code ran without errors.
    perfect : Optional[bool]
        Indicates whether the generated code met all the user's requirements.
    works : Optional[bool]
        Indicates whether the generated code was useful, even if not perfect.
    comments : str
        Any additional comments provided by the user.
    raw : str
        A raw string representation of the user's responses.
    """

    ran: Optional[bool]
    perfect: Optional[bool]
    works: Optional[bool]
    comments: str
    raw: str


@dataclass_json
@dataclass
class Learning:
    """
    A dataclass that encapsulates the learning data collected during a GPT Engineer session.

    Attributes
    ----------
    prompt : str
        A JSON string representing the prompt provided to GPT Engineer.
    model : str
        The name of the model used during the session.
    temperature : float
        The temperature setting used for the model's responses.
    config : str
        A JSON string representing the configuration settings for the session.
    logs : str
        A JSON string representing the logs of the session.
    session : str
        A unique identifier for the user session.
    review : Optional[Review]
        The user's review of the generated code.
    timestamp : str
        The UTC timestamp when the learning data was created.
    version : str
        The version of the learning data schema.
    """

    prompt: str
    model: str
    temperature: float
    config: str
    logs: str
    session: str
    review: Optional[Review]
    timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
    version: str = "0.3"


TERM_CHOICES = (
    colored("y", "green")
    + "/"
    + colored("n", "red")
    + "/"
    + colored("u", "yellow")
    + "(ncertain): "
)


[docs] def human_review_input() -> Optional[Review]: """ Interactively prompts the user to review the generated code and returns their feedback encapsulated in a Review object. This function will first check if the user has given consent to collect their feedback. If consent is given, it will ask the user a series of questions about the generated code's performance and capture their responses. Returns ------- Optional[Review] A Review object containing the user's feedback, or None if consent is not given. """ print() if not check_collection_consent(): return None print() print( colored("To help gpt-engineer learn, please answer 3 questions:", "light_green") ) print() ran = input("Did the generated code run at all? " + TERM_CHOICES) ran = ask_for_valid_input(ran) if ran == "y": perfect = input( "Did the generated code do everything you wanted? " + TERM_CHOICES ) perfect = ask_for_valid_input(perfect) if perfect != "y": useful = input("Did the generated code do anything useful? " + TERM_CHOICES) useful = ask_for_valid_input(useful) else: useful = "" else: perfect = "" useful = "" if perfect != "y": comments = input( "If you have time, please explain what was not working " + colored("(ok to leave blank)\n", "light_green") ) else: comments = "" return Review( raw=", ".join([ran, perfect, useful]), ran={"y": True, "n": False, "u": None, "": None}[ran], works={"y": True, "n": False, "u": None, "": None}[useful], perfect={"y": True, "n": False, "u": None, "": None}[perfect], comments=comments, )
[docs] def ask_for_valid_input(ran): while ran not in ("y", "n", "u"): ran = input("Invalid input. Please enter y, n, or u: ") return ran
[docs] def extract_learning( prompt: Prompt, model: str, temperature: float, config: Tuple[str, ...], memory: DiskMemory, review: Review, ) -> Learning: """ Constructs a Learning object containing the session's metadata and user feedback. Parameters ---------- prompt : str The initial prompt provided to the GPT Engineer. model : str The name of the model used during the session. temperature : float The temperature setting used for the model's responses. config : Tuple[str, ...] A tuple representing the configuration settings for the session. memory : DiskMemory An object representing the disk memory used during the session. review : Review The user's review of the generated code. Returns ------- Learning An instance of Learning containing all the session details and user feedback. """ return Learning( prompt=prompt.to_json(), model=model, temperature=temperature, config=json.dumps(config), session=get_session(), logs=memory.to_json(), review=review, )
[docs] def get_session() -> str: """ Retrieves or generates a unique identifier for the current user session. This function attempts to read a unique user ID from a temporary file. If the file does not exist, it generates a new random ID, writes it to the file, and returns it. This ID is used to uniquely identify the user's session. Returns ------- str A unique identifier for the user session. """ path = Path(tempfile.gettempdir()) / "gpt_engineer_user_id.txt" try: if path.exists(): user_id = path.read_text() else: # random uuid: user_id = str(random.randint(0, 2**32)) path.write_text(user_id) return user_id except IOError: return "ephemeral_" + str(random.randint(0, 2**32))