@rimori/client 1.4.0 → 1.4.4

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.
Files changed (133) hide show
  1. package/README.md +77 -71
  2. package/dist/cli/scripts/init/dev-registration.d.ts +1 -1
  3. package/dist/cli/scripts/init/dev-registration.js +4 -4
  4. package/dist/cli/scripts/init/main.js +1 -1
  5. package/dist/cli/scripts/init/package-setup.d.ts +1 -1
  6. package/dist/cli/scripts/init/package-setup.js +3 -3
  7. package/dist/cli/scripts/init/router-transformer.js +19 -12
  8. package/dist/cli/scripts/init/vite-config.d.ts +2 -2
  9. package/dist/cli/scripts/init/vite-config.js +2 -2
  10. package/dist/cli/scripts/release/release-config-upload.js +9 -9
  11. package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
  12. package/dist/cli/scripts/release/release-db-update.js +9 -9
  13. package/dist/cli/scripts/release/release-file-upload.js +1 -1
  14. package/dist/cli/scripts/release/release.js +2 -2
  15. package/dist/components/CRUDModal.d.ts +1 -1
  16. package/dist/components/CRUDModal.js +3 -3
  17. package/dist/components/MarkdownEditor.js +16 -16
  18. package/dist/components/Spinner.js +2 -2
  19. package/dist/components/ai/Assistant.js +7 -8
  20. package/dist/components/ai/Avatar.d.ts +2 -2
  21. package/dist/components/ai/Avatar.js +10 -5
  22. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +5 -6
  23. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +1 -1
  24. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -2
  25. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -2
  26. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +4 -2
  27. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -3
  28. package/dist/components/audio/Playbutton.js +10 -7
  29. package/dist/components/components/ContextMenu.d.ts +1 -1
  30. package/dist/components/components/ContextMenu.js +19 -16
  31. package/dist/components.d.ts +10 -10
  32. package/dist/components.js +10 -10
  33. package/dist/core/controller/AIController.d.ts +2 -2
  34. package/dist/core/controller/AIController.js +12 -12
  35. package/dist/core/controller/ExerciseController.d.ts +2 -2
  36. package/dist/core/controller/ExerciseController.js +2 -2
  37. package/dist/core/controller/ObjectController.js +5 -5
  38. package/dist/core/controller/SettingsController.d.ts +22 -7
  39. package/dist/core/controller/SettingsController.js +73 -8
  40. package/dist/core/controller/SharedContentController.d.ts +3 -3
  41. package/dist/core/controller/SharedContentController.js +38 -20
  42. package/dist/core/controller/VoiceController.js +6 -4
  43. package/dist/core/core.d.ts +15 -15
  44. package/dist/core/core.js +7 -7
  45. package/dist/fromRimori/EventBus.js +23 -23
  46. package/dist/fromRimori/PluginTypes.d.ts +4 -4
  47. package/dist/hooks/UseChatHook.d.ts +3 -3
  48. package/dist/hooks/UseChatHook.js +9 -3
  49. package/dist/index.d.ts +10 -10
  50. package/dist/index.js +9 -9
  51. package/dist/plugin/AccomplishmentHandler.d.ts +5 -5
  52. package/dist/plugin/AccomplishmentHandler.js +31 -27
  53. package/dist/plugin/AudioController.d.ts +1 -1
  54. package/dist/plugin/AudioController.js +6 -6
  55. package/dist/plugin/Logger.js +15 -13
  56. package/dist/plugin/PluginController.d.ts +7 -1
  57. package/dist/plugin/PluginController.js +32 -27
  58. package/dist/plugin/RimoriClient.d.ts +17 -18
  59. package/dist/plugin/RimoriClient.js +31 -31
  60. package/dist/plugin/StandaloneClient.d.ts +1 -1
  61. package/dist/plugin/StandaloneClient.js +35 -16
  62. package/dist/plugin/ThemeSetter.js +4 -4
  63. package/dist/providers/PluginProvider.js +44 -14
  64. package/dist/utils/Language.js +57 -57
  65. package/dist/utils/PluginUtils.js +3 -3
  66. package/dist/utils/difficultyConverter.d.ts +1 -1
  67. package/dist/utils/difficultyConverter.js +1 -1
  68. package/dist/utils/endpoint.js +2 -2
  69. package/dist/worker/WorkerSetup.d.ts +1 -1
  70. package/dist/worker/WorkerSetup.js +6 -6
  71. package/example/docs/devdocs.md +50 -40
  72. package/example/docs/overview.md +1 -1
  73. package/example/docs/userdocs.md +4 -1
  74. package/example/rimori.config.ts +51 -49
  75. package/example/worker/vite.config.ts +3 -3
  76. package/example/worker/worker.ts +2 -2
  77. package/package.json +14 -8
  78. package/prettier.config.js +1 -1
  79. package/src/cli/scripts/init/dev-registration.ts +5 -8
  80. package/src/cli/scripts/init/env-setup.ts +1 -1
  81. package/src/cli/scripts/init/file-operations.ts +1 -1
  82. package/src/cli/scripts/init/html-cleaner.ts +2 -5
  83. package/src/cli/scripts/init/main.ts +16 -13
  84. package/src/cli/scripts/init/package-setup.ts +11 -15
  85. package/src/cli/scripts/init/router-transformer.ts +40 -37
  86. package/src/cli/scripts/init/tailwind-config.ts +17 -26
  87. package/src/cli/scripts/init/vite-config.ts +3 -3
  88. package/src/cli/scripts/release/release-config-upload.ts +11 -11
  89. package/src/cli/scripts/release/release-db-update.ts +12 -12
  90. package/src/cli/scripts/release/release-file-upload.ts +2 -2
  91. package/src/cli/scripts/release/release.ts +4 -4
  92. package/src/cli/types/DatabaseTypes.ts +2 -10
  93. package/src/components/CRUDModal.tsx +64 -48
  94. package/src/components/MarkdownEditor.tsx +58 -27
  95. package/src/components/Spinner.tsx +24 -17
  96. package/src/components/ai/Assistant.tsx +70 -70
  97. package/src/components/ai/Avatar.tsx +17 -14
  98. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +63 -54
  99. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +14 -5
  100. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +75 -74
  101. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +3 -4
  102. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +109 -94
  103. package/src/components/ai/utils.ts +4 -4
  104. package/src/components/audio/Playbutton.tsx +101 -93
  105. package/src/components/components/ContextMenu.tsx +47 -35
  106. package/src/components.ts +10 -10
  107. package/src/core/controller/AIController.ts +29 -19
  108. package/src/core/controller/ExerciseController.ts +16 -23
  109. package/src/core/controller/ObjectController.ts +15 -10
  110. package/src/core/controller/SettingsController.ts +89 -16
  111. package/src/core/controller/SharedContentController.ts +80 -44
  112. package/src/core/controller/VoiceController.ts +10 -8
  113. package/src/core/core.ts +15 -16
  114. package/src/fromRimori/EventBus.ts +76 -47
  115. package/src/fromRimori/PluginTypes.ts +26 -17
  116. package/src/fromRimori/readme.md +2 -2
  117. package/src/hooks/UseChatHook.ts +25 -15
  118. package/src/index.ts +10 -10
  119. package/src/plugin/AccomplishmentHandler.ts +53 -35
  120. package/src/plugin/AudioController.ts +18 -12
  121. package/src/plugin/Logger.ts +28 -21
  122. package/src/plugin/PluginController.ts +60 -44
  123. package/src/plugin/RimoriClient.ts +102 -72
  124. package/src/plugin/StandaloneClient.ts +51 -24
  125. package/src/plugin/ThemeSetter.ts +5 -5
  126. package/src/providers/PluginProvider.tsx +90 -36
  127. package/src/style.scss +3 -3
  128. package/src/utils/Language.ts +58 -58
  129. package/src/utils/PluginUtils.ts +16 -20
  130. package/src/utils/difficultyConverter.ts +2 -2
  131. package/src/utils/endpoint.ts +3 -2
  132. package/src/worker/WorkerSetup.ts +8 -9
  133. package/tsconfig.json +2 -4
