diff --git a/README.md b/README.md index 695ca14..a5cf218 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,26 @@ # GraphGPT -### Natural Language → Knowledge Graph + +## Intro +**GraphGPT** converts unstructured natural language into a knowledge graph. +Pass in the synopsis of your favorite movie, a passage from a confusing Wikipedia page, or transcript from a video to generate a graph visualization of entities and their relationships. + +Successive queries can update the existing state of the graph or create an entirely new structure. For example, updating the current state could involve injecting new information through nodes and edges or changing the color of certain nodes. + +The current few-shot prompt guides GPT-3 in accurately understanding the JSON formatting GraphGPT or Azure AI Studio API requires for proper rendering. You can see the entire prompt in `public/prompts/main.prompt`. A major issue at the moment is latency. Due to the nature of OpenAI API calls, it takes up to 20 seconds to receive a response. + + +## How it works? + +### Natural Language → Knowledge Graph (demo first version) ![demo](demo.gif) -*Note: this is a toy project I built out over a weekend. If you want to use knowledge graphs in your project, check out [GPT Index](https://github.com/jerryjliu/gpt_index).* +### Natural Language → Knowledge Graph (demo version with Azure AI Studio switch) -GraphGPT converts unstructured natural language into a knowledge graph. Pass in the synopsis of your favorite movie, a passage from a confusing Wikipedia page, or transcript from a video to generate a graph visualization of entities and their relationships. +![screen-with-azure-switch](screen-with-azure-switch.png) -Successive queries can update the existing state of the graph or create an entirely new structure. For example, updating the current state could involve injecting new information through nodes and edges or changing the color of certain nodes. +*Note: this is a toy project I built out over a weekend. If you want to use knowledge graphs in your project, check out [GPT Index](https://github.com/jerryjliu/gpt_index).* -The current few-shot prompt guides GPT-3 in accurately understanding the JSON formatting GraphGPT requires for proper rendering. You can see the entire prompt in `public/prompts/main.prompt`. A major issue at the moment is latency. Due to the nature of OpenAI API calls, it takes up to 20 seconds to receive a response. ## Prompts diff --git a/package-lock.json b/package-lock.json index 5db0c8d..5ffe8dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "graphgpt", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 2, "requires": true, "packages": { @@ -11,7 +11,6 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "openai": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-graph-vis": "^1.0.7", @@ -4972,14 +4971,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "dependencies": { - "follow-redirects": "^1.14.8" - } - }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -12357,28 +12348,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/openai": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-3.1.0.tgz", - "integrity": "sha512-v5kKFH5o+8ld+t0arudj833Mgm3GcgBnbyN9946bj6u7bvel4Yg6YFz2A4HLIYDzmMjIo0s6vSG9x73kOwvdCg==", - "dependencies": { - "axios": "^0.26.0", - "form-data": "^4.0.0" - } - }, - "node_modules/openai/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -20708,14 +20677,6 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==" }, - "axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "requires": { - "follow-redirects": "^1.14.8" - } - }, "axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -26068,27 +26029,6 @@ "is-wsl": "^2.2.0" } }, - "openai": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-3.1.0.tgz", - "integrity": "sha512-v5kKFH5o+8ld+t0arudj833Mgm3GcgBnbyN9946bj6u7bvel4Yg6YFz2A4HLIYDzmMjIo0s6vSG9x73kOwvdCg==", - "requires": { - "axios": "^0.26.0", - "form-data": "^4.0.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", diff --git a/package.json b/package.json index acdf269..0e27abd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphgpt", - "version": "0.1.0", + "version": "0.2.0", "private": true, "dependencies": { "@testing-library/jest-dom": "^5.16.5", @@ -36,4 +36,4 @@ "last 1 safari version" ] } -} \ No newline at end of file +} diff --git a/public/prompts/stateless.prompt b/public/prompts/stateless.prompt index cad57b0..8656e3d 100644 --- a/public/prompts/stateless.prompt +++ b/public/prompts/stateless.prompt @@ -4,7 +4,7 @@ If an update is a relationship, provide [ENTITY 1, RELATIONSHIP, ENTITY 2]. The If an update is related to a color, provide [ENTITY, COLOR]. Color is in hex format. -If an update is related to deleting an entity, provide ["DELETE", ENTITY]. +If an update is related to deleting an entity, provide [DELETE, ENTITY]. Example: prompt: Alice is Bob's roommate. Make her node green. diff --git a/screen-with-azure-switch.png b/screen-with-azure-switch.png new file mode 100644 index 0000000..fbf60c3 Binary files /dev/null and b/screen-with-azure-switch.png differ diff --git a/src/App.css b/src/App.css index 56fd67e..013bed1 100644 --- a/src/App.css +++ b/src/App.css @@ -79,7 +79,7 @@ margin-bottom: 50px; } -.searchBar { +.searchBar, .apiURI { border: 1px solid rgb(0, 0, 0); border-radius: 5px; box-shadow: 0 0 5px rgb(0 0 0 / 10%); @@ -150,4 +150,4 @@ .clearButton:hover { background-color: rgba(239, 239, 239, 0.8); -} \ No newline at end of file +} diff --git a/src/App.js b/src/App.js index 4c0985f..d47e4d4 100644 --- a/src/App.js +++ b/src/App.js @@ -23,6 +23,25 @@ const options = { }; function App() { + const [useAzureOpenAIApi, setChecked] = React.useState(false); + const [apiURI, setApiURI] = React.useState('https://api.openai.com/v1/completions'); + + const handleChangeApiURI = () => { + setChecked(!useAzureOpenAIApi); + }; + + const handleApiURI = (event) => { + setApiURI(event.target.value); + }; + + const Checkbox = ({ label, value, onChange }) => { + return ( + + ); + }; const [graphState, setGraphState] = useState( { @@ -121,20 +140,50 @@ function App() { .then(response => response.text()) .then(text => text.replace("$prompt", prompt)) .then(prompt => { - console.log(prompt) + console.log("prompt:", prompt); + + let params = {}; + if (useAzureOpenAIApi == true) { + // Azure OpenAI Studio API + params = { + messages: [ + { + role: 'system', + content: 'You are an AI assistant that helps people find information.', + }, + { + role: 'user', + content: prompt, + }, + ], + stop: "\n", + max_tokens: 100, + }; + } else { + // OpenAI ChatGPT API + params = { ...DEFAULT_PARAMS, prompt: prompt, stop: "\n" }; + } - const params = { ...DEFAULT_PARAMS, prompt: prompt, stop: "\n" }; + let headers = { + 'Content-Type': 'application/json', + }; + headers['api-key'] = String(apiKey); + if (useAzureOpenAIApi == true) { + // Azure OpenAI Studio API + headers['api-key'] = String(apiKey); + } else { + // OpenAI ChatGPT API + headers['Authorization'] = 'Bearer ' + String(apiKey); + } const requestOptions = { method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + String(apiKey) - }, + headers: headers, body: JSON.stringify(params) }; - fetch('https://api.openai.com/v1/completions', requestOptions) + fetch(apiURI, requestOptions) .then(response => { + console.log("response: ", response); if (!response.ok) { switch (response.status) { case 401: // 401: Unauthorized: API key is wrong @@ -142,15 +191,25 @@ function App() { case 429: // 429: Too Many Requests: Need to pay throw new Error('You exceeded your current quota, please check your plan and billing details.'); default: - throw new Error('Something went wrong with the request, please check the Network log'); + throw new Error('Something went wrong with the request, please check the Network log - response:', response, ", response.text", response.text()); } } return response.json(); }) .then((response) => { + console.log("response 1: ", response); const { choices } = response; - const text = choices[0].text; - console.log(text); + console.log("choices 1: ", choices); + + let text = ""; + if (useAzureOpenAIApi == true) { + // Azure OpenAI Studio API + text = choices[0].message.content; + } else { + // OpenAI ChatGPT API + text = choices[0].text; + } + console.log("text 1: ", text); const updates = JSON.parse(text); console.log(updates); @@ -175,17 +234,46 @@ function App() { .then(prompt => { console.log(prompt) - const params = { ...DEFAULT_PARAMS, prompt: prompt }; + let params = {}; + if (useAzureOpenAIApi == true) { + // Azure OpenAI Studio API + params = { + messages: [ + { + role: 'system', + content: 'You are an AI assistant that helps people find information.', + }, + { + role: 'user', + content: prompt, + }, + ], + // stop: "\n", + max_tokens: 100, + }; + } else { + // OpenAI ChatGPT API + params = { ...DEFAULT_PARAMS, prompt: prompt }; + } + + let headers = { + 'Content-Type': 'application/json', + }; + headers['api-key'] = String(apiKey); + if (useAzureOpenAIApi == true) { + // Azure OpenAI Studio API + headers['api-key'] = String(apiKey); + } else { + // OpenAI ChatGPT API + headers['Authorization'] = 'Bearer ' + String(apiKey); + } const requestOptions = { method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + String(apiKey) - }, + headers: headers, body: JSON.stringify(params) }; - fetch('https://api.openai.com/v1/completions', requestOptions) + fetch(apiURI, requestOptions) .then(response => { if (!response.ok) { switch (response.status) { @@ -200,9 +288,18 @@ function App() { return response.json(); }) .then((response) => { + console.log("response 2: ", response); const { choices } = response; - const text = choices[0].text; - console.log(text); + console.log("choices 2: ", choices); + + let text = ""; + if (useAzureOpenAIApi == true) { + // Azure OpenAI Studio API + text = choices[0].message.content; + } else { + text = choices[0].text; + } + console.log("text 2: ", text); const new_graph = JSON.parse(text); @@ -234,7 +331,18 @@ function App() { const createGraph = () => { document.body.style.cursor = 'wait'; - document.getElementsByClassName("generateButton")[0].disabled = true; + //document.getElementsByClassName("generateButton")[0].disabled = true; + const prompt = document.getElementsByClassName("searchBar")[0].value; + const apiKey = document.getElementsByClassName("apiKeyTextField")[0].value; + + queryPrompt(prompt, apiKey); + } + + + const switchOpenAIApi = () => { + document.body.style.cursor = 'wait'; + + //document.getElementsByClassName("generateButton")[0].disabled = true; const prompt = document.getElementsByClassName("searchBar")[0].value; const apiKey = document.getElementsByClassName("apiKeyTextField")[0].value; @@ -248,8 +356,19 @@ function App() {

GraphGPT is open-source 🎉

- + + + +