@magmamath/students-features 0.2.13 → 0.2.15

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 (56) hide show
  1. package/dist/commonjs/features/chatbot/helpers.js +8 -1
  2. package/dist/commonjs/features/chatbot/helpers.js.map +1 -1
  3. package/dist/commonjs/features/chatbot/model/api.js +3 -7
  4. package/dist/commonjs/features/chatbot/model/api.js.map +1 -1
  5. package/dist/commonjs/features/chatbot/model/model.js +4 -1
  6. package/dist/commonjs/features/chatbot/model/model.js.map +1 -1
  7. package/dist/commonjs/features/chatbot/model/t2s.js +10 -15
  8. package/dist/commonjs/features/chatbot/model/t2s.js.map +1 -1
  9. package/dist/commonjs/features/chatbot/types/model.types.js.map +1 -1
  10. package/dist/commonjs/features/feedback/types.js.map +1 -1
  11. package/dist/module/features/chatbot/helpers.js +5 -0
  12. package/dist/module/features/chatbot/helpers.js.map +1 -1
  13. package/dist/module/features/chatbot/model/api.js +3 -7
  14. package/dist/module/features/chatbot/model/api.js.map +1 -1
  15. package/dist/module/features/chatbot/model/model.js +5 -2
  16. package/dist/module/features/chatbot/model/model.js.map +1 -1
  17. package/dist/module/features/chatbot/model/t2s.js +8 -13
  18. package/dist/module/features/chatbot/model/t2s.js.map +1 -1
  19. package/dist/module/features/chatbot/types/model.types.js.map +1 -1
  20. package/dist/module/features/feedback/types.js.map +1 -1
  21. package/dist/typescript/commonjs/features/chatbot/helpers.d.ts +1 -0
  22. package/dist/typescript/commonjs/features/chatbot/helpers.d.ts.map +1 -1
  23. package/dist/typescript/commonjs/features/chatbot/model/api.d.ts +2 -3
  24. package/dist/typescript/commonjs/features/chatbot/model/api.d.ts.map +1 -1
  25. package/dist/typescript/commonjs/features/chatbot/model/model.d.ts +3 -3
  26. package/dist/typescript/commonjs/features/chatbot/model/model.d.ts.map +1 -1
  27. package/dist/typescript/commonjs/features/chatbot/model/t2s.d.ts +10 -4
  28. package/dist/typescript/commonjs/features/chatbot/model/t2s.d.ts.map +1 -1
  29. package/dist/typescript/commonjs/features/chatbot/types/api.types.d.ts +4 -0
  30. package/dist/typescript/commonjs/features/chatbot/types/api.types.d.ts.map +1 -1
  31. package/dist/typescript/commonjs/features/chatbot/types/model.types.d.ts +2 -2
  32. package/dist/typescript/commonjs/features/chatbot/types/model.types.d.ts.map +1 -1
  33. package/dist/typescript/commonjs/features/feedback/types.d.ts +1 -0
  34. package/dist/typescript/commonjs/features/feedback/types.d.ts.map +1 -1
  35. package/dist/typescript/module/features/chatbot/helpers.d.ts +1 -0
  36. package/dist/typescript/module/features/chatbot/helpers.d.ts.map +1 -1
  37. package/dist/typescript/module/features/chatbot/model/api.d.ts +2 -3
  38. package/dist/typescript/module/features/chatbot/model/api.d.ts.map +1 -1
  39. package/dist/typescript/module/features/chatbot/model/model.d.ts +3 -3
  40. package/dist/typescript/module/features/chatbot/model/model.d.ts.map +1 -1
  41. package/dist/typescript/module/features/chatbot/model/t2s.d.ts +10 -4
  42. package/dist/typescript/module/features/chatbot/model/t2s.d.ts.map +1 -1
  43. package/dist/typescript/module/features/chatbot/types/api.types.d.ts +4 -0
  44. package/dist/typescript/module/features/chatbot/types/api.types.d.ts.map +1 -1
  45. package/dist/typescript/module/features/chatbot/types/model.types.d.ts +2 -2
  46. package/dist/typescript/module/features/chatbot/types/model.types.d.ts.map +1 -1
  47. package/dist/typescript/module/features/feedback/types.d.ts +1 -0
  48. package/dist/typescript/module/features/feedback/types.d.ts.map +1 -1
  49. package/package.json +1 -1
  50. package/src/features/chatbot/helpers.ts +16 -1
  51. package/src/features/chatbot/model/api.ts +5 -17
  52. package/src/features/chatbot/model/model.ts +243 -252
  53. package/src/features/chatbot/model/t2s.ts +90 -92
  54. package/src/features/chatbot/types/api.types.ts +5 -0
  55. package/src/features/chatbot/types/model.types.ts +44 -43
  56. package/src/features/feedback/types.ts +1 -0
