diff --git a/README.md b/README.md index 852adc6b1397d552679528c841191df2561eec98..fafaa017c7836f519e145f2f5e843e84748af5b7 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ --- -title: Http Server -emoji: 🐨 -colorFrom: yellow -colorTo: red +title: Python + HTTP Server +emoji: 🐍 +colorFrom: blue +colorTo: yellow sdk: gradio sdk_version: 2.9.1 +python_version: 3.10.4 app_file: app.py -pinned: false +models: [osanseviero/BigGAN-deep-128, t5-small] +datasets: [emotion] license: mit +pinned: false --- Check out the configuration reference at https://huggingface.co/docs/hub/spaces#reference diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..d64fe01479a04a7589744cbdfc059cbd8d596ca1 --- /dev/null +++ b/app.py @@ -0,0 +1,79 @@ +import os +import json +import requests +from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +from urllib.parse import parse_qs, urlparse + +from inference import infer_t5 +from dataset import query_emotion + +# https://huggingface.co/settings/tokens +# https://huggingface.co/spaces/{username}/{space}/settings +API_TOKEN = os.getenv("BIG_GAN_TOKEN") + + +class RequestHandler(SimpleHTTPRequestHandler): + def do_GET(self): + if self.path == "/": + self.path = "index.html" + + return SimpleHTTPRequestHandler.do_GET(self) + + if self.path.startswith("/infer_biggan"): + url = urlparse(self.path) + query = parse_qs(url.query) + input = query.get("input", None)[0] + + output = requests.request( + "POST", + "https://api-inference.huggingface.co/models/osanseviero/BigGAN-deep-128", + headers={"Authorization": f"Bearer {API_TOKEN}"}, + data=json.dumps(input), + ) + + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.end_headers() + + self.wfile.write(output.content) + + return SimpleHTTPRequestHandler + + elif self.path.startswith("/infer_t5"): + url = urlparse(self.path) + query = parse_qs(url.query) + input = query.get("input", None)[0] + + output = infer_t5(input) + + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.end_headers() + + self.wfile.write(json.dumps({"output": output}).encode("utf-8")) + + return SimpleHTTPRequestHandler + + elif self.path.startswith("/query_emotion"): + url = urlparse(self.path) + query = parse_qs(url.query) + start = int(query.get("start", None)[0]) + end = int(query.get("end", None)[0]) + + output = query_emotion(start, end) + + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.end_headers() + + self.wfile.write(json.dumps({"output": output}).encode("utf-8")) + + return SimpleHTTPRequestHandler + + else: + return SimpleHTTPRequestHandler.do_GET(self) + + +server = ThreadingHTTPServer(("", 7860), RequestHandler) + +server.serve_forever() diff --git a/dataset.py b/dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..26d9108c537d6fbb2b054e23bc169e1c4fd2aa07 --- /dev/null +++ b/dataset.py @@ -0,0 +1,19 @@ +from datasets import load_dataset + +dataset = load_dataset("emotion", split="train") + +emotions = dataset.info.features["label"].names + +def query_emotion(start, end): + rows = dataset[start:end] + texts, labels = [rows[k] for k in rows.keys()] + + observations = [] + + for i, text in enumerate(texts): + observations.append({ + "text": text, + "emotion": emotions[labels[i]], + }) + + return observations diff --git a/index.html b/index.html new file mode 100644 index 0000000000000000000000000000000000000000..9e1a29c6c0f358f77f2cc71dad9e75bb58f0705e --- /dev/null +++ b/index.html @@ -0,0 +1,1901 @@ + + + + + + Python 🤗 Space served with http module + + + + +
+

Python 🤗 Space served with http module

+
+

Image generation from Inference API

+

+ Model: + osanseviero/BigGAN-deep-128 +

+ + + pelican generated from BigGAN AI model +
+
+

Text generation from transformers library

+

+ Model: + t5-small +

+
+ + + +

+
+
+
+

Dataset from datasets library

+

+ Dataset: + emotion +

