defano / wyldcard

A clone of Apple's HyperCard and HyperTalk scripting language.
https://github.com/defano/wyldcard/wiki
MIT License
117 stars 12 forks source link

feature:Inferring UI from a chatGPT prompt #125

Open ooper-zz opened 1 year ago

ooper-zz commented 1 year ago

I realize our goal for trying to stay true to good-old Hypercard, but a UI by inference would be a cutting-edge approach for Wyldcard to build user interfaces that leverages natural language processing and machine learning techniques to generate UI elements on the fly based on text input. With UI by inference, developers can create highly flexible and customizable conversational interfaces that can adapt to a wide range of use cases, without the need for manual UI design or coding. By empowering users to interact with technology using natural language, UI by inference represents the next frontier in user interface design.

I realize there are challenges, like layout templating and the like, but here is one way we can do it:

Provided we have integrated ChatGPT, take a prompt as input, parse it into object(s), nouns, adjectives and propositions using the Stanford NLP library, and then building a script that creates a memory-resident card (or a persistent one, depending on use case) for that object, then show it to the user for input, and finally build and pass the response back out to ChatGPT for further processing. Here is sample code, followed by a a few prompts. None of it has been tested.

import io.github.bensku.spacelang.SpaCyLang; import io.github.bensku.spacelang.api.Model; import io.github.bensku.spacelang.api.ModelPackage; import io.github.bensku.spacelang.api.Token; import io.github.bensku.spacelang.api.Tokenizer; import io.github.bensku.spacelang.impl.SpaCy; import io.github.bensku.spacelang.impl.model.BaseModelPackage; import com.defano.wyldcard.runtime.ExecutionContext; import com.defano.hypertalk.exception.HtException; import com.defano.hypertalk.exception.HtSemanticException;

import java.util.ArrayList; import java.util.List;

public class InferredPromptCard { private String prompt; private List fieldNames; private ExecutionContext context;

public InferredPromptCard(ExecutionContext context, String prompt, List<String> fieldNames) {
    this.context = context;
    this.prompt = prompt;
    this.fieldNames = fieldNames;
}

public void createCard() throws HtException {
    // Load the Spacy model
    ModelPackage spacyModel = new BaseModelPackage();
    Model model = spacyModel.load("en_core_web_sm");

    // Parse the prompt using the Spacy model
    List<Token> tokens = new ArrayList<>();
    Tokenizer tokenizer = new SpaCy().tokenizer(model);
    tokenizer.tokenize(prompt).forEachRemaining(tokens::add);

    // Extract the field values from the parsed prompt
    List<String> fieldValues = new ArrayList<>();
    for (String fieldName : fieldNames) {
        String fieldValue = "";
        for (Token token : tokens) {
            if (token.getPos().equals("NOUN") && token.getDep().equals("dobj") && token.getText().toLowerCase().contains(fieldName.toLowerCase())) {
                fieldValue = token.getText();
            }
        }
        fieldValues.add(fieldValue);
    }

    // Generate a HyperTalk script to create a new card with input fields for each field name
    String script = "domenu \"new card\"\n";
    script += "set the name of card to \"Inferred Prompt Card\"\n";

    for (int i = 0; i < fieldNames.size(); i++) {
        String fieldName = fieldNames.get(i);
        String fieldValue = fieldValues.get(i);

        script += "domenu \"new field\"\n";
        script += "set the name of last field to \"" + fieldName + "\"\n";
        script += "set the width of last field to 150\n";
        script += "set the height of last field to 20\n";
        if (i > 0) {
            script += "set the top of last field to the bottom of last card's field \"" + fieldNames.get(i - 1) + "\" + 10\n";
        }

        if (!fieldValue.isEmpty()) {
            script += "set the text of field \"" + fieldName + "\" to \"" + fieldValue + "\"\n";
        }
    }

    // Execute the HyperTalk script in the WyldCard context
    try {
        context.evaluate(script);
    } catch (HtSemanticException e) {
        throw new HtException("Error creating inferred prompt card: " + e.getMessage());
    }
}

} -------‐-------- unit test ------------- import com.defano.hypertalk.exception.HtException; import com.defano.wyldcard.runtime.ExecutionContext; import org.junit.jupiter.api.Test;

import java.util.Arrays; import java.util.List;

import static org.mockito.Mockito.*;

public class InferredPromptCardTest {

@Test
public void testCreateCard() throws HtException {
    // Create a mocked WyldCard execution context
    ExecutionContext context = mock(ExecutionContext.class);

    // Define the prompts and corresponding field names
    String pizzaPrompt = "I would like a pepperoni pizza with mushrooms, delivered to 123 Main St.";
    List<String> pizzaFields = Arrays.asList("size", "toppings", "address");

    String drinkPrompt = "I want a large iced coffee with cream and sugar.";
    List<String> drinkFields = Arrays.asList("size", "drink", "cream", "sugar");

    String emailPrompt = "Please send an email to John Doe at john.doe@example.com with the subject \"Important meeting\".";
    List<String> emailFields = Arrays.asList("recipient", "recipient email", "subject");

    String flightPrompt = "I need to book a flight from Los Angeles to New York City for two people departing on June 15th and returning on June 20th.";
    List<String> flightFields = Arrays.asList("origin", "destination", "passengers", "departure date", "return date");

    // Create the inferred prompt cards and execute the unit tests
    InferredPromptCard pizzaCard = new InferredPromptCard(context, pizzaPrompt, pizzaFields);
    pizzaCard.createCard();
    verify(context, times(1)).evaluate(anyString()); // Verify that the card creation script was executed once

    InferredPromptCard drinkCard = new InferredPromptCard(context, drinkPrompt, drinkFields);
    drinkCard.createCard();
    verify(context, times(2)).evaluate(anyString()); // Verify that the card creation script was executed twice

    InferredPromptCard emailCard = new InferredPromptCard(context, emailPrompt, emailFields);
    emailCard.createCard();
    verify(context, times(3)).evaluate(anyString()); // Verify that the card creation script was executed thrice

    InferredPromptCard flightCard = new InferredPromptCard(context, flightPrompt, flightFields);
    flightCard.createCard();
    verify(context, times(4)).evaluate(anyString()); // Verify that the card creation script was executed four times
}

}

IoIxD commented 1 year ago

For a variety of reasons, the project in its current state will not catch on beyond people who used the original HyperCard, those who want to research the original, and those who want to make projects with a unique retro aesthetic (see: people who make PS1-style horror games). None of those people will care for ChatGPT compatibility, especially given it's shortcomings (despite what people seem to think, ChatGPT is not perfect, and it is especially not good with programming - the exception now being Python, due to GPT-4's built-in interpreter).

If you want the project to appeal to the modern consumer, there are a multitude of changes you need to make, to the point where you may as well just make a new project (and at that point, you have the freedom to not have to bother with a new programming language. Just save tokens by asking ChatGPT to create a json file that corresponds to what you want to do).

Also ChatGPT is going to create slightly new code every time you ask it the same question. I can't imagine programming with only a text prompt to ChatGPT, it would be a debugging nightmare.