@@ -3,9 +3,9 @@ export type EventPayload = Record<string, any>;
3
3
 
4
4
  /**
5
5
  * Interface representing a message sent through the EventBus
6
- *
6
+ *
7
7
  * Debug capabilities:
8
- * - System-wide debugging: Send an event to "global.system.requestDebug"
8
+ * - System-wide debugging: Send an event to "global.system.requestDebug"
9
9
  * Example: `EventBus.emit("yourPluginId", "global.system.requestDebug");`
10
10
  */
11
11
  export interface EventBusMessage<T = EventPayload> {
@@ -40,7 +40,7 @@ export class EventBusHandler {
40
40
  private responseResolvers: Map<number, (value: EventBusMessage<unknown>) => void> = new Map();
41
41
  private static instance: EventBusHandler | null = null;
42
42
  private debugEnabled: boolean = false;
43
- private evName: string = "";
43
+ private evName: string = '';
44
44
 
45
45
  private constructor() {
46
46
  //private constructor
@@ -50,12 +50,14 @@ export class EventBusHandler {
50
50
  if (!EventBusHandler.instance) {
51
51
  EventBusHandler.instance = new EventBusHandler();
52
52
 
53
- EventBusHandler.instance.on("global.system.requestDebug", () => {
53
+ EventBusHandler.instance.on('global.system.requestDebug', () => {
54
54
  EventBusHandler.instance!.debugEnabled = true;
55
- console.log(`[${EventBusHandler.instance!.evName}] Debug mode enabled. Make sure debugging messages are enabled in the browser console.`);
55
+ console.log(
56
+ `[${EventBusHandler.instance!.evName}] Debug mode enabled. Make sure debugging messages are enabled in the browser console.`,
57
+ );
56
58
  });
57
59
  }
58
- if (name && EventBusHandler.instance.evName === "") {
60
+ if (name && EventBusHandler.instance.evName === '') {
59
61
  EventBusHandler.instance.evName = name;
60
62
  }
61
63
  return EventBusHandler.instance;
@@ -80,9 +82,9 @@ export class EventBusHandler {
80
82
  * @param topic - The topic of the event.
81
83
  * @param data - The data of the event.
82
84
  * @param eventId - The event id of the event.
83
- *
85
+ *
84
86
  * The topic format is: **pluginId.area.action**
85
- *
87
+ *
86
88
  * Example topics:
87
89
  * - pl1234.card.requestHard
88
90
  * - pl1234.card.requestNew
@@ -96,7 +98,13 @@ export class EventBusHandler {
96
98
  this.emitInternal(sender, topic, data || {}, eventId);
97
99
  }
98
100
 
99
- private emitInternal(sender: string, topic: string, data: EventPayload, eventId?: number, skipResponseTrigger = false): void {
101
+ private emitInternal(
102
+ sender: string,
103
+ topic: string,
104
+ data: EventPayload,
105
+ eventId?: number,
106
+ skipResponseTrigger = false,
107
+ ): void {
100
108
  if (!this.validateTopic(topic)) {
101
109
  this.logAndThrowError(false, `Invalid topic: ` + topic);
102
110
  return;
@@ -105,7 +113,7 @@ export class EventBusHandler {
105
113
  const event = this.createEvent(sender, topic, data, eventId);
106
114
 
107
115
  const handlers = this.getMatchingHandlers(event.topic);
108
- handlers.forEach(handler => {
116
+ handlers.forEach((handler) => {
109
117
  if (handler.ignoreSender && handler.ignoreSender.includes(sender)) {
110
118
  // console.log("ignore event as its in the ignoreSender list", { event, ignoreList: handler.ignoreSender });
111
119
  return;
@@ -132,8 +140,12 @@ export class EventBusHandler {
132
140
  * @param ignoreSender - The senders to ignore.
133
141
  * @returns An EventListener object containing an off() method to unsubscribe the listeners.
134
142
  */
135
- public on<T = EventPayload>(topics: string | string[], handler: EventHandler<T>, ignoreSender: string[] = []): EventListener {
136
- const ids = this.toArray(topics).map(topic => {
143
+ public on<T = EventPayload>(
144
+ topics: string | string[],
145
+ handler: EventHandler<T>,
146
+ ignoreSender: string[] = [],
147
+ ): EventListener {
148
+ const ids = this.toArray(topics).map((topic) => {
137
149
  this.logIfDebug(`Subscribing to ` + topic, { ignoreSender });
138
150
  if (!this.validateTopic(topic)) {
139
151
  this.logAndThrowError(true, `Invalid topic: ` + topic);
@@ -154,7 +166,7 @@ export class EventBusHandler {
154
166
  });
155
167
 
156
168
  return {
157
- off: () => this.off(ids)
169
+ off: () => this.off(ids),
158
170
  };
159
171
  }
160
172
 
@@ -165,33 +177,41 @@ export class EventBusHandler {
165
177
  * @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
178
  * @returns An EventListener object containing an off() method to unsubscribe the listeners.
167
179
  */
168
- public respond(sender: string, topic: string | string[], handler: EventPayload | ((data: EventBusMessage) => EventPayload | Promise<EventPayload>)): EventListener {
180
+ public respond(
181
+ sender: string,
182
+ topic: string | string[],
183
+ handler: EventPayload | ((data: EventBusMessage) => EventPayload | Promise<EventPayload>),
184
+ ): EventListener {
169
185
  const topics = Array.isArray(topic) ? topic : [topic];
170
- const listeners = topics.map(topic => {
186
+ const listeners = topics.map((topic) => {
171
187
  const blackListedEventIds: number[] = [];
172
188
  //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
+ const finalIgnoreSender = !topic.startsWith('self.') ? [sender] : [];
190
+
191
+ const listener = this.on(
192
+ topic,
193
+ async (data: EventBusMessage) => {
194
+ if (blackListedEventIds.includes(data.eventId)) {
195
+ // console.log("BLACKLISTED EVENT ID", data.eventId);
196
+ return;
197
+ }
198
+ blackListedEventIds.push(data.eventId);
199
+ if (blackListedEventIds.length > 20) {
200
+ blackListedEventIds.shift();
201
+ }
202
+ const response = typeof handler === 'function' ? await handler(data) : handler;
203
+ this.emit(sender, topic, response, data.eventId);
204
+ },
205
+ finalIgnoreSender,
206
+ );
207
+
208
+ this.logIfDebug(`Added respond listener ` + sender + ' to topic ' + topic, { listener, sender });
189
209
  return {
190
- off: () => listener.off()
210
+ off: () => listener.off(),
191
211
  };
192
212
  });
193
213
  return {
194
- off: () => listeners.forEach(listener => listener.off())
214
+ off: () => listeners.forEach((listener) => listener.off()),
195
215
  };
196
216
  }
197
217
 
@@ -221,12 +241,12 @@ export class EventBusHandler {
221
241
  * @param listenerIds - The ids of the listeners to unsubscribe from.
222
242
  */
223
243
  private off(listenerIds: string | string[]): void {
224
- this.toArray(listenerIds).forEach(fullId => {
244
+ this.toArray(listenerIds).forEach((fullId) => {
225
245
  const { topic, id } = JSON.parse(atob(fullId));
226
246
 
227
247
  const listeners = this.listeners.get(topic) || new Set();
228
248
 
229
- listeners.forEach(listener => {
249
+ listeners.forEach((listener) => {
230
250
  if (listener.id === Number(id)) {
231
251
  listeners.delete(listener);
232
252
  this.logIfDebug(`Removed listener ` + fullId, { topic, listenerId: id });
@@ -246,7 +266,11 @@ export class EventBusHandler {
246
266
  * @param data - The data of the event.
247
267
  * @returns A promise that resolves to the event.
248
268
  */
249
- public async request<T = EventPayload>(sender: string, topic: string, data?: EventPayload): Promise<EventBusMessage<T>> {
269
+ public async request<T = EventPayload>(
270
+ sender: string,
271
+ topic: string,
272
+ data?: EventPayload,
273
+ ): Promise<EventBusMessage<T>> {
250
274
  if (!this.validateTopic(topic)) {
251
275
  this.logAndThrowError(true, `Invalid topic: ` + topic);
252
276
  }
@@ -255,8 +279,10 @@ export class EventBusHandler {
255
279
 
256
280
  this.logIfDebug(`Requesting data from ` + topic, { event });
257
281
 
258
- return new Promise<EventBusMessage<T>>(resolve => {
259
- this.responseResolvers.set(event.eventId, (value: EventBusMessage<unknown>) => resolve(value as EventBusMessage<T>));
282
+ return new Promise<EventBusMessage<T>>((resolve) => {
283
+ this.responseResolvers.set(event.eventId, (value: EventBusMessage<unknown>) =>
284
+ resolve(value as EventBusMessage<T>),
285
+ );
260
286
  this.emitInternal(sender, topic, data || {}, event.eventId, true);
261
287
  });
262
288
  }
@@ -271,7 +297,7 @@ export class EventBusHandler {
271
297
 
272
298
  // Find wildcard matches
273
299
  const wildcard = [...this.listeners.entries()]
274
- .filter(([key]) => key.endsWith("*") && topic.startsWith(key.slice(0, -1)))
300
+ .filter(([key]) => key.endsWith('*') && topic.startsWith(key.slice(0, -1)))
275
301
  .flatMap(([_, handlers]) => [...handlers]);
276
302
  return new Set([...exact, ...wildcard]);
277
303
  }
@@ -283,32 +309,35 @@ export class EventBusHandler {
283
309
  */
284
310
  private validateTopic(topic: string): boolean {
285
311
  // Split event type into parts
286
- const parts = topic.split(".");
312
+ const parts = topic.split('.');
287
313
  const [plugin, area, action] = parts;
288
314
 
289
315
  if (parts.length !== 3) {
290
- if (parts.length === 1 && plugin === "*") {
316
+ if (parts.length === 1 && plugin === '*') {
291
317
  return true;
292
318
  }
293
- if (parts.length === 2 && plugin !== "*" && area === "*") {
319
+ if (parts.length === 2 && plugin !== '*' && area === '*') {
294
320
  return true;
295
321
  }
296
322
  this.logAndThrowError(false, `Event type must have 3 parts separated by dots. Received: ` + topic);
297
323
  return false;
298
324
  }
299
325
 
300
- if (action === "*") {
326
+ if (action === '*') {
301
327
  return true;
302
328
  }
303
329
 
304
330
  // Validate action part
305
- const validActions = ["request", "create", "update", "delete", "trigger"];
331
+ const validActions = ['request', 'create', 'update', 'delete', 'trigger'];
306
332
 
307
- if (validActions.some(a => action.startsWith(a))) {
333
+ if (validActions.some((a) => action.startsWith(a))) {
308
334
  return true;
309
335
  }
310
336
 
311
- this.logAndThrowError(false, `Invalid event topic name. The action: ` + action + ". Must be or start with one of: " + validActions.join(", "));
337
+ this.logAndThrowError(
338
+ false,
339
+ `Invalid event topic name. The action: ` + action + '. Must be or start with one of: ' + validActions.join(', '),
340
+ );
312
341
  return false;
313
342
  }
314
343
 
@@ -327,4 +356,4 @@ export class EventBusHandler {
327
356
  }
328
357
  }
329
358
 
330
- export const EventBus = EventBusHandler.getInstance();
359
+ export const EventBus = EventBusHandler.getInstance();
@@ -4,10 +4,10 @@ export type Plugin<T extends {} = {}> = Omit<RimoriPluginConfig<T>, 'context_men
4
4
  endpoint: string;
5
5
  assetEndpoint: string;
6
6
  context_menu_actions: MenuEntry[];
7
- release_channel: "alpha" | "beta" | "stable";
8
- }
7
+ release_channel: 'alpha' | 'beta' | 'stable';
8
+ };
9
9
 
10
- export type ActivePlugin = Plugin<{ active?: boolean }>
10
+ export type ActivePlugin = Plugin<{ active?: boolean }>;
11
11
 
12
12
  // browsable page of a plugin
13
13
  export interface PluginPage {
@@ -17,13 +17,22 @@ export interface PluginPage {
17
17
  // Whether the page should be shown in the navbar
18
18
  show: boolean;
19
19
  description: string;
20
- root: "vocabulary" | "grammar" | "reading" | "listening" | "watching" | "writing" | "speaking" | "other" | "community";
20
+ root:
21
+ | 'vocabulary'
22
+ | 'grammar'
23
+ | 'reading'
24
+ | 'listening'
25
+ | 'watching'
26
+ | 'writing'
27
+ | 'speaking'
28
+ | 'other'
29
+ | 'community';
21
30
  // The actions that can be triggered in the plugin
22
31
  // The key is the action key. The other entries are additional properties needed when triggering the action
23
32
  action?: {
24
33
  key: string;
25
34
  parameters: ObjectTool;
26
- }
35
+ };
27
36
  }
28
37
 
29
38
  // a sidebar page of a plugin
@@ -65,7 +74,7 @@ export interface ContextMenuAction {
65
74
  // id of the plugin that the action belongs to
66
75
  plugin_id: string;
67
76
  // key of the action. Used to know which action to trigger when clicking on the context menu
68
- action_key: string
77
+ action_key: string;
69
78
  }
70
79
 
71
80
  /**
@@ -86,7 +95,7 @@ export interface RimoriPluginConfig<T extends {} = {}> {
86
95
  logo: string;
87
96
  /** Optional website URL for the plugin's homepage or link to plugins owner for contributions */
88
97
  website?: string;
89
- }
98
+ };
90
99
  /**
91
100
  * Configuration for different types of pages.
92
101
  */
@@ -101,11 +110,11 @@ export interface RimoriPluginConfig<T extends {} = {}> {
101
110
  settings?: string;
102
111
  /** Optional array of event topics the plugin pages can listen to for cross-plugin communication */
103
112
  topics?: string[];
104
- }
113
+ };
105
114
  /**
106
115
  * Context menu actions that the plugin registers to appear in right-click menus throughout the application.
107
116
  */
108
- context_menu_actions: Omit<MenuEntry, "plugin_id">[];
117
+ context_menu_actions: Omit<MenuEntry, 'plugin_id'>[];
109
118
  /**
110
119
  * Documentation paths for different types of plugin documentation.
111
120
  */
@@ -116,7 +125,7 @@ export interface RimoriPluginConfig<T extends {} = {}> {
116
125
  user_path: string;
117
126
  /** Path to developer documentation for plugin development */
118
127
  developer_path: string;
119
- }
128
+ };
120
129
  /**
121
130
  * Configuration for the plugin's web worker if it uses background processing or exposes actions to other plugins.
122
131
  */
@@ -136,7 +145,7 @@ export interface Tool {
136
145
  parameters: {
137
146
  name: string;
138
147
  description: string;
139
- type: "string" | "number" | "boolean";
148
+ type: 'string' | 'number' | 'boolean';
140
149
  }[];
141
150
  execute?: (args: Record<string, any>) => Promise<unknown> | unknown | void;
142
151
  }
@@ -145,7 +154,7 @@ export interface Tool {
145
154
  * The tool definition structure is used for LLM function calling and plugin action parameters.
146
155
  * It defines the schema for tools that can be used by Language Learning Models (LLMs)
147
156
  * and plugin actions.
148
- *
157
+ *
149
158
  * @example
150
159
  * ```typescript
151
160
  * const flashcardTool: Tool = {
@@ -155,13 +164,13 @@ export interface Tool {
155
164
  * description: 'Number of flashcards to practice'
156
165
  * },
157
166
  * deck: {
158
- * type: 'string',
167
+ * type: 'string',
159
168
  * enum: ['latest', 'random', 'oldest', 'mix', 'best_known'],
160
169
  * description: 'Type of deck to practice'
161
170
  * }
162
171
  * };
163
172
  * ```
164
- *
173
+ *
165
174
  */
166
175
  export type ObjectTool = {
167
176
  [key: string]: ToolParameter;
@@ -188,15 +197,15 @@ interface ToolParameter {
188
197
  * Supports primitive types, nested objects for complex data structures,
189
198
  * and arrays of objects for collections. The tuple notation [{}] indicates
190
199
  * arrays of objects with a specific structure.
191
- *
200
+ *
192
201
  * @example Primitive: 'string' | 'number' | 'boolean'
193
202
  * @example Nested object: { name: { type: 'string' }, age: { type: 'number' } }
194
203
  * @example Array of objects: [{ id: { type: 'string' }, value: { type: 'number' } }]
195
204
  */
196
205
  type ToolParameterType =
197
206
  | PrimitiveType
198
- | { [key: string]: ToolParameter } // for nested objects
199
- | [{ [key: string]: ToolParameter }]; // for arrays of objects (notice the tuple type)
207
+ | { [key: string]: ToolParameter } // for nested objects
208
+ | [{ [key: string]: ToolParameter }]; // for arrays of objects (notice the tuple type)
200
209
 
201
210
  /**
202
211
  * Primitive data types supported by the LLM tool system.
@@ -1,2 +1,2 @@
1
- Files inside this folder are copied from Rimori and should not be changed.
2
- If change is needed adapt it in Rimori and copy the file then over.
1
+ Files inside this folder are copied from Rimori and should not be changed.
2
+ If change is needed adapt it in Rimori and copy the file then over.
@@ -1,7 +1,7 @@
1
- import React from "react";
2
- import { Tool } from "../fromRimori/PluginTypes";
3
- import { useRimori } from "../providers/PluginProvider";
4
- import { Message, ToolInvocation } from "../core/controller/AIController";
1
+ import React from 'react';
2
+ import { Tool } from '../fromRimori/PluginTypes';
3
+ import { useRimori } from '../providers/PluginProvider';
4
+ import { Message, ToolInvocation } from '../core/controller/AIController';
5
5
 
6
6
  export function useChat(tools?: Tool[]) {
7
7
  const [messages, setMessages] = React.useState<Message[]>([]);
@@ -11,18 +11,28 @@ export function useChat(tools?: Tool[]) {
11
11
  const append = (appendMessages: Message[]) => {
12
12
  const allMessages = [...messages, ...appendMessages];
13
13
  setMessages(allMessages);
14
- ai.getSteamedText(allMessages, (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
15
- const lastMessage = messages[messages.length - 1];
16
- setIsLoading(!finished);
14
+ ai.getSteamedText(
15
+ allMessages,
16
+ (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
17
+ const lastMessage = messages[messages.length - 1];
18
+ setIsLoading(!finished);
17
19
 
18
- if (lastMessage?.id === id) {
19
- lastMessage.content = message;
20
- setMessages([...messages, lastMessage]);
21
- } else {
22
- setMessages([...allMessages, { id, role: 'assistant', content: message, toolCalls: toolInvocations }]);
23
- }
24
- }, tools);
20
+ if (lastMessage?.id === id) {
21
+ lastMessage.content = message;
22
+ setMessages([...messages, lastMessage]);
23
+ } else {
24
+ setMessages([...allMessages, { id, role: 'assistant', content: message, toolCalls: toolInvocations }]);
25
+ }
26
+ },
27
+ tools,
28
+ );
25
29
  };
26
30
 
27
- return { messages, append, isLoading, setMessages, lastMessage: messages[messages.length - 1] as Message | undefined };
31
+ return {
32
+ messages,
33
+ append,
34
+ isLoading,
35
+ setMessages,
36
+ lastMessage: messages[messages.length - 1] as Message | undefined,
37
+ };
28
38
  }
package/src/index.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  // Re-export everything
2
2
  export * from './components';
3
- export * from "./hooks/UseChatHook";
4
- export * from "./plugin/PluginController";
5
- export * from "./providers/PluginProvider";
6
- export * from "./cli/types/DatabaseTypes";
7
- export * from "./utils/difficultyConverter";
8
- export * from "./utils/PluginUtils";
9
- export * from "./utils/Language";
10
- export * from "./fromRimori/PluginTypes";
11
- export { FirstMessages } from "./components/ai/utils";
12
- export { AudioController } from "./plugin/AudioController";
3
+ export * from './hooks/UseChatHook';
4
+ export * from './plugin/PluginController';
5
+ export * from './providers/PluginProvider';
6
+ export * from './cli/types/DatabaseTypes';
7
+ export * from './utils/difficultyConverter';
8
+ export * from './utils/PluginUtils';
9
+ export * from './utils/Language';
10
+ export * from './fromRimori/PluginTypes';
11
+ export { FirstMessages } from './components/ai/utils';
12
+ export { AudioController } from './plugin/AudioController';
@@ -1,11 +1,11 @@
1
- import { EventBus, EventBusMessage } from "../fromRimori/EventBus";
1
+ import { EventBus, EventBusMessage } from '../fromRimori/EventBus';
2
2
 
3
3
  export type AccomplishmentMessage = EventBusMessage<MicroAccomplishmentPayload>;
4
4
 
5
- export const skillCategories = ["reading", "listening", "speaking", "writing", "learning", "community"] as const;
5
+ export const skillCategories = ['reading', 'listening', 'speaking', 'writing', 'learning', 'community'] as const;
6
6
 
7
7
  interface BaseAccomplishmentPayload {
8
- type: "micro" | "macro";
8
+ type: 'micro' | 'macro';
9
9
  skillCategory: (typeof skillCategories)[number];
10
10
  /*
11
11
  what is the accomplishment? e.g. chapter, flashcard, story, etc.
@@ -25,11 +25,11 @@ interface BaseAccomplishmentPayload {
25
25
  }
26
26
 
27
27
  export interface MicroAccomplishmentPayload extends BaseAccomplishmentPayload {
28
- type: "micro";
28
+ type: 'micro';
29
29
  }
30
30
 
31
31
  export interface MacroAccomplishmentPayload extends BaseAccomplishmentPayload {
32
- type: "macro";
32
+ type: 'macro';
33
33
  errorRatio: number;
34
34
  durationMinutes: number;
35
35
  }
@@ -43,14 +43,17 @@ export class AccomplishmentHandler {
43
43
  this.pluginId = pluginId;
44
44
  }
45
45
 
46
- emitAccomplishment(payload: Omit<AccomplishmentPayload, "type">) {
47
- const accomplishmentPayload = { ...payload, type: "durationMinutes" in payload ? "macro" : "micro" } as AccomplishmentPayload;
46
+ emitAccomplishment(payload: Omit<AccomplishmentPayload, 'type'>) {
47
+ const accomplishmentPayload = {
48
+ ...payload,
49
+ type: 'durationMinutes' in payload ? 'macro' : 'micro',
50
+ } as AccomplishmentPayload;
48
51
 
49
52
  this.validateAccomplishment(accomplishmentPayload);
50
53
 
51
54
  const sanitizedPayload = this.sanitizeAccomplishment(accomplishmentPayload);
52
55
 
53
- const topic = "global.accomplishment.trigger" + (accomplishmentPayload.type === "macro" ? "Macro" : "Micro");
56
+ const topic = 'global.accomplishment.trigger' + (accomplishmentPayload.type === 'macro' ? 'Macro' : 'Micro');
54
57
 
55
58
  EventBus.emit(this.pluginId, topic, sanitizedPayload);
56
59
  }
@@ -62,53 +65,59 @@ export class AccomplishmentHandler {
62
65
 
63
66
  //regex validate accomplishmentKeyword
64
67
  if (!/^[a-z_-]+$/.test(payload.accomplishmentKeyword)) {
65
- throw new Error(`The accomplishment keyword: ${payload.accomplishmentKeyword} is invalid. Only lowercase letters, minuses and underscores are allowed`);
68
+ throw new Error(
69
+ `The accomplishment keyword: ${payload.accomplishmentKeyword} is invalid. Only lowercase letters, minuses and underscores are allowed`,
70
+ );
66
71
  }
67
72
 
68
73
  //description is required
69
74
  if (payload.description.length < 10) {
70
- throw new Error("Description is too short");
75
+ throw new Error('Description is too short');
71
76
  }
72
77
 
73
78
  //check that the type is valid
74
- if (!["micro", "macro"].includes(payload.type)) {
75
- throw new Error("Invalid accomplishment type " + payload.type);
79
+ if (!['micro', 'macro'].includes(payload.type)) {
80
+ throw new Error('Invalid accomplishment type ' + payload.type);
76
81
  }
77
82
 
78
83
  //durationMinutes is required
79
- if (payload.type === "macro" && payload.durationMinutes < 4) {
80
- throw new Error("The duration must be at least 4 minutes");
84
+ if (payload.type === 'macro' && payload.durationMinutes < 4) {
85
+ throw new Error('The duration must be at least 4 minutes');
81
86
  }
82
87
 
83
88
  //errorRatio is required
84
- if (payload.type === "macro" && (payload.errorRatio < 0 || payload.errorRatio > 1)) {
85
- throw new Error("The error ratio must be between 0 and 1");
89
+ if (payload.type === 'macro' && (payload.errorRatio < 0 || payload.errorRatio > 1)) {
90
+ throw new Error('The error ratio must be between 0 and 1');
86
91
  }
87
92
 
88
93
  //regex check meta data key
89
94
  if (payload.meta) {
90
- payload.meta.forEach(meta => {
95
+ payload.meta.forEach((meta) => {
91
96
  if (!/^[a-z_]+$/.test(meta.key)) {
92
- throw new Error("Invalid meta data key " + meta.key + ", only lowercase letters and underscores are allowed");
97
+ throw new Error('Invalid meta data key ' + meta.key + ', only lowercase letters and underscores are allowed');
93
98
  }
94
99
  });
95
100
  }
96
101
  }
97
102
 
98
103
  private sanitizeAccomplishment(payload: AccomplishmentPayload) {
99
- payload.description = payload.description.replace(/[^\x20-\x7E]/g, "");
104
+ payload.description = payload.description.replace(/[^\x20-\x7E]/g, '');
100
105
 
101
106
  payload.meta?.forEach((meta) => {
102
- meta.description = meta.description.replace(/[^\x20-\x7E]/g, "");
107
+ meta.description = meta.description.replace(/[^\x20-\x7E]/g, '');
103
108
  });
104
109
 
105
110
  return payload;
106
111
  }
107
112
 
108
113
  private getDecoupledTopic(topic: string) {
109
- const [plugin, skillCategory, accomplishmentKeyword] = topic.split(".");
114
+ const [plugin, skillCategory, accomplishmentKeyword] = topic.split('.');
110
115
 
111
- return { plugin: plugin || "*", skillCategory: skillCategory || "*", accomplishmentKeyword: accomplishmentKeyword || "*" };
116
+ return {
117
+ plugin: plugin || '*',
118
+ skillCategory: skillCategory || '*',
119
+ accomplishmentKeyword: accomplishmentKeyword || '*',
120
+ };
112
121
  }
113
122
 
114
123
  /**
@@ -116,30 +125,39 @@ export class AccomplishmentHandler {
116
125
  * @param accomplishmentTopic - The topic of the accomplishment event. The pattern can be any pattern of plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword
117
126
  * @param callback - The callback function to be called when the accomplishment event is triggered
118
127
  */
119
- subscribe(accomplishmentTopics = "*" as string | string[], callback: (payload: EventBusMessage<AccomplishmentPayload>) => void) {
120
- if (typeof accomplishmentTopics === "string") {
128
+ subscribe(
129
+ accomplishmentTopics = '*' as string | string[],
130
+ callback: (payload: EventBusMessage<AccomplishmentPayload>) => void,
131
+ ) {
132
+ if (typeof accomplishmentTopics === 'string') {
121
133
  accomplishmentTopics = [accomplishmentTopics];
122
134
  }
123
135
 
124
136
  accomplishmentTopics.forEach((accomplishmentTopic) => {
125
- const topicLength = accomplishmentTopic.split(".").length
137
+ const topicLength = accomplishmentTopic.split('.').length;
126
138
  if (topicLength === 1) {
127
- accomplishmentTopic += ".*.*"
139
+ accomplishmentTopic += '.*.*';
128
140
  } else if (topicLength === 2) {
129
- accomplishmentTopic += ".*"
141
+ accomplishmentTopic += '.*';
130
142
  } else if (topicLength !== 3) {
131
- throw new Error("Invalid accomplishment topic pattern. The pattern must be plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword");
143
+ throw new Error(
144
+ 'Invalid accomplishment topic pattern. The pattern must be plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword',
145
+ );
132
146
  }
133
147
 
134
- EventBus.on<AccomplishmentPayload>(["global.accomplishment.triggerMicro", "global.accomplishment.triggerMacro"], (event) => {
135
- const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
148
+ EventBus.on<AccomplishmentPayload>(
149
+ ['global.accomplishment.triggerMicro', 'global.accomplishment.triggerMacro'],
150
+ (event) => {
151
+ const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
136
152
 
137
- if (plugin !== "*" && event.sender !== plugin) return;
138
- if (skillCategory !== "*" && event.data.skillCategory !== skillCategory) return;
139
- if (accomplishmentKeyword !== "*" && event.data.accomplishmentKeyword !== accomplishmentKeyword) return;
153
+ if (plugin !== '*' && event.sender !== plugin) return;
154
+ if (skillCategory !== '*' && event.data.skillCategory !== skillCategory) return;
155
+ if (accomplishmentKeyword !== '*' && event.data.accomplishmentKeyword !== accomplishmentKeyword) return;
140
156
 
141
- callback(event);
142
- }, [this.pluginId]);
157
+ callback(event);
158
+ },
159
+ [this.pluginId],
160
+ );
143
161
  });
144
162
  }
145
163
  }