@rimori/client 1.3.1 → 1.4.3
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/.prettierignore +35 -0
- package/README.md +77 -71
- package/dist/cli/scripts/init/dev-registration.d.ts +1 -1
- package/dist/cli/scripts/init/dev-registration.js +4 -4
- package/dist/cli/scripts/init/main.js +1 -1
- package/dist/cli/scripts/init/package-setup.d.ts +1 -1
- package/dist/cli/scripts/init/package-setup.js +3 -3
- package/dist/cli/scripts/init/router-transformer.js +19 -12
- package/dist/cli/scripts/init/vite-config.d.ts +2 -2
- package/dist/cli/scripts/init/vite-config.js +2 -2
- package/dist/cli/scripts/release/release-config-upload.js +9 -9
- package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
- package/dist/cli/scripts/release/release-db-update.js +9 -9
- package/dist/cli/scripts/release/release-file-upload.js +2 -2
- package/dist/cli/scripts/release/release.js +2 -2
- package/dist/cli/types/DatabaseTypes.d.ts +2 -2
- package/dist/components/CRUDModal.d.ts +1 -1
- package/dist/components/CRUDModal.js +3 -3
- package/dist/components/MarkdownEditor.js +16 -16
- package/dist/components/Spinner.js +2 -2
- package/dist/components/ai/Assistant.js +7 -8
- package/dist/components/ai/Avatar.d.ts +2 -2
- package/dist/components/ai/Avatar.js +14 -7
- package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +5 -6
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +1 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +4 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +1 -1
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -3
- package/dist/components/audio/Playbutton.js +10 -7
- package/dist/components/components/ContextMenu.d.ts +1 -1
- package/dist/components/components/ContextMenu.js +19 -16
- package/dist/components.d.ts +10 -10
- package/dist/components.js +10 -10
- package/dist/core/controller/AIController.d.ts +2 -2
- package/dist/core/controller/AIController.js +20 -18
- package/dist/core/controller/ExerciseController.d.ts +52 -0
- package/dist/core/controller/ExerciseController.js +73 -0
- package/dist/core/controller/ObjectController.js +5 -5
- package/dist/core/controller/SettingsController.d.ts +22 -7
- package/dist/core/controller/SettingsController.js +73 -8
- package/dist/core/controller/SharedContentController.d.ts +3 -3
- package/dist/core/controller/SharedContentController.js +38 -20
- package/dist/core/controller/VoiceController.js +6 -4
- package/dist/core/core.d.ts +15 -14
- package/dist/core/core.js +7 -7
- package/dist/fromRimori/EventBus.js +23 -23
- package/dist/fromRimori/PluginTypes.d.ts +4 -4
- package/dist/hooks/UseChatHook.d.ts +3 -3
- package/dist/hooks/UseChatHook.js +9 -3
- package/dist/index.d.ts +10 -10
- package/dist/index.js +9 -9
- package/dist/plugin/AccomplishmentHandler.d.ts +5 -5
- package/dist/plugin/AccomplishmentHandler.js +31 -27
- package/dist/plugin/AudioController.d.ts +1 -1
- package/dist/plugin/AudioController.js +6 -6
- package/dist/plugin/Logger.d.ts +5 -0
- package/dist/plugin/Logger.js +65 -13
- package/dist/plugin/PluginController.d.ts +7 -1
- package/dist/plugin/PluginController.js +32 -27
- package/dist/plugin/RimoriClient.d.ts +39 -14
- package/dist/plugin/RimoriClient.js +60 -31
- package/dist/plugin/StandaloneClient.d.ts +1 -1
- package/dist/plugin/StandaloneClient.js +35 -16
- package/dist/plugin/ThemeSetter.js +4 -4
- package/dist/providers/PluginProvider.js +44 -14
- package/dist/utils/Language.js +57 -57
- package/dist/utils/PluginUtils.js +3 -3
- package/dist/utils/difficultyConverter.d.ts +1 -1
- package/dist/utils/difficultyConverter.js +1 -1
- package/dist/utils/endpoint.js +2 -2
- package/dist/worker/WorkerSetup.d.ts +1 -1
- package/dist/worker/WorkerSetup.js +6 -6
- package/eslint.config.js +53 -0
- package/example/docs/devdocs.md +50 -40
- package/example/docs/overview.md +1 -1
- package/example/docs/userdocs.md +4 -1
- package/example/rimori.config.ts +51 -49
- package/example/worker/vite.config.ts +3 -3
- package/example/worker/worker.ts +2 -2
- package/package.json +17 -4
- package/prettier.config.js +8 -0
- package/src/cli/scripts/init/dev-registration.ts +5 -8
- package/src/cli/scripts/init/env-setup.ts +1 -1
- package/src/cli/scripts/init/file-operations.ts +1 -1
- package/src/cli/scripts/init/html-cleaner.ts +2 -5
- package/src/cli/scripts/init/main.ts +16 -13
- package/src/cli/scripts/init/package-setup.ts +11 -15
- package/src/cli/scripts/init/router-transformer.ts +40 -37
- package/src/cli/scripts/init/tailwind-config.ts +17 -26
- package/src/cli/scripts/init/vite-config.ts +3 -3
- package/src/cli/scripts/release/release-config-upload.ts +11 -11
- package/src/cli/scripts/release/release-db-update.ts +12 -12
- package/src/cli/scripts/release/release-file-upload.ts +3 -3
- package/src/cli/scripts/release/release.ts +4 -4
- package/src/cli/types/DatabaseTypes.ts +7 -8
- package/src/components/CRUDModal.tsx +64 -48
- package/src/components/MarkdownEditor.tsx +58 -27
- package/src/components/Spinner.tsx +24 -17
- package/src/components/ai/Assistant.tsx +70 -70
- package/src/components/ai/Avatar.tsx +20 -16
- package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +63 -54
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +14 -5
- package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +75 -74
- package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +177 -178
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +109 -94
- package/src/components/ai/utils.ts +4 -4
- package/src/components/audio/Playbutton.tsx +101 -93
- package/src/components/components/ContextMenu.tsx +47 -35
- package/src/components.ts +10 -10
- package/src/core/controller/AIController.ts +62 -50
- package/src/core/controller/ExerciseController.ts +98 -0
- package/src/core/controller/ObjectController.ts +15 -10
- package/src/core/controller/SettingsController.ts +89 -16
- package/src/core/controller/SharedContentController.ts +80 -44
- package/src/core/controller/VoiceController.ts +10 -8
- package/src/core/core.ts +15 -15
- package/src/fromRimori/EventBus.ts +76 -47
- package/src/fromRimori/PluginTypes.ts +26 -17
- package/src/fromRimori/readme.md +2 -2
- package/src/hooks/UseChatHook.ts +25 -15
- package/src/index.ts +10 -10
- package/src/plugin/AccomplishmentHandler.ts +53 -35
- package/src/plugin/AudioController.ts +18 -12
- package/src/plugin/Logger.ts +77 -19
- package/src/plugin/PluginController.ts +60 -44
- package/src/plugin/RimoriClient.ts +133 -69
- package/src/plugin/StandaloneClient.ts +51 -24
- package/src/plugin/ThemeSetter.ts +5 -5
- package/src/providers/PluginProvider.tsx +90 -36
- package/src/style.scss +3 -3
- package/src/utils/Language.ts +58 -58
- package/src/utils/PluginUtils.ts +16 -20
- package/src/utils/difficultyConverter.ts +2 -2
- package/src/utils/endpoint.ts +3 -2
- package/src/worker/WorkerSetup.ts +8 -9
- package/tsconfig.json +2 -4
- package/dist/components/LoggerExample.d.ts +0 -6
- package/dist/components/LoggerExample.js +0 -79
- package/dist/core/controller/AudioController.d.ts +0 -0
- package/dist/core/controller/AudioController.js +0 -1
- package/dist/hooks/UseLogger.d.ts +0 -30
- package/dist/hooks/UseLogger.js +0 -122
- package/dist/plugin/LoggerExample.d.ts +0 -16
- package/dist/plugin/LoggerExample.js +0 -140
- package/dist/utils/audioFormats.d.ts +0 -26
- package/dist/utils/audioFormats.js +0 -67
|
@@ -12,7 +12,7 @@ import { useRimori } from '../../../components';
|
|
|
12
12
|
import { FaMicrophone, FaSpinner } from 'react-icons/fa6';
|
|
13
13
|
import { AudioController } from '../../../plugin/AudioController';
|
|
14
14
|
import { useState, useRef, forwardRef, useImperativeHandle, useEffect } from 'react';
|
|
15
|
-
export const VoiceRecorder = forwardRef(({ onVoiceRecorded, iconSize, className, disabled, loading, onRecordingStatusChange, enablePushToTalk = false }, ref) => {
|
|
15
|
+
export const VoiceRecorder = forwardRef(({ onVoiceRecorded, iconSize, className, disabled, loading, onRecordingStatusChange, enablePushToTalk = false, }, ref) => {
|
|
16
16
|
const [isRecording, setIsRecording] = useState(false);
|
|
17
17
|
const [internalIsProcessing, setInternalIsProcessing] = useState(false);
|
|
18
18
|
const audioControllerRef = useRef(null);
|
|
@@ -91,6 +91,5 @@ export const VoiceRecorder = forwardRef(({ onVoiceRecorded, iconSize, className,
|
|
|
91
91
|
window.removeEventListener('keyup', handleKeyUp);
|
|
92
92
|
};
|
|
93
93
|
}, [enablePushToTalk]);
|
|
94
|
-
return (_jsx("button", { className:
|
|
95
|
-
_jsx(FaMicrophone, { size: iconSize, className: (isRecording ? "text-red-600" : "") }) }));
|
|
94
|
+
return (_jsx("button", { className: 'flex flex-row justify-center items-center rounded-full mx-auto disabled:opacity-50 ' + className, onClick: isRecording ? stopRecording : startRecording, disabled: disabled || loading || internalIsProcessing, children: loading || internalIsProcessing ? (_jsx(FaSpinner, { className: "animate-spin" })) : (_jsx(FaMicrophone, { size: iconSize, className: isRecording ? 'text-red-600' : '' })) }));
|
|
96
95
|
});
|
|
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { useState, useEffect } from 'react';
|
|
12
|
-
import { FaPlayCircle, FaStopCircle } from
|
|
13
|
-
import { useRimori } from
|
|
12
|
+
import { FaPlayCircle, FaStopCircle } from 'react-icons/fa';
|
|
13
|
+
import { useRimori } from '../../providers/PluginProvider';
|
|
14
14
|
import { Spinner } from '../Spinner';
|
|
15
15
|
import { EventBus } from '../../fromRimori/EventBus';
|
|
16
16
|
export const AudioPlayOptions = [0.8, 0.9, 1.0, 1.1, 1.2, 1.5];
|
|
@@ -35,7 +35,7 @@ export const AudioPlayer = ({ text, voice, language, hide, playListenerEvent, in
|
|
|
35
35
|
// Function to generate audio from text using API
|
|
36
36
|
const generateAudio = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
37
|
setIsLoading(true);
|
|
38
|
-
const blob = yield ai.getVoice(text, voice || (language ?
|
|
38
|
+
const blob = yield ai.getVoice(text, voice || (language ? 'aws_default' : 'openai_alloy'), 1, language);
|
|
39
39
|
setAudioUrl(URL.createObjectURL(blob));
|
|
40
40
|
setIsLoading(false);
|
|
41
41
|
});
|
|
@@ -45,13 +45,16 @@ export const AudioPlayer = ({ text, voice, language, hide, playListenerEvent, in
|
|
|
45
45
|
return;
|
|
46
46
|
const audio = new Audio(audioUrl);
|
|
47
47
|
audio.playbackRate = speed;
|
|
48
|
-
audio
|
|
48
|
+
audio
|
|
49
|
+
.play()
|
|
50
|
+
.then(() => {
|
|
49
51
|
audio.onended = () => {
|
|
50
52
|
setIsPlaying(false);
|
|
51
53
|
isFetchingAudio = false;
|
|
52
54
|
};
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
+
})
|
|
56
|
+
.catch((e) => {
|
|
57
|
+
console.warn('Error playing audio:', e);
|
|
55
58
|
setIsPlaying(false);
|
|
56
59
|
});
|
|
57
60
|
return () => {
|
|
@@ -73,5 +76,5 @@ export const AudioPlayer = ({ text, voice, language, hide, playListenerEvent, in
|
|
|
73
76
|
// console.log("playOnMount", playOnMount);
|
|
74
77
|
togglePlayback();
|
|
75
78
|
}, [playOnMount]);
|
|
76
|
-
return (_jsx("div", { className: "group relative", children: _jsxs("div", { className:
|
|
79
|
+
return (_jsx("div", { className: "group relative", children: _jsxs("div", { className: "flex flex-row items-end", children: [!hide && (_jsx("button", { className: "text-gray-500", onClick: togglePlayback, disabled: isLoading, children: isLoading ? _jsx(Spinner, {}) : isPlaying ? _jsx(FaStopCircle, { size: '25px' }) : _jsx(FaPlayCircle, { size: '25px' }) })), enableSpeedAdjustment && (_jsxs("div", { className: "ml-1 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-row text-sm text-gray-500", children: [_jsx("span", { className: "pr-1", children: "Speed: " }), _jsx("select", { value: speed, className: "appearance-none cursor-pointer pr-0 p-0 rounded shadow leading-tight focus:outline-none focus:bg-gray-800 focus:ring bg-transparent border-0", onChange: (e) => setSpeed(parseFloat(e.target.value)), disabled: isLoading, children: AudioPlayOptions.map((s) => (_jsx("option", { value: s, children: s }, s))) })] }))] }) }));
|
|
77
80
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect, useRef } from
|
|
3
|
-
import { EventBus } from
|
|
2
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
|
+
import { EventBus } from '../../fromRimori/EventBus';
|
|
4
4
|
const ContextMenu = ({ client }) => {
|
|
5
5
|
const [isOpen, setIsOpen] = useState(false);
|
|
6
6
|
const [actions, setActions] = useState([]);
|
|
@@ -24,16 +24,19 @@ const ContextMenu = ({ client }) => {
|
|
|
24
24
|
const range = selection.getRangeAt(0);
|
|
25
25
|
const rect = range.getBoundingClientRect();
|
|
26
26
|
// Center horizontally over the selected text, accounting for menu width
|
|
27
|
-
const centerX = rect.left +
|
|
27
|
+
const centerX = rect.left + rect.width / 2 - menuWidth / 2;
|
|
28
28
|
// Position 12px below where the text ends vertically
|
|
29
29
|
const textEndY = rect.bottom + 12;
|
|
30
30
|
return { x: centerX, y: textEndY, text: selectedText };
|
|
31
31
|
};
|
|
32
32
|
useEffect(() => {
|
|
33
|
-
const actions = client.plugin
|
|
33
|
+
const actions = client.plugin
|
|
34
|
+
.getPluginInfo()
|
|
35
|
+
.installedPlugins.flatMap((p) => p.context_menu_actions)
|
|
36
|
+
.filter(Boolean);
|
|
34
37
|
setActions(actions);
|
|
35
38
|
setOpenOnTextSelect(client.plugin.getUserInfo().context_menu_on_select);
|
|
36
|
-
EventBus.on(
|
|
39
|
+
EventBus.on('global.contextMenu.createActions', ({ data }) => {
|
|
37
40
|
setActions([...data.actions, ...actions]);
|
|
38
41
|
});
|
|
39
42
|
}, []);
|
|
@@ -67,8 +70,8 @@ const ContextMenu = ({ client }) => {
|
|
|
67
70
|
}
|
|
68
71
|
// Prevent context menu on textarea or text input selection
|
|
69
72
|
const target = e.target;
|
|
70
|
-
const isTextInput = target &&
|
|
71
|
-
(target.tagName === 'INPUT' && target.type === 'text'));
|
|
73
|
+
const isTextInput = target &&
|
|
74
|
+
(target.tagName === 'TEXTAREA' || (target.tagName === 'INPUT' && target.type === 'text'));
|
|
72
75
|
if (isTextInput) {
|
|
73
76
|
setIsOpen(false);
|
|
74
77
|
return;
|
|
@@ -105,15 +108,15 @@ const ContextMenu = ({ client }) => {
|
|
|
105
108
|
setPosition(calculateMobilePosition(selectedText, menuWidth));
|
|
106
109
|
}
|
|
107
110
|
};
|
|
108
|
-
document.addEventListener(
|
|
109
|
-
window.addEventListener(
|
|
110
|
-
document.addEventListener(
|
|
111
|
-
document.addEventListener(
|
|
111
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
112
|
+
window.addEventListener('mousemove', handleMouseMove);
|
|
113
|
+
document.addEventListener('contextmenu', handleMouseUp);
|
|
114
|
+
document.addEventListener('selectionchange', handleSelectionChange);
|
|
112
115
|
return () => {
|
|
113
|
-
document.removeEventListener(
|
|
114
|
-
window.removeEventListener(
|
|
115
|
-
document.removeEventListener(
|
|
116
|
-
document.removeEventListener(
|
|
116
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
117
|
+
window.removeEventListener('mousemove', handleMouseMove);
|
|
118
|
+
document.removeEventListener('contextmenu', handleMouseUp);
|
|
119
|
+
document.removeEventListener('selectionchange', handleSelectionChange);
|
|
117
120
|
};
|
|
118
121
|
}, [openOnTextSelect, isOpen, position.text]);
|
|
119
122
|
if (!isOpen) {
|
|
@@ -127,6 +130,6 @@ const ContextMenu = ({ client }) => {
|
|
|
127
130
|
} }, index))) }));
|
|
128
131
|
};
|
|
129
132
|
function MenuEntryItem(props) {
|
|
130
|
-
return _jsxs("button", { onClick: props.onClick, className: "px-4 py-2 text-left hover:bg-gray-500 dark:hover:bg-gray-600 w-full flex flex-row", children: [_jsx("span", { className: "flex-grow", children: props.icon }), _jsx("span", { className: "flex-grow", children: props.text })] });
|
|
133
|
+
return (_jsxs("button", { onClick: props.onClick, className: "px-4 py-2 text-left hover:bg-gray-500 dark:hover:bg-gray-600 w-full flex flex-row", children: [_jsx("span", { className: "flex-grow", children: props.icon }), _jsx("span", { className: "flex-grow", children: props.text })] }));
|
|
131
134
|
}
|
|
132
135
|
export default ContextMenu;
|
package/dist/components.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
1
|
+
export * from './components/ai/Assistant';
|
|
2
|
+
export * from './components/ai/Avatar';
|
|
3
|
+
export * from './components/ai/EmbeddedAssistent/VoiceRecoder';
|
|
4
|
+
export * from './components/audio/Playbutton';
|
|
5
|
+
export * from './components/CRUDModal';
|
|
6
|
+
export * from './components/MarkdownEditor';
|
|
7
|
+
export * from './components/Spinner';
|
|
8
|
+
export * from './hooks/UseChatHook';
|
|
9
|
+
export * from './plugin/ThemeSetter';
|
|
10
|
+
export * from './providers/PluginProvider';
|
package/dist/components.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// React components and hooks exports
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
11
|
-
export * from
|
|
2
|
+
export * from './components/ai/Assistant';
|
|
3
|
+
export * from './components/ai/Avatar';
|
|
4
|
+
export * from './components/ai/EmbeddedAssistent/VoiceRecoder';
|
|
5
|
+
export * from './components/audio/Playbutton';
|
|
6
|
+
export * from './components/CRUDModal';
|
|
7
|
+
export * from './components/MarkdownEditor';
|
|
8
|
+
export * from './components/Spinner';
|
|
9
|
+
export * from './hooks/UseChatHook';
|
|
10
|
+
export * from './plugin/ThemeSetter';
|
|
11
|
+
export * from './providers/PluginProvider';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Tool } from
|
|
1
|
+
import { Tool } from '../../fromRimori/PluginTypes';
|
|
2
2
|
export interface ToolInvocation {
|
|
3
3
|
toolCallId: string;
|
|
4
4
|
toolName: string;
|
|
@@ -6,7 +6,7 @@ export interface ToolInvocation {
|
|
|
6
6
|
}
|
|
7
7
|
export interface Message {
|
|
8
8
|
id?: string;
|
|
9
|
-
role:
|
|
9
|
+
role: 'user' | 'assistant' | 'system';
|
|
10
10
|
content: string;
|
|
11
11
|
toolCalls?: ToolInvocation[];
|
|
12
12
|
}
|
|
@@ -23,7 +23,7 @@ export function generateText(backendUrl, messages, tools, token) {
|
|
|
23
23
|
const response = yield fetch(`${backendUrl}/ai/llm`, {
|
|
24
24
|
method: 'POST',
|
|
25
25
|
body: JSON.stringify({ messages, tools }),
|
|
26
|
-
headers: {
|
|
26
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
27
27
|
});
|
|
28
28
|
return yield response.json();
|
|
29
29
|
});
|
|
@@ -36,7 +36,7 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
36
36
|
messageId,
|
|
37
37
|
messageCount: messages.length,
|
|
38
38
|
toolCount: tools.length,
|
|
39
|
-
backendUrl
|
|
39
|
+
backendUrl,
|
|
40
40
|
});
|
|
41
41
|
while (true) {
|
|
42
42
|
const messagesForApi = currentMessages.map((_a) => {
|
|
@@ -47,7 +47,7 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
47
47
|
const response = yield fetch(`${backendUrl}/ai/llm`, {
|
|
48
48
|
method: 'POST',
|
|
49
49
|
body: JSON.stringify({ messages: messagesForApi, tools, stream: true }),
|
|
50
|
-
headers: {
|
|
50
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
51
51
|
});
|
|
52
52
|
if (!response.ok) {
|
|
53
53
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
@@ -58,12 +58,12 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
58
58
|
}
|
|
59
59
|
const reader = response.body.getReader();
|
|
60
60
|
const decoder = new TextDecoder('utf-8');
|
|
61
|
-
let content =
|
|
61
|
+
let content = '';
|
|
62
62
|
let done = false;
|
|
63
63
|
let toolInvocations = [];
|
|
64
|
-
let currentTextId =
|
|
64
|
+
let currentTextId = '';
|
|
65
65
|
let isToolCallMode = false;
|
|
66
|
-
let buffer =
|
|
66
|
+
let buffer = ''; // Buffer for incomplete chunks
|
|
67
67
|
while (!done) {
|
|
68
68
|
const { value, done: readerDone } = yield reader.read();
|
|
69
69
|
if (value) {
|
|
@@ -73,7 +73,7 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
73
73
|
const lines = buffer.split('\n');
|
|
74
74
|
// Keep the last line in buffer if it's incomplete
|
|
75
75
|
if (lines.length > 1) {
|
|
76
|
-
buffer = lines.pop() ||
|
|
76
|
+
buffer = lines.pop() || '';
|
|
77
77
|
}
|
|
78
78
|
for (const line of lines) {
|
|
79
79
|
if (line.trim() === '')
|
|
@@ -90,16 +90,16 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
90
90
|
const data = JSON.parse(dataStr);
|
|
91
91
|
// Log the first message to understand the format
|
|
92
92
|
if (!content && !isToolCallMode) {
|
|
93
|
-
console.log('First stream message received:', data);
|
|
93
|
+
// console.log('First stream message received:', data);
|
|
94
94
|
}
|
|
95
95
|
switch (data.type) {
|
|
96
96
|
case 'start':
|
|
97
97
|
// Stream started, no action needed
|
|
98
|
-
console.log('Stream started');
|
|
98
|
+
// console.log('Stream started');
|
|
99
99
|
break;
|
|
100
100
|
case 'start-step':
|
|
101
101
|
// Step started, no action needed
|
|
102
|
-
console.log('Step started');
|
|
102
|
+
// console.log('Step started');
|
|
103
103
|
break;
|
|
104
104
|
case 'reasoning-start':
|
|
105
105
|
// Reasoning started, no action needed
|
|
@@ -127,26 +127,28 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
127
127
|
break;
|
|
128
128
|
case 'finish-step':
|
|
129
129
|
// Step finished, no action needed
|
|
130
|
-
console.log('Step finished');
|
|
130
|
+
// console.log('Step finished');
|
|
131
131
|
break;
|
|
132
132
|
case 'finish':
|
|
133
133
|
// Stream finished
|
|
134
|
-
console.log('Stream finished');
|
|
134
|
+
// console.log('Stream finished');
|
|
135
135
|
done = true;
|
|
136
136
|
break;
|
|
137
137
|
// Additional message types that might be present in the AI library
|
|
138
138
|
case 'tool-call':
|
|
139
|
+
case 'tool-input-available': //for now input calls should be handled the same way as tool calls
|
|
139
140
|
// Tool call initiated
|
|
140
141
|
console.log('Tool call initiated:', data);
|
|
141
142
|
isToolCallMode = true;
|
|
142
|
-
if (data.toolCallId && data.toolName && data.args) {
|
|
143
|
+
if (data.toolCallId && data.toolName && (data.args || data.input)) {
|
|
143
144
|
toolInvocations.push({
|
|
144
145
|
toolCallId: data.toolCallId,
|
|
145
146
|
toolName: data.toolName,
|
|
146
|
-
args: data.args
|
|
147
|
+
args: data.args || data.input,
|
|
147
148
|
});
|
|
148
149
|
}
|
|
149
150
|
break;
|
|
151
|
+
case 'tool-input-delta': //for now input calls should be handled the same way as tool calls
|
|
150
152
|
case 'tool-call-delta':
|
|
151
153
|
// Tool call delta (for streaming tool calls)
|
|
152
154
|
console.log('Tool call delta:', data);
|
|
@@ -196,7 +198,7 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
196
198
|
if (content || toolInvocations.length > 0) {
|
|
197
199
|
currentMessages.push({
|
|
198
200
|
id: messageId,
|
|
199
|
-
role:
|
|
201
|
+
role: 'assistant',
|
|
200
202
|
content: content,
|
|
201
203
|
toolCalls: toolInvocations.length > 0 ? toolInvocations : undefined,
|
|
202
204
|
});
|
|
@@ -206,13 +208,13 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
206
208
|
console.log('Tool calls detected, executing tools...');
|
|
207
209
|
const toolResults = [];
|
|
208
210
|
for (const toolInvocation of toolInvocations) {
|
|
209
|
-
const tool = tools.find(t => t.name === toolInvocation.toolName);
|
|
211
|
+
const tool = tools.find((t) => t.name === toolInvocation.toolName);
|
|
210
212
|
if (tool && tool.execute) {
|
|
211
213
|
try {
|
|
212
214
|
const result = yield tool.execute(toolInvocation.args);
|
|
213
215
|
toolResults.push({
|
|
214
216
|
id: Math.random().toString(36).substring(3),
|
|
215
|
-
role:
|
|
217
|
+
role: 'user',
|
|
216
218
|
content: `Tool '${toolInvocation.toolName}' returned: ${JSON.stringify(result)}`,
|
|
217
219
|
});
|
|
218
220
|
}
|
|
@@ -220,7 +222,7 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
220
222
|
console.error(`Error executing tool ${toolInvocation.toolName}:`, error);
|
|
221
223
|
toolResults.push({
|
|
222
224
|
id: Math.random().toString(36).substring(3),
|
|
223
|
-
role:
|
|
225
|
+
role: 'user',
|
|
224
226
|
content: `Tool '${toolInvocation.toolName}' failed with error: ${error}`,
|
|
225
227
|
});
|
|
226
228
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import { PluginController } from '../../plugin/PluginController';
|
|
3
|
+
export type TriggerAction = {
|
|
4
|
+
action_key: string;
|
|
5
|
+
} & Record<string, string | number | boolean>;
|
|
6
|
+
export interface CreateExerciseParams {
|
|
7
|
+
plugin_id: string;
|
|
8
|
+
start_date: string;
|
|
9
|
+
end_date: string;
|
|
10
|
+
trigger_action: TriggerAction;
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
estimated_duration: number;
|
|
14
|
+
}
|
|
15
|
+
export interface Exercise {
|
|
16
|
+
id: string;
|
|
17
|
+
plugin_id: string;
|
|
18
|
+
start_date: string;
|
|
19
|
+
end_date: string;
|
|
20
|
+
trigger_action: TriggerAction;
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
estimated_duration: number;
|
|
24
|
+
created_at?: string;
|
|
25
|
+
updated_at?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare class ExerciseController {
|
|
28
|
+
private supabase;
|
|
29
|
+
private pluginController;
|
|
30
|
+
constructor(supabase: SupabaseClient, pluginController: PluginController);
|
|
31
|
+
/**
|
|
32
|
+
* Fetches weekly exercises from the weekly_exercises view.
|
|
33
|
+
* Shows exercises for the current week that haven't expired.
|
|
34
|
+
* @returns Array of exercise objects.
|
|
35
|
+
*/
|
|
36
|
+
viewWeeklyExercises(): Promise<Exercise[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new exercise via the backend API.
|
|
39
|
+
* @param params Exercise creation parameters.
|
|
40
|
+
* @returns Created exercise object.
|
|
41
|
+
*/
|
|
42
|
+
addExercise(params: CreateExerciseParams): Promise<Exercise>;
|
|
43
|
+
/**
|
|
44
|
+
* Deletes an exercise via the backend API.
|
|
45
|
+
* @param id The exercise ID to delete.
|
|
46
|
+
* @returns Success status.
|
|
47
|
+
*/
|
|
48
|
+
deleteExercise(id: string): Promise<{
|
|
49
|
+
success: boolean;
|
|
50
|
+
message: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export class ExerciseController {
|
|
11
|
+
constructor(supabase, pluginController) {
|
|
12
|
+
this.supabase = supabase;
|
|
13
|
+
this.pluginController = pluginController;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Fetches weekly exercises from the weekly_exercises view.
|
|
17
|
+
* Shows exercises for the current week that haven't expired.
|
|
18
|
+
* @returns Array of exercise objects.
|
|
19
|
+
*/
|
|
20
|
+
viewWeeklyExercises() {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
const { data, error } = yield this.supabase.from('weekly_exercises').select('*');
|
|
23
|
+
if (error) {
|
|
24
|
+
throw new Error(`Failed to fetch weekly exercises: ${error.message}`);
|
|
25
|
+
}
|
|
26
|
+
return data || [];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Creates a new exercise via the backend API.
|
|
31
|
+
* @param params Exercise creation parameters.
|
|
32
|
+
* @returns Created exercise object.
|
|
33
|
+
*/
|
|
34
|
+
addExercise(params) {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const token = yield this.pluginController.getToken();
|
|
37
|
+
const response = yield fetch(`${this.pluginController.getBackendUrl()}/exercises`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
Authorization: `Bearer ${token}`,
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify(params),
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
const errorText = yield response.text();
|
|
47
|
+
throw new Error(`Failed to create exercise: ${errorText}`);
|
|
48
|
+
}
|
|
49
|
+
return yield response.json();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Deletes an exercise via the backend API.
|
|
54
|
+
* @param id The exercise ID to delete.
|
|
55
|
+
* @returns Success status.
|
|
56
|
+
*/
|
|
57
|
+
deleteExercise(id) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
const token = yield this.pluginController.getToken();
|
|
60
|
+
const response = yield fetch(`${this.pluginController.getBackendUrl()}/exercises/${id}`, {
|
|
61
|
+
method: 'DELETE',
|
|
62
|
+
headers: {
|
|
63
|
+
Authorization: `Bearer ${token}`,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const errorText = yield response.text();
|
|
68
|
+
throw new Error(`Failed to delete exercise: ${errorText}`);
|
|
69
|
+
}
|
|
70
|
+
return yield response.json();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -17,8 +17,8 @@ export function generateObject(backendUrl, request, token) {
|
|
|
17
17
|
behaviour: request.behaviour,
|
|
18
18
|
instructions: request.instructions,
|
|
19
19
|
}),
|
|
20
|
-
headers: {
|
|
21
|
-
}).then(response => response.json());
|
|
20
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
21
|
+
}).then((response) => response.json());
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
export function streamObject(backendUrl, request, onResponse, token) {
|
|
@@ -32,7 +32,7 @@ export function streamObject(backendUrl, request, onResponse, token) {
|
|
|
32
32
|
systemInstructions: request.behaviour,
|
|
33
33
|
secondaryInstructions: request.instructions,
|
|
34
34
|
}),
|
|
35
|
-
headers: {
|
|
35
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
36
36
|
});
|
|
37
37
|
if (!response.body) {
|
|
38
38
|
console.error('No response body.');
|
|
@@ -40,14 +40,14 @@ export function streamObject(backendUrl, request, onResponse, token) {
|
|
|
40
40
|
}
|
|
41
41
|
const reader = response.body.getReader();
|
|
42
42
|
const decoder = new TextDecoder('utf-8');
|
|
43
|
-
let content =
|
|
43
|
+
let content = '';
|
|
44
44
|
let done = false;
|
|
45
45
|
let toolInvocations = [];
|
|
46
46
|
while (!done) {
|
|
47
47
|
const { value } = yield reader.read();
|
|
48
48
|
if (value) {
|
|
49
49
|
const chunk = decoder.decode(value, { stream: true });
|
|
50
|
-
const lines = chunk.split('\n').filter(line => line.trim() !== '');
|
|
50
|
+
const lines = chunk.split('\n').filter((line) => line.trim() !== '');
|
|
51
51
|
for (const line of lines) {
|
|
52
52
|
const data = line.substring(3, line.length - 1);
|
|
53
53
|
const command = line.substring(0, 1);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { SupabaseClient } from
|
|
2
|
-
import { LanguageLevel } from
|
|
3
|
-
import { Language } from
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import { LanguageLevel } from '../../utils/difficultyConverter';
|
|
3
|
+
import { Language } from '../../utils/Language';
|
|
4
|
+
import { Guild } from '../core';
|
|
4
5
|
export interface Buddy {
|
|
5
6
|
id: string;
|
|
6
7
|
name: string;
|
|
@@ -35,19 +36,33 @@ export interface UserInfo {
|
|
|
35
36
|
context_menu_on_select: boolean;
|
|
36
37
|
user_name?: string;
|
|
37
38
|
/**
|
|
38
|
-
* ISO 3166-1 alpha-2 country code of user's location (exposed to plugins)
|
|
39
|
+
* ISO 3166-1 alpha-2 country code of user's target location (exposed to plugins)
|
|
39
40
|
*/
|
|
40
|
-
|
|
41
|
+
target_country: string;
|
|
41
42
|
/**
|
|
42
43
|
* Optional: nearest big city (>100,000) near user's location
|
|
43
44
|
*/
|
|
44
|
-
|
|
45
|
+
target_city?: string;
|
|
45
46
|
}
|
|
46
47
|
export declare class SettingsController {
|
|
47
48
|
private pluginId;
|
|
48
49
|
private supabase;
|
|
49
|
-
|
|
50
|
+
private guild;
|
|
51
|
+
constructor(supabase: SupabaseClient, pluginId: string, guild: Guild);
|
|
52
|
+
/**
|
|
53
|
+
* Fetches settings based on guild configuration.
|
|
54
|
+
* If guild doesn't allow user settings, fetches guild-level settings.
|
|
55
|
+
* Otherwise, fetches user-specific settings.
|
|
56
|
+
* @returns The settings object or null if not found.
|
|
57
|
+
*/
|
|
50
58
|
private fetchSettings;
|
|
59
|
+
/**
|
|
60
|
+
* Sets settings for the plugin.
|
|
61
|
+
* Automatically saves as guild settings if guild doesn't allow user settings,
|
|
62
|
+
* otherwise saves as user-specific settings.
|
|
63
|
+
* @param settings - The settings object to save.
|
|
64
|
+
* @throws {Error} if RLS blocks the operation.
|
|
65
|
+
*/
|
|
51
66
|
setSettings(settings: any): Promise<void>;
|
|
52
67
|
/**
|
|
53
68
|
* Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
|