@@ -2,276 +2,267 @@ import { attach, createEvent, restore, sample, type Store, type Effect } from 'e
2
2
  import { createControllerEffect, createEntityFieldStore } from '../../../lib/effector'
3
3
 
4
4
  import {
5
- AvailableChatsModel,
6
- ChatbotModelProps,
7
- ChatMessagesModel,
8
- ConversationMessage,
9
- EntityIds,
10
- SendHelpParams,
11
- SendHelpProps,
12
- SetHintMessageProps,
13
- ThreadIdsModel,
5
+ AvailableChatsModel,
6
+ ChatbotModelProps,
7
+ ChatMessagesModel,
8
+ ConversationMessage,
9
+ EntityIds,
10
+ SendHelpParams,
11
+ SendHelpProps,
12
+ SetHintMessageProps,
13
+ ThreadIdsModel,
14
14
  } from '../types/model.types'
15
15
  import { GetAlternativesResponse, PostMessageResponse } from '../types/api.types'
16
16
 
17
17
  import { generateRandomId } from '../../../lib/helpers/helpers'
18
18
  import { ChatbotAPI } from './api'
19
19
  import { ChatbotTranslation } from './translation'
20
- import { ChatTextToSpeech } from './t2s'
20
+ import { ChatTextToSpeechModel } from './t2s'
21
21
 
