mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
Feat: PaLM 2 (#262)
* feat(api): add googleapis package to package.json
feat(api): add reqDemo.js file to make a request to Google Cloud AI Platform API to get a response from a chatbot model.
* feat: add PaLM2 support
* feat(conversationPreset.js): add support for topP and topK for google endpoint
feat(askGoogle.js): add support for topP and topK for google endpoint
feat(ask/index.js): add google endpoint
feat(endpoints.js): add google endpoint
feat(MessageHeader.jsx): add support for modelLabel for google endpoint
feat(PresetItem.jsx): add support for modelLabel for google endpoint
feat(HoverButtons.jsx): add support for google endpoint
feat(createPayload.ts): add google endpoint
feat(types.ts): add google endpoint
feat(store/endpoints.js): add google endpoint
feat(cleanupPreset.js): add support for topP and topK for google endpoint
feat(getDefaultConversation.js): add support for topP and topK for google endpoint
feat(handleSubmit.js): add support for topP and topK for google endpoint
* fix: messages payload
* refactor(GoogleClient.js): set maxContextTokens based on isTextModel value
feat(GoogleClient.js): add delay option to TextStream constructor
feat(getIcon.jsx): add support for google endpoint and PaLM2 model label
* feat: palm frontend changes
* feat(askGoogle.js): set default example to empty input and output
feat(Examples.jsx): add ability to add and remove examples
refactor(Settings.jsx): remove examples from props and setOption function
style(GoogleOptions): remove unnecessary whitespace after Settings2 import
feat(GoogleOptions): add addExample and removeExample functions to manage examples
fix(cleanupPreset): set default example to [{ input: '', output: ''}]
fix(getDefaultConversation): set default example to [{ input: '', output: ''}]
fix(handleSubmit): set default example to [{ input: '', output: ''}]
* style(client): adjust height of settings and examples components to 350px
fix(client): fix path to palm.png image in getIcon.jsx file
* style(EndpointOptionsPopover.jsx, Examples.jsx, Settings.jsx): improve button styles and update input placeholders
* feat (palm): finalize examples on the frontend
* feat(GoogleClient.js): filter out empty examples in options
feat(GoogleClient.js): add support for promptPrefix in buildPayload method
feat(GoogleClient.js): add support for examples in buildPayload method
feat(conversationPreset.js): add maxOutputTokens field to conversation preset schema
feat(presetSchema.js): add examples field to preset schema
feat(askGoogle.js): add support for examples and promptPrefix in endpointOption
feat(EditPresetDialog.jsx): add Examples component for Google endpoint
feat(EditPresetDialog.jsx): add button to show/hide Examples component
feat(EditPresetDialog.jsx): add functionality to add, remove, and edit examples in Examples component
feat(EndpointOptionsDialog.jsx): change endpoint name to PaLM for Google endpoint
feat(Settings.jsx): add maxHeight prop to limit height of Settings component in EditPresetDialog and EndpointOptionsDialog
fix(Settings.jsx): add examples prop to ChatGPTBrowser component
fix(EndpointItem.jsx): add alternate name for google endpoint
fix(MessageHeader.jsx): change title for google endpoint to PaLM
feat(endpoints.js): add google endpoint to endpointsConfig
fix(cleanupPreset.js): add missing comma in examples array
* chore: change endpoint order
* feat(PaLM 2): complete for testing
* fix(PaLM): handle blocked messages
This commit is contained in:
parent
95c97561ae
commit
3414690e42
40 changed files with 2369 additions and 79 deletions
|
|
@ -89,6 +89,33 @@ CHATGPT_MODELS=text-davinci-002-render-sha,text-davinci-002-render-paid,gpt-4
|
|||
# By default, the server will use the node-chatgpt-api recommended proxy (a third party server).
|
||||
# CHATGPT_REVERSE_PROXY=
|
||||
|
||||
##########################
|
||||
# PaLM (Google) Endpoint:
|
||||
##########################
|
||||
|
||||
# PaLM 2 Client (via Google Cloud Vertex AI API)
|
||||
# Steps:
|
||||
# Enable the Vertex AI API on Google Cloud:
|
||||
# https://console.cloud.google.com/vertex-ai
|
||||
# Create a Service Account:
|
||||
# https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create?walkthrough_id=iam--create-service-account#step_index=1
|
||||
# Make sure to click 'Create and Continue' to give at least the 'Vertex AI User' role.
|
||||
# Create a JSON key, rename as 'auth.json' and save it in /api/data/.
|
||||
# Alternatively
|
||||
# Uncomment below PALM_KEY and set as "user_provided" to allow the user to provide a Service Account key JSON from the UI.
|
||||
# They will follow the steps above except for renaming the file.
|
||||
# Leave blank or omit to disable this endpoint
|
||||
|
||||
# PALM_KEY="user_provided"
|
||||
|
||||
# In case you need a reverse proxy for this endpoint:
|
||||
# GOOGLE_REVERSE_PROXY=
|
||||
|
||||
##########################
|
||||
# Proxy: To be Used by all endpoints
|
||||
##########################
|
||||
PROXY=
|
||||
|
||||
##########################
|
||||
# Search:
|
||||
##########################
|
||||
|
|
|
|||
390
api/app/google/GoogleClient.js
Normal file
390
api/app/google/GoogleClient.js
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
const crypto = require('crypto');
|
||||
const TextStream = require('../stream');
|
||||
const { google } = require('googleapis');
|
||||
const { Agent, ProxyAgent } = require('undici');
|
||||
const { getMessages, saveMessage, saveConvo } = require('../../models');
|
||||
const { encoding_for_model: encodingForModel, get_encoding: getEncoding } = require('@dqbd/tiktoken');
|
||||
|
||||
const tokenizersCache = {};
|
||||
|
||||
class GoogleAgent {
|
||||
constructor(credentials, options = {}) {
|
||||
this.client_email = credentials.client_email;
|
||||
this.project_id = credentials.project_id;
|
||||
this.private_key = credentials.private_key;
|
||||
this.setOptions(options);
|
||||
this.currentDateString = new Date().toLocaleDateString('en-us', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
constructUrl() {
|
||||
return `https://us-central1-aiplatform.googleapis.com/v1/projects/${this.project_id}/locations/us-central1/publishers/google/models/${this.modelOptions.model}:predict`;
|
||||
}
|
||||
|
||||
setOptions(options) {
|
||||
if (this.options && !this.options.replaceOptions) {
|
||||
// nested options aren't spread properly, so we need to do this manually
|
||||
this.options.modelOptions = {
|
||||
...this.options.modelOptions,
|
||||
...options.modelOptions
|
||||
};
|
||||
delete options.modelOptions;
|
||||
// now we can merge options
|
||||
this.options = {
|
||||
...this.options,
|
||||
...options
|
||||
};
|
||||
} else {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
this.options.examples = this.options.examples.filter(
|
||||
obj => obj.input.content !== '' && obj.output.content !== ''
|
||||
);
|
||||
|
||||
const modelOptions = this.options.modelOptions || {};
|
||||
this.modelOptions = {
|
||||
...modelOptions,
|
||||
// set some good defaults (check for undefined in some cases because they may be 0)
|
||||
model: modelOptions.model || 'chat-bison',
|
||||
temperature: typeof modelOptions.temperature === 'undefined' ? 0.2 : modelOptions.temperature, // 0 - 1, 0.2 is recommended
|
||||
topP: typeof modelOptions.topP === 'undefined' ? 0.95 : modelOptions.topP, // 0 - 1, default: 0.95
|
||||
topK: typeof modelOptions.topK === 'undefined' ? 40 : modelOptions.topK // 1-40, default: 40
|
||||
// stop: modelOptions.stop // no stop method for now
|
||||
};
|
||||
|
||||
this.isChatModel = this.modelOptions.model.startsWith('chat-');
|
||||
const { isChatModel } = this;
|
||||
this.isTextModel = this.modelOptions.model.startsWith('text-');
|
||||
const { isTextModel } = this;
|
||||
|
||||
this.maxContextTokens = this.options.maxContextTokens || (isTextModel ? 8000 : 4096);
|
||||
// The max prompt tokens is determined by the max context tokens minus the max response tokens.
|
||||
// Earlier messages will be dropped until the prompt is within the limit.
|
||||
this.maxResponseTokens = this.modelOptions.maxOutputTokens || 1024;
|
||||
this.maxPromptTokens = this.options.maxPromptTokens || this.maxContextTokens - this.maxResponseTokens;
|
||||
|
||||
if (this.maxPromptTokens + this.maxResponseTokens > this.maxContextTokens) {
|
||||
throw new Error(
|
||||
`maxPromptTokens + maxOutputTokens (${this.maxPromptTokens} + ${this.maxResponseTokens} = ${
|
||||
this.maxPromptTokens + this.maxResponseTokens
|
||||
}) must be less than or equal to maxContextTokens (${this.maxContextTokens})`
|
||||
);
|
||||
}
|
||||
|
||||
this.userLabel = this.options.userLabel || 'User';
|
||||
this.modelLabel = this.options.modelLabel || 'Assistant';
|
||||
|
||||
if (isChatModel) {
|
||||
// Use these faux tokens to help the AI understand the context since we are building the chat log ourselves.
|
||||
// Trying to use "<|im_start|>" causes the AI to still generate "<" or "<|" at the end sometimes for some reason,
|
||||
// without tripping the stop sequences, so I'm using "||>" instead.
|
||||
this.startToken = '||>';
|
||||
this.endToken = '';
|
||||
this.gptEncoder = this.constructor.getTokenizer('cl100k_base');
|
||||
} else if (isTextModel) {
|
||||
this.startToken = '<|im_start|>';
|
||||
this.endToken = '<|im_end|>';
|
||||
this.gptEncoder = this.constructor.getTokenizer('text-davinci-003', true, {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265
|
||||
});
|
||||
} else {
|
||||
// Previously I was trying to use "<|endoftext|>" but there seems to be some bug with OpenAI's token counting
|
||||
// system that causes only the first "<|endoftext|>" to be counted as 1 token, and the rest are not treated
|
||||
// as a single token. So we're using this instead.
|
||||
this.startToken = '||>';
|
||||
this.endToken = '';
|
||||
try {
|
||||
this.gptEncoder = this.constructor.getTokenizer(this.modelOptions.model, true);
|
||||
} catch {
|
||||
this.gptEncoder = this.constructor.getTokenizer('text-davinci-003', true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.modelOptions.stop) {
|
||||
const stopTokens = [this.startToken];
|
||||
if (this.endToken && this.endToken !== this.startToken) {
|
||||
stopTokens.push(this.endToken);
|
||||
}
|
||||
stopTokens.push(`\n${this.userLabel}:`);
|
||||
stopTokens.push('<|diff_marker|>');
|
||||
// I chose not to do one for `modelLabel` because I've never seen it happen
|
||||
this.modelOptions.stop = stopTokens;
|
||||
}
|
||||
|
||||
if (this.options.reverseProxyUrl) {
|
||||
this.completionsUrl = this.options.reverseProxyUrl;
|
||||
} else {
|
||||
this.completionsUrl = this.constructUrl();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
static getTokenizer(encoding, isModelName = false, extendSpecialTokens = {}) {
|
||||
if (tokenizersCache[encoding]) {
|
||||
return tokenizersCache[encoding];
|
||||
}
|
||||
let tokenizer;
|
||||
if (isModelName) {
|
||||
tokenizer = encodingForModel(encoding, extendSpecialTokens);
|
||||
} else {
|
||||
tokenizer = getEncoding(encoding, extendSpecialTokens);
|
||||
}
|
||||
tokenizersCache[encoding] = tokenizer;
|
||||
return tokenizer;
|
||||
}
|
||||
|
||||
async getClient() {
|
||||
const scopes = ['https://www.googleapis.com/auth/cloud-platform'];
|
||||
const jwtClient = new google.auth.JWT(this.client_email, null, this.private_key, scopes);
|
||||
|
||||
jwtClient.authorize((err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
return jwtClient;
|
||||
}
|
||||
|
||||
buildPayload(input, { messages = [] }) {
|
||||
let payload = {
|
||||
instances: [
|
||||
{
|
||||
messages: [...messages, { author: this.userLabel, content: input }]
|
||||
}
|
||||
],
|
||||
parameters: this.options.modelOptions
|
||||
};
|
||||
|
||||
if (this.options.promptPrefix) {
|
||||
payload.instances[0].context = this.options.promptPrefix;
|
||||
}
|
||||
|
||||
if (this.options.examples.length > 0) {
|
||||
payload.instances[0].examples = this.options.examples;
|
||||
}
|
||||
|
||||
if (this.isTextModel) {
|
||||
payload.instances = [
|
||||
{
|
||||
prompt: input
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (this.options.debug) {
|
||||
console.debug('buildPayload');
|
||||
console.dir(payload, { depth: null });
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
async getCompletion(input, messages = [], abortController = null) {
|
||||
if (!abortController) {
|
||||
abortController = new AbortController();
|
||||
}
|
||||
const { debug } = this.options;
|
||||
const url = this.completionsUrl;
|
||||
if (debug) {
|
||||
console.debug();
|
||||
console.debug(url);
|
||||
console.debug(this.modelOptions);
|
||||
console.debug();
|
||||
}
|
||||
const opts = {
|
||||
method: 'POST',
|
||||
agent: new Agent({
|
||||
bodyTimeout: 0,
|
||||
headersTimeout: 0
|
||||
}),
|
||||
signal: abortController.signal
|
||||
};
|
||||
|
||||
if (this.options.proxy) {
|
||||
opts.agent = new ProxyAgent(this.options.proxy);
|
||||
}
|
||||
|
||||
const client = await this.getClient();
|
||||
const payload = this.buildPayload(input, { messages });
|
||||
const res = await client.request({ url, method: 'POST', data: payload });
|
||||
console.dir(res.data, { depth: null });
|
||||
return res.data;
|
||||
}
|
||||
|
||||
async loadHistory(conversationId, parentMessageId = null) {
|
||||
if (this.options.debug) {
|
||||
console.debug('Loading history for conversation', conversationId, parentMessageId);
|
||||
}
|
||||
|
||||
if (!parentMessageId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const messages = (await getMessages({ conversationId })) || [];
|
||||
|
||||
if (messages.length === 0) {
|
||||
this.currentMessages = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
const orderedMessages = this.constructor.getMessagesForConversation(messages, parentMessageId);
|
||||
return orderedMessages.map((message) => {
|
||||
return {
|
||||
author: message.isCreatedByUser ? this.userLabel : this.modelLabel,
|
||||
content: message.content
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async saveMessageToDatabase(message, user = null) {
|
||||
await saveMessage({ ...message, unfinished: false });
|
||||
await saveConvo(user, {
|
||||
conversationId: message.conversationId,
|
||||
endpoint: 'google',
|
||||
...this.modelOptions
|
||||
});
|
||||
}
|
||||
|
||||
async sendMessage(message, opts = {}) {
|
||||
if (opts && typeof opts === 'object') {
|
||||
this.setOptions(opts);
|
||||
}
|
||||
console.log('sendMessage', message, opts);
|
||||
|
||||
const user = opts.user || null;
|
||||
const conversationId = opts.conversationId || crypto.randomUUID();
|
||||
const parentMessageId = opts.parentMessageId || '00000000-0000-0000-0000-000000000000';
|
||||
const userMessageId = crypto.randomUUID();
|
||||
const responseMessageId = crypto.randomUUID();
|
||||
const messages = await this.loadHistory(conversationId, this.options?.parentMessageId);
|
||||
|
||||
const userMessage = {
|
||||
messageId: userMessageId,
|
||||
parentMessageId,
|
||||
conversationId,
|
||||
sender: 'User',
|
||||
text: message,
|
||||
isCreatedByUser: true
|
||||
};
|
||||
|
||||
if (typeof opts?.getIds === 'function') {
|
||||
opts.getIds({
|
||||
userMessage,
|
||||
conversationId,
|
||||
responseMessageId
|
||||
});
|
||||
}
|
||||
|
||||
console.log('userMessage', userMessage);
|
||||
|
||||
await this.saveMessageToDatabase(userMessage, user);
|
||||
let reply = '';
|
||||
let blocked = false;
|
||||
try {
|
||||
const result = await this.getCompletion(message, messages, opts.abortController);
|
||||
blocked = result?.predictions?.[0]?.safetyAttributes?.blocked;
|
||||
reply = result?.predictions?.[0]?.candidates?.[0]?.content || result?.predictions?.[0]?.content || '';
|
||||
if (blocked === true) {
|
||||
reply = `Google blocked a proper response to your message:\n${JSON.stringify(
|
||||
result.predictions[0].safetyAttributes
|
||||
)}${reply.length > 0 ? `\nAI Response:\n${reply}` : ''}`;
|
||||
}
|
||||
if (this.options.debug) {
|
||||
console.debug('result');
|
||||
console.debug(result);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
if (this.options.debug) {
|
||||
console.debug('options');
|
||||
console.debug(this.options);
|
||||
}
|
||||
|
||||
if (!blocked) {
|
||||
const textStream = new TextStream(reply, { delay: 0.5 });
|
||||
await textStream.processTextStream(opts.onProgress);
|
||||
}
|
||||
|
||||
const responseMessage = {
|
||||
messageId: responseMessageId,
|
||||
conversationId,
|
||||
parentMessageId: userMessage.messageId,
|
||||
sender: 'PaLM2',
|
||||
text: reply,
|
||||
error: blocked,
|
||||
isCreatedByUser: false
|
||||
};
|
||||
|
||||
await this.saveMessageToDatabase(responseMessage, user);
|
||||
return responseMessage;
|
||||
}
|
||||
|
||||
getTokenCount(text) {
|
||||
return this.gptEncoder.encode(text, 'all').length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm adapted from "6. Counting tokens for chat API calls" of
|
||||
* https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
|
||||
*
|
||||
* An additional 2 tokens need to be added for metadata after all messages have been counted.
|
||||
*
|
||||
* @param {*} message
|
||||
*/
|
||||
getTokenCountForMessage(message) {
|
||||
// Map each property of the message to the number of tokens it contains
|
||||
const propertyTokenCounts = Object.entries(message).map(([key, value]) => {
|
||||
// Count the number of tokens in the property value
|
||||
const numTokens = this.getTokenCount(value);
|
||||
|
||||
// Subtract 1 token if the property key is 'name'
|
||||
const adjustment = key === 'name' ? 1 : 0;
|
||||
return numTokens - adjustment;
|
||||
});
|
||||
|
||||
// Sum the number of tokens in all properties and add 4 for metadata
|
||||
return propertyTokenCounts.reduce((a, b) => a + b, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through messages, building an array based on the parentMessageId.
|
||||
* Each message has an id and a parentMessageId. The parentMessageId is the id of the message that this message is a reply to.
|
||||
* @param messages
|
||||
* @param parentMessageId
|
||||
* @returns {*[]} An array containing the messages in the order they should be displayed, starting with the root message.
|
||||
*/
|
||||
static getMessagesForConversation(messages, parentMessageId) {
|
||||
const orderedMessages = [];
|
||||
let currentMessageId = parentMessageId;
|
||||
while (currentMessageId) {
|
||||
// eslint-disable-next-line no-loop-func
|
||||
const message = messages.find(m => m.messageId === currentMessageId);
|
||||
if (!message) {
|
||||
break;
|
||||
}
|
||||
orderedMessages.unshift(message);
|
||||
currentMessageId = message.parentMessageId;
|
||||
}
|
||||
|
||||
if (orderedMessages.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return orderedMessages.map(msg => ({
|
||||
isCreatedByUser: msg.isCreatedByUser,
|
||||
content: msg.text
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GoogleAgent;
|
||||
62
api/app/stream.js
Normal file
62
api/app/stream.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
const { Readable } = require('stream');
|
||||
|
||||
class TextStream extends Readable {
|
||||
constructor(text, options = {}) {
|
||||
super(options);
|
||||
this.text = text;
|
||||
this.currentIndex = 0;
|
||||
this.delay = options.delay || 20; // Time in milliseconds
|
||||
}
|
||||
|
||||
_read() {
|
||||
const minChunkSize = 2;
|
||||
const maxChunkSize = 4;
|
||||
const { delay } = this;
|
||||
|
||||
if (this.currentIndex < this.text.length) {
|
||||
setTimeout(() => {
|
||||
const remainingChars = this.text.length - this.currentIndex;
|
||||
const chunkSize = Math.min(
|
||||
this.randomInt(minChunkSize, maxChunkSize + 1),
|
||||
remainingChars
|
||||
);
|
||||
|
||||
const chunk = this.text.slice(this.currentIndex, this.currentIndex + chunkSize);
|
||||
this.push(chunk);
|
||||
this.currentIndex += chunkSize;
|
||||
}, delay);
|
||||
} else {
|
||||
this.push(null); // signal end of data
|
||||
}
|
||||
}
|
||||
|
||||
randomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min)) + min;
|
||||
}
|
||||
|
||||
async processTextStream(onProgressCallback) {
|
||||
const streamPromise = new Promise((resolve, reject) => {
|
||||
this.on('data', (chunk) => {
|
||||
onProgressCallback(chunk.toString());
|
||||
});
|
||||
|
||||
this.on('end', () => {
|
||||
console.log('Stream ended');
|
||||
resolve();
|
||||
});
|
||||
|
||||
this.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await streamPromise;
|
||||
} catch (err) {
|
||||
console.error('Error processing text stream:', err);
|
||||
// Handle the error appropriately, e.g., return an error message or throw an error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TextStream;
|
||||
|
|
@ -17,6 +17,12 @@ module.exports = {
|
|||
default: null,
|
||||
required: false
|
||||
},
|
||||
// for google only
|
||||
modelLabel: {
|
||||
type: String,
|
||||
default: null,
|
||||
required: false
|
||||
},
|
||||
promptPrefix: {
|
||||
type: String,
|
||||
default: null,
|
||||
|
|
@ -32,6 +38,22 @@ module.exports = {
|
|||
default: 1,
|
||||
required: false
|
||||
},
|
||||
// for google only
|
||||
topP: {
|
||||
type: Number,
|
||||
default: 0.95,
|
||||
required: false
|
||||
},
|
||||
topK: {
|
||||
type: Number,
|
||||
default: 40,
|
||||
required: false
|
||||
},
|
||||
maxOutputTokens: {
|
||||
type: Number,
|
||||
default: 1024,
|
||||
required: false
|
||||
},
|
||||
presence_penalty: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ const convoSchema = mongoose.Schema(
|
|||
default: null
|
||||
},
|
||||
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
|
||||
// google only
|
||||
examples: [{ type: mongoose.Schema.Types.Mixed }],
|
||||
...conversationPreset,
|
||||
// for bingAI only
|
||||
jailbreakConversationId: {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ const presetSchema = mongoose.Schema(
|
|||
type: String,
|
||||
default: null
|
||||
},
|
||||
// google only
|
||||
examples: [{ type: mongoose.Schema.Types.Mixed }],
|
||||
...conversationPreset
|
||||
},
|
||||
{ timestamps: true }
|
||||
|
|
|
|||
357
api/package-lock.json
generated
357
api/package-lock.json
generated
|
|
@ -22,6 +22,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"eslint": "^8.36.0",
|
||||
"express": "^4.18.2",
|
||||
"googleapis": "^118.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"html": "^1.0.0",
|
||||
"joi": "^14.3.1",
|
||||
|
|
@ -1998,6 +1999,14 @@
|
|||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/arrify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
|
||||
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
|
@ -2081,6 +2090,14 @@
|
|||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
|
||||
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
|
||||
},
|
||||
"node_modules/bignumber.js": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
|
||||
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
|
|
@ -3017,6 +3034,11 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"node_modules/external-editor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
|
||||
|
|
@ -3109,6 +3131,11 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-text-encoding": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
|
||||
"integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.2.0.tgz",
|
||||
|
|
@ -3445,6 +3472,32 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/gaxios": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.0.tgz",
|
||||
"integrity": "sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==",
|
||||
"dependencies": {
|
||||
"extend": "^3.0.2",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"node-fetch": "^2.6.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/gcp-metadata": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz",
|
||||
"integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==",
|
||||
"dependencies": {
|
||||
"gaxios": "^5.0.0",
|
||||
"json-bigint": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
|
|
@ -3529,6 +3582,94 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.8.0.tgz",
|
||||
"integrity": "sha512-0iJn7IDqObDG5Tu9Tn2WemmJ31ksEa96IyK0J0OZCpTh6CrC6FrattwKX87h3qKVuprCJpdOGKc1Xi8V0kMh8Q==",
|
||||
"dependencies": {
|
||||
"arrify": "^2.0.0",
|
||||
"base64-js": "^1.3.0",
|
||||
"ecdsa-sig-formatter": "^1.0.11",
|
||||
"fast-text-encoding": "^1.0.0",
|
||||
"gaxios": "^5.0.0",
|
||||
"gcp-metadata": "^5.2.0",
|
||||
"gtoken": "^6.1.0",
|
||||
"jws": "^4.0.0",
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/google-p12-pem": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz",
|
||||
"integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==",
|
||||
"dependencies": {
|
||||
"node-forge": "^1.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"gp12-pem": "build/src/bin/gp12-pem.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/googleapis": {
|
||||
"version": "118.0.0",
|
||||
"resolved": "https://registry.npmjs.org/googleapis/-/googleapis-118.0.0.tgz",
|
||||
"integrity": "sha512-Ny6zJOGn5P/YDT6GQbJU6K0lSzEu4Yuxnsn45ZgBIeSQ1RM0FolEjUToLXquZd89DU9wUfqA5XYHPEctk1TFWg==",
|
||||
"dependencies": {
|
||||
"google-auth-library": "^8.0.2",
|
||||
"googleapis-common": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/googleapis-common": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-6.0.4.tgz",
|
||||
"integrity": "sha512-m4ErxGE8unR1z0VajT6AYk3s6a9gIMM6EkDZfkPnES8joeOlEtFEJeF8IyZkb0tjPXkktUfYrE4b3Li1DNyOwA==",
|
||||
"dependencies": {
|
||||
"extend": "^3.0.2",
|
||||
"gaxios": "^5.0.1",
|
||||
"google-auth-library": "^8.0.2",
|
||||
"qs": "^6.7.0",
|
||||
"url-template": "^2.0.8",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/googleapis-common/node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
|
|
@ -3539,6 +3680,38 @@
|
|||
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
|
||||
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
|
||||
},
|
||||
"node_modules/gtoken": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz",
|
||||
"integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==",
|
||||
"dependencies": {
|
||||
"gaxios": "^5.0.1",
|
||||
"google-p12-pem": "^4.0.0",
|
||||
"jws": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gtoken/node_modules/jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/gtoken/node_modules/jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/handlebars": {
|
||||
"version": "4.7.7",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
|
||||
|
|
@ -4025,6 +4198,14 @@
|
|||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||
"dependencies": {
|
||||
"bignumber.js": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
|
|
@ -4486,6 +4667,14 @@
|
|||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
|
||||
"engines": {
|
||||
"node": ">= 6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz",
|
||||
|
|
@ -5903,6 +6092,11 @@
|
|||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-template": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
|
||||
"integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="
|
||||
},
|
||||
"node_modules/util": {
|
||||
"version": "0.10.4",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||
|
|
@ -7901,6 +8095,11 @@
|
|||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"arrify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
|
||||
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
|
@ -7960,6 +8159,11 @@
|
|||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
|
||||
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
|
||||
},
|
||||
"bignumber.js": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
|
||||
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig=="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
|
|
@ -8644,6 +8848,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"external-editor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
|
||||
|
|
@ -8728,6 +8937,11 @@
|
|||
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz",
|
||||
"integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw=="
|
||||
},
|
||||
"fast-text-encoding": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
|
||||
"integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
|
||||
},
|
||||
"fast-uri": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.2.0.tgz",
|
||||
|
|
@ -8983,6 +9197,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"gaxios": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.0.tgz",
|
||||
"integrity": "sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==",
|
||||
"requires": {
|
||||
"extend": "^3.0.2",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
},
|
||||
"gcp-metadata": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz",
|
||||
"integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==",
|
||||
"requires": {
|
||||
"gaxios": "^5.0.0",
|
||||
"json-bigint": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
|
|
@ -9039,6 +9273,80 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"google-auth-library": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.8.0.tgz",
|
||||
"integrity": "sha512-0iJn7IDqObDG5Tu9Tn2WemmJ31ksEa96IyK0J0OZCpTh6CrC6FrattwKX87h3qKVuprCJpdOGKc1Xi8V0kMh8Q==",
|
||||
"requires": {
|
||||
"arrify": "^2.0.0",
|
||||
"base64-js": "^1.3.0",
|
||||
"ecdsa-sig-formatter": "^1.0.11",
|
||||
"fast-text-encoding": "^1.0.0",
|
||||
"gaxios": "^5.0.0",
|
||||
"gcp-metadata": "^5.2.0",
|
||||
"gtoken": "^6.1.0",
|
||||
"jws": "^4.0.0",
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"requires": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"requires": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"google-p12-pem": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz",
|
||||
"integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==",
|
||||
"requires": {
|
||||
"node-forge": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"googleapis": {
|
||||
"version": "118.0.0",
|
||||
"resolved": "https://registry.npmjs.org/googleapis/-/googleapis-118.0.0.tgz",
|
||||
"integrity": "sha512-Ny6zJOGn5P/YDT6GQbJU6K0lSzEu4Yuxnsn45ZgBIeSQ1RM0FolEjUToLXquZd89DU9wUfqA5XYHPEctk1TFWg==",
|
||||
"requires": {
|
||||
"google-auth-library": "^8.0.2",
|
||||
"googleapis-common": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"googleapis-common": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-6.0.4.tgz",
|
||||
"integrity": "sha512-m4ErxGE8unR1z0VajT6AYk3s6a9gIMM6EkDZfkPnES8joeOlEtFEJeF8IyZkb0tjPXkktUfYrE4b3Li1DNyOwA==",
|
||||
"requires": {
|
||||
"extend": "^3.0.2",
|
||||
"gaxios": "^5.0.1",
|
||||
"google-auth-library": "^8.0.2",
|
||||
"qs": "^6.7.0",
|
||||
"url-template": "^2.0.8",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
|
|
@ -9049,6 +9357,37 @@
|
|||
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
|
||||
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
|
||||
},
|
||||
"gtoken": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz",
|
||||
"integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==",
|
||||
"requires": {
|
||||
"gaxios": "^5.0.1",
|
||||
"google-p12-pem": "^4.0.0",
|
||||
"jws": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"requires": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"requires": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.7.7",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
|
||||
|
|
@ -9387,6 +9726,14 @@
|
|||
"argparse": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||
"requires": {
|
||||
"bignumber.js": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
|
|
@ -9747,6 +10094,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
|
||||
},
|
||||
"nodemailer": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz",
|
||||
|
|
@ -10802,6 +11154,11 @@
|
|||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"url-template": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
|
||||
"integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="
|
||||
},
|
||||
"util": {
|
||||
"version": "0.10.4",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"eslint": "^8.36.0",
|
||||
"express": "^4.18.2",
|
||||
"googleapis": "^118.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"html": "^1.0.0",
|
||||
"joi": "^14.3.1",
|
||||
|
|
|
|||
156
api/server/routes/ask/askGoogle.js
Normal file
156
api/server/routes/ask/askGoogle.js
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { titleConvo } = require('../../../app/');
|
||||
const GoogleClient = require('../../../app/google/GoogleClient');
|
||||
const { saveMessage, getConvoTitle, saveConvo, getConvo } = require('../../../models');
|
||||
const { handleError, sendMessage, createOnProgress } = require('./handlers');
|
||||
const requireJwtAuth = require('../../../middleware/requireJwtAuth');
|
||||
|
||||
router.post('/', requireJwtAuth, async (req, res) => {
|
||||
const { endpoint, text, parentMessageId, conversationId } = req.body;
|
||||
if (text.length === 0) return handleError(res, { text: 'Prompt empty or too short' });
|
||||
if (endpoint !== 'google') return handleError(res, { text: 'Illegal request' });
|
||||
|
||||
// build endpoint option
|
||||
const endpointOption = {
|
||||
examples: req.body?.examples ?? [{ input: { content: '' }, output: { content: '' } }],
|
||||
promptPrefix: req.body?.promptPrefix ?? null,
|
||||
token: req.body?.token ?? null,
|
||||
modelOptions: {
|
||||
model: req.body?.model ?? 'chat-bison',
|
||||
modelLabel: req.body?.modelLabel ?? null,
|
||||
temperature: req.body?.temperature ?? 0.2,
|
||||
maxOutputTokens: req.body?.maxOutputTokens ?? 1024,
|
||||
topP: req.body?.topP ?? 0.95,
|
||||
topK: req.body?.topK ?? 40
|
||||
}
|
||||
};
|
||||
|
||||
const availableModels = ['chat-bison', 'text-bison'];
|
||||
if (availableModels.find(model => model === endpointOption.modelOptions.model) === undefined) {
|
||||
return handleError(res, { text: `Illegal request: model` });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
return await ask({
|
||||
text,
|
||||
endpointOption,
|
||||
conversationId,
|
||||
parentMessageId,
|
||||
req,
|
||||
res
|
||||
});
|
||||
});
|
||||
|
||||
const ask = async ({ text, endpointOption, parentMessageId = null, conversationId, req, res }) => {
|
||||
res.writeHead(200, {
|
||||
Connection: 'keep-alive',
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache, no-transform',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'X-Accel-Buffering': 'no'
|
||||
});
|
||||
let userMessage;
|
||||
let userMessageId;
|
||||
let responseMessageId;
|
||||
let lastSavedTimestamp = 0;
|
||||
|
||||
try {
|
||||
const getIds = (data) => {
|
||||
userMessage = data.userMessage;
|
||||
userMessageId = userMessage.messageId;
|
||||
responseMessageId = data.responseMessageId;
|
||||
if (!conversationId) {
|
||||
conversationId = data.conversationId;
|
||||
}
|
||||
};
|
||||
|
||||
const { onProgress: progressCallback } = createOnProgress({
|
||||
onProgress: ({ text: partialText }) => {
|
||||
const currentTimestamp = Date.now();
|
||||
if (currentTimestamp - lastSavedTimestamp > 500) {
|
||||
lastSavedTimestamp = currentTimestamp;
|
||||
saveMessage({
|
||||
messageId: responseMessageId,
|
||||
sender: 'PaLM2',
|
||||
conversationId,
|
||||
parentMessageId: userMessageId,
|
||||
text: partialText,
|
||||
unfinished: true,
|
||||
cancelled: false,
|
||||
error: false
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const abortController = new AbortController();
|
||||
|
||||
let key;
|
||||
if (endpointOption.token) {
|
||||
key = JSON.parse(endpointOption.token);
|
||||
delete endpointOption.token;
|
||||
console.log('Using service account key provided by User for PaLM models');
|
||||
}
|
||||
|
||||
try {
|
||||
if (!key) {
|
||||
key = require('../../data/auth.json');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("No 'auth.json' file (service account key) found in /api/data/ for PaLM models");
|
||||
}
|
||||
|
||||
const clientOptions = {
|
||||
// debug: true, // for testing
|
||||
reverseProxyUrl: process.env.GOOGLE_REVERSE_PROXY || null,
|
||||
proxy: process.env.PROXY || null,
|
||||
...endpointOption
|
||||
};
|
||||
|
||||
const client = new GoogleClient(key, clientOptions);
|
||||
|
||||
let response = await client.sendMessage(text, {
|
||||
getIds,
|
||||
user: req.user.id,
|
||||
parentMessageId,
|
||||
conversationId,
|
||||
onProgress: progressCallback.call(null, { res, text, parentMessageId: userMessageId }),
|
||||
abortController
|
||||
});
|
||||
|
||||
await saveMessage(response);
|
||||
sendMessage(res, {
|
||||
title: await getConvoTitle(req.user.id, conversationId),
|
||||
final: true,
|
||||
conversation: await getConvo(req.user.id, conversationId),
|
||||
requestMessage: userMessage,
|
||||
responseMessage: response
|
||||
});
|
||||
res.end();
|
||||
|
||||
if (parentMessageId == '00000000-0000-0000-0000-000000000000') {
|
||||
const title = await titleConvo({ text, response });
|
||||
await saveConvo(req.user.id, {
|
||||
conversationId: conversationId,
|
||||
title
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const errorMessage = {
|
||||
messageId: responseMessageId,
|
||||
sender: 'PaLM2',
|
||||
conversationId,
|
||||
parentMessageId,
|
||||
unfinished: false,
|
||||
cancelled: false,
|
||||
error: true,
|
||||
text: error.message
|
||||
};
|
||||
await saveMessage(errorMessage);
|
||||
handleError(res, errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -2,11 +2,13 @@ const express = require('express');
|
|||
const router = express.Router();
|
||||
// const askAzureOpenAI = require('./askAzureOpenAI';)
|
||||
const askOpenAI = require('./askOpenAI');
|
||||
const askGoogle = require('./askGoogle');
|
||||
const askBingAI = require('./askBingAI');
|
||||
const askChatGPTBrowser = require('./askChatGPTBrowser');
|
||||
|
||||
// router.use('/azureOpenAI', askAzureOpenAI);
|
||||
router.use('/openAI', askOpenAI);
|
||||
router.use('/google', askGoogle);
|
||||
router.use('/bingAI', askBingAI);
|
||||
router.use('/chatGPTBrowser', askChatGPTBrowser);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,33 @@ const getChatGPTBrowserModels = () => {
|
|||
return models;
|
||||
};
|
||||
|
||||
router.get('/', function (req, res) {
|
||||
let i = 0;
|
||||
router.get('/', async function (req, res) {
|
||||
let key, palmUser;
|
||||
try {
|
||||
key = require('../../data/auth.json');
|
||||
} catch (e) {
|
||||
if (i === 0) {
|
||||
console.log("No 'auth.json' file (service account key) found in /api/data/ for PaLM models");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.PALM_KEY === 'user_provided') {
|
||||
palmUser = true;
|
||||
if (i <= 1) {
|
||||
console.log('User will provide key for PaLM models');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
const google =
|
||||
key || palmUser ? { userProvide: palmUser, availableModels: ['chat-bison', 'text-bison'] } : false;
|
||||
const azureOpenAI = !!process.env.AZURE_OPENAI_KEY;
|
||||
const openAI = process.env.OPENAI_KEY || process.env.AZURE_OPENAI_API_KEY ? { availableModels: getOpenAIModels() } : false;
|
||||
const openAI =
|
||||
process.env.OPENAI_KEY || process.env.AZURE_OPENAI_API_KEY
|
||||
? { availableModels: getOpenAIModels() }
|
||||
: false;
|
||||
const bingAI = process.env.BINGAI_TOKEN
|
||||
? { userProvide: process.env.BINGAI_TOKEN == 'user_provided' }
|
||||
: false;
|
||||
|
|
@ -28,7 +52,7 @@ router.get('/', function (req, res) {
|
|||
}
|
||||
: false;
|
||||
|
||||
res.send(JSON.stringify({ azureOpenAI, openAI, bingAI, chatGPTBrowser }));
|
||||
res.send(JSON.stringify({ azureOpenAI, openAI, google, bingAI, chatGPTBrowser }));
|
||||
});
|
||||
|
||||
module.exports = { router, getOpenAIModels, getChatGPTBrowserModels };
|
||||
|
|
|
|||
BIN
client/public/assets/palm.png
Normal file
BIN
client/public/assets/palm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -40,7 +40,7 @@ function Settings(props) {
|
|||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="max-h-[350px] overflow-y-auto">
|
||||
<div className="grid gap-6 sm:grid-cols-2">
|
||||
<div className="col-span-1 flex flex-col items-center justify-start gap-6">
|
||||
<div className="grid w-full items-center gap-2">
|
||||
|
|
@ -141,7 +141,7 @@ function Settings(props) {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import Examples from './Google/Examples.jsx';
|
||||
import MessagesSquared from '~/components/svg/MessagesSquared.jsx';
|
||||
import { useSetRecoilState, useRecoilValue } from 'recoil';
|
||||
import filenamify from 'filenamify';
|
||||
import axios from 'axios';
|
||||
|
|
@ -7,6 +9,7 @@ import DialogTemplate from '../ui/DialogTemplate';
|
|||
import { Dialog, DialogClose, DialogButton } from '../ui/Dialog.tsx';
|
||||
import { Input } from '../ui/Input.tsx';
|
||||
import { Label } from '../ui/Label.tsx';
|
||||
import { Button } from '../ui/Button.tsx';
|
||||
import Dropdown from '../ui/Dropdown';
|
||||
import { cn } from '~/utils/';
|
||||
import cleanupPreset from '~/utils/cleanupPreset';
|
||||
|
|
@ -18,11 +21,14 @@ import store from '~/store';
|
|||
const EditPresetDialog = ({ open, onOpenChange, preset: _preset, title }) => {
|
||||
// const [title, setTitle] = useState('My Preset');
|
||||
const [preset, setPreset] = useState(_preset);
|
||||
const [showExamples, setShowExamples] = useState(false);
|
||||
const setPresets = useSetRecoilState(store.presets);
|
||||
|
||||
const availableEndpoints = useRecoilValue(store.availableEndpoints);
|
||||
const endpointsConfig = useRecoilValue(store.endpointsConfig);
|
||||
|
||||
const triggerExamples = () => setShowExamples(prev => !prev);
|
||||
|
||||
const setOption = param => newValue => {
|
||||
let update = {};
|
||||
update[param] = newValue;
|
||||
|
|
@ -37,6 +43,69 @@ const EditPresetDialog = ({ open, onOpenChange, preset: _preset, title }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const setExample = (i, type, newValue = null) => {
|
||||
let update = {};
|
||||
let current = preset?.examples.slice() || [];
|
||||
let currentExample = { ...current[i] } || {};
|
||||
currentExample[type] = { content: newValue };
|
||||
current[i] = currentExample;
|
||||
update.examples = current;
|
||||
setPreset(prevState =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update
|
||||
},
|
||||
endpointsConfig
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const addExample = () => {
|
||||
let update = {};
|
||||
let current = preset?.examples.slice() || [];
|
||||
current.push({ input: { content: '' }, output: { content: '' } });
|
||||
update.examples = current;
|
||||
setPreset(prevState =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update
|
||||
},
|
||||
endpointsConfig
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const removeExample = () => {
|
||||
let update = {};
|
||||
let current = preset?.examples.slice() || [];
|
||||
if (current.length <= 1) {
|
||||
update.examples = [{ input: { content: '' }, output: { content: '' } }];
|
||||
setPreset(prevState =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update
|
||||
},
|
||||
endpointsConfig
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
current.pop();
|
||||
update.examples = current;
|
||||
setPreset(prevState =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update
|
||||
},
|
||||
endpointsConfig
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const defaultTextProps =
|
||||
'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0';
|
||||
|
||||
|
|
@ -111,14 +180,35 @@ const EditPresetDialog = ({ open, onOpenChange, preset: _preset, title }) => {
|
|||
)}
|
||||
containerClassName="flex w-full resize-none"
|
||||
/>
|
||||
{preset?.endpoint === 'google' && (
|
||||
<Button
|
||||
type="button"
|
||||
className="ml-1 flex h-auto w-full bg-transparent px-2 py-1 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-700 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
onClick={triggerExamples}
|
||||
>
|
||||
<MessagesSquared className="mr-1 w-[14px]" />
|
||||
{(showExamples ? 'Hide' : 'Show') + ' Examples'}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-4 w-full border-t border-gray-300 dark:border-gray-500" />
|
||||
<div className="w-full p-0">
|
||||
<Settings
|
||||
preset={preset}
|
||||
setOption={setOption}
|
||||
/>
|
||||
{((preset?.endpoint === 'google' && !showExamples) || preset?.endpoint !== 'google') && (
|
||||
<Settings
|
||||
preset={_preset}
|
||||
setOption={setOption}
|
||||
/>
|
||||
)}
|
||||
{preset?.endpoint === 'google' && showExamples && (
|
||||
<Examples
|
||||
examples={preset.examples}
|
||||
setExample={setExample}
|
||||
addExample={addExample}
|
||||
removeExample={removeExample}
|
||||
edit={true}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,16 @@ import store from '~/store';
|
|||
|
||||
// A preset dialog to show readonly preset values.
|
||||
const EndpointOptionsDialog = ({ open, onOpenChange, preset: _preset, title }) => {
|
||||
// const [title, setTitle] = useState('My Preset');
|
||||
const [preset, setPreset] = useState(_preset);
|
||||
const [endpointName, setEndpointName] = useState(preset?.endpoint);
|
||||
|
||||
const [saveAsDialogShow, setSaveAsDialogShow] = useState(false);
|
||||
const endpointsConfig = useRecoilValue(store.endpointsConfig);
|
||||
|
||||
if (endpointName === 'google') {
|
||||
setEndpointName('PaLM');
|
||||
}
|
||||
|
||||
const setOption = param => newValue => {
|
||||
let update = {};
|
||||
update[param] = newValue;
|
||||
|
|
@ -50,7 +54,7 @@ const EndpointOptionsDialog = ({ open, onOpenChange, preset: _preset, title }) =
|
|||
onOpenChange={onOpenChange}
|
||||
>
|
||||
<DialogTemplate
|
||||
title={`${title || 'View Options'} - ${preset?.endpoint}`}
|
||||
title={`${title || 'View Options'} - ${endpointName}`}
|
||||
className="max-w-full sm:max-w-4xl"
|
||||
main={
|
||||
<div className="flex w-full flex-col items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@ import CrossIcon from '../svg/CrossIcon';
|
|||
// import SaveIcon from '../svg/SaveIcon';
|
||||
import { Save } from 'lucide-react';
|
||||
|
||||
function EndpointOptionsPopover({ content, visible, saveAsPreset, switchToSimpleMode }) {
|
||||
function EndpointOptionsPopover({
|
||||
content,
|
||||
visible,
|
||||
saveAsPreset,
|
||||
switchToSimpleMode,
|
||||
additionalButton = null
|
||||
}) {
|
||||
const cardStyle =
|
||||
'shadow-md rounded-md min-w-[75px] font-normal bg-white border-black/10 border dark:bg-gray-700 text-black dark:text-white';
|
||||
|
||||
|
|
@ -12,29 +18,39 @@ function EndpointOptionsPopover({ content, visible, saveAsPreset, switchToSimple
|
|||
<>
|
||||
<div
|
||||
className={
|
||||
' endpointOptionsPopover-container absolute bottom-[-10px] flex w-full flex-col items-center justify-center md:px-4' +
|
||||
' endpointOptionsPopover-container absolute bottom-[-10px] flex w-full flex-col items-center md:px-4' +
|
||||
(visible ? ' show' : '')
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
cardStyle +
|
||||
' border-s-0 border-d-0 flex w-full flex-col overflow-hidden rounded-none border-t bg-slate-200 px-0 pb-[10px] dark:border-white/10 md:rounded-md md:border lg:w-[736px]'
|
||||
' border-d-0 flex w-full flex-col overflow-hidden rounded-none border-s-0 border-t bg-slate-200 px-0 pb-[10px] dark:border-white/10 md:rounded-md md:border lg:w-[736px]'
|
||||
}
|
||||
>
|
||||
<div className="flex w-full items-center justify-between bg-slate-100 px-2 py-2 dark:bg-gray-800/60">
|
||||
<div className="flex w-full items-center bg-slate-100 px-2 py-2 dark:bg-gray-800/60">
|
||||
{/* <span className="text-xs font-medium font-normal">Advanced settings for OpenAI endpoint</span> */}
|
||||
<Button
|
||||
type="button"
|
||||
className="h-auto bg-transparent px-2 py-1 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black dark:bg-transparent dark:text-white dark:hover:bg-gray-700 dark:hover:text-white"
|
||||
className="h-auto justify-start bg-transparent px-2 py-1 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-700 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
onClick={saveAsPreset}
|
||||
>
|
||||
<Save className="mr-1 w-[14px]" />
|
||||
Save as preset
|
||||
</Button>
|
||||
{additionalButton && (
|
||||
<Button
|
||||
type="button"
|
||||
className="ml-1 h-auto justify-start bg-transparent px-2 py-1 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-700 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
onClick={additionalButton.handler}
|
||||
>
|
||||
{additionalButton.icon}
|
||||
{additionalButton.label}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="button"
|
||||
className="h-auto bg-transparent px-2 py-1 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black dark:bg-transparent dark:text-white dark:hover:bg-gray-700 dark:hover:text-white"
|
||||
className="ml-auto h-auto bg-transparent px-2 py-1 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-700 dark:hover:text-white"
|
||||
onClick={switchToSimpleMode}
|
||||
>
|
||||
<CrossIcon className="mr-1" />
|
||||
|
|
|
|||
98
client/src/components/Endpoints/Google/Examples.jsx
Normal file
98
client/src/components/Endpoints/Google/Examples.jsx
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import React from 'react';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { Button } from '~/components/ui/Button.tsx';
|
||||
import { Label } from '~/components/ui/Label.tsx';
|
||||
import { Plus, Minus } from 'lucide-react';
|
||||
import { cn } from '~/utils/';
|
||||
const defaultTextProps =
|
||||
'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0';
|
||||
|
||||
function Examples({ readonly, examples, setExample, addExample, removeExample, edit = false }) {
|
||||
const maxHeight = edit ? 'max-h-[233px]' : 'max-h-[350px]';
|
||||
return (
|
||||
<>
|
||||
<div className={`${maxHeight} overflow-y-auto`}>
|
||||
<div
|
||||
id="examples-grid"
|
||||
className="grid gap-6 sm:grid-cols-2"
|
||||
>
|
||||
{examples.map((example, idx) => (
|
||||
<React.Fragment key={idx}>
|
||||
{/* Input */}
|
||||
<div
|
||||
className={`col-span-${
|
||||
examples.length === 1 ? '1' : 'full'
|
||||
} flex flex-col items-center justify-start gap-6 sm:col-span-1`}
|
||||
>
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label
|
||||
htmlFor={`input-${idx}`}
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Input <small className="opacity-40">(default: blank)</small>
|
||||
</Label>
|
||||
<TextareaAutosize
|
||||
id={`input-${idx}`}
|
||||
disabled={readonly}
|
||||
value={example?.input?.content || ''}
|
||||
onChange={e => setExample(idx, 'input', e.target.value || null)}
|
||||
placeholder="Set example input. Example is ignored if empty."
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex max-h-[300px] min-h-[75px] w-full resize-none px-3 py-2 '
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Output */}
|
||||
<div
|
||||
className={`col-span-${
|
||||
examples.length === 1 ? '1' : 'full'
|
||||
} flex flex-col items-center justify-start gap-6 sm:col-span-1`}
|
||||
>
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label
|
||||
htmlFor={`output-${idx}`}
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Output <small className="opacity-40">(default: blank)</small>
|
||||
</Label>
|
||||
<TextareaAutosize
|
||||
id={`output-${idx}`}
|
||||
disabled={readonly}
|
||||
value={example?.output?.content || ''}
|
||||
onChange={e => setExample(idx, 'output', e.target.value || null)}
|
||||
placeholder={`Set example output. Example is ignored if empty.`}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex max-h-[300px] min-h-[75px] w-full resize-none px-3 py-2 '
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<Button
|
||||
type="button"
|
||||
className="mr-2 mt-1 h-auto items-center justify-center bg-transparent px-3 py-2 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-600 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
onClick={removeExample}
|
||||
>
|
||||
<Minus className="w-[16px]" />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
className="mt-1 h-auto items-center justify-center bg-transparent px-3 py-2 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-600 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
onClick={addExample}
|
||||
>
|
||||
<Plus className="w-[16px]" />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Examples;
|
||||
32
client/src/components/Endpoints/Google/OptionHover.jsx
Normal file
32
client/src/components/Endpoints/Google/OptionHover.jsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
import { HoverCardPortal, HoverCardContent } from '~/components/ui/HoverCard.tsx';
|
||||
|
||||
const types = {
|
||||
temp: 'Higher values = more random, while lower values = more focused and deterministic. We recommend altering this or Top P but not both.',
|
||||
topp: 'Top-p changes how the model selects tokens for output. Tokens are selected from most K (see topK parameter) probable to least until the sum of their probabilities equals the top-p value.',
|
||||
topk: "Top-k changes how the model selects tokens for output. A top-k of 1 means the selected token is the most probable among all tokens in the model's vocabulary (also called greedy decoding), while a top-k of 3 means that the next token is selected from among the 3 most probable tokens (using temperature).",
|
||||
maxoutputtokens: " Maximum number of tokens that can be generated in the response. Specify a lower value for shorter responses and a higher value for longer responses."
|
||||
};
|
||||
|
||||
function OptionHover({ type, side }) {
|
||||
// const options = {};
|
||||
// if (type === 'pres') {
|
||||
// options.sideOffset = 45;
|
||||
// }
|
||||
|
||||
return (
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent
|
||||
side={side}
|
||||
className="w-80 "
|
||||
// {...options}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">{types[type]}</p>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
);
|
||||
}
|
||||
|
||||
export default OptionHover;
|
||||
271
client/src/components/Endpoints/Google/Settings.jsx
Normal file
271
client/src/components/Endpoints/Google/Settings.jsx
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import SelectDropDown from '../../ui/SelectDropDown';
|
||||
import { Input } from '~/components/ui/Input.tsx';
|
||||
import { Label } from '~/components/ui/Label.tsx';
|
||||
import { Slider } from '~/components/ui/Slider.tsx';
|
||||
import { InputNumber } from '~/components/ui/InputNumber.tsx';
|
||||
import OptionHover from './OptionHover';
|
||||
import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx';
|
||||
import { cn } from '~/utils/';
|
||||
const defaultTextProps =
|
||||
'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0';
|
||||
|
||||
const optionText =
|
||||
'p-0 shadow-none text-right pr-1 h-8 border-transparent focus:ring-[#10a37f] focus:ring-offset-0 focus:ring-opacity-100 hover:bg-gray-800/10 dark:hover:bg-white/10 focus:bg-gray-800/10 dark:focus:bg-white/10 transition-colors';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
function Settings(props) {
|
||||
const { readonly, model, modelLabel, promptPrefix, temperature, topP, topK, maxOutputTokens, setOption, edit = false } = props;
|
||||
const maxHeight = edit ? 'max-h-[233px]' : 'max-h-[350px]';
|
||||
const endpointsConfig = useRecoilValue(store.endpointsConfig);
|
||||
|
||||
const setModel = setOption('model');
|
||||
const setModelLabel = setOption('modelLabel');
|
||||
const setPromptPrefix = setOption('promptPrefix');
|
||||
const setTemperature = setOption('temperature');
|
||||
const setTopP = setOption('topP');
|
||||
const setTopK = setOption('topK');
|
||||
const setMaxOutputTokens = setOption('maxOutputTokens');
|
||||
|
||||
const models = endpointsConfig?.['google']?.['availableModels'] || [];
|
||||
|
||||
return (
|
||||
<div className={`${maxHeight} overflow-y-auto`}>
|
||||
<div className="grid gap-6 sm:grid-cols-2">
|
||||
<div className="col-span-1 flex flex-col items-center justify-start gap-6">
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<SelectDropDown
|
||||
value={model}
|
||||
setValue={setModel}
|
||||
availableValues={models}
|
||||
disabled={readonly}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex w-full resize-none focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
|
||||
)}
|
||||
containerClassName="flex w-full resize-none"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label
|
||||
htmlFor="modelLabel"
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Custom Name <small className="opacity-40">(default: blank)</small>
|
||||
</Label>
|
||||
<Input
|
||||
id="modelLabel"
|
||||
disabled={readonly}
|
||||
value={modelLabel || ''}
|
||||
onChange={e => setModelLabel(e.target.value || null)}
|
||||
placeholder="Set a custom name for PaLM2"
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label
|
||||
htmlFor="promptPrefix"
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Prompt Prefix <small className="opacity-40">(default: blank)</small>
|
||||
</Label>
|
||||
<TextareaAutosize
|
||||
id="promptPrefix"
|
||||
disabled={readonly}
|
||||
value={promptPrefix || ''}
|
||||
onChange={e => setPromptPrefix(e.target.value || null)}
|
||||
placeholder="Set custom instructions or context. Ignored if empty."
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex max-h-[300px] min-h-[100px] w-full resize-none px-3 py-2 '
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-1 flex flex-col items-center justify-start gap-6">
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label
|
||||
htmlFor="temp-int"
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Temperature <small className="opacity-40">(default: 0.2)</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="temp-int"
|
||||
disabled={readonly}
|
||||
value={temperature}
|
||||
onChange={value => setTemperature(value)}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200'
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[temperature]}
|
||||
onValueChange={value => setTemperature(value[0])}
|
||||
doubleClickHandler={() => setTemperature(1)}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover
|
||||
type="temp"
|
||||
side="left"
|
||||
/>
|
||||
</HoverCard>
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label
|
||||
htmlFor="top-p-int"
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Top P <small className="opacity-40">(default: 0.95)</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="top-p-int"
|
||||
disabled={readonly}
|
||||
value={topP}
|
||||
onChange={value => setTopP(value)}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200'
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[topP]}
|
||||
onValueChange={value => setTopP(value[0])}
|
||||
doubleClickHandler={() => setTopP(1)}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover
|
||||
type="topp"
|
||||
side="left"
|
||||
/>
|
||||
</HoverCard>
|
||||
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label
|
||||
htmlFor="top-k-int"
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Top K <small className="opacity-40">(default: 40)</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="top-k-int"
|
||||
disabled={readonly}
|
||||
value={topK}
|
||||
onChange={value => setTopK(value)}
|
||||
max={40}
|
||||
min={1}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200'
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[topK]}
|
||||
onValueChange={value => setTopK(value[0])}
|
||||
doubleClickHandler={() => setTopK(0)}
|
||||
max={40}
|
||||
min={1}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover
|
||||
type="topk"
|
||||
side="left"
|
||||
/>
|
||||
</HoverCard>
|
||||
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label
|
||||
htmlFor="max-tokens-int"
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Max Output Tokens <small className="opacity-40">(default: 1024)</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="max-tokens-int"
|
||||
disabled={readonly}
|
||||
value={maxOutputTokens}
|
||||
onChange={value => setMaxOutputTokens(value)}
|
||||
max={1024}
|
||||
min={1}
|
||||
step={1}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200'
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[maxOutputTokens]}
|
||||
onValueChange={value => setMaxOutputTokens(value[0])}
|
||||
doubleClickHandler={() => setMaxOutputTokens(0)}
|
||||
max={1024}
|
||||
min={1}
|
||||
step={1}
|
||||
className="flex h-4 w-full"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover
|
||||
type="maxoutputtokens"
|
||||
side="left"
|
||||
/>
|
||||
</HoverCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Settings;
|
||||
|
|
@ -32,7 +32,7 @@ function Settings(props) {
|
|||
const models = endpointsConfig?.['openAI']?.['availableModels'] || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="max-h-[350px] overflow-y-auto">
|
||||
<div className="grid gap-6 sm:grid-cols-2">
|
||||
<div className="col-span-1 flex flex-col items-center justify-start gap-6">
|
||||
<div className="grid w-full items-center gap-2">
|
||||
|
|
@ -264,7 +264,7 @@ function Settings(props) {
|
|||
</HoverCard>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@ import React from 'react';
|
|||
|
||||
import OpenAISettings from './OpenAI/Settings.jsx';
|
||||
import BingAISettings from './BingAI/Settings.jsx';
|
||||
import GoogleSettings from './Google/Settings.jsx';
|
||||
|
||||
// A preset dialog to show readonly preset values.
|
||||
const Settings = ({ preset, ...props }) => {
|
||||
const renderSettings = () => {
|
||||
const { endpoint } = preset || {};
|
||||
// console.log('preset', preset);
|
||||
|
||||
if (endpoint === 'openAI')
|
||||
if (endpoint === 'openAI') {
|
||||
return (
|
||||
<OpenAISettings
|
||||
model={preset?.model}
|
||||
|
|
@ -21,7 +23,7 @@ const Settings = ({ preset, ...props }) => {
|
|||
{...props}
|
||||
/>
|
||||
);
|
||||
else if (endpoint === 'bingAI')
|
||||
} else if (endpoint === 'bingAI') {
|
||||
return (
|
||||
<BingAISettings
|
||||
toneStyle={preset?.toneStyle}
|
||||
|
|
@ -31,7 +33,24 @@ const Settings = ({ preset, ...props }) => {
|
|||
{...props}
|
||||
/>
|
||||
);
|
||||
else return <div className="text-black dark:text-white">Not implemented</div>;
|
||||
} else if (endpoint === 'google') {
|
||||
return (
|
||||
<GoogleSettings
|
||||
model={preset?.model}
|
||||
modelLabel={preset?.modelLabel}
|
||||
promptPrefix={preset?.promptPrefix}
|
||||
examples={preset?.examples}
|
||||
temperature={preset?.temperature}
|
||||
topP={preset?.topP}
|
||||
topK={preset?.topK}
|
||||
maxOutputTokens={preset?.maxOutputTokens}
|
||||
edit={true}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <div className="text-black dark:text-white">Not implemented</div>;
|
||||
}
|
||||
};
|
||||
|
||||
return renderSettings();
|
||||
|
|
|
|||
170
client/src/components/Input/GoogleOptions/index.jsx
Normal file
170
client/src/components/Input/GoogleOptions/index.jsx
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import { useState } from 'react';
|
||||
import { Settings2 } from 'lucide-react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import MessagesSquared from '~/components/svg/MessagesSquared.jsx';
|
||||
import SelectDropDown from '../../ui/SelectDropDown';
|
||||
import EndpointOptionsPopover from '../../Endpoints/EndpointOptionsPopover';
|
||||
import SaveAsPresetDialog from '../../Endpoints/SaveAsPresetDialog';
|
||||
import { Button } from '../../ui/Button.tsx';
|
||||
import Settings from '../../Endpoints/Google/Settings.jsx';
|
||||
import Examples from '../../Endpoints/Google/Examples.jsx';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
function GoogleOptions() {
|
||||
const [advancedMode, setAdvancedMode] = useState(false);
|
||||
const [showExamples, setShowExamples] = useState(false);
|
||||
const [saveAsDialogShow, setSaveAsDialogShow] = useState(false);
|
||||
|
||||
const [conversation, setConversation] = useRecoilState(store.conversation) || {};
|
||||
const { endpoint, conversationId } = conversation;
|
||||
const { model, modelLabel, promptPrefix, examples, temperature, topP, topK, maxOutputTokens } =
|
||||
conversation;
|
||||
|
||||
const endpointsConfig = useRecoilValue(store.endpointsConfig);
|
||||
|
||||
if (endpoint !== 'google') return null;
|
||||
if (conversationId !== 'new') return null;
|
||||
|
||||
const models = endpointsConfig?.['google']?.['availableModels'] || [];
|
||||
|
||||
const triggerAdvancedMode = () => setAdvancedMode(prev => !prev);
|
||||
const triggerExamples = () => setShowExamples(prev => !prev);
|
||||
|
||||
const switchToSimpleMode = () => {
|
||||
setAdvancedMode(false);
|
||||
};
|
||||
|
||||
const saveAsPreset = () => {
|
||||
setSaveAsDialogShow(true);
|
||||
};
|
||||
|
||||
const setOption = param => newValue => {
|
||||
let update = {};
|
||||
update[param] = newValue;
|
||||
setConversation(prevState => ({
|
||||
...prevState,
|
||||
...update
|
||||
}));
|
||||
};
|
||||
|
||||
const setExample = (i, type, newValue = null) => {
|
||||
let update = {};
|
||||
let current = conversation?.examples.slice() || [];
|
||||
let currentExample = { ...current[i] } || {};
|
||||
currentExample[type] = { content: newValue };
|
||||
current[i] = currentExample;
|
||||
update.examples = current;
|
||||
setConversation(prevState => ({
|
||||
...prevState,
|
||||
...update
|
||||
}));
|
||||
};
|
||||
|
||||
const addExample = () => {
|
||||
let update = {};
|
||||
let current = conversation?.examples.slice() || [];
|
||||
current.push({ input: { content: '' }, output: { content: '' } });
|
||||
update.examples = current;
|
||||
setConversation(prevState => ({
|
||||
...prevState,
|
||||
...update
|
||||
}));
|
||||
};
|
||||
|
||||
const removeExample = () => {
|
||||
let update = {};
|
||||
let current = conversation?.examples.slice() || [];
|
||||
if (current.length <= 1) {
|
||||
update.examples = [{ input: { content: '' }, output: { content: '' } }];
|
||||
setConversation(prevState => ({
|
||||
...prevState,
|
||||
...update
|
||||
}));
|
||||
return;
|
||||
}
|
||||
current.pop();
|
||||
update.examples = current;
|
||||
setConversation(prevState => ({
|
||||
...prevState,
|
||||
...update
|
||||
}));
|
||||
};
|
||||
|
||||
const cardStyle =
|
||||
'transition-colors shadow-md rounded-md min-w-[75px] font-normal bg-white border-black/10 hover:border-black/10 focus:border-black/10 dark:border-black/10 dark:hover:border-black/10 dark:focus:border-black/10 border dark:bg-gray-700 text-black dark:text-white';
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
'openAIOptions-simple-container flex w-full flex-wrap items-center justify-center gap-2' +
|
||||
(!advancedMode ? ' show' : '')
|
||||
}
|
||||
>
|
||||
<SelectDropDown
|
||||
value={model}
|
||||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={true}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-48 z-50 flex h-[40px] w-48 flex-none items-center justify-center px-4 ring-0 hover:cursor-pointer hover:bg-slate-50 focus:ring-0 focus:ring-offset-0 data-[state=open]:bg-slate-50 dark:bg-gray-700 dark:hover:bg-gray-600 dark:data-[state=open]:bg-gray-600'
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-4 z-50 flex h-[40px] flex-none items-center justify-center px-4 hover:bg-slate-50 focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-600'
|
||||
)}
|
||||
onClick={triggerAdvancedMode}
|
||||
>
|
||||
<Settings2 className="w-4 text-gray-600 dark:text-white" />
|
||||
</Button>
|
||||
</div>
|
||||
<EndpointOptionsPopover
|
||||
content={
|
||||
<div className="px-4 py-4">
|
||||
{showExamples ? (
|
||||
<Examples
|
||||
examples={examples}
|
||||
setExample={setExample}
|
||||
addExample={addExample}
|
||||
removeExample={removeExample}
|
||||
/>
|
||||
) : (
|
||||
<Settings
|
||||
model={model}
|
||||
modelLabel={modelLabel}
|
||||
promptPrefix={promptPrefix}
|
||||
temperature={temperature}
|
||||
topP={topP}
|
||||
topK={topK}
|
||||
maxOutputTokens={maxOutputTokens}
|
||||
setOption={setOption}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
visible={advancedMode}
|
||||
saveAsPreset={saveAsPreset}
|
||||
switchToSimpleMode={switchToSimpleMode}
|
||||
additionalButton={{
|
||||
label: (showExamples ? 'Hide' : 'Show') + ' Examples',
|
||||
handler: triggerExamples,
|
||||
icon: <MessagesSquared className="mr-1 w-[14px]" />
|
||||
}}
|
||||
/>
|
||||
<SaveAsPresetDialog
|
||||
open={saveAsDialogShow}
|
||||
onOpenChange={setSaveAsDialogShow}
|
||||
preset={conversation}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default GoogleOptions;
|
||||
|
|
@ -7,6 +7,14 @@ import SetTokenDialog from '../SetTokenDialog';
|
|||
|
||||
import store from '../../../store';
|
||||
|
||||
const alternateName = {
|
||||
openAI: 'OpenAI',
|
||||
azureOpenAI: 'Azure OpenAI',
|
||||
bingAI: 'Bing',
|
||||
chatGPTBrowser: 'ChatGPT',
|
||||
google: 'PaLM',
|
||||
}
|
||||
|
||||
export default function ModelItem({ endpoint, value, onSelect }) {
|
||||
const [setTokenDialogOpen, setSetTokenDialogOpen] = useState(false);
|
||||
const endpointsConfig = useRecoilValue(store.endpointsConfig);
|
||||
|
|
@ -28,7 +36,7 @@ export default function ModelItem({ endpoint, value, onSelect }) {
|
|||
className="group dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
|
||||
>
|
||||
{icon}
|
||||
{endpoint}
|
||||
{alternateName[endpoint] || endpoint}
|
||||
{!!['azureOpenAI', 'openAI'].find(e => e === endpoint) && <sup>$</sup>}
|
||||
<div className="flex w-4 flex-1" />
|
||||
{isuserProvide ? (
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
import React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { FileUp } from 'lucide-react';
|
||||
import cleanupPreset from '~/utils/cleanupPreset.js';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
const FileUpload = ({ onFileSelected }) => {
|
||||
// const setPresets = useSetRecoilState(store.presets);
|
||||
const endpointsConfig = useRecoilValue(store.endpointsConfig);
|
||||
const FileUpload = ({ onFileSelected, successText = null, invalidText = null, validator = null, text = null, id = '1' }) => {
|
||||
const [statusColor, setStatusColor] = useState('text-gray-600');
|
||||
const [status, setStatus] = useState(null);
|
||||
|
||||
const handleFileChange = event => {
|
||||
const file = event.target.files[0];
|
||||
|
|
@ -16,20 +13,34 @@ const FileUpload = ({ onFileSelected }) => {
|
|||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
const jsonData = JSON.parse(e.target.result);
|
||||
onFileSelected({ ...cleanupPreset({ preset: jsonData, endpointsConfig }), presetId: null });
|
||||
if (validator && !validator(jsonData)) {
|
||||
setStatus('invalid');
|
||||
setStatusColor('text-red-600');
|
||||
return;
|
||||
}
|
||||
|
||||
if (validator) {
|
||||
setStatus('success');
|
||||
setStatusColor('text-green-500 dark:text-green-500');
|
||||
}
|
||||
|
||||
onFileSelected(jsonData);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
|
||||
return (
|
||||
<label
|
||||
htmlFor="file-upload"
|
||||
className=" mr-1 flex h-auto cursor-pointer items-center rounded bg-transparent px-2 py-1 text-xs font-medium font-normal text-gray-600 transition-colors hover:bg-slate-200 hover:text-green-700 dark:bg-transparent dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-green-500"
|
||||
htmlFor={`file-upload-${id}`}
|
||||
className={cn(
|
||||
'mr-1 flex h-auto cursor-pointer items-center rounded bg-transparent px-2 py-1 text-xs font-medium font-normal transition-colors hover:bg-slate-200 hover:text-green-700 dark:bg-transparent dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-green-500',
|
||||
statusColor
|
||||
)}
|
||||
>
|
||||
<FileUp className="mr-1 flex w-[22px] items-center stroke-1" />
|
||||
<span className="flex text-xs ">Import</span>
|
||||
<span className="flex text-xs ">{!status ? text || 'Import' : (status === 'success' ? successText : invalidText)}</span>
|
||||
<input
|
||||
id="file-upload"
|
||||
id={`file-upload-${id}`}
|
||||
value=""
|
||||
type="file"
|
||||
className="hidden "
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ export default function PresetItem({ preset = {}, value, onSelect, onChangePrese
|
|||
const { chatGptLabel, model } = preset;
|
||||
if (model) _title += `: ${model}`;
|
||||
if (chatGptLabel) _title += ` as ${chatGptLabel}`;
|
||||
} else if (endpoint === 'google') {
|
||||
const { modelLabel, model } = preset;
|
||||
if (model) _title += `: ${model}`;
|
||||
if (modelLabel) _title += ` as ${modelLabel}`;
|
||||
} else if (endpoint === 'bingAI') {
|
||||
const { jailbreak, toneStyle } = preset;
|
||||
if (toneStyle) _title += `: ${toneStyle}`;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import cleanupPreset from '~/utils/cleanupPreset.js';
|
||||
import { useRecoilValue, useRecoilState } from 'recoil';
|
||||
import EditPresetDialog from '../../Endpoints/EditPresetDialog';
|
||||
import EndpointItems from './EndpointItems';
|
||||
|
|
@ -27,6 +28,7 @@ export default function NewConversationMenu() {
|
|||
const [preset, setPreset] = useState(false);
|
||||
|
||||
const availableEndpoints = useRecoilValue(store.availableEndpoints);
|
||||
const endpointsConfig = useRecoilValue(store.endpointsConfig);
|
||||
const [presets, setPresets] = useRecoilState(store.presets);
|
||||
|
||||
const conversation = useRecoilValue(store.conversation) || {};
|
||||
|
|
@ -50,6 +52,11 @@ export default function NewConversationMenu() {
|
|||
);
|
||||
};
|
||||
|
||||
const onFileSelected = jsonData => {
|
||||
const jsonPreset = { ...cleanupPreset({ preset: jsonData, endpointsConfig }), presetId: null };
|
||||
importPreset(jsonPreset);
|
||||
};
|
||||
|
||||
// update the default model when availableModels changes
|
||||
// typically, availableModels changes => modelsFilter or customGPTModels changes
|
||||
useEffect(() => {
|
||||
|
|
@ -151,7 +158,7 @@ export default function NewConversationMenu() {
|
|||
<DropdownMenuLabel className="flex items-center dark:text-gray-300">
|
||||
<span>Select a Preset</span>
|
||||
<div className="flex-1" />
|
||||
<FileUpload onFileSelected={importPreset} />
|
||||
<FileUpload onFileSelected={onFileSelected} />
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<label
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import DialogTemplate from '../../ui/DialogTemplate';
|
||||
import { Dialog } from '../../ui/Dialog.tsx';
|
||||
import { Input } from '../../ui/Input.tsx';
|
||||
import { Label } from '../../ui/Label.tsx';
|
||||
import { cn } from '~/utils/';
|
||||
import cleanupPreset from '~/utils/cleanupPreset';
|
||||
import { useCreatePresetMutation } from '~/data-provider';
|
||||
import FileUpload from '../NewConversationMenu/FileUpload';
|
||||
import store from '~/store';
|
||||
|
||||
const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
|
||||
|
|
@ -54,6 +52,30 @@ const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
|
|||
</a>
|
||||
. Copy access token.
|
||||
</small>
|
||||
),
|
||||
google: (
|
||||
<small className="break-all text-gray-600">
|
||||
You need to{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://console.cloud.google.com/vertex-ai"
|
||||
rel="noreferrer"
|
||||
className="text-blue-600 underline"
|
||||
>
|
||||
Enable Vertex AI
|
||||
</a>{' '}
|
||||
API on Google Cloud, then{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create?walkthrough_id=iam--create-service-account#step_index=1"
|
||||
rel="noreferrer"
|
||||
className="text-blue-600 underline"
|
||||
>
|
||||
Create a Service Account
|
||||
</a>
|
||||
. Make sure to click 'Create and Continue' to give at least the 'Vertex AI User' role. Lastly, create
|
||||
a JSON key to import here.
|
||||
</small>
|
||||
)
|
||||
};
|
||||
|
||||
|
|
@ -73,19 +95,61 @@ const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
|
|||
Token Name
|
||||
<br />
|
||||
</Label>
|
||||
<Input
|
||||
id="chatGptLabel"
|
||||
value={token || ''}
|
||||
onChange={e => setToken(e.target.value || '')}
|
||||
placeholder="Set the token."
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
|
||||
)}
|
||||
/>
|
||||
<small className="text-red-600">
|
||||
Your token will be sent to the server, but not saved.
|
||||
</small>
|
||||
{endpoint === 'google' ? (
|
||||
<FileUpload
|
||||
id="googleKey"
|
||||
className="w-full"
|
||||
text="Import Service Account JSON Key"
|
||||
successText="Successfully Imported Service Account JSON Key"
|
||||
invalidText="Invalid Service Account JSON Key, Did you import the correct file?"
|
||||
validator={credentials => {
|
||||
if (!credentials) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!credentials.client_email ||
|
||||
typeof credentials.client_email !== 'string' ||
|
||||
credentials.client_email.length <= 2
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!credentials.project_id ||
|
||||
typeof credentials.project_id !== 'string' ||
|
||||
credentials.project_id.length <= 2
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!credentials.private_key ||
|
||||
typeof credentials.private_key !== 'string' ||
|
||||
credentials.private_key.length <= 600
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}}
|
||||
onFileSelected={data => {
|
||||
setToken(JSON.stringify(data));
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
id="chatGptLabel"
|
||||
value={token || ''}
|
||||
onChange={e => setToken(e.target.value || '')}
|
||||
placeholder="Set the token."
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<small className="text-red-600">Your token will be sent to the server, but not saved.</small>
|
||||
{helpText?.[endpoint]}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import SubmitButton from './SubmitButton';
|
|||
import OpenAIOptions from './OpenAIOptions';
|
||||
import ChatGPTOptions from './ChatGPTOptions';
|
||||
import BingAIOptions from './BingAIOptions';
|
||||
import GoogleOptions from './GoogleOptions';
|
||||
// import BingStyles from './BingStyles';
|
||||
import NewConversationMenu from './NewConversationMenu';
|
||||
import AdjustToneButton from './AdjustToneButton';
|
||||
|
|
@ -139,6 +140,7 @@ export default function TextChat({ isSearchView = false }) {
|
|||
<span className="flex w-full flex-col items-center justify-center gap-0 md:order-none md:m-auto md:gap-2">
|
||||
<OpenAIOptions />
|
||||
<ChatGPTOptions />
|
||||
<GoogleOptions />
|
||||
<BingAIOptions show={showBingToneSetting} />
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export default function HoverButtons({
|
|||
|
||||
const branchingSupported =
|
||||
// azureOpenAI, openAI, chatGPTBrowser support branching, so edit enabled
|
||||
!!['azureOpenAI', 'openAI', 'chatGPTBrowser'].find(e => e === endpoint) ||
|
||||
!!['azureOpenAI', 'openAI', 'chatGPTBrowser', 'google'].find(e => e === endpoint) ||
|
||||
// Sydney in bingAI supports branching, so edit enabled
|
||||
(endpoint === 'bingAI' && jailbreak);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ const MessageHeader = ({ isSearchView = false }) => {
|
|||
const { chatGptLabel, model } = conversation;
|
||||
if (model) _title += `: ${model}`;
|
||||
if (chatGptLabel) _title += ` as ${chatGptLabel}`;
|
||||
} else if (endpoint === 'google') {
|
||||
_title = 'PaLM';
|
||||
const { modelLabel, model } = conversation;
|
||||
if (model) _title += `: ${model}`;
|
||||
if (modelLabel) _title += ` as ${modelLabel}`;
|
||||
} else if (endpoint === 'bingAI') {
|
||||
const { jailbreak, toneStyle } = conversation;
|
||||
if (toneStyle) _title += `: ${toneStyle}`;
|
||||
|
|
|
|||
21
client/src/components/svg/MessagesSquared.jsx
Normal file
21
client/src/components/svg/MessagesSquared.jsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { cn } from '~/utils/';
|
||||
|
||||
export default function MessagesSquared({ className }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={cn(className, 'lucide lucide-messages-square')}
|
||||
>
|
||||
<path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z" />
|
||||
<path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -29,26 +29,6 @@ export default function DialogTemplate({
|
|||
<DialogTitle className="text-gray-800 dark:text-white">{title}</DialogTitle>
|
||||
<DialogDescription className="text-gray-600 dark:text-gray-300">{description}</DialogDescription>
|
||||
</DialogHeader>
|
||||
{/* <div className="grid gap-4 py-4">
|
||||
<div className="grid grid-cols-4 items-center gap-4"> //input template
|
||||
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label
|
||||
htmlFor="promptPrefix"
|
||||
className="text-right"
|
||||
>
|
||||
Prompt Prefix
|
||||
</Label>
|
||||
<TextareaAutosize
|
||||
id="promptPrefix"
|
||||
value={promptPrefix}
|
||||
onChange={(e) => setPromptPrefix(e.target.value)}
|
||||
placeholder="Set custom instructions. Defaults to: 'You are ChatGPT, a large language model trained by OpenAI.'"
|
||||
className="col-span-3 flex h-20 w-full resize-none rounded-md border border-gray-300 bg-transparent py-2 px-3 text-sm shadow-[0_0_10px_rgba(0,0,0,0.10)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-none dark:bg-gray-700 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-none dark:focus:border-transparent dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0"
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
{main ? main : null}
|
||||
<DialogFooter>
|
||||
<div>{leftButtons ? leftButtons : null}</div>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export default function createPayload(submission: TSubmission) {
|
|||
const endpointUrlMap = {
|
||||
azureOpenAI: '/api/ask/azureOpenAI',
|
||||
openAI: '/api/ask/openAI',
|
||||
google: '/api/ask/google',
|
||||
bingAI: '/api/ask/bingAI',
|
||||
chatGPTBrowser: '/api/ask/chatGPTBrowser'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ export type TMessage = {
|
|||
updatedAt: string,
|
||||
};
|
||||
|
||||
export type TExample = {
|
||||
input: string,
|
||||
output: string,
|
||||
};
|
||||
|
||||
export type TSubmission = {
|
||||
clientId?: string;
|
||||
context?: string;
|
||||
|
|
@ -43,7 +48,8 @@ export enum EModelEndpoint {
|
|||
openAI = 'openAI',
|
||||
bingAI = 'bingAI',
|
||||
chatGPT = 'chatGPT',
|
||||
chatGPTBrowser = 'chatGPTBrowser'
|
||||
chatGPTBrowser = 'chatGPTBrowser',
|
||||
google = 'google',
|
||||
}
|
||||
|
||||
export type TConversation = {
|
||||
|
|
@ -55,11 +61,19 @@ export type TConversation = {
|
|||
messages?: TMessage[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
// google only
|
||||
modelLabel?: string;
|
||||
examples?: TExample[];
|
||||
// for azureOpenAI, openAI only
|
||||
chatGptLabel?: string;
|
||||
userLabel?: string;
|
||||
model?: string;
|
||||
promptPrefix?: string;
|
||||
temperature?: number;
|
||||
topP?: number;
|
||||
topK?: number;
|
||||
// bing and google
|
||||
context?: string;
|
||||
top_p?: number;
|
||||
presence_penalty?: number;
|
||||
// for bingAI only
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ const endpointsConfig = atom({
|
|||
azureOpenAI: null,
|
||||
openAI: null,
|
||||
bingAI: null,
|
||||
chatGPTBrowser: null
|
||||
chatGPTBrowser: null,
|
||||
google: null,
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ const endpointsFilter = selector({
|
|||
const availableEndpoints = selector({
|
||||
key: 'availableEndpoints',
|
||||
get: ({ get }) => {
|
||||
const endpoints = ['azureOpenAI', 'openAI', 'bingAI', 'chatGPTBrowser'];
|
||||
const endpoints = ['azureOpenAI', 'openAI', 'chatGPTBrowser', 'bingAI', 'google'];
|
||||
const f = get(endpointsFilter);
|
||||
return endpoints.filter(endpoint => f[endpoint]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,20 @@ const cleanupPreset = ({ preset: _preset, endpointsConfig = {} }) => {
|
|||
frequency_penalty: _preset?.frequency_penalty ?? 0,
|
||||
title: _preset?.title ?? 'New Preset'
|
||||
};
|
||||
} else if (endpoint === 'google') {
|
||||
preset = {
|
||||
endpoint,
|
||||
presetId: _preset?.presetId ?? null,
|
||||
model: _preset?.model ?? endpointsConfig[endpoint]?.availableModels?.[0] ?? 'chat-bison',
|
||||
modelLabel: _preset?.modelLabel ?? null,
|
||||
examples: _preset?.examples ?? [{ input: { content: '' }, output: { content: '' } }],
|
||||
promptPrefix: _preset?.promptPrefix ?? null,
|
||||
temperature: _preset?.temperature ?? 0.2,
|
||||
maxOutputTokens: _preset?.maxOutputTokens ?? 1024,
|
||||
topP: _preset?.topP ?? 0.95,
|
||||
topK: _preset?.topK ?? 40,
|
||||
title: _preset?.title ?? 'New Preset'
|
||||
};
|
||||
} else if (endpoint === 'bingAI') {
|
||||
preset = {
|
||||
endpoint,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,23 @@ const buildDefaultConversation = ({
|
|||
presence_penalty: lastConversationSetup?.presence_penalty ?? 0,
|
||||
frequency_penalty: lastConversationSetup?.frequency_penalty ?? 0
|
||||
};
|
||||
} else if (endpoint === 'google') {
|
||||
conversation = {
|
||||
...conversation,
|
||||
endpoint,
|
||||
model:
|
||||
lastConversationSetup?.model ??
|
||||
lastSelectedModel[endpoint] ??
|
||||
endpointsConfig[endpoint]?.availableModels?.[0] ??
|
||||
'chat-bison',
|
||||
modelLabel: lastConversationSetup?.modelLabel ?? null,
|
||||
promptPrefix: lastConversationSetup?.promptPrefix ?? null,
|
||||
examples: lastConversationSetup?.examples ?? [{ input: { content: '' }, output: { content: '' }}],
|
||||
temperature: lastConversationSetup?.temperature ?? 0.2,
|
||||
maxOutputTokens: lastConversationSetup?.maxOutputTokens ?? 1024,
|
||||
topP: lastConversationSetup?.topP ?? 0.95,
|
||||
topK: lastConversationSetup?.topK ?? 40,
|
||||
};
|
||||
} else if (endpoint === 'bingAI') {
|
||||
conversation = {
|
||||
...conversation,
|
||||
|
|
@ -108,7 +125,7 @@ const getDefaultConversation = ({ conversation, prevConversation, endpointsConfi
|
|||
|
||||
// if anything happens, reset to default model
|
||||
|
||||
const endpoint = ['openAI', 'azureOpenAI', 'bingAI', 'chatGPTBrowser'].find(e => endpointsConfig?.[e]);
|
||||
const endpoint = ['openAI', 'azureOpenAI', 'bingAI', 'chatGPTBrowser', 'google'].find(e => endpointsConfig?.[e]);
|
||||
if (endpoint) {
|
||||
conversation = buildDefaultConversation({ conversation, endpoint, endpointsConfig });
|
||||
return conversation;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ const getIcon = props => {
|
|||
? `rgba(16, 163, 127, ${button ? 0.75 : 1})`
|
||||
: `rgba(16, 163, 127, ${button ? 0.75 : 1})`);
|
||||
name = chatGptLabel || 'ChatGPT';
|
||||
} else if (endpoint === 'google') {
|
||||
const { modelLabel } = props;
|
||||
icon = <img src='/assets/palm.png' />;
|
||||
name = modelLabel || 'PaLM2';
|
||||
} else if (endpoint === 'bingAI') {
|
||||
const { jailbreak } = props;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,21 @@ const useMessageHandler = () => {
|
|||
frequency_penalty: currentConversation?.frequency_penalty ?? 0
|
||||
};
|
||||
responseSender = endpointOption.chatGptLabel ?? 'ChatGPT';
|
||||
} else if (endpoint === 'google') {
|
||||
endpointOption = {
|
||||
endpoint,
|
||||
model:
|
||||
currentConversation?.model ?? endpointsConfig[endpoint]?.availableModels?.[0] ?? 'chat-bison',
|
||||
chatGptLabel: currentConversation?.chatGptLabel ?? null,
|
||||
promptPrefix: currentConversation?.promptPrefix ?? null,
|
||||
examples: currentConversation?.examples ?? [{ input: { content: '' }, output: { content: '' }}],
|
||||
temperature: currentConversation?.temperature ?? 0.2,
|
||||
maxOutputTokens: currentConversation?.maxOutputTokens ?? 1024,
|
||||
topP: currentConversation?.topP ?? 0.95,
|
||||
topK: currentConversation?.topK ?? 40,
|
||||
token: endpointsConfig[endpoint]?.userProvide ? getToken() : null
|
||||
};
|
||||
responseSender = endpointOption.chatGptLabel ?? 'ChatGPT';
|
||||
} else if (endpoint === 'bingAI') {
|
||||
endpointOption = {
|
||||
endpoint,
|
||||
|
|
@ -125,7 +140,7 @@ const useMessageHandler = () => {
|
|||
initialResponse
|
||||
};
|
||||
|
||||
console.log('User Input:', text);
|
||||
console.log('User Input:', text, submission);
|
||||
|
||||
if (isRegenerate) {
|
||||
setMessages([...currentMessages, initialResponse]);
|
||||
|
|
|
|||
377
package-lock.json
generated
377
package-lock.json
generated
|
|
@ -35,6 +35,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"eslint": "^8.36.0",
|
||||
"express": "^4.18.2",
|
||||
"googleapis": "^118.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"html": "^1.0.0",
|
||||
"joi": "^14.3.1",
|
||||
|
|
@ -5522,6 +5523,14 @@
|
|||
"get-intrinsic": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/arrify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
|
||||
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/asn1.js": {
|
||||
"version": "5.4.1",
|
||||
"license": "MIT",
|
||||
|
|
@ -5868,6 +5877,14 @@
|
|||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/bignumber.js": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
|
||||
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"license": "MIT",
|
||||
|
|
@ -8173,6 +8190,11 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-text-encoding": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
|
||||
"integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "2.2.0",
|
||||
"license": "MIT"
|
||||
|
|
@ -8661,6 +8683,32 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/gaxios": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.0.tgz",
|
||||
"integrity": "sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==",
|
||||
"dependencies": {
|
||||
"extend": "^3.0.2",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"node-fetch": "^2.6.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/gcp-metadata": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz",
|
||||
"integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==",
|
||||
"dependencies": {
|
||||
"gaxios": "^5.0.0",
|
||||
"json-bigint": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"dev": true,
|
||||
|
|
@ -8800,6 +8848,110 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.8.0.tgz",
|
||||
"integrity": "sha512-0iJn7IDqObDG5Tu9Tn2WemmJ31ksEa96IyK0J0OZCpTh6CrC6FrattwKX87h3qKVuprCJpdOGKc1Xi8V0kMh8Q==",
|
||||
"dependencies": {
|
||||
"arrify": "^2.0.0",
|
||||
"base64-js": "^1.3.0",
|
||||
"ecdsa-sig-formatter": "^1.0.11",
|
||||
"fast-text-encoding": "^1.0.0",
|
||||
"gaxios": "^5.0.0",
|
||||
"gcp-metadata": "^5.2.0",
|
||||
"gtoken": "^6.1.0",
|
||||
"jws": "^4.0.0",
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/google-p12-pem": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz",
|
||||
"integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==",
|
||||
"dependencies": {
|
||||
"node-forge": "^1.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"gp12-pem": "build/src/bin/gp12-pem.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/googleapis": {
|
||||
"version": "118.0.0",
|
||||
"resolved": "https://registry.npmjs.org/googleapis/-/googleapis-118.0.0.tgz",
|
||||
"integrity": "sha512-Ny6zJOGn5P/YDT6GQbJU6K0lSzEu4Yuxnsn45ZgBIeSQ1RM0FolEjUToLXquZd89DU9wUfqA5XYHPEctk1TFWg==",
|
||||
"dependencies": {
|
||||
"google-auth-library": "^8.0.2",
|
||||
"googleapis-common": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/googleapis-common": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-6.0.4.tgz",
|
||||
"integrity": "sha512-m4ErxGE8unR1z0VajT6AYk3s6a9gIMM6EkDZfkPnES8joeOlEtFEJeF8IyZkb0tjPXkktUfYrE4b3Li1DNyOwA==",
|
||||
"dependencies": {
|
||||
"extend": "^3.0.2",
|
||||
"gaxios": "^5.0.1",
|
||||
"google-auth-library": "^8.0.2",
|
||||
"qs": "^6.7.0",
|
||||
"url-template": "^2.0.8",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/googleapis-common/node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"dev": true,
|
||||
|
|
@ -8819,6 +8971,38 @@
|
|||
"version": "1.0.4",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/gtoken": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz",
|
||||
"integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==",
|
||||
"dependencies": {
|
||||
"gaxios": "^5.0.1",
|
||||
"google-p12-pem": "^4.0.0",
|
||||
"jws": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gtoken/node_modules/jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/gtoken/node_modules/jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/hamt_plus": {
|
||||
"version": "1.0.2",
|
||||
"license": "MIT"
|
||||
|
|
@ -10517,6 +10701,14 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||
"dependencies": {
|
||||
"bignumber.js": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"license": "MIT"
|
||||
|
|
@ -12011,6 +12203,14 @@
|
|||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
|
||||
"engines": {
|
||||
"node": ">= 6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-html-parser": {
|
||||
"version": "5.4.2",
|
||||
"dev": true,
|
||||
|
|
@ -15978,6 +16178,11 @@
|
|||
"querystring": "0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-template": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
|
||||
"integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="
|
||||
},
|
||||
"node_modules/url/node_modules/punycode": {
|
||||
"version": "1.3.2",
|
||||
"license": "MIT"
|
||||
|
|
@ -19967,6 +20172,11 @@
|
|||
"get-intrinsic": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"arrify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
|
||||
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="
|
||||
},
|
||||
"asn1.js": {
|
||||
"version": "5.4.1",
|
||||
"requires": {
|
||||
|
|
@ -20203,6 +20413,11 @@
|
|||
"version": "5.2.2",
|
||||
"dev": true
|
||||
},
|
||||
"bignumber.js": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
|
||||
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig=="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0"
|
||||
},
|
||||
|
|
@ -20457,6 +20672,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"eslint": "^8.36.0",
|
||||
"express": "^4.18.2",
|
||||
"googleapis": "^118.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"html": "^1.0.0",
|
||||
"joi": "^14.3.1",
|
||||
|
|
@ -21802,6 +22018,11 @@
|
|||
"fast-redact": {
|
||||
"version": "3.1.2"
|
||||
},
|
||||
"fast-text-encoding": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
|
||||
"integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
|
||||
},
|
||||
"fast-uri": {
|
||||
"version": "2.2.0"
|
||||
},
|
||||
|
|
@ -22107,6 +22328,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"gaxios": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.0.tgz",
|
||||
"integrity": "sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==",
|
||||
"requires": {
|
||||
"extend": "^3.0.2",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
},
|
||||
"gcp-metadata": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz",
|
||||
"integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==",
|
||||
"requires": {
|
||||
"gaxios": "^5.0.0",
|
||||
"json-bigint": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"dev": true
|
||||
|
|
@ -22187,6 +22428,93 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"google-auth-library": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.8.0.tgz",
|
||||
"integrity": "sha512-0iJn7IDqObDG5Tu9Tn2WemmJ31ksEa96IyK0J0OZCpTh6CrC6FrattwKX87h3qKVuprCJpdOGKc1Xi8V0kMh8Q==",
|
||||
"requires": {
|
||||
"arrify": "^2.0.0",
|
||||
"base64-js": "^1.3.0",
|
||||
"ecdsa-sig-formatter": "^1.0.11",
|
||||
"fast-text-encoding": "^1.0.0",
|
||||
"gaxios": "^5.0.0",
|
||||
"gcp-metadata": "^5.2.0",
|
||||
"gtoken": "^6.1.0",
|
||||
"jws": "^4.0.0",
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"requires": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"requires": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"google-p12-pem": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz",
|
||||
"integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==",
|
||||
"requires": {
|
||||
"node-forge": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"googleapis": {
|
||||
"version": "118.0.0",
|
||||
"resolved": "https://registry.npmjs.org/googleapis/-/googleapis-118.0.0.tgz",
|
||||
"integrity": "sha512-Ny6zJOGn5P/YDT6GQbJU6K0lSzEu4Yuxnsn45ZgBIeSQ1RM0FolEjUToLXquZd89DU9wUfqA5XYHPEctk1TFWg==",
|
||||
"requires": {
|
||||
"google-auth-library": "^8.0.2",
|
||||
"googleapis-common": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"googleapis-common": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-6.0.4.tgz",
|
||||
"integrity": "sha512-m4ErxGE8unR1z0VajT6AYk3s6a9gIMM6EkDZfkPnES8joeOlEtFEJeF8IyZkb0tjPXkktUfYrE4b3Li1DNyOwA==",
|
||||
"requires": {
|
||||
"extend": "^3.0.2",
|
||||
"gaxios": "^5.0.1",
|
||||
"google-auth-library": "^8.0.2",
|
||||
"qs": "^6.7.0",
|
||||
"url-template": "^2.0.8",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"gopd": {
|
||||
"version": "1.0.1",
|
||||
"dev": true,
|
||||
|
|
@ -22200,6 +22528,37 @@
|
|||
"grapheme-splitter": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"gtoken": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz",
|
||||
"integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==",
|
||||
"requires": {
|
||||
"gaxios": "^5.0.1",
|
||||
"google-p12-pem": "^4.0.0",
|
||||
"jws": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"requires": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"requires": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"hamt_plus": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
|
|
@ -23189,6 +23548,14 @@
|
|||
"version": "2.5.2",
|
||||
"dev": true
|
||||
},
|
||||
"json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||
"requires": {
|
||||
"bignumber.js": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"json-buffer": {
|
||||
"version": "3.0.1"
|
||||
},
|
||||
|
|
@ -24058,6 +24425,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
|
||||
},
|
||||
"node-html-parser": {
|
||||
"version": "5.4.2",
|
||||
"dev": true,
|
||||
|
|
@ -26332,6 +26704,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"url-template": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
|
||||
"integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="
|
||||
},
|
||||
"use-callback-ref": {
|
||||
"version": "1.3.0",
|
||||
"requires": {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue