@rimori/client 1.0.3 → 1.0.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 +51 -0
  2. package/dist/components/CRUDModal.js +0 -1
  3. package/dist/components/ai/Assistant.d.ts +9 -0
  4. package/dist/components/ai/Assistant.js +59 -0
  5. package/dist/components/ai/Avatar.d.ts +11 -0
  6. package/dist/components/ai/Avatar.js +39 -0
  7. package/dist/components/ai/EmbeddedAssistent/AudioInputField.d.ts +7 -0
  8. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +38 -0
  9. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +7 -0
  10. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +59 -0
  11. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +19 -0
  12. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +86 -0
  13. package/dist/components/ai/EmbeddedAssistent/TTS/Player.d.ts +25 -0
  14. package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +180 -0
  15. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +7 -0
  16. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +45 -0
  17. package/dist/components/ai/utils.d.ts +6 -0
  18. package/dist/components/ai/utils.js +14 -0
  19. package/dist/components/audio/Playbutton.js +4 -5
  20. package/dist/components/avatar/Assistant.d.ts +9 -0
  21. package/dist/components/avatar/Assistant.js +59 -0
  22. package/dist/components/avatar/Avatar.d.ts +12 -0
  23. package/dist/components/avatar/Avatar.js +42 -0
  24. package/dist/components/avatar/EmbeddedAssistent/AudioInputField.d.ts +7 -0
  25. package/dist/components/avatar/EmbeddedAssistent/AudioInputField.js +38 -0
  26. package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.d.ts +7 -0
  27. package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.js +59 -0
  28. package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.d.ts +19 -0
  29. package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.js +84 -0
  30. package/dist/components/avatar/EmbeddedAssistent/TTS/Player.d.ts +25 -0
  31. package/dist/components/avatar/EmbeddedAssistent/TTS/Player.js +180 -0
  32. package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.d.ts +7 -0
  33. package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.js +45 -0
  34. package/dist/components/avatar/utils.d.ts +6 -0
  35. package/dist/components/avatar/utils.js +14 -0
  36. package/dist/components.d.ts +9 -0
  37. package/dist/components.js +10 -0
  38. package/dist/controller/AIController.d.ts +4 -3
  39. package/dist/controller/AIController.js +32 -8
  40. package/dist/controller/ObjectController.d.ts +2 -2
  41. package/dist/controller/ObjectController.js +4 -5
  42. package/dist/controller/SettingsController.d.ts +2 -1
  43. package/dist/controller/SettingsController.js +9 -0
  44. package/dist/controller/SharedContentController.js +6 -6
  45. package/dist/core.d.ts +9 -0
  46. package/dist/core.js +10 -0
  47. package/dist/hooks/UseChatHook.js +2 -2
  48. package/dist/index.d.ts +3 -2
  49. package/dist/index.js +4 -2
  50. package/dist/plugin/PluginController.d.ts +4 -12
  51. package/dist/plugin/PluginController.js +43 -70
  52. package/dist/plugin/RimoriClient.d.ts +85 -32
  53. package/dist/plugin/RimoriClient.js +98 -77
  54. package/dist/plugin/fromRimori/EventBus.d.ts +98 -0
  55. package/dist/plugin/fromRimori/EventBus.js +240 -0
  56. package/dist/providers/PluginProvider.d.ts +1 -0
  57. package/dist/providers/PluginProvider.js +10 -12
  58. package/dist/worker/WorkerSetup.d.ts +6 -0
  59. package/dist/worker/WorkerSetup.js +79 -0
  60. package/package.json +16 -3
  61. package/src/components/CRUDModal.tsx +1 -3
  62. package/src/components/ai/Assistant.tsx +96 -0
  63. package/src/components/ai/Avatar.tsx +61 -0
  64. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +64 -0
  65. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +75 -0
  66. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +91 -0
  67. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +192 -0
  68. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +56 -0
  69. package/src/components/ai/utils.ts +23 -0
  70. package/src/components/audio/Playbutton.tsx +4 -5
  71. package/src/components.ts +10 -0
  72. package/src/controller/AIController.ts +84 -60
  73. package/src/controller/ObjectController.ts +4 -6
  74. package/src/controller/SettingsController.ts +9 -1
  75. package/src/controller/SharedContentController.ts +6 -6
  76. package/src/core.ts +10 -0
  77. package/src/hooks/UseChatHook.ts +2 -2
  78. package/src/index.ts +4 -2
  79. package/src/plugin/PluginController.ts +46 -76
  80. package/src/plugin/RimoriClient.ts +147 -85
  81. package/src/plugin/fromRimori/EventBus.ts +301 -0
  82. package/src/plugin/fromRimori/readme.md +2 -0
  83. package/src/providers/PluginProvider.tsx +12 -14
  84. package/src/worker/WorkerSetup.ts +80 -0
  85. package/dist/CRUDModal.d.ts +0 -16
  86. package/dist/CRUDModal.js +0 -31
  87. package/dist/MarkdownEditor.d.ts +0 -8
  88. package/dist/MarkdownEditor.js +0 -46
  89. package/dist/audio/Playbutton.d.ts +0 -14
  90. package/dist/audio/Playbutton.js +0 -73
  91. package/dist/components/hooks/UseChatHook.d.ts +0 -15
  92. package/dist/components/hooks/UseChatHook.js +0 -21
  93. package/dist/controller/PluginController.d.ts +0 -14
  94. package/dist/controller/PluginController.js +0 -30
  95. package/dist/plugin/AIController copy.d.ts +0 -22
  96. package/dist/plugin/AIController copy.js +0 -68
  97. package/dist/plugin/AIController.d.ts +0 -22
  98. package/dist/plugin/AIController.js +0 -68
  99. package/dist/plugin/ObjectController.d.ts +0 -34
  100. package/dist/plugin/ObjectController.js +0 -77
  101. package/dist/plugin/SettingController.d.ts +0 -13
  102. package/dist/plugin/SettingController.js +0 -55
  103. package/dist/plugin/VoiceController.d.ts +0 -2
  104. package/dist/plugin/VoiceController.js +0 -27
  105. package/dist/providers/EventEmitter.d.ts +0 -11
  106. package/dist/providers/EventEmitter.js +0 -41
  107. package/dist/providers/EventEmitterContext.d.ts +0 -6
  108. package/dist/providers/EventEmitterContext.js +0 -19
  109. package/dist/utils/DifficultyConverter.d.ts +0 -3
  110. package/dist/utils/DifficultyConverter.js +0 -7
  111. package/dist/utils/constants.d.ts +0 -4
  112. package/dist/utils/constants.js +0 -12
  113. package/dist/utils/plugin/Client.d.ts +0 -72
  114. package/dist/utils/plugin/Client.js +0 -118
  115. package/dist/utils/plugin/PluginController.d.ts +0 -36
  116. package/dist/utils/plugin/PluginController.js +0 -119
  117. package/dist/utils/plugin/PluginUtils.d.ts +0 -2
  118. package/dist/utils/plugin/PluginUtils.js +0 -23
  119. package/dist/utils/plugin/RimoriClient.d.ts +0 -72
  120. package/dist/utils/plugin/RimoriClient.js +0 -118
  121. package/dist/utils/plugin/ThemeSetter.d.ts +0 -1
  122. package/dist/utils/plugin/ThemeSetter.js +0 -13
  123. package/dist/utils/plugin/WhereClauseBuilder.d.ts +0 -24
  124. package/dist/utils/plugin/WhereClauseBuilder.js +0 -79
  125. package/dist/utils/plugin/providers/EventEmitter.d.ts +0 -11
  126. package/dist/utils/plugin/providers/EventEmitter.js +0 -41
  127. package/dist/utils/plugin/providers/EventEmitterContext.d.ts +0 -6
  128. package/dist/utils/plugin/providers/EventEmitterContext.js +0 -19
  129. package/dist/utils/plugin/providers/PluginProvider.d.ts +0 -8
  130. package/dist/utils/plugin/providers/PluginProvider.js +0 -49
  131. package/src/providers/EventEmitter.ts +0 -48
  132. package/src/providers/EventEmitterContext.tsx +0 -27
  133. package/src/utils/constants.ts +0 -18
