diff --git a/package.json b/package.json
index c95df8a..f4fdd91 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "shibuya",
"author": {
- "name": "Bohan Cheng",
- "email": "cbh778899@outlook.com"
+ "name": "Bohan Cheng",
+ "email": "cbh778899@outlook.com"
},
"version": "0.1.11",
"main": "electron.js",
@@ -20,6 +20,7 @@
"@aws-sdk/credential-providers": "^3.650.0",
"@huggingface/jinja": "^0.3.0",
"@wllama/wllama": "^1.16.0",
+ "openai": "^4.61.0",
"react": "^18.3.1",
"react-bootstrap-icons": "^1.11.4",
"react-dom": "^18.3.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b14b16a..3cd601e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,9 @@ importers:
'@wllama/wllama':
specifier: ^1.16.0
version: 1.16.1
+ openai:
+ specifier: ^4.61.0
+ version: 4.61.0(encoding@0.1.13)
react:
specifier: ^18.3.1
version: 18.3.1
@@ -901,6 +904,12 @@ packages:
'@types/ms@0.7.34':
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
+ '@types/node-fetch@2.6.11':
+ resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
+
+ '@types/node@18.19.50':
+ resolution: {integrity: sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==}
+
'@types/node@20.16.1':
resolution: {integrity: sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==}
@@ -913,6 +922,9 @@ packages:
'@types/prop-types@15.7.12':
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
+ '@types/qs@6.9.16':
+ resolution: {integrity: sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==}
+
'@types/react-dom@18.3.0':
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
@@ -953,6 +965,10 @@ packages:
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+ abort-controller@3.0.0:
+ resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
+ engines: {node: '>=6.5'}
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -1551,6 +1567,10 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
+ event-target-shim@5.0.1:
+ resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
+ engines: {node: '>=6'}
+
exponential-backoff@3.1.1:
resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
@@ -1610,10 +1630,17 @@ packages:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'}
+ form-data-encoder@1.7.2:
+ resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
+
form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
+ formdata-node@4.4.1:
+ resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
+ engines: {node: '>= 12.20'}
+
fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
@@ -2318,6 +2345,19 @@ packages:
node-api-version@0.2.0:
resolution: {integrity: sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==}
+ node-domexception@1.0.0:
+ resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
+ engines: {node: '>=10.5.0'}
+
+ node-fetch@2.7.0:
+ resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
node-gyp@9.4.1:
resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==}
engines: {node: ^12.13 || ^14.13 || >=16}
@@ -2379,6 +2419,15 @@ packages:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
+ openai@4.61.0:
+ resolution: {integrity: sha512-xkygRBRLIUumxzKGb1ug05pWmJROQsHkGuj/N6Jiw2dj0dI19JvbFpErSZKmJ/DA+0IvpcugZqCAyk8iLpyM6Q==}
+ hasBin: true
+ peerDependencies:
+ zod: ^3.23.8
+ peerDependenciesMeta:
+ zod:
+ optional: true
+
optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
@@ -2490,6 +2539,10 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
+ qs@6.13.0:
+ resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
+ engines: {node: '>=0.6'}
+
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -2836,6 +2889,9 @@ packages:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
@@ -2880,6 +2936,9 @@ packages:
unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+ undici-types@5.26.5:
+ resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+
undici-types@6.19.8:
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
@@ -2980,6 +3039,16 @@ packages:
wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
+ web-streams-polyfill@4.0.0-beta.3:
+ resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
+ engines: {node: '>= 14'}
+
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
@@ -4335,6 +4404,15 @@ snapshots:
'@types/ms@0.7.34': {}
+ '@types/node-fetch@2.6.11':
+ dependencies:
+ '@types/node': 22.4.2
+ form-data: 4.0.0
+
+ '@types/node@18.19.50':
+ dependencies:
+ undici-types: 5.26.5
+
'@types/node@20.16.1':
dependencies:
undici-types: 6.19.8
@@ -4351,6 +4429,8 @@ snapshots:
'@types/prop-types@15.7.12': {}
+ '@types/qs@6.9.16': {}
+
'@types/react-dom@18.3.0':
dependencies:
'@types/react': 18.3.5
@@ -4395,6 +4475,10 @@ snapshots:
abbrev@1.1.1: {}
+ abort-controller@3.0.0:
+ dependencies:
+ event-target-shim: 5.0.1
+
acorn-jsx@5.3.2(acorn@8.12.1):
dependencies:
acorn: 8.12.1
@@ -5253,6 +5337,8 @@ snapshots:
esutils@2.0.3: {}
+ event-target-shim@5.0.1: {}
+
exponential-backoff@3.1.1: {}
extend@3.0.2: {}
@@ -5317,12 +5403,19 @@ snapshots:
cross-spawn: 7.0.3
signal-exit: 4.1.0
+ form-data-encoder@1.7.2: {}
+
form-data@4.0.0:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
+ formdata-node@4.4.1:
+ dependencies:
+ node-domexception: 1.0.0
+ web-streams-polyfill: 4.0.0-beta.3
+
fs-constants@1.0.0: {}
fs-extra@10.1.0:
@@ -6182,6 +6275,14 @@ snapshots:
dependencies:
semver: 7.6.3
+ node-domexception@1.0.0: {}
+
+ node-fetch@2.7.0(encoding@0.1.13):
+ dependencies:
+ whatwg-url: 5.0.0
+ optionalDependencies:
+ encoding: 0.1.13
+
node-gyp@9.4.1:
dependencies:
env-paths: 2.2.1
@@ -6256,6 +6357,20 @@ snapshots:
dependencies:
mimic-fn: 2.1.0
+ openai@4.61.0(encoding@0.1.13):
+ dependencies:
+ '@types/node': 18.19.50
+ '@types/node-fetch': 2.6.11
+ '@types/qs': 6.9.16
+ abort-controller: 3.0.0
+ agentkeepalive: 4.5.0
+ form-data-encoder: 1.7.2
+ formdata-node: 4.4.1
+ node-fetch: 2.7.0(encoding@0.1.13)
+ qs: 6.13.0
+ transitivePeerDependencies:
+ - encoding
+
optionator@0.9.4:
dependencies:
deep-is: 0.1.4
@@ -6369,6 +6484,10 @@ snapshots:
punycode@2.3.1: {}
+ qs@6.13.0:
+ dependencies:
+ side-channel: 1.0.6
+
queue-microtask@1.2.3: {}
quick-lru@5.1.1: {}
@@ -6816,6 +6935,8 @@ snapshots:
to-fast-properties@2.0.0: {}
+ tr46@0.0.3: {}
+
trim-lines@3.0.1: {}
trough@2.2.0: {}
@@ -6874,6 +6995,8 @@ snapshots:
has-symbols: 1.0.3
which-boxed-primitive: 1.0.2
+ undici-types@5.26.5: {}
+
undici-types@6.19.8: {}
unified@11.0.5:
@@ -6968,6 +7091,15 @@ snapshots:
dependencies:
defaults: 1.0.4
+ web-streams-polyfill@4.0.0-beta.3: {}
+
+ webidl-conversions@3.0.1: {}
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
which-boxed-primitive@1.0.2:
dependencies:
is-bigint: 1.0.4
diff --git a/src/components/chat/Conversation.jsx b/src/components/chat/Conversation.jsx
index 792103f..4ad1894 100644
--- a/src/components/chat/Conversation.jsx
+++ b/src/components/chat/Conversation.jsx
@@ -4,9 +4,10 @@ import { FileImageFill, FileTextFill, Paperclip, Send, StopCircleFill } from 're
import useIDB from "../../utils/idb";
import { isModelLoaded, loadModel } from '../../utils/workers/worker'
import { getCompletionFunctions } from "../../utils/workers";
-import { setClient } from "../../utils/workers/aws-worker";
+import { setClient as setAwsClient } from "../../utils/workers/aws-worker";
+import { setClient as setOpenaiClient } from "../../utils/workers/openai-worker";
-export default function Conversation({ uid, client }) {
+export default function Conversation({ uid, client, updateClient }) {
const [conversation, setConversation] = useState([]);
const [message, setMessage] = useState('');
@@ -130,15 +131,21 @@ export default function Conversation({ uid, client }) {
useEffect(()=>{
if(!chat_functions.current) return;
- if(chat_functions.current.platform === 'AWS') {
+ const platform = chat_functions.current.platform
+ if(platform) {
(async function() {
- if(await setClient(client)) {
- await idb.updateOne('chat-history', {client}, [{uid}])
+ let set_result =
+ platform === "AWS" ? await setAwsClient(client) :
+ platform === "OpenAI" ? await setOpenaiClient(client) :
+ null;
+
+ if(set_result) {
+ updateClient(set_result);
}
})()
}
// eslint-disable-next-line
- }, [chat_functions, client])
+ }, [client])
return (
diff --git a/src/components/chat/Tickets.jsx b/src/components/chat/Tickets.jsx
index 3265434..d91491b 100644
--- a/src/components/chat/Tickets.jsx
+++ b/src/components/chat/Tickets.jsx
@@ -1,17 +1,16 @@
-import { useEffect, useState } from "react";
+import { useEffect } from "react";
import Ticket from "./Ticket";
import useIDB from "../../utils/idb";
import { genRandomID } from "../../utils/tools";
-export default function Tickets({selectChat, current_chat}) {
+export default function Tickets({selectChat, current_chat, history, setHistory}) {
- const [tickets, setTickets] = useState([]);
const idb = useIDB();
async function syncHistory() {
const history = await idb.getAll('chat-history')
history.sort((a, b)=>b.updatedAt - a.updatedAt)
- setTickets(history)
+ setHistory(history)
}
async function startNewConversation() {
@@ -27,9 +26,9 @@ export default function Tickets({selectChat, current_chat}) {
)
const new_conv_info = await idb.getByID('chat-history', conv_id);
new_conv_info &&
- setTickets([
+ setHistory([
new_conv_info,
- ...tickets
+ ...history
])
selectChat(new_conv_info)
}
@@ -47,7 +46,7 @@ export default function Tickets({selectChat, current_chat}) {
>
Start New Chat
- { tickets.map(elem => {
+ { history.map(elem => {
const { title, uid } = elem;
return (
e.uid === chat.uid)
+ ].client = client;
+ setHistory(history_cp);
+
+ idb.updateOne('chat-history', {client}, [{uid:chat.uid}])
+ }
return (
-
-
+
+
)
}
\ No newline at end of file
diff --git a/src/components/settings/AwsSettings.jsx b/src/components/settings/AwsSettings.jsx
index b0ed73f..aa7de91 100644
--- a/src/components/settings/AwsSettings.jsx
+++ b/src/components/settings/AwsSettings.jsx
@@ -4,29 +4,16 @@ import SettingSection from "./SettingSection";
import TextComponent from "./components/TextComponent";
import PasswordComponent from "./components/PasswordComponent";
import { getJSONCredentials, storeCredentials } from "../../utils/workers/aws-worker";
-import { getPlatformSettings } from "../../utils/general_settings";
+import { getPlatformSettings, updatePlatformSettings } from "../../utils/general_settings";
-export default function AwsSettings({ trigger, platform_setting, updatePlatformSetting }) {
+export default function AwsSettings({ trigger, enabled, updateEnabled }) {
- const [ aws_enabled, setAwsEnabled ] = useState(false);
const [ aws_region, setAwsRegion ] = useState('');
const [ aws_key_id, setAwsKeyID ] = useState('');
const [ aws_secret_key, setAwsSecretKey ] = useState('');
const [ aws_session_token, setAwsSessionToken ] = useState('');
const [ aws_model_id, setAwsModelID ] = useState('');
-
- function setEnabled(is_enabled) {
- if(aws_enabled && !is_enabled) {
- updatePlatformSetting({
- enabled_platform: null
- })
- } else if(!aws_enabled && is_enabled) {
- updatePlatformSetting({
- enabled_platform: 'AWS'
- })
- }
- }
-
+
function saveSettings() {
const credentials = {
key_id: aws_key_id, secret_key: aws_secret_key
@@ -36,9 +23,9 @@ export default function AwsSettings({ trigger, platform_setting, updatePlatformS
}
storeCredentials(
credentials, aws_key_id && aws_secret_key,
- platform_setting.enabled_platform === 'AWS'
+ enabled
)
- updatePlatformSetting({
+ updatePlatformSettings({
aws_model_id, aws_region
})
}
@@ -60,10 +47,6 @@ export default function AwsSettings({ trigger, platform_setting, updatePlatformS
})()
}, [])
- useEffect(()=>{
- setAwsEnabled(platform_setting.enabled_platform === 'AWS');
- }, [platform_setting])
-
useEffect(()=>{
trigger && saveSettings();
// eslint-disable-next-line
@@ -73,37 +56,37 @@ export default function AwsSettings({ trigger, platform_setting, updatePlatformS
)
diff --git a/src/components/settings/OpenaiSettings.jsx b/src/components/settings/OpenaiSettings.jsx
new file mode 100644
index 0000000..4611506
--- /dev/null
+++ b/src/components/settings/OpenaiSettings.jsx
@@ -0,0 +1,58 @@
+import { useEffect, useState } from "react";
+import SettingSection from "./SettingSection";
+import TrueFalseComponent from "./components/TrueFalseComponent";
+import TextComponent from "./components/TextComponent";
+import PasswordComponent from "./components/PasswordComponent";
+import { getPlatformSettings, updatePlatformSettings } from "../../utils/general_settings";
+import { getCredentials, storeCredentials } from "../../utils/workers/openai-worker";
+
+export default function OpenaiSettings({ trigger, enabled, updateEnabled }) {
+
+ const [api_key, setAPIKey] = useState('');
+ const [model_name, setModelName] = useState('');
+
+ function saveSettings() {
+ updatePlatformSettings({
+ openai_model: model_name
+ })
+ storeCredentials({api_key})
+ }
+
+ useEffect(()=>{
+ trigger && saveSettings();
+ // eslint-disable-next-line
+ }, [trigger])
+
+ useEffect(()=>{
+ (async function() {
+ const credentials = await getCredentials();
+ if(credentials) {
+ setAPIKey(credentials.api_key || '')
+ }
+
+ const { openai_model } = getPlatformSettings();
+ setModelName(openai_model);
+ })()
+ }, [])
+
+ return (
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/settings/index.jsx b/src/components/settings/index.jsx
index 2f3e2fd..7cd84b4 100644
--- a/src/components/settings/index.jsx
+++ b/src/components/settings/index.jsx
@@ -1,20 +1,17 @@
import { useState } from "react";
import AwsSettings from "./AwsSettings";
-import { getPlatformSettings, updatePlatformSettings as setStorageSetting } from "../../utils/general_settings";
+import { getPlatformSettings, updatePlatformSettings } from "../../utils/general_settings";
import ModelSettings from "./ModelSettings";
+import OpenaiSettings from "./OpenaiSettings";
export default function Settings() {
- const [platfom_settings, updatePlatformSettings] = useState(getPlatformSettings())
+ const [enabled_platform, setEnabledPlatform] = useState(getPlatformSettings().enabled_platform)
const [ saveSettingTrigger, toggleSaveSetting ] = useState(false);
- function updateSettings(settings) {
- const new_settings = {
- ...platfom_settings,
- ...settings
- }
- updatePlatformSettings(new_settings);
- setStorageSetting(new_settings);
+ function updatePlatform(platform = null) {
+ setEnabledPlatform(platform);
+ updatePlatformSettings({ enabled_platform: platform })
}
function save() {
@@ -29,8 +26,13 @@ export default function Settings() {
/>
updatePlatform(set ? "AWS" : null)}
+ />
+ updatePlatform(set ? "OpenAI" : null)}
/>
{ saveSettingTrigger ? "Settings Saved!" : "Save Settings" }
diff --git a/src/utils/general_settings.js b/src/utils/general_settings.js
index 4910a62..b8fc1c5 100644
--- a/src/utils/general_settings.js
+++ b/src/utils/general_settings.js
@@ -2,7 +2,9 @@ const PLATFORM_SETTINGS_KEY = 'platform-settings'
const DEFAULT_PLATFORM_SETTINGS = {
enabled_platform: null,
// aws
- aws_model_id: '', aws_region: ''
+ aws_model_id: '', aws_region: '',
+ // openai
+ openai_model: ''
}
const MODEL_SETTINGS_KEY = 'general-model-settings'
@@ -12,7 +14,7 @@ const DEFAULT_MODEL_SETTINGS = {
temperature: 0.7
}
-function getSettings(key, default_settings) {
+function loadSettings(key, default_settings) {
const setting = localStorage.getItem(key);
if(!setting) {
localStorage.setItem(key, JSON.stringify(default_settings))
@@ -20,22 +22,33 @@ function getSettings(key, default_settings) {
return setting ? JSON.parse(setting) : default_settings;
}
-function updateSettings(key, settings, default_settings) {
- localStorage.setItem(key, JSON.stringify({...default_settings, ...settings}));
-}
+// =============================================================
+// MODEL
+// =============================================================
-export function getPlatformSettings() {
- return getSettings(PLATFORM_SETTINGS_KEY, DEFAULT_PLATFORM_SETTINGS);
+let model_settings = loadSettings(MODEL_SETTINGS_KEY, DEFAULT_MODEL_SETTINGS);
+
+export function getModelSettings() {
+ return model_settings;
}
-export function updatePlatformSettings(settings) {
- updateSettings(PLATFORM_SETTINGS_KEY, settings, DEFAULT_PLATFORM_SETTINGS);
+export function updateModelSettings(settings) {
+ model_settings = { ...model_settings, ...settings };
+ localStorage.setItem(MODEL_SETTINGS_KEY, JSON.stringify(model_settings));
}
-export function getModelSettings() {
- return getSettings(MODEL_SETTINGS_KEY, DEFAULT_MODEL_SETTINGS);
+
+// =============================================================
+// PLATFORM
+// =============================================================
+
+let platform_settings = loadSettings(PLATFORM_SETTINGS_KEY, DEFAULT_PLATFORM_SETTINGS);
+
+export function getPlatformSettings() {
+ return platform_settings;
}
-export function updateModelSettings(settings) {
- updateSettings(MODEL_SETTINGS_KEY, settings, DEFAULT_MODEL_SETTINGS);
+export function updatePlatformSettings(settings) {
+ platform_settings = { ...platform_settings, ...settings }
+ localStorage.setItem(PLATFORM_SETTINGS_KEY, JSON.stringify(platform_settings));
}
\ No newline at end of file
diff --git a/src/utils/workers/aws-worker.js b/src/utils/workers/aws-worker.js
index 1e9c148..1248e43 100644
--- a/src/utils/workers/aws-worker.js
+++ b/src/utils/workers/aws-worker.js
@@ -16,10 +16,8 @@ export async function getCredentials(json_credentials = null) {
return obj
}
-export async function storeCredentials(credentials, all_filled, enabled = false) {
- const update_result = await instance.updateByID('credentials', 'AWS', {json: credentials})
- if(all_filled && enabled) await initBedrockClient();
- return !!update_result
+export async function storeCredentials(credentials) {
+ return !!(await instance.updateByID('credentials', 'AWS', {json: credentials}))
}
export async function getJSONCredentials() {
@@ -34,7 +32,7 @@ export async function getJSONCredentials() {
let bedrock_client = null;
export async function setClient(client) {
- if(!client) {
+ if(!client || !(client instanceof BedrockRuntimeClient)) {
await initBedrockClient();
return bedrock_client;
} else {
@@ -153,7 +151,12 @@ export async function chatCompletions(messages, cb = null) {
if(system.length) input.system = system;
let response_text = '', usage = {}
- abort_signal = false;
+ if(abort_signal) {
+ abort_signal = false;
+ cb && cb('', true);
+ return null;
+ }
+
try {
const command = new ConverseStreamCommand(input);
const response = await bedrock_client.send(command);
@@ -168,11 +171,12 @@ export async function chatCompletions(messages, cb = null) {
if(abort_signal) break;
}
cb && cb(response_text, true);
- abort_signal = false;
} catch(error) {
console.error(error);
cb && cb(`**${error.name}**:\n\`\`\`\n${error.message}\n\`\`\``, true);
return null;
+ } finally {
+ abort_signal = false;
}
return { content: response_text, usage };
diff --git a/src/utils/workers/index.js b/src/utils/workers/index.js
index cb32bcb..df30481 100644
--- a/src/utils/workers/index.js
+++ b/src/utils/workers/index.js
@@ -1,12 +1,13 @@
import { getPlatformSettings } from "../general_settings";
import { chatCompletions as WllamaCompletions, abortCompletion as WllamaAbort } from "./worker";
import { chatCompletions as AwsCompletions, abortCompletion as AwsAbort } from "./aws-worker"
+import { chatCompletions as OpenaiCompletions, abortCompletion as OpenaiAbort } from "./openai-worker";
/**
* @typedef CompletionFunctions
* @property {Function} completions
* @property {Function} abort
- * @property {"Wllama" | "AWS"} platform
+ * @property {"Wllama" | "AWS" | "OpenAI"} platform
*/
/**
@@ -19,6 +20,8 @@ export function getCompletionFunctions() {
switch(platform_settings.enabled_platform ) {
case 'AWS':
return { completions: AwsCompletions, abort: AwsAbort, platform: "AWS" }
+ case 'OpenAI':
+ return { completions: OpenaiCompletions, abort: OpenaiAbort, platform: "OpenAI"}
default:
return { completions: WllamaCompletions, abort: WllamaAbort, platform: "Wllama" }
}
diff --git a/src/utils/workers/openai-worker.js b/src/utils/workers/openai-worker.js
new file mode 100644
index 0000000..0eb0c26
--- /dev/null
+++ b/src/utils/workers/openai-worker.js
@@ -0,0 +1,112 @@
+import OpenAI from 'openai';
+import {instance} from '../idb'
+import { getPlatformSettings } from '../general_settings';
+
+/**
+ * @type {OpenAI?}
+ */
+let openai_client = null;
+
+let abort_signal = false;
+
+export async function getCredentials() {
+ const record = await instance.getByID('credentials', 'OpenAI', ['json']);
+ if(!record) return null;
+ return record.json;
+}
+
+export async function storeCredentials(credentials) {
+ return !!(await instance.updateByID('credentials', 'OpenAI', {json: credentials}))
+}
+
+async function initOpenAIClient() {
+ const credentials = await getCredentials();
+ if(!credentials) return false;
+ openai_client = new OpenAI({
+ apiKey: credentials.api_key,
+ dangerouslyAllowBrowser: true
+ })
+ return true;
+}
+
+export async function setClient(client) {
+ if(!client || !(client instanceof OpenAI)) {
+ await initOpenAIClient();
+ return openai_client;
+ } else {
+ openai_client = client;
+ return null;
+ }
+}
+
+/**
+ * @typedef Message
+ * @property {"user"|"assistant"|"system"} role Sender
+ * @property {String} content Message content
+ */
+
+/**
+ * @typedef UsageObj
+ * @property {Number} inputTokens
+ * @property {Number} outputTokens
+ * @property {Number} totalTokens
+ */
+
+/**
+ * @typedef CompletionResponse
+ * @property { String } content Content of response
+ * @property { UsageObj } usage
+ */
+
+/**
+ * @callback CompletionCallback
+ * @param {String} text Whole text message been generated from start
+ * @param {Boolean} is_finished Specify is response finished or not
+ */
+
+/**
+ * Do completion use aws settings
+ * @param {Message[]} messages Messages you need to send
+ * @param {CompletionCallback} cb Callback function
+ * @returns { Promise }
+ */
+export async function chatCompletions(messages, cb = null) {
+ if(!openai_client && !(await initOpenAIClient())) return;
+
+ const { openai_model:model } = getPlatformSettings();
+
+ if(abort_signal) {
+ abort_signal = false;
+ cb && cb('', true);
+ return null;
+ }
+
+ let response_text = '', usage = {};
+ try {
+ const stream = await openai_client.chat.completions.create({
+ model, stream: true, stream_options: { include_usage: true },
+ messages
+ })
+
+ for await (const chunk of stream) {
+ const delta = chunk.choices[0].delta
+ response_text += delta.content || ''
+ cb && cb(response_text, false);
+ if(chunk.usage) usage = chunk.usage;
+ if(chunk.choices[0].finish_reason) break;
+ if(abort_signal) break;
+ }
+ } catch(error) {
+ console.error(error);
+ cb && cb(`**${error.name}**:\n\`\`\`\n${error.message}\n\`\`\``, true);
+ return null;
+ } finally {
+ abort_signal = false;
+ }
+
+ return { content: response_text, usage }
+}
+
+export function abortCompletion() {
+ abort_signal = true;
+}
\ No newline at end of file