Skip to content

Commit

Permalink
Use a separate endpoint to handle examples.
Browse files Browse the repository at this point in the history
Examples are handled by the /example endpoint. This commit
also unifies the python code examples and the ones created in UI.
  • Loading branch information
gulan28 committed Jul 25, 2020
1 parent 40136d8 commit 27ed1fe
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 26 deletions.
54 changes: 48 additions & 6 deletions api/demo_web_app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Runs the web app given a GPT object and UI configuration."""

import json
import subprocess
import openai

from flask import Flask, request
from flask import Flask, request, Response

from .gpt import set_openai_key, Example
from .ui_config import UIConfig
Expand All @@ -25,14 +26,55 @@ def get_params():
response = config.json()
return response

def error(err_msg, status_code):
return Response(json.dumps({"error": err_msg}), status=status_code)

@app.route(
"/examples", methods=["GET", "POST"], defaults={"example_id": ""},
)
@app.route(
"/examples/<example_id>", methods=["GET", "PUT", "DELETE"],
)
def examples(example_id):
method = request.method
args = request.json
if method == "GET":
# gets either a single example or all of them.
if example_id:
example = gpt.get_example(example_id)
if example:
return json.dumps(example.as_dict())
return error("id not found", 404)
return json.dumps(gpt.get_all_examples())
elif method == "POST":
# adds an empty example.
new_example = Example("", "")
gpt.add_example(new_example)
return json.dumps(gpt.get_all_examples())
elif method == "PUT":
# modifies an existing example.
if example_id:
example = gpt.get_example(example_id)
if example:
if args.get("input", None):
example.input = args["input"]
if args.get("output", None):
example.output = args["output"]
return json.dumps(example.as_dict())
return error("id not found", 404)
return error("id required", 400)
elif method == "DELETE":
# deletes an example.
if example_id:
gpt.clear_example(example_id)
return json.dumps(gpt.get_all_examples())
return error("id required", 400)
return error("Not implemented", 501)

@app.route("/translate", methods=["GET", "POST"])
def translate():
# pylint: disable=unused-variable
req_json = request.json
prompt = req_json["prompt"]
if config.show_example_form and len(req_json["examples"]) > 0:
for ex in req_json["examples"]:
gpt.add_example(Example(ex["input"], ex["output"]))
prompt = request.json["prompt"]
response = gpt.submit_request(prompt)
return {"text": response["choices"][0]["text"][7:]}

Expand Down
39 changes: 36 additions & 3 deletions api/gpt.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
"""Creates the Example and GPT classes for a user to interface with the OpenAI API."""

import openai

import uuid

def set_openai_key(key):
"""Sets OpenAI key."""
openai.api_key = key


class Example():
"""Stores an input, output pair and formats it to prime the model."""

def __init__(self, inp, out):
self.input = inp
self.output = out
self.id = uuid.uuid4().hex

def get_input(self):
"""Returns the input of the example."""
Expand All @@ -22,6 +24,15 @@ def get_output(self):
"""Returns the intended output of the example."""
return self.output

def get_id(self):
"""Returns the unique ID of the example."""
return self.id

def as_dict(self):
return {"input": self.get_input(),
"output": self.get_output(),
"id": self.get_id()}

def format(self):
"""Formats the input, output pair."""
return f"input: {self.input}\noutput: {self.output}\n"
Expand All @@ -43,11 +54,33 @@ def add_example(self, ex):
"""Adds an example to the object. Example must be an instance
of the Example class."""
assert isinstance(ex, Example), "Please create an Example object."
self.examples.append(ex.format())
self.examples.append(ex)

def find_example(self, id):
""" Returns the index of the example with specific id"""
for i, example in enumerate(self.examples):
if example.get_id() == id:
return i

def clear_example(self, id):
"""Clears example with the specific id"""
index = self.find_example(id)
if index is not None:
del self.examples[index]

def get_example(self, id):
""" Get a single example """
index = self.find_example(id)
if index is not None:
return self.examples[index]

def get_all_examples(self):
""" Returns all examples as a list of dicts"""
return [i.as_dict() for i in self.examples]

def get_prime_text(self):
"""Formats all examples to prime the model."""
return '\n'.join(self.examples) + '\n'
return '\n'.join([i.format() for i in self.examples]) + '\n'

def get_engine(self):
"""Returns the engine specified for the API."""
Expand Down
18 changes: 11 additions & 7 deletions examples/run_blank_example.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

from api import GPT, Example, UIConfig
from api import demo_web_app


# Construct GPT object and show some examples
gpt = GPT(engine="davinci",
temperature=0.5,
max_tokens=100)
gpt = GPT(engine="davinci", temperature=0.5, max_tokens=100)

gpt.add_example(Example("Who are you?", "I'm an example."))
gpt.add_example(Example("What are you?", "I'm an example."))

# Define UI configuration
config = UIConfig(description="Prompt",
button_text="Result",
placeholder="Where is Mt. Rushmore ?",
show_example_form=True)
config = UIConfig(
description="Prompt",
button_text="Result",
placeholder="Where are you?",
show_example_form=True,
)

demo_web_app(gpt, config)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@testing-library/user-event": "^7.1.2",
"axios": "^0.19.2",
"bootstrap": "^4.5.0",
"lodash": "^4.17.19",
"react": "^16.13.1",
"react-bootstrap": "^1.2.2",
"react-dom": "^16.13.1",
Expand Down
41 changes: 31 additions & 10 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from "react";
import { Form, Button, Row, Col } from "react-bootstrap";
import axios from "axios";
import { debounce } from "lodash";

import "bootstrap/dist/css/bootstrap.min.css";

const UI_PARAMS_API_URL = "/params";
const TRANSLATE_API_URL = "/translate";
const EXAMPLE_API_URL = "/examples";

class App extends React.Component {
constructor(props) {
Expand All @@ -16,8 +18,8 @@ class App extends React.Component {
buttonText: "Submit",
description: "Description",
showExampleForm: false,
};

examples: [],
}
// Bind the event handlers
this.handleInputChange = this.handleInputChange.bind(this);
this.handleClick = this.handleClick.bind(this);
Expand All @@ -33,30 +35,49 @@ class App extends React.Component {
buttonText: button_text,
description: description,
showExampleForm: show_example_form,
examples: [],
});
if (this.state.showExampleForm) {
axios
.get(EXAMPLE_API_URL)
.then(({ data: examples }) => {
this.setState({ examples: examples })
});
}
});
}

updateExample(id, body) {
axios.put(EXAMPLE_API_URL + "/" + id, body)
}

debouncedUpdateExample = debounce(this.updateExample, 50)

handleExampleChange = (index, field) => e => {
let body = {};
body[field]=e.target.value;
let id = this.state.examples[index].id;
let examples = [...this.state.examples];
examples[index][field] = e.target.value;
this.setState({ examples: examples });
this.debouncedUpdateExample(id, body);
}

handleExampleDelete = (index) => e => {
e.preventDefault();
let examples = [
...this.state.examples.slice(0, index),
...this.state.examples.slice(index + 1)
];
this.setState({ examples: examples});
axios
.delete(EXAMPLE_API_URL + "/" + this.state.examples[index].id)
.then(({ data: examples }) => {
this.setState({ examples: examples });
});
}

handleExampleAdd = e => {
e.preventDefault();
let examples = this.state.examples.concat({input: "", output: ""});
this.setState({ examples: examples });
axios
.post(EXAMPLE_API_URL)
.then(({ data: examples }) => {
this.setState({ examples: examples });
});
}

handleInputChange(e) {
Expand Down

0 comments on commit 27ed1fe

Please sign in to comment.