@aj-archipelago/cortex 1.3.5 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/helper-apps/cortex-autogen/agents.py +31 -2
- package/helper-apps/cortex-realtime-voice-server/.env.sample +6 -0
- package/helper-apps/cortex-realtime-voice-server/README.md +22 -0
- package/helper-apps/cortex-realtime-voice-server/bun.lockb +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/bun.lockb +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/index.html +12 -0
- package/helper-apps/cortex-realtime-voice-server/client/package.json +65 -0
- package/helper-apps/cortex-realtime-voice-server/client/postcss.config.js +6 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/favicon.ico +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/index.html +43 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/logo192.png +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/logo512.png +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/manifest.json +25 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/robots.txt +3 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/sounds/connect.mp3 +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/public/sounds/disconnect.mp3 +0 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/App.test.tsx +9 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/App.tsx +126 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/SettingsModal.tsx +207 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/Chat.tsx +553 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubble.tsx +22 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubbleLeft.tsx +22 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubbleRight.tsx +21 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatMessage.tsx +27 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatMessageInput.tsx +74 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatTile.tsx +211 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/SoundEffects.ts +56 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavPacker.ts +112 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavRecorder.ts +571 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavStreamPlayer.ts +290 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/analysis/AudioAnalysis.ts +186 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/analysis/constants.ts +59 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/worklets/AudioProcessor.ts +214 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/worklets/StreamProcessor.ts +183 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/AudioVisualizer.tsx +151 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/CopyButton.tsx +32 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/ImageOverlay.tsx +166 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/MicrophoneVisualizer.tsx +95 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/ScreenshotCapture.tsx +116 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/hooks/useWindowResize.ts +27 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/utils/audio.ts +33 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/index.css +20 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/index.tsx +19 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/logo.svg +1 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/react-app-env.d.ts +1 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/reportWebVitals.ts +15 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/setupTests.ts +5 -0
- package/helper-apps/cortex-realtime-voice-server/client/src/utils/logger.ts +45 -0
- package/helper-apps/cortex-realtime-voice-server/client/tailwind.config.js +14 -0
- package/helper-apps/cortex-realtime-voice-server/client/tsconfig.json +30 -0
- package/helper-apps/cortex-realtime-voice-server/client/vite.config.ts +22 -0
- package/helper-apps/cortex-realtime-voice-server/index.ts +19 -0
- package/helper-apps/cortex-realtime-voice-server/package.json +28 -0
- package/helper-apps/cortex-realtime-voice-server/src/ApiServer.ts +35 -0
- package/helper-apps/cortex-realtime-voice-server/src/SocketServer.ts +737 -0
- package/helper-apps/cortex-realtime-voice-server/src/Tools.ts +520 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/expert.ts +29 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/image.ts +29 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/memory.ts +91 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/reason.ts +29 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/search.ts +30 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/style.ts +31 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/utils.ts +95 -0
- package/helper-apps/cortex-realtime-voice-server/src/cortex/vision.ts +34 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/client.ts +499 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/realtimeTypes.ts +279 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/socket.ts +27 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/transcription.ts +75 -0
- package/helper-apps/cortex-realtime-voice-server/src/realtime/utils.ts +33 -0
- package/helper-apps/cortex-realtime-voice-server/src/utils/logger.ts +45 -0
- package/helper-apps/cortex-realtime-voice-server/src/utils/prompt.ts +81 -0
- package/helper-apps/cortex-realtime-voice-server/tsconfig.json +28 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +3 -1
- package/pathways/system/entity/memory/sys_memory_manager.js +3 -0
- package/pathways/system/entity/memory/sys_memory_update.js +44 -45
- package/pathways/system/entity/memory/sys_read_memory.js +86 -6
- package/pathways/system/entity/memory/sys_search_memory.js +66 -0
- package/pathways/system/entity/shared/sys_entity_constants.js +2 -2
- package/pathways/system/entity/sys_entity_continue.js +2 -1
- package/pathways/system/entity/sys_entity_start.js +10 -0
- package/pathways/system/entity/sys_generator_expert.js +0 -2
- package/pathways/system/entity/sys_generator_memory.js +31 -0
- package/pathways/system/entity/sys_generator_voice_sample.js +36 -0
- package/pathways/system/entity/sys_router_tool.js +13 -10
- package/pathways/system/sys_parse_numbered_object_list.js +1 -1
- package/server/pathwayResolver.js +41 -31
- package/server/plugins/azureVideoTranslatePlugin.js +28 -16
- package/server/plugins/claude3VertexPlugin.js +0 -9
- package/server/plugins/gemini15ChatPlugin.js +18 -5
- package/server/plugins/modelPlugin.js +27 -6
- package/server/plugins/openAiChatPlugin.js +10 -8
- package/server/plugins/openAiVisionPlugin.js +56 -0
- package/tests/memoryfunction.test.js +73 -1
|
@@ -20,6 +20,7 @@ import shutil
|
|
|
20
20
|
|
|
21
21
|
human_input_queues = {}
|
|
22
22
|
human_input_text_queues = {}
|
|
23
|
+
request_stored_message_queues = {}
|
|
23
24
|
def background_human_input_check(request_id):
|
|
24
25
|
while True:
|
|
25
26
|
human_input = check_for_human_input(request_id)
|
|
@@ -176,6 +177,14 @@ def chat_with_agents(**kwargs):
|
|
|
176
177
|
except Exception as e:
|
|
177
178
|
logging.error(f"Error extracting code corrector result: {e}")
|
|
178
179
|
|
|
180
|
+
try:
|
|
181
|
+
request_stored_message_queues[request_id].put(all_messages[-2]["message"] or all_messages[-2]["content"])
|
|
182
|
+
request_stored_message_queues[request_id].put(all_messages[-1]["message"] or all_messages[-1]["content"])
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logging.error(f"Error storing messages in queue: {e}")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
179
188
|
if return_type == "chat_history":
|
|
180
189
|
return chat_result.chat_history
|
|
181
190
|
if return_type == "chat_result":
|
|
@@ -249,6 +258,9 @@ def process_message(original_request_message_data, original_request_message_data
|
|
|
249
258
|
|
|
250
259
|
human_input_queues[request_id] = queue.Queue()
|
|
251
260
|
human_input_text_queues[request_id] = queue.Queue()
|
|
261
|
+
if not request_stored_message_queues.get(request_id):
|
|
262
|
+
request_stored_message_queues[request_id] = queue.Queue()
|
|
263
|
+
request_stored_message_queues[request_id].put(original_request_message)
|
|
252
264
|
|
|
253
265
|
if first_run:
|
|
254
266
|
thread = threading.Thread(target=background_human_input_check, args=(request_id,))
|
|
@@ -275,7 +287,7 @@ def process_message(original_request_message_data, original_request_message_data
|
|
|
275
287
|
|
|
276
288
|
#wait for any human input before terminating
|
|
277
289
|
#if you receive human input start the conversation again
|
|
278
|
-
for i in range(
|
|
290
|
+
for i in range(31*6): # 30+1 minutes
|
|
279
291
|
if human_input_queues[request_id].empty():
|
|
280
292
|
time.sleep(1)
|
|
281
293
|
else:
|
|
@@ -284,11 +296,28 @@ def process_message(original_request_message_data, original_request_message_data
|
|
|
284
296
|
logging.info(f"Human input to assistant: {human_input}")
|
|
285
297
|
#update request with human input
|
|
286
298
|
new_message_data = original_request_message_data.copy()
|
|
287
|
-
|
|
299
|
+
|
|
300
|
+
old_task = original_request_message_data.get("message")
|
|
301
|
+
|
|
302
|
+
#get request_stored_message_queues
|
|
303
|
+
old_messages = []
|
|
304
|
+
if request_stored_message_queues.get(request_id):
|
|
305
|
+
while not request_stored_message_queues[request_id].empty():
|
|
306
|
+
old_messages.append(request_stored_message_queues[request_id].get())
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
#convert to text, limit to max 2000 characters, keep most recent
|
|
310
|
+
old_messages_text = "\n".join(old_messages)
|
|
311
|
+
old_messages_text = old_messages_text[-2000:]
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
new_message_data['message'] = f"NEW TASK: {human_input}\n\nPREV TASK: {old_task} STUFF DONE IN PREV TASK: {old_messages_text}\n\n{final_msg}\n\n"
|
|
288
315
|
new_message_data['keywords'] = ''
|
|
289
316
|
# new_message_data_obj = original_request_message_data_obj.copy()
|
|
290
317
|
# new_message_data_obj['message'] = new_message_data['message']
|
|
291
318
|
|
|
319
|
+
|
|
320
|
+
|
|
292
321
|
process_message(new_message_data, original_request_message_data_obj, first_run=False)
|
|
293
322
|
return
|
|
294
323
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# cortex-realtime-voice
|
|
2
|
+
|
|
3
|
+
To install dependencies:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun install
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
To run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd client
|
|
13
|
+
bun run build
|
|
14
|
+
cd ..
|
|
15
|
+
bun run start
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
To run in production:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bun run start:prod
|
|
22
|
+
```
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Cortex Realtime Voice</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/index.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@emotion/react": "^11.14.0",
|
|
7
|
+
"@emotion/styled": "^11.14.0",
|
|
8
|
+
"@mui/icons-material": "^6.2.0",
|
|
9
|
+
"@mui/material": "^6.2.0",
|
|
10
|
+
"@tailwindcss/typography": "^0.5.15",
|
|
11
|
+
"katex": "^0.16.15",
|
|
12
|
+
"react": "^19.0.0",
|
|
13
|
+
"react-dom": "^19.0.0",
|
|
14
|
+
"react-markdown": "^9.0.1",
|
|
15
|
+
"react-spinners": "0.15.0",
|
|
16
|
+
"react-syntax-highlighter": "^15.6.1",
|
|
17
|
+
"rehype-katex": "^7.0.1",
|
|
18
|
+
"rehype-raw": "^7.0.0",
|
|
19
|
+
"remark-gfm": "^4.0.0",
|
|
20
|
+
"remark-math": "^6.0.0",
|
|
21
|
+
"socket.io-client": "4.8.1",
|
|
22
|
+
"typescript": "^4.4.2",
|
|
23
|
+
"web-vitals": "^2.1.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
|
|
27
|
+
"@testing-library/jest-dom": "^5.14.1",
|
|
28
|
+
"@testing-library/react": "^13.0.0",
|
|
29
|
+
"@testing-library/user-event": "^13.2.1",
|
|
30
|
+
"@types/jest": "^27.0.1",
|
|
31
|
+
"@types/node": "^16.7.13",
|
|
32
|
+
"@types/react": "^18.0.0",
|
|
33
|
+
"@types/react-dom": "^18.0.0",
|
|
34
|
+
"@types/react-syntax-highlighter": "^15.5.13",
|
|
35
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
36
|
+
"autoprefixer": "^10.4.20",
|
|
37
|
+
"postcss": "^8.4.49",
|
|
38
|
+
"tailwindcss": "^3.4.17",
|
|
39
|
+
"vite": "^6.0.5"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"start": "vite",
|
|
43
|
+
"dev": "vite",
|
|
44
|
+
"build": "vite build",
|
|
45
|
+
"preview": "vite preview"
|
|
46
|
+
},
|
|
47
|
+
"eslintConfig": {
|
|
48
|
+
"extends": [
|
|
49
|
+
"react-app",
|
|
50
|
+
"react-app/jest"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
"browserslist": {
|
|
54
|
+
"production": [
|
|
55
|
+
">0.2%",
|
|
56
|
+
"not dead",
|
|
57
|
+
"not op_mini all"
|
|
58
|
+
],
|
|
59
|
+
"development": [
|
|
60
|
+
"last 1 chrome version",
|
|
61
|
+
"last 1 firefox version",
|
|
62
|
+
"last 1 safari version"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<meta name="theme-color" content="#000000" />
|
|
8
|
+
<meta
|
|
9
|
+
name="description"
|
|
10
|
+
content="Chat with the Cortex Realtime Voice Server"
|
|
11
|
+
/>
|
|
12
|
+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
13
|
+
<!--
|
|
14
|
+
manifest.json provides metadata used when your web app is installed on a
|
|
15
|
+
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
16
|
+
-->
|
|
17
|
+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
18
|
+
<!--
|
|
19
|
+
Notice the use of %PUBLIC_URL% in the tags above.
|
|
20
|
+
It will be replaced with the URL of the `public` folder during the build.
|
|
21
|
+
Only files inside the `public` folder can be referenced from the HTML.
|
|
22
|
+
|
|
23
|
+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
24
|
+
work correctly both with client-side routing and a non-root public URL.
|
|
25
|
+
Learn how to configure a non-root public URL by running `npm run build`.
|
|
26
|
+
-->
|
|
27
|
+
<title>Cortex Realtime Voice</title>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
31
|
+
<div id="root"></div>
|
|
32
|
+
<!--
|
|
33
|
+
This HTML file is a template.
|
|
34
|
+
If you open it directly in the browser, you will see an empty page.
|
|
35
|
+
|
|
36
|
+
You can add webfonts, meta tags, or analytics to this file.
|
|
37
|
+
The build step will place the bundled scripts into the <body> tag.
|
|
38
|
+
|
|
39
|
+
To begin the development, run `npm start` or `yarn start`.
|
|
40
|
+
To create a production bundle, use `npm run build` or `yarn build`.
|
|
41
|
+
-->
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"short_name": "React App",
|
|
3
|
+
"name": "Create React App Sample",
|
|
4
|
+
"icons": [
|
|
5
|
+
{
|
|
6
|
+
"src": "favicon.ico",
|
|
7
|
+
"sizes": "64x64 32x32 24x24 16x16",
|
|
8
|
+
"type": "image/x-icon"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"src": "logo192.png",
|
|
12
|
+
"type": "image/png",
|
|
13
|
+
"sizes": "192x192"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"src": "logo512.png",
|
|
17
|
+
"type": "image/png",
|
|
18
|
+
"sizes": "512x512"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"start_url": ".",
|
|
22
|
+
"display": "standalone",
|
|
23
|
+
"theme_color": "#000000",
|
|
24
|
+
"background_color": "#ffffff"
|
|
25
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import App from './App';
|
|
4
|
+
|
|
5
|
+
test('renders learn react link', () => {
|
|
6
|
+
render(<App />);
|
|
7
|
+
const linkElement = screen.getByText(/learn react/i);
|
|
8
|
+
expect(linkElement).toBeInTheDocument();
|
|
9
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react';
|
|
2
|
+
import ClipLoader from "react-spinners/ClipLoader";
|
|
3
|
+
import Chat from "./chat/Chat";
|
|
4
|
+
import {SettingsModal} from "./SettingsModal";
|
|
5
|
+
import { SettingsData } from "./SettingsModal";
|
|
6
|
+
import type { Voice } from '../../src/realtime/realtimeTypes';
|
|
7
|
+
|
|
8
|
+
function App() {
|
|
9
|
+
const [loading, setLoading] = useState(true);
|
|
10
|
+
const [userName, setUserName] = useState('');
|
|
11
|
+
const [userId, setUserId] = useState('');
|
|
12
|
+
const [aiName, setAiName] = useState('Jarvis');
|
|
13
|
+
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
14
|
+
const [language, setLanguage] = useState('en');
|
|
15
|
+
const [aiMemorySelfModify, setAiMemorySelfModify] = useState(false);
|
|
16
|
+
const [aiStyle, setAiStyle] = useState('Anthropic');
|
|
17
|
+
const [voice, setVoice] = useState('alloy' as Voice);
|
|
18
|
+
|
|
19
|
+
const onCloseSettings = () => setSettingsOpen(false);
|
|
20
|
+
const onSaveSettings = (settings: SettingsData) => {
|
|
21
|
+
console.log('Saving settings', settings);
|
|
22
|
+
setUserName(settings.userName);
|
|
23
|
+
localStorage.setItem('userName', settings.userName);
|
|
24
|
+
|
|
25
|
+
let newUserId = settings.userId;
|
|
26
|
+
if (!newUserId || newUserId.length === 0) {
|
|
27
|
+
newUserId = Math.random().toString(36).substring(7);
|
|
28
|
+
}
|
|
29
|
+
setUserId(newUserId);
|
|
30
|
+
localStorage.setItem('userId', newUserId);
|
|
31
|
+
|
|
32
|
+
setAiName(settings.aiName);
|
|
33
|
+
localStorage.setItem('aiName', settings.aiName);
|
|
34
|
+
|
|
35
|
+
setLanguage(settings.language);
|
|
36
|
+
localStorage.setItem('language', settings.language);
|
|
37
|
+
|
|
38
|
+
setAiMemorySelfModify(settings.aiMemorySelfModify);
|
|
39
|
+
localStorage.setItem('aiMemorySelfModify', String(settings.aiMemorySelfModify));
|
|
40
|
+
|
|
41
|
+
setAiStyle(settings.aiStyle);
|
|
42
|
+
localStorage.setItem('aiStyle', settings.aiStyle);
|
|
43
|
+
|
|
44
|
+
setVoice(settings.voice as Voice);
|
|
45
|
+
localStorage.setItem('voice', settings.voice);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const name = localStorage.getItem('userName');
|
|
50
|
+
if (name) {
|
|
51
|
+
setUserName(localStorage.getItem('userName') as string);
|
|
52
|
+
} else {
|
|
53
|
+
setSettingsOpen(true);
|
|
54
|
+
}
|
|
55
|
+
const id = localStorage.getItem('userId');
|
|
56
|
+
if (id) {
|
|
57
|
+
setUserId(localStorage.getItem('userId') as string);
|
|
58
|
+
} else {
|
|
59
|
+
setSettingsOpen(true);
|
|
60
|
+
}
|
|
61
|
+
const ai = localStorage.getItem('aiName');
|
|
62
|
+
if (ai) {
|
|
63
|
+
setAiName(localStorage.getItem('aiName') as string);
|
|
64
|
+
} else {
|
|
65
|
+
setSettingsOpen(true);
|
|
66
|
+
}
|
|
67
|
+
const savedLanguage = localStorage.getItem('language');
|
|
68
|
+
if (savedLanguage) setLanguage(savedLanguage);
|
|
69
|
+
|
|
70
|
+
const savedAiMemorySelfModify = localStorage.getItem('aiMemorySelfModify');
|
|
71
|
+
if (savedAiMemorySelfModify) setAiMemorySelfModify(savedAiMemorySelfModify === 'true');
|
|
72
|
+
|
|
73
|
+
const savedAiStyle = localStorage.getItem('aiStyle');
|
|
74
|
+
if (savedAiStyle) setAiStyle(savedAiStyle);
|
|
75
|
+
|
|
76
|
+
const savedVoice = localStorage.getItem('voice');
|
|
77
|
+
if (savedVoice) setVoice(savedVoice as Voice);
|
|
78
|
+
|
|
79
|
+
setLoading(false);
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
if (loading) {
|
|
83
|
+
return (
|
|
84
|
+
<div className="min-h-screen bg-gradient-to-b from-gray-900 via-gray-800 to-gray-900 flex justify-center items-center">
|
|
85
|
+
<ClipLoader color={'#0EA5E9'} loading={true} size={150}/>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div className="h-screen overflow-hidden bg-gradient-to-b from-gray-900 via-gray-800 to-gray-900">
|
|
92
|
+
<button
|
|
93
|
+
className="fixed top-4 right-4 z-10 p-2 text-gray-400 hover:text-cyan-400 transition-colors duration-200"
|
|
94
|
+
onClick={() => setSettingsOpen(true)}
|
|
95
|
+
aria-label="Settings"
|
|
96
|
+
>
|
|
97
|
+
⚙️
|
|
98
|
+
</button>
|
|
99
|
+
|
|
100
|
+
{userName && userName.length > 0 && (
|
|
101
|
+
<Chat
|
|
102
|
+
userId={userId}
|
|
103
|
+
userName={userName}
|
|
104
|
+
aiName={aiName}
|
|
105
|
+
language={language}
|
|
106
|
+
aiMemorySelfModify={aiMemorySelfModify}
|
|
107
|
+
aiStyle={aiStyle}
|
|
108
|
+
voice={voice}
|
|
109
|
+
/>
|
|
110
|
+
)}
|
|
111
|
+
<SettingsModal
|
|
112
|
+
aiName={aiName}
|
|
113
|
+
userName={userName}
|
|
114
|
+
userId={userId}
|
|
115
|
+
voice={voice}
|
|
116
|
+
aiStyle={aiStyle}
|
|
117
|
+
language={language}
|
|
118
|
+
isOpen={settingsOpen}
|
|
119
|
+
onClose={onCloseSettings}
|
|
120
|
+
onSave={onSaveSettings}
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default App;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {ChangeEvent, FormEvent, useState} from 'react';
|
|
2
|
+
import type { Voice, OpenAIVoice, AzureVoice } from '../../src/realtime/realtimeTypes';
|
|
3
|
+
|
|
4
|
+
type SettingsModalProps = {
|
|
5
|
+
aiName: string;
|
|
6
|
+
userName: string;
|
|
7
|
+
userId: string;
|
|
8
|
+
voice: Voice;
|
|
9
|
+
aiStyle: string;
|
|
10
|
+
language: string;
|
|
11
|
+
isOpen: boolean;
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
onSave: (settings: SettingsData) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type SettingsData = {
|
|
17
|
+
userName: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
aiName: string;
|
|
20
|
+
language: string;
|
|
21
|
+
aiMemorySelfModify: boolean;
|
|
22
|
+
aiStyle: string;
|
|
23
|
+
voice: Voice;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Define voice lists
|
|
27
|
+
const openaiVoices: OpenAIVoice[] = ['alloy', 'echo', 'shimmer', 'ash', 'ballad', 'coral', 'sage', 'verse'];
|
|
28
|
+
const azureVoices: AzureVoice[] = ['amuch', 'dan', 'elan', 'marilyn', 'meadow', 'breeze', 'cove', 'ember', 'jupiter', 'alloy', 'echo', 'shimmer'];
|
|
29
|
+
|
|
30
|
+
// Check if we're using Azure based on the environment variable
|
|
31
|
+
const isAzure = import.meta.env.VITE_VOICE_PROVIDER === 'azure';
|
|
32
|
+
|
|
33
|
+
export const SettingsModal = (
|
|
34
|
+
{aiName, userName, userId, voice, aiStyle, language, isOpen, onClose, onSave}: SettingsModalProps
|
|
35
|
+
) => {
|
|
36
|
+
const [formData, setFormData] = useState<SettingsData>({
|
|
37
|
+
aiName,
|
|
38
|
+
userName,
|
|
39
|
+
userId,
|
|
40
|
+
language,
|
|
41
|
+
aiMemorySelfModify: false,
|
|
42
|
+
aiStyle,
|
|
43
|
+
voice
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
|
47
|
+
const { name, value, type } = e.target;
|
|
48
|
+
setFormData({
|
|
49
|
+
...formData,
|
|
50
|
+
[name]: type === 'checkbox' ? (e.target as HTMLInputElement).checked : value
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleSubmit = (e: FormEvent) => {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
onSave(formData);
|
|
57
|
+
onClose();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className={`fixed inset-0 z-50 overflow-y-auto ${isOpen ? 'block' : 'hidden'}`}>
|
|
62
|
+
<div className="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
|
63
|
+
<div className="fixed inset-0 transition-opacity bg-gray-900 bg-opacity-75 backdrop-blur-sm" aria-hidden="true" />
|
|
64
|
+
|
|
65
|
+
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
|
66
|
+
|
|
67
|
+
<div className="inline-block w-full max-w-md p-6 my-8 overflow-hidden text-left align-middle transition-all transform bg-gray-800 border border-gray-700/50 rounded-2xl shadow-2xl sm:align-middle">
|
|
68
|
+
<h3 className="text-lg font-medium leading-6 text-gray-100 mb-4">
|
|
69
|
+
System Configuration
|
|
70
|
+
</h3>
|
|
71
|
+
|
|
72
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
73
|
+
<div>
|
|
74
|
+
<label className="block text-sm font-medium text-gray-300 mb-1">
|
|
75
|
+
Your Name
|
|
76
|
+
</label>
|
|
77
|
+
<input
|
|
78
|
+
type="text"
|
|
79
|
+
name="userName"
|
|
80
|
+
className="w-full px-3 py-2 bg-gray-700/50 border border-gray-600/50 rounded-lg text-gray-100 focus:outline-none focus:ring-2 focus:ring-cyan-500/50"
|
|
81
|
+
value={formData.userName}
|
|
82
|
+
onChange={handleChange}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div>
|
|
87
|
+
<label className="block text-sm font-medium text-gray-300 mb-1">
|
|
88
|
+
AI Name
|
|
89
|
+
</label>
|
|
90
|
+
<input
|
|
91
|
+
type="text"
|
|
92
|
+
name="aiName"
|
|
93
|
+
className="w-full px-3 py-2 bg-gray-700/50 border border-gray-600/50 rounded-lg text-gray-100 focus:outline-none focus:ring-2 focus:ring-cyan-500/50"
|
|
94
|
+
value={formData.aiName}
|
|
95
|
+
onChange={handleChange}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div>
|
|
100
|
+
<label className="block text-sm font-medium text-gray-300 mb-1">
|
|
101
|
+
Voice
|
|
102
|
+
</label>
|
|
103
|
+
<select
|
|
104
|
+
name="voice"
|
|
105
|
+
className="w-full px-3 py-2 bg-gray-700/50 border border-gray-600/50 rounded-lg text-gray-100 focus:outline-none focus:ring-2 focus:ring-cyan-500/50"
|
|
106
|
+
value={formData.voice}
|
|
107
|
+
onChange={handleChange}
|
|
108
|
+
>
|
|
109
|
+
{isAzure ? (
|
|
110
|
+
<optgroup label="Azure Voices">
|
|
111
|
+
{azureVoices.map(voice => (
|
|
112
|
+
<option key={voice} value={voice}>
|
|
113
|
+
{voice.charAt(0).toUpperCase() + voice.slice(1)}
|
|
114
|
+
</option>
|
|
115
|
+
))}
|
|
116
|
+
</optgroup>
|
|
117
|
+
) : (
|
|
118
|
+
<optgroup label="OpenAI Voices">
|
|
119
|
+
{openaiVoices.map(voice => (
|
|
120
|
+
<option key={voice} value={voice}>
|
|
121
|
+
{voice.charAt(0).toUpperCase() + voice.slice(1)}
|
|
122
|
+
</option>
|
|
123
|
+
))}
|
|
124
|
+
</optgroup>
|
|
125
|
+
)}
|
|
126
|
+
</select>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div>
|
|
130
|
+
<label className="block text-sm font-medium text-gray-300 mb-1">
|
|
131
|
+
Language
|
|
132
|
+
</label>
|
|
133
|
+
<select
|
|
134
|
+
name="language"
|
|
135
|
+
className="w-full px-3 py-2 bg-gray-700/50 border border-gray-600/50 rounded-lg text-gray-100 focus:outline-none focus:ring-2 focus:ring-cyan-500/50"
|
|
136
|
+
value={formData.language}
|
|
137
|
+
onChange={handleChange}
|
|
138
|
+
>
|
|
139
|
+
<option value="en">English</option>
|
|
140
|
+
<option value="es">Español</option>
|
|
141
|
+
<option value="fr">Français</option>
|
|
142
|
+
<option value="de">Deutsch</option>
|
|
143
|
+
<option value="it">Italiano</option>
|
|
144
|
+
<option value="pt">Português</option>
|
|
145
|
+
<option value="ru">Русский</option>
|
|
146
|
+
<option value="zh">中文</option>
|
|
147
|
+
<option value="ja">日本語</option>
|
|
148
|
+
<option value="ko">한국어</option>
|
|
149
|
+
</select>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div>
|
|
153
|
+
<label className="block text-sm font-medium text-gray-300 mb-1">
|
|
154
|
+
AI Style
|
|
155
|
+
</label>
|
|
156
|
+
<select
|
|
157
|
+
name="aiStyle"
|
|
158
|
+
className="w-full px-3 py-2 bg-gray-700/50 border border-gray-600/50 rounded-lg text-gray-100 focus:outline-none focus:ring-2 focus:ring-cyan-500/50"
|
|
159
|
+
value={formData.aiStyle}
|
|
160
|
+
onChange={handleChange}
|
|
161
|
+
>
|
|
162
|
+
<option value="Anthropic">Anthropic</option>
|
|
163
|
+
<option value="OpenAI">OpenAI</option>
|
|
164
|
+
</select>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div>
|
|
168
|
+
<label className="block text-sm font-medium text-gray-300 mb-1">
|
|
169
|
+
Memory Key
|
|
170
|
+
</label>
|
|
171
|
+
<input
|
|
172
|
+
type="text"
|
|
173
|
+
name="userId"
|
|
174
|
+
className="w-full px-3 py-2 bg-gray-700/50 border border-gray-600/50 rounded-lg text-gray-100 focus:outline-none focus:ring-2 focus:ring-cyan-500/50"
|
|
175
|
+
value={formData.userId}
|
|
176
|
+
onChange={handleChange}
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div className="flex items-center">
|
|
181
|
+
<input
|
|
182
|
+
type="checkbox"
|
|
183
|
+
id="aiMemorySelfModify"
|
|
184
|
+
name="aiMemorySelfModify"
|
|
185
|
+
checked={formData.aiMemorySelfModify}
|
|
186
|
+
onChange={handleChange}
|
|
187
|
+
className="w-4 h-4 border-gray-600 rounded bg-gray-700 text-cyan-500 focus:ring-cyan-500/50"
|
|
188
|
+
/>
|
|
189
|
+
<label htmlFor="aiMemorySelfModify" className="ml-2 text-sm text-gray-300">
|
|
190
|
+
Enable AI Memory Self-Modification
|
|
191
|
+
</label>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div className="flex justify-end mt-6">
|
|
195
|
+
<button
|
|
196
|
+
type="submit"
|
|
197
|
+
className="px-4 py-2 bg-gradient-to-r from-blue-500 to-cyan-500 hover:from-blue-600 hover:to-cyan-600 text-white rounded-lg shadow-lg shadow-cyan-500/20 focus:outline-none focus:ring-2 focus:ring-cyan-500/50"
|
|
198
|
+
>
|
|
199
|
+
Save Configuration
|
|
200
|
+
</button>
|
|
201
|
+
</div>
|
|
202
|
+
</form>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|