@rimori/client 1.1.9 → 1.2.0
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/README.md +128 -45
- package/dist/cli/scripts/init/dev-registration.d.ts +35 -0
- package/dist/cli/scripts/init/dev-registration.js +175 -0
- package/dist/cli/scripts/init/env-setup.d.ts +9 -0
- package/dist/cli/scripts/init/env-setup.js +43 -0
- package/dist/cli/scripts/init/file-operations.d.ts +4 -0
- package/dist/cli/scripts/init/file-operations.js +51 -0
- package/dist/cli/scripts/init/html-cleaner.d.ts +4 -0
- package/dist/cli/scripts/init/html-cleaner.js +38 -0
- package/dist/cli/scripts/init/main.d.ts +2 -0
- package/dist/cli/scripts/init/main.js +159 -0
- package/dist/cli/scripts/init/package-setup.d.ts +32 -0
- package/dist/cli/scripts/init/package-setup.js +75 -0
- package/dist/cli/scripts/init/router-transformer.d.ts +6 -0
- package/dist/cli/scripts/init/router-transformer.js +254 -0
- package/dist/cli/scripts/init/tailwind-config.d.ts +4 -0
- package/dist/cli/scripts/init/tailwind-config.js +56 -0
- package/dist/cli/scripts/init/vite-config.d.ts +20 -0
- package/dist/cli/scripts/init/vite-config.js +54 -0
- package/dist/cli/scripts/release/release-config-upload.d.ts +7 -0
- package/dist/cli/scripts/release/release-config-upload.js +116 -0
- package/dist/cli/scripts/release/release-db-update.d.ts +6 -0
- package/dist/cli/scripts/release/release-db-update.js +100 -0
- package/dist/cli/scripts/release/release-file-upload.d.ts +6 -0
- package/dist/cli/scripts/release/release-file-upload.js +136 -0
- package/dist/cli/scripts/release/release.d.ts +23 -0
- package/dist/cli/scripts/release/release.js +70 -0
- package/dist/cli/types/DatabaseTypes.d.ts +103 -0
- package/dist/cli/types/DatabaseTypes.js +2 -0
- package/dist/components/ai/Assistant.js +4 -4
- package/dist/components/ai/Avatar.d.ts +3 -2
- package/dist/components/ai/Avatar.js +10 -5
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -1
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +1 -0
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +12 -6
- package/dist/components/ai/utils.js +0 -1
- package/dist/components/audio/Playbutton.js +3 -3
- package/dist/{core → components}/components/ContextMenu.js +2 -2
- package/dist/components.d.ts +5 -5
- package/dist/components.js +5 -5
- package/dist/core/controller/AIController.d.ts +15 -0
- package/dist/core/controller/AIController.js +120 -0
- package/dist/{controller → core/controller}/ObjectController.d.ts +8 -0
- package/dist/{controller → core/controller}/SettingsController.d.ts +12 -4
- package/dist/{controller → core/controller}/SettingsController.js +0 -25
- package/dist/{controller → core/controller}/SharedContentController.d.ts +10 -19
- package/dist/{controller → core/controller}/SharedContentController.js +11 -11
- package/dist/core/core.d.ts +13 -0
- package/dist/core/core.js +8 -0
- package/dist/{plugin/fromRimori → fromRimori}/EventBus.d.ts +3 -3
- package/dist/{plugin/fromRimori → fromRimori}/EventBus.js +25 -8
- package/dist/fromRimori/PluginTypes.d.ts +171 -0
- package/dist/hooks/UseChatHook.d.ts +2 -1
- package/dist/hooks/UseChatHook.js +3 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -3
- package/dist/plugin/AccomplishmentHandler.d.ts +1 -1
- package/dist/plugin/AccomplishmentHandler.js +1 -1
- package/dist/plugin/PluginController.d.ts +16 -3
- package/dist/plugin/PluginController.js +24 -18
- package/dist/plugin/RimoriClient.d.ts +22 -17
- package/dist/plugin/RimoriClient.js +35 -25
- package/dist/plugin/StandaloneClient.js +11 -8
- package/dist/plugin/ThemeSetter.d.ts +1 -0
- package/dist/plugin/ThemeSetter.js +9 -6
- package/dist/providers/PluginProvider.d.ts +3 -0
- package/dist/providers/PluginProvider.js +4 -4
- package/dist/utils/Language.d.ts +2 -1
- package/dist/utils/Language.js +4 -2
- package/dist/utils/difficultyConverter.js +1 -1
- package/dist/utils/endpoint.d.ts +2 -0
- package/dist/utils/endpoint.js +2 -0
- package/dist/worker/WorkerSetup.js +3 -1
- package/example/docs/devdocs.md +231 -0
- package/example/docs/overview.md +29 -0
- package/example/docs/userdocs.md +123 -0
- package/example/rimori.config.ts +89 -0
- package/example/worker/vite.config.ts +23 -0
- package/example/worker/worker.ts +11 -0
- package/package.json +15 -9
- package/src/cli/scripts/init/dev-registration.ts +193 -0
- package/src/cli/scripts/init/env-setup.ts +44 -0
- package/src/cli/scripts/init/file-operations.ts +58 -0
- package/src/cli/scripts/init/html-cleaner.ts +48 -0
- package/src/cli/scripts/init/main.ts +171 -0
- package/src/cli/scripts/init/package-setup.ts +117 -0
- package/src/cli/scripts/init/router-transformer.ts +329 -0
- package/src/cli/scripts/init/tailwind-config.ts +75 -0
- package/src/cli/scripts/init/vite-config.ts +73 -0
- package/src/cli/scripts/release/release-config-upload.ts +114 -0
- package/src/cli/scripts/release/release-db-update.ts +97 -0
- package/src/cli/scripts/release/release-file-upload.ts +138 -0
- package/src/cli/scripts/release/release.ts +69 -0
- package/src/cli/types/DatabaseTypes.ts +117 -0
- package/src/components/ai/Assistant.tsx +4 -4
- package/src/components/ai/Avatar.tsx +24 -7
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +1 -1
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +16 -8
- package/src/components/ai/utils.ts +0 -2
- package/src/components/audio/Playbutton.tsx +3 -3
- package/src/{core → components}/components/ContextMenu.tsx +3 -3
- package/src/components.ts +6 -6
- package/src/core/controller/AIController.ts +122 -0
- package/src/core/controller/ObjectController.ts +115 -0
- package/src/{controller → core/controller}/SettingsController.ts +13 -29
- package/src/{controller → core/controller}/SharedContentController.ts +18 -28
- package/src/core/core.ts +15 -0
- package/src/{plugin/fromRimori → fromRimori}/EventBus.ts +28 -10
- package/src/fromRimori/PluginTypes.ts +203 -0
- package/src/hooks/UseChatHook.ts +5 -4
- package/src/index.ts +5 -3
- package/src/plugin/AccomplishmentHandler.ts +1 -1
- package/src/plugin/PluginController.ts +35 -23
- package/src/plugin/RimoriClient.ts +48 -41
- package/src/plugin/StandaloneClient.ts +11 -8
- package/src/plugin/ThemeSetter.ts +12 -8
- package/src/providers/PluginProvider.tsx +7 -4
- package/src/utils/Language.ts +4 -2
- package/src/utils/difficultyConverter.ts +3 -3
- package/src/utils/endpoint.ts +2 -0
- package/src/worker/WorkerSetup.ts +4 -2
- package/dist/components/PluginController.d.ts +0 -21
- package/dist/components/PluginController.js +0 -116
- package/dist/controller/AIController.d.ts +0 -23
- package/dist/controller/AIController.js +0 -93
- package/dist/controller/SidePluginController.d.ts +0 -3
- package/dist/controller/SidePluginController.js +0 -31
- package/dist/core.d.ts +0 -7
- package/dist/core.js +0 -7
- package/dist/plugin/ContextMenu.d.ts +0 -17
- package/dist/plugin/ContextMenu.js +0 -45
- package/dist/plugin/fromRimori/PluginTypes.d.ts +0 -48
- package/dist/plugin/fromRimori/SupabaseHandler.d.ts +0 -13
- package/dist/plugin/fromRimori/SupabaseHandler.js +0 -55
- package/dist/providers/PluginController.d.ts +0 -21
- package/dist/providers/PluginController.js +0 -116
- package/dist/types/Actions.d.ts +0 -4
- package/dist/types/Actions.js +0 -1
- package/src/controller/AIController.ts +0 -112
- package/src/controller/ObjectController.ts +0 -107
- package/src/controller/SidePluginController.ts +0 -25
- package/src/core.ts +0 -8
- package/src/plugin/fromRimori/PluginTypes.ts +0 -64
- package/src/types/Actions.ts +0 -6
- /package/dist/{core → components}/components/ContextMenu.d.ts +0 -0
- /package/dist/{controller → core/controller}/ObjectController.js +0 -0
- /package/dist/{controller → core/controller}/VoiceController.d.ts +0 -0
- /package/dist/{controller → core/controller}/VoiceController.js +0 -0
- /package/dist/{plugin/fromRimori → fromRimori}/PluginTypes.js +0 -0
- /package/src/{controller → core/controller}/VoiceController.ts +0 -0
- /package/src/{plugin/fromRimori → fromRimori}/readme.md +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Tool } from "../../fromRimori/PluginTypes";
|
|
2
|
+
|
|
3
|
+
export interface ToolInvocation {
|
|
4
|
+
toolCallId: string;
|
|
5
|
+
toolName: string;
|
|
6
|
+
args: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Message {
|
|
10
|
+
id?: string;
|
|
11
|
+
role: "user" | "assistant" | "system"
|
|
12
|
+
content: string;
|
|
13
|
+
toolCalls?: ToolInvocation[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function generateText(backendUrl: string, messages: Message[], tools: Tool[], token: string) {
|
|
17
|
+
const response = await fetch(`${backendUrl}/ai/llm`, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
body: JSON.stringify({ messages, tools }),
|
|
20
|
+
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return await response.json();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type OnLLMResponse = (id: string, response: string, finished: boolean, toolInvocations?: ToolInvocation[]) => void;
|
|
27
|
+
|
|
28
|
+
export async function streamChatGPT(backendUrl: string, messages: Message[], tools: Tool[], onResponse: OnLLMResponse, token: string) {
|
|
29
|
+
const messageId = Math.random().toString(36).substring(3);
|
|
30
|
+
let currentMessages: Message[] = [...messages];
|
|
31
|
+
|
|
32
|
+
while (true) {
|
|
33
|
+
const messagesForApi = currentMessages.map(({ id, ...rest }) => rest);
|
|
34
|
+
|
|
35
|
+
const response = await fetch(`${backendUrl}/ai/llm`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
body: JSON.stringify({ messages: messagesForApi, tools, stream: true }),
|
|
38
|
+
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!response.body) {
|
|
42
|
+
console.error('No response body.');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const reader = response.body.getReader();
|
|
47
|
+
const decoder = new TextDecoder('utf-8');
|
|
48
|
+
|
|
49
|
+
let content = "";
|
|
50
|
+
let done = false;
|
|
51
|
+
let toolInvocations: { toolCallId: string, toolName: string, args: any }[] = [];
|
|
52
|
+
let finishReason = "";
|
|
53
|
+
|
|
54
|
+
while (!done) {
|
|
55
|
+
const { value, done: readerDone } = await reader.read();
|
|
56
|
+
|
|
57
|
+
if (value) {
|
|
58
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
59
|
+
const lines = chunk.split('\n').filter(line => line.trim() !== '');
|
|
60
|
+
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
const command = line.substring(0, 1);
|
|
63
|
+
|
|
64
|
+
if (command === '0') {
|
|
65
|
+
const data = line.substring(3, line.length - 1);
|
|
66
|
+
content += data;
|
|
67
|
+
onResponse(messageId, content.replace(/\\n/g, '\n').replace(/\\+"/g, '"'), false);
|
|
68
|
+
} else if (command === 'd' || command === 'e') {
|
|
69
|
+
const eventData = JSON.parse(line.substring(2));
|
|
70
|
+
finishReason = eventData.finishReason;
|
|
71
|
+
done = true;
|
|
72
|
+
break;
|
|
73
|
+
} else if (command === '9') {
|
|
74
|
+
const toolInvocation = JSON.parse(line.substring(2));
|
|
75
|
+
toolInvocations.push(toolInvocation);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (readerDone) {
|
|
81
|
+
done = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (content || toolInvocations.length > 0) {
|
|
86
|
+
currentMessages.push({
|
|
87
|
+
id: messageId,
|
|
88
|
+
role: "assistant",
|
|
89
|
+
content: content,
|
|
90
|
+
toolCalls: toolInvocations.length > 0 ? toolInvocations: undefined,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (finishReason !== 'tool-calls') {
|
|
95
|
+
onResponse(messageId, content.replace(/\\n/g, '\n'), true, toolInvocations);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const toolResults: Message[] = [];
|
|
100
|
+
for (const toolInvocation of toolInvocations) {
|
|
101
|
+
const tool = tools.find(t => t.name === toolInvocation.toolName);
|
|
102
|
+
if (tool && tool.execute) {
|
|
103
|
+
try {
|
|
104
|
+
const result = await tool.execute(toolInvocation.args);
|
|
105
|
+
toolResults.push({
|
|
106
|
+
id: Math.random().toString(36).substring(3),
|
|
107
|
+
role: "user",
|
|
108
|
+
content: `Tool '${toolInvocation.toolName}' returned: ${JSON.stringify(result)}`,
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(`Error executing tool ${toolInvocation.toolName}:`, error);
|
|
112
|
+
toolResults.push({
|
|
113
|
+
id: Math.random().toString(36).substring(3),
|
|
114
|
+
role: "user",
|
|
115
|
+
content: `Tool '${toolInvocation.toolName}' failed with error: ${error}`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
currentMessages.push(...toolResults);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
type PrimitiveType = 'string' | 'number' | 'boolean';
|
|
2
|
+
|
|
3
|
+
// This is the type that can appear in the `type` property
|
|
4
|
+
type ObjectToolParameterType =
|
|
5
|
+
| PrimitiveType
|
|
6
|
+
| { [key: string]: ObjectToolParameter } // for nested objects
|
|
7
|
+
| [{ [key: string]: ObjectToolParameter }]; // for arrays of objects (notice the tuple type)
|
|
8
|
+
|
|
9
|
+
interface ObjectToolParameter {
|
|
10
|
+
type: ObjectToolParameterType;
|
|
11
|
+
description?: string;
|
|
12
|
+
enum?: string[];
|
|
13
|
+
optional?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The tools that the AI can use.
|
|
18
|
+
*
|
|
19
|
+
* The key is the name of the tool.
|
|
20
|
+
* The value is the parameter of the tool.
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
export type ObjectTool = {
|
|
24
|
+
[key: string]: ObjectToolParameter;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export interface ObjectRequest {
|
|
28
|
+
/**
|
|
29
|
+
* The tools that the AI can use.
|
|
30
|
+
*/
|
|
31
|
+
tool: ObjectTool;
|
|
32
|
+
/**
|
|
33
|
+
* High level instructions for the AI to follow. Behaviour, tone, restrictions, etc.
|
|
34
|
+
* Example: "Act like a recipe writer."
|
|
35
|
+
*/
|
|
36
|
+
behaviour?: string;
|
|
37
|
+
/**
|
|
38
|
+
* The specific instruction for the AI to follow.
|
|
39
|
+
* Example: "Generate a recipe using chicken, rice and vegetables."
|
|
40
|
+
*/
|
|
41
|
+
instructions: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function generateObject(supabaseUrl: string, request: ObjectRequest, token: string) {
|
|
45
|
+
return await fetch(`${supabaseUrl}/functions/v1/llm-object`, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
stream: false,
|
|
49
|
+
tool: request.tool,
|
|
50
|
+
behaviour: request.behaviour,
|
|
51
|
+
instructions: request.instructions,
|
|
52
|
+
}),
|
|
53
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
54
|
+
}).then(response => response.json());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// TODO adjust stream to work with object
|
|
58
|
+
export type OnLLMResponse = (id: string, response: string, finished: boolean, toolInvocations?: any[]) => void;
|
|
59
|
+
|
|
60
|
+
export async function streamObject(supabaseUrl: string, request: ObjectRequest, onResponse: OnLLMResponse, token: string) {
|
|
61
|
+
const messageId = Math.random().toString(36).substring(3);
|
|
62
|
+
const response = await fetch(`${supabaseUrl}/functions/v1/llm-object`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
stream: true,
|
|
66
|
+
tools: request.tool,
|
|
67
|
+
systemInstructions: request.behaviour,
|
|
68
|
+
secondaryInstructions: request.instructions,
|
|
69
|
+
}),
|
|
70
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!response.body) {
|
|
74
|
+
console.error('No response body.');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const reader = response.body.getReader();
|
|
79
|
+
const decoder = new TextDecoder('utf-8');
|
|
80
|
+
|
|
81
|
+
let content = "";
|
|
82
|
+
let done = false;
|
|
83
|
+
let toolInvocations: any[] = [];
|
|
84
|
+
while (!done) {
|
|
85
|
+
const { value } = await reader.read();
|
|
86
|
+
|
|
87
|
+
if (value) {
|
|
88
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
89
|
+
const lines = chunk.split('\n').filter(line => line.trim() !== '');
|
|
90
|
+
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
const data = line.substring(3, line.length - 1);
|
|
93
|
+
const command = line.substring(0, 1);
|
|
94
|
+
// console.log("data: ", { line, data, command });
|
|
95
|
+
|
|
96
|
+
if (command === '0') {
|
|
97
|
+
content += data;
|
|
98
|
+
// console.log("AI response:", content);
|
|
99
|
+
|
|
100
|
+
//content \n\n should be real line break when message is displayed
|
|
101
|
+
onResponse(messageId, content.replace(/\\n/g, '\n'), false);
|
|
102
|
+
} else if (command === 'd') {
|
|
103
|
+
// console.log("AI usage:", JSON.parse(line.substring(2)));
|
|
104
|
+
done = true;
|
|
105
|
+
break;
|
|
106
|
+
} else if (command === '9') {
|
|
107
|
+
// console.log("tool call:", JSON.parse(line.substring(2)));
|
|
108
|
+
// console.log("tools", tools);
|
|
109
|
+
toolInvocations.push(JSON.parse(line.substring(2)));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
onResponse(messageId, content.replace(/\\n/g, '\n'), true, toolInvocations);
|
|
115
|
+
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
-
import { LanguageLevel } from "
|
|
3
|
-
import { Language } from "
|
|
2
|
+
import { LanguageLevel } from "../../utils/difficultyConverter";
|
|
3
|
+
import { Language } from "../../utils/Language";
|
|
4
|
+
|
|
5
|
+
export interface Buddy {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
avatarUrl: string;
|
|
10
|
+
voiceId: string;
|
|
11
|
+
aiPersonality: string;
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
export interface UserInfo {
|
|
6
15
|
skill_level_reading: LanguageLevel;
|
|
@@ -11,13 +20,14 @@ export interface UserInfo {
|
|
|
11
20
|
skill_level_understanding: LanguageLevel;
|
|
12
21
|
goal_longterm: string;
|
|
13
22
|
goal_weekly: string;
|
|
14
|
-
study_buddy:
|
|
23
|
+
study_buddy: Buddy;
|
|
15
24
|
story_genre: string;
|
|
16
25
|
study_duration: number;
|
|
17
26
|
mother_tongue: Language;
|
|
18
27
|
motivation_type: string;
|
|
19
28
|
onboarding_completed: boolean;
|
|
20
29
|
context_menu_on_select: boolean;
|
|
30
|
+
user_name?: string;
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
export class SettingsController {
|
|
@@ -43,32 +53,6 @@ export class SettingsController {
|
|
|
43
53
|
await this.supabase.from("plugin_settings").upsert({ plugin_id: this.pluginId, settings });
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
public async getUserInfo(): Promise<UserInfo> {
|
|
47
|
-
const { data } = await this.supabase.from("profiles").select("*");
|
|
48
|
-
|
|
49
|
-
if (!data || data.length === 0) {
|
|
50
|
-
return {
|
|
51
|
-
mother_tongue: "en",
|
|
52
|
-
skill_level_listening: "Pre-A1",
|
|
53
|
-
skill_level_reading: "Pre-A1",
|
|
54
|
-
skill_level_speaking: "Pre-A1",
|
|
55
|
-
skill_level_writing: "Pre-A1",
|
|
56
|
-
skill_level_understanding: "Pre-A1",
|
|
57
|
-
skill_level_grammar: "Pre-A1",
|
|
58
|
-
goal_longterm: "",
|
|
59
|
-
goal_weekly: "",
|
|
60
|
-
study_buddy: "clarence",
|
|
61
|
-
story_genre: "adventure",
|
|
62
|
-
study_duration: 30,
|
|
63
|
-
motivation_type: "self-motivated",
|
|
64
|
-
onboarding_completed: false,
|
|
65
|
-
context_menu_on_select: false,
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return data[0].settings;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
56
|
/**
|
|
73
57
|
* Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
|
|
74
58
|
* @param defaultSettings The default settings to use if no settings are found.
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
import { RimoriClient } from "
|
|
2
|
+
import { RimoriClient } from "../../plugin/RimoriClient";
|
|
3
3
|
import { ObjectRequest } from "./ObjectController";
|
|
4
4
|
|
|
5
|
-
export interface BasicAssignment<T> {
|
|
6
|
-
id: string;
|
|
7
|
-
createdAt: Date;
|
|
8
|
-
topic: string;
|
|
9
|
-
createdBy: string;
|
|
10
|
-
verified: boolean;
|
|
11
|
-
keywords: any;
|
|
12
|
-
data: T;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
5
|
export interface SharedContentObjectRequest extends ObjectRequest {
|
|
16
6
|
fixedProperties?: Record<string, string | number | boolean>
|
|
17
7
|
}
|
|
@@ -41,7 +31,7 @@ export class SharedContentController {
|
|
|
41
31
|
//this filter is there if the content should be filtered additionally by a column and value
|
|
42
32
|
filter?: SharedContentFilter,
|
|
43
33
|
privateTopic?: boolean,
|
|
44
|
-
): Promise<
|
|
34
|
+
): Promise<SharedContent<T>> {
|
|
45
35
|
const query = this.supabase.from("shared_content")
|
|
46
36
|
.select("*, scc:shared_content_completed(id)")
|
|
47
37
|
.eq('content_type', contentType)
|
|
@@ -72,16 +62,16 @@ export class SharedContentController {
|
|
|
72
62
|
|
|
73
63
|
console.log('fullInstructions:', fullInstructions);
|
|
74
64
|
|
|
75
|
-
const instructions = await this.rimoriClient.
|
|
65
|
+
const instructions = await this.rimoriClient.ai.getObject(fullInstructions);
|
|
76
66
|
|
|
77
67
|
console.log('instructions:', instructions);
|
|
78
68
|
|
|
79
69
|
const { data: newAssignment, error: insertError } = await this.supabase.from("shared_content").insert({
|
|
80
70
|
private: privateTopic,
|
|
81
71
|
content_type: contentType,
|
|
82
|
-
|
|
72
|
+
title: instructions.title,
|
|
83
73
|
keywords: instructions.keywords.map(({ text }: { text: string }) => text),
|
|
84
|
-
data: { ...instructions,
|
|
74
|
+
data: { ...instructions, title: undefined, keywords: undefined, ...generatorInstructions.fixedProperties },
|
|
85
75
|
}).select();
|
|
86
76
|
|
|
87
77
|
if (insertError) {
|
|
@@ -98,7 +88,7 @@ export class SharedContentController {
|
|
|
98
88
|
generatorInstructions.instructions += `
|
|
99
89
|
The following topics are already taken: ${completedTopics.join(', ')}`;
|
|
100
90
|
|
|
101
|
-
generatorInstructions.tool.
|
|
91
|
+
generatorInstructions.tool.title = {
|
|
102
92
|
type: "string",
|
|
103
93
|
description: "What the topic is about. Short. ",
|
|
104
94
|
}
|
|
@@ -111,7 +101,7 @@ export class SharedContentController {
|
|
|
111
101
|
|
|
112
102
|
private async getCompletedTopics(contentType: string, filter?: SharedContentFilter): Promise<string[]> {
|
|
113
103
|
const query = this.supabase.from("shared_content")
|
|
114
|
-
.select("
|
|
104
|
+
.select("title, keywords, scc:shared_content_completed(id)")
|
|
115
105
|
.eq('content_type', contentType)
|
|
116
106
|
.not('scc.id', 'is', null)
|
|
117
107
|
.is('deleted_at', null)
|
|
@@ -126,10 +116,10 @@ export class SharedContentController {
|
|
|
126
116
|
console.error('error fetching old assignments:', error);
|
|
127
117
|
return [];
|
|
128
118
|
}
|
|
129
|
-
return oldAssignments.map(({
|
|
119
|
+
return oldAssignments.map(({ title, keywords }) => `${title}(${keywords.join(',')})`);
|
|
130
120
|
}
|
|
131
121
|
|
|
132
|
-
public async getSharedContent<T>(contentType: string, id: string): Promise<
|
|
122
|
+
public async getSharedContent<T>(contentType: string, id: string): Promise<SharedContent<T>> {
|
|
133
123
|
const { data, error } = await this.supabase.from("shared_content").select().eq('content_type', contentType).eq('id', id).is('deleted_at', null).single();
|
|
134
124
|
if (error) {
|
|
135
125
|
console.error('error fetching shared content:', error);
|
|
@@ -149,7 +139,7 @@ export class SharedContentController {
|
|
|
149
139
|
* @param limit - Optional limit for the number of results.
|
|
150
140
|
* @returns Array of shared content matching the criteria.
|
|
151
141
|
*/
|
|
152
|
-
public async getSharedContentList<T>(contentType: string, filter?: SharedContentFilter, limit?: number): Promise<
|
|
142
|
+
public async getSharedContentList<T>(contentType: string, filter?: SharedContentFilter, limit?: number): Promise<SharedContent<T>[]> {
|
|
153
143
|
const query = this.supabase.from("shared_content").select("*").eq('content_type', contentType).is('deleted_at', null).limit(limit ?? 30);
|
|
154
144
|
|
|
155
145
|
if (filter) {
|
|
@@ -170,18 +160,18 @@ export class SharedContentController {
|
|
|
170
160
|
* Insert new shared content into the database.
|
|
171
161
|
* @param param
|
|
172
162
|
* @param param.contentType - The type of content to insert.
|
|
173
|
-
* @param param.
|
|
163
|
+
* @param param.title - The title of the content.
|
|
174
164
|
* @param param.keywords - Keywords associated with the content.
|
|
175
165
|
* @param param.data - The content data to store.
|
|
176
166
|
* @param param.privateTopic - Optional flag to indicate if the topic should be private.
|
|
177
167
|
* @returns The inserted shared content.
|
|
178
168
|
* @throws {Error} if insertion fails.
|
|
179
169
|
*/
|
|
180
|
-
public async createSharedContent<T>({ contentType,
|
|
170
|
+
public async createSharedContent<T>({ contentType, title, keywords, data, privateTopic }: Omit<SharedContent<T>, 'id'>): Promise<SharedContent<T>> {
|
|
181
171
|
const { data: newContent, error } = await this.supabase.from("shared_content").insert({
|
|
182
172
|
private: privateTopic,
|
|
183
173
|
content_type: contentType,
|
|
184
|
-
|
|
174
|
+
title,
|
|
185
175
|
keywords,
|
|
186
176
|
data,
|
|
187
177
|
}).select();
|
|
@@ -201,11 +191,11 @@ export class SharedContentController {
|
|
|
201
191
|
* @returns The updated shared content.
|
|
202
192
|
* @throws {Error} if update fails.
|
|
203
193
|
*/
|
|
204
|
-
public async updateSharedContent<T>(id: string, updates: Partial<SharedContent<T>>): Promise<
|
|
194
|
+
public async updateSharedContent<T>(id: string, updates: Partial<SharedContent<T>>): Promise<SharedContent<T>> {
|
|
205
195
|
const updateData: any = {};
|
|
206
196
|
|
|
207
197
|
if (updates.contentType) updateData.content_type = updates.contentType;
|
|
208
|
-
if (updates.
|
|
198
|
+
if (updates.title) updateData.title = updates.title;
|
|
209
199
|
if (updates.keywords) updateData.keywords = updates.keywords;
|
|
210
200
|
if (updates.data) updateData.data = updates.data;
|
|
211
201
|
if (updates.privateTopic !== undefined) updateData.private = updates.privateTopic;
|
|
@@ -230,7 +220,7 @@ export class SharedContentController {
|
|
|
230
220
|
* @returns The deleted shared content record.
|
|
231
221
|
* @throws {Error} if deletion fails or content not found.
|
|
232
222
|
*/
|
|
233
|
-
public async removeSharedContent(id: string): Promise<
|
|
223
|
+
public async removeSharedContent(id: string): Promise<SharedContent<any>> {
|
|
234
224
|
const { data: deletedContent, error } = await this.supabase
|
|
235
225
|
.from("shared_content")
|
|
236
226
|
.update({ deleted_at: new Date().toISOString() })
|
|
@@ -261,8 +251,8 @@ export interface SharedContent<T> {
|
|
|
261
251
|
/** The type/category of the content (e.g. 'grammar_exercises', 'flashcards', etc.) */
|
|
262
252
|
contentType: string;
|
|
263
253
|
|
|
264
|
-
/** The human readable title
|
|
265
|
-
|
|
254
|
+
/** The human readable title of the content */
|
|
255
|
+
title: string;
|
|
266
256
|
|
|
267
257
|
/** Array of keywords/tags associated with the content for search and categorization */
|
|
268
258
|
keywords: string[];
|
package/src/core/core.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Core functionality exports
|
|
2
|
+
export * from "../fromRimori/PluginTypes";
|
|
3
|
+
export * from "../plugin/PluginController";
|
|
4
|
+
export * from "../plugin/RimoriClient";
|
|
5
|
+
export * from "../utils/difficultyConverter";
|
|
6
|
+
export * from "../utils/Language";
|
|
7
|
+
export * from "../utils/PluginUtils";
|
|
8
|
+
export * from "../worker/WorkerSetup";
|
|
9
|
+
export { EventBusMessage } from "../fromRimori/EventBus";
|
|
10
|
+
export { Buddy, UserInfo } from "./controller/SettingsController";
|
|
11
|
+
export { SharedContent } from "./controller/SharedContentController";
|
|
12
|
+
export { Message, OnLLMResponse, ToolInvocation } from "./controller/AIController";
|
|
13
|
+
export { MacroAccomplishmentPayload, MicroAccomplishmentPayload } from "../plugin/AccomplishmentHandler";
|
|
14
|
+
export { Tool } from "../fromRimori/PluginTypes";
|
|
15
|
+
|
|
@@ -130,7 +130,7 @@ export class EventBusHandler {
|
|
|
130
130
|
* @param topics - The topic of the event.
|
|
131
131
|
* @param handler - The handler to be called when the event is emitted.
|
|
132
132
|
* @param ignoreSender - The senders to ignore.
|
|
133
|
-
* @returns
|
|
133
|
+
* @returns An EventListener object containing an off() method to unsubscribe the listeners.
|
|
134
134
|
*/
|
|
135
135
|
public on<T = EventPayload>(topics: string | string[], handler: EventHandler<T>, ignoreSender: string[] = []): EventListener {
|
|
136
136
|
const ids = this.toArray(topics).map(topic => {
|
|
@@ -163,17 +163,35 @@ export class EventBusHandler {
|
|
|
163
163
|
* @param sender - The sender of the event.
|
|
164
164
|
* @param topic - The topic of the event.
|
|
165
165
|
* @param handler - The handler to be called when the event is received. The handler returns the data to be emitted. Can be a static object or a function.
|
|
166
|
-
* @returns
|
|
166
|
+
* @returns An EventListener object containing an off() method to unsubscribe the listeners.
|
|
167
167
|
*/
|
|
168
|
-
public respond(sender: string, topic: string, handler: EventPayload | ((data: EventBusMessage) => EventPayload | Promise<EventPayload>)): EventListener {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
public respond(sender: string, topic: string | string[], handler: EventPayload | ((data: EventBusMessage) => EventPayload | Promise<EventPayload>)): EventListener {
|
|
169
|
+
const topics = Array.isArray(topic) ? topic : [topic];
|
|
170
|
+
const listeners = topics.map(topic => {
|
|
171
|
+
const blackListedEventIds: number[] = [];
|
|
172
|
+
//To allow event communication inside the same plugin the sender needs to be ignored but the events still need to be checked for the same event just reaching the subscriber to prevent infinite loops
|
|
173
|
+
const finalIgnoreSender = !topic.startsWith("self.") ? [sender] : [];
|
|
174
|
+
|
|
175
|
+
const listener = this.on(topic, async (data: EventBusMessage) => {
|
|
176
|
+
if (blackListedEventIds.includes(data.eventId)) {
|
|
177
|
+
// console.log("BLACKLISTED EVENT ID", data.eventId);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
blackListedEventIds.push(data.eventId);
|
|
181
|
+
if (blackListedEventIds.length > 20) {
|
|
182
|
+
blackListedEventIds.shift();
|
|
183
|
+
}
|
|
184
|
+
const response = typeof handler === "function" ? await handler(data) : handler;
|
|
185
|
+
this.emit(sender, topic, response, data.eventId);
|
|
186
|
+
}, finalIgnoreSender);
|
|
187
|
+
|
|
188
|
+
this.logIfDebug(`Added respond listener ` + sender + " to topic " + topic, { listener, sender });
|
|
189
|
+
return {
|
|
190
|
+
off: () => listener.off()
|
|
191
|
+
};
|
|
192
|
+
});
|
|
175
193
|
return {
|
|
176
|
-
off: () => listener.off()
|
|
194
|
+
off: () => listeners.forEach(listener => listener.off())
|
|
177
195
|
};
|
|
178
196
|
}
|
|
179
197
|
|