+
+ + +
+
+
+
+ + + diff --git a/index.js b/index.js new file mode 100644 index 0000000000000000000000000000000000000000..da58d658fda06c7aed1a8384db3cd19e5f8f7a3e --- /dev/null +++ b/index.js @@ -0,0 +1,126 @@ +if (document.location.search.includes('dark-theme=true')) { + document.body.classList.add('dark-theme'); +} + +let cursor = 0; +const RANGE = 5; +const LIMIT = 16_000; + +const textToImage = async (text) => { + const inferenceResponse = await fetch(`infer_biggan?input=${text}`); + const inferenceBlob = await inferenceResponse.blob(); + + return URL.createObjectURL(inferenceBlob); +}; + +const translateText = async (text) => { + const inferResponse = await fetch(`infer_t5?input=${text}`); + const inferJson = await inferResponse.json(); + + return inferJson.output; +}; + +const queryDataset = async (start, end) => { + const queryResponse = await fetch(`query_emotion?start=${start}&end=${end}`); + const queryJson = await queryResponse.json(); + + return queryJson.output; +}; + +const updateTable = async (cursor, range = RANGE) => { + const table = document.querySelector('.dataset-output'); + + const fragment = new DocumentFragment(); + + const observations = await queryDataset(cursor, cursor + range); + + for (const observation of observations) { + let row = document.createElement('tr'); + let text = document.createElement('td'); + let emotion = document.createElement('td'); + + text.textContent = observation.text; + emotion.textContent = observation.emotion; + + row.appendChild(text); + row.appendChild(emotion); + fragment.appendChild(row); + } + + table.innerHTML = ''; + + table.appendChild(fragment); + + table.insertAdjacentHTML( + 'afterbegin', + ` + + text + emotion + + ` + ); +}; + +const imageGenSelect = document.getElementById('image-gen-input'); +const imageGenImage = document.querySelector('.image-gen-output'); +const textGenForm = document.querySelector('.text-gen-form'); +const tableButtonPrev = document.querySelector('.table-previous'); +const tableButtonNext = document.querySelector('.table-next'); + +imageGenSelect.addEventListener('change', async (event) => { + const value = event.target.value; + + try { + imageGenImage.src = await textToImage(value); + imageGenImage.alt = value + ' generated from BigGAN AI model'; + } catch (err) { + console.error(err); + } +}); + +textGenForm.addEventListener('submit', async (event) => { + event.preventDefault(); + + const textGenInput = document.getElementById('text-gen-input'); + const textGenParagraph = document.querySelector('.text-gen-output'); + + try { + textGenParagraph.textContent = await translateText(textGenInput.value); + } catch (err) { + console.error(err); + } +}); + +tableButtonPrev.addEventListener('click', () => { + cursor = cursor > RANGE ? cursor - RANGE : 0; + + if (cursor < RANGE) { + tableButtonPrev.classList.add('hidden'); + } + if (cursor < LIMIT - RANGE) { + tableButtonNext.classList.remove('hidden'); + } + + updateTable(cursor); +}); + +tableButtonNext.addEventListener('click', () => { + cursor = cursor < LIMIT - RANGE ? cursor + RANGE : cursor; + + if (cursor >= RANGE) { + tableButtonPrev.classList.remove('hidden'); + } + if (cursor >= LIMIT - RANGE) { + tableButtonNext.classList.add('hidden'); + } + + updateTable(cursor); +}); + +textToImage(imageGenSelect.value) + .then((image) => (imageGenImage.src = image)) + .catch(console.error); + +updateTable(cursor) + .catch(console.error); diff --git a/inference.py b/inference.py new file mode 100644 index 0000000000000000000000000000000000000000..fbf5cce09c4dd0844bb300e7afb161a15f7b0149 --- /dev/null +++ b/inference.py @@ -0,0 +1,11 @@ +from transformers import T5Tokenizer, T5ForConditionalGeneration + +tokenizer = T5Tokenizer.from_pretrained("t5-small") +model = T5ForConditionalGeneration.from_pretrained("t5-small") + + +def infer_t5(input): + input_ids = tokenizer(input, return_tensors="pt").input_ids + outputs = model.generate(input_ids) + + return tokenizer.decode(outputs[0], skip_special_tokens=True) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..04b7f71b10ec6d0ad29c65445c10d9fc5d327b14 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +datasets==2.* +requests==2.27.* +sentencepiece==0.1.* +torch==1.11.* +transformers==4.* diff --git a/style.css b/style.css new file mode 100644 index 0000000000000000000000000000000000000000..6a3c98f8fab848caaaf7b844b24ce23c8c5c8dde --- /dev/null +++ b/style.css @@ -0,0 +1,79 @@ +body { + --text: hsl(0 0% 15%); + padding: 2.5rem; + font-family: sans-serif; + color: var(--text); +} +body.dark-theme { + --text: hsl(0 0% 90%); + background-color: hsl(223 39% 7%); +} + +main { + max-width: 80rem; + text-align: center; +} + +section { + display: flex; + flex-direction: column; + align-items: center; +} + +a { + color: var(--text); +} + +select, input, button, .text-gen-output { + padding: 0.5rem 1rem; +} + +select, img, input { + margin: 0.5rem auto 1rem; +} + +form { + width: 25rem; + margin: 0 auto; +} + +input { + width: 70%; +} + +button { + cursor: pointer; +} + +.text-gen-output { + min-height: 1.2rem; + margin: 1rem; + border: 0.5px solid grey; +} + +#dataset button { + width: 6rem; + margin: 0.5rem; +} + +#dataset button.hidden { + visibility: hidden; +} + +table { + max-width: 40rem; + text-align: left; + border-collapse: collapse; +} + +thead { + font-weight: bold; +} + +td { + padding: 0.5rem; +} + +td:not(thead td) { + border: 0.5px solid grey; +}