diff --git a/trivia-forge/backend/endpoints/chatgpt.py b/trivia-forge/backend/endpoints/chatgpt.py new file mode 100644 index 00000000..82cc6c61 --- /dev/null +++ b/trivia-forge/backend/endpoints/chatgpt.py @@ -0,0 +1,102 @@ +import os +from dotenv import load_dotenv +from flask import Blueprint, request +from openai import OpenAI + +load_dotenv() + +bp = Blueprint('chatgpt', __name__, url_prefix='/chatgpt') +# initialize openai client using configuration specified in vite environment variables +# reference: https://platform.openai.com/docs/api-reference/making-requests +openai = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) + +@bp.post('') +def post_prompt(): + data = request.get_json() + responses = [] + + for i in range(len(data['categories'])): + prompt = f"Generate {data['numberOfQuestions']} trivia questions that have an overall theme of {data['Theme']} about {data['categories'][i]['name']}." + + if data['isMultipleChoice']: + prompt += "Each question should be in the format Question:...\nChoice:...\nChoice:...\nChoice:...\nChoice:...\nAnswer:...\nHint:...\n---\nQuestion:... ect. Do not include A), B), C), or D) in the choices." + else: + prompt += "Each question should be in the format \nQuestion:...\nAnswer:...\nHint:...\n---\nQuestion:... ect." + # api call + try: + # API call to OpenAI + completion = openai.chat.completions.create( + messages = [{ "role": "user", "content": prompt }], + model = "gpt-3.5-turbo", + # adjust and use token limit if necessary + # max_tokens: 200 + # implment and adjust temperature if needed + # temperature scale is 0-1 and used to tune randomness of output + # temperature: .5 + ) + response = completion.choices[0].message.content.split('\n'); + responses.append(response) + except Exception as error: + print('Error calling OpenAI API:', error) + # create a new game and category object and add category to game + # need to change third parameter to current User ID once Users can sign in. + game = {'id': None, + 'name': data['Title'], + 'theme': data['Theme'], + 'categories': [], + 'userID': data['user']['id']} + + for i in range(len(data['categories'])): + newCategory = {'id': None, + 'name': data['categories'][i]['name'], + 'gameID': None, + 'questions': []} + game['categories'].append(newCategory) + # parse response from API + sections = responses[i] # store trivia questions + temp = [] + for i in range(len(sections)): + if sections[i] != "": + temp.append(sections[i]) + sections = temp + # loop through sections and create question and choice objects + if data['isMultipleChoice']: + for i in range(0, len(sections), 7): + question = sections[i][10:] + choices = [] + for k in range(4): + choice = sections[i + k + 1] + newChoice = {'id': None, + 'text': choice[8:], + 'questionID': None} + choices.append(newChoice) + + answer = sections[i + 5][8:] + hint = sections[i + 6][6:] + # create question object and add it to category + newQuestion = {'id': None, + 'question': question, + 'answer': answer, + 'hint': hint, + 'multipleChoice': data['isMultipleChoice'], + 'categoryID': None, + 'choices': []} + newCategory['questions'].append(newQuestion) + # add choices to question object + for i in range(len(choices)): + newQuestion['choices'].append(choices[i]) + else: + for j in range(0, len(sections), 3): + question = sections[j][10:] + answer = sections[j + 1][8:] + hint = sections[j + 2][6:] + # create question object and add it to category + newQuestion = {'id': None, + 'question': question, + 'answer': answer, + 'hint': hint, + 'multipleChoice': data['isMultipleChoice'], + 'categoryID': None, + 'choices': []} + newCategory['questions'].append(newQuestion) + return game diff --git a/trivia-forge/backend/main.py b/trivia-forge/backend/main.py index 53a34be9..e460a114 100644 --- a/trivia-forge/backend/main.py +++ b/trivia-forge/backend/main.py @@ -1,7 +1,7 @@ import os from dotenv import load_dotenv from flask import Flask -from endpoints import home, user, game, category, question, choice +from endpoints import home, user, game, category, question, choice, chatgpt from flask_cors import CORS load_dotenv() @@ -18,6 +18,7 @@ app.register_blueprint(category.bp) app.register_blueprint(question.bp) app.register_blueprint(choice.bp) +app.register_blueprint(chatgpt.bp) if __name__ == '__main__': diff --git a/trivia-forge/backend/poetry.lock b/trivia-forge/backend/poetry.lock index 5eca96f2..33537d97 100644 --- a/trivia-forge/backend/poetry.lock +++ b/trivia-forge/backend/poetry.lock @@ -94,6 +94,17 @@ files = [ [package.dependencies] packaging = "*" +[[package]] +name = "distro" +version = "1.9.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, +] + [[package]] name = "exceptiongroup" version = "1.2.1" @@ -344,6 +355,29 @@ files = [ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] +[[package]] +name = "openai" +version = "1.31.0" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-1.31.0-py3-none-any.whl", hash = "sha256:82044ee3122113f2a468a1f308a8882324d09556ba5348687c535d3655ee331c"}, + {file = "openai-1.31.0.tar.gz", hash = "sha256:54ae0625b005d6a3b895db2b8438dae1059cffff0cd262a26e9015c13a29ab06"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.7,<5" + +[package.extras] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] + [[package]] name = "packaging" version = "24.0" @@ -613,6 +647,26 @@ files = [ [package.dependencies] httpx = ">=0.24,<0.28" +[[package]] +name = "tqdm" +version = "4.66.4" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, + {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "typing-extensions" version = "4.11.0" @@ -725,4 +779,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "ba7787ac15ba772c330b79f6288422264f796377e46c8687f9325194f7c33382" +content-hash = "7588984a319dbe73eb61b3eccb427807dd223e6f776019cf26090d15b2703d4e" diff --git a/trivia-forge/backend/pyproject.toml b/trivia-forge/backend/pyproject.toml index c2aa4745..1343b440 100644 --- a/trivia-forge/backend/pyproject.toml +++ b/trivia-forge/backend/pyproject.toml @@ -12,6 +12,7 @@ flask = "^3.0.3" python-dotenv = "^1.0.1" flask-cors = "^4.0.1" gunicorn = "^22.0.0" +openai = "^1.31.0" [build-system] diff --git a/trivia-forge/frontend/README.md b/trivia-forge/frontend/README.md deleted file mode 100644 index f768e33f..00000000 --- a/trivia-forge/frontend/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# React + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/trivia-forge/frontend/package-lock.json b/trivia-forge/frontend/package-lock.json index da7715c2..c90b4e61 100644 --- a/trivia-forge/frontend/package-lock.json +++ b/trivia-forge/frontend/package-lock.json @@ -11,7 +11,6 @@ "axios": "^1.6.8", "bootstrap": "^5.3.3", "node-datetime": "^2.1.2", - "openai": "^4.38.3", "react": "^18.2.0", "react-bootstrap": "^2.10.2", "react-dom": "^18.2.0", @@ -1283,19 +1282,13 @@ "version": "18.19.31", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -1357,17 +1350,6 @@ "vite": "^4.2.0 || ^5.0.0" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -1389,17 +1371,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2471,14 +2442,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2595,31 +2558,6 @@ "node": ">= 6" } }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/formdata-node/node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "engines": { - "node": ">= 14" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2876,14 +2814,6 @@ "node": ">= 0.4" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -3489,7 +3419,8 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/nanoid": { "version": "3.3.7", @@ -3520,43 +3451,6 @@ "resolved": "https://registry.npmjs.org/node-datetime/-/node-datetime-2.1.2.tgz", "integrity": "sha512-eev1F0IPKSu3zvASMtH8ic4znGpfXdq9yc8yc/EVx6bk57u7bS2iZKVZ8ll1ZeH/IEQ3qFb04FB70PCNXSIp4w==" }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -3682,24 +3576,6 @@ "wrappy": "1" } }, - "node_modules/openai": { - "version": "4.38.3", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.38.3.tgz", - "integrity": "sha512-mIL9WtrFNOanpx98mJ+X/wkoepcxdqqu0noWFoNQHl/yODQ47YM7NEYda7qp8JfjqpLFVxY9mQhshoS/Fqac0A==", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "web-streams-polyfill": "^3.2.1" - }, - "bin": { - "openai": "bin/cli" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -4447,11 +4323,6 @@ "node": ">=4" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -4586,7 +4457,10 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "optional": true, + "peer": true }, "node_modules/update-browserslist-db": { "version": "1.0.13", @@ -4698,28 +4572,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5650,19 +5502,13 @@ "version": "18.19.31", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", + "dev": true, + "optional": true, + "peer": true, "requires": { "undici-types": "~5.26.4" } }, - "@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", - "requires": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, "@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -5718,14 +5564,6 @@ "react-refresh": "^0.14.0" } }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, "acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -5739,14 +5577,6 @@ "dev": true, "requires": {} }, - "agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "requires": { - "humanize-ms": "^1.2.1" - } - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -6531,11 +6361,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6623,27 +6448,6 @@ "mime-types": "^2.1.12" } }, - "form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "requires": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "dependencies": { - "web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==" - } - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6815,14 +6619,6 @@ "function-bind": "^1.1.2" } }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "requires": { - "ms": "^2.0.0" - } - }, "ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -7245,7 +7041,8 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "nanoid": { "version": "3.3.7", @@ -7264,19 +7061,6 @@ "resolved": "https://registry.npmjs.org/node-datetime/-/node-datetime-2.1.2.tgz", "integrity": "sha512-eev1F0IPKSu3zvASMtH8ic4znGpfXdq9yc8yc/EVx6bk57u7bS2iZKVZ8ll1ZeH/IEQ3qFb04FB70PCNXSIp4w==" }, - "node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" - }, - "node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, "node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -7366,21 +7150,6 @@ "wrappy": "1" } }, - "openai": { - "version": "4.38.3", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.38.3.tgz", - "integrity": "sha512-mIL9WtrFNOanpx98mJ+X/wkoepcxdqqu0noWFoNQHl/yODQ47YM7NEYda7qp8JfjqpLFVxY9mQhshoS/Fqac0A==", - "requires": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "web-streams-polyfill": "^3.2.1" - } - }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -7888,11 +7657,6 @@ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -7991,7 +7755,10 @@ "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "optional": true, + "peer": true }, "update-browserslist-db": { "version": "1.0.13", @@ -8038,25 +7805,6 @@ "loose-envify": "^1.0.0" } }, - "web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/trivia-forge/frontend/package.json b/trivia-forge/frontend/package.json index 6c4e26b9..c8dd7ba6 100644 --- a/trivia-forge/frontend/package.json +++ b/trivia-forge/frontend/package.json @@ -13,7 +13,6 @@ "axios": "^1.6.8", "bootstrap": "^5.3.3", "node-datetime": "^2.1.2", - "openai": "^4.38.3", "react": "^18.2.0", "react-bootstrap": "^2.10.2", "react-dom": "^18.2.0", diff --git a/trivia-forge/frontend/src/pages/loginPage.jsx b/trivia-forge/frontend/src/pages/loginPage.jsx index 0cecb84e..4c6106d7 100644 --- a/trivia-forge/frontend/src/pages/loginPage.jsx +++ b/trivia-forge/frontend/src/pages/loginPage.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { React, useState } from 'react'; import { Link, useNavigate } from "react-router-dom"; import { Form, Button, Card } from "react-bootstrap"; import { getUser} from '../services/triviaForgeApiService'; diff --git a/trivia-forge/frontend/src/pages/signUpPage.jsx b/trivia-forge/frontend/src/pages/signUpPage.jsx index 2a61d070..317191e7 100644 --- a/trivia-forge/frontend/src/pages/signUpPage.jsx +++ b/trivia-forge/frontend/src/pages/signUpPage.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { React, useState } from 'react'; import { useNavigate } from "react-router-dom"; import { Form, Button, Card } from "react-bootstrap"; import { addUser } from '../services/triviaForgeApiService'; diff --git a/trivia-forge/frontend/src/pages/triviaGenPage.jsx b/trivia-forge/frontend/src/pages/triviaGenPage.jsx index abaded04..f7bf24fb 100644 --- a/trivia-forge/frontend/src/pages/triviaGenPage.jsx +++ b/trivia-forge/frontend/src/pages/triviaGenPage.jsx @@ -1,22 +1,12 @@ -import React, { useState } from "react"; // variables that cause the component to re-render when they change -import OpenAI from "openai"; -import { Game } from "../models/game"; +import { React, useState } from "react"; // variables that cause the component to re-render when they change import { useNavigate } from "react-router-dom"; -import { Question } from "../models/question"; -import { Choice } from "../models/choice"; -import { Category } from "../models/category"; import { Card } from "react-bootstrap"; import useStore from '../hooks/useStore'; // global state management import Spinner from 'react-bootstrap/Spinner'; import Button from 'react-bootstrap/Button'; import GenerateButtonTooltip from "../components/GenerateButtonTooltip"; +import { promtChatGPT } from "../services/triviaForgeApiService"; -// initialize openai client using configuration specified in vite environment variables -// reference: https://platform.openai.com/docs/api-reference/making-requests -const openai = new OpenAI({ - apiKey: import.meta.env.VITE_REACT_APP_OPENAI_API_KEY, // vite does not process 'process.env' like Create React APP, use import.meta.env - dangerouslyAllowBrowser: true // set to true to enable local testing (not recommended for production) -}); function TriviaGenPage() { // state hooks for managaing number of questions and catergory input by user @@ -59,86 +49,18 @@ function TriviaGenPage() { setSubmitBtnLabel("Generating...") setSpinnerVisibility("") - let responses = [] - - for (let i = 0; i < categories.length; i++) { - let prompt = `Generate ${numberOfQuestions} trivia questions that have an overall theme of ${Theme} about ${categories[i].name}.`; - if (isMultipleChoice) { - prompt += "Each question should be in the format Question:...\nChoice:...\nChoice:...\nChoice:...\nChoice:...\nAnswer:...\nHint:...\n---\nQuestion:... ect. Do not include A), B), C), or D) in the choices."; - } else { - prompt += "Each question should be in the format \nQuestion:...\nAnswer:...\nHint:...\n---\nQuestion:... ect."; - } - - // api call - try { - - // API call to OpenAI - const completion = await openai.chat.completions.create({ - model: "gpt-3.5-turbo", - messages: [{ role: "user", content: prompt }], - // adjust and use token limit if necessary - // max_tokens: 200 - // implment and adjust temperature if needed - // temperature scale is 0-1 and used to tune randomness of output - // temperature: .5 - }); - let response = completion.choices[0].message.content.split('\n'); - - responses.push(response); - } - catch (error) { - console.error('Error calling OpenAI API:', error); - } - } - //create a new game and category object and add category to game - //need to change third parameter to current User ID once Users can sign in. - let game = new Game(Title, Theme, user.id); - - for (let i = 0; i < categories.length; i++) { - let newCategory = new Category(categories[i].name); - game.addCategory(newCategory); - //parse response from API - let sections = responses[i]; // store trivia questions - for (let i = 0; i < sections.length; i++) { - if (sections[i] === '') { sections.splice(i, 1); } - } - //loop through sections and create question and choice objects - if (isMultipleChoice) { - for (let i = 0; i < sections.length; i += 7) { - let question = sections[i].slice(10); - let choices = []; - for (let k = 0; k < 4; k++) { - let choice = sections[i + k + 1]; - let newChoice = new Choice(choice.slice(8)); - choices.push(newChoice); - } - let answer = sections[i + 5].slice(8); - let hint = sections[i + 6].slice(6); - - //create question object and add it to category - let newQuestion = new Question(question, answer, hint, isMultipleChoice); - newCategory.addQuestion(newQuestion); - - //add choices to question object - for (let i = 0; i < choices.length; i++) { - newQuestion.addChoice(choices[i]); - } - } - } else { - for (let j = 0; j < sections.length; j += 3) { - let question = sections[j].slice(10); - let answer = sections[j + 1].slice(8); - let hint = sections[j + 2].slice(6); - - //create question object and add it to category - let newQuestion = new Question(question, answer, hint, isMultipleChoice); - newCategory.addQuestion(newQuestion); - } - } + const message = { + 'categories': categories, + 'numberOfQuestions': numberOfQuestions, + 'Theme': Theme, + 'isMultipleChoice': isMultipleChoice, + 'Title': Title, + 'user': user } + + let game = await promtChatGPT(message) // state property to pass data as object to new route navigate('/review', { state: { game: game, page: 'review', isMultipleChoice: isMultipleChoice } }); - //console.log(completion.choices[0].message); }; // render component as a form return ( diff --git a/trivia-forge/frontend/src/services/triviaForgeApiService.jsx b/trivia-forge/frontend/src/services/triviaForgeApiService.jsx index 493fc431..352c8c88 100644 --- a/trivia-forge/frontend/src/services/triviaForgeApiService.jsx +++ b/trivia-forge/frontend/src/services/triviaForgeApiService.jsx @@ -338,4 +338,16 @@ export const editChoice = async (choice) => { } } -/* ************************************************************************************ */ \ No newline at end of file +/* ************************************************************************************ */ + +/* ************************************ ChatGPT ************************************ */ + +export const promtChatGPT = async (message) => { + try { + const response = await axios.post(`${API_URL}/chatgpt`, message); + return response.data; + } catch (error) { + console.error('Failed to prompt ChatGPT'); + return {}; + } +} \ No newline at end of file