@rimori/client 1.2.0 → 1.3.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 +61 -18
- package/dist/cli/scripts/init/dev-registration.js +0 -1
- package/dist/cli/scripts/init/main.d.ts +1 -1
- package/dist/cli/scripts/init/main.js +1 -0
- package/dist/components/LoggerExample.d.ts +6 -0
- package/dist/components/LoggerExample.js +79 -0
- package/dist/components/ai/Assistant.js +2 -2
- package/dist/components/ai/Avatar.js +2 -2
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +41 -32
- package/dist/components/audio/Playbutton.js +2 -2
- package/dist/components/components/ContextMenu.js +48 -9
- package/dist/core/controller/AIController.js +202 -69
- package/dist/core/controller/AudioController.d.ts +0 -0
- package/dist/core/controller/AudioController.js +1 -0
- package/dist/core/controller/ObjectController.d.ts +2 -2
- package/dist/core/controller/ObjectController.js +8 -8
- package/dist/core/controller/SettingsController.d.ts +16 -0
- package/dist/core/controller/SharedContentController.d.ts +30 -2
- package/dist/core/controller/SharedContentController.js +74 -23
- package/dist/core/controller/VoiceController.d.ts +2 -3
- package/dist/core/controller/VoiceController.js +11 -4
- package/dist/core/core.d.ts +1 -0
- package/dist/fromRimori/EventBus.js +1 -1
- package/dist/fromRimori/PluginTypes.d.ts +7 -4
- package/dist/hooks/UseChatHook.js +6 -4
- package/dist/hooks/UseLogger.d.ts +30 -0
- package/dist/hooks/UseLogger.js +122 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugin/AudioController.d.ts +37 -0
- package/dist/plugin/AudioController.js +68 -0
- package/dist/plugin/Logger.d.ts +68 -0
- package/dist/plugin/Logger.js +256 -0
- package/dist/plugin/LoggerExample.d.ts +16 -0
- package/dist/plugin/LoggerExample.js +140 -0
- package/dist/plugin/PluginController.d.ts +15 -3
- package/dist/plugin/PluginController.js +162 -39
- package/dist/plugin/RimoriClient.d.ts +55 -13
- package/dist/plugin/RimoriClient.js +60 -23
- package/dist/plugin/StandaloneClient.d.ts +1 -0
- package/dist/plugin/StandaloneClient.js +16 -5
- package/dist/plugin/ThemeSetter.d.ts +2 -2
- package/dist/plugin/ThemeSetter.js +8 -5
- package/dist/providers/PluginProvider.d.ts +1 -1
- package/dist/providers/PluginProvider.js +36 -10
- package/dist/utils/audioFormats.d.ts +26 -0
- package/dist/utils/audioFormats.js +67 -0
- package/dist/worker/WorkerSetup.d.ts +3 -2
- package/dist/worker/WorkerSetup.js +22 -67
- package/package.json +2 -1
- package/src/cli/scripts/init/dev-registration.ts +0 -1
- package/src/cli/scripts/init/main.ts +1 -0
- package/src/components/ai/Assistant.tsx +2 -2
- package/src/components/ai/Avatar.tsx +2 -2
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +39 -32
- package/src/components/audio/Playbutton.tsx +2 -2
- package/src/components/components/ContextMenu.tsx +53 -9
- package/src/core/controller/AIController.ts +236 -75
- package/src/core/controller/ObjectController.ts +8 -8
- package/src/core/controller/SettingsController.ts +16 -0
- package/src/core/controller/SharedContentController.ts +87 -25
- package/src/core/controller/VoiceController.ts +24 -19
- package/src/core/core.ts +1 -0
- package/src/fromRimori/EventBus.ts +1 -1
- package/src/fromRimori/PluginTypes.ts +6 -4
- package/src/hooks/UseChatHook.ts +6 -4
- package/src/index.ts +1 -0
- package/src/plugin/AudioController.ts +58 -0
- package/src/plugin/Logger.ts +324 -0
- package/src/plugin/PluginController.ts +171 -43
- package/src/plugin/RimoriClient.ts +95 -30
- package/src/plugin/StandaloneClient.ts +22 -6
- package/src/plugin/ThemeSetter.ts +8 -5
- package/src/providers/PluginProvider.tsx +40 -10
- package/src/worker/WorkerSetup.ts +14 -63
|
@@ -32,89 +32,222 @@ export function streamChatGPT(backendUrl, messages, tools, onResponse, token) {
|
|
|
32
32
|
return __awaiter(this, void 0, void 0, function* () {
|
|
33
33
|
const messageId = Math.random().toString(36).substring(3);
|
|
34
34
|
let currentMessages = [...messages];
|
|
35
|
+
console.log('Starting streamChatGPT with:', {
|
|
36
|
+
messageId,
|
|
37
|
+
messageCount: messages.length,
|
|
38
|
+
toolCount: tools.length,
|
|
39
|
+
backendUrl
|
|
40
|
+
});
|
|
35
41
|
while (true) {
|
|
36
42
|
const messagesForApi = currentMessages.map((_a) => {
|
|
37
43
|
var { id } = _a, rest = __rest(_a, ["id"]);
|
|
38
44
|
return rest;
|
|
39
45
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
break;
|
|
46
|
+
try {
|
|
47
|
+
const response = yield fetch(`${backendUrl}/ai/llm`, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
body: JSON.stringify({ messages: messagesForApi, tools, stream: true }),
|
|
50
|
+
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
54
|
+
}
|
|
55
|
+
if (!response.body) {
|
|
56
|
+
console.error('No response body.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const reader = response.body.getReader();
|
|
60
|
+
const decoder = new TextDecoder('utf-8');
|
|
61
|
+
let content = "";
|
|
62
|
+
let done = false;
|
|
63
|
+
let toolInvocations = [];
|
|
64
|
+
let currentTextId = "";
|
|
65
|
+
let isToolCallMode = false;
|
|
66
|
+
let buffer = ""; // Buffer for incomplete chunks
|
|
67
|
+
while (!done) {
|
|
68
|
+
const { value, done: readerDone } = yield reader.read();
|
|
69
|
+
if (value) {
|
|
70
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
71
|
+
buffer += chunk;
|
|
72
|
+
// Split by lines, but handle incomplete lines
|
|
73
|
+
const lines = buffer.split('\n');
|
|
74
|
+
// Keep the last line in buffer if it's incomplete
|
|
75
|
+
if (lines.length > 1) {
|
|
76
|
+
buffer = lines.pop() || "";
|
|
72
77
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
if (line.trim() === '')
|
|
80
|
+
continue;
|
|
81
|
+
// Handle the new streaming format
|
|
82
|
+
if (line.startsWith('data: ')) {
|
|
83
|
+
const dataStr = line.substring(6); // Remove 'data: ' prefix
|
|
84
|
+
// Handle [DONE] marker
|
|
85
|
+
if (dataStr === '[DONE]') {
|
|
86
|
+
done = true;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const data = JSON.parse(dataStr);
|
|
91
|
+
// Log the first message to understand the format
|
|
92
|
+
if (!content && !isToolCallMode) {
|
|
93
|
+
console.log('First stream message received:', data);
|
|
94
|
+
}
|
|
95
|
+
switch (data.type) {
|
|
96
|
+
case 'start':
|
|
97
|
+
// Stream started, no action needed
|
|
98
|
+
console.log('Stream started');
|
|
99
|
+
break;
|
|
100
|
+
case 'start-step':
|
|
101
|
+
// Step started, no action needed
|
|
102
|
+
console.log('Step started');
|
|
103
|
+
break;
|
|
104
|
+
case 'reasoning-start':
|
|
105
|
+
// Reasoning started, no action needed
|
|
106
|
+
console.log('Reasoning started:', data.id);
|
|
107
|
+
break;
|
|
108
|
+
case 'reasoning-end':
|
|
109
|
+
// Reasoning ended, no action needed
|
|
110
|
+
console.log('Reasoning ended:', data.id);
|
|
111
|
+
break;
|
|
112
|
+
case 'text-start':
|
|
113
|
+
// Text generation started, store the ID
|
|
114
|
+
currentTextId = data.id;
|
|
115
|
+
console.log('Text generation started:', data.id);
|
|
116
|
+
break;
|
|
117
|
+
case 'text-delta':
|
|
118
|
+
// Text delta received, append to content
|
|
119
|
+
if (data.delta) {
|
|
120
|
+
content += data.delta;
|
|
121
|
+
onResponse(messageId, content, false);
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
case 'text-end':
|
|
125
|
+
// Text generation ended
|
|
126
|
+
console.log('Text generation ended:', data.id);
|
|
127
|
+
break;
|
|
128
|
+
case 'finish-step':
|
|
129
|
+
// Step finished, no action needed
|
|
130
|
+
console.log('Step finished');
|
|
131
|
+
break;
|
|
132
|
+
case 'finish':
|
|
133
|
+
// Stream finished
|
|
134
|
+
console.log('Stream finished');
|
|
135
|
+
done = true;
|
|
136
|
+
break;
|
|
137
|
+
// Additional message types that might be present in the AI library
|
|
138
|
+
case 'tool-call':
|
|
139
|
+
// Tool call initiated
|
|
140
|
+
console.log('Tool call initiated:', data);
|
|
141
|
+
isToolCallMode = true;
|
|
142
|
+
if (data.toolCallId && data.toolName && data.args) {
|
|
143
|
+
toolInvocations.push({
|
|
144
|
+
toolCallId: data.toolCallId,
|
|
145
|
+
toolName: data.toolName,
|
|
146
|
+
args: data.args
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
case 'tool-call-delta':
|
|
151
|
+
// Tool call delta (for streaming tool calls)
|
|
152
|
+
console.log('Tool call delta:', data);
|
|
153
|
+
break;
|
|
154
|
+
case 'tool-call-end':
|
|
155
|
+
// Tool call completed
|
|
156
|
+
console.log('Tool call completed:', data);
|
|
157
|
+
break;
|
|
158
|
+
case 'tool-result':
|
|
159
|
+
// Tool execution result
|
|
160
|
+
console.log('Tool result:', data);
|
|
161
|
+
break;
|
|
162
|
+
case 'error':
|
|
163
|
+
// Error occurred
|
|
164
|
+
console.error('Stream error:', data);
|
|
165
|
+
break;
|
|
166
|
+
case 'usage':
|
|
167
|
+
// Usage information
|
|
168
|
+
console.log('Usage info:', data);
|
|
169
|
+
break;
|
|
170
|
+
case 'model':
|
|
171
|
+
// Model information
|
|
172
|
+
console.log('Model info:', data);
|
|
173
|
+
break;
|
|
174
|
+
case 'stop':
|
|
175
|
+
// Stop signal
|
|
176
|
+
console.log('Stop signal received');
|
|
177
|
+
done = true;
|
|
178
|
+
break;
|
|
179
|
+
default:
|
|
180
|
+
// Unknown type, log for debugging
|
|
181
|
+
console.log('Unknown stream type:', data.type, data);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.error('Error parsing stream data:', error, dataStr);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
76
189
|
}
|
|
77
190
|
}
|
|
191
|
+
if (readerDone) {
|
|
192
|
+
done = true;
|
|
193
|
+
}
|
|
78
194
|
}
|
|
79
|
-
if
|
|
80
|
-
|
|
195
|
+
// Check if we have content or if this was a tool call response
|
|
196
|
+
if (content || toolInvocations.length > 0) {
|
|
197
|
+
currentMessages.push({
|
|
198
|
+
id: messageId,
|
|
199
|
+
role: "assistant",
|
|
200
|
+
content: content,
|
|
201
|
+
toolCalls: toolInvocations.length > 0 ? toolInvocations : undefined,
|
|
202
|
+
});
|
|
81
203
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
204
|
+
// Handle tool call scenario if tools were provided
|
|
205
|
+
if (tools.length > 0 && toolInvocations.length > 0) {
|
|
206
|
+
console.log('Tool calls detected, executing tools...');
|
|
207
|
+
const toolResults = [];
|
|
208
|
+
for (const toolInvocation of toolInvocations) {
|
|
209
|
+
const tool = tools.find(t => t.name === toolInvocation.toolName);
|
|
210
|
+
if (tool && tool.execute) {
|
|
211
|
+
try {
|
|
212
|
+
const result = yield tool.execute(toolInvocation.args);
|
|
213
|
+
toolResults.push({
|
|
214
|
+
id: Math.random().toString(36).substring(3),
|
|
215
|
+
role: "user",
|
|
216
|
+
content: `Tool '${toolInvocation.toolName}' returned: ${JSON.stringify(result)}`,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
console.error(`Error executing tool ${toolInvocation.toolName}:`, error);
|
|
221
|
+
toolResults.push({
|
|
222
|
+
id: Math.random().toString(36).substring(3),
|
|
223
|
+
role: "user",
|
|
224
|
+
content: `Tool '${toolInvocation.toolName}' failed with error: ${error}`,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
106
228
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
role: "user",
|
|
112
|
-
content: `Tool '${toolInvocation.toolName}' failed with error: ${error}`,
|
|
113
|
-
});
|
|
229
|
+
if (toolResults.length > 0) {
|
|
230
|
+
currentMessages.push(...toolResults);
|
|
231
|
+
// Continue the loop to handle the next response
|
|
232
|
+
continue;
|
|
114
233
|
}
|
|
115
234
|
}
|
|
235
|
+
// Since the new format doesn't seem to support tool calls in the same way,
|
|
236
|
+
// we'll assume the stream is complete when we reach the end
|
|
237
|
+
// If tools are provided and no content was generated, this might indicate a tool call
|
|
238
|
+
if (tools.length > 0 && !content && !isToolCallMode) {
|
|
239
|
+
// This might be a tool call scenario, but we need more information
|
|
240
|
+
// For now, we'll just finish the stream
|
|
241
|
+
console.log('No content generated, but tools provided - might be tool call scenario');
|
|
242
|
+
}
|
|
243
|
+
onResponse(messageId, content, true, toolInvocations);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
console.error('Error in streamChatGPT:', error);
|
|
248
|
+
onResponse(messageId, `Error: ${error instanceof Error ? error.message : String(error)}`, true, []);
|
|
249
|
+
return;
|
|
116
250
|
}
|
|
117
|
-
currentMessages.push(...toolResults);
|
|
118
251
|
}
|
|
119
252
|
});
|
|
120
253
|
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -36,7 +36,7 @@ export interface ObjectRequest {
|
|
|
36
36
|
*/
|
|
37
37
|
instructions: string;
|
|
38
38
|
}
|
|
39
|
-
export declare function generateObject(
|
|
39
|
+
export declare function generateObject(backendUrl: string, request: ObjectRequest, token: string): Promise<any>;
|
|
40
40
|
export type OnLLMResponse = (id: string, response: string, finished: boolean, toolInvocations?: any[]) => void;
|
|
41
|
-
export declare function streamObject(
|
|
41
|
+
export declare function streamObject(backendUrl: string, request: ObjectRequest, onResponse: OnLLMResponse, token: string): Promise<void>;
|
|
42
42
|
export {};
|
|
@@ -7,9 +7,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
export function generateObject(
|
|
10
|
+
export function generateObject(backendUrl, request, token) {
|
|
11
11
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12
|
-
return yield fetch(`${
|
|
12
|
+
return yield fetch(`${backendUrl}/ai/llm-object`, {
|
|
13
13
|
method: 'POST',
|
|
14
14
|
body: JSON.stringify({
|
|
15
15
|
stream: false,
|
|
@@ -17,14 +17,14 @@ export function generateObject(supabaseUrl, request, token) {
|
|
|
17
17
|
behaviour: request.behaviour,
|
|
18
18
|
instructions: request.instructions,
|
|
19
19
|
}),
|
|
20
|
-
headers: { 'Authorization': `Bearer ${token}
|
|
20
|
+
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }
|
|
21
21
|
}).then(response => response.json());
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
export function streamObject(
|
|
24
|
+
export function streamObject(backendUrl, request, onResponse, token) {
|
|
25
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26
26
|
const messageId = Math.random().toString(36).substring(3);
|
|
27
|
-
const response = yield fetch(`${
|
|
27
|
+
const response = yield fetch(`${backendUrl}/ai/llm-object`, {
|
|
28
28
|
method: 'POST',
|
|
29
29
|
body: JSON.stringify({
|
|
30
30
|
stream: true,
|
|
@@ -32,7 +32,7 @@ export function streamObject(supabaseUrl, request, onResponse, token) {
|
|
|
32
32
|
systemInstructions: request.behaviour,
|
|
33
33
|
secondaryInstructions: request.instructions,
|
|
34
34
|
}),
|
|
35
|
-
headers: { 'Authorization': `Bearer ${token}
|
|
35
|
+
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }
|
|
36
36
|
});
|
|
37
37
|
if (!response.body) {
|
|
38
38
|
console.error('No response body.');
|
|
@@ -56,7 +56,7 @@ export function streamObject(supabaseUrl, request, onResponse, token) {
|
|
|
56
56
|
content += data;
|
|
57
57
|
// console.log("AI response:", content);
|
|
58
58
|
//content \n\n should be real line break when message is displayed
|
|
59
|
-
onResponse(messageId, content.replace(/\\n/g, '\n'), false);
|
|
59
|
+
onResponse(messageId, content.replace(/\\n/g, '\n').replace(/\\+"/g, '"'), false);
|
|
60
60
|
}
|
|
61
61
|
else if (command === 'd') {
|
|
62
62
|
// console.log("AI usage:", JSON.parse(line.substring(2)));
|
|
@@ -71,6 +71,6 @@ export function streamObject(supabaseUrl, request, onResponse, token) {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
onResponse(messageId, content.replace(/\\n/g, '\n'), true, toolInvocations);
|
|
74
|
+
onResponse(messageId, content.replace(/\\n/g, '\n').replace(/\\+"/g, '"'), true, toolInvocations);
|
|
75
75
|
});
|
|
76
76
|
}
|
|
@@ -21,11 +21,27 @@ export interface UserInfo {
|
|
|
21
21
|
study_buddy: Buddy;
|
|
22
22
|
story_genre: string;
|
|
23
23
|
study_duration: number;
|
|
24
|
+
/**
|
|
25
|
+
* The 2 letter language code of the language the user speaks natively.
|
|
26
|
+
* With the function getLanguageName, the language name can be retrieved.
|
|
27
|
+
*/
|
|
24
28
|
mother_tongue: Language;
|
|
29
|
+
/**
|
|
30
|
+
* The language the user targets to learn.
|
|
31
|
+
*/
|
|
32
|
+
target_language: Language;
|
|
25
33
|
motivation_type: string;
|
|
26
34
|
onboarding_completed: boolean;
|
|
27
35
|
context_menu_on_select: boolean;
|
|
28
36
|
user_name?: string;
|
|
37
|
+
/**
|
|
38
|
+
* ISO 3166-1 alpha-2 country code of user's location (exposed to plugins)
|
|
39
|
+
*/
|
|
40
|
+
location_country: string;
|
|
41
|
+
/**
|
|
42
|
+
* Optional: nearest big city (>100,000) near user's location
|
|
43
|
+
*/
|
|
44
|
+
location_city?: string;
|
|
29
45
|
}
|
|
30
46
|
export declare class SettingsController {
|
|
31
47
|
private pluginId;
|
|
@@ -14,14 +14,42 @@ export declare class SharedContentController {
|
|
|
14
14
|
* @param contentType - The type of content to fetch.
|
|
15
15
|
* @param generatorInstructions - The instructions for the generator. The object needs to have a tool property with a topic and keywords property to let a new unique topic be generated.
|
|
16
16
|
* @param filter - An optional filter to apply to the query.
|
|
17
|
-
* @param
|
|
17
|
+
* @param options - Optional options.
|
|
18
|
+
* @param options.privateTopic - If the topic should be private and only be visible to the user.
|
|
19
|
+
* @param options.skipDbSave - If true, do not persist a newly generated content to the DB (default false).
|
|
20
|
+
* @param options.alwaysGenerateNew - If true, always generate a new content even if there is already a content with the same filter.
|
|
21
|
+
* @param options.excludeIds - Optional list of shared_content ids to exclude from selection.
|
|
18
22
|
* @returns The new shared content.
|
|
19
23
|
*/
|
|
20
|
-
getNewSharedContent<T>(contentType: string, generatorInstructions: SharedContentObjectRequest, filter?: SharedContentFilter,
|
|
24
|
+
getNewSharedContent<T>(contentType: string, generatorInstructions: SharedContentObjectRequest, filter?: SharedContentFilter, options?: {
|
|
25
|
+
privateTopic?: boolean;
|
|
26
|
+
skipDbSave?: boolean;
|
|
27
|
+
alwaysGenerateNew?: boolean;
|
|
28
|
+
excludeIds?: string[];
|
|
29
|
+
}): Promise<SharedContent<T>>;
|
|
30
|
+
private generateNewAssignment;
|
|
21
31
|
private getGeneratorInstructions;
|
|
22
32
|
private getCompletedTopics;
|
|
23
33
|
getSharedContent<T>(contentType: string, id: string): Promise<SharedContent<T>>;
|
|
24
34
|
completeSharedContent(contentType: string, assignmentId: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Update state details for a shared content entry in shared_content_completed.
|
|
37
|
+
* Assumes table has columns: state ('completed'|'ongoing'|'hidden'), reaction ('liked'|'disliked'|null), bookmarked boolean.
|
|
38
|
+
* Upserts per (id, content_type, user).
|
|
39
|
+
* @param param
|
|
40
|
+
* @param param.contentType - The content type.
|
|
41
|
+
* @param param.id - The shared content id.
|
|
42
|
+
* @param param.state - The state to set.
|
|
43
|
+
* @param param.reaction - Optional reaction.
|
|
44
|
+
* @param param.bookmarked - Optional bookmark flag.
|
|
45
|
+
*/
|
|
46
|
+
updateSharedContentState({ contentType, id, state, reaction, bookmarked, }: {
|
|
47
|
+
contentType: string;
|
|
48
|
+
id: string;
|
|
49
|
+
state?: 'completed' | 'ongoing' | 'hidden';
|
|
50
|
+
reaction?: 'liked' | 'disliked' | null;
|
|
51
|
+
bookmarked?: boolean;
|
|
52
|
+
}): Promise<void>;
|
|
25
53
|
/**
|
|
26
54
|
* Fetch shared content from the database based on optional filters.
|
|
27
55
|
* @param contentType - The type of content to fetch.
|
|
@@ -17,49 +17,63 @@ export class SharedContentController {
|
|
|
17
17
|
* @param contentType - The type of content to fetch.
|
|
18
18
|
* @param generatorInstructions - The instructions for the generator. The object needs to have a tool property with a topic and keywords property to let a new unique topic be generated.
|
|
19
19
|
* @param filter - An optional filter to apply to the query.
|
|
20
|
-
* @param
|
|
20
|
+
* @param options - Optional options.
|
|
21
|
+
* @param options.privateTopic - If the topic should be private and only be visible to the user.
|
|
22
|
+
* @param options.skipDbSave - If true, do not persist a newly generated content to the DB (default false).
|
|
23
|
+
* @param options.alwaysGenerateNew - If true, always generate a new content even if there is already a content with the same filter.
|
|
24
|
+
* @param options.excludeIds - Optional list of shared_content ids to exclude from selection.
|
|
21
25
|
* @returns The new shared content.
|
|
22
26
|
*/
|
|
23
27
|
getNewSharedContent(contentType, generatorInstructions,
|
|
24
28
|
//this filter is there if the content should be filtered additionally by a column and value
|
|
25
|
-
filter,
|
|
29
|
+
filter, options) {
|
|
26
30
|
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
-
|
|
28
|
-
.select("*, scc:shared_content_completed(id)")
|
|
31
|
+
let query = this.supabase.from("shared_content")
|
|
32
|
+
.select("*, scc:shared_content_completed(id, state)")
|
|
29
33
|
.eq('content_type', contentType)
|
|
30
|
-
.
|
|
31
|
-
.is('deleted_at', null)
|
|
32
|
-
|
|
34
|
+
.not('scc.state', 'in', '("completed","ongoing","hidden")')
|
|
35
|
+
.is('deleted_at', null);
|
|
36
|
+
if ((options === null || options === void 0 ? void 0 : options.excludeIds) && options.excludeIds.length > 0) {
|
|
37
|
+
const excludeIds = options.excludeIds.filter((id) => !id.startsWith('internal-temp-id-'));
|
|
38
|
+
// Supabase expects raw PostgREST syntax like '("id1","id2")'.
|
|
39
|
+
const excludeList = `(${excludeIds.map((id) => `"${id}"`).join(',')})`;
|
|
40
|
+
query = query.not('id', 'in', excludeList);
|
|
41
|
+
}
|
|
33
42
|
if (filter) {
|
|
34
43
|
query.contains('data', filter);
|
|
35
44
|
}
|
|
36
|
-
const { data: newAssignments, error } = yield query;
|
|
45
|
+
const { data: newAssignments, error } = yield query.limit(30);
|
|
37
46
|
if (error) {
|
|
38
47
|
console.error('error fetching new assignments:', error);
|
|
39
48
|
throw new Error('error fetching new assignments');
|
|
40
49
|
}
|
|
41
|
-
console.log('newAssignments:', newAssignments);
|
|
42
|
-
if (newAssignments.length > 0) {
|
|
50
|
+
// console.log('newAssignments:', newAssignments);
|
|
51
|
+
if (!(options === null || options === void 0 ? void 0 : options.alwaysGenerateNew) && newAssignments.length > 0) {
|
|
43
52
|
const index = Math.floor(Math.random() * newAssignments.length);
|
|
44
53
|
return newAssignments[index];
|
|
45
54
|
}
|
|
46
|
-
|
|
47
|
-
const fullInstructions = yield this.getGeneratorInstructions(contentType, generatorInstructions, filter);
|
|
48
|
-
console.log('fullInstructions:', fullInstructions);
|
|
49
|
-
const instructions = yield this.rimoriClient.ai.getObject(fullInstructions);
|
|
55
|
+
const instructions = yield this.generateNewAssignment(contentType, generatorInstructions, filter);
|
|
50
56
|
console.log('instructions:', instructions);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
//create the shared content object
|
|
58
|
+
const data = {
|
|
59
|
+
id: "internal-temp-id-" + Math.random().toString(36).substring(2, 15),
|
|
60
|
+
contentType,
|
|
54
61
|
title: instructions.title,
|
|
55
62
|
keywords: instructions.keywords.map(({ text }) => text),
|
|
56
63
|
data: Object.assign(Object.assign(Object.assign({}, instructions), { title: undefined, keywords: undefined }), generatorInstructions.fixedProperties),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
privateTopic: options === null || options === void 0 ? void 0 : options.privateTopic,
|
|
65
|
+
};
|
|
66
|
+
if (options === null || options === void 0 ? void 0 : options.skipDbSave) {
|
|
67
|
+
return data;
|
|
61
68
|
}
|
|
62
|
-
return
|
|
69
|
+
return yield this.createSharedContent(data);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
generateNewAssignment(contentType, generatorInstructions, filter) {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
const fullInstructions = yield this.getGeneratorInstructions(contentType, generatorInstructions, filter);
|
|
75
|
+
console.log('fullInstructions:', fullInstructions);
|
|
76
|
+
return yield this.rimoriClient.ai.getObject(fullInstructions);
|
|
63
77
|
});
|
|
64
78
|
}
|
|
65
79
|
getGeneratorInstructions(contentType, generatorInstructions, filter) {
|
|
@@ -108,7 +122,44 @@ export class SharedContentController {
|
|
|
108
122
|
}
|
|
109
123
|
completeSharedContent(contentType, assignmentId) {
|
|
110
124
|
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
-
|
|
125
|
+
// Idempotent completion: upsert on (id, user_id) so repeated calls don't fail
|
|
126
|
+
const { error } = yield this.supabase
|
|
127
|
+
.from("shared_content_completed")
|
|
128
|
+
.upsert({ content_type: contentType, id: assignmentId }, { onConflict: 'id' });
|
|
129
|
+
if (error) {
|
|
130
|
+
console.error('error completing shared content:', error);
|
|
131
|
+
throw new Error('error completing shared content');
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Update state details for a shared content entry in shared_content_completed.
|
|
137
|
+
* Assumes table has columns: state ('completed'|'ongoing'|'hidden'), reaction ('liked'|'disliked'|null), bookmarked boolean.
|
|
138
|
+
* Upserts per (id, content_type, user).
|
|
139
|
+
* @param param
|
|
140
|
+
* @param param.contentType - The content type.
|
|
141
|
+
* @param param.id - The shared content id.
|
|
142
|
+
* @param param.state - The state to set.
|
|
143
|
+
* @param param.reaction - Optional reaction.
|
|
144
|
+
* @param param.bookmarked - Optional bookmark flag.
|
|
145
|
+
*/
|
|
146
|
+
updateSharedContentState(_a) {
|
|
147
|
+
return __awaiter(this, arguments, void 0, function* ({ contentType, id, state, reaction, bookmarked, }) {
|
|
148
|
+
const payload = { content_type: contentType, id };
|
|
149
|
+
if (state !== undefined)
|
|
150
|
+
payload.state = state;
|
|
151
|
+
if (reaction !== undefined)
|
|
152
|
+
payload.reaction = reaction;
|
|
153
|
+
if (bookmarked !== undefined)
|
|
154
|
+
payload.bookmarked = bookmarked;
|
|
155
|
+
// Prefer upsert, fall back to insert/update if upsert not allowed
|
|
156
|
+
const { error } = yield this.supabase
|
|
157
|
+
.from('shared_content_completed')
|
|
158
|
+
.upsert(payload, { onConflict: 'id' });
|
|
159
|
+
if (error) {
|
|
160
|
+
console.error('error updating shared content state:', error);
|
|
161
|
+
throw new Error('error updating shared content state');
|
|
162
|
+
}
|
|
112
163
|
});
|
|
113
164
|
}
|
|
114
165
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function getTTSResponse(supabaseUrl: string, request: TTSRequest, token: string): Promise<Blob>;
|
|
1
|
+
export declare function getSTTResponse(backendUrl: string, audio: Blob, token: string): Promise<any>;
|
|
2
|
+
export declare function getTTSResponse(backendUrl: string, request: TTSRequest, token: string): Promise<Blob>;
|
|
4
3
|
interface TTSRequest {
|
|
5
4
|
input: string;
|
|
6
5
|
voice: string;
|
|
@@ -7,16 +7,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
export function getSTTResponse(
|
|
10
|
+
export function getSTTResponse(backendUrl, audio, token) {
|
|
11
11
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12
12
|
const formData = new FormData();
|
|
13
13
|
formData.append('file', audio);
|
|
14
|
-
return yield
|
|
14
|
+
return yield fetch(`${backendUrl}/voice/stt`, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'Authorization': `Bearer ${token}` },
|
|
17
|
+
body: formData,
|
|
18
|
+
}).then(r => r.json()).then(r => {
|
|
19
|
+
// console.log("STT response: ", r);
|
|
20
|
+
return r.text;
|
|
21
|
+
});
|
|
15
22
|
});
|
|
16
23
|
}
|
|
17
|
-
export function getTTSResponse(
|
|
24
|
+
export function getTTSResponse(backendUrl, request, token) {
|
|
18
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
-
return yield fetch(`${
|
|
26
|
+
return yield fetch(`${backendUrl}/voice/tts`, {
|
|
20
27
|
method: 'POST',
|
|
21
28
|
headers: {
|
|
22
29
|
'Content-Type': 'application/json',
|
package/dist/core/core.d.ts
CHANGED
|
@@ -11,3 +11,4 @@ export { SharedContent } from "./controller/SharedContentController";
|
|
|
11
11
|
export { Message, OnLLMResponse, ToolInvocation } from "./controller/AIController";
|
|
12
12
|
export { MacroAccomplishmentPayload, MicroAccomplishmentPayload } from "../plugin/AccomplishmentHandler";
|
|
13
13
|
export { Tool } from "../fromRimori/PluginTypes";
|
|
14
|
+
export { SharedContentObjectRequest } from "./controller/SharedContentController";
|