web3.db-fileconnector
Version:
GraphQL System Built on web3 technologies. Web3.DB is an open database built on top of web3 technologies. It is a decentralized, open-source database that allows users to store and query data in a secure and efficient manner. The system is designed to be
277 lines (247 loc) • 8.83 kB
JavaScript
import logger from "../../logger/index.js";
export default class ChatGPTPlugin {
async init() {
let HOOKS = {};
switch (this.action) {
case "update":
HOOKS.update = (stream) => this.query(stream);
break;
case "add_metadata":
HOOKS.add_metadata = (stream) => this.enhance(stream);
break;
case "generate":
HOOKS.generate = () => this.start();
break;
}
return {
ROUTES: {
GET: {
chat: (req, res) => this.chatHtml(req, res),
},
POST: {
"chat-submit": (req, res) => this.chatSubmit(req, res),
},
},
HOOKS: HOOKS,
};
}
async start() {
// Perform first call
this.createStream();
// Start the interval function
this.interval = setInterval(() => {
this.createStream();
}, this.secs_interval * 1000);
}
/** Will stop the plugin's interval */
async stop() {
logger.debug("Stopping plugin:", this.uuid);
if (this.interval) {
clearInterval(this.interval);
this.interval = null; // Clear the stored interval ID
}
}
/** Will create a stream every x seconds based on the user prompt */
async createStream() {
const parsedPrompt = await this.buildPrompt(this.prompt);
const result = await this.fetchFromOpenAI({
role: "user",
content: parsedPrompt,
});
if (result) {
/** We then create the stream in Ceramic with the updated content */
try {
let stream = await global.indexingService.ceramic.orbisdb
.insert(this.model_id)
.value(result)
.context(this.context)
.run();
} catch (e) {
logger.error(
"Error creating stream with model:" + this.model_id + ":",
e
);
}
} else {
logger.error("Couldn't create stream as `result` is undefined.");
}
}
/** Will query ChatGPT based on the plugin settings */
async query(content) {
// Parse the prompt string and replace placeholders with actual values
const parsedPrompt = await this.buildPrompt(this.prompt, content);
const result = await this.fetchFromOpenAI({
role: "user",
content: parsedPrompt,
});
/** Will update the stream's content to add the description generated by GPT */
let _content = { ...content };
_content[this.field] = result;
return _content;
}
/** Will return a JSON object classifying the book */
async enhance(stream) {
const parsedPrompt = await this.buildPrompt(this.prompt, stream.content);
const result = await this.fetchFromOpenAI({
role: "user",
content: parsedPrompt,
});
/** Will return the classification of the book */
return result;
}
/** Will build the final prompt by parsing variables and performing queries if needed */
async buildPrompt(prompt, content) {
// Find all matches
const matches = [...prompt.matchAll(/\$\{([\w.]+)\}/g)];
// Process each match
for (const match of matches) {
const fullMatch = match[0];
const variableName = match[1];
let replacement;
/** Handle reserved variable names such as query.results */
switch (variableName) {
case "query.results":
try {
let response = await global.indexingService.database.query(
this.query
);
if (response && response.data) {
replacement = JSON.stringify(response.data?.rows || "");
}
} catch (e) {
logger.error(
"There was an error replacing query.results with the actual OrbisDB results."
);
}
break;
default:
replacement = (content && content[variableName]) || "";
break;
}
// Replace the match in the prompt
prompt = prompt.replace(fullMatch, replacement);
}
return prompt;
}
async chatHtml(req, res) {
res.type("text/html");
return `
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<style>
.chat-body {
background: #f1f5f9;
}
.chat-history {
height: 80%;
overflow: auto;
margin: 0;
}
.chat-input {
padding: 10px;
background-color: #f8fafc;
border-radius: 4px;
}
.chat-submit {
padding: 10px;
border: none;
background: #007BFF;
color: #fff;
border-radius: 4px;
}
.chat-loading {
display: block;
font-style: italic;
color: #888;
}
</style>
</head>
<body class="chat-body bg-slate-100 text-sm w-full h-full flex flex-col">
<div id="chat-history" class="p-6 chat-history overflow-y-scroll flex flex-1 flex-col w-full border-b border-slate-200"></div>
<form class="chat-form bg-white p-4 flex flex-col mb-0">
<textarea id="content" name="content" class="border border-slate-200 chat-input w-full bg-slate-50 mb-2" placeholder="Type your question here..."></textarea>
<input type="submit" value="Submit" class="chat-submit w-full cursor-pointer">
</form>
<script>
document.getElementById('chat-form').addEventListener('submit', function(event) {
event.preventDefault();
var content = document.getElementById('content').value;
document.getElementById('chat-history').innerHTML += '<p class="chat-message"><strong>You:</strong> ' + content + '</p>';
document.getElementById('chat-history').innerHTML += '<p id="loading" class="chat-loading"><strong>Bot:</strong> Is typing...</p>';
fetch('./chat-submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ content: content })
})
.then(response => response.json())
.then(result => {
console.log("Data:", result.data);
document.getElementById('loading').style.display = 'none';
document.getElementById('chat-history').innerHTML += '<p class="chat-message"><strong>Bot:</strong> ' + result.data + '</p>';
});
});
</script>
</body>
</html>
`;
}
async chatSubmit(req, res) {
if (!req.body) {
return {
data: null,
};
}
const { content } = req.body;
logger.debug("Question asked:", content);
const result = await this.fetchFromOpenAI({
role: "user",
content: content,
});
logger.debug("Answer:", result);
return {
data: result,
};
}
/** Helper function to easily submit question to the OpenAI API */
async fetchFromOpenAI(userMessage) {
const messages = [
{
role: "system",
content: "You are a helpful assistant.",
},
userMessage,
];
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.secret_key}`,
"OpenAI-Organization": this.organization_id,
},
body: JSON.stringify({
model: "gpt-3.5-turbo-1106",
response_format: {
type: this.is_json == "yes" ? "json_object" : "text",
},
messages: messages,
max_tokens: 4096,
}),
});
if (!response.ok) {
logger.error(
"Error with ChatGPTPlugin " + response.statusText + ": ",
response.status
);
return;
}
const data = await response.json();
if (this.is_json == "yes") {
return JSON.parse(data.choices[0].message.content);
} else {
return data.choices[0].message.content;
}
}
}