@rimori/client 1.1.10 → 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 +1 -1
- package/dist/{controller → core/controller}/SharedContentController.js +4 -4
- 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 +16 -11
- 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 +5 -5
- 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 +42 -35
- 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,5 +1,5 @@
|
|
|
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
5
|
export interface SharedContentObjectRequest extends ObjectRequest {
|
|
@@ -62,16 +62,16 @@ export class SharedContentController {
|
|
|
62
62
|
|
|
63
63
|
console.log('fullInstructions:', fullInstructions);
|
|
64
64
|
|
|
65
|
-
const instructions = await this.rimoriClient.
|
|
65
|
+
const instructions = await this.rimoriClient.ai.getObject(fullInstructions);
|
|
66
66
|
|
|
67
67
|
console.log('instructions:', instructions);
|
|
68
68
|
|
|
69
69
|
const { data: newAssignment, error: insertError } = await this.supabase.from("shared_content").insert({
|
|
70
70
|
private: privateTopic,
|
|
71
71
|
content_type: contentType,
|
|
72
|
-
|
|
72
|
+
title: instructions.title,
|
|
73
73
|
keywords: instructions.keywords.map(({ text }: { text: string }) => text),
|
|
74
|
-
data: { ...instructions,
|
|
74
|
+
data: { ...instructions, title: undefined, keywords: undefined, ...generatorInstructions.fixedProperties },
|
|
75
75
|
}).select();
|
|
76
76
|
|
|
77
77
|
if (insertError) {
|
|
@@ -88,7 +88,7 @@ export class SharedContentController {
|
|
|
88
88
|
generatorInstructions.instructions += `
|
|
89
89
|
The following topics are already taken: ${completedTopics.join(', ')}`;
|
|
90
90
|
|
|
91
|
-
generatorInstructions.tool.
|
|
91
|
+
generatorInstructions.tool.title = {
|
|
92
92
|
type: "string",
|
|
93
93
|
description: "What the topic is about. Short. ",
|
|
94
94
|
}
|
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
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// whole configuration of a plugin (from the database)
|
|
2
|
+
export type Plugin = Omit<RimoriPluginConfig, 'context_menu_actions'> & {
|
|
3
|
+
version: string;
|
|
4
|
+
endpoint: string;
|
|
5
|
+
assetEndpoint: string;
|
|
6
|
+
context_menu_actions: MenuEntry[];
|
|
7
|
+
release_channel: "alpha" | "beta" | "stable";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// browsable page of a plugin
|
|
11
|
+
export interface PluginPage {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
url: string;
|
|
15
|
+
// Whether the page should be shown in the navbar
|
|
16
|
+
show: boolean;
|
|
17
|
+
description: string;
|
|
18
|
+
root: "vocabulary" | "grammar" | "reading" | "listening" | "watching" | "writing" | "speaking" | "other" | "community";
|
|
19
|
+
// The actions that can be triggered in the plugin
|
|
20
|
+
// The key is the action key. The other entries are additional properties needed when triggering the action
|
|
21
|
+
action?: {
|
|
22
|
+
key: string;
|
|
23
|
+
parameters: ObjectTool;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// a sidebar page of a plugin
|
|
28
|
+
export interface SidebarPage {
|
|
29
|
+
// identifier of the page. Used to know which page to trigger when clicking on the sidebar
|
|
30
|
+
id: string;
|
|
31
|
+
// name of the page. Shown in the settings
|
|
32
|
+
name: string;
|
|
33
|
+
// description of the page. Shown in the settings
|
|
34
|
+
description: string;
|
|
35
|
+
// relative or absolute URL or path to the plugin's page
|
|
36
|
+
url: string;
|
|
37
|
+
// relative or absolute URL or path to the plugin's icon image
|
|
38
|
+
icon: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// context menu entry being configured in the plugin configuration
|
|
42
|
+
export interface MenuEntry {
|
|
43
|
+
// id of the plugin that the menu entry belongs to
|
|
44
|
+
plugin_id: string;
|
|
45
|
+
// identifier of the menu entry action. Used to know which entry to trigger when clicking on the context menu
|
|
46
|
+
action_key: string;
|
|
47
|
+
// text of the menu entry. Shown in the context menu
|
|
48
|
+
text: string;
|
|
49
|
+
// icon of the menu entry. Shown in the context menu
|
|
50
|
+
icon?: React.ReactNode;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// an action from the main panel that can be triggered and performs an action in the main panel
|
|
54
|
+
export type MainPanelAction = {
|
|
55
|
+
plugin_id: string;
|
|
56
|
+
action_key: string;
|
|
57
|
+
} & Record<string, string>;
|
|
58
|
+
|
|
59
|
+
// an action from the context menu that can be triggered and performs an action in the sidebar plugin
|
|
60
|
+
export interface ContextMenuAction {
|
|
61
|
+
// selected text when clicking on the context menu
|
|
62
|
+
text: string;
|
|
63
|
+
// id of the plugin that the action belongs to
|
|
64
|
+
plugin_id: string;
|
|
65
|
+
// key of the action. Used to know which action to trigger when clicking on the context menu
|
|
66
|
+
action_key: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Rimori plugin structure representing the complete configuration
|
|
71
|
+
* of a Rimori plugin with all metadata and configuration options.
|
|
72
|
+
*/
|
|
73
|
+
export interface RimoriPluginConfig {
|
|
74
|
+
id: string;
|
|
75
|
+
/**
|
|
76
|
+
* Basic information about the plugin including branding and core details.
|
|
77
|
+
*/
|
|
78
|
+
info: {
|
|
79
|
+
/** The display name of the plugin shown to users */
|
|
80
|
+
title: string;
|
|
81
|
+
/** Detailed description introducing the plugin */
|
|
82
|
+
description: string;
|
|
83
|
+
/** relative or absolute URL or path to the plugin's logo/icon image */
|
|
84
|
+
logo: string;
|
|
85
|
+
/** Optional website URL for the plugin's homepage or link to plugins owner for contributions */
|
|
86
|
+
website?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Configuration for different types of pages.
|
|
90
|
+
*/
|
|
91
|
+
pages: {
|
|
92
|
+
/** Optional external URL where the plugin is hosted instead of the default CDN */
|
|
93
|
+
external_hosted_url?: string;
|
|
94
|
+
/** Array of main plugin pages that appear in the application's main navigation (can be disabled using the 'show' flag) */
|
|
95
|
+
main: PluginPage[];
|
|
96
|
+
/** Array of sidebar pages that appear in the sidebar for quick access (can be disabled using the 'show' flag) */
|
|
97
|
+
sidebar: SidebarPage[];
|
|
98
|
+
/** Optional path to the plugin's settings/configuration page */
|
|
99
|
+
settings?: string;
|
|
100
|
+
/** Optional array of event topics the plugin pages can listen to for cross-plugin communication */
|
|
101
|
+
topics?: string[];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Context menu actions that the plugin registers to appear in right-click menus throughout the application.
|
|
105
|
+
*/
|
|
106
|
+
context_menu_actions: Omit<MenuEntry, "plugin_id">[];
|
|
107
|
+
/**
|
|
108
|
+
* Documentation paths for different types of plugin documentation.
|
|
109
|
+
*/
|
|
110
|
+
documentation: {
|
|
111
|
+
/** Path to the general overview documentation. It's shown upon installation of the plugin. */
|
|
112
|
+
overview_path: string;
|
|
113
|
+
/** Path to user-facing documentation and guides */
|
|
114
|
+
user_path: string;
|
|
115
|
+
/** Path to developer documentation for plugin development */
|
|
116
|
+
developer_path: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Configuration for the plugin's web worker if it uses background processing or exposes actions to other plugins.
|
|
120
|
+
*/
|
|
121
|
+
worker?: {
|
|
122
|
+
/** Relative path to the web worker JavaScript file. Mostly it's 'web-worker.js' which is located in the public folder. */
|
|
123
|
+
url: string;
|
|
124
|
+
/** Optional array of event topics the worker should listen to in addition to events having the pluginId in the topic. Can be a wildcard. Example: 'global.topic.*' or 'pluginId.*' */
|
|
125
|
+
topics?: string[];
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// copied from llm edge function
|
|
130
|
+
|
|
131
|
+
export interface Tool {
|
|
132
|
+
name: string;
|
|
133
|
+
description: string;
|
|
134
|
+
parameters: {
|
|
135
|
+
name: string;
|
|
136
|
+
description: string;
|
|
137
|
+
type: "string" | "number" | "boolean";
|
|
138
|
+
}[];
|
|
139
|
+
execute?: (args: Record<string, any>) => Promise<unknown> | unknown | void;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The tool definition structure is used for LLM function calling and plugin action parameters.
|
|
144
|
+
* It defines the schema for tools that can be used by Language Learning Models (LLMs)
|
|
145
|
+
* and plugin actions.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const flashcardTool: Tool = {
|
|
150
|
+
* total_amount: {
|
|
151
|
+
* type: 'string',
|
|
152
|
+
* enum: ['default', '10', '20', '50'],
|
|
153
|
+
* description: 'Number of flashcards to practice'
|
|
154
|
+
* },
|
|
155
|
+
* deck: {
|
|
156
|
+
* type: 'string',
|
|
157
|
+
* enum: ['latest', 'random', 'oldest', 'mix', 'best_known'],
|
|
158
|
+
* description: 'Type of deck to practice'
|
|
159
|
+
* }
|
|
160
|
+
* };
|
|
161
|
+
* ```
|
|
162
|
+
*
|
|
163
|
+
*/
|
|
164
|
+
export type ObjectTool = {
|
|
165
|
+
[key: string]: ToolParameter;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Parameter definition for LLM tools and plugin actions.
|
|
170
|
+
* Defines the structure, validation rules, and metadata for individual tool parameters.
|
|
171
|
+
* Used to create type-safe interfaces between LLMs, plugins, and the Rimori platform.
|
|
172
|
+
*/
|
|
173
|
+
interface ToolParameter {
|
|
174
|
+
/** The data type of the parameter - can be primitive, nested object, or array */
|
|
175
|
+
type: ToolParameterType;
|
|
176
|
+
/** Human-readable description of the parameter's purpose and usage */
|
|
177
|
+
description: string;
|
|
178
|
+
/** Optional array of allowed values for enumerated parameters */
|
|
179
|
+
enum?: string[];
|
|
180
|
+
/** Whether the parameter is optional */
|
|
181
|
+
optional?: boolean;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Union type defining all possible parameter types for LLM tools.
|
|
186
|
+
* Supports primitive types, nested objects for complex data structures,
|
|
187
|
+
* and arrays of objects for collections. The tuple notation [{}] indicates
|
|
188
|
+
* arrays of objects with a specific structure.
|
|
189
|
+
*
|
|
190
|
+
* @example Primitive: 'string' | 'number' | 'boolean'
|
|
191
|
+
* @example Nested object: { name: { type: 'string' }, age: { type: 'number' } }
|
|
192
|
+
* @example Array of objects: [{ id: { type: 'string' }, value: { type: 'number' } }]
|
|
193
|
+
*/
|
|
194
|
+
type ToolParameterType =
|
|
195
|
+
| PrimitiveType
|
|
196
|
+
| { [key: string]: ToolParameter } // for nested objects
|
|
197
|
+
| [{ [key: string]: ToolParameter }]; // for arrays of objects (notice the tuple type)
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Primitive data types supported by the LLM tool system.
|
|
201
|
+
* These align with JSON schema primitive types and TypeScript basic types.
|
|
202
|
+
*/
|
|
203
|
+
type PrimitiveType = 'string' | 'number' | 'boolean';
|
package/src/hooks/UseChatHook.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { Tool } from "../fromRimori/PluginTypes";
|
|
3
3
|
import { usePlugin } from "../providers/PluginProvider";
|
|
4
|
+
import { Message, ToolInvocation } from "../core/controller/AIController";
|
|
4
5
|
|
|
5
6
|
export function useChat(tools?: Tool[]) {
|
|
6
7
|
const [messages, setMessages] = React.useState<Message[]>([]);
|
|
7
8
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
8
|
-
const {
|
|
9
|
+
const { ai } = usePlugin();
|
|
9
10
|
|
|
10
11
|
const append = (appendMessages: Message[]) => {
|
|
11
|
-
|
|
12
|
+
ai.getSteamedText([...messages, ...appendMessages], (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
|
|
12
13
|
const lastMessage = messages[messages.length - 1];
|
|
13
14
|
setIsLoading(!finished);
|
|
14
15
|
|
|
@@ -16,7 +17,7 @@ export function useChat(tools?: Tool[]) {
|
|
|
16
17
|
lastMessage.content = message;
|
|
17
18
|
setMessages([...messages, lastMessage]);
|
|
18
19
|
} else {
|
|
19
|
-
setMessages([...messages, ...appendMessages, { id, role: 'assistant', content: message, toolInvocations }]);
|
|
20
|
+
setMessages([...messages, ...appendMessages, { id, role: 'assistant', content: message, toolCalls: toolInvocations }]);
|
|
20
21
|
}
|
|
21
22
|
}, tools);
|
|
22
23
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// Re-export everything
|
|
2
|
-
export * from './core';
|
|
3
2
|
export * from './components';
|
|
4
3
|
export * from "./hooks/UseChatHook";
|
|
5
|
-
export * from "./plugin/
|
|
4
|
+
export * from "./plugin/PluginController";
|
|
6
5
|
export * from "./providers/PluginProvider";
|
|
6
|
+
export * from "./cli/types/DatabaseTypes";
|
|
7
7
|
export * from "./utils/difficultyConverter";
|
|
8
8
|
export * from "./utils/PluginUtils";
|
|
9
|
-
export * from "./
|
|
9
|
+
export * from "./utils/Language";
|
|
10
|
+
export * from "./fromRimori/PluginTypes";
|
|
11
|
+
export { FirstMessages } from "./components/ai/utils";
|