22
22
  export class ChatbotModel {
23
- readonly api: ChatbotAPI
24
- readonly translation: ChatbotTranslation
25
- readonly t2s: ChatTextToSpeech
26
-
27
- readonly credits: ChatbotModelProps['credits']
28
-
29
- readonly resetAlternatives = createEvent()
30
- readonly setAlternatives = createEvent<GetAlternativesResponse | null>()
31
- readonly $alternatives: Store<GetAlternativesResponse | null>
32
-
33
- readonly setIsOpen = createEvent<boolean>()
34
- readonly toggleOpen = createEvent()
35
- readonly $isOpen = restore(this.setIsOpen, false).on(this.toggleOpen, isOpen => !isOpen)
36
-
37
- // separate messages and alternatives class?
38
- readonly removeLastMessage = createEvent()
39
- readonly setLastMessageIds = createEvent<EntityIds | null>()
40
- readonly $lastMessageIds = restore(this.setLastMessageIds, null)
41
-
42
- readonly setScrollActive = createEvent<boolean>()
43
- readonly $isScrollActive = restore(this.setScrollActive, false)
44
-
45
- readonly sendHelpRequestFx: Effect<SendHelpParams, PostMessageResponse | ConversationMessage>
46
-
47
- readonly defaultMessages: ChatbotModelProps['messages']
48
-
49
- constructor({
50
- credits,
51
- api,
23
+ readonly api: ChatbotAPI
24
+ readonly translation: ChatbotTranslation
25
+ readonly t2s: ChatTextToSpeechModel
26
+
27
+ readonly credits: ChatbotModelProps['credits']
28
+
29
+ readonly resetAlternatives = createEvent()
30
+ readonly setAlternatives = createEvent<GetAlternativesResponse | null>()
31
+ readonly $alternatives: Store<GetAlternativesResponse | null>
32
+
33
+ readonly setIsOpen = createEvent<boolean>()
34
+ readonly toggleOpen = createEvent()
35
+ readonly $isOpen = restore(this.setIsOpen, false).on(this.toggleOpen, isOpen => !isOpen)
36
+
37
+ // separate messages and alternatives class?
38
+ readonly removeLastMessage = createEvent()
39
+ readonly setLastMessageIds = createEvent<EntityIds | null>()
40
+ readonly $lastMessageIds = restore(this.setLastMessageIds, null)
41
+
42
+ readonly setScrollActive = createEvent<boolean>()
43
+ readonly $isScrollActive = restore(this.setScrollActive, false)
44
+
45
+ readonly sendHelpRequestFx: Effect<SendHelpParams, PostMessageResponse | ConversationMessage>
46
+
47
+ readonly defaultMessages: ChatbotModelProps['messages']
48
+
49
+ constructor({ credits, api, messages }: ChatbotModelProps) {
50
+ this.api = new ChatbotAPI(api)
51
+
52
+ this.defaultMessages = messages
53
+ this.credits = credits
54
+
55
+ this.$alternatives = restore(this.api.getAlternativesFx.doneData, null)
56
+ .on(this.setAlternatives, (_, payload) => payload)
57
+ .reset(this.resetAlternatives)
58
+
59
+ // TODO: do not pass this, just what you need
60
+ this.translation = new ChatbotTranslation(this)
61
+ this.t2s = new ChatTextToSpeechModel({ $isOpen: this.$isOpen, api: this.api })
62
+
63
+ this.sendHelpRequestFx = this.createSendHelpRequestFx()
64
+ this.initChatReplyWatcher()
65
+ }
66
+
67
+ readonly messages = createEntityFieldStore({
68
+ mapper: ({ entityId, fieldId, messages }: ChatMessagesModel) => ({
69
+ entityId,
70
+ fieldId,
71
+ data: { messages },
72
+ }),
73
+ })
74
+
75
+ readonly threadIds = createEntityFieldStore({
76
+ mapper: ({ entityId, fieldId, threadId }: ThreadIdsModel) => ({
77
+ entityId,
78
+ fieldId,
79
+ data: { threadId },
80
+ }),
81
+ })
82
+
83
+ readonly availableChats = createEntityFieldStore({
84
+ mapper: ({ entityId, fieldId, isAvailable = true }: AvailableChatsModel) => ({
85
+ entityId,
86
+ fieldId,
87
+ data: { isAvailable },
88
+ }),
89
+ })
90
+
91
+ private createSendHelpRequestFx() {
92
+ return attach({
93
+ source: [
94
+ this.credits.$assignmentId,
95
+ this.credits.$exerciseId,
96
+ this.messages.$state,
97
+ this.translation.$isTranslated,
98
+ ],
99
+ mapParams: (params: SendHelpParams, [assignmentId, exerciseId, messages, isTranslated]) => ({
100
+ ...params,
101
+ assignmentId,
102
+ exerciseId,
52
103
  messages,
53
- }: ChatbotModelProps) {
54
- this.api = new ChatbotAPI(api)
55
-
56
- this.defaultMessages = messages
57
- this.credits = credits
58
-
59
- this.$alternatives = restore(this.api.getAlternativesFx.doneData, null)
60
- .on(this.setAlternatives, (_, payload) => payload)
61
- .reset(this.resetAlternatives)
62
-
63
- // TODO: do not pass this, just what you need
64
- this.translation = new ChatbotTranslation(this)
65
- this.t2s = new ChatTextToSpeech(this)
66
-
67
- this.sendHelpRequestFx = this.createSendHelpRequestFx()
68
- this.initChatReplyWatcher()
69
- }
70
-
71
- readonly messages = createEntityFieldStore({
72
- mapper: ({ entityId, fieldId, messages }: ChatMessagesModel) => ({
73
- entityId,
74
- fieldId,
75
- data: { messages },
76
- }),
77
- })
78
-
79
- readonly threadIds = createEntityFieldStore({
80
- mapper: ({ entityId, fieldId, threadId }: ThreadIdsModel) => ({
81
- entityId,
82
- fieldId,
83
- data: { threadId },
84
- }),
85
- })
104
+ isTranslated,
105
+ }),
106
+ effect: createControllerEffect(
107
+ async ({
108
+ payload,
109
+ message = '',
110
+ translationTargetLanguage,
111
+ translatedMessage,
112
+ assignmentId,
113
+ exerciseId,
114
+ messages,
115
+ isTranslated,
116
+ }: SendHelpProps) => {
117
+ this.setLastMessageIds({
118
+ entityId: assignmentId,
119
+ fieldId: exerciseId,
120
+ })
86
121
 
87
- readonly availableChats = createEntityFieldStore({
88
- mapper: ({ entityId, fieldId, isAvailable = true }: AvailableChatsModel) => ({
89
- entityId,
90
- fieldId,
91
- data: { isAvailable },
92
- }),
93
- })
122
+ const currentMessages = messages[assignmentId]?.[exerciseId]?.messages || []
94
123
 
95
- private createSendHelpRequestFx() {
96
- return attach({
97
- source: [
98
- this.credits.$assignmentId,
99
- this.credits.$exerciseId,
100
- this.messages.$state,
101
- this.translation.$isTranslated,
124
+ this.messages.setField({
125
+ entityId: assignmentId,
126
+ fieldId: exerciseId,
127
+ messages: [
128
+ ...currentMessages,
129
+ {
130
+ id: generateRandomId(),
131
+ message: message ?? '',
132
+ translatedMessage,
133
+ isOwnMessage: true,
134
+ threadId: payload.threadId ?? '',
135
+ options: [],
136
+ },
102
137
  ],
103
- mapParams: (
104
- params: SendHelpParams,
105
- [assignmentId, exerciseId, messages, isTranslated],
106
- ) => ({
107
- ...params,
108
- assignmentId,
109
- exerciseId,
110
- messages,
111
- isTranslated,
112
- }),
113
- effect: createControllerEffect(
114
- async ({
115
- payload,
116
- message = '',
117
- translationTargetLanguage,
118
- translatedMessage,
119
- assignmentId,
120
- exerciseId,
121
- messages,
122
- isTranslated,
123
- }: SendHelpProps) => {
124
- this.setLastMessageIds({
125
- entityId: assignmentId,
126
- fieldId: exerciseId,
127
- })
128
-
129
- const currentMessages = messages[assignmentId]?.[exerciseId]?.messages || []
130
-
131
- this.messages.setField({
132
- entityId: assignmentId,
133
- fieldId: exerciseId,
134
- messages: [
135
- ...currentMessages,
136
- {
137
- id: generateRandomId(),
138
- message: message ?? '',
139
- translatedMessage,
140
- isOwnMessage: true,
141
- threadId: payload.threadId ?? '',
142
- options: [],
143
- },
144
- ],
145
- })
146
-
147
- const response = await this.api.postMessageFx(payload)
148
- if (isTranslated) {
149
- return await this.translation.translateMessageFx({
150
- targetLanguage: translationTargetLanguage as string,
151
- message: {
152
- ...response,
153
- id: generateRandomId(),
154
- isOwnMessage: false,
155
- },
156
- })
157
- }
158
-
159
- return response
160
- },
161
- ),
162
- })
163
- }
164
-
165
- private initChatReplyWatcher() {
166
- sample({
167
- clock: this.sendHelpRequestFx.doneData,
168
- source: [this.credits.$assignmentId, this.credits.$exerciseId, this.messages.$state] as const,
169
- fn: ([assignmentId, exerciseId, messages], response) => ({
170
- assignmentId,
171
- exerciseId,
172
- messages: messages[assignmentId]?.[exerciseId]?.messages || [],
173
- response,
174
- }),
175
- }).watch(({ assignmentId, exerciseId, messages, response }) => {
176
- this.messages.setField({
177
- entityId: assignmentId,
178
- fieldId: exerciseId,
179
- messages: [
180
- ...messages,
181
- {
182
- id: generateRandomId(),
183
- message: response.message,
184
- isOwnMessage: false,
185
- threadId: response.threadId,
186
- options: response.options,
187
- translatedMessage:
188
- 'translatedMessage' in response
189
- ? response.translatedMessage
190
- : undefined,
191
- },
192
- ],
138
+ })
139
+
140
+ const response = await this.api.postMessageFx(payload)
141
+ if (isTranslated) {
142
+ return await this.translation.translateMessageFx({
143
+ targetLanguage: translationTargetLanguage as string,
144
+ message: {
145
+ ...response,
146
+ id: generateRandomId(),
147
+ isOwnMessage: false,
148
+ },
193
149
  })
150
+ }
194
151
 
195
- this.threadIds.setField({
196
- entityId: assignmentId,
197
- fieldId: exerciseId,
198
- threadId: response.threadId,
199
- })
200
- })
201
-
202
- sample({
203
- clock: this.sendHelpRequestFx.fail,
204
- source: [this.credits.$assignmentId, this.credits.$exerciseId, this.messages.$state] as const,
205
- fn: ([assignmentId, exerciseId, messages]) => ({
206
- assignmentId,
207
- exerciseId,
208
- messages: messages[assignmentId]?.[exerciseId]?.messages || [],
209
- }),
210
- filter: (_, { error }) => error.message !== 'Request aborted manually',
211
- }).watch(({ assignmentId, exerciseId, messages }) => {
212
- this.messages.setField({
213
- entityId: assignmentId,
214
- fieldId: exerciseId,
215
- messages: [...messages, this.defaultMessages.error],
216
- })
217
- })
218
-
219
- sample({
220
- clock: this.removeLastMessage,
221
- source: [this.$lastMessageIds, this.messages.$state] as const,
222
- fn: ([lastMessageIds, messages]) => ({
223
- lastMessageIds,
224
- messages,
225
- }),
226
- }).watch(({ lastMessageIds, messages }) => {
227
- if (!lastMessageIds) return
228
-
229
- const updatedMessages = [
230
- ...messages[lastMessageIds.entityId][lastMessageIds.fieldId].messages,
231
- ]
232
- if (updatedMessages) updatedMessages.pop()
233
-
234
- this.messages.setField({
235
- entityId: lastMessageIds.entityId,
236
- fieldId: lastMessageIds.fieldId,
237
- messages: updatedMessages,
238
- })
239
- })
240
- }
152
+ return response
153
+ },
154
+ ),
155
+ })
156
+ }
157
+
158
+ private initChatReplyWatcher() {
159
+ sample({
160
+ clock: this.sendHelpRequestFx.doneData,
161
+ source: [this.credits.$assignmentId, this.credits.$exerciseId, this.messages.$state] as const,
162
+ fn: ([assignmentId, exerciseId, messages], response) => ({
163
+ assignmentId,
164
+ exerciseId,
165
+ messages: messages[assignmentId]?.[exerciseId]?.messages || [],
166
+ response,
167
+ }),
168
+ }).watch(({ assignmentId, exerciseId, messages, response }) => {
169
+ this.messages.setField({
170
+ entityId: assignmentId,
171
+ fieldId: exerciseId,
172
+ messages: [
173
+ ...messages,
174
+ {
175
+ id: generateRandomId(),
176
+ message: response.message,
177
+ isOwnMessage: false,
178
+ threadId: response.threadId,
179
+ options: response.options,
180
+ translatedMessage:
181
+ 'translatedMessage' in response ? response.translatedMessage : undefined,
182
+ },
183
+ ],
184
+ })
185
+
186
+ this.threadIds.setField({
187
+ entityId: assignmentId,
188
+ fieldId: exerciseId,
189
+ threadId: response.threadId,
190
+ })
191
+ })
241
192
 
242
- public startConversation() {
243
- const assignmentId = this.credits.$assignmentId.getState()
244
- const exerciseId = this.credits.$exerciseId.getState()
245
- const messages = this.messages.$state.getState()[assignmentId]?.[exerciseId]?.messages ?? []
193
+ sample({
194
+ clock: this.sendHelpRequestFx.fail,
195
+ source: [this.credits.$assignmentId, this.credits.$exerciseId, this.messages.$state] as const,
196
+ fn: ([assignmentId, exerciseId, messages]) => ({
197
+ assignmentId,
198
+ exerciseId,
199
+ messages: messages[assignmentId]?.[exerciseId]?.messages || [],
200
+ }),
201
+ filter: (_, { error }) => error.message !== 'Request aborted manually',
202
+ }).watch(({ assignmentId, exerciseId, messages }) => {
203
+ this.messages.setField({
204
+ entityId: assignmentId,
205
+ fieldId: exerciseId,
206
+ messages: [...messages, this.defaultMessages.error],
207
+ })
208
+ })
246
209
 
247
- if (messages.length) return
210
+ sample({
211
+ clock: this.removeLastMessage,
212
+ source: [this.$lastMessageIds, this.messages.$state] as const,
213
+ fn: ([lastMessageIds, messages]) => ({
214
+ lastMessageIds,
215
+ messages,
216
+ }),
217
+ }).watch(({ lastMessageIds, messages }) => {
218
+ if (!lastMessageIds) return
219
+
220
+ const updatedMessages = [
221
+ ...messages[lastMessageIds.entityId][lastMessageIds.fieldId].messages,
222
+ ]
223
+ if (updatedMessages) updatedMessages.pop()
224
+
225
+ this.messages.setField({
226
+ entityId: lastMessageIds.entityId,
227
+ fieldId: lastMessageIds.fieldId,
228
+ messages: updatedMessages,
229
+ })
230
+ })
231
+ }
248
232
 
249
- this.messages.setField({
250
- entityId: assignmentId,
251
- fieldId: exerciseId,
252
- messages: [this.defaultMessages.start],
253
- })
233
+ public startConversation() {
234
+ const assignmentId = this.credits.$assignmentId.getState()
235
+ const exerciseId = this.credits.$exerciseId.getState()
236
+ const messages = this.messages.$state.getState()[assignmentId]?.[exerciseId]?.messages ?? []
254
237
 
255
- this.availableChats.setField({
256
- entityId: assignmentId,
257
- fieldId: exerciseId,
258
- isAvailable: true,
259
- });
260
- }
238
+ if (messages.length) return
261
239
 
262
- public setHintMessage({ entityId, fieldId, ...message }: SetHintMessageProps) {
263
- const messages = this.messages.$state.getState()?.[entityId]?.[fieldId]?.messages
264
- if (messages?.length > 1) return
240
+ this.messages.setField({
241
+ entityId: assignmentId,
242
+ fieldId: exerciseId,
243
+ messages: [this.defaultMessages.start],
244
+ })
265
245
 
266
- this.messages.setField({
267
- entityId,
268
- fieldId,
269
- messages: [
270
- {
271
- ...message,
272
- options: this.$alternatives.getState()?.options.hints ?? message.options,
273
- },
274
- ],
275
- })
276
- }
246
+ this.availableChats.setField({
247
+ entityId: assignmentId,
248
+ fieldId: exerciseId,
249
+ isAvailable: true,
250
+ })
251
+ }
252
+
253
+ public setHintMessage({ entityId, fieldId, ...message }: SetHintMessageProps) {
254
+ const messages = this.messages.$state.getState()?.[entityId]?.[fieldId]?.messages
255
+ if (messages?.length > 1) return
256
+
257
+ this.messages.setField({
258
+ entityId,
259
+ fieldId,
260
+ messages: [
261
+ {
262
+ ...message,
263
+ options: this.$alternatives.getState()?.options.hints ?? message.options,
264
+ },
265
+ ],
266
+ })
267
+ }
277
268
  }