@@ -1,5 +1,3 @@
1
- import { env } from "../utils/constants";
2
-
3
1
  type PrimitiveType = 'string' | 'number' | 'boolean';
4
2
 
5
3
  // This is the type that can appear in the `type` property
@@ -35,8 +33,8 @@ export interface ObjectRequest {
35
33
  instructions: string;
36
34
  }
37
35
 
38
- export async function generateObject(request: ObjectRequest, token: string) {
39
- return await fetch(`${env.SUPABASE_URL}/functions/v1/llm-object`, {
36
+ export async function generateObject(supabaseUrl: string, request: ObjectRequest, token: string) {
37
+ return await fetch(`${supabaseUrl}/functions/v1/llm-object`, {
40
38
  method: 'POST',
41
39
  body: JSON.stringify({
42
40
  stream: false,
@@ -51,9 +49,9 @@ export async function generateObject(request: ObjectRequest, token: string) {
51
49
  // TODO adjust stream to work with object
52
50
  export type OnLLMResponse = (id: string, response: string, finished: boolean, toolInvocations?: any[]) => void;
53
51
 
54
- export async function streamObject(request: ObjectRequest, onResponse: OnLLMResponse, token: string) {
52
+ export async function streamObject(supabaseUrl: string, request: ObjectRequest, onResponse: OnLLMResponse, token: string) {
55
53
  const messageId = Math.random().toString(36).substring(3);
56
- const response = await fetch(`${env.SUPABASE_URL}/functions/v1/llm-object`, {
54
+ const response = await fetch(`${supabaseUrl}/functions/v1/llm-object`, {
57
55
  method: 'POST',
58
56
  body: JSON.stringify({
59
57
  stream: true,
@@ -3,7 +3,7 @@ import { LanguageLevel } from "../utils/difficultyConverter";
3
3
 
4
4
  type SettingsType = "user" | "system" | "plugin";
5
5
 
6
- export interface UserSettings {
6
+ export interface UserInfo {
7
7
  motherTongue: string;
8
8
  languageLevel: LanguageLevel;
9
9
  contextMenuOnSelect: boolean;
@@ -45,6 +45,14 @@ export class SettingsController {
45
45
  await this.supabase.from("plugin_settings").upsert({ plugin_id: this.pluginId, settings });
46
46
  }
47
47
 
48
+ public async getUserInfo(): Promise<UserInfo> {
49
+ return this.getSettings<UserInfo>({
50
+ motherTongue: "sv",
51
+ languageLevel: "A1",
52
+ contextMenuOnSelect: true,
53
+ }, "user");
54
+ }
55
+
48
56
  /**
49
57
  * Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
50
58
  * @param defaultSettings The default settings to use if no settings are found.
@@ -24,14 +24,14 @@ export class SharedContentController {
24
24
  filter?: { column: string, value: string | number | boolean },
25
25
  ): Promise<R[]> {
26
26
  const queryParameter = { filter_column: filter?.column || null, filter_value: filter?.value || null, unread: true }
27
- const { data: newAssignments } = await this.rimoriClient.rpc(type + "_entries", queryParameter)
27
+ const { data: newAssignments } = await this.rimoriClient.db.rpc(type + "_entries", queryParameter)
28
28
  console.log('newAssignments:', newAssignments);
29
29
 
30
30
  if ((newAssignments as any[]).length > 0) {
31
31
  return newAssignments as R[];
32
32
  }
33
33
  // generate new assignments
34
- const { data: oldAssignments } = await this.rimoriClient.rpc(type + "_entries", { ...queryParameter, unread: false })
34
+ const { data: oldAssignments } = await this.rimoriClient.db.rpc(type + "_entries", { ...queryParameter, unread: false })
35
35
  console.log('oldAssignments:', oldAssignments);
36
36
  const reservedTopics = this.getReservedTopics(oldAssignments as BasicAssignment[]);
37
37
 
@@ -39,7 +39,7 @@ export class SharedContentController {
39
39
  if (!request.tool.keywords || !request.tool.topic) {
40
40
  throw new Error("topic or keywords not found in the request schema");
41
41
  }
42
- const instructions = await this.rimoriClient.generateObject(request);
42
+ const instructions = await this.rimoriClient.llm.getObject(request);
43
43
  console.log('instructions:', instructions);
44
44
 
45
45
  const preparedData = {
@@ -47,7 +47,7 @@ export class SharedContentController {
47
47
  ...instructions,
48
48
  keywords: this.purifyStringArray(instructions.keywords),
49
49
  };
50
- return await this.rimoriClient.from(type).insert(preparedData).then(() => [preparedData] as R[]);
50
+ return await this.rimoriClient.db.from(type).insert(preparedData).then(() => [preparedData] as R[]);
51
51
  }
52
52
 
53
53
  private getReservedTopics(oldAssignments: BasicAssignment[]) {
@@ -62,10 +62,10 @@ export class SharedContentController {
62
62
  }
63
63
 
64
64
  public async getSharedContent<T extends BasicAssignment>(type: string, id: string): Promise<T> {
65
- return await this.rimoriClient.from(type).select().eq('id', id).single() as unknown as T;
65
+ return await this.rimoriClient.db.from(type).select().eq('id', id).single() as unknown as T;
66
66
  }
67
67
 
68
68
  public async completeSharedContent(type: string, assignmentId: string) {
69
- await this.rimoriClient.from(type + "_result").insert({ assignment_id: assignmentId });
69
+ await this.rimoriClient.db.from(type + "_result").insert({ assignment_id: assignmentId });
70
70
  }
71
71
  }
package/src/core.ts ADDED
@@ -0,0 +1,10 @@
1
+ // Core functionality exports
2
+ export * from "./controller/AIController";
3
+ export * from "./controller/SharedContentController";
4
+ export * from "./controller/SettingsController";
5
+ export * from "./plugin/RimoriClient";
6
+ export * from "./plugin/PluginController";
7
+ export * from "./utils/difficultyConverter";
8
+ export * from "./utils/PluginUtils";
9
+ export * from "./worker/WorkerSetup";
10
+ export * from "./plugin/fromRimori/EventBus";
@@ -5,10 +5,10 @@ import { ToolInvocation, Tool, Message } from "../controller/AIController";
5
5
  export function useChat(tools?: Tool[]) {
6
6
  const [messages, setMessages] = React.useState<Message[]>([]);
7
7
  const [isLoading, setIsLoading] = React.useState(false);
8
- const { getAIResponseStream } = usePlugin();
8
+ const { llm } = usePlugin();
9
9
 
10
10
  const append = (appendMessages: Message[]) => {
11
- getAIResponseStream([...messages, ...appendMessages], (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
11
+ llm.getSteamedText([...messages, ...appendMessages], (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
12
12
  const lastMessage = messages[messages.length - 1];
13
13
  setIsLoading(!finished);
14
14
 
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // Re-export everything
2
+ export * from './core';
3
+ export * from './components';
1
4
  export * from "./components/MarkdownEditor";
2
5
  export * from "./components/CRUDModal";
3
6
  export * from "./components/Spinner";
@@ -8,8 +11,7 @@ export * from "./controller/SettingsController";
8
11
  export * from "./hooks/UseChatHook";
9
12
  export * from "./plugin/RimoriClient";
10
13
  export * from "./plugin/ThemeSetter";
11
- export * from "./providers/EventEmitter";
12
- export * from "./providers/EventEmitterContext";
13
14
  export * from "./providers/PluginProvider";
14
15
  export * from "./utils/difficultyConverter";
15
16
  export * from "./utils/PluginUtils";
17
+ export * from "./plugin/PluginController";
@@ -1,6 +1,9 @@
1
- import { Child } from "ibridge-flex";
2
1
  import { createClient, SupabaseClient } from '@supabase/supabase-js'
3
2
  import { RimoriClient } from "./RimoriClient";
3
+ import { EventBus, EventBusMessage } from './fromRimori/EventBus';
4
+ import { setTheme } from './ThemeSetter';
5
+
6
+ setTheme();
4
7
 
5
8
  interface SupabaseInfo {
6
9
  url: string,
@@ -12,55 +15,43 @@ interface SupabaseInfo {
12
15
  }
13
16
 
14
17
  export class PluginController {
15
- private static instance: PluginController;
16
18
  private static client: RimoriClient;
17
- private plugin: Child<null, null>;
18
- private onceListeners: Map<string, any[]> = new Map();
19
- private listeners: Map<string, any[]> = new Map();
19
+ private static instance: PluginController;
20
20
  private communicationSecret: string | null = null;
21
- private initialized = false;
22
21
  private supabase: SupabaseClient | null = null;
23
22
  private supabaseInfo: SupabaseInfo | null = null;
23
+ private pluginId: string;
24
24
 
25
- private constructor() {
26
- // localStorage.debug = "*";
27
- this.plugin = new Child({
28
- triggerChild: ({ topic, data, _id }: any) => {
29
- // console.log("trigger child with topic:" + topic + " and data: ", data);
30
- this.onceListeners.get(topic)?.forEach((callback: any) => callback(_id, data));
31
- this.onceListeners.set(topic, []);
32
- this.listeners.get(topic)?.forEach((callback: any) => callback(_id, data));
33
- }
25
+ private constructor(pluginId: string) {
26
+ this.pluginId = pluginId;
27
+ this.getClient = this.getClient.bind(this);
28
+
29
+ window.addEventListener("message", (event) => {
30
+ // console.log("client: message received", event);
31
+ const { topic, sender, data, eventId } = event.data.event as EventBusMessage;
32
+
33
+ // skip forwarding messages from own plugin
34
+ if (sender === pluginId) return;
35
+
36
+ EventBus.emit(sender, topic, data, eventId);
34
37
  });
35
38
 
36
- this.emit = this.emit.bind(this);
37
- this.onOnce = this.onOnce.bind(this);
38
- this.getClient = this.getClient.bind(this);
39
- this.subscribe = this.subscribe.bind(this);
40
- this.internalEmit = this.internalEmit.bind(this);
41
- this.request = this.request.bind(this);
39
+ EventBus.on("*", (event) => {
40
+ // skip messages which are not from the own plugin
41
+ if (event.sender !== this.pluginId) return;
42
+ if (event.topic.startsWith("self.")) return;
43
+ window.parent.postMessage({ event, secret: this.getSecret() }, "*")
44
+ });
42
45
  }
43
46
 
44
- public static async getInstance(): Promise<RimoriClient> {
47
+ public static async getInstance(sender: string): Promise<RimoriClient> {
45
48
  if (!PluginController.instance) {
46
- PluginController.instance = new PluginController();
47
- await PluginController.instance.init();
48
- PluginController.client = await RimoriClient.getInstance(
49
- PluginController.instance
50
- );
49
+ PluginController.instance = new PluginController(sender);
50
+ PluginController.client = await RimoriClient.getInstance(PluginController.instance);
51
51
  }
52
52
  return PluginController.client;
53
53
  }
54
54
 
55
- async init() {
56
- if (this.initialized) return;
57
-
58
- // Wait for the plugin to be ready
59
- await this.plugin.handshake().then(() => this.initialized = true).catch((error: any) => {
60
- console.error("Failed to initialize the plugin communication:", error);
61
- });
62
- }
63
-
64
55
  private getSecret() {
65
56
  if (!this.communicationSecret) {
66
57
  const secret = new URLSearchParams(window.location.search).get("secret");
@@ -81,8 +72,8 @@ export class PluginController {
81
72
  return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
82
73
  }
83
74
 
84
- this.supabaseInfo = await this.request<SupabaseInfo>("getSupabaseAccess");
85
-
75
+ const {data} = await EventBus.request<SupabaseInfo>(this.pluginId, "global.supabase.requestAccess");
76
+ this.supabaseInfo = data;
86
77
  this.supabase = createClient(this.supabaseInfo.url, this.supabaseInfo.key, {
87
78
  accessToken: () => Promise.resolve(this.getToken())
88
79
  });
@@ -95,14 +86,14 @@ export class PluginController {
95
86
  return this.supabaseInfo.token;
96
87
  }
97
88
 
98
- const response = await this.request<{ token: string, expiration: Date }>("getSupabaseAccess");
89
+ const {data} = await EventBus.request<{ token: string, expiration: Date }>(this.pluginId, "global.supabase.requestAccess");
99
90
 
100
91
  if (!this.supabaseInfo) {
101
92
  throw new Error("Supabase info not found");
102
93
  }
103
94
 
104
- this.supabaseInfo.token = response.token;
105
- this.supabaseInfo.expiration = response.expiration;
95
+ this.supabaseInfo.token = data.token;
96
+ this.supabaseInfo.expiration = data.expiration;
106
97
 
107
98
  return this.supabaseInfo.token;
108
99
  }
@@ -115,44 +106,23 @@ export class PluginController {
115
106
  return this.supabaseInfo.url;
116
107
  }
117
108
 
118
- public emit(eventName: string, data?: any) {
119
- this.internalEmit(eventName, 0, data);
120
- }
121
-
122
- // the communication needs to have an id to be able to distinguish between different responses
123
- private internalEmit(eventName: string, id: number, data?: any) {
124
- this.init().then(() => this.plugin.emitToParent(eventName, { data, _id: id, secret: this.getSecret() }));
125
- }
126
-
127
- public subscribe(eventName: string, callback: (_id: number, data: any) => void) {
128
- if (!this.listeners.has(eventName)) {
129
- this.listeners.set(eventName, []);
109
+ public getGlobalEventTopic(preliminaryTopic: string) {
110
+ if (preliminaryTopic.startsWith("global.")) {
111
+ return preliminaryTopic;
130
112
  }
131
-
132
- this.listeners.get(eventName)?.push(callback);
133
- }
134
-
135
- public onOnce(eventName: string, callback: (data: any) => void) {
136
- if (!this.onceListeners.has(eventName)) {
137
- this.onceListeners.set(eventName, []);
113
+ if (preliminaryTopic.startsWith("self.")) {
114
+ return preliminaryTopic;
115
+ }
116
+ const topicParts = preliminaryTopic.split(".");
117
+ if (topicParts.length === 3) {
118
+ if (![this.supabaseInfo?.pluginId, "global"].includes(topicParts[0])) {
119
+ throw new Error("The event topic must start with the plugin id or 'global'.");
120
+ }
121
+ return preliminaryTopic;
138
122
  }
139
123
 
140
- this.onceListeners.get(eventName)?.push(callback);
124
+ const topicRoot = this.supabaseInfo?.pluginId ?? "global";
125
+ return `${topicRoot}.${preliminaryTopic}`;
141
126
  }
142
127
 
143
- async request<T>(topic: string, data: any = {}): Promise<T> {
144
- return await new Promise((resolve) => {
145
- let triggered = false;
146
- const id = Math.random();
147
-
148
- this.internalEmit(topic, id, data);
149
-
150
- this.subscribe(topic, (_id: number, data: any) => {
151
- if (triggered || (_id !== id && _id !== 0)) return;
152
- triggered = true;
153
-
154
- resolve(data)
155
- })
156
- });
157
- }
158
128
  }
@@ -1,5 +1,6 @@
1
1
  import { PluginController } from "./PluginController";
2
2
  import { SupabaseClient } from "@supabase/supabase-js";
3
+ import { EventBusMessage, EventPayload } from "./fromRimori/EventBus";
3
4
  import { SettingsController } from "../controller/SettingsController";
4
5
  import { GenericSchema } from "@supabase/supabase-js/dist/module/lib/types";
5
6
  import { getSTTResponse, getTTSResponse } from "../controller/VoiceController";
@@ -8,6 +9,8 @@ import { SharedContentController, BasicAssignment } from "../controller/SharedCo
8
9
  import { streamChatGPT, Message, Tool, OnLLMResponse, generateText } from "../controller/AIController";
9
10
  import { generateObject as generateObjectFunction, ObjectRequest } from "../controller/ObjectController";
10
11
  import { getPlugins, Plugin } from "../controller/SidePluginController";
12
+ import { UserInfo } from "../controller/SettingsController";
13
+ import { EventBus, EventHandler } from "./fromRimori/EventBus";
11
14
 
12
15
  interface RimoriClientOptions {
13
16
  pluginController: PluginController;
@@ -16,38 +19,132 @@ interface RimoriClientOptions {
16
19
  pluginId: string;
17
20
  }
18
21
 
22
+ interface Db {
23
+ from: {
24
+ <TableName extends string & keyof GenericSchema['Tables'], Table extends GenericSchema['Tables'][TableName]>(relation: TableName): PostgrestQueryBuilder<GenericSchema, Table, TableName>;
25
+ <ViewName extends string & keyof GenericSchema['Views'], View extends GenericSchema['Views'][ViewName]>(relation: ViewName): PostgrestQueryBuilder<GenericSchema, View, ViewName>;
26
+ };
27
+ rpc: <Fn extends GenericSchema['Functions'][string], FnName extends string & keyof GenericSchema['Functions']>(functionName: FnName, args?: Fn["Args"], options?: {
28
+ head?: boolean;
29
+ get?: boolean;
30
+ count?: "exact" | "planned" | "estimated";
31
+ }) => PostgrestFilterBuilder<GenericSchema, Fn["Returns"] extends any[] ? Fn["Returns"][number] extends Record<string, unknown> ? Fn["Returns"][number] : never : never, Fn["Returns"], string, null>;
32
+ functions: SupabaseClient["functions"];
33
+ storage: SupabaseClient["storage"];
34
+ }
35
+
36
+ interface PluginInterface {
37
+ pluginId: string;
38
+ tablePrefix: string;
39
+ setSettings: (settings: any) => Promise<void>;
40
+ /**
41
+ * Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
42
+ * @param defaultSettings The default settings to use if no settings are found.
43
+ * @param genericSettings The type of settings to get.
44
+ * @returns The settings for the plugin.
45
+ */
46
+ getSettings: <T extends object>(defaultSettings: T) => Promise<T>;
47
+ /**
48
+ * Fetches all installed plugins.
49
+ * @returns A promise that resolves to an array of plugins
50
+ */
51
+ getInstalled: () => Promise<Plugin[]>;
52
+ getUserInfo: () => Promise<UserInfo>;
53
+ }
54
+
19
55
  export class RimoriClient {
20
56
  private static instance: RimoriClient;
21
57
  private superbase: SupabaseClient;
22
- private plugin: PluginController;
23
- public functions: SupabaseClient["functions"];
24
- public storage: SupabaseClient["storage"];
25
- public pluginId: string;
26
- public tablePrefix: string;
58
+ private pluginController: PluginController;
27
59
  private settingsController: SettingsController;
28
60
  private sharedContentController: SharedContentController;
61
+ private supabaseUrl: string;
62
+ public db: Db;
63
+ public plugin: PluginInterface;
29
64
 
30
65
  private constructor(options: RimoriClientOptions) {
31
66
  this.superbase = options.supabase;
32
- this.pluginId = options.pluginId;
33
- this.plugin = options.pluginController;
34
- this.tablePrefix = options.tablePrefix;
35
- this.storage = this.superbase.storage;
36
- this.functions = this.superbase.functions;
67
+ this.pluginController = options.pluginController;
37
68
  this.settingsController = new SettingsController(options.supabase, options.pluginId);
38
69
  this.sharedContentController = new SharedContentController(this);
70
+ this.supabaseUrl = this.pluginController.getSupabaseUrl();
71
+
39
72
  this.rpc = this.rpc.bind(this);
40
73
  this.from = this.from.bind(this);
41
- this.emit = this.emit.bind(this);
42
- this.request = this.request.bind(this);
43
- this.subscribe = this.subscribe.bind(this);
44
- this.getSettings = this.getSettings.bind(this);
45
- this.setSettings = this.setSettings.bind(this);
46
- this.getAIResponse = this.getAIResponse.bind(this);
47
- this.generateObject = this.generateObject.bind(this);
48
- this.getVoiceResponse = this.getVoiceResponse.bind(this);
49
- this.getAIResponseStream = this.getAIResponseStream.bind(this);
50
- this.getVoiceToTextResponse = this.getVoiceToTextResponse.bind(this);
74
+
75
+ this.db = {
76
+ rpc: this.rpc,
77
+ from: this.from,
78
+ storage: this.superbase.storage,
79
+ functions: this.superbase.functions,
80
+ }
81
+ this.plugin = {
82
+ pluginId: options.pluginId,
83
+ tablePrefix: options.tablePrefix,
84
+ setSettings: async (settings: any) => {
85
+ await this.settingsController.setSettings(settings);
86
+ },
87
+ getSettings: async <T extends object>(defaultSettings: T): Promise<T> => {
88
+ return await this.settingsController.getSettings<T>(defaultSettings);
89
+ },
90
+ getInstalled: async (): Promise<Plugin[]> => {
91
+ return getPlugins(this.superbase);
92
+ },
93
+ getUserInfo: async (): Promise<UserInfo> => {
94
+ return this.settingsController.getUserInfo();
95
+ }
96
+ }
97
+ }
98
+
99
+ public event = {
100
+ /**
101
+ * Emit an event to Rimori or a plugin.
102
+ * The topic schema is:
103
+ * {pluginId}.{eventId}
104
+ * Check out the event bus documentation for more information.
105
+ * For triggering events from Rimori like context menu actions use the "global" keyword.
106
+ * @param topic The topic to emit the event on.
107
+ * @param data The data to emit.
108
+ * @param eventId The event id.
109
+ */
110
+ emit: (topic: string, data: any, eventId?: number) => {
111
+ const globalTopic = this.pluginController.getGlobalEventTopic(topic);
112
+ EventBus.emit(this.plugin.pluginId, globalTopic, data, eventId);
113
+ },
114
+ /**
115
+ * Request an event.
116
+ * @param topic The topic to request the event on.
117
+ * @param data The data to request.
118
+ * @returns The response from the event.
119
+ */
120
+ request: <T>(topic: string, data?: any): Promise<EventBusMessage<T>> => {
121
+ const globalTopic = this.pluginController.getGlobalEventTopic(topic);
122
+ return EventBus.request<T>(this.plugin.pluginId, globalTopic, data);
123
+ },
124
+ /**
125
+ * Subscribe to an event.
126
+ * @param topic The topic to subscribe to.
127
+ * @param callback The callback to call when the event is emitted.
128
+ */
129
+ on: <T = EventPayload>(topic: string, callback: EventHandler<T>) => {
130
+ EventBus.on<T>(this.pluginController.getGlobalEventTopic(topic), callback);
131
+ },
132
+ /**
133
+ * Subscribe to an event once.
134
+ * @param topic The topic to subscribe to.
135
+ * @param callback The callback to call when the event is emitted.
136
+ */
137
+ once: <T = EventPayload>(topic: string, callback: EventHandler<T>) => {
138
+ EventBus.once<T>(this.pluginController.getGlobalEventTopic(topic), callback);
139
+ },
140
+ /**
141
+ * Respond to an event.
142
+ * @param topic The topic to respond to.
143
+ * @param data The data to respond with.
144
+ */
145
+ respond: <T = EventPayload>(topic: string, data: EventPayload | ((data: EventBusMessage<T>) => EventPayload | Promise<EventPayload>)) => {
146
+ EventBus.respond(this.plugin.pluginId, this.pluginController.getGlobalEventTopic(topic), data);
147
+ }
51
148
  }
52
149
 
53
150
  public static async getInstance(pluginController: PluginController): Promise<RimoriClient> {
@@ -58,15 +155,15 @@ export class RimoriClient {
58
155
  return RimoriClient.instance;
59
156
  }
60
157
 
61
- public from<
158
+ private from<
62
159
  TableName extends string & keyof GenericSchema['Tables'],
63
160
  Table extends GenericSchema['Tables'][TableName]
64
161
  >(relation: TableName): PostgrestQueryBuilder<GenericSchema, Table, TableName>
65
- public from<
162
+ private from<
66
163
  ViewName extends string & keyof GenericSchema['Views'],
67
164
  View extends GenericSchema['Views'][ViewName]
68
165
  >(relation: ViewName): PostgrestQueryBuilder<GenericSchema, View, ViewName>
69
- public from(relation: string): PostgrestQueryBuilder<GenericSchema, any, any> {
166
+ private from(relation: string): PostgrestQueryBuilder<GenericSchema, any, any> {
70
167
  return this.superbase.from(this.getTableName(relation));
71
168
  }
72
169
 
@@ -93,7 +190,7 @@ export class RimoriClient {
93
190
  * `"estimated"`: Uses exact count for low numbers and planned count for high
94
191
  * numbers.
95
192
  */
96
- rpc<Fn extends GenericSchema['Functions'][string], FnName extends string & keyof GenericSchema['Functions']>(
193
+ private rpc<Fn extends GenericSchema['Functions'][string], FnName extends string & keyof GenericSchema['Functions']>(
97
194
  functionName: FnName,
98
195
  args: Fn['Args'] = {},
99
196
  options: {
@@ -116,68 +213,33 @@ export class RimoriClient {
116
213
  }
117
214
 
118
215
  private getTableName(type: string) {
119
- return this.tablePrefix + "_" + type;
120
- }
121
-
122
- public subscribe(eventName: string, callback: (_id: number, data: any) => void) {
123
- this.plugin.subscribe(eventName, callback);
124
- }
125
-
126
- public request<T>(eventName: string, data?: any): Promise<T> {
127
- return this.plugin.request(eventName, data);
128
- }
129
-
130
- public emit(eventName: string, data: any) {
131
- this.plugin.emit(eventName, data);
132
- }
133
-
134
- /**
135
- * Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
136
- * @param defaultSettings The default settings to use if no settings are found.
137
- * @param genericSettings The type of settings to get.
138
- * @returns The settings for the plugin.
139
- */
140
- public async getSettings<T extends object>(defaultSettings: T, genericSettings?: "user" | "system"): Promise<T> {
141
- return this.settingsController.getSettings<T>(defaultSettings, genericSettings);
142
- }
143
-
144
- public async setSettings(settings: any, genericSettings?: "user" | "system") {
145
- await this.settingsController.setSettings(settings, genericSettings);
146
- }
147
-
148
- public async getAIResponse(messages: Message[], tools?: Tool[]): Promise<string> {
149
- const token = await this.plugin.getToken();
150
- return generateText(messages, tools || [], token).then(response => response.messages[0].content[0].text);
151
- }
152
-
153
- public async getAIResponseStream(messages: Message[], onMessage: OnLLMResponse, tools?: Tool[]) {
154
- const token = await this.plugin.getToken();
155
- streamChatGPT(messages, tools || [], onMessage, token);
156
- }
157
-
158
- public async getVoiceResponse(text: string, voice = "alloy", speed = 1, language?: string): Promise<Blob> {
159
- return getTTSResponse(
160
- this.plugin.getSupabaseUrl(),
161
- { input: text, voice, speed, language },
162
- await this.plugin.getToken()
163
- );
164
- }
165
-
166
- public getVoiceToTextResponse(file: Blob): Promise<string> {
167
- return getSTTResponse(this.superbase, file);
168
- }
169
-
170
- /**
171
- * Fetches all installed plugins.
172
- * @returns A promise that resolves to an array of plugins
173
- */
174
- public async getPlugins(): Promise<Plugin[]> {
175
- return getPlugins(this.superbase);
216
+ return this.plugin.tablePrefix + "_" + type;
176
217
  }
177
218
 
178
- public async generateObject(request: ObjectRequest): Promise<any> {
179
- const token = await this.plugin.getToken();
180
- return generateObjectFunction(request, token);
219
+ public llm = {
220
+ getText: async (messages: Message[], tools?: Tool[]): Promise<string> => {
221
+ const token = await this.pluginController.getToken();
222
+ return generateText(this.supabaseUrl, messages, tools || [], token).then(response => response.messages[0].content[0].text);
223
+ },
224
+ getSteamedText: async (messages: Message[], onMessage: OnLLMResponse, tools?: Tool[]) => {
225
+ const token = await this.pluginController.getToken();
226
+ streamChatGPT(this.supabaseUrl, messages, tools || [], onMessage, token);
227
+ },
228
+ getVoice: async (text: string, voice = "alloy", speed = 1, language?: string): Promise<Blob> => {
229
+ return getTTSResponse(
230
+ this.pluginController.getSupabaseUrl(),
231
+ { input: text, voice, speed, language },
232
+ await this.pluginController.getToken()
233
+ );
234
+ },
235
+ getTextFromVoice: (file: Blob): Promise<string> => {
236
+ return getSTTResponse(this.superbase, file);
237
+ },
238
+ getObject: async (request: ObjectRequest): Promise<any> => {
239
+ const token = await this.pluginController.getToken();
240
+ return generateObjectFunction(this.pluginController.getSupabaseUrl(), request, token);
241
+ },
242
+ // getSteamedObject: this.generateObjectStream,
181
243
  }
182
244
 
183
245
  /**
@@ -215,6 +277,6 @@ export class RimoriClient {
215
277
  }
216
278
 
217
279
  public triggerSidebarAction(pluginId: string, actionKey: string, text?: string) {
218
- this.emit("triggerSidebarAction", { pluginId, actionKey, text });
280
+ this.event.emit("global.sidebar.triggerAction", { pluginId, actionKey, text });
219
281
  }
220